diff --git a/packages/@ember/-internals/metal/lib/computed.ts b/packages/@ember/-internals/metal/lib/computed.ts index aa164cb4ba5..ea48857d65c 100644 --- a/packages/@ember/-internals/metal/lib/computed.ts +++ b/packages/@ember/-internals/metal/lib/computed.ts @@ -4,10 +4,7 @@ import toString from '@ember/-internals/utils/lib/to-string'; import inspect from '@ember/debug/lib/inspect'; import { assert } from '@ember/debug'; import { isDestroyed } from '@glimmer/destroyable'; -import { DEBUG } from '@glimmer/env'; -import type { UpdatableTag } from '@glimmer/interfaces'; import { - ALLOW_CYCLES, UPDATE_TAG as updateTag, validateTag, valueForTag, @@ -392,17 +389,14 @@ export class ComputedProperty extends ComputedDescriptor { let meta = metaFor(obj); let tagMeta = tagMetaFor(obj); - let propertyTag = tagFor(obj, keyName, tagMeta) as UpdatableTag; + let propertyTag = tagFor(obj, keyName, tagMeta); let ret; - let revision = meta.revisionFor(keyName); if (revision !== undefined && validateTag(propertyTag, revision)) { ret = meta.valueFor(keyName); } else { - // For backwards compatibility, we only throw if the CP has any dependencies. CPs without dependencies - // should be allowed, even after the object has been destroyed, which is why we check _dependentKeys. assert( `Attempted to access the computed ${obj}.${keyName} on a destroyed object, which is not allowed`, this._dependentKeys === undefined || !isDestroyed(obj) @@ -410,17 +404,12 @@ export class ComputedProperty extends ComputedDescriptor { let { _getter, _dependentKeys } = this; - // Create a tracker that absorbs any trackable actions inside the CP untrack(() => { ret = _getter!.call(obj, keyName); }); if (_dependentKeys !== undefined) { updateTag(propertyTag, getChainTagsForKeys(obj, _dependentKeys, tagMeta, meta)); - - if (DEBUG) { - ALLOW_CYCLES!.set(propertyTag, true); - } } meta.setValueFor(keyName, ret); @@ -489,16 +478,11 @@ export class ComputedProperty extends ComputedDescriptor { finishLazyChains(meta, keyName, ret); let tagMeta = tagMetaFor(obj); - let propertyTag = tagFor(obj, keyName, tagMeta) as UpdatableTag; + let propertyTag = tagFor(obj, keyName, tagMeta); let { _dependentKeys } = this; - if (_dependentKeys !== undefined) { updateTag(propertyTag, getChainTagsForKeys(obj, _dependentKeys, tagMeta, meta)); - - if (DEBUG) { - ALLOW_CYCLES!.set(propertyTag, true); - } } meta.setRevisionFor(keyName, valueForTag(propertyTag)); @@ -556,10 +540,9 @@ class AutoComputedProperty extends ComputedProperty { let meta = metaFor(obj); let tagMeta = tagMetaFor(obj); - let propertyTag = tagFor(obj, keyName, tagMeta) as UpdatableTag; + let propertyTag = tagFor(obj, keyName, tagMeta); let ret; - let revision = meta.revisionFor(keyName); if (revision !== undefined && validateTag(propertyTag, revision)) { @@ -572,7 +555,6 @@ class AutoComputedProperty extends ComputedProperty { let { _getter } = this; - // Create a tracker that absorbs any trackable actions inside the CP let tag = track(() => { ret = _getter!.call(obj, keyName); }); @@ -587,8 +569,6 @@ class AutoComputedProperty extends ComputedProperty { consumeTag(propertyTag); - // Add the tag of the returned value if it is an array, since arrays - // should always cause updates if they are consumed and then changed if (Array.isArray(ret)) { consumeTag(tagFor(ret, '[]', tagMeta)); } diff --git a/packages/@glimmer/interfaces/lib/tags.d.ts b/packages/@glimmer/interfaces/lib/tags.d.ts index 00bf6227d27..2257b1b6ee4 100644 --- a/packages/@glimmer/interfaces/lib/tags.d.ts +++ b/packages/@glimmer/interfaces/lib/tags.d.ts @@ -1,57 +1,11 @@ -declare const TYPE: unique symbol; -export type TagTypeSymbol = typeof TYPE; - -declare const COMPUTE: unique symbol; -export type TagComputeSymbol = typeof COMPUTE; - -export type DIRTYABLE_TAG_ID = 0; -export type UPDATABLE_TAG_ID = 1; -export type COMBINATOR_TAG_ID = 2; -export type CONSTANT_TAG_ID = 3; - -/** - * This union represents all of the possible tag types for the monomorphic tag class. - * Other custom tag classes can exist, such as CurrentTag and VolatileTag, but for - * performance reasons, any type of tag that is meant to be used frequently should - * be added to the monomorphic tag. - */ -export type MonomorphicTagId = - | DIRTYABLE_TAG_ID - | UPDATABLE_TAG_ID - | COMBINATOR_TAG_ID - | CONSTANT_TAG_ID; - -export type VOLATILE_TAG_ID = 100; -export type CURRENT_TAG_ID = 101; - -export type PolymorphicTagId = VOLATILE_TAG_ID | CURRENT_TAG_ID; - -export type TagId = MonomorphicTagId | PolymorphicTagId; - export type Revision = number; -export interface Tag { - readonly [TYPE]: TagId; - readonly subtag?: Tag | Tag[] | null | undefined; - [COMPUTE](): Revision; -} - -export interface MonomorphicTag extends Tag { - readonly [TYPE]: MonomorphicTagId; -} - -export interface UpdatableTag extends MonomorphicTag { - readonly [TYPE]: UPDATABLE_TAG_ID; -} - -export interface DirtyableTag extends MonomorphicTag { - readonly [TYPE]: DIRTYABLE_TAG_ID; -} - -export interface ConstantTag extends MonomorphicTag { - readonly [TYPE]: CONSTANT_TAG_ID; -} +// A Tag is just a callable that returns the current revision. Reading it +// inside an alien-signals subscriber registers a dependency. +export type Tag = () => Revision; -export interface CombinatorTag extends MonomorphicTag { - readonly [TYPE]: COMBINATOR_TAG_ID; -} +export type MonomorphicTag = Tag; +export type UpdatableTag = Tag; +export type DirtyableTag = Tag; +export type ConstantTag = Tag; +export type CombinatorTag = Tag; diff --git a/packages/@glimmer/runtime/lib/compiled/opcodes/-debug-strip.ts b/packages/@glimmer/runtime/lib/compiled/opcodes/-debug-strip.ts index 0795d1c1507..ffd277147ba 100644 --- a/packages/@glimmer/runtime/lib/compiled/opcodes/-debug-strip.ts +++ b/packages/@glimmer/runtime/lib/compiled/opcodes/-debug-strip.ts @@ -35,15 +35,11 @@ import { wrap, } from '@glimmer/debug/lib/stack-check'; import { REFERENCE, UNDEFINED_REFERENCE } from '@glimmer/reference/lib/reference'; -import { COMPUTE } from '@glimmer/validator/lib/validators'; - import { ScopeImpl } from '../../scope'; import { VMArgumentsImpl } from '../../vm/arguments'; import { ComponentElementOperations } from './component'; -export const CheckTag: Checker = CheckInterface({ - [COMPUTE]: CheckFunction, -}); +export const CheckTag: Checker = CheckFunction as unknown as Checker; export const CheckOperations: Checker> = wrap(() => CheckNullable(CheckInstanceof(ComponentElementOperations)) diff --git a/packages/@glimmer/validator/index.ts b/packages/@glimmer/validator/index.ts index a3abcaa5007..ee97b263bd7 100644 --- a/packages/@glimmer/validator/index.ts +++ b/packages/@glimmer/validator/index.ts @@ -33,16 +33,12 @@ export { untrack, } from './lib/tracking'; export { - ALLOW_CYCLES, bump, combine, - COMPUTE, - CONSTANT, CONSTANT_TAG, createTag, createUpdatableTag, CURRENT_TAG, - CurrentTag, DIRTY_TAG as dirtyTag, INITIAL, isConstTag, @@ -50,9 +46,7 @@ export { UPDATE_TAG as updateTag, validateTag, valueForTag, - VOLATILE, VOLATILE_TAG, - VolatileTag, } from './lib/validators'; export type { CombinatorTag, diff --git a/packages/@glimmer/validator/lib/meta.ts b/packages/@glimmer/validator/lib/meta.ts index 56e30cc7be1..856d39bcffa 100644 --- a/packages/@glimmer/validator/lib/meta.ts +++ b/packages/@glimmer/validator/lib/meta.ts @@ -1,5 +1,5 @@ import { DEBUG } from '@glimmer/env'; -import type { ConstantTag, UpdatableTag } from '@glimmer/interfaces'; +import type { UpdatableTag } from '@glimmer/interfaces'; import type { Indexable } from './utils'; @@ -59,7 +59,7 @@ export function tagFor( obj: T, key: keyof T | string | symbol, meta?: TagMeta -): UpdatableTag | ConstantTag { +): UpdatableTag { let tags = meta === undefined ? tagMetaFor(obj) : meta; let tag = tags.get(key); diff --git a/packages/@glimmer/validator/lib/validators.ts b/packages/@glimmer/validator/lib/validators.ts index a701a133e53..6fcbb4849cb 100644 --- a/packages/@glimmer/validator/lib/validators.ts +++ b/packages/@glimmer/validator/lib/validators.ts @@ -1,300 +1,142 @@ import { DEBUG } from '@glimmer/env'; -import type { - COMBINATOR_TAG_ID as ICOMBINATOR_TAG_ID, - CONSTANT_TAG_ID as ICONSTANT_TAG_ID, - ConstantTag, - CURRENT_TAG_ID as ICURRENT_TAG_ID, - DIRTYABLE_TAG_ID as IDIRTYABLE_TAG_ID, - DirtyableTag, - MonomorphicTagId, - Tag, - TagComputeSymbol, - TagTypeSymbol, - UPDATABLE_TAG_ID as IUPDATABLE_TAG_ID, - UpdatableTag, - VOLATILE_TAG_ID as IVOLATILE_TAG_ID, -} from '@glimmer/interfaces'; +import type { Tag } from '@glimmer/interfaces'; import { scheduleRevalidate } from '@glimmer/global-context'; +import { signal } from 'alien-signals'; import { debug } from './debug'; import { unwrap } from './utils'; -////////// +// `Tag` is just `() => Revision`. Read it to get the current revision and, +// inside an alien-signals subscriber, register a dependency. export type Revision = number; - -export const CONSTANT: Revision = 0; export const INITIAL: Revision = 1; -export const VOLATILE: Revision = NaN; - -export let $REVISION = INITIAL; - -export function bump(): void { - $REVISION++; -} - -////////// - -const DIRYTABLE_TAG_ID: IDIRTYABLE_TAG_ID = 0; -const UPDATABLE_TAG_ID: IUPDATABLE_TAG_ID = 1; -const COMBINATOR_TAG_ID: ICOMBINATOR_TAG_ID = 2; -const CONSTANT_TAG_ID: ICONSTANT_TAG_ID = 3; - -////////// - -export const COMPUTE: TagComputeSymbol = Symbol('TAG_COMPUTE') as TagComputeSymbol; -Reflect.set(globalThis, 'COMPUTE_SYMBOL', COMPUTE); - -////////// - -/** - * `value` receives a tag and returns an opaque Revision based on that tag. This - * snapshot can then later be passed to `validate` with the same tag to - * determine if the tag has changed at all since the time that `value` was - * called. - * - * @param tag - */ -export function valueForTag(tag: Tag): Revision { - return tag[COMPUTE](); +const VOLATILE: Revision = NaN; + +const $tick = signal(INITIAL); +const advance = (): Revision => { + const n = $tick() + 1; + $tick(n); + return n; +}; +export const bump = (): void => { + advance(); +}; + +// Dirtyable tags: read-only Tag fn paired with a hidden `dirty` writer. +const dirtyWriters = new WeakMap void>(); + +export function createTag(): Tag { + const s = signal(INITIAL); + const tag: Tag = () => s(); + dirtyWriters.set(tag, s); + return tag; } -/** - * `validate` receives a tag and a snapshot from a previous call to `value` with - * the same tag, and determines if the tag is still valid compared to the - * snapshot. If the tag's state has changed at all since then, `validate` will - * return false, otherwise it will return true. This is used to determine if a - * calculation related to the tags should be rerun. - * - * @param tag - * @param snapshot - */ -export function validateTag(tag: Tag, snapshot: Revision): boolean { - return snapshot >= tag[COMPUTE](); +// Updatable tags: a dirtyable that can have its subtag re-pointed via +// updateTag. Two pieces of bookkeeping survive (each verified by removing +// it and watching specific tests fail): +// +// 1. Cycle re-entrance guard. ember's CP set/get both call +// `updateTag(propertyTag, depsChain)`, building reciprocal subtag +// pointers between two property tags whose CPs depend on each other. +// alien-signals re-entry returns NaN; ember needs a stable last value +// plus a tick bump so neighbouring caches revalidate. +// 2. Adoption "buffer". `updateTag` may adopt a subtag whose revision is +// already higher than the parent's; naive `max(own, sub())` makes the +// parent jump and fires observers on the parent without anything they +// care about having changed. +const computing = new WeakSet(); +interface UpdatableState { + buffer: Revision | null; + last: Revision; + own: (v: Revision) => void; + sub: (s: Tag | null) => void; } - -////////// - -const TYPE: TagTypeSymbol = Symbol('TAG_TYPE') as TagTypeSymbol; - -// this is basically a const -export let ALLOW_CYCLES: WeakMap | undefined; - -if (DEBUG) { - ALLOW_CYCLES = new WeakMap(); -} - -function allowsCycles(tag: Tag): boolean { - if (ALLOW_CYCLES === undefined) { - return true; - } else { - return ALLOW_CYCLES.has(tag); - } -} - -class MonomorphicTagImpl { - static combine(this: void, tags: Tag[]): Tag { - switch (tags.length) { - case 0: - return CONSTANT_TAG; - case 1: - return tags[0] as Tag; - default: { - let tag: MonomorphicTagImpl = new MonomorphicTagImpl(COMBINATOR_TAG_ID); - tag.subtag = tags; - return tag; - } +const updatables = new WeakMap(); + +export function createUpdatableTag(): Tag { + const ownSig = signal(INITIAL); + const subSig = signal(null); + const state: UpdatableState = { buffer: null, last: INITIAL, own: ownSig, sub: subSig }; + const tag: Tag = () => { + if (computing.has(tag)) { + advance(); + return state.last; } - } - - private revision = INITIAL; - private lastChecked = INITIAL; - private lastValue = INITIAL; - - private isUpdating = false; - public subtag: Tag | Tag[] | null = null; - private subtagBufferCache: Revision | null = null; - - declare [TYPE]: T; - - constructor(type: T) { - this[TYPE] = type; - } - - [COMPUTE](): Revision { - let { lastChecked } = this; - - if (this.isUpdating) { - if (DEBUG && !allowsCycles(this)) { - throw new Error('Cycles in tags are not allowed'); - } - - this.lastChecked = ++$REVISION; - } else if (lastChecked !== $REVISION) { - this.isUpdating = true; - this.lastChecked = $REVISION; - - try { - let { subtag, revision } = this; - - if (subtag !== null) { - if (Array.isArray(subtag)) { - for (const tag of subtag) { - let value = tag[COMPUTE](); - revision = Math.max(value, revision); - } - } else { - let subtagValue = subtag[COMPUTE](); - - if (subtagValue === this.subtagBufferCache) { - revision = Math.max(revision, this.lastValue); - } else { - // Clear the temporary buffer cache - this.subtagBufferCache = null; - revision = Math.max(revision, subtagValue); - } - } - } - - this.lastValue = revision; - } finally { - this.isUpdating = false; + computing.add(tag); + try { + const o = ownSig(); + const s = subSig(); + if (s === null) { + state.last = o; + return o; } + const sv = s(); + const r = + sv === state.buffer ? Math.max(o, state.last) : ((state.buffer = null), Math.max(o, sv)); + state.last = r; + return r; + } finally { + computing.delete(tag); } + }; + dirtyWriters.set(tag, ownSig); + updatables.set(tag, state); + return tag; +} - return this.lastValue; - } - - static updateTag(this: void, _tag: UpdatableTag, _subtag: Tag) { - // catch bug by non-TS users - - if (DEBUG && _tag[TYPE] !== UPDATABLE_TAG_ID) { - throw new Error('Attempted to update a tag that was not updatable'); - } - - // TODO: TS 3.7 should allow us to do this via assertion - let tag = _tag as MonomorphicTagImpl; - let subtag = _subtag as MonomorphicTagImpl; - - if (subtag === CONSTANT_TAG) { - tag.subtag = null; - } else { - // There are two different possibilities when updating a subtag: - // - // 1. subtag[COMPUTE]() <= tag[COMPUTE](); - // 2. subtag[COMPUTE]() > tag[COMPUTE](); - // - // The first possibility is completely fine within our caching model, but - // the second possibility presents a problem. If the parent tag has - // already been read, then it's value is cached and will not update to - // reflect the subtag's greater value. Next time the cache is busted, the - // subtag's value _will_ be read, and it's value will be _greater_ than - // the saved snapshot of the parent, causing the resulting calculation to - // be rerun erroneously. - // - // In order to prevent this, when we first update to a new subtag we store - // its computed value, and then check against that computed value on - // subsequent updates. If its value hasn't changed, then we return the - // parent's previous value. Once the subtag changes for the first time, - // we clear the cache and everything is finally in sync with the parent. - tag.subtagBufferCache = subtag[COMPUTE](); - tag.subtag = subtag; - } +export function updateTag(tag: Tag, sub: Tag): void { + const state = updatables.get(tag); + if (state === undefined) { + if (DEBUG) throw new Error('Attempted to update a tag that was not updatable'); + return; } - - static dirtyTag( - this: void, - tag: DirtyableTag | UpdatableTag, - disableConsumptionAssertion?: boolean - ) { - if ( - DEBUG && - // catch bug by non-TS users - - !(tag[TYPE] === UPDATABLE_TAG_ID || tag[TYPE] === DIRYTABLE_TAG_ID) - ) { - throw new Error('Attempted to dirty a tag that was not dirtyable'); - } - - if (DEBUG && disableConsumptionAssertion !== true) { - // Usually by this point, we've already asserted with better error information, - // but this is our last line of defense. - unwrap(debug.assertTagNotConsumed)(tag); - } - - (tag as MonomorphicTagImpl).revision = ++$REVISION; - - scheduleRevalidate(); + if (sub === CONSTANT_TAG) { + state.buffer = null; + state.sub(null); + } else { + state.buffer = sub(); + state.sub(sub); } } - -export const DIRTY_TAG = MonomorphicTagImpl.dirtyTag; -export const UPDATE_TAG = MonomorphicTagImpl.updateTag; - -////////// - -export function createTag(): DirtyableTag { - return new MonomorphicTagImpl(DIRYTABLE_TAG_ID); -} - -export function createUpdatableTag(): UpdatableTag { - return new MonomorphicTagImpl(UPDATABLE_TAG_ID); +export const UPDATE_TAG = updateTag; + +export function combine(tags: Tag[]): Tag { + if (tags.length === 0) return CONSTANT_TAG; + if (tags.length === 1) return tags[0] as Tag; + return () => { + let max: Revision = INITIAL; + // Math.max propagates NaN, keeping combinators that include VOLATILE_TAG + // always-stale (validateTag is `snapshot >= NaN`). + for (const t of tags) max = Math.max(max, t()); + return max; + }; } -////////// +export const CONSTANT_TAG: Tag = () => INITIAL; +export const isConstTag = (tag: Tag): boolean => tag === CONSTANT_TAG; -export const CONSTANT_TAG: ConstantTag = new MonomorphicTagImpl(CONSTANT_TAG_ID); +export const VOLATILE_TAG: Tag = () => { + $tick(); + return VOLATILE; +}; -export function isConstTag(tag: Tag): tag is ConstantTag { - return tag === CONSTANT_TAG; -} +export const CURRENT_TAG: Tag = () => $tick(); -////////// - -const VOLATILE_TAG_ID: IVOLATILE_TAG_ID = 100; - -export class VolatileTag implements Tag { - readonly [TYPE] = VOLATILE_TAG_ID; - [COMPUTE](): Revision { - return VOLATILE; +export function dirtyTag(tag: Tag, skipAssertion?: boolean): void { + const s = dirtyWriters.get(tag); + if (s === undefined) { + if (DEBUG) throw new Error('Attempted to dirty a tag that was not dirtyable'); + return; } -} - -export const VOLATILE_TAG = new VolatileTag(); - -////////// - -const CURRENT_TAG_ID: ICURRENT_TAG_ID = 101; - -export class CurrentTag implements Tag { - readonly [TYPE] = CURRENT_TAG_ID; - [COMPUTE](): Revision { - return $REVISION; + if (DEBUG && skipAssertion !== true) { + unwrap(debug.assertTagNotConsumed)(tag); } + s(advance()); + scheduleRevalidate(); } +export const DIRTY_TAG = dirtyTag; -export const CURRENT_TAG = new CurrentTag(); - -////////// - -export const combine = MonomorphicTagImpl.combine; - -// Warm - -let tag1 = createUpdatableTag(); -let tag2 = createUpdatableTag(); -let tag3 = createUpdatableTag(); - -valueForTag(tag1); -DIRTY_TAG(tag1); -valueForTag(tag1); -UPDATE_TAG(tag1, combine([tag2, tag3])); -valueForTag(tag1); -DIRTY_TAG(tag2); -valueForTag(tag1); -DIRTY_TAG(tag3); -valueForTag(tag1); -UPDATE_TAG(tag1, tag3); -valueForTag(tag1); -DIRTY_TAG(tag3); -valueForTag(tag1); +export const valueForTag = (tag: Tag): Revision => tag(); +export const validateTag = (tag: Tag, snapshot: Revision): boolean => snapshot >= tag(); diff --git a/packages/@glimmer/validator/package.json b/packages/@glimmer/validator/package.json index fdcfc25357e..7b02742b88c 100644 --- a/packages/@glimmer/validator/package.json +++ b/packages/@glimmer/validator/package.json @@ -38,7 +38,8 @@ }, "dependencies": { "@glimmer/global-context": "workspace:*", - "@glimmer/interfaces": "workspace:*" + "@glimmer/interfaces": "workspace:*", + "alien-signals": "^3.2.0" }, "devDependencies": { "@glimmer/debug-util": "workspace:*", diff --git a/packages/@glimmer/validator/test/validators-test.ts b/packages/@glimmer/validator/test/validators-test.ts index 9c963a1c928..23bd7cb94ef 100644 --- a/packages/@glimmer/validator/test/validators-test.ts +++ b/packages/@glimmer/validator/test/validators-test.ts @@ -2,7 +2,6 @@ import { DEBUG } from '@glimmer/env'; import type { UpdatableTag } from '@glimmer/interfaces'; import { testOverrideGlobalContext } from '@glimmer/global-context'; import { - ALLOW_CYCLES, bump, combine, CONSTANT_TAG, @@ -66,7 +65,7 @@ module('@glimmer/validator: validators', () => { let subtag = createTag(); assert.throws( - () => updateTag(tag as unknown as UpdatableTag, subtag), + () => updateTag(tag, subtag), /Error: Attempted to update a tag that was not updatable/u ); }); @@ -128,55 +127,17 @@ module('@glimmer/validator: validators', () => { let tag = createUpdatableTag(); let subtag = createUpdatableTag(); - // First, we get a snapshot of the parent let snapshot = valueForTag(tag); - // Then we dirty the currently unrelated subtag dirtyTag(subtag); - - // Now, we update the parent tag with the subtag, and revalidate it updateTag(tag, subtag); assert.ok(validateTag(tag, snapshot), 'tag is still valid after being updated'); - // Finally, dirty the subtag one final time to bust the buffer cache dirtyTag(subtag); assert.notOk(validateTag(tag, snapshot), 'tag is invalid after subtag is dirtied again'); }); - - if (DEBUG) { - test('does not allow cycles on tags that have not been marked with ALLOW_CYCLES', (assert) => { - let tag = createUpdatableTag(); - let subtag = createUpdatableTag(); - - let snapshot = valueForTag(tag); - - updateTag(tag, subtag); - updateTag(subtag, tag); - - dirtyTag(tag); - - assert.throws(() => validateTag(tag, snapshot)); - }); - - test('does allow cycles on tags that have been marked with ALLOW_CYCLES', (assert) => { - let tag = createUpdatableTag(); - let subtag = createUpdatableTag(); - - let snapshot = valueForTag(tag); - - unwrap(ALLOW_CYCLES).set(tag, true); - unwrap(ALLOW_CYCLES).set(subtag, true); - - updateTag(tag, subtag); - updateTag(subtag, tag); - - dirtyTag(tag); - - assert.notOk(validateTag(tag, snapshot)); - }); - } }); module('CombinatorTag', () => { @@ -203,7 +164,6 @@ module('@glimmer/validator: validators', () => { let combined = combine([tag1, tag2]); assert.throws( - // @ts-expect-error this is an error condition () => dirtyTag(combined), /Error: Attempted to dirty a tag that was not dirtyable/u ); @@ -216,7 +176,6 @@ module('@glimmer/validator: validators', () => { let combined = combine([tag1, tag2]); assert.throws( - // @ts-expect-error this is an error condition () => updateTag(combined, tag1), /Error: Attempted to update a tag that was not updatable/u ); @@ -228,7 +187,6 @@ module('@glimmer/validator: validators', () => { if (DEBUG) { test('it cannot be dirtied', (assert) => { assert.throws( - // @ts-expect-error this is an error condition () => dirtyTag(CONSTANT_TAG), /Error: Attempted to dirty a tag that was not dirtyable/u ); @@ -238,7 +196,6 @@ module('@glimmer/validator: validators', () => { let subtag = createTag(); assert.throws( - // @ts-expect-error this is an error condition () => updateTag(CONSTANT_TAG, subtag), /Error: Attempted to update a tag that was not updatable/u ); @@ -266,7 +223,6 @@ module('@glimmer/validator: validators', () => { if (DEBUG) { test('it cannot be dirtied', (assert) => { assert.throws( - // @ts-expect-error this is an error condition () => dirtyTag(VOLATILE_TAG), /Error: Attempted to dirty a tag that was not dirtyable/u ); @@ -276,7 +232,6 @@ module('@glimmer/validator: validators', () => { let subtag = createTag(); assert.throws( - // @ts-expect-error this is an error condition () => updateTag(VOLATILE_TAG, subtag), /Error: Attempted to update a tag that was not updatable/u ); @@ -311,7 +266,6 @@ module('@glimmer/validator: validators', () => { if (DEBUG) { test('it cannot be dirtied', (assert) => { assert.throws( - // @ts-expect-error this is an error condition () => dirtyTag(CURRENT_TAG), /Error: Attempted to dirty a tag that was not dirtyable/u ); @@ -321,7 +275,6 @@ module('@glimmer/validator: validators', () => { let subtag = createTag(); assert.throws( - // @ts-expect-error this is an error condition () => updateTag(CURRENT_TAG, subtag), /Error: Attempted to update a tag that was not updatable/u ); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 652dd7d501e..f66905f9636 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2270,6 +2270,9 @@ importers: '@glimmer/interfaces': specifier: workspace:* version: link:../interfaces + alien-signals: + specifier: ^3.2.0 + version: 3.2.1 devDependencies: '@glimmer/debug-util': specifier: workspace:* @@ -6185,6 +6188,9 @@ packages: ajv@8.18.0: resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==} + alien-signals@3.2.1: + resolution: {integrity: sha512-I8FjmltrfnDFoZedi5CG8DghVYNhzb/Ijluz7tCSJH0xpd0484Kowhbb1XDYOxfJpU1p5wnM2X54dA+IfGyD1g==} + amd-name-resolver@1.3.1: resolution: {integrity: sha512-26qTEWqZQ+cxSYygZ4Cf8tsjDBLceJahhtewxtKZA3SRa4PluuqYCuheemDQD+7Mf5B7sr+zhTDWAHDh02a1Dw==} engines: {node: 6.* || 8.* || >= 10.*} @@ -16344,6 +16350,8 @@ snapshots: json-schema-traverse: 1.0.0 require-from-string: 2.0.2 + alien-signals@3.2.1: {} + amd-name-resolver@1.3.1: dependencies: ensure-posix-path: 1.1.1 diff --git a/rollup.config.mjs b/rollup.config.mjs index aac5c953a52..83d113111c3 100644 --- a/rollup.config.mjs +++ b/rollup.config.mjs @@ -303,6 +303,10 @@ export function hiddenDependencies() { findFromProject('decorator-transforms').root, 'dist/runtime.js' ), + 'alien-signals': resolve( + findFromProject('@glimmer/validator', 'alien-signals').root, + 'esm/index.mjs' + ), }; }