@expressive/mvc 0.88.0 → 0.90.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"ref.js","names":[],"sources":["../../src/field/ref.ts"],"sourcesContent":["import { listener, capture } from '../observable';\nimport { State, update } from '../state';\nimport { def } from './def';\n\nconst { defineProperty, defineProperties } = Object;\n\ndeclare namespace ref {\n type Map<T extends State, R> = (\n this: T,\n key: State.Field<T> & string,\n state: T\n ) => R;\n\n type Callback<T> = (\n argument: T\n ) => ((next: T | null) => void) | Promise<void> | void | boolean;\n\n /** Callable ref that also exposes ref-object properties. */\n interface Object<T = any> {\n /** Set the current value of this reference. */\n (value: T | null): void;\n\n /** Current value held by this reference. */\n current: T;\n\n /** State instance this reference belongs to. */\n is: State;\n\n /** Key of property on state this reference belongs to. */\n key: string;\n\n /**\n * Subscribe to changes of this reference.\n *\n * @param callback - Callback to invoke with the current value when it changes.\n * @returns Unsubscribe function to stop listening to changes.\n */\n get(callback: (value: T) => void): () => void;\n\n /** Retrieve current value. */\n get(): T | null;\n }\n\n /** Object with references to all managed values of `T`. */\n type Proxy<T extends State> = { [P in State.Field<T>]-?: Object<T[P]> } & {\n get(): T;\n };\n\n type CustomProxy<T extends State, R> = { [P in State.Field<T>]-?: R } & {\n get(): T;\n };\n}\n\n/**\n * Creates an object with references to all managed values.\n * Each property is a function to set value in state when invoked.\n *\n * *Properties are simultaneously a ref-function and ref-object, use as needed.*\n *\n * Non-enumerable; excluded from snapshots and `ref(this)`.\n *\n * @param state - Source state from which to reference values.\n */\nfunction ref<T extends State>(state: T): ref.Proxy<T>;\n\n/**\n * Creates an object with values based on managed values.\n * Each property will invoke map function on first access supply\n * the its return value going forward.\n *\n * Non-enumerable; excluded from snapshots and `ref(this)`.\n *\n * @param state - Source state from which to reference values.\n * @param map - Function producing the placeholder value for any given property.\n */\nfunction ref<T extends State, R>(\n state: T,\n map: ref.Map<T, R>\n): ref.CustomProxy<T, R>;\n\n/**\n * Creates a ref-compatible property.\n * Callable to set the value, with ref-object properties for reading and subscribing.\n *\n * Non-enumerable; excluded from snapshots and `ref(this)`.\n *\n * @param callback - Optional callback to synchronously fire when reference is first set or does update.\n */\nfunction ref<T>(callback?: ref.Callback<T>): ref.Object<T>;\n\n/**\n * Creates a ref-compatible property.\n * Callable to set the value, with ref-object properties for reading and subscribing.\n *\n * Non-enumerable; excluded from snapshots and `ref(this)`.\n *\n * @param callback - Optional callback to synchronously fire when reference is first set or does update.\n * @param ignoreNull - Default `true`. If `false`, will still callback with `null`.\n */\nfunction ref<T>(\n callback: ref.Callback<T | null>,\n ignoreNull: boolean\n): ref.Object<T>;\n\nfunction ref<T extends State>(\n arg?: ref.Callback<T> | T,\n arg2?: ref.Map<T, any> | boolean\n) {\n if (arg instanceof State)\n return proxy(arg, arg2 as ref.Map<T, any> | undefined);\n\n if (typeof arg == 'object')\n throw new Error(\n 'ref instruction requires a State instance, got a plain object'\n );\n\n return property(arg, arg2 as boolean | undefined);\n}\n\nfunction defaultMap(this: State, key: string) {\n const source = this as any as Record<string, any>;\n const set = (x: any) => (source[key] = x);\n const get = (cb?: (value: any) => void) =>\n cb ? listener(this, () => cb(source[key]), key) : source[key];\n\n defineProperties(set, {\n current: { get, set },\n get: { value: get },\n is: { value: this },\n key: { value: key }\n });\n\n return set;\n}\n\nfunction proxy(target: State, map: ref.Map<any, any> = defaultMap) {\n return def((_key, _subject) => {\n const value = {} as Record<string, any>;\n const source = target as any as Record<string, any>;\n\n listener(target, () => {\n for (const key in source)\n defineProperty(value, key, {\n configurable: true,\n get() {\n const out = map.call(target, key, target);\n defineProperty(value, key, { value: out });\n return out;\n }\n });\n\n return null;\n });\n\n return {\n get: () => value,\n enumerable: false\n };\n });\n}\n\nfunction property<T>(callback?: ref.Callback<T>, ignoreNull?: boolean) {\n return def((key, subject, state) => {\n let unset: ((next: T) => void) | undefined;\n\n function get(cb?: (value: T) => void) {\n return cb ? listener(subject, () => cb(state[key]), key) : state[key];\n }\n\n function set(value?: any) {\n if (!update(subject, key, value) || !callback) return;\n if (unset) unset = void unset(value);\n if (value === null && ignoreNull !== false) return;\n\n capture((release) => {\n const out = callback.call(subject, value);\n\n unset = (next) => {\n if (typeof out == 'function') out(next);\n release();\n };\n });\n }\n\n state[key] = null;\n\n defineProperties(set, {\n current: { get, set },\n get: { value: get },\n is: { value: subject },\n key: { value: key }\n });\n\n return {\n get: () => set,\n enumerable: false\n };\n });\n}\n\nexport { ref };\n"],"mappings":";;;;;AAIA,MAAM,EAAE,gBAAgB,qBAAqB;AAoG7C,SAAS,IACP,KACA,MACA;AACA,KAAI,eAAe,MACjB,QAAO,MAAM,KAAK,KAAoC;AAExD,KAAI,OAAO,OAAO,SAChB,OAAM,IAAI,MACR,gEACD;AAEH,QAAO,SAAS,KAAK,KAA4B;;AAGnD,SAAS,WAAwB,KAAa;CAC5C,MAAM,SAAS;CACf,MAAM,OAAO,MAAY,OAAO,OAAO;CACvC,MAAM,OAAO,OACX,KAAK,SAAS,YAAY,GAAG,OAAO,KAAK,EAAE,IAAI,GAAG,OAAO;AAE3D,kBAAiB,KAAK;EACpB,SAAS;GAAE;GAAK;GAAK;EACrB,KAAK,EAAE,OAAO,KAAK;EACnB,IAAI,EAAE,OAAO,MAAM;EACnB,KAAK,EAAE,OAAO,KAAK;EACpB,CAAC;AAEF,QAAO;;AAGT,SAAS,MAAM,QAAe,MAAyB,YAAY;AACjE,QAAO,KAAK,MAAM,aAAa;EAC7B,MAAM,QAAQ,EAAE;EAChB,MAAM,SAAS;AAEf,WAAS,cAAc;AACrB,QAAK,MAAM,OAAO,OAChB,gBAAe,OAAO,KAAK;IACzB,cAAc;IACd,MAAM;KACJ,MAAM,MAAM,IAAI,KAAK,QAAQ,KAAK,OAAO;AACzC,oBAAe,OAAO,KAAK,EAAE,OAAO,KAAK,CAAC;AAC1C,YAAO;;IAEV,CAAC;AAEJ,UAAO;IACP;AAEF,SAAO;GACL,WAAW;GACX,YAAY;GACb;GACD;;AAGJ,SAAS,SAAY,UAA4B,YAAsB;AACrE,QAAO,KAAK,KAAK,SAAS,UAAU;EAClC,IAAI;EAEJ,SAAS,IAAI,IAAyB;AACpC,UAAO,KAAK,SAAS,eAAe,GAAG,MAAM,KAAK,EAAE,IAAI,GAAG,MAAM;;EAGnE,SAAS,IAAI,OAAa;AACxB,OAAI,CAAC,OAAO,SAAS,KAAK,MAAM,IAAI,CAAC,SAAU;AAC/C,OAAI,MAAO,SAAQ,KAAK,MAAM,MAAM;AACpC,OAAI,UAAU,QAAQ,eAAe,MAAO;AAE5C,YAAS,YAAY;IACnB,MAAM,MAAM,SAAS,KAAK,SAAS,MAAM;AAEzC,aAAS,SAAS;AAChB,SAAI,OAAO,OAAO,WAAY,KAAI,KAAK;AACvC,cAAS;;KAEX;;AAGJ,QAAM,OAAO;AAEb,mBAAiB,KAAK;GACpB,SAAS;IAAE;IAAK;IAAK;GACrB,KAAK,EAAE,OAAO,KAAK;GACnB,IAAI,EAAE,OAAO,SAAS;GACtB,KAAK,EAAE,OAAO,KAAK;GACpB,CAAC;AAEF,SAAO;GACL,WAAW;GACX,YAAY;GACb;GACD"}
1
+ {"version":3,"file":"ref.js","names":[],"sources":["../../src/field/ref.ts"],"sourcesContent":["import { listener, capture } from '../observable';\nimport { Model, update } from '../state';\nimport { def } from './def';\n\nconst { defineProperty, defineProperties } = Object;\n\ndeclare namespace ref {\n type Map<T extends Model, R> = (\n this: T,\n key: Model.Field<T> & string,\n state: T\n ) => R;\n\n type Callback<T> = (\n argument: T\n ) => ((next: T | null) => void) | Promise<void> | void | boolean;\n\n /** Callable ref that also exposes ref-object properties. */\n interface Object<T = any> {\n /** Set the current value of this reference. */\n (value: T | null): void;\n\n /** Current value held by this reference. */\n current: T;\n\n /** Model instance this reference belongs to. */\n is: Model;\n\n /** Key of property on state this reference belongs to. */\n key: string;\n\n /**\n * Subscribe to changes of this reference.\n *\n * @param callback - Callback to invoke with the current value when it changes.\n * @returns Unsubscribe function to stop listening to changes.\n */\n get(callback: (value: T) => void): () => void;\n\n /** Retrieve current value. */\n get(): T | null;\n }\n\n /** Object with references to all managed values of `T`. */\n type Proxy<T extends Model> = { [P in Model.Field<T>]-?: Object<T[P]> } & {\n get(): T;\n };\n\n type CustomProxy<T extends Model, R> = { [P in Model.Field<T>]-?: R } & {\n get(): T;\n };\n}\n\n/**\n * Creates an object with references to all managed values.\n * Each property is a function to set value in state when invoked.\n *\n * *Properties are simultaneously a ref-function and ref-object, use as needed.*\n *\n * Non-enumerable; excluded from snapshots and `ref(this)`.\n *\n * @param state - Source state from which to reference values.\n */\nfunction ref<T extends Model>(state: T): ref.Proxy<T>;\n\n/**\n * Creates an object with values based on managed values.\n * Each property will invoke map function on first access supply\n * the its return value going forward.\n *\n * Non-enumerable; excluded from snapshots and `ref(this)`.\n *\n * @param state - Source state from which to reference values.\n * @param map - Function producing the placeholder value for any given property.\n */\nfunction ref<T extends Model, R>(\n state: T,\n map: ref.Map<T, R>\n): ref.CustomProxy<T, R>;\n\n/**\n * Creates a ref-compatible property.\n * Callable to set the value, with ref-object properties for reading and subscribing.\n *\n * Non-enumerable; excluded from snapshots and `ref(this)`.\n *\n * @param callback - Optional callback to synchronously fire when reference is first set or does update.\n */\nfunction ref<T>(callback?: ref.Callback<T>): ref.Object<T>;\n\n/**\n * Creates a ref-compatible property.\n * Callable to set the value, with ref-object properties for reading and subscribing.\n *\n * Non-enumerable; excluded from snapshots and `ref(this)`.\n *\n * @param callback - Optional callback to synchronously fire when reference is first set or does update.\n * @param ignoreNull - Default `true`. If `false`, will still callback with `null`.\n */\nfunction ref<T>(\n callback: ref.Callback<T | null>,\n ignoreNull: boolean\n): ref.Object<T>;\n\nfunction ref<T extends Model>(\n arg?: ref.Callback<T> | T,\n arg2?: ref.Map<T, any> | boolean\n) {\n if (arg instanceof Model)\n return proxy(arg, arg2 as ref.Map<T, any> | undefined);\n\n if (typeof arg == 'object')\n throw new Error(\n 'ref instruction requires a Model instance, got a plain object'\n );\n\n return property(arg, arg2 as boolean | undefined);\n}\n\nfunction defaultMap(this: Model, key: string) {\n const source = this as any as Record<string, any>;\n const set = (x: any) => (source[key] = x);\n const get = (cb?: (value: any) => void) =>\n cb ? listener(this, () => cb(source[key]), key) : source[key];\n\n defineProperties(set, {\n current: { get, set },\n get: { value: get },\n is: { value: this },\n key: { value: key }\n });\n\n return set;\n}\n\nfunction proxy(target: Model, map: ref.Map<any, any> = defaultMap) {\n return def((_key, _subject) => {\n const value = {} as Record<string, any>;\n const source = target as any as Record<string, any>;\n\n listener(target, () => {\n for (const key in source)\n defineProperty(value, key, {\n configurable: true,\n get() {\n const out = map.call(target, key, target);\n defineProperty(value, key, { value: out });\n return out;\n }\n });\n\n return null;\n });\n\n return {\n get: () => value,\n enumerable: false\n };\n });\n}\n\nfunction property<T>(callback?: ref.Callback<T>, ignoreNull?: boolean) {\n return def((key, subject, state) => {\n let unset: ((next: T) => void) | undefined;\n\n function get(cb?: (value: T) => void) {\n return cb ? listener(subject, () => cb(state[key]), key) : state[key];\n }\n\n function set(value?: any) {\n if (!update(subject, key, value) || !callback) return;\n if (unset) unset = void unset(value);\n if (value === null && ignoreNull !== false) return;\n\n capture((release) => {\n const out = callback.call(subject, value);\n\n unset = (next) => {\n if (typeof out == 'function') out(next);\n release();\n };\n });\n }\n\n state[key] = null;\n\n defineProperties(set, {\n current: { get, set },\n get: { value: get },\n is: { value: subject },\n key: { value: key }\n });\n\n return {\n get: () => set,\n enumerable: false\n };\n });\n}\n\nexport { ref };\n"],"mappings":";;;;;AAIA,MAAM,EAAE,gBAAgB,qBAAqB;AAoG7C,SAAS,IACP,KACA,MACA;AACA,KAAI,eAAe,MACjB,QAAO,MAAM,KAAK,KAAoC;AAExD,KAAI,OAAO,OAAO,SAChB,OAAM,IAAI,MACR,gEACD;AAEH,QAAO,SAAS,KAAK,KAA4B;;AAGnD,SAAS,WAAwB,KAAa;CAC5C,MAAM,SAAS;CACf,MAAM,OAAO,MAAY,OAAO,OAAO;CACvC,MAAM,OAAO,OACX,KAAK,SAAS,YAAY,GAAG,OAAO,KAAK,EAAE,IAAI,GAAG,OAAO;AAE3D,kBAAiB,KAAK;EACpB,SAAS;GAAE;GAAK;GAAK;EACrB,KAAK,EAAE,OAAO,KAAK;EACnB,IAAI,EAAE,OAAO,MAAM;EACnB,KAAK,EAAE,OAAO,KAAK;EACpB,CAAC;AAEF,QAAO;;AAGT,SAAS,MAAM,QAAe,MAAyB,YAAY;AACjE,QAAO,KAAK,MAAM,aAAa;EAC7B,MAAM,QAAQ,EAAE;EAChB,MAAM,SAAS;AAEf,WAAS,cAAc;AACrB,QAAK,MAAM,OAAO,OAChB,gBAAe,OAAO,KAAK;IACzB,cAAc;IACd,MAAM;KACJ,MAAM,MAAM,IAAI,KAAK,QAAQ,KAAK,OAAO;AACzC,oBAAe,OAAO,KAAK,EAAE,OAAO,KAAK,CAAC;AAC1C,YAAO;;IAEV,CAAC;AAEJ,UAAO;IACP;AAEF,SAAO;GACL,WAAW;GACX,YAAY;GACb;GACD;;AAGJ,SAAS,SAAY,UAA4B,YAAsB;AACrE,QAAO,KAAK,KAAK,SAAS,UAAU;EAClC,IAAI;EAEJ,SAAS,IAAI,IAAyB;AACpC,UAAO,KAAK,SAAS,eAAe,GAAG,MAAM,KAAK,EAAE,IAAI,GAAG,MAAM;;EAGnE,SAAS,IAAI,OAAa;AACxB,OAAI,CAAC,OAAO,SAAS,KAAK,MAAM,IAAI,CAAC,SAAU;AAC/C,OAAI,MAAO,SAAQ,KAAK,MAAM,MAAM;AACpC,OAAI,UAAU,QAAQ,eAAe,MAAO;AAE5C,YAAS,YAAY;IACnB,MAAM,MAAM,SAAS,KAAK,SAAS,MAAM;AAEzC,aAAS,SAAS;AAChB,SAAI,OAAO,OAAO,WAAY,KAAI,KAAK;AACvC,cAAS;;KAEX;;AAGJ,QAAM,OAAO;AAEb,mBAAiB,KAAK;GACpB,SAAS;IAAE;IAAK;IAAK;GACrB,KAAK,EAAE,OAAO,KAAK;GACnB,IAAI,EAAE,OAAO,SAAS;GACtB,KAAK,EAAE,OAAO,KAAK;GACpB,CAAC;AAEF,SAAO;GACL,WAAW;GACX,YAAY;GACb;GACD"}
@@ -1 +1 @@
1
- {"version":3,"file":"set.js","names":[],"sources":["../../src/field/set.ts"],"sourcesContent":["import { listener, capture, observer } from '../observable';\nimport { access, compute, event, State, update } from '../state';\nimport { def } from './def';\n\ndeclare namespace set {\n type Callback<T, S = any> = (\n this: S,\n next: T,\n previous: T\n ) => ((next: T) => void) | Promise<any> | void | boolean;\n\n type Factory<T, S = any> = (this: S, property: string) => Promise<T> | T;\n}\n\n/**\n * Set a property as a placeholder, initially `undefined` but required.\n *\n * Property cannot be accessed until it is defined. If accessed while undefined, a hybrid\n * `Promise`/`Error` (aka: [Suspense](https://reactjs.org/docs/concurrent-mode-suspense.html)) will be thrown.\n *\n * Non-enumerable; writable only if `onUpdate` callback is provided.\n *\n * @param value - Starting value (undefined for suspense placeholder).\n * @param onUpdate - Callback run when property is set.\n */\nfunction set<T>(value?: undefined, onUpdate?: set.Callback<T>): T;\n\n/**\n * Set property with an async factory function.\n * If required is not defined, factory runs lazily on first access.\n * If async, or returns a promise, suspense is thrown upon access until resolved.\n *\n * Non-enumerable and read-only.\n *\n * @param factory - Zero-arg async callback to produce property value.\n * @param required - Pass `true` to run factory immediately and require value.\n */\nfunction set<T>(factory: () => T | Promise<T>, required?: true): T;\n\n/**\n * Set property with a lazy factory, no suspense.\n *\n * Non-enumerable and read-only.\n *\n * @param factory - Zero-arg callback to produce property value.\n * @param required - Pass `false` to allow undefined while pending.\n */\nfunction set<T>(\n factory: () => T | Promise<T>,\n required: boolean\n): T | undefined;\n\n/**\n * Set property with a factory and update callback.\n *\n * Non-enumerable but writable via the callback.\n *\n * @param factory - Callback to produce initial value.\n * @param onUpdate - Callback run when property is updated.\n */\nfunction set<T>(factory: () => T | Promise<T>, onUpdate: set.Callback<T>): T;\n\n/**\n * Define a reactive computed property - the instruction equivalent of a getter.\n *\n * Unlike a zero-arg factory (which runs once and caches), the compute function\n * re-runs whenever any managed property it reads is updated. It receives the\n * instance as both `this` and its first argument, so arrow functions work.\n *\n * Enumerable and read-only, matching a prototype getter. Because it assigns\n * through an instruction rather than a concrete getter, subclasses may refine\n * the property's type with `declare`.\n *\n * @param compute - Function of arity >= 1; receives the instance to derive from.\n */\nfunction set<T, S = any>(compute: (this: S, self: S) => T): T;\n\n/**\n * Set a property with a non-function, non-Promise value.\n *\n * Non-enumerable but writable. Unlike plain assignment (`= value`), this\n * property is excluded from snapshots and `ref(this)`.\n *\n * @param value - Starting value for property.\n * @param onUpdate - Optional callback run when property is set.\n */\n// TODO: if onUpdate is not defined, should this have behavior unique to simple assignment?\n// I'm thinking default behavior should be to assign value directly to property, without triggering any additional updates.\n// All this requires is a default callback which throws true, silent updates are already implemented.\nfunction set<T>(\n value: T extends Promise<any> ? never : T,\n onUpdate?: set.Callback<T>\n): T;\n\nfunction set<T = any>(value?: unknown, argument?: unknown): any {\n return def<T>((key, subject) => {\n if (value instanceof Promise)\n throw new TypeError(\n `Direct promises are not supported in set(${subject}.${key}). Use set(() => promise) instead.`\n );\n\n if (typeof value == 'function' && value.length > 0) {\n compute.call(subject, value as () => unknown, key);\n return;\n }\n\n const config: State.Apply = {\n enumerable: false,\n set: false\n };\n\n let computed = false;\n\n // One-shot factory\n if (typeof value == 'function') {\n const factory = value.bind(subject, key)\n computed = true;\n\n function init() {\n let output: unknown;\n\n try {\n output = attempt(factory);\n } catch (err) {\n console.warn(\n `Generating initial value for ${subject}.${key} failed.`\n );\n throw err;\n }\n\n config.get = argument !== false;\n\n function assign(next: any, silent?: boolean) {\n if (typeof argument == 'function') subject[key] = next;\n else update(subject, key, next, silent, true);\n }\n\n if (output instanceof Promise)\n output.then((next) => {\n if (observer(subject) !== null) assign(next);\n }, (error) => {\n if (observer(subject) === null) return;\n event(subject, key);\n config.get = () => {\n throw error;\n };\n });\n else assign(output, true);\n\n if (argument) return null;\n\n if (output instanceof Promise && argument !== false)\n return access(subject, key, true);\n\n return subject[key];\n }\n\n if (argument) {\n listener(subject, () => {\n init();\n return null;\n });\n } else {\n config.get = init;\n }\n } else if (value !== undefined) {\n config.value = value;\n }\n\n if (typeof argument == 'function') {\n let unset: ((next: T) => void) | undefined;\n\n config.set = function (this: any, value: any, previous: any) {\n capture((release) => {\n const returns = argument.call(this, value, previous);\n\n if (unset) unset(value);\n\n unset = (next: T) => {\n if (typeof returns == 'function') returns(next);\n release();\n };\n });\n };\n } else if (!computed)\n config.set = () => {\n config.get = undefined;\n config.set = undefined;\n };\n\n return config;\n });\n}\n\nfunction attempt(fn: () => any): any {\n function retry(err: unknown) {\n if (err instanceof Promise) return err.then(compute);\n else throw err;\n }\n\n function compute(): any {\n try {\n const output = fn();\n\n return output instanceof Promise ? output.catch(retry) : output;\n } catch (err) {\n return retry(err);\n }\n }\n\n return compute();\n}\n\nexport { set };\n"],"mappings":";;;;;AA8FA,SAAS,IAAa,OAAiB,UAAyB;AAC9D,QAAO,KAAQ,KAAK,YAAY;AAC9B,MAAI,iBAAiB,QACnB,OAAM,IAAI,UACR,4CAA4C,QAAQ,GAAG,IAAI,oCAC5D;AAEH,MAAI,OAAO,SAAS,cAAc,MAAM,SAAS,GAAG;AAClD,WAAQ,KAAK,SAAS,OAAwB,IAAI;AAClD;;EAGF,MAAM,SAAsB;GAC1B,YAAY;GACZ,KAAK;GACN;EAED,IAAI,WAAW;AAGf,MAAI,OAAO,SAAS,YAAY;GAC9B,MAAM,UAAU,MAAM,KAAK,SAAS,IAAI;AACxC,cAAW;GAEX,SAAS,OAAO;IACd,IAAI;AAEJ,QAAI;AACF,cAAS,QAAQ,QAAQ;aAClB,KAAK;AACZ,aAAQ,KACN,gCAAgC,QAAQ,GAAG,IAAI,UAChD;AACD,WAAM;;AAGR,WAAO,MAAM,aAAa;IAE1B,SAAS,OAAO,MAAW,QAAkB;AAC3C,SAAI,OAAO,YAAY,WAAY,SAAQ,OAAO;SAC7C,QAAO,SAAS,KAAK,MAAM,QAAQ,KAAK;;AAG/C,QAAI,kBAAkB,QACpB,QAAO,MAAM,SAAS;AACpB,SAAI,SAAS,QAAQ,KAAK,KAAM,QAAO,KAAK;QAC1C,UAAU;AACZ,SAAI,SAAS,QAAQ,KAAK,KAAM;AAChC,WAAM,SAAS,IAAI;AACnB,YAAO,YAAY;AACjB,YAAM;;MAER;QACC,QAAO,QAAQ,KAAK;AAEzB,QAAI,SAAU,QAAO;AAErB,QAAI,kBAAkB,WAAW,aAAa,MAC5C,QAAO,OAAO,SAAS,KAAK,KAAK;AAEnC,WAAO,QAAQ;;AAGjB,OAAI,SACF,UAAS,eAAe;AACtB,UAAM;AACN,WAAO;KACP;OAEF,QAAO,MAAM;aAEN,UAAU,OACnB,QAAO,QAAQ;AAGjB,MAAI,OAAO,YAAY,YAAY;GACjC,IAAI;AAEJ,UAAO,MAAM,SAAqB,OAAY,UAAe;AAC3D,aAAS,YAAY;KACnB,MAAM,UAAU,SAAS,KAAK,MAAM,OAAO,SAAS;AAEpD,SAAI,MAAO,OAAM,MAAM;AAEvB,cAAS,SAAY;AACnB,UAAI,OAAO,WAAW,WAAY,SAAQ,KAAK;AAC/C,eAAS;;MAEX;;aAEK,CAAC,SACV,QAAO,YAAY;AACjB,UAAO,MAAM;AACb,UAAO,MAAM;;AAGjB,SAAO;GACP;;AAGJ,SAAS,QAAQ,IAAoB;CACnC,SAAS,MAAM,KAAc;AAC3B,MAAI,eAAe,QAAS,QAAO,IAAI,KAAK,QAAQ;MAC/C,OAAM;;CAGb,SAAS,UAAe;AACtB,MAAI;GACF,MAAM,SAAS,IAAI;AAEnB,UAAO,kBAAkB,UAAU,OAAO,MAAM,MAAM,GAAG;WAClD,KAAK;AACZ,UAAO,MAAM,IAAI;;;AAIrB,QAAO,SAAS"}
1
+ {"version":3,"file":"set.js","names":[],"sources":["../../src/field/set.ts"],"sourcesContent":["import { listener, capture, observer } from '../observable';\nimport { access, compute, event, Model, update } from '../state';\nimport { def } from './def';\n\ndeclare namespace set {\n type Callback<T, S = any> = (\n this: S,\n next: T,\n previous: T\n ) => ((next: T) => void) | Promise<any> | void | boolean;\n\n type Factory<T, S = any> = (this: S, property: string) => Promise<T> | T;\n}\n\n/**\n * Set a property as a placeholder, initially `undefined` but required.\n *\n * Property cannot be accessed until it is defined. If accessed while undefined, a hybrid\n * `Promise`/`Error` (aka: [Suspense](https://reactjs.org/docs/concurrent-mode-suspense.html)) will be thrown.\n *\n * Non-enumerable; writable only if `onUpdate` callback is provided.\n *\n * @param value - Starting value (undefined for suspense placeholder).\n * @param onUpdate - Callback run when property is set.\n */\nfunction set<T>(value?: undefined, onUpdate?: set.Callback<T>): T;\n\n/**\n * Set property with an async factory function.\n * If required is not defined, factory runs lazily on first access.\n * If async, or returns a promise, suspense is thrown upon access until resolved.\n *\n * Non-enumerable and read-only.\n *\n * @param factory - Zero-arg async callback to produce property value.\n * @param required - Pass `true` to run factory immediately and require value.\n */\nfunction set<T>(factory: () => T | Promise<T>, required?: true): T;\n\n/**\n * Set property with a lazy factory, no suspense.\n *\n * Non-enumerable and read-only.\n *\n * @param factory - Zero-arg callback to produce property value.\n * @param required - Pass `false` to allow undefined while pending.\n */\nfunction set<T>(\n factory: () => T | Promise<T>,\n required: boolean\n): T | undefined;\n\n/**\n * Set property with a factory and update callback.\n *\n * Non-enumerable but writable via the callback.\n *\n * @param factory - Callback to produce initial value.\n * @param onUpdate - Callback run when property is updated.\n */\nfunction set<T>(factory: () => T | Promise<T>, onUpdate: set.Callback<T>): T;\n\n/**\n * Define a reactive computed property - the instruction equivalent of a getter.\n *\n * Unlike a zero-arg factory (which runs once and caches), the compute function\n * re-runs whenever any managed property it reads is updated. It receives the\n * instance as both `this` and its first argument, so arrow functions work.\n *\n * Enumerable and read-only, matching a prototype getter. Because it assigns\n * through an instruction rather than a concrete getter, subclasses may refine\n * the property's type with `declare`.\n *\n * @param compute - Function of arity >= 1; receives the instance to derive from.\n */\nfunction set<T, S = any>(compute: (this: S, self: S) => T): T;\n\n/**\n * Set a property with a non-function, non-Promise value.\n *\n * Non-enumerable but writable. Unlike plain assignment (`= value`), this\n * property is excluded from snapshots and `ref(this)`.\n *\n * @param value - Starting value for property.\n * @param onUpdate - Optional callback run when property is set.\n */\n// TODO: if onUpdate is not defined, should this have behavior unique to simple assignment?\n// I'm thinking default behavior should be to assign value directly to property, without triggering any additional updates.\n// All this requires is a default callback which throws true, silent updates are already implemented.\nfunction set<T>(\n value: T extends Promise<any> ? never : T,\n onUpdate?: set.Callback<T>\n): T;\n\nfunction set<T = any>(value?: unknown, argument?: unknown): any {\n return def<T>((key, subject) => {\n if (value instanceof Promise)\n throw new TypeError(\n `Direct promises are not supported in set(${subject}.${key}). Use set(() => promise) instead.`\n );\n\n if (typeof value == 'function' && value.length > 0) {\n compute.call(subject, value as () => unknown, key);\n return;\n }\n\n const config: Model.Apply = {\n enumerable: false,\n set: false\n };\n\n let computed = false;\n\n // One-shot factory\n if (typeof value == 'function') {\n const factory = value.bind(subject, key)\n computed = true;\n\n function init() {\n let output: unknown;\n\n try {\n output = attempt(factory);\n } catch (err) {\n console.warn(\n `Generating initial value for ${subject}.${key} failed.`\n );\n throw err;\n }\n\n config.get = argument !== false;\n\n function assign(next: any, silent?: boolean) {\n if (typeof argument == 'function') subject[key] = next;\n else update(subject, key, next, silent, true);\n }\n\n if (output instanceof Promise)\n output.then((next) => {\n if (observer(subject) !== null) assign(next);\n }, (error) => {\n if (observer(subject) === null) return;\n event(subject, key);\n config.get = () => {\n throw error;\n };\n });\n else assign(output, true);\n\n if (argument) return null;\n\n if (output instanceof Promise && argument !== false)\n return access(subject, key, true);\n\n return subject[key];\n }\n\n if (argument) {\n listener(subject, () => {\n init();\n return null;\n });\n } else {\n config.get = init;\n }\n } else if (value !== undefined) {\n config.value = value;\n }\n\n if (typeof argument == 'function') {\n let unset: ((next: T) => void) | undefined;\n\n config.set = function (this: any, value: any, previous: any) {\n capture((release) => {\n const returns = argument.call(this, value, previous);\n\n if (unset) unset(value);\n\n unset = (next: T) => {\n if (typeof returns == 'function') returns(next);\n release();\n };\n });\n };\n } else if (!computed)\n config.set = () => {\n config.get = undefined;\n config.set = undefined;\n };\n\n return config;\n });\n}\n\nfunction attempt(fn: () => any): any {\n function retry(err: unknown) {\n if (err instanceof Promise) return err.then(compute);\n else throw err;\n }\n\n function compute(): any {\n try {\n const output = fn();\n\n return output instanceof Promise ? output.catch(retry) : output;\n } catch (err) {\n return retry(err);\n }\n }\n\n return compute();\n}\n\nexport { set };\n"],"mappings":";;;;;AA8FA,SAAS,IAAa,OAAiB,UAAyB;AAC9D,QAAO,KAAQ,KAAK,YAAY;AAC9B,MAAI,iBAAiB,QACnB,OAAM,IAAI,UACR,4CAA4C,QAAQ,GAAG,IAAI,oCAC5D;AAEH,MAAI,OAAO,SAAS,cAAc,MAAM,SAAS,GAAG;AAClD,WAAQ,KAAK,SAAS,OAAwB,IAAI;AAClD;;EAGF,MAAM,SAAsB;GAC1B,YAAY;GACZ,KAAK;GACN;EAED,IAAI,WAAW;AAGf,MAAI,OAAO,SAAS,YAAY;GAC9B,MAAM,UAAU,MAAM,KAAK,SAAS,IAAI;AACxC,cAAW;GAEX,SAAS,OAAO;IACd,IAAI;AAEJ,QAAI;AACF,cAAS,QAAQ,QAAQ;aAClB,KAAK;AACZ,aAAQ,KACN,gCAAgC,QAAQ,GAAG,IAAI,UAChD;AACD,WAAM;;AAGR,WAAO,MAAM,aAAa;IAE1B,SAAS,OAAO,MAAW,QAAkB;AAC3C,SAAI,OAAO,YAAY,WAAY,SAAQ,OAAO;SAC7C,QAAO,SAAS,KAAK,MAAM,QAAQ,KAAK;;AAG/C,QAAI,kBAAkB,QACpB,QAAO,MAAM,SAAS;AACpB,SAAI,SAAS,QAAQ,KAAK,KAAM,QAAO,KAAK;QAC1C,UAAU;AACZ,SAAI,SAAS,QAAQ,KAAK,KAAM;AAChC,WAAM,SAAS,IAAI;AACnB,YAAO,YAAY;AACjB,YAAM;;MAER;QACC,QAAO,QAAQ,KAAK;AAEzB,QAAI,SAAU,QAAO;AAErB,QAAI,kBAAkB,WAAW,aAAa,MAC5C,QAAO,OAAO,SAAS,KAAK,KAAK;AAEnC,WAAO,QAAQ;;AAGjB,OAAI,SACF,UAAS,eAAe;AACtB,UAAM;AACN,WAAO;KACP;OAEF,QAAO,MAAM;aAEN,UAAU,OACnB,QAAO,QAAQ;AAGjB,MAAI,OAAO,YAAY,YAAY;GACjC,IAAI;AAEJ,UAAO,MAAM,SAAqB,OAAY,UAAe;AAC3D,aAAS,YAAY;KACnB,MAAM,UAAU,SAAS,KAAK,MAAM,OAAO,SAAS;AAEpD,SAAI,MAAO,OAAM,MAAM;AAEvB,cAAS,SAAY;AACnB,UAAI,OAAO,WAAW,WAAY,SAAQ,KAAK;AAC/C,eAAS;;MAEX;;aAEK,CAAC,SACV,QAAO,YAAY;AACjB,UAAO,MAAM;AACb,UAAO,MAAM;;AAGjB,SAAO;GACP;;AAGJ,SAAS,QAAQ,IAAoB;CACnC,SAAS,MAAM,KAAc;AAC3B,MAAI,eAAe,QAAS,QAAO,IAAI,KAAK,QAAQ;MAC/C,OAAM;;CAGb,SAAS,UAAe;AACtB,MAAI;GACF,MAAM,SAAS,IAAI;AAEnB,UAAO,kBAAkB,UAAU,OAAO,MAAM,MAAM,GAAG;WAClD,KAAK;AACZ,UAAO,MAAM,IAAI;;;AAIrB,QAAO,SAAS"}
package/dist/hot.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { State } from "./state.js";
1
+ import { Model } from "./state.js";
2
2
 
3
3
  //#region src/hot.d.ts
4
4
  interface Replaced {
@@ -7,12 +7,12 @@ interface Replaced {
7
7
  /** Local declaration name of the class. */
8
8
  name: string;
9
9
  /** Class live instances belong to. */
10
- prev: State.Extends;
10
+ prev: Model.Extends;
11
11
  /** Class that took its place. */
12
- next: State.Extends;
12
+ next: Model.Extends;
13
13
  }
14
14
  /**
15
- * Keep a module's State classes stable across runs. A class seen before is
15
+ * Keep a module's Model classes stable across runs. A class seen before is
16
16
  * patched with its replacement and returned in its place, refreshing live
17
17
  * instances; one whose shape or parent changed is returned as-is, and
18
18
  * reported to `replaced` listeners before `accept` returns.
package/dist/hot.js CHANGED
@@ -1,6 +1,5 @@
1
1
  import { event } from "./observable.js";
2
- import { GETTERS, LATEST, METHODS, SETUP, State, UNBIND, classify, compute } from "./state.js";
3
- import { rechain } from "./component.js";
2
+ import { GETTERS, LATEST, METHODS, Model, SETUP, UNBIND, classify, compute } from "./state.js";
4
3
 
5
4
  //#region src/hot.ts
6
5
  const define = Object.defineProperty;
@@ -18,7 +17,7 @@ const PRIVATE = /(?:^|[\s;{}*])#[\w$]+(?=[\s=;(}])/;
18
17
  function track() {
19
18
  if (LIVE) return;
20
19
  LIVE = /* @__PURE__ */ new Set();
21
- State.on({
20
+ Model.on({
22
21
  type(type) {
23
22
  const parent = Object.getPrototypeOf(type);
24
23
  let children = SUBCLASSES.get(parent);
@@ -84,7 +83,7 @@ function patch(prev, next, own) {
84
83
  if (keys) {
85
84
  for (let T = prev;; T = Object.getPrototypeOf(T)) {
86
85
  for (const handler of SETUP.get(T) || []) if (handler.type) handler.type(prev);
87
- if (T === State) break;
86
+ if (T === Model) break;
88
87
  }
89
88
  classify(prev, keys, getters);
90
89
  }
@@ -123,8 +122,8 @@ function patch(prev, next, own) {
123
122
  function handlers(type) {
124
123
  return [...SETUP.get(type) || []];
125
124
  }
126
- function isState(value) {
127
- return typeof value == "function" && value.prototype instanceof State;
125
+ function isModel(value) {
126
+ return typeof value == "function" && value.prototype instanceof Model;
128
127
  }
129
128
  /** What a patch cannot carry: class source less its members, and member kinds. */
130
129
  function describe(type) {
@@ -154,7 +153,7 @@ function compatible(prev, next) {
154
153
  return true;
155
154
  }
156
155
  /**
157
- * Keep a module's State classes stable across runs. A class seen before is
156
+ * Keep a module's Model classes stable across runs. A class seen before is
158
157
  * patched with its replacement and returned in its place, refreshing live
159
158
  * instances; one whose shape or parent changed is returned as-is, and
160
159
  * reported to `replaced` listeners before `accept` returns.
@@ -165,10 +164,9 @@ function accept(id, classes) {
165
164
  const output = { ...classes };
166
165
  const refresh = /* @__PURE__ */ new Set();
167
166
  const replace = [];
168
- let patched = false;
169
167
  track();
170
168
  for (const [name, type] of Object.entries(classes)) {
171
- if (!isState(type)) continue;
169
+ if (!isModel(type)) continue;
172
170
  const prev = known[name];
173
171
  if (prev?.type === type) continue;
174
172
  const next = describe(type);
@@ -184,7 +182,6 @@ function accept(id, classes) {
184
182
  }
185
183
  try {
186
184
  for (const state of patch(prev.type, type, prev.own)) refresh.add(state);
187
- patched = true;
188
185
  known[name] = {
189
186
  ...next,
190
187
  type: prev.type
@@ -201,7 +198,6 @@ function accept(id, classes) {
201
198
  known[name] = next;
202
199
  }
203
200
  }
204
- if (patched) rechain();
205
201
  for (const state of refresh) event(state, REFRESH);
206
202
  for (const event of replace) for (const listener of LISTENERS) try {
207
203
  listener(event);
package/dist/hot.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"hot.js","names":[],"sources":["../src/hot.ts"],"sourcesContent":["import { rechain } from './component';\nimport { event } from './observable';\nimport {\n State,\n GETTERS,\n LATEST,\n METHODS,\n SETUP,\n UNBIND,\n classify,\n compute,\n type Handler\n} from './state';\n\ninterface Entry {\n type: Function;\n shape: string;\n kinds: Record<string, 'get' | 'accessor' | 'fn'>;\n own: Handler[];\n}\n\ninterface Replaced {\n /** Module id the class was accepted under. */\n id: string;\n /** Local declaration name of the class. */\n name: string;\n /** Class live instances belong to. */\n prev: State.Extends;\n /** Class that took its place. */\n next: State.Extends;\n}\n\nconst define = Object.defineProperty;\n\n/** Method implementations replaced by a hot patch. */\nconst PAST = new WeakSet<Function>();\n\n/** Live instances, tracked once hot patching is enabled. */\nlet LIVE: Set<WeakRef<State>> | undefined;\n\n/** Bootstrapped subclasses of each class, tracked once hot patching is enabled. */\nconst SUBCLASSES = new WeakMap<Function, Set<State.Extends>>();\n\nconst MODULES = new Map<string, Record<string, Entry>>();\nconst LISTENERS = new Set<(event: Replaced) => void>();\nconst REFRESH = Symbol.for('@expressive/mvc.refresh');\nconst PRIVATE = /(?:^|[\\s;{}*])#[\\w$]+(?=[\\s=;(}])/;\n\n/** Begin tracking live instances, so a hot patch can reach them. */\nfunction track() {\n if (LIVE) return;\n\n LIVE = new Set();\n\n State.on({\n type(type) {\n const parent = Object.getPrototypeOf(type);\n let children = SUBCLASSES.get(parent);\n if (!children) SUBCLASSES.set(parent, (children = new Set()));\n children.add(type);\n },\n setup(state) {\n const ref = new WeakRef(state);\n LIVE!.add(ref);\n return () => LIVE!.delete(ref);\n }\n });\n}\n\n/**\n * Move the members of `next` onto `prev`, which keeps its identity. Returns the\n * live instances of `prev`, each needing a refresh.\n */\nfunction patch(prev: State.Extends, next: State.Extends, own: Handler[]): State[] {\n const proto = prev.prototype;\n const incoming = Object.getOwnPropertyDescriptors(next.prototype);\n const keys = METHODS.get(prev);\n const getters = GETTERS.get(prev);\n const methods = new Map<string, Function>();\n const knownKeys = new Set(keys?.keys());\n const knownGetters = new Set(getters?.keys());\n\n let setup = SETUP.get(prev);\n\n for (const handler of own) setup!.delete(handler);\n for (const handler of handlers(next)) {\n if (!setup) SETUP.set(prev, (setup = new Set()));\n setup.add(handler);\n }\n\n for (const key of Reflect.ownKeys(next)) {\n if (key == 'prototype' || key == 'length' || key == 'name') continue;\n if (Object.getOwnPropertyDescriptor(prev, key)?.configurable !== false)\n define(prev, key, Object.getOwnPropertyDescriptor(next, key)!);\n }\n\n const removed = new Set<string>();\n\n for (const key of Object.getOwnPropertyNames(proto))\n if (key != 'constructor' && !(key in incoming)) {\n const bind = keys?.get(key);\n\n if (bind && Object.getOwnPropertyDescriptor(proto, key)!.get === bind) {\n PAST.add(UNBIND.get(bind));\n keys!.delete(key);\n }\n\n removed.add(key);\n Reflect.deleteProperty(proto, key);\n getters?.delete(key);\n }\n\n for (const [key, desc] of Object.entries(incoming)) {\n if (key == 'constructor') continue;\n\n const current = Object.getOwnPropertyDescriptor(proto, key);\n\n if (typeof desc.value == 'function') methods.set(key, desc.value);\n if (current?.configurable === false) {\n if (current.writable) (proto as any)[key] = desc.value;\n continue;\n }\n\n if (current?.get && keys?.get(key) === current.get)\n PAST.add(UNBIND.get(current.get));\n\n const getter = getters?.get(key);\n\n if (getter) LATEST.set(getter, desc.get!);\n\n define(proto, key, { ...desc, configurable: true });\n }\n\n if (keys) {\n for (let T: State.Extends = prev; ; T = Object.getPrototypeOf(T)) {\n for (const handler of SETUP.get(T) || [])\n if (handler.type) handler.type(prev);\n\n if (T === State) break;\n }\n\n classify(prev, keys, getters!);\n }\n\n const live: State[] = [];\n\n for (const ref of LIVE!) {\n const state = ref.deref();\n\n if (!state) LIVE!.delete(ref);\n else if (state instanceof prev) live.push(state);\n }\n\n const descendants = new Set([prev, ...live.map((state) => state.constructor as State.Extends)]);\n\n for (const type of descendants) for (const child of SUBCLASSES.get(type) || []) descendants.add(child);\n\n descendants.delete(prev);\n\n for (const type of descendants) {\n const inherit = METHODS.get(type)!;\n const computed = GETTERS.get(type)!;\n\n for (const [key, bind] of keys!) if (!knownKeys.has(key) && !inherit.has(key)) inherit.set(key, bind);\n for (const [key, get] of getters!) if (!knownGetters.has(key) && !computed.has(key)) computed.set(key, get);\n for (const key of removed)\n if (!(key in type.prototype)) {\n inherit.delete(key);\n computed.delete(key);\n }\n }\n\n for (const state of live) {\n for (const [key, value] of methods) {\n const desc = Object.getOwnPropertyDescriptor(state, key);\n\n if (!desc) continue;\n if (desc.set) desc.set.call(state, value);\n else if (PAST.has(UNBIND.get(desc.value))) delete (state as any)[key];\n }\n\n for (const key of removed)\n if (PAST.has(UNBIND.get(Object.getOwnPropertyDescriptor(state, key)?.value))) delete (state as any)[key];\n\n for (const [key, get] of GETTERS.get(state.constructor)!)\n if (!knownGetters.has(key) && get === getters!.get(key)) compute.call(state, get, key);\n }\n\n return live;\n}\n\n/** Lifecycle handlers registered on a class itself. */\nfunction handlers(type: State.Extends): Handler[] {\n return [...(SETUP.get(type) || [])];\n}\n\nfunction isState(value: unknown): value is State.Extends {\n return typeof value == 'function' && value.prototype instanceof State;\n}\n\n/** What a patch cannot carry: class source less its members, and member kinds. */\nfunction describe(type: Function): Entry {\n const text = Function.prototype.toString;\n const kinds: Entry['kinds'] = {};\n let shape = text.call(type);\n\n for (const target of [type.prototype, type])\n for (const [key, desc] of Object.entries(Object.getOwnPropertyDescriptors(target)))\n for (const fn of [desc.value, desc.get, desc.set])\n if (typeof fn == 'function' && fn !== type) {\n shape = shape.replace(text.call(fn), '');\n if (target !== type) kinds[key] = desc.set ? 'accessor' : desc.get ? 'get' : 'fn';\n }\n\n shape = shape.replace(/\\s+/g, ' ').replace(/__vite_ssr_import_\\d+__/g, '__vite_ssr_import__');\n\n return { type, shape, kinds, own: handlers(type as State.Extends) };\n}\n\nfunction compatible(prev: Entry, next: Entry) {\n if (prev.shape !== next.shape || PRIVATE.test(next.shape)) return false;\n if (Object.getPrototypeOf(prev.type) !== Object.getPrototypeOf(next.type)) return false;\n\n for (const key in next.kinds)\n if (key in prev.kinds && prev.kinds[key] !== next.kinds[key]) return false;\n\n return true;\n}\n\n/**\n * Keep a module's State classes stable across runs. A class seen before is\n * patched with its replacement and returned in its place, refreshing live\n * instances; one whose shape or parent changed is returned as-is, and\n * reported to `replaced` listeners before `accept` returns.\n */\nfunction accept<T extends Record<string, unknown>>(id: string, classes: T): T {\n let known = MODULES.get(id);\n\n if (!known) MODULES.set(id, (known = {}));\n\n const output: Record<string, unknown> = { ...classes };\n const refresh = new Set<State>();\n const replace: Replaced[] = [];\n let patched = false;\n\n track();\n\n for (const [name, type] of Object.entries(classes)) {\n if (!isState(type)) continue;\n\n const prev = known[name];\n\n if (prev?.type === type) continue;\n\n const next = describe(type);\n\n if (!prev || !compatible(prev, next)) {\n if (prev) replace.push({ id, name, prev: prev.type as State.Extends, next: type });\n known[name] = next;\n continue;\n }\n\n try {\n for (const state of patch(prev.type as State.Extends, type, prev.own)) refresh.add(state);\n patched = true;\n known[name] = { ...next, type: prev.type };\n output[name] = prev.type;\n } catch (error) {\n console.error(error);\n replace.push({ id, name, prev: prev.type as State.Extends, next: type });\n known[name] = next;\n }\n }\n\n if (patched) rechain();\n\n for (const state of refresh) event(state, REFRESH);\n\n for (const event of replace)\n for (const listener of LISTENERS)\n try {\n listener(event);\n } catch (error) {\n console.error(error);\n }\n\n return output as T;\n}\n\n/**\n * Observe classes `accept` replaced instead of patching. Fires synchronously\n * inside `accept`, while the module re-runs, so a host can retire instances of\n * `prev` before anything resolves the module again.\n */\nfunction replaced(listener: (event: Replaced) => void): () => void {\n LISTENERS.add(listener);\n return () => void LISTENERS.delete(listener);\n}\n\nexport { accept, replaced };\nexport type { Replaced };\n"],"mappings":"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"}
1
+ {"version":3,"file":"hot.js","names":[],"sources":["../src/hot.ts"],"sourcesContent":["import { event } from './observable';\nimport {\n Model,\n GETTERS,\n LATEST,\n METHODS,\n SETUP,\n UNBIND,\n classify,\n compute,\n type Handler\n} from './state';\n\ninterface Entry {\n type: Function;\n shape: string;\n kinds: Record<string, 'get' | 'accessor' | 'fn'>;\n own: Handler[];\n}\n\ninterface Replaced {\n /** Module id the class was accepted under. */\n id: string;\n /** Local declaration name of the class. */\n name: string;\n /** Class live instances belong to. */\n prev: Model.Extends;\n /** Class that took its place. */\n next: Model.Extends;\n}\n\nconst define = Object.defineProperty;\n\n/** Method implementations replaced by a hot patch. */\nconst PAST = new WeakSet<Function>();\n\n/** Live instances, tracked once hot patching is enabled. */\nlet LIVE: Set<WeakRef<Model>> | undefined;\n\n/** Bootstrapped subclasses of each class, tracked once hot patching is enabled. */\nconst SUBCLASSES = new WeakMap<Function, Set<Model.Extends>>();\n\nconst MODULES = new Map<string, Record<string, Entry>>();\nconst LISTENERS = new Set<(event: Replaced) => void>();\nconst REFRESH = Symbol.for('@expressive/mvc.refresh');\nconst PRIVATE = /(?:^|[\\s;{}*])#[\\w$]+(?=[\\s=;(}])/;\n\n/** Begin tracking live instances, so a hot patch can reach them. */\nfunction track() {\n if (LIVE) return;\n\n LIVE = new Set();\n\n Model.on({\n type(type) {\n const parent = Object.getPrototypeOf(type);\n let children = SUBCLASSES.get(parent);\n if (!children) SUBCLASSES.set(parent, (children = new Set()));\n children.add(type);\n },\n setup(state) {\n const ref = new WeakRef(state);\n LIVE!.add(ref);\n return () => LIVE!.delete(ref);\n }\n });\n}\n\n/**\n * Move the members of `next` onto `prev`, which keeps its identity. Returns the\n * live instances of `prev`, each needing a refresh.\n */\nfunction patch(prev: Model.Extends, next: Model.Extends, own: Handler[]): Model[] {\n const proto = prev.prototype;\n const incoming = Object.getOwnPropertyDescriptors(next.prototype);\n const keys = METHODS.get(prev);\n const getters = GETTERS.get(prev);\n const methods = new Map<string, Function>();\n const knownKeys = new Set(keys?.keys());\n const knownGetters = new Set(getters?.keys());\n\n let setup = SETUP.get(prev);\n\n for (const handler of own) setup!.delete(handler);\n for (const handler of handlers(next)) {\n if (!setup) SETUP.set(prev, (setup = new Set()));\n setup.add(handler);\n }\n\n for (const key of Reflect.ownKeys(next)) {\n if (key == 'prototype' || key == 'length' || key == 'name') continue;\n if (Object.getOwnPropertyDescriptor(prev, key)?.configurable !== false)\n define(prev, key, Object.getOwnPropertyDescriptor(next, key)!);\n }\n\n const removed = new Set<string>();\n\n for (const key of Object.getOwnPropertyNames(proto))\n if (key != 'constructor' && !(key in incoming)) {\n const bind = keys?.get(key);\n\n if (bind && Object.getOwnPropertyDescriptor(proto, key)!.get === bind) {\n PAST.add(UNBIND.get(bind));\n keys!.delete(key);\n }\n\n removed.add(key);\n Reflect.deleteProperty(proto, key);\n getters?.delete(key);\n }\n\n for (const [key, desc] of Object.entries(incoming)) {\n if (key == 'constructor') continue;\n\n const current = Object.getOwnPropertyDescriptor(proto, key);\n\n if (typeof desc.value == 'function') methods.set(key, desc.value);\n if (current?.configurable === false) {\n if (current.writable) (proto as any)[key] = desc.value;\n continue;\n }\n\n if (current?.get && keys?.get(key) === current.get)\n PAST.add(UNBIND.get(current.get));\n\n const getter = getters?.get(key);\n\n if (getter) LATEST.set(getter, desc.get!);\n\n define(proto, key, { ...desc, configurable: true });\n }\n\n if (keys) {\n for (let T: Model.Extends = prev; ; T = Object.getPrototypeOf(T)) {\n for (const handler of SETUP.get(T) || [])\n if (handler.type) handler.type(prev);\n\n if (T === Model) break;\n }\n\n classify(prev, keys, getters!);\n }\n\n const live: Model[] = [];\n\n for (const ref of LIVE!) {\n const state = ref.deref();\n\n if (!state) LIVE!.delete(ref);\n else if (state instanceof prev) live.push(state);\n }\n\n const descendants = new Set([prev, ...live.map((state) => state.constructor as Model.Extends)]);\n\n for (const type of descendants) for (const child of SUBCLASSES.get(type) || []) descendants.add(child);\n\n descendants.delete(prev);\n\n for (const type of descendants) {\n const inherit = METHODS.get(type)!;\n const computed = GETTERS.get(type)!;\n\n for (const [key, bind] of keys!) if (!knownKeys.has(key) && !inherit.has(key)) inherit.set(key, bind);\n for (const [key, get] of getters!) if (!knownGetters.has(key) && !computed.has(key)) computed.set(key, get);\n for (const key of removed)\n if (!(key in type.prototype)) {\n inherit.delete(key);\n computed.delete(key);\n }\n }\n\n for (const state of live) {\n for (const [key, value] of methods) {\n const desc = Object.getOwnPropertyDescriptor(state, key);\n\n if (!desc) continue;\n if (desc.set) desc.set.call(state, value);\n else if (PAST.has(UNBIND.get(desc.value))) delete (state as any)[key];\n }\n\n for (const key of removed)\n if (PAST.has(UNBIND.get(Object.getOwnPropertyDescriptor(state, key)?.value))) delete (state as any)[key];\n\n for (const [key, get] of GETTERS.get(state.constructor)!)\n if (!knownGetters.has(key) && get === getters!.get(key)) compute.call(state, get, key);\n }\n\n return live;\n}\n\n/** Lifecycle handlers registered on a class itself. */\nfunction handlers(type: Model.Extends): Handler[] {\n return [...(SETUP.get(type) || [])];\n}\n\nfunction isModel(value: unknown): value is Model.Extends {\n return typeof value == 'function' && value.prototype instanceof Model;\n}\n\n/** What a patch cannot carry: class source less its members, and member kinds. */\nfunction describe(type: Function): Entry {\n const text = Function.prototype.toString;\n const kinds: Entry['kinds'] = {};\n let shape = text.call(type);\n\n for (const target of [type.prototype, type])\n for (const [key, desc] of Object.entries(Object.getOwnPropertyDescriptors(target)))\n for (const fn of [desc.value, desc.get, desc.set])\n if (typeof fn == 'function' && fn !== type) {\n shape = shape.replace(text.call(fn), '');\n if (target !== type) kinds[key] = desc.set ? 'accessor' : desc.get ? 'get' : 'fn';\n }\n\n shape = shape.replace(/\\s+/g, ' ').replace(/__vite_ssr_import_\\d+__/g, '__vite_ssr_import__');\n\n return { type, shape, kinds, own: handlers(type as Model.Extends) };\n}\n\nfunction compatible(prev: Entry, next: Entry) {\n if (prev.shape !== next.shape || PRIVATE.test(next.shape)) return false;\n if (Object.getPrototypeOf(prev.type) !== Object.getPrototypeOf(next.type)) return false;\n\n for (const key in next.kinds)\n if (key in prev.kinds && prev.kinds[key] !== next.kinds[key]) return false;\n\n return true;\n}\n\n/**\n * Keep a module's Model classes stable across runs. A class seen before is\n * patched with its replacement and returned in its place, refreshing live\n * instances; one whose shape or parent changed is returned as-is, and\n * reported to `replaced` listeners before `accept` returns.\n */\nfunction accept<T extends Record<string, unknown>>(id: string, classes: T): T {\n let known = MODULES.get(id);\n\n if (!known) MODULES.set(id, (known = {}));\n\n const output: Record<string, unknown> = { ...classes };\n const refresh = new Set<Model>();\n const replace: Replaced[] = [];\n\n track();\n\n for (const [name, type] of Object.entries(classes)) {\n if (!isModel(type)) continue;\n\n const prev = known[name];\n\n if (prev?.type === type) continue;\n\n const next = describe(type);\n\n if (!prev || !compatible(prev, next)) {\n if (prev) replace.push({ id, name, prev: prev.type as Model.Extends, next: type });\n known[name] = next;\n continue;\n }\n\n try {\n for (const state of patch(prev.type as Model.Extends, type, prev.own)) refresh.add(state);\n known[name] = { ...next, type: prev.type };\n output[name] = prev.type;\n } catch (error) {\n console.error(error);\n replace.push({ id, name, prev: prev.type as Model.Extends, next: type });\n known[name] = next;\n }\n }\n\n for (const state of refresh) event(state, REFRESH);\n\n for (const event of replace)\n for (const listener of LISTENERS)\n try {\n listener(event);\n } catch (error) {\n console.error(error);\n }\n\n return output as T;\n}\n\n/**\n * Observe classes `accept` replaced instead of patching. Fires synchronously\n * inside `accept`, while the module re-runs, so a host can retire instances of\n * `prev` before anything resolves the module again.\n */\nfunction replaced(listener: (event: Replaced) => void): () => void {\n LISTENERS.add(listener);\n return () => void LISTENERS.delete(listener);\n}\n\nexport { accept, replaced };\nexport type { Replaced };\n"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Context } from "./context.js";
2
- import { State, unbind } from "./state.js";
2
+ import { Model, unbind } from "./state.js";
3
3
  import { Component } from "./component.js";
4
4
  import { pending } from "./dispatch.js";
5
5
  import { def } from "./field/def.js";
@@ -8,4 +8,4 @@ import { has } from "./field/has.js";
8
8
  import { map } from "./field/map.js";
9
9
  import { ref } from "./field/ref.js";
10
10
  import { set } from "./field/set.js";
11
- export { Component, Context, State, def, State as default, get, has, map, pending, ref, set, unbind };
11
+ export { Component, Context, Model, Model as State, def, Model as default, get, has, map, pending, ref, set, unbind };
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { pending } from "./dispatch.js";
2
- import { State, unbind } from "./state.js";
2
+ import { Model, unbind } from "./state.js";
3
3
  import { Context } from "./context.js";
4
4
  import { def } from "./field/def.js";
5
5
  import { set } from "./field/set.js";
@@ -10,9 +10,9 @@ import { map } from "./field/map.js";
10
10
  import { ref } from "./field/ref.js";
11
11
 
12
12
  //#region src/index.ts
13
- /** @deprecated Import `{ State }` as a named export. Adapter-augmented `State.*` types are not visible through the default alias. */
14
- var src_default = State;
13
+ /** @deprecated Import `{ Model }` as a named export. Adapter-augmented `Model.*` types are not visible through the default alias. */
14
+ var src_default = Model;
15
15
 
16
16
  //#endregion
17
- export { Component, Context, State, def, src_default as default, get, has, map, pending, ref, set, unbind };
17
+ export { Component, Context, Model, Model as State, def, src_default as default, get, has, map, pending, ref, set, unbind };
18
18
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["export { def } from './field/def';\nexport { get } from './field/get';\nexport { has } from './field/has';\nexport { map } from './field/map';\nexport { set } from './field/set';\nexport { ref } from './field/ref';\n\nimport { State } from './state';\n\nexport { State, unbind } from './state';\n\n/** @deprecated Import `{ State }` as a named export. Adapter-augmented `State.*` types are not visible through the default alias. */\nexport default State;\nexport { Context } from './context';\nexport { Component } from './component';\nexport { pending } from './dispatch';\n"],"mappings":";;;;;;;;;;;;;AAYA,kBAAe"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["export { def } from './field/def';\nexport { get } from './field/get';\nexport { has } from './field/has';\nexport { map } from './field/map';\nexport { set } from './field/set';\nexport { ref } from './field/ref';\n\nimport { Model } from './state';\n\nexport { Model, unbind } from './state';\n\n/** @deprecated Renamed to `Model`. */\nexport { Model as State } from './state';\n\n/** @deprecated Import `{ Model }` as a named export. Adapter-augmented `Model.*` types are not visible through the default alias. */\nexport default Model;\nexport { Context } from './context';\nexport { Component } from './component';\nexport { pending } from './dispatch';\n"],"mappings":";;;;;;;;;;;;;AAeA,kBAAe"}
@@ -1,4 +1,4 @@
1
- import { Component, compose } from "./component.js";
1
+ import { Component } from "./component.js";
2
2
 
3
3
  //#region src/jsx-runtime.d.ts
4
4
  /**
@@ -105,5 +105,5 @@ declare namespace JSX {
105
105
  } ? T : {};
106
106
  }
107
107
  //#endregion
108
- export { Fragment, Host, HostRuntime, JSX, childrenOf, compose, host, isElement, jsx, jsxDEV, jsxs, propsOf, typeOf };
108
+ export { Fragment, Host, HostRuntime, JSX, childrenOf, host, isElement, jsx, jsxDEV, jsxs, propsOf, typeOf };
109
109
  //# sourceMappingURL=jsx-runtime.d.ts.map
@@ -1,5 +1,3 @@
1
- import { compose } from "./component.js";
2
-
3
1
  //#region src/jsx-runtime.ts
4
2
  /**
5
3
  * Agnostic Fragment sentinel. The runtime entries translate it to the host's
@@ -66,5 +64,5 @@ function propsOf(node) {
66
64
  }
67
65
 
68
66
  //#endregion
69
- export { Fragment, childrenOf, compose, host, isElement, jsx, jsxDEV, jsxs, propsOf, typeOf };
67
+ export { Fragment, childrenOf, host, isElement, jsx, jsxDEV, jsxs, propsOf, typeOf };
70
68
  //# sourceMappingURL=jsx-runtime.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"jsx-runtime.js","names":[],"sources":["../src/jsx-runtime.ts"],"sourcesContent":["import type { Component } from './component';\n\nexport { compose } from './component';\n\n/**\n * Per-adapter interpretation manifest. Each adapter augments this interface to\n * declare how it renders; the first member is `node` - the element type produced\n * by `Component.render` (surfaced as `Component.Node`) - alongside `intrinsics`,\n * the host's tag map (surfaced as `JSX.IntrinsicElements`), `elementClass`,\n * the instance a class must construct to render as an element, and optionally\n * `element`, further element types the host renders (joined into\n * `JSX.ElementType`).\n *\n * ```ts\n * declare module '@expressive/mvc/jsx-runtime' {\n * interface Host {\n * node: React.ReactNode;\n * intrinsics: React.JSX.IntrinsicElements;\n * elementClass: React.Component<any, any>;\n * }\n * }\n * ```\n *\n * Only one adapter is expected per compilation; two augmenting the same member\n * with different types in the same build would conflict - by design.\n */\nexport interface Host { }\n\n/**\n * Host runtime, registered by an adapter (e.g. `@expressive/react`) as an\n * import side effect. One host per build - a second registration with a\n * different runtime throws.\n *\n * Members are ambient host capabilities: plain functions the host uniquely\n * owns, callable outside render - element mechanics always, plus optional\n * extras that fall back sanely when absent. Hook-shaped (render-resident)\n * capabilities do not belong here; those live with the adapter.\n */\nexport interface HostRuntime {\n childrenOf(children: unknown): Component.Node[];\n isElement(node: unknown): boolean;\n jsx(type: unknown, props: object, key?: unknown): Component.Node;\n jsxDEV?(type: unknown, props: object, key?: unknown, isStatic?: boolean, source?: object, self?: unknown): Component.Node;\n jsxs(type: unknown, props: object, key?: unknown): Component.Node;\n propsOf(node: unknown): Record<string, unknown>;\n typeOf(node: unknown): unknown;\n Fragment: unknown;\n}\n\n/**\n * Agnostic Fragment sentinel. The runtime entries translate it to the host's\n * Fragment on element creation; `typeOf` translates back, so identity checks\n * against this value hold for fragments from either pragma.\n */\nexport const Fragment = Symbol.for('@expressive/mvc.Fragment');\n\n/** Default for seams only a registered host can provide. */\nfunction absent(): never {\n throw new Error(\n 'No JSX host is registered for @expressive/mvc. ' +\n \"Import a host adapter (e.g. '@expressive/react') before anything renders - \" +\n 'typically as the first import of your application entry module.'\n );\n}\n\nconst HOST: HostRuntime = {\n childrenOf: absent,\n isElement: absent,\n jsx: absent,\n jsxs: absent,\n propsOf: absent,\n typeOf: absent,\n Fragment\n};\n\nlet registered: HostRuntime | undefined;\n\n/**\n * Register the host runtime. Idempotent for the same runtime - re-registering\n * picks up members added since (e.g. an optional dev seam). Element mechanics\n * the host leaves unset keep defaults that throw a setup-pointing error;\n * `jsxDEV` is routed around at the call site instead.\n */\nexport function host(runtime: HostRuntime) {\n if (registered && registered !== runtime)\n throw new Error(\n 'A different JSX host is already registered for @expressive/mvc. ' +\n 'Only one host adapter may be active per build.'\n );\n\n registered = runtime;\n Object.assign(HOST, runtime);\n}\n\n/** Translate the agnostic Fragment sentinel to the host's Fragment. */\nfunction hostType(type: unknown): unknown {\n return type === Fragment ? HOST.Fragment : type;\n}\n\nexport function jsx(type: unknown, props: object, key?: unknown): Component.Node {\n return HOST.jsx(hostType(type), props, key);\n}\n\nexport function jsxs(type: unknown, props: object, key?: unknown): Component.Node {\n return HOST.jsxs(hostType(type), props, key);\n}\n\n/** Dev-transpiled element creation. Hosts without a `jsxDEV` of their own fall\n * back to the production entries (prod-only host under a dev build). */\nexport function jsxDEV(\n type: unknown,\n props: object,\n key?: unknown,\n isStatic?: boolean,\n source?: object,\n self?: unknown\n): Component.Node {\n return HOST.jsxDEV\n ? HOST.jsxDEV(hostType(type), props, key, isStatic, source, self)\n : isStatic\n ? jsxs(type, props, key)\n : jsx(type, props, key);\n}\n\n/** Flatten `children` to an array of nodes, per the host's semantics. */\nexport function childrenOf(children: unknown): Component.Node[] {\n return HOST.childrenOf(children);\n}\n\n/** Is `node` a host element? */\nexport function isElement(node: unknown): boolean {\n return HOST.isElement(node);\n}\n\n/** Element type of `node`; host Fragments surface as the agnostic `Fragment`. */\nexport function typeOf(node: unknown): unknown {\n const type = HOST.typeOf(node);\n return type === HOST.Fragment ? Fragment : type;\n}\n\n/** Props carried by element `node`. */\nexport function propsOf(node: unknown): Record<string, unknown> {\n return HOST.propsOf(node);\n}\n\n\nexport declare namespace JSX {\n type Element = Component.Node;\n type ElementType =\n | keyof IntrinsicElements\n | ((props: any) => Component.Node)\n | (new (...args: any[]) => Host extends { elementClass: infer I } ? I : ElementClass)\n | (Host extends { element: infer T } ? T : never);\n /**\n * Class instance contract when no host declares `elementClass`. Gates on the\n * `render` contract alone - requiring full\n * `Component` assignability trips contravariance on `props` members (e.g.\n * the `is` callback narrows per subclass), rejecting every subclass.\n */\n interface ElementClass { render(props?: any): Component.Node }\n interface ElementAttributesProperty { props: {} }\n interface ElementChildrenAttribute { children: {} }\n interface IntrinsicAttributes { key?: string | number | null }\n /**\n * Host tag manifest, via the {@link Host} seam. Resolves to `{}` until an\n * adapter augments `Host` with `intrinsics` - agnostic code is\n * components-only by design, since it cannot know what tags a host renders.\n */\n type IntrinsicElements =\n Host extends { intrinsics: infer T extends object } ? T : {};\n}\n"],"mappings":";;;;;;;;AAsDA,MAAa,WAAW,OAAO,IAAI,2BAA2B;;AAG9D,SAAS,SAAgB;AACvB,OAAM,IAAI,MACR,4LAGD;;AAGH,MAAM,OAAoB;CACxB,YAAY;CACZ,WAAW;CACX,KAAK;CACL,MAAM;CACN,SAAS;CACT,QAAQ;CACR;CACD;AAED,IAAI;;;;;;;AAQJ,SAAgB,KAAK,SAAsB;AACzC,KAAI,cAAc,eAAe,QAC/B,OAAM,IAAI,MACR,iHAED;AAEH,cAAa;AACb,QAAO,OAAO,MAAM,QAAQ;;;AAI9B,SAAS,SAAS,MAAwB;AACxC,QAAO,SAAS,WAAW,KAAK,WAAW;;AAG7C,SAAgB,IAAI,MAAe,OAAe,KAA+B;AAC/E,QAAO,KAAK,IAAI,SAAS,KAAK,EAAE,OAAO,IAAI;;AAG7C,SAAgB,KAAK,MAAe,OAAe,KAA+B;AAChF,QAAO,KAAK,KAAK,SAAS,KAAK,EAAE,OAAO,IAAI;;;;AAK9C,SAAgB,OACd,MACA,OACA,KACA,UACA,QACA,MACgB;AAChB,QAAO,KAAK,SACR,KAAK,OAAO,SAAS,KAAK,EAAE,OAAO,KAAK,UAAU,QAAQ,KAAK,GAC/D,WACE,KAAK,MAAM,OAAO,IAAI,GACtB,IAAI,MAAM,OAAO,IAAI;;;AAI7B,SAAgB,WAAW,UAAqC;AAC9D,QAAO,KAAK,WAAW,SAAS;;;AAIlC,SAAgB,UAAU,MAAwB;AAChD,QAAO,KAAK,UAAU,KAAK;;;AAI7B,SAAgB,OAAO,MAAwB;CAC7C,MAAM,OAAO,KAAK,OAAO,KAAK;AAC9B,QAAO,SAAS,KAAK,WAAW,WAAW;;;AAI7C,SAAgB,QAAQ,MAAwC;AAC9D,QAAO,KAAK,QAAQ,KAAK"}
1
+ {"version":3,"file":"jsx-runtime.js","names":[],"sources":["../src/jsx-runtime.ts"],"sourcesContent":["import type { Component } from './component';\n\n/**\n * Per-adapter interpretation manifest. Each adapter augments this interface to\n * declare how it renders; the first member is `node` - the element type produced\n * by `Component.render` (surfaced as `Component.Node`) - alongside `intrinsics`,\n * the host's tag map (surfaced as `JSX.IntrinsicElements`), `elementClass`,\n * the instance a class must construct to render as an element, and optionally\n * `element`, further element types the host renders (joined into\n * `JSX.ElementType`).\n *\n * ```ts\n * declare module '@expressive/mvc/jsx-runtime' {\n * interface Host {\n * node: React.ReactNode;\n * intrinsics: React.JSX.IntrinsicElements;\n * elementClass: React.Component<any, any>;\n * }\n * }\n * ```\n *\n * Only one adapter is expected per compilation; two augmenting the same member\n * with different types in the same build would conflict - by design.\n */\nexport interface Host { }\n\n/**\n * Host runtime, registered by an adapter (e.g. `@expressive/react`) as an\n * import side effect. One host per build - a second registration with a\n * different runtime throws.\n *\n * Members are ambient host capabilities: plain functions the host uniquely\n * owns, callable outside render - element mechanics always, plus optional\n * extras that fall back sanely when absent. Hook-shaped (render-resident)\n * capabilities do not belong here; those live with the adapter.\n */\nexport interface HostRuntime {\n childrenOf(children: unknown): Component.Node[];\n isElement(node: unknown): boolean;\n jsx(type: unknown, props: object, key?: unknown): Component.Node;\n jsxDEV?(type: unknown, props: object, key?: unknown, isStatic?: boolean, source?: object, self?: unknown): Component.Node;\n jsxs(type: unknown, props: object, key?: unknown): Component.Node;\n propsOf(node: unknown): Record<string, unknown>;\n typeOf(node: unknown): unknown;\n Fragment: unknown;\n}\n\n/**\n * Agnostic Fragment sentinel. The runtime entries translate it to the host's\n * Fragment on element creation; `typeOf` translates back, so identity checks\n * against this value hold for fragments from either pragma.\n */\nexport const Fragment = Symbol.for('@expressive/mvc.Fragment');\n\n/** Default for seams only a registered host can provide. */\nfunction absent(): never {\n throw new Error(\n 'No JSX host is registered for @expressive/mvc. ' +\n \"Import a host adapter (e.g. '@expressive/react') before anything renders - \" +\n 'typically as the first import of your application entry module.'\n );\n}\n\nconst HOST: HostRuntime = {\n childrenOf: absent,\n isElement: absent,\n jsx: absent,\n jsxs: absent,\n propsOf: absent,\n typeOf: absent,\n Fragment\n};\n\nlet registered: HostRuntime | undefined;\n\n/**\n * Register the host runtime. Idempotent for the same runtime - re-registering\n * picks up members added since (e.g. an optional dev seam). Element mechanics\n * the host leaves unset keep defaults that throw a setup-pointing error;\n * `jsxDEV` is routed around at the call site instead.\n */\nexport function host(runtime: HostRuntime) {\n if (registered && registered !== runtime)\n throw new Error(\n 'A different JSX host is already registered for @expressive/mvc. ' +\n 'Only one host adapter may be active per build.'\n );\n\n registered = runtime;\n Object.assign(HOST, runtime);\n}\n\n/** Translate the agnostic Fragment sentinel to the host's Fragment. */\nfunction hostType(type: unknown): unknown {\n return type === Fragment ? HOST.Fragment : type;\n}\n\nexport function jsx(type: unknown, props: object, key?: unknown): Component.Node {\n return HOST.jsx(hostType(type), props, key);\n}\n\nexport function jsxs(type: unknown, props: object, key?: unknown): Component.Node {\n return HOST.jsxs(hostType(type), props, key);\n}\n\n/** Dev-transpiled element creation. Hosts without a `jsxDEV` of their own fall\n * back to the production entries (prod-only host under a dev build). */\nexport function jsxDEV(\n type: unknown,\n props: object,\n key?: unknown,\n isStatic?: boolean,\n source?: object,\n self?: unknown\n): Component.Node {\n return HOST.jsxDEV\n ? HOST.jsxDEV(hostType(type), props, key, isStatic, source, self)\n : isStatic\n ? jsxs(type, props, key)\n : jsx(type, props, key);\n}\n\n/** Flatten `children` to an array of nodes, per the host's semantics. */\nexport function childrenOf(children: unknown): Component.Node[] {\n return HOST.childrenOf(children);\n}\n\n/** Is `node` a host element? */\nexport function isElement(node: unknown): boolean {\n return HOST.isElement(node);\n}\n\n/** Element type of `node`; host Fragments surface as the agnostic `Fragment`. */\nexport function typeOf(node: unknown): unknown {\n const type = HOST.typeOf(node);\n return type === HOST.Fragment ? Fragment : type;\n}\n\n/** Props carried by element `node`. */\nexport function propsOf(node: unknown): Record<string, unknown> {\n return HOST.propsOf(node);\n}\n\n\nexport declare namespace JSX {\n type Element = Component.Node;\n type ElementType =\n | keyof IntrinsicElements\n | ((props: any) => Component.Node)\n | (new (...args: any[]) => Host extends { elementClass: infer I } ? I : ElementClass)\n | (Host extends { element: infer T } ? T : never);\n /**\n * Class instance contract when no host declares `elementClass`. Gates on the\n * `render` contract alone - requiring full\n * `Component` assignability trips contravariance on `props` members (e.g.\n * the `is` callback narrows per subclass), rejecting every subclass.\n */\n interface ElementClass { render(props?: any): Component.Node }\n interface ElementAttributesProperty { props: {} }\n interface ElementChildrenAttribute { children: {} }\n interface IntrinsicAttributes { key?: string | number | null }\n /**\n * Host tag manifest, via the {@link Host} seam. Resolves to `{}` until an\n * adapter augments `Host` with `intrinsics` - agnostic code is\n * components-only by design, since it cannot know what tags a host renders.\n */\n type IntrinsicElements =\n Host extends { intrinsics: infer T extends object } ? T : {};\n}\n"],"mappings":";;;;;;AAoDA,MAAa,WAAW,OAAO,IAAI,2BAA2B;;AAG9D,SAAS,SAAgB;AACvB,OAAM,IAAI,MACR,4LAGD;;AAGH,MAAM,OAAoB;CACxB,YAAY;CACZ,WAAW;CACX,KAAK;CACL,MAAM;CACN,SAAS;CACT,QAAQ;CACR;CACD;AAED,IAAI;;;;;;;AAQJ,SAAgB,KAAK,SAAsB;AACzC,KAAI,cAAc,eAAe,QAC/B,OAAM,IAAI,MACR,iHAED;AAEH,cAAa;AACb,QAAO,OAAO,MAAM,QAAQ;;;AAI9B,SAAS,SAAS,MAAwB;AACxC,QAAO,SAAS,WAAW,KAAK,WAAW;;AAG7C,SAAgB,IAAI,MAAe,OAAe,KAA+B;AAC/E,QAAO,KAAK,IAAI,SAAS,KAAK,EAAE,OAAO,IAAI;;AAG7C,SAAgB,KAAK,MAAe,OAAe,KAA+B;AAChF,QAAO,KAAK,KAAK,SAAS,KAAK,EAAE,OAAO,IAAI;;;;AAK9C,SAAgB,OACd,MACA,OACA,KACA,UACA,QACA,MACgB;AAChB,QAAO,KAAK,SACR,KAAK,OAAO,SAAS,KAAK,EAAE,OAAO,KAAK,UAAU,QAAQ,KAAK,GAC/D,WACE,KAAK,MAAM,OAAO,IAAI,GACtB,IAAI,MAAM,OAAO,IAAI;;;AAI7B,SAAgB,WAAW,UAAqC;AAC9D,QAAO,KAAK,WAAW,SAAS;;;AAIlC,SAAgB,UAAU,MAAwB;AAChD,QAAO,KAAK,UAAU,KAAK;;;AAI7B,SAAgB,OAAO,MAAwB;CAC7C,MAAM,OAAO,KAAK,OAAO,KAAK;AAC9B,QAAO,SAAS,KAAK,WAAW,WAAW;;;AAI7C,SAAgB,QAAQ,MAAwC;AAC9D,QAAO,KAAK,QAAQ,KAAK"}
@@ -11,7 +11,10 @@ declare namespace Observer {
11
11
  */
12
12
  type Notify = (key: Signal) => (() => void) | null | void;
13
13
  type EffectCleanup = (update: boolean | null) => void;
14
- type Effect<T extends object> = (this: T, proxy: T, changed: readonly Event[]) => EffectCleanup | Promise<void> | null | void;
14
+ type EffectOutput = EffectCleanup | null | void;
15
+ type Effect<T extends object, R = EffectOutput | Promise<void>> = (this: T, proxy: T, changed: readonly Event[]) => R;
16
+ /** Stops the effect when called; awaited, cancels it and yields its value. */
17
+ type Release<R> = (() => void) & PromiseLike<R>;
15
18
  type Event = number | string | symbol;
16
19
  type Signal = Event | true | false | null;
17
20
  type Callback = () => void | null;
@@ -46,12 +49,12 @@ declare function event(state: object, key?: Observer.Event | null, silent?: bool
46
49
  * Create a side-effect which will update whenever values accessed change.
47
50
  * Callback is called immediately and whenever values are stale.
48
51
  *
49
- * @param target - Instance of State to observe.
52
+ * @param target - Instance of Model to observe.
50
53
  * @param callback - Function to invoke when values change.
51
54
  * @param requireValues - If `true` will throw if accessing a value which is `undefined`.
52
55
  */
53
- declare function watch<T extends object>(target: T, callback: Observer.Effect<Required<T>>, requireValues: true, transition?: (work: () => void) => void): () => void;
54
- declare function watch<T extends object>(target: T, callback: Observer.Effect<T>, recursive?: boolean, transition?: (work: () => void) => void): () => void;
56
+ declare function watch<T extends object, R = void>(target: T, callback: Observer.Effect<Required<T>, Observer.EffectOutput | Promise<R>>, requireValues: true, transition?: (work: () => void) => void): Observer.Release<R>;
57
+ declare function watch<T extends object, R = void>(target: T, callback: Observer.Effect<T, Observer.EffectOutput | Promise<R>>, recursive?: boolean, transition?: (work: () => void) => void): Observer.Release<R>;
55
58
  declare function capture(scope: (release: (update?: boolean | null) => void) => void): void;
56
59
  //#endregion
57
60
  export { Observer, capture, event, listener, observer, queued, touch, watch };
@@ -1,4 +1,4 @@
1
- import { enqueue, hold } from "./dispatch.js";
1
+ import { REPORT, enqueue, hold } from "./dispatch.js";
2
2
 
3
3
  //#region src/observable.ts
4
4
  const Observer = Symbol("Observer");
@@ -119,18 +119,61 @@ function watch(target, callback, argument, transition) {
119
119
  let previous;
120
120
  let latest;
121
121
  let queued = false;
122
+ let pending = false;
122
123
  let suspense;
123
- function resume() {
124
+ let outcome;
125
+ let awaiting;
126
+ let settle;
127
+ function conclude(ok, value) {
128
+ outcome = {
129
+ ok,
130
+ value
131
+ };
132
+ if (!ok || !queued) finish();
133
+ }
134
+ function finish() {
135
+ const done = awaiting && settle;
136
+ if (!done) return;
137
+ settle = void 0;
138
+ done();
139
+ cleanup();
140
+ }
141
+ function fail(reason) {
142
+ if (!outcome) outcome = {
143
+ ok: false,
144
+ value: /* @__PURE__ */ new Error(`${target} ${reason}`)
145
+ };
146
+ finish();
147
+ }
148
+ function stop(key) {
149
+ if (unset) unset(key);
150
+ unset = void 0;
124
151
  suspense?.();
125
152
  suspense = void 0;
153
+ reset = null;
154
+ pending = queued = false;
155
+ }
156
+ function suspend(on) {
157
+ reset = void 0;
158
+ const suspended = suspense = hold(true, transition);
159
+ const replay = () => {
160
+ if (suspense !== suspended) return;
161
+ suspense = void 0;
162
+ suspended(invoke);
163
+ };
164
+ on.then(replay, replay);
126
165
  }
127
166
  function invoke() {
128
167
  if (reset === null || observer(target) === null) return;
129
168
  queued = false;
130
169
  let ignore = true;
131
170
  function onUpdate() {
132
- events = [...o.events];
171
+ events = queued ? [...new Set([...events, ...o.events])] : [...o.events];
133
172
  if (reset === null) return null;
173
+ if (pending) {
174
+ queued = true;
175
+ return;
176
+ }
134
177
  if (ignore) {
135
178
  if (queued) enqueue(invoke, transition, target);
136
179
  return;
@@ -141,62 +184,86 @@ function watch(target, callback, argument, transition) {
141
184
  unset = void 0;
142
185
  enqueue(invoke, transition, target);
143
186
  }
187
+ function apply(output, release) {
188
+ ignore = false;
189
+ reset = output === null ? null : invoke;
190
+ unset = (key) => {
191
+ if (typeof output == "function") output(key);
192
+ if (release) release(key);
193
+ };
194
+ }
144
195
  function run(release) {
145
196
  if (previous !== latest) unobserve(latest);
146
197
  const proxy = observe(target, onUpdate, argument === true);
147
198
  const current = latest = proxy[Observing];
148
199
  const output = callback.call(proxy, proxy, events);
149
200
  if (observer(target) === null) {
150
- if (typeof output == "function") output(null);
151
201
  if (release) release(null);
152
- return;
202
+ if (output instanceof Promise) output.catch(() => {});
203
+ else if (typeof output == "function") output(null);
204
+ return fail("is destroyed.");
153
205
  }
154
206
  if (previous && argument === false && !current.watching.size) previous.callback = onUpdate;
155
207
  else {
156
208
  if (previous) unobserve(previous);
157
209
  previous = current;
158
210
  }
159
- ignore = false;
160
- reset = output === null ? null : invoke;
161
- unset = (key) => {
162
- if (typeof output == "function") output(key);
163
- if (release) release(key);
164
- };
165
211
  events = [];
212
+ if (!(output instanceof Promise)) {
213
+ apply(output, release);
214
+ return conclude(true, void 0);
215
+ }
216
+ pending = true;
217
+ reset = invoke;
218
+ unset = release;
219
+ output.then((value) => {
220
+ pending = false;
221
+ if (reset === null) return;
222
+ apply(void 0, release);
223
+ conclude(true, value);
224
+ if (queued) onUpdate();
225
+ }, (err) => {
226
+ pending = false;
227
+ if (reset === null) return;
228
+ if (err instanceof Promise) return suspend(err);
229
+ queued = false;
230
+ conclude(false, err);
231
+ if (!awaiting) REPORT.as("effect", target, err);
232
+ });
166
233
  }
167
234
  try {
168
235
  if (argument !== false) capture(run);
169
236
  else run();
170
237
  } catch (err) {
171
- if (err instanceof Promise) {
172
- reset = void 0;
173
- const suspended = suspense = hold(true, transition);
174
- const replay = () => {
175
- if (suspense !== suspended) return;
176
- suspense = void 0;
177
- suspended(invoke);
178
- };
179
- err.then(replay, replay);
180
- } else throw err;
238
+ if (err instanceof Promise) return suspend(err);
239
+ conclude(false, err);
240
+ if (!awaiting) throw err;
181
241
  }
182
242
  }
183
243
  const unlisten = listener(target, (key) => {
184
244
  if (key === true) invoke();
185
245
  else if (key === null) {
186
- if (unset) unset(null);
187
- resume();
188
- reset = null;
246
+ stop(null);
247
+ fail("is destroyed.");
189
248
  } else if (!reset) {
190
249
  suspense?.(false);
191
250
  return reset;
192
251
  }
193
252
  });
194
253
  function cleanup() {
195
- if (unset) unset(false);
196
- resume();
197
- reset = null;
254
+ stop(false);
198
255
  unlisten();
256
+ fail("effect was cancelled before it settled.");
199
257
  }
258
+ cleanup.then = ((onFulfilled, onRejected) => {
259
+ if (!awaiting) {
260
+ awaiting = new Promise((resolve, reject) => {
261
+ settle = () => outcome.ok ? resolve(outcome.value) : reject(outcome.value);
262
+ });
263
+ if (outcome && !pending && !queued && !suspense) finish();
264
+ }
265
+ return awaiting.then(onFulfilled, onRejected);
266
+ });
200
267
  if (EffectContext && argument !== false) EffectContext.add(cleanup);
201
268
  return cleanup;
202
269
  }