j-templates 9.0.0 → 10.0.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.
Files changed (57) hide show
  1. package/Node/vNode.js +2 -0
  2. package/SYNTAX_PRIMER.md +181 -101
  3. package/Store/Diff/channelClient.d.ts +106 -0
  4. package/Store/Diff/channelClient.js +228 -0
  5. package/Store/Diff/defaultDiffTreeWorker.js +3 -4
  6. package/Store/Diff/diffTree.d.ts +56 -0
  7. package/Store/Diff/diffTree.js +110 -4
  8. package/Store/Diff/diffTree.types.d.ts +191 -21
  9. package/Store/Diff/diffTree.types.js +2 -10
  10. package/Store/Diff/diffTreeEndpoint.d.ts +101 -0
  11. package/Store/Diff/diffTreeEndpoint.js +173 -0
  12. package/Store/Diff/diffTreeEndpoint.types.d.ts +71 -0
  13. package/Store/Diff/diffTreeEndpoint.types.js +1 -0
  14. package/Store/Diff/diffTreeHub.d.ts +139 -0
  15. package/Store/Diff/diffTreeHub.js +346 -0
  16. package/Store/Diff/hostPublisher.d.ts +39 -0
  17. package/Store/Diff/hostPublisher.js +151 -0
  18. package/Store/Diff/index.d.ts +3 -0
  19. package/Store/Diff/index.js +3 -0
  20. package/Store/Diff/observableDiffTree.d.ts +93 -0
  21. package/Store/Diff/observableDiffTree.js +199 -0
  22. package/Store/Diff/onConnect.d.ts +33 -0
  23. package/Store/Diff/onConnect.js +83 -0
  24. package/Store/Diff/utils/diffTree.types.d.ts +9 -0
  25. package/Store/Diff/utils/homes.d.ts +12 -0
  26. package/Store/Diff/utils/homes.js +40 -0
  27. package/Store/Diff/utils/routes.d.ts +18 -0
  28. package/Store/Diff/utils/routes.js +33 -2
  29. package/Store/Store/store.d.ts +36 -4
  30. package/Store/Store/store.js +47 -6
  31. package/Store/Store/storeAsync.d.ts +80 -25
  32. package/Store/Store/storeAsync.js +102 -33
  33. package/Store/Store/storeSync.d.ts +76 -11
  34. package/Store/Store/storeSync.js +105 -12
  35. package/Store/Tree/observableNode.js +68 -13
  36. package/Store/Tree/observableScope.d.ts +1 -0
  37. package/Store/Tree/observableScope.js +4 -0
  38. package/Store/Tree/opaque.d.ts +30 -0
  39. package/Store/Tree/opaque.js +35 -0
  40. package/Store/Tree/snapshot.js +5 -4
  41. package/Store/index.d.ts +4 -1
  42. package/Store/index.js +1 -1
  43. package/Utils/decorators.d.ts +0 -98
  44. package/Utils/decorators.js +0 -156
  45. package/Utils/index.d.ts +2 -0
  46. package/Utils/index.js +1 -0
  47. package/Utils/subscriptionManager.d.ts +78 -0
  48. package/Utils/subscriptionManager.js +162 -0
  49. package/package.json +1 -1
  50. package/Store/Diff/diffTreeWorker.d.ts +0 -6
  51. package/Store/Diff/diffTreeWorker.js +0 -22
  52. package/Store/Diff/diffTreeWriter.d.ts +0 -92
  53. package/Store/Diff/diffTreeWriter.js +0 -137
  54. package/Store/Diff/workerQueue.d.ts +0 -7
  55. package/Store/Diff/workerQueue.js +0 -29
  56. package/Store/Store/pushStore.d.ts +0 -53
  57. package/Store/Store/pushStore.js +0 -77
@@ -5,7 +5,6 @@
5
5
  * @module Utils/Decorators
6
6
  * @see ObservableScope
7
7
  * @see ObservableNode
8
- * @see StoreAsync
9
8
  * @see StoreSync
10
9
  * @see Component
11
10
  *
@@ -20,7 +19,6 @@
20
19
  * | arrays (User[], Todo[]) | @State | Array mutations tracked |
21
20
  * | getters only (expensive) | @Computed | Cached + object reuse via Store |
22
21
  * | getters only (simple) | @Scope | Cached, but new reference on update |
23
- * | getters only (sync, StoreAsync) | @ComputedAsync | Sync getter, StoreAsync caching + object reuse |
24
22
  * | async functions (direct scope) | ObservableScope.Create(async) | Direct async, new reference, initial null |
25
23
  * | subscribe to changes | @Watch | Calls method when scope value changes |
26
24
  * | dependency injection | @Inject | Gets value from component injector |
@@ -58,7 +56,6 @@
58
56
  */
59
57
  import { ObservableScope, PeekCallback, } from "../Store/Tree/observableScope";
60
58
  import { ObservableNode } from "../Store/Tree/observableNode";
61
- import { StoreAsync } from "../Store";
62
59
  /**
63
60
  * WeakMap for storing node instances and their associated properties.
64
61
  * @private
@@ -122,27 +119,6 @@ function GetDestroyArrayForPrototype(prototype, create = true) {
122
119
  }
123
120
  return array;
124
121
  }
125
- function CreateStoreScope(getter, store, defaultValue) {
126
- const getterScope = ObservableScope.Gated(function () {
127
- const value = getter();
128
- return ObservableNode.Clone(value);
129
- });
130
- ObservableScope.Watch(getterScope, (scope) => {
131
- const data = ObservableScope.Peek(scope);
132
- store.Write(data, "root");
133
- });
134
- const data = ObservableScope.Peek(getterScope);
135
- store.Write(data, "root");
136
- const propertyScope = ObservableScope.Create(() => {
137
- return store.Get("root", defaultValue);
138
- });
139
- ObservableScope.OnDestroyed(propertyScope, function () {
140
- ObservableScope.Destroy(getterScope);
141
- if (store instanceof StoreAsync)
142
- store.Destroy();
143
- });
144
- return propertyScope;
145
- }
146
122
  /**
147
123
  * Creates an observable scope that exposes a getter's value through a persistent
148
124
  * observable node, preserving the value's object identity across updates.
@@ -281,10 +257,8 @@ function CreateNodeScope(getter) {
281
257
  * - Use @Scope() for simple computations where object identity doesn't matter
282
258
  * - The diff computation overhead is justified when you need object reuse
283
259
  * @see {@link Scope} for simple getter-based reactive properties (caches but new reference)
284
- * @see {@link ComputedAsync} for async computed properties
285
260
  * @see {@link ObservableNode.Apply} for how diffs are applied in-place to maintain object identity
286
261
  * @see {@link ObservableScope} for the Gated scope mechanism (batched updates)
287
- * @see {@link StoreSync} for the store-backed variant used by @ComputedAsync
288
262
  */
289
263
  export function Computed() {
290
264
  return function (target, propertyKey, descriptor) {
@@ -320,134 +294,6 @@ function ComputedDecorator(target, prop, descriptor) {
320
294
  },
321
295
  };
322
296
  }
323
- /**
324
- * ComputedAsync decorator factory for creating synchronous computed properties with StoreAsync caching.
325
- * A computed property is derived from other properties and automatically updates when its dependencies change.
326
- *
327
- * IMPORTANT: Despite the name, @ComputedAsync expects a SYNC getter, NOT an async function.
328
- * The "Async" refers to the internal StoreAsync caching mechanism, not the getter signature.
329
- *
330
- * Use @ComputedAsync when you need synchronous caching with StoreAsync backend.
331
- * For direct async operations (no object reuse), use ObservableScope.Create(async () => ...) instead.
332
- *
333
- * @example
334
- * ```typescript
335
- * // ✅ Good: Synchronous getter with StoreAsync caching
336
- * @Value()
337
- * userId: string = "123";
338
- *
339
- * @ComputedAsync(null)
340
- * get userData(): User | null {
341
- * // Sync getter - returns value directly
342
- * return getUserSync(this.userId);
343
- * }
344
- * ```
345
- *
346
- * @example
347
- * ```typescript
348
- * // ❌ WRONG - Do NOT use async keyword or return Promise
349
- * @Value()
350
- * userId: string = "123";
351
- *
352
- * @ComputedAsync(null)
353
- * async getUser(): Promise<User> { // ERROR: getter must be synchronous!
354
- * return await fetch(`/api/users/${this.userId}`);
355
- * }
356
- *
357
- * // ✅ CORRECT - Use ObservableScope.Create for direct async operations
358
- * import { ObservableScope } from "j-templates/Store";
359
- *
360
- * @Value()
361
- * userId: string = "123";
362
- *
363
- * private userDataScope = ObservableScope.Create(async () => {
364
- * return await fetch(`/api/users/${this.userId}`); // Async supported here!
365
- * });
366
- *
367
- * get userData(): User | null {
368
- * return ObservableScope.Value(this.userDataScope); // null while loading
369
- * }
370
- * ```
371
- *
372
- * @param defaultValue The default value to be used if the computed property is not defined.
373
- * @returns A property decorator that can be applied to a getter method.
374
- * @throws Will throw an error if the property is not a getter or if it has a setter.
375
- * @remarks
376
- * The @ComputedAsync decorator uses StoreAsync for caching. The getter must be a synchronous function.
377
- * Unlike @Computed which uses StoreSync, @ComputedAsync uses StoreAsync internally for caching.
378
- *
379
- * **Initialization**: @ComputedAsync uses lazy initialization - the scopes are created on first access:
380
- * ```typescript
381
- * @ComputedAsync(null)
382
- * get userData(): User | null {
383
- * return getUserSync(this.userId);
384
- * }
385
- *
386
- * // Scope created here, on first access:
387
- * const user = this.userData;
388
- * ```
389
- *
390
- * **Caching**: Like @Computed, @ComputedAsync caches the result and maintains object identity
391
- * through diff-based updates. The getter only runs when dependencies change.
392
- *
393
- * **Object Reuse**: Like @Computed, the result maintains its identity across updates.
394
- * Only changed properties are modified in-place, preserving object references.
395
- *
396
- * **Key difference from @Computed**: Uses StoreAsync instead of StoreSync for caching.
397
- * Both use synchronous getters and provide object identity preservation.
398
- *
399
- * **Comparison**:
400
- * | Aspect | @Scope | @Computed | @ComputedAsync | scope(async) + @Scope |
401
- * |--------|--------|-----------|----------------|----------------------|
402
- * | Caches value | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes (memoized) |
403
- * | Getter type | Sync | Sync | Sync | Async supported |
404
- * | Object identity | ❌ New | ✅ Same | ✅ Same | ❌ New |
405
- * | Store backend | Single scope | StoreSync | StoreAsync | Direct scope |
406
- * | Initial value | First access | First access | defaultValue | Promise |
407
- * | Best for | Simple sync, async | Complex sync | StoreAsync sync | Component async ops |
408
- *
409
- * @see {@link Computed} for synchronous computed properties with StoreSync caching
410
- * @see {@link Scope} for simple getter-based reactive properties (caches, new reference)
411
- * @see {@link StoreAsync} for async store implementation
412
- * @see {@link ObservableScope.Create} for async function support
413
- */
414
- export function ComputedAsync(defaultValue) {
415
- return function (target, propertyKey, descriptor) {
416
- return ComputedAsyncDecorator(target, propertyKey, descriptor, defaultValue);
417
- };
418
- }
419
- /**
420
- * ComputedAsync decorator implementation for creating asynchronous computed properties.
421
- * @private
422
- * @param target The target object.
423
- * @param prop The property key.
424
- * @param descriptor The property descriptor.
425
- * @param defaultValue The default value to be used if the computed property is not defined.
426
- * @returns A property descriptor that replaces the original descriptor with a computed implementation.
427
- * @throws Will throw an error if the property is not a getter or if it has a setter.
428
- */
429
- function ComputedAsyncDecorator(target, prop, descriptor, defaultValue) {
430
- const propertyKey = prop;
431
- if (!(descriptor && descriptor.get))
432
- throw "Computed decorator requires a getter";
433
- if (descriptor && descriptor.set)
434
- throw "Computed decorator does not support setters";
435
- const getter = descriptor.get;
436
- return {
437
- configurable: false,
438
- enumerable: true,
439
- get: function () {
440
- const scopeMap = GetScopeMapForInstance(this);
441
- if (scopeMap[propertyKey] === undefined) {
442
- const worker = new Worker(new URL("../Store/Diff/defaultDiffTreeWorker.js", import.meta.url), { type: "module" });
443
- const storeAsync = new StoreAsync(worker);
444
- const propertyScope = CreateStoreScope(getter.bind(this), storeAsync, defaultValue);
445
- scopeMap[propertyKey] = [propertyScope, undefined];
446
- }
447
- return ObservableScope.Value(scopeMap[propertyKey][0]);
448
- },
449
- };
450
- }
451
297
  /**
452
298
  * State decorator factory for creating reactive properties backed by ObservableNode proxies.
453
299
  *
@@ -749,7 +595,6 @@ function ValueDecorator(target, propertyKey) {
749
595
  * | Best for | Primitives, cheap ops, async ops | Complex objects, expensive ops |
750
596
  *
751
597
  * @see {@link Computed} for cached computed properties with object reuse
752
- * @see {@link ComputedAsync} for sync getters with StoreAsync backend
753
598
  * @see {@link ObservableNode.Apply} for how @Computed maintains object identity
754
599
  * @see {@link ObservableScope} for the scope-based reactivity system
755
600
  * @see {@link scope} for inline computed scopes within @Scope
@@ -1118,7 +963,6 @@ export var Bound;
1118
963
  * - All @Value-decorated property scopes
1119
964
  * - All @Scope-decorated property scopes
1120
965
  * - All @Computed-decorated property scopes (both getter and property scopes)
1121
- * - All @ComputedAsync-decorated property scopes (including StoreAsync)
1122
966
  * - All @Destroy-marked properties (calls .Destroy() method)
1123
967
  * - All @Watch subscriptions (via scope destruction)
1124
968
  *
package/Utils/index.d.ts CHANGED
@@ -2,3 +2,5 @@ export * from "./decorators";
2
2
  export * from "./animation";
3
3
  export { Injector } from "./injector";
4
4
  export { IDestroyable } from "./utils.types";
5
+ export { SubscriptionManager } from "./subscriptionManager";
6
+ export type { KeyFilter, ISubscriptionMatch } from "./subscriptionManager";
package/Utils/index.js CHANGED
@@ -1,3 +1,4 @@
1
1
  export * from "./decorators";
2
2
  export * from "./animation";
3
3
  export { Injector } from "./injector";
4
+ export { SubscriptionManager } from "./subscriptionManager";
@@ -0,0 +1,78 @@
1
+ /**
2
+ * A key filter: exact keys, and patterns ending in `*` that match every key starting with the text
3
+ * before it (`task_*` matches `task_1`). Undefined matches every key; an empty array matches none.
4
+ */
5
+ export type KeyFilter = readonly string[] | undefined;
6
+ /** A subscription that matched some changed keys, and the keys it matched */
7
+ export interface ISubscriptionMatch<S> {
8
+ id: number;
9
+ subscription: S;
10
+ keys: string[];
11
+ }
12
+ /**
13
+ * Subscriptions to keys, for any use: it matches keys to subscriptions and counts who holds a
14
+ * key, and knows nothing about what a key's value is or how it's delivered.
15
+ *
16
+ * - **Exact keys hold a key; patterns don't.** `SubscriberCount(key)` counts the subscriptions
17
+ * naming the key exactly, and `OnReleased` reports when the last of them ends. A pattern
18
+ * subscription matches keys without holding them, or a `whatif_*` subscriber would keep every key.
19
+ * - **Groups end together.** A subscription can name a group (to a host, a channel), and
20
+ * `Disconnect(group)` ends every subscription in it.
21
+ * - Callbacks run synchronously, inside the call that caused them.
22
+ *
23
+ * @template S What a subscription carries, e.g. its callback and form.
24
+ */
25
+ export declare class SubscriptionManager<S> {
26
+ private entries;
27
+ private nextId;
28
+ /** Exact key -> the subscriptions naming it */
29
+ private counts;
30
+ private groups;
31
+ private released;
32
+ /**
33
+ * Adds a subscription.
34
+ * @param keys The keys it matches, see `KeyFilter`.
35
+ * @param subscription What the subscription carries.
36
+ * @param options `group`: the group `Disconnect` ends it with.
37
+ * @returns Its id.
38
+ */
39
+ Subscribe(keys: KeyFilter, subscription: S, options?: {
40
+ group?: unknown;
41
+ }): number;
42
+ /**
43
+ * Ends a subscription. Each exact key it was the last to name is released.
44
+ * @returns Whether it existed.
45
+ */
46
+ Unsubscribe(id: number): boolean;
47
+ /** Ends every subscription in a group */
48
+ Disconnect(group: unknown): void;
49
+ /** Whether a subscription exists */
50
+ Has(id: number): boolean;
51
+ /** What a subscription carries, or undefined when it doesn't exist */
52
+ Get(id: number): S | undefined;
53
+ /**
54
+ * The keys a subscription matches, in the order given.
55
+ * @returns The keys, or an empty array when the subscription doesn't exist.
56
+ */
57
+ MatchingKeys(id: number, keys: Iterable<string>): string[];
58
+ /**
59
+ * Each subscription that matches any of the keys, with the keys it matches, in subscription
60
+ * order. Subscriptions that match none are left out.
61
+ */
62
+ Matching(keys: Iterable<string>): ISubscriptionMatch<S>[];
63
+ /**
64
+ * Whether a subscription matches every key, with no filter, so a caller can hand it the whole
65
+ * of something without filtering it.
66
+ */
67
+ MatchesAll(id: number): boolean;
68
+ /**
69
+ * Calls back when a key matching a pattern loses its last exact subscription, inside the
70
+ * `Unsubscribe` or `Disconnect` that ended it. A key never named exactly is never released.
71
+ * @param pattern An exact key or a pattern ending in `*`.
72
+ * @returns A function that removes the callback.
73
+ */
74
+ OnReleased(pattern: string, callback: (key: string) => void): () => void;
75
+ /** How many subscriptions name a key exactly now; patterns don't count */
76
+ SubscriberCount(key: string): number;
77
+ private Release;
78
+ }
@@ -0,0 +1,162 @@
1
+ const WILDCARD = "*";
2
+ /**
3
+ * Subscriptions to keys, for any use: it matches keys to subscriptions and counts who holds a
4
+ * key, and knows nothing about what a key's value is or how it's delivered.
5
+ *
6
+ * - **Exact keys hold a key; patterns don't.** `SubscriberCount(key)` counts the subscriptions
7
+ * naming the key exactly, and `OnReleased` reports when the last of them ends. A pattern
8
+ * subscription matches keys without holding them, or a `whatif_*` subscriber would keep every key.
9
+ * - **Groups end together.** A subscription can name a group (to a host, a channel), and
10
+ * `Disconnect(group)` ends every subscription in it.
11
+ * - Callbacks run synchronously, inside the call that caused them.
12
+ *
13
+ * @template S What a subscription carries, e.g. its callback and form.
14
+ */
15
+ export class SubscriptionManager {
16
+ constructor() {
17
+ this.entries = new Map();
18
+ this.nextId = 1;
19
+ /** Exact key -> the subscriptions naming it */
20
+ this.counts = new Map();
21
+ this.groups = new Map();
22
+ this.released = [];
23
+ }
24
+ /**
25
+ * Adds a subscription.
26
+ * @param keys The keys it matches, see `KeyFilter`.
27
+ * @param subscription What the subscription carries.
28
+ * @param options `group`: the group `Disconnect` ends it with.
29
+ * @returns Its id.
30
+ */
31
+ Subscribe(keys, subscription, options = {}) {
32
+ const id = this.nextId++;
33
+ const entry = { subscription, all: keys === undefined, exact: new Set(), prefixes: [], group: options.group };
34
+ if (keys)
35
+ for (let x = 0; x < keys.length; x++) {
36
+ const key = keys[x];
37
+ if (key.endsWith(WILDCARD))
38
+ entry.prefixes.push(key.slice(0, -1));
39
+ else
40
+ entry.exact.add(key);
41
+ }
42
+ this.entries.set(id, entry);
43
+ for (const key of entry.exact)
44
+ this.counts.set(key, (this.counts.get(key) ?? 0) + 1);
45
+ if (options.group !== undefined) {
46
+ let ids = this.groups.get(options.group);
47
+ ids || this.groups.set(options.group, ids = new Set());
48
+ ids.add(id);
49
+ }
50
+ return id;
51
+ }
52
+ /**
53
+ * Ends a subscription. Each exact key it was the last to name is released.
54
+ * @returns Whether it existed.
55
+ */
56
+ Unsubscribe(id) {
57
+ const entry = this.entries.get(id);
58
+ if (!entry)
59
+ return false;
60
+ this.entries.delete(id);
61
+ if (entry.group !== undefined) {
62
+ const ids = this.groups.get(entry.group);
63
+ ids.delete(id);
64
+ ids.size === 0 && this.groups.delete(entry.group);
65
+ }
66
+ const releasedKeys = [];
67
+ for (const key of entry.exact) {
68
+ const count = this.counts.get(key) - 1;
69
+ if (count > 0)
70
+ this.counts.set(key, count);
71
+ else {
72
+ this.counts.delete(key);
73
+ releasedKeys.push(key);
74
+ }
75
+ }
76
+ for (const key of releasedKeys)
77
+ this.Release(key);
78
+ return true;
79
+ }
80
+ /** Ends every subscription in a group */
81
+ Disconnect(group) {
82
+ const ids = this.groups.get(group);
83
+ if (ids)
84
+ for (const id of Array.from(ids))
85
+ this.Unsubscribe(id);
86
+ }
87
+ /** Whether a subscription exists */
88
+ Has(id) {
89
+ return this.entries.has(id);
90
+ }
91
+ /** What a subscription carries, or undefined when it doesn't exist */
92
+ Get(id) {
93
+ return this.entries.get(id)?.subscription;
94
+ }
95
+ /**
96
+ * The keys a subscription matches, in the order given.
97
+ * @returns The keys, or an empty array when the subscription doesn't exist.
98
+ */
99
+ MatchingKeys(id, keys) {
100
+ const entry = this.entries.get(id);
101
+ const matched = [];
102
+ if (entry)
103
+ for (const key of keys)
104
+ Matches(entry, key) && matched.push(key);
105
+ return matched;
106
+ }
107
+ /**
108
+ * Each subscription that matches any of the keys, with the keys it matches, in subscription
109
+ * order. Subscriptions that match none are left out.
110
+ */
111
+ Matching(keys) {
112
+ const list = Array.isArray(keys) ? keys : Array.from(keys);
113
+ const matches = [];
114
+ for (const [id, entry] of this.entries) {
115
+ const matched = entry.all ? list : list.filter(key => Matches(entry, key));
116
+ matched.length > 0 && matches.push({ id, subscription: entry.subscription, keys: matched });
117
+ }
118
+ return matches;
119
+ }
120
+ /**
121
+ * Whether a subscription matches every key, with no filter, so a caller can hand it the whole
122
+ * of something without filtering it.
123
+ */
124
+ MatchesAll(id) {
125
+ return this.entries.get(id)?.all === true;
126
+ }
127
+ /**
128
+ * Calls back when a key matching a pattern loses its last exact subscription, inside the
129
+ * `Unsubscribe` or `Disconnect` that ended it. A key never named exactly is never released.
130
+ * @param pattern An exact key or a pattern ending in `*`.
131
+ * @returns A function that removes the callback.
132
+ */
133
+ OnReleased(pattern, callback) {
134
+ const entry = { pattern, callback };
135
+ this.released.push(entry);
136
+ return () => Remove(this.released, entry);
137
+ }
138
+ /** How many subscriptions name a key exactly now; patterns don't count */
139
+ SubscriberCount(key) {
140
+ return this.counts.get(key) ?? 0;
141
+ }
142
+ Release(key) {
143
+ for (const { pattern, callback } of this.released.slice())
144
+ if (PatternMatches(pattern, key))
145
+ callback(key);
146
+ }
147
+ }
148
+ function Matches(entry, key) {
149
+ if (entry.all || entry.exact.has(key))
150
+ return true;
151
+ for (let x = 0; x < entry.prefixes.length; x++)
152
+ if (key.startsWith(entry.prefixes[x]))
153
+ return true;
154
+ return false;
155
+ }
156
+ function PatternMatches(pattern, key) {
157
+ return pattern.endsWith(WILDCARD) ? key.startsWith(pattern.slice(0, -1)) : pattern === key;
158
+ }
159
+ function Remove(list, item) {
160
+ const index = list.indexOf(item);
161
+ index >= 0 && list.splice(index, 1);
162
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "j-templates",
3
- "version": "9.0.0",
3
+ "version": "10.0.0",
4
4
  "description": "j-templates",
5
5
  "license": "MIT",
6
6
  "repository": "https://github.com/TypesInCode/jTemplates",
@@ -1,6 +0,0 @@
1
- import { DiffTree } from "./diffTree";
2
- /**
3
- * Runs each batch of method calls a `StoreAsync` sends on the tree, then its projections, and
4
- * posts back the tree's `Commit()` diff for the store to apply.
5
- */
6
- export declare function ConnectWorkerToDiffTree(diffTree: DiffTree, worker: Worker): void;
@@ -1,22 +0,0 @@
1
- /**
2
- * Runs each batch of method calls a `StoreAsync` sends on the tree, then its projections, and
3
- * posts back the tree's `Commit()` diff for the store to apply.
4
- */
5
- export function ConnectWorkerToDiffTree(diffTree, worker) {
6
- worker.onmessage = function (event) {
7
- const data = event.data;
8
- for (let x = 0; x < data.length; x++)
9
- RunMethod(diffTree, data[x]);
10
- diffTree.RunProjections();
11
- worker.postMessage(diffTree.Commit());
12
- };
13
- }
14
- function RunMethod(diffTree, call) {
15
- const args = call.arguments;
16
- switch (call.method) {
17
- case "writepath": return diffTree.WritePath(args[0], args[1]);
18
- case "splicepath": return diffTree.SplicePath(args[0], args[1], args[2], args[3]);
19
- case "pushpath": return diffTree.PushPath(args[0], args[1]);
20
- case "patchpath": return diffTree.PatchPath(args[0], args[1]);
21
- }
22
- }
@@ -1,92 +0,0 @@
1
- import { JsonDiffResult } from "../../Utils/json";
2
- import { DiffTree } from "./diffTree";
3
- import { IKeyFunc, IRouteDefinitions } from "./utils/diffTree.types";
4
- /**
5
- * A `DiffTree` that pushes its changes to a `PushStore` instead of answering a store's
6
- * requests. Use it inside a worker that produces data itself (for example, the result of a
7
- * computation it runs): write with the usual `DiffTree` methods, then call `Commit()` to run
8
- * projections and post everything since the last commit as one message.
9
- *
10
- * - Push only: don't pass a `DiffTreeWriter` to `ConnectWorkerToDiffTree`, which commits
11
- * every batch itself and would post it to the wrong place.
12
- * - After a change fails, every later change before `Commit()` throws that same error. The
13
- * changes made before the failure are already in the tree, so `Commit()` still sends them,
14
- * followed by a `DIFF_TREE_ERROR` message. Projections aren't run for that batch; the next
15
- * `Commit()` brings them up to date.
16
- * - A `PushStore` only receives the changes committed after it starts listening, so
17
- * create it before the writer's first `Commit()`.
18
- *
19
- * @example
20
- * // engine-worker.ts
21
- * const tree = new DiffTreeWriter(self, undefined, { "/": { projections } });
22
- * tree.WritePath("results", computeResults());
23
- * tree.Commit();
24
- *
25
- * // main thread
26
- * const store = new PushStore(worker);
27
- *
28
- * @see PushStore
29
- */
30
- export declare class DiffTreeWriter extends DiffTree {
31
- private worker;
32
- private uncommittedError;
33
- /**
34
- * Creates a DiffTreeWriter.
35
- * @param worker Where `Commit()` posts its messages: the worker's `self`, or a `MessagePort`.
36
- * On the same thread as the store, anything whose `postMessage` dispatches a `"message"`
37
- * event on the store's `EventTarget`. The store clones every value it applies, so it never
38
- * shares objects with the writer's tree.
39
- * @param keyFunc Optional function to extract a key from objects. Pass the same key logic to
40
- * the `PushStore` that receives the changes.
41
- * @param routes Optional route definitions. Projections run once per `Commit()`, after the
42
- * batch's changes.
43
- */
44
- constructor(worker: {
45
- postMessage(message: unknown): void;
46
- }, keyFunc?: IKeyFunc, routes?: IRouteDefinitions);
47
- /**
48
- * Not available on a DiffTreeWriter: `Commit()` runs projections once per batch, after its
49
- * changes. Running them earlier would push a projection result computed from part of a batch.
50
- * @throws Always.
51
- */
52
- RunProjections(): void;
53
- /**
54
- * Writes a value at a path.
55
- * @throws The change's error, or an earlier uncommitted change's error.
56
- */
57
- WritePath(path: string, value: any): void;
58
- /**
59
- * Merges a patch into the current value at a path.
60
- * @throws The change's error, or an earlier uncommitted change's error.
61
- */
62
- PatchPath(path: string, patch: any): void;
63
- /**
64
- * Appends items to the array at a path.
65
- * @throws The change's error, or an earlier uncommitted change's error.
66
- */
67
- PushPath(path: string, items: unknown[]): void;
68
- /**
69
- * Splices the array at a path.
70
- * @throws The change's error, or an earlier uncommitted change's error.
71
- */
72
- SplicePath(path: string, start: number, deleteCount: number, items: any[]): void;
73
- /**
74
- * Deletes a route, its child routes and its route instances.
75
- * @throws The change's error, or an earlier uncommitted change's error.
76
- */
77
- DeleteRoute(routePath: string): void;
78
- /**
79
- * Runs projections and posts every change since the last commit as one `DIFF_TREE_EVENT`
80
- * message, which a `PushStore` applies as a unit. Posts nothing when nothing changed.
81
- *
82
- * If a change in the batch failed, projections are skipped, the applied changes are still
83
- * posted, and a `DIFF_TREE_ERROR` message follows with the error; `Commit()` itself doesn't
84
- * throw, since the failed change already did. If a projection throws, the batch's changes
85
- * and the error are posted the same way, then `Commit()` rethrows. Either way the next batch
86
- * starts clean.
87
- * @returns The diff that was posted.
88
- * @throws A projection's error.
89
- */
90
- Commit(): JsonDiffResult;
91
- private Guard;
92
- }