@expressive/mvc 0.52.0 → 0.52.2-alpha.74

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.
package/dist/index.d.ts CHANGED
@@ -5,16 +5,16 @@ declare namespace Model {
5
5
  type Init<T extends Model = Model> = (new (...args: Model.Args<T>) => T) & Omit<typeof Model, never>;
6
6
  /**
7
7
  * Model constructor callback - is called when Model finishes intializing.
8
- * Returned function will call when model is destroyed.
8
+ * Returned function will run when model is destroyed.
9
9
  */
10
10
  type Callback<T extends Model = Model> = (this: T, thisArg: T) => Promise<void> | (() => void) | Assign<T> | void;
11
11
  /** Model constructor argument */
12
12
  type Argument<T extends Model = Model> = Assign<T> | Callback<T> | string | void;
13
13
  /** Model constructor arguments */
14
- type Args<T extends Model = any> = Argument<T>[];
14
+ type Args<T extends Model = any> = (Argument<T> | Args<T>)[];
15
15
  /** Subset of `keyof T` which are not methods or defined by base Model U. **/
16
16
  type Field<T> = Exclude<keyof T, keyof Model>;
17
- /** Any valid key for controller, including but not limited to Field<T>. */
17
+ /** Any valid key for model, including but not limited to Field<T>. */
18
18
  type Event<T> = Field<T> | (string & {}) | number | symbol;
19
19
  /** Object overlay to override values and methods on a model. */
20
20
  type Assign<T> = {
@@ -24,12 +24,12 @@ declare namespace Model {
24
24
  type Export<R> = R extends {
25
25
  get(): infer T;
26
26
  } ? T : R;
27
- /** Value for a property managed by a controller. */
27
+ /** Value for a property managed by a model. */
28
28
  type Value<T extends Model, K extends Event<T>> = K extends keyof T ? Export<T[K]> : unknown;
29
29
  type OnEvent<T extends Model> = (this: T, key: unknown, source: T) => (() => void) | void | null;
30
30
  type OnUpdate<T extends Model, K extends Event<T>> = (this: T, value: Value<T, K>, key: K, thisArg: K) => void;
31
31
  /**
32
- * Values from current state of given controller.
32
+ * Values from current state of given model.
33
33
  * Differs from `Values` as values here will drill
34
34
  * into "real" values held by exotics like ref.Object.
35
35
  */
@@ -48,7 +48,7 @@ declare namespace Model {
48
48
  /**
49
49
  * A callback function which is subscribed to parent and updates when accessed properties change.
50
50
  *
51
- * @param current - Current state of this controller. This is a proxy which detects properties which
51
+ * @param current - Current state of this model. This is a proxy which detects properties which
52
52
  * where accessed, and thus depended upon to stay current.
53
53
  *
54
54
  * @param update - Set of properties which have changed, and events fired, since last update.
@@ -59,7 +59,7 @@ declare namespace Model {
59
59
  /**
60
60
  * A callback function returned by effect. Will be called when effect is stale.
61
61
  *
62
- * @param update - `true` if update is pending, `false` effect has been cancelled, `null` if controller is destroyed.
62
+ * @param update - `true` if update is pending, `false` effect has been cancelled, `null` if model is destroyed.
63
63
  */
64
64
  type EffectCallback = ((update: boolean | null) => void);
65
65
  /**
@@ -88,7 +88,7 @@ declare abstract class Model {
88
88
  /**
89
89
  * Pull current values from state. Flattens all models and exotic values recursively.
90
90
  *
91
- * @returns Object with all values from this controller.
91
+ * @returns Object with all values from this model.
92
92
  **/
93
93
  get(): Model.State<this>;
94
94
  /**
@@ -96,7 +96,7 @@ declare abstract class Model {
96
96
  *
97
97
  * @param effect Function to run, and again whenever accessed values change.
98
98
  * If effect returns a function, it will be called when a change occurs (syncronously),
99
- * effect is cancelled, or parent controller is destroyed.
99
+ * effect is cancelled, or parent model is destroyed.
100
100
  * @returns Function to cancel listener.
101
101
  */
102
102
  get(effect: Model.Effect<this>): () => void;
@@ -133,29 +133,17 @@ declare abstract class Model {
133
133
  /**
134
134
  * Get update in progress.
135
135
  *
136
- * @returns Promise which resolves object with updated values. Is `undefined` if there is no update.
136
+ * @returns Promise which resolves object with updated values, `undefined` if there no update is pending.
137
137
  **/
138
138
  set(): PromiseLike<Model.Event<this>[]> | undefined;
139
139
  /**
140
- * Call a function when update occurs.
141
- *
142
- * Given function is called for every assignment (which changes value) or explicit `set`.
143
- *
144
- * To run logic on final value only, callback may return a function. Using the same
145
- * function for one or more events will ensure it is called only when events are settled.
146
- *
147
- * @param callback - Function to call when update occurs.
148
- * @returns Function to remove listener. Will return `true` if removed, `false` if inactive already.
149
- *
150
- */
151
- set(callback: Model.OnEvent<this>): () => boolean;
152
- /**
153
- * Declare an end to updates. This event is final and will freeze state.
154
- * This event can be watched for as well, to run cleanup logic and internally will remove all listeners.
140
+ * Update mulitple properties at once. Merges argument with current state.
141
+ * Properties which are not managed by this model will be ignored.
155
142
  *
156
- * @param status - `null` to end updates.
143
+ * @param assign - Object with properties to update.
144
+ * @returns Promise resolving an array of keys updated, `undefined` (immediately) if a noop.
157
145
  */
158
- set(status: null): void;
146
+ set(assign: Model.Assign<this>): PromiseLike<Model.Event<this>[]> | undefined;
159
147
  /**
160
148
  * Push an update. This will not change the value of associated property.
161
149
  *
@@ -168,32 +156,45 @@ declare abstract class Model {
168
156
  * to prefix an event with "!" and/or use dash-case. e.g. `set("!event")` or `set("my-event")`.
169
157
  *
170
158
  * @param key - Property or event to dispatch.
171
- * @returns Promise which resolves an array of keys which were updated.
159
+ * @returns Promise resolves an array of keys updated.
172
160
  */
173
161
  set(key: Model.Event<this>): PromiseLike<Model.Event<this>[]>;
174
- /**
175
- * Update mulitple properties at once. Merges argument with current state.
176
- * Properties which are not managed by this controller will be ignored.
177
- *
178
- * @param assign - Object with properties to update.
179
- * @returns Promise which resolves an array of keys which were updated.
180
- */
181
- set(assign: Model.Assign<this>): PromiseLike<Model.Event<this>[]> | undefined;
182
162
  /**
183
163
  * Update a property with value.
184
164
  *
185
165
  * @param key - property to update
186
166
  * @param value - value to update property with (if the same as current, no update will occur)
187
167
  * @param silent - if true, will not notify listeners of an update
168
+ * @returns Promise resolving an array of keys updated, `undefined` (immediately) if a noop.
169
+ */
170
+ set<K extends string>(key: K, value: Model.Value<this, K>, silent?: boolean): PromiseLike<Model.Event<this>[]> | undefined;
171
+ /**
172
+ * Call a function when update occurs.
173
+ *
174
+ * Given function is called for every assignment (which changes value) or explicit `set`.
175
+ *
176
+ * To run logic on final value only, callback may return a function. Using the same
177
+ * function for one or more events will ensure it is called only when events are settled.
178
+ *
179
+ * @param callback - Function to call when update occurs.
180
+ * @returns Function to remove listener. Will return `true` if removed, `false` if inactive already.
181
+ *
182
+ */
183
+ set(callback: Model.OnEvent<this>): () => boolean;
184
+ /**
185
+ * Declare an end to updates. This event is final and will freeze state.
186
+ * This event can be watched for as well, to run cleanup logic and internally will remove all listeners.
187
+ *
188
+ * @param status - `null` to end updates.
188
189
  */
189
- set<K extends string>(key: K, value?: Model.Value<this, K>, silent?: boolean): PromiseLike<Model.Event<this>[]> | undefined;
190
+ set(status: null): void;
190
191
  /**
191
192
  * Iterate over managed properties in this instance of Model.
192
193
  * Yeilds the key and underlying value for each property.
193
194
  */
194
195
  [Symbol.iterator](): Iterator<[string, unknown]>;
195
196
  /**
196
- * Create and activate a new instance of this controller.
197
+ * Create and activate a new instance of this model.
197
198
  *
198
199
  * **Important** - Unlike `new this(...)` - this method also activates state.
199
200
  *
@@ -325,10 +326,10 @@ declare namespace set {
325
326
  type Factory<T, S = any> = (this: S, key: string) => Promise<T> | T;
326
327
  }
327
328
  /**
328
- * Set property with a placeholder.
329
+ * Set property as `undefined` but required.
329
330
  *
330
331
  * Property cannot be accessed until it is defined. If accessed while undefined, a hybrid
331
- * `Promise`/`Error` (ala: [Suspense](https://reactjs.org/docs/concurrent-mode-suspense.html)) will be thrown.
332
+ * `Promise`/`Error` (aka: [Suspense](https://reactjs.org/docs/concurrent-mode-suspense.html)) will be thrown.
332
333
  */
333
334
  declare function set<T = any>(): T;
334
335
  /**
@@ -366,6 +367,21 @@ declare namespace has {
366
367
  declare function has<T extends Model>(type: Model.Type<T>, callback?: has.Callback<T>): readonly T[];
367
368
  declare function has(callback?: has.Callback): readonly Model[];
368
369
 
370
+ /**
371
+ * Update callback function.
372
+ *
373
+ * @param key -
374
+ * - `string` - property which has updated.
375
+ * - `false` - a normal update has completed.
376
+ * - `true` - initial event; instance is ready.
377
+ * - `null` - terminal event; instance is expired.
378
+ * @param source - Instance of Model for which update has occured.
379
+ */
380
+ type OnUpdate<T = any> = (this: T, key: unknown, source: T) => (() => void) | null | void;
381
+ type OnAccess<T, R = unknown> = (from: T, key: string | number, value: R) => unknown;
382
+ type Event = number | string | null | boolean | symbol;
383
+ declare function addListener(subject: {}, callback: OnUpdate, select?: Event): () => boolean;
384
+ declare function createProxy<T extends {}>(from: T, observer: OnAccess<T>): any;
369
385
  type Effect<T extends {}> = (this: T, argument: T) => ((update: boolean | null) => void) | Promise<void> | null | void;
370
386
  /**
371
387
  * Create a side-effect which will update whenever values accessed change.
@@ -375,11 +391,15 @@ type Effect<T extends {}> = (this: T, argument: T) => ((update: boolean | null)
375
391
  * @param callback - Function to invoke when values change.
376
392
  * @param requireValues - If `true` will throw if accessing a value which is `undefined`.
377
393
  */
378
- declare function effect<T extends {}>(target: T, callback: Effect<Required<T>>, requireValues: true): () => void;
379
- declare function effect<T extends {}>(target: T, callback: Effect<T>, requireValues?: boolean): () => void;
394
+ declare function createEffect<T extends {}>(target: T, callback: Effect<Required<T>>, requireValues: true): () => void;
395
+ declare function createEffect<T extends {}>(target: T, callback: Effect<T>, requireValues?: boolean): () => void;
380
396
 
381
397
  declare namespace Context {
382
- type Input = Record<string | number, Model | Model.Init>;
398
+ type Multiple<T extends Model> = {
399
+ [key: string | number]: Model.Init<T> | T;
400
+ };
401
+ type Accept<T extends Model = Model> = T | Model.Init<T> | Multiple<T>;
402
+ type ForEach = (added: Model, explicit: boolean) => void;
383
403
  type Expect = (model: Model) => (() => void) | void;
384
404
  }
385
405
  interface Context {
@@ -391,30 +411,15 @@ declare class Context {
391
411
  id: string;
392
412
  protected layer: Map<string | number, Model | Model.Type<Model>>;
393
413
  protected cleanup: Set<() => void>;
394
- constructor(inputs?: Context.Input);
414
+ constructor(inputs?: Context.Accept);
415
+ /** Run callback when a specified type is registered to a **child** context. */
416
+ has<T extends Model>(Type: Model.Type<T>, callback: (model: T) => void): void;
417
+ /** Find specified type registered to a parent context. Returns undefined if none are found. */
395
418
  get<T extends Model>(Type: Model.Type<T>): T | undefined;
396
- get<T extends Model>(Type: Model.Type<T>, callback: (model: T) => void): void;
397
- push(inputs?: Context.Input): this;
419
+ push(inputs?: Context.Accept): this;
398
420
  pop(): void;
399
- include(inputs: Context.Input, forEach?: (added: Model, explicit: boolean) => void): void;
421
+ include<T extends Model>(inputs: Context.Accept<T>, forEach?: Context.ForEach | Model.Assign<T>): void;
400
422
  protected add<T extends Model>(input: T | Model.Init<T>, implicit?: boolean): T;
401
423
  }
402
424
 
403
- declare global {
404
- /**
405
- * Defines MVC specific matchers for [Jest](https://jestjs.io/) test suites.
406
- *
407
- * For these to work you will need to add `@expressive/mvc/jest`
408
- * under `setupFilesAfterEnv` in your Jest configuration.
409
- */
410
- namespace jest {
411
- interface Matchers<R> {
412
- /** Assert model does have one or more updates pending. */
413
- toUpdate(timeout?: number): Promise<R>;
414
- /** Assert model did update with keys specified. */
415
- toHaveUpdated<R>(...keys: (string | symbol | number)[]): Promise<R>;
416
- }
417
- }
418
- }
419
-
420
- export { Context, Model, Model as default, effect, get, has, ref, set, use };
425
+ export { Context, Model, addListener, createEffect, createProxy, Model as default, get, has, ref, set, use };
package/dist/index.js CHANGED
@@ -22,8 +22,10 @@ var src_exports = {};
22
22
  __export(src_exports, {
23
23
  Context: () => Context,
24
24
  Model: () => Model,
25
+ addListener: () => addListener,
26
+ createEffect: () => createEffect,
27
+ createProxy: () => createProxy,
25
28
  default: () => Model,
26
- effect: () => effect,
27
29
  get: () => get,
28
30
  has: () => has,
29
31
  ref: () => ref,
@@ -35,25 +37,30 @@ module.exports = __toCommonJS(src_exports);
35
37
  // src/control.ts
36
38
  var DISPATCH = /* @__PURE__ */ new Set();
37
39
  var OBSERVER = /* @__PURE__ */ new WeakMap();
38
- var LISTENER = /* @__PURE__ */ new WeakMap();
40
+ var LISTENERS = /* @__PURE__ */ new WeakMap();
41
+ var PENDING = /* @__PURE__ */ new WeakMap();
39
42
  var onReady = () => null;
40
43
  function addListener(subject, callback, select) {
41
- let subs = LISTENER.get(subject);
44
+ let subs = LISTENERS.get(subject);
42
45
  if (!subs)
43
- LISTENER.set(subject, subs = /* @__PURE__ */ new Map([[onReady, false]]));
44
- const filter = select !== void 0 && /* @__PURE__ */ new Set([select]);
45
- if (!filter && !subs.has(onReady))
46
+ LISTENERS.set(subject, subs = /* @__PURE__ */ new Map([[onReady, void 0]]));
47
+ const filter = select === void 0 ? void 0 : /* @__PURE__ */ new Set([select]);
48
+ if (!subs.has(onReady) && !filter)
46
49
  callback(true, subject);
47
50
  subs.set(callback, filter);
48
51
  return () => subs.delete(callback);
49
52
  }
53
+ function createProxy(from, observer) {
54
+ const proxy = Object.create(from);
55
+ OBSERVER.set(proxy, observer);
56
+ return proxy;
57
+ }
50
58
  function watch(from, key2, value) {
51
59
  const access = OBSERVER.get(from);
52
60
  return access ? access(from, key2, value) : value;
53
61
  }
54
- var PENDING = /* @__PURE__ */ new WeakMap();
55
62
  function emit(source, key2) {
56
- const subs = LISTENER.get(source);
63
+ const subs = LISTENERS.get(source);
57
64
  const ready = !subs.has(onReady);
58
65
  if (key2 === true && ready)
59
66
  return;
@@ -79,27 +86,28 @@ function emit(source, key2) {
79
86
  });
80
87
  PENDING.delete(source);
81
88
  }
82
- function queue(event2) {
89
+ function queue(eventHandler) {
83
90
  if (!DISPATCH.size)
84
91
  setTimeout(() => {
85
- DISPATCH.forEach((event3) => {
92
+ DISPATCH.forEach((event2) => {
86
93
  try {
87
- event3();
94
+ event2();
88
95
  } catch (err) {
89
96
  console.error(err);
90
97
  }
91
98
  });
92
99
  DISPATCH.clear();
93
100
  }, 0);
94
- DISPATCH.add(event2);
101
+ DISPATCH.add(eventHandler);
95
102
  }
96
- function effect(target, callback, requireValues) {
97
- const listeners = LISTENER.get(target);
103
+ function createEffect(target, callback, requireValues) {
104
+ const listeners = LISTENERS.get(target);
98
105
  let unset;
99
106
  let reset;
100
107
  function invoke() {
101
108
  let stale;
102
- const subscriber = Object.create(target);
109
+ const subscriber = createProxy(target, access);
110
+ LISTENERS.set(subscriber, listeners);
103
111
  function onUpdate() {
104
112
  if (stale)
105
113
  return null;
@@ -113,20 +121,19 @@ function effect(target, callback, requireValues) {
113
121
  function access(from, key2, value) {
114
122
  if (value === void 0 && requireValues)
115
123
  throw new Error(`${from}.${key2} is required in this context.`);
116
- const listeners2 = LISTENER.get(from);
124
+ const listeners2 = LISTENERS.get(from);
117
125
  let listener = listeners2.get(onUpdate);
118
126
  if (!listener)
119
127
  listeners2.set(onUpdate, listener = /* @__PURE__ */ new Set());
120
128
  listener.add(key2);
121
- const nested = LISTENER.get(value);
122
- if (nested) {
123
- LISTENER.set(value = Object.create(value), nested);
124
- OBSERVER.set(value, access);
125
- }
129
+ const nested = LISTENERS.get(value);
130
+ if (nested)
131
+ LISTENERS.set(
132
+ value = createProxy(value, access),
133
+ nested
134
+ );
126
135
  return value;
127
136
  }
128
- LISTENER.set(subscriber, listeners);
129
- OBSERVER.set(subscriber, access);
130
137
  try {
131
138
  const out = callback.call(subscriber, subscriber);
132
139
  unset = typeof out == "function" ? out : void 0;
@@ -163,27 +170,24 @@ var PENDING2 = /* @__PURE__ */ new WeakMap();
163
170
  var PARENT = /* @__PURE__ */ new WeakMap();
164
171
  var METHODS = /* @__PURE__ */ new WeakMap();
165
172
  var METHOD = /* @__PURE__ */ new WeakMap();
166
- var EXPORT;
167
- var Model = class _Model {
173
+ var Model = class {
168
174
  constructor(...args) {
169
- ID.set(this, `${this.constructor}-${uid()}`);
170
- STATE.set(this, {});
171
175
  prepare(this);
172
176
  define(this, "is", { value: this });
173
- apply(this, [...args, init]);
177
+ init(this, args);
174
178
  }
175
179
  get(arg1, arg2) {
176
180
  const self = this.is;
177
181
  if (arg1 === void 0)
178
- return values(self);
182
+ return snapshot(self);
179
183
  if (typeof arg1 == "function") {
180
184
  let pending = /* @__PURE__ */ new Set();
181
- return effect(self, (state) => {
185
+ return createEffect(self, (state) => {
182
186
  const cb = arg1.call(state, state, pending);
183
187
  return cb === null ? cb : (update2) => {
184
188
  pending = PENDING2.get(self);
185
189
  if (typeof cb == "function")
186
- return cb(update2);
190
+ cb(update2);
187
191
  };
188
192
  });
189
193
  }
@@ -238,14 +242,13 @@ var Model = class _Model {
238
242
  return Object.entries(STATE.get(this.is))[Symbol.iterator]();
239
243
  }
240
244
  /**
241
- * Create and activate a new instance of this controller.
245
+ * Create and activate a new instance of this model.
242
246
  *
243
247
  * **Important** - Unlike `new this(...)` - this method also activates state.
244
248
  *
245
249
  * @param args - arguments sent to constructor
246
250
  */
247
251
  static new(...args) {
248
- _Model.is(this);
249
252
  const instance = new this(...args);
250
253
  emit(instance, true);
251
254
  return instance;
@@ -257,9 +260,7 @@ var Model = class _Model {
257
260
  * methods and properties of this class.
258
261
  */
259
262
  static is(maybe) {
260
- if (maybe === _Model)
261
- throw new Error("Model is abstract and not a valid type.");
262
- return typeof maybe == "function" && maybe.prototype instanceof this;
263
+ return maybe === this || typeof maybe == "function" && maybe.prototype instanceof this;
263
264
  }
264
265
  /**
265
266
  * Register a callback to run when any instance of this Model is updated.
@@ -283,50 +284,59 @@ define(Model, "toString", {
283
284
  }
284
285
  });
285
286
  function prepare(model) {
287
+ let T = model.constructor;
288
+ if (T === Model)
289
+ throw new Error("Cannot create base Model.");
286
290
  const chain = [];
287
- let type = model.constructor;
288
291
  let keys2 = /* @__PURE__ */ new Set();
289
- while (type.name) {
290
- chain.unshift(type);
291
- type = Object.getPrototypeOf(type);
292
- }
293
- for (const type2 of chain) {
294
- for (const cb of NOTIFY.get(type2) || [])
292
+ ID.set(model, `${T}-${uid()}`);
293
+ while (T.name) {
294
+ for (const cb of NOTIFY.get(T) || [])
295
295
  addListener(model, cb);
296
- if (type2 == Model)
297
- continue;
298
- if (METHODS.has(type2)) {
299
- keys2 = METHODS.get(type2);
296
+ if (T === Model)
297
+ break;
298
+ else
299
+ chain.unshift(T);
300
+ T = Object.getPrototypeOf(T);
301
+ }
302
+ for (const type of chain) {
303
+ if (METHODS.has(type)) {
304
+ keys2 = METHODS.get(type);
300
305
  continue;
301
306
  }
302
- METHODS.set(type2, keys2 = new Set(keys2));
303
- for (const { key: key2, value } of entries(type2.prototype)) {
304
- let set3 = function(method) {
307
+ METHODS.set(type, keys2 = new Set(keys2));
308
+ for (const [key2, { value }] of Object.entries(
309
+ Object.getOwnPropertyDescriptors(type.prototype)
310
+ )) {
311
+ let get3 = function() {
312
+ return this.hasOwnProperty(key2) ? value : set3.call(this, value);
313
+ }, set3 = function(method) {
305
314
  const value2 = method.bind(this.is);
306
315
  METHOD.set(value2, method);
307
316
  define(this.is, key2, { value: value2, writable: true });
308
317
  return value2;
309
318
  };
310
- var set2 = set3;
319
+ var get2 = get3, set2 = set3;
311
320
  if (!value || key2 == "constructor")
312
321
  continue;
313
322
  keys2.add(key2);
314
- Object.defineProperty(type2.prototype, key2, {
315
- set: set3,
316
- get() {
317
- return this.hasOwnProperty(key2) ? value : set3.call(this, value);
318
- }
319
- });
323
+ define(type.prototype, key2, { get: get3, set: set3 });
320
324
  }
321
325
  }
322
326
  }
323
- function apply(model, args) {
324
- for (const arg of args)
325
- if (typeof arg == "string")
326
- ID.set(model, arg);
327
+ function init(model, args) {
328
+ const state = {};
329
+ const clean = /* @__PURE__ */ new Set();
330
+ STATE.set(model, state);
331
+ args = args.flat().filter((arg) => {
332
+ if (typeof arg != "string")
333
+ return true;
334
+ ID.set(model, arg);
335
+ });
327
336
  addListener(model, () => {
328
- const done = /* @__PURE__ */ new Set();
329
- for (const arg of args) {
337
+ if (!PARENT.has(model))
338
+ PARENT.set(model, null);
339
+ args.forEach((arg) => {
330
340
  const use2 = typeof arg == "function" ? arg.call(model, model) : arg;
331
341
  if (use2 instanceof Promise)
332
342
  use2.catch((err) => {
@@ -336,37 +346,30 @@ function apply(model, args) {
336
346
  else if (typeof use2 == "object")
337
347
  assign(model, use2);
338
348
  else if (typeof use2 == "function")
339
- done.add(use2);
349
+ clean.add(use2);
350
+ });
351
+ for (const key2 in model) {
352
+ const desc = Object.getOwnPropertyDescriptor(model, key2);
353
+ if ("value" in desc) {
354
+ update(model, key2, desc.value, true);
355
+ define(model, key2, {
356
+ configurable: false,
357
+ set: (x) => update(model, key2, x),
358
+ get() {
359
+ return watch(this, key2, state[key2]);
360
+ }
361
+ });
362
+ }
340
363
  }
341
- addListener(model, () => {
342
- done.forEach((x) => x());
343
- }, null);
344
364
  return null;
345
365
  });
346
- }
347
- function init(model) {
348
- const state = STATE.get(model);
349
- if (!PARENT.has(model))
350
- PARENT.set(model, null);
351
- for (const key2 in model) {
352
- const desc = Object.getOwnPropertyDescriptor(model, key2);
353
- if ("value" in desc) {
354
- update(model, key2, desc.value, true);
355
- define(model, key2, {
356
- configurable: false,
357
- set: (x) => update(model, key2, x),
358
- get() {
359
- return watch(this, key2, state[key2]);
360
- }
361
- });
362
- }
363
- }
364
- return () => {
366
+ addListener(model, () => {
367
+ clean.forEach((x) => x());
365
368
  for (const [_, value] of model)
366
369
  if (value instanceof Model && PARENT.get(value) === model)
367
370
  value.set(null);
368
371
  Object.freeze(state);
369
- };
372
+ }, null);
370
373
  }
371
374
  function fetch(subject, property, required) {
372
375
  const state = STATE.get(subject);
@@ -393,6 +396,25 @@ function fetch(subject, property, required) {
393
396
  stack: error.stack
394
397
  });
395
398
  }
399
+ var EXPORT;
400
+ function snapshot(model) {
401
+ const values = {};
402
+ let isNotRecursive;
403
+ if (!EXPORT) {
404
+ isNotRecursive = true;
405
+ EXPORT = /* @__PURE__ */ new Map([[model, values]]);
406
+ }
407
+ for (let [key2, value] of model) {
408
+ if (EXPORT.has(value))
409
+ value = EXPORT.get(value);
410
+ else if (value && typeof value == "object" && "get" in value && typeof value.get === "function")
411
+ EXPORT.set(value, value = value.get());
412
+ values[key2] = value;
413
+ }
414
+ if (isNotRecursive)
415
+ EXPORT = void 0;
416
+ return Object.freeze(values);
417
+ }
396
418
  function update(subject, key2, value, arg) {
397
419
  const state = STATE.get(subject);
398
420
  if (Object.isFrozen(state))
@@ -429,42 +451,23 @@ function event(subject, key2, silent) {
429
451
  if (!silent)
430
452
  emit(subject, key2);
431
453
  }
432
- function assign(to, values2) {
454
+ function assign(to, values) {
433
455
  const methods = METHODS.get(to.constructor);
434
- for (const key2 in values2)
456
+ for (const key2 in values)
435
457
  if (key2 in to || methods.has(key2))
436
- to[key2] = values2[key2];
437
- }
438
- function values(model) {
439
- const values2 = {};
440
- const current = !EXPORT && (EXPORT = /* @__PURE__ */ new Map([[model, values2]]));
441
- for (let [key2, value] of model) {
442
- if (EXPORT.has(value))
443
- value = EXPORT.get(value);
444
- else if (value && typeof value.get === "function")
445
- EXPORT.set(value, value = value.get());
446
- values2[key2] = value;
447
- }
448
- if (current)
449
- EXPORT = void 0;
450
- return Object.freeze(values2);
458
+ to[key2] = values[key2];
451
459
  }
452
460
  function uid() {
453
461
  return (Math.random() * 0.722 + 0.278).toString(36).substring(2, 8).toUpperCase();
454
462
  }
455
- function entries(object) {
456
- return Object.entries(
457
- Object.getOwnPropertyDescriptors(object)
458
- ).map(([key2, desc]) => ({ key: key2, ...desc }));
459
- }
460
463
 
461
464
  // src/context.ts
462
- var Register = /* @__PURE__ */ new WeakMap();
463
- var Table = /* @__PURE__ */ new Map();
465
+ var LOOKUP = /* @__PURE__ */ new WeakMap();
466
+ var KEYS = /* @__PURE__ */ new Map();
464
467
  function key(T, upstream) {
465
- let K = Table.get(T);
468
+ let K = KEYS.get(T);
466
469
  if (!K)
467
- Table.set(T, K = Symbol(
470
+ KEYS.set(T, K = Symbol(
468
471
  typeof T == "symbol" ? "get " + T.description : String(T)
469
472
  ));
470
473
  return upstream ? key(K) : K;
@@ -484,7 +487,7 @@ var Context = class _Context {
484
487
  this.include(inputs);
485
488
  }
486
489
  static get(from, callback) {
487
- const waiting = Register.get(from);
490
+ const waiting = LOOKUP.get(from);
488
491
  if (waiting instanceof _Context) {
489
492
  if (callback)
490
493
  callback(waiting);
@@ -494,11 +497,14 @@ var Context = class _Context {
494
497
  if (waiting)
495
498
  waiting.push(callback);
496
499
  else
497
- Register.set(from, [callback]);
500
+ LOOKUP.set(from, [callback]);
498
501
  }
499
- get(Type, callback) {
500
- if (callback)
501
- Object.defineProperty(this, key(Type, true), { value: callback });
502
+ /** Run callback when a specified type is registered to a **child** context. */
503
+ has(Type, callback) {
504
+ define(this, key(Type, true), { value: callback });
505
+ }
506
+ /** Find specified type registered to a parent context. Returns undefined if none are found. */
507
+ get(Type) {
502
508
  const result = this[key(Type)];
503
509
  if (result === null)
504
510
  throw new Error(`Did find ${Type} in context, but multiple were defined.`);
@@ -520,9 +526,16 @@ var Context = class _Context {
520
526
  }
521
527
  include(inputs, forEach) {
522
528
  const init2 = /* @__PURE__ */ new Map();
529
+ if (typeof inputs == "function" || inputs instanceof Model)
530
+ inputs = { 0: inputs };
523
531
  Object.entries(inputs).forEach(([K, V]) => {
524
- if (!(Model.is(V) || V instanceof Model))
525
- throw new Error(`Context can only include Model or instance but got ${V}${K && " as " + K}.`);
532
+ if (Model.is(V) || V instanceof Model)
533
+ return;
534
+ if (K && K != V)
535
+ V = `${V} (as '${K}')`;
536
+ throw new Error(
537
+ `Context may only include instance or class \`extends Model\` but got ${V}.`
538
+ );
526
539
  });
527
540
  for (const key2 in inputs) {
528
541
  const input = inputs[key2];
@@ -538,9 +551,11 @@ var Context = class _Context {
538
551
  }
539
552
  }
540
553
  for (const [model, explicit] of init2) {
541
- model.set();
542
- if (forEach)
554
+ emit(model, true);
555
+ if (typeof forEach == "function")
543
556
  forEach(model, explicit);
557
+ else if (forEach && explicit)
558
+ assign(model, forEach);
544
559
  for (const [_key, value] of model)
545
560
  if (PARENT.get(value) === model) {
546
561
  this.add(value, true);
@@ -571,15 +586,15 @@ var Context = class _Context {
571
586
  keys(T).forEach((K) => {
572
587
  const value = this.hasOwnProperty(K) ? null : I;
573
588
  if (value || this[K] !== I && !implicit)
574
- Object.defineProperty(this, K, {
589
+ define(this, K, {
575
590
  configurable: true,
576
591
  value
577
592
  });
578
593
  });
579
- const waiting = Register.get(I);
594
+ const waiting = LOOKUP.get(I);
580
595
  if (waiting instanceof Array)
581
596
  waiting.forEach((cb) => cb(this));
582
- Register.set(I, this);
597
+ LOOKUP.set(I, this);
583
598
  return I;
584
599
  }
585
600
  };
@@ -611,7 +626,8 @@ function use(arg1, arg2) {
611
626
  }
612
627
  Model.on((_, model) => {
613
628
  const state = STATE.get(model);
614
- for (const { key: key2, value } of entries(model)) {
629
+ for (const key2 in model) {
630
+ const { value } = Object.getOwnPropertyDescriptor(model, key2);
615
631
  const instruction = INSTRUCT.get(value);
616
632
  if (!instruction) {
617
633
  if (value instanceof Model && key2 !== "is")
@@ -626,7 +642,7 @@ Model.on((_, model) => {
626
642
  const desc = typeof output == "object" ? output : { get: output };
627
643
  if ("value" in desc)
628
644
  state[key2] = desc.value;
629
- Object.defineProperty(model, key2, {
645
+ define(model, key2, {
630
646
  enumerable: desc.enumerable !== false,
631
647
  get() {
632
648
  return watch(
@@ -679,7 +695,7 @@ function get(arg0, arg1) {
679
695
  }
680
696
  if (typeof arg1 == "function") {
681
697
  let connect2 = function(model) {
682
- reset = effect(model, (current) => {
698
+ reset = createEffect(model, (current) => {
683
699
  proxy = current;
684
700
  if (!(key2 in state) || STALE.delete(compute2))
685
701
  compute2(!reset);
@@ -723,7 +739,6 @@ function get(arg0, arg1) {
723
739
  }
724
740
 
725
741
  // src/instruction/ref.ts
726
- var define2 = Object.defineProperty;
727
742
  function ref(arg, arg2) {
728
743
  return use((key2, subject, state) => {
729
744
  let value = {};
@@ -731,20 +746,20 @@ function ref(arg, arg2) {
731
746
  subject.get(() => {
732
747
  for (const key3 in state)
733
748
  if (typeof arg2 == "function")
734
- define2(value, key3, {
749
+ define(value, key3, {
735
750
  configurable: true,
736
751
  get() {
737
752
  const out = arg2(key3);
738
- define2(value, key3, { value: out });
753
+ define(value, key3, { value: out });
739
754
  return out;
740
755
  }
741
756
  });
742
757
  else {
743
758
  const get2 = () => state[key3];
744
759
  const set2 = (value2) => subject.set(key3, value2);
745
- define2(set2, "current", { get: get2, set: set2 });
746
- define2(set2, "get", { value: get2 });
747
- define2(value, key3, { value: set2 });
760
+ define(set2, "current", { get: get2, set: set2 });
761
+ define(set2, "get", { value: get2 });
762
+ define(value, key3, { value: set2 });
748
763
  }
749
764
  });
750
765
  else if (typeof arg == "object")
@@ -769,7 +784,7 @@ function ref(arg, arg2) {
769
784
  get: { value: get2 }
770
785
  });
771
786
  }
772
- define2(subject, key2, { value });
787
+ define(subject, key2, { value });
773
788
  });
774
789
  }
775
790
 
@@ -856,7 +871,7 @@ function has(arg1, arg2) {
856
871
  };
857
872
  if (Model.is(arg1))
858
873
  Context.get(subject, (context) => {
859
- context.get(arg1, (got) => {
874
+ context.has(arg1, (got) => {
860
875
  let remove;
861
876
  let disconnect;
862
877
  if (applied.has(got))
@@ -920,7 +935,9 @@ function has(arg1, arg2) {
920
935
  0 && (module.exports = {
921
936
  Context,
922
937
  Model,
923
- effect,
938
+ addListener,
939
+ createEffect,
940
+ createProxy,
924
941
  get,
925
942
  has,
926
943
  ref,
package/dist/index.mjs CHANGED
@@ -1,25 +1,30 @@
1
1
  // src/control.ts
2
2
  var DISPATCH = /* @__PURE__ */ new Set();
3
3
  var OBSERVER = /* @__PURE__ */ new WeakMap();
4
- var LISTENER = /* @__PURE__ */ new WeakMap();
4
+ var LISTENERS = /* @__PURE__ */ new WeakMap();
5
+ var PENDING = /* @__PURE__ */ new WeakMap();
5
6
  var onReady = () => null;
6
7
  function addListener(subject, callback, select) {
7
- let subs = LISTENER.get(subject);
8
+ let subs = LISTENERS.get(subject);
8
9
  if (!subs)
9
- LISTENER.set(subject, subs = /* @__PURE__ */ new Map([[onReady, false]]));
10
- const filter = select !== void 0 && /* @__PURE__ */ new Set([select]);
11
- if (!filter && !subs.has(onReady))
10
+ LISTENERS.set(subject, subs = /* @__PURE__ */ new Map([[onReady, void 0]]));
11
+ const filter = select === void 0 ? void 0 : /* @__PURE__ */ new Set([select]);
12
+ if (!subs.has(onReady) && !filter)
12
13
  callback(true, subject);
13
14
  subs.set(callback, filter);
14
15
  return () => subs.delete(callback);
15
16
  }
17
+ function createProxy(from, observer) {
18
+ const proxy = Object.create(from);
19
+ OBSERVER.set(proxy, observer);
20
+ return proxy;
21
+ }
16
22
  function watch(from, key2, value) {
17
23
  const access = OBSERVER.get(from);
18
24
  return access ? access(from, key2, value) : value;
19
25
  }
20
- var PENDING = /* @__PURE__ */ new WeakMap();
21
26
  function emit(source, key2) {
22
- const subs = LISTENER.get(source);
27
+ const subs = LISTENERS.get(source);
23
28
  const ready = !subs.has(onReady);
24
29
  if (key2 === true && ready)
25
30
  return;
@@ -45,27 +50,28 @@ function emit(source, key2) {
45
50
  });
46
51
  PENDING.delete(source);
47
52
  }
48
- function queue(event2) {
53
+ function queue(eventHandler) {
49
54
  if (!DISPATCH.size)
50
55
  setTimeout(() => {
51
- DISPATCH.forEach((event3) => {
56
+ DISPATCH.forEach((event2) => {
52
57
  try {
53
- event3();
58
+ event2();
54
59
  } catch (err) {
55
60
  console.error(err);
56
61
  }
57
62
  });
58
63
  DISPATCH.clear();
59
64
  }, 0);
60
- DISPATCH.add(event2);
65
+ DISPATCH.add(eventHandler);
61
66
  }
62
- function effect(target, callback, requireValues) {
63
- const listeners = LISTENER.get(target);
67
+ function createEffect(target, callback, requireValues) {
68
+ const listeners = LISTENERS.get(target);
64
69
  let unset;
65
70
  let reset;
66
71
  function invoke() {
67
72
  let stale;
68
- const subscriber = Object.create(target);
73
+ const subscriber = createProxy(target, access);
74
+ LISTENERS.set(subscriber, listeners);
69
75
  function onUpdate() {
70
76
  if (stale)
71
77
  return null;
@@ -79,20 +85,19 @@ function effect(target, callback, requireValues) {
79
85
  function access(from, key2, value) {
80
86
  if (value === void 0 && requireValues)
81
87
  throw new Error(`${from}.${key2} is required in this context.`);
82
- const listeners2 = LISTENER.get(from);
88
+ const listeners2 = LISTENERS.get(from);
83
89
  let listener = listeners2.get(onUpdate);
84
90
  if (!listener)
85
91
  listeners2.set(onUpdate, listener = /* @__PURE__ */ new Set());
86
92
  listener.add(key2);
87
- const nested = LISTENER.get(value);
88
- if (nested) {
89
- LISTENER.set(value = Object.create(value), nested);
90
- OBSERVER.set(value, access);
91
- }
93
+ const nested = LISTENERS.get(value);
94
+ if (nested)
95
+ LISTENERS.set(
96
+ value = createProxy(value, access),
97
+ nested
98
+ );
92
99
  return value;
93
100
  }
94
- LISTENER.set(subscriber, listeners);
95
- OBSERVER.set(subscriber, access);
96
101
  try {
97
102
  const out = callback.call(subscriber, subscriber);
98
103
  unset = typeof out == "function" ? out : void 0;
@@ -129,27 +134,24 @@ var PENDING2 = /* @__PURE__ */ new WeakMap();
129
134
  var PARENT = /* @__PURE__ */ new WeakMap();
130
135
  var METHODS = /* @__PURE__ */ new WeakMap();
131
136
  var METHOD = /* @__PURE__ */ new WeakMap();
132
- var EXPORT;
133
- var Model = class _Model {
137
+ var Model = class {
134
138
  constructor(...args) {
135
- ID.set(this, `${this.constructor}-${uid()}`);
136
- STATE.set(this, {});
137
139
  prepare(this);
138
140
  define(this, "is", { value: this });
139
- apply(this, [...args, init]);
141
+ init(this, args);
140
142
  }
141
143
  get(arg1, arg2) {
142
144
  const self = this.is;
143
145
  if (arg1 === void 0)
144
- return values(self);
146
+ return snapshot(self);
145
147
  if (typeof arg1 == "function") {
146
148
  let pending = /* @__PURE__ */ new Set();
147
- return effect(self, (state) => {
149
+ return createEffect(self, (state) => {
148
150
  const cb = arg1.call(state, state, pending);
149
151
  return cb === null ? cb : (update2) => {
150
152
  pending = PENDING2.get(self);
151
153
  if (typeof cb == "function")
152
- return cb(update2);
154
+ cb(update2);
153
155
  };
154
156
  });
155
157
  }
@@ -204,14 +206,13 @@ var Model = class _Model {
204
206
  return Object.entries(STATE.get(this.is))[Symbol.iterator]();
205
207
  }
206
208
  /**
207
- * Create and activate a new instance of this controller.
209
+ * Create and activate a new instance of this model.
208
210
  *
209
211
  * **Important** - Unlike `new this(...)` - this method also activates state.
210
212
  *
211
213
  * @param args - arguments sent to constructor
212
214
  */
213
215
  static new(...args) {
214
- _Model.is(this);
215
216
  const instance = new this(...args);
216
217
  emit(instance, true);
217
218
  return instance;
@@ -223,9 +224,7 @@ var Model = class _Model {
223
224
  * methods and properties of this class.
224
225
  */
225
226
  static is(maybe) {
226
- if (maybe === _Model)
227
- throw new Error("Model is abstract and not a valid type.");
228
- return typeof maybe == "function" && maybe.prototype instanceof this;
227
+ return maybe === this || typeof maybe == "function" && maybe.prototype instanceof this;
229
228
  }
230
229
  /**
231
230
  * Register a callback to run when any instance of this Model is updated.
@@ -249,50 +248,59 @@ define(Model, "toString", {
249
248
  }
250
249
  });
251
250
  function prepare(model) {
251
+ let T = model.constructor;
252
+ if (T === Model)
253
+ throw new Error("Cannot create base Model.");
252
254
  const chain = [];
253
- let type = model.constructor;
254
255
  let keys2 = /* @__PURE__ */ new Set();
255
- while (type.name) {
256
- chain.unshift(type);
257
- type = Object.getPrototypeOf(type);
258
- }
259
- for (const type2 of chain) {
260
- for (const cb of NOTIFY.get(type2) || [])
256
+ ID.set(model, `${T}-${uid()}`);
257
+ while (T.name) {
258
+ for (const cb of NOTIFY.get(T) || [])
261
259
  addListener(model, cb);
262
- if (type2 == Model)
263
- continue;
264
- if (METHODS.has(type2)) {
265
- keys2 = METHODS.get(type2);
260
+ if (T === Model)
261
+ break;
262
+ else
263
+ chain.unshift(T);
264
+ T = Object.getPrototypeOf(T);
265
+ }
266
+ for (const type of chain) {
267
+ if (METHODS.has(type)) {
268
+ keys2 = METHODS.get(type);
266
269
  continue;
267
270
  }
268
- METHODS.set(type2, keys2 = new Set(keys2));
269
- for (const { key: key2, value } of entries(type2.prototype)) {
270
- let set3 = function(method) {
271
+ METHODS.set(type, keys2 = new Set(keys2));
272
+ for (const [key2, { value }] of Object.entries(
273
+ Object.getOwnPropertyDescriptors(type.prototype)
274
+ )) {
275
+ let get3 = function() {
276
+ return this.hasOwnProperty(key2) ? value : set3.call(this, value);
277
+ }, set3 = function(method) {
271
278
  const value2 = method.bind(this.is);
272
279
  METHOD.set(value2, method);
273
280
  define(this.is, key2, { value: value2, writable: true });
274
281
  return value2;
275
282
  };
276
- var set2 = set3;
283
+ var get2 = get3, set2 = set3;
277
284
  if (!value || key2 == "constructor")
278
285
  continue;
279
286
  keys2.add(key2);
280
- Object.defineProperty(type2.prototype, key2, {
281
- set: set3,
282
- get() {
283
- return this.hasOwnProperty(key2) ? value : set3.call(this, value);
284
- }
285
- });
287
+ define(type.prototype, key2, { get: get3, set: set3 });
286
288
  }
287
289
  }
288
290
  }
289
- function apply(model, args) {
290
- for (const arg of args)
291
- if (typeof arg == "string")
292
- ID.set(model, arg);
291
+ function init(model, args) {
292
+ const state = {};
293
+ const clean = /* @__PURE__ */ new Set();
294
+ STATE.set(model, state);
295
+ args = args.flat().filter((arg) => {
296
+ if (typeof arg != "string")
297
+ return true;
298
+ ID.set(model, arg);
299
+ });
293
300
  addListener(model, () => {
294
- const done = /* @__PURE__ */ new Set();
295
- for (const arg of args) {
301
+ if (!PARENT.has(model))
302
+ PARENT.set(model, null);
303
+ args.forEach((arg) => {
296
304
  const use2 = typeof arg == "function" ? arg.call(model, model) : arg;
297
305
  if (use2 instanceof Promise)
298
306
  use2.catch((err) => {
@@ -302,37 +310,30 @@ function apply(model, args) {
302
310
  else if (typeof use2 == "object")
303
311
  assign(model, use2);
304
312
  else if (typeof use2 == "function")
305
- done.add(use2);
313
+ clean.add(use2);
314
+ });
315
+ for (const key2 in model) {
316
+ const desc = Object.getOwnPropertyDescriptor(model, key2);
317
+ if ("value" in desc) {
318
+ update(model, key2, desc.value, true);
319
+ define(model, key2, {
320
+ configurable: false,
321
+ set: (x) => update(model, key2, x),
322
+ get() {
323
+ return watch(this, key2, state[key2]);
324
+ }
325
+ });
326
+ }
306
327
  }
307
- addListener(model, () => {
308
- done.forEach((x) => x());
309
- }, null);
310
328
  return null;
311
329
  });
312
- }
313
- function init(model) {
314
- const state = STATE.get(model);
315
- if (!PARENT.has(model))
316
- PARENT.set(model, null);
317
- for (const key2 in model) {
318
- const desc = Object.getOwnPropertyDescriptor(model, key2);
319
- if ("value" in desc) {
320
- update(model, key2, desc.value, true);
321
- define(model, key2, {
322
- configurable: false,
323
- set: (x) => update(model, key2, x),
324
- get() {
325
- return watch(this, key2, state[key2]);
326
- }
327
- });
328
- }
329
- }
330
- return () => {
330
+ addListener(model, () => {
331
+ clean.forEach((x) => x());
331
332
  for (const [_, value] of model)
332
333
  if (value instanceof Model && PARENT.get(value) === model)
333
334
  value.set(null);
334
335
  Object.freeze(state);
335
- };
336
+ }, null);
336
337
  }
337
338
  function fetch(subject, property, required) {
338
339
  const state = STATE.get(subject);
@@ -359,6 +360,25 @@ function fetch(subject, property, required) {
359
360
  stack: error.stack
360
361
  });
361
362
  }
363
+ var EXPORT;
364
+ function snapshot(model) {
365
+ const values = {};
366
+ let isNotRecursive;
367
+ if (!EXPORT) {
368
+ isNotRecursive = true;
369
+ EXPORT = /* @__PURE__ */ new Map([[model, values]]);
370
+ }
371
+ for (let [key2, value] of model) {
372
+ if (EXPORT.has(value))
373
+ value = EXPORT.get(value);
374
+ else if (value && typeof value == "object" && "get" in value && typeof value.get === "function")
375
+ EXPORT.set(value, value = value.get());
376
+ values[key2] = value;
377
+ }
378
+ if (isNotRecursive)
379
+ EXPORT = void 0;
380
+ return Object.freeze(values);
381
+ }
362
382
  function update(subject, key2, value, arg) {
363
383
  const state = STATE.get(subject);
364
384
  if (Object.isFrozen(state))
@@ -395,42 +415,23 @@ function event(subject, key2, silent) {
395
415
  if (!silent)
396
416
  emit(subject, key2);
397
417
  }
398
- function assign(to, values2) {
418
+ function assign(to, values) {
399
419
  const methods = METHODS.get(to.constructor);
400
- for (const key2 in values2)
420
+ for (const key2 in values)
401
421
  if (key2 in to || methods.has(key2))
402
- to[key2] = values2[key2];
403
- }
404
- function values(model) {
405
- const values2 = {};
406
- const current = !EXPORT && (EXPORT = /* @__PURE__ */ new Map([[model, values2]]));
407
- for (let [key2, value] of model) {
408
- if (EXPORT.has(value))
409
- value = EXPORT.get(value);
410
- else if (value && typeof value.get === "function")
411
- EXPORT.set(value, value = value.get());
412
- values2[key2] = value;
413
- }
414
- if (current)
415
- EXPORT = void 0;
416
- return Object.freeze(values2);
422
+ to[key2] = values[key2];
417
423
  }
418
424
  function uid() {
419
425
  return (Math.random() * 0.722 + 0.278).toString(36).substring(2, 8).toUpperCase();
420
426
  }
421
- function entries(object) {
422
- return Object.entries(
423
- Object.getOwnPropertyDescriptors(object)
424
- ).map(([key2, desc]) => ({ key: key2, ...desc }));
425
- }
426
427
 
427
428
  // src/context.ts
428
- var Register = /* @__PURE__ */ new WeakMap();
429
- var Table = /* @__PURE__ */ new Map();
429
+ var LOOKUP = /* @__PURE__ */ new WeakMap();
430
+ var KEYS = /* @__PURE__ */ new Map();
430
431
  function key(T, upstream) {
431
- let K = Table.get(T);
432
+ let K = KEYS.get(T);
432
433
  if (!K)
433
- Table.set(T, K = Symbol(
434
+ KEYS.set(T, K = Symbol(
434
435
  typeof T == "symbol" ? "get " + T.description : String(T)
435
436
  ));
436
437
  return upstream ? key(K) : K;
@@ -450,7 +451,7 @@ var Context = class _Context {
450
451
  this.include(inputs);
451
452
  }
452
453
  static get(from, callback) {
453
- const waiting = Register.get(from);
454
+ const waiting = LOOKUP.get(from);
454
455
  if (waiting instanceof _Context) {
455
456
  if (callback)
456
457
  callback(waiting);
@@ -460,11 +461,14 @@ var Context = class _Context {
460
461
  if (waiting)
461
462
  waiting.push(callback);
462
463
  else
463
- Register.set(from, [callback]);
464
+ LOOKUP.set(from, [callback]);
464
465
  }
465
- get(Type, callback) {
466
- if (callback)
467
- Object.defineProperty(this, key(Type, true), { value: callback });
466
+ /** Run callback when a specified type is registered to a **child** context. */
467
+ has(Type, callback) {
468
+ define(this, key(Type, true), { value: callback });
469
+ }
470
+ /** Find specified type registered to a parent context. Returns undefined if none are found. */
471
+ get(Type) {
468
472
  const result = this[key(Type)];
469
473
  if (result === null)
470
474
  throw new Error(`Did find ${Type} in context, but multiple were defined.`);
@@ -486,9 +490,16 @@ var Context = class _Context {
486
490
  }
487
491
  include(inputs, forEach) {
488
492
  const init2 = /* @__PURE__ */ new Map();
493
+ if (typeof inputs == "function" || inputs instanceof Model)
494
+ inputs = { 0: inputs };
489
495
  Object.entries(inputs).forEach(([K, V]) => {
490
- if (!(Model.is(V) || V instanceof Model))
491
- throw new Error(`Context can only include Model or instance but got ${V}${K && " as " + K}.`);
496
+ if (Model.is(V) || V instanceof Model)
497
+ return;
498
+ if (K && K != V)
499
+ V = `${V} (as '${K}')`;
500
+ throw new Error(
501
+ `Context may only include instance or class \`extends Model\` but got ${V}.`
502
+ );
492
503
  });
493
504
  for (const key2 in inputs) {
494
505
  const input = inputs[key2];
@@ -504,9 +515,11 @@ var Context = class _Context {
504
515
  }
505
516
  }
506
517
  for (const [model, explicit] of init2) {
507
- model.set();
508
- if (forEach)
518
+ emit(model, true);
519
+ if (typeof forEach == "function")
509
520
  forEach(model, explicit);
521
+ else if (forEach && explicit)
522
+ assign(model, forEach);
510
523
  for (const [_key, value] of model)
511
524
  if (PARENT.get(value) === model) {
512
525
  this.add(value, true);
@@ -537,15 +550,15 @@ var Context = class _Context {
537
550
  keys(T).forEach((K) => {
538
551
  const value = this.hasOwnProperty(K) ? null : I;
539
552
  if (value || this[K] !== I && !implicit)
540
- Object.defineProperty(this, K, {
553
+ define(this, K, {
541
554
  configurable: true,
542
555
  value
543
556
  });
544
557
  });
545
- const waiting = Register.get(I);
558
+ const waiting = LOOKUP.get(I);
546
559
  if (waiting instanceof Array)
547
560
  waiting.forEach((cb) => cb(this));
548
- Register.set(I, this);
561
+ LOOKUP.set(I, this);
549
562
  return I;
550
563
  }
551
564
  };
@@ -577,7 +590,8 @@ function use(arg1, arg2) {
577
590
  }
578
591
  Model.on((_, model) => {
579
592
  const state = STATE.get(model);
580
- for (const { key: key2, value } of entries(model)) {
593
+ for (const key2 in model) {
594
+ const { value } = Object.getOwnPropertyDescriptor(model, key2);
581
595
  const instruction = INSTRUCT.get(value);
582
596
  if (!instruction) {
583
597
  if (value instanceof Model && key2 !== "is")
@@ -592,7 +606,7 @@ Model.on((_, model) => {
592
606
  const desc = typeof output == "object" ? output : { get: output };
593
607
  if ("value" in desc)
594
608
  state[key2] = desc.value;
595
- Object.defineProperty(model, key2, {
609
+ define(model, key2, {
596
610
  enumerable: desc.enumerable !== false,
597
611
  get() {
598
612
  return watch(
@@ -645,7 +659,7 @@ function get(arg0, arg1) {
645
659
  }
646
660
  if (typeof arg1 == "function") {
647
661
  let connect2 = function(model) {
648
- reset = effect(model, (current) => {
662
+ reset = createEffect(model, (current) => {
649
663
  proxy = current;
650
664
  if (!(key2 in state) || STALE.delete(compute2))
651
665
  compute2(!reset);
@@ -689,7 +703,6 @@ function get(arg0, arg1) {
689
703
  }
690
704
 
691
705
  // src/instruction/ref.ts
692
- var define2 = Object.defineProperty;
693
706
  function ref(arg, arg2) {
694
707
  return use((key2, subject, state) => {
695
708
  let value = {};
@@ -697,20 +710,20 @@ function ref(arg, arg2) {
697
710
  subject.get(() => {
698
711
  for (const key3 in state)
699
712
  if (typeof arg2 == "function")
700
- define2(value, key3, {
713
+ define(value, key3, {
701
714
  configurable: true,
702
715
  get() {
703
716
  const out = arg2(key3);
704
- define2(value, key3, { value: out });
717
+ define(value, key3, { value: out });
705
718
  return out;
706
719
  }
707
720
  });
708
721
  else {
709
722
  const get2 = () => state[key3];
710
723
  const set2 = (value2) => subject.set(key3, value2);
711
- define2(set2, "current", { get: get2, set: set2 });
712
- define2(set2, "get", { value: get2 });
713
- define2(value, key3, { value: set2 });
724
+ define(set2, "current", { get: get2, set: set2 });
725
+ define(set2, "get", { value: get2 });
726
+ define(value, key3, { value: set2 });
714
727
  }
715
728
  });
716
729
  else if (typeof arg == "object")
@@ -735,7 +748,7 @@ function ref(arg, arg2) {
735
748
  get: { value: get2 }
736
749
  });
737
750
  }
738
- define2(subject, key2, { value });
751
+ define(subject, key2, { value });
739
752
  });
740
753
  }
741
754
 
@@ -822,7 +835,7 @@ function has(arg1, arg2) {
822
835
  };
823
836
  if (Model.is(arg1))
824
837
  Context.get(subject, (context) => {
825
- context.get(arg1, (got) => {
838
+ context.has(arg1, (got) => {
826
839
  let remove;
827
840
  let disconnect;
828
841
  if (applied.has(got))
@@ -885,8 +898,10 @@ function has(arg1, arg2) {
885
898
  export {
886
899
  Context,
887
900
  Model,
901
+ addListener,
902
+ createEffect,
903
+ createProxy,
888
904
  Model as default,
889
- effect,
890
905
  get,
891
906
  has,
892
907
  ref,
package/package.json CHANGED
@@ -32,6 +32,6 @@
32
32
  "jest": "^27.0.6",
33
33
  "tsup": "^8.0.2"
34
34
  },
35
- "version": "0.52.0",
36
- "gitHead": "b6b92114b8b0c69f1a36eae8c03fec2e068d03c4"
35
+ "version": "0.52.2-alpha.74+088f4ba5",
36
+ "gitHead": "088f4ba53762f0b185871570c58f349084eb29af"
37
37
  }