@mmstack/worker 22.0.0 → 22.2.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mmstack/worker",
3
- "version": "22.0.0",
3
+ "version": "22.2.0",
4
4
  "keywords": [
5
5
  "angular",
6
6
  "signals",
@@ -19,7 +19,7 @@
19
19
  "peerDependencies": {
20
20
  "@angular/core": ">=22 <23",
21
21
  "@angular/common": ">=22 <23",
22
- "@mmstack/primitives": ">=22.5.1 <23"
22
+ "@mmstack/primitives": ">=22.8 <23"
23
23
  },
24
24
  "sideEffects": false,
25
25
  "module": "fesm2022/mmstack-worker.mjs",
@@ -41,6 +41,13 @@ type WorkerHost<M extends WorkerSchema = WorkerSchema> = HasSchema<M> & {
41
41
  readonly hostId: string;
42
42
  /** Serve an additional transport (multi-client / tests). Returns a disconnect handle. */
43
43
  connect(port: WorkerPortLike): () => void;
44
+ /**
45
+ * Apply an AUTHORITATIVE owner correction to an owned store: writes made inside `fn` are stamped
46
+ * at a bumped epoch, so they deterministically win the merge against any concurrent replica write
47
+ * (owner authority rides the epoch fold). Readers can gate effects on owner-settled values by the
48
+ * winning op's epoch. No-op for a read-only (published) or unknown store.
49
+ */
50
+ override(store: string, fn: () => void): void;
44
51
  /**
45
52
  * Synchronously emit any pending owned-store changes to subscribers NOW, instead of waiting for
46
53
  * the microtask driver. Makes the mirror deterministic (tests call it before asserting) and is
@@ -1,4 +1,4 @@
1
- import { OpBatch, StoreOp } from '@mmstack/primitives';
1
+ import { OpSyncCheckpoint, OpEnvelope } from '@mmstack/primitives';
2
2
 
3
3
  /**
4
4
  * A structured-clone-safe rendering of an `Error` for cross-thread propagation. A worker throw
@@ -24,7 +24,7 @@ type RemoteStatus = 'idle' | 'loading' | 'reloading' | 'resolved' | 'error';
24
24
  /**
25
25
  * Every message exchanged over a {@link WorkerPortLike}. One discriminated union shared by the
26
26
  * main-thread client and the worker host (and, later, `@mmstack/mesh`), so the two sides can never
27
- * drift. Grouped: lifecycle · tasks (rung 1) · stores (rung 2) · remote status (rung 3).
27
+ * drift.
28
28
  */
29
29
  type WorkerEnvelope = {
30
30
  type: 'hello';
@@ -64,30 +64,13 @@ type WorkerEnvelope = {
64
64
  store: string;
65
65
  clientId: string;
66
66
  } | {
67
- type: 'store:snapshot';
67
+ type: 'store:checkpoint';
68
68
  store: string;
69
- version: number;
70
- value: unknown;
71
- } | {
72
- type: 'store:ops';
73
- store: string;
74
- batch: OpBatch;
69
+ checkpoint: OpSyncCheckpoint;
75
70
  } | {
76
- type: 'store:write';
71
+ type: 'store:sync';
77
72
  store: string;
78
- writeId: number;
79
- clientId: string;
80
- ops: readonly StoreOp[];
81
- } | {
82
- type: 'store:write:ack';
83
- store: string;
84
- writeId: number;
85
- version: number;
86
- } | {
87
- type: 'store:write:error';
88
- store: string;
89
- writeId: number;
90
- error: SerializedError;
73
+ env: OpEnvelope;
91
74
  } | {
92
75
  type: 'store:unsubscribe';
93
76
  store: string;
@@ -1,7 +1,7 @@
1
1
  import { WorkerSchema, HasSchema, TaskInput, TaskOutput, WorkerEnvelope, WorkerPortLike, SchemaOf, StoreKeys, StoreValueOf, IsWritableKey } from '@mmstack/worker/protocol';
2
2
  export * from '@mmstack/worker/protocol';
3
3
  import { Injector, Signal, ValueEqualityFn, ResourceStatus } from '@angular/core';
4
- import { SignalStore, WritableSignalStore } from '@mmstack/primitives';
4
+ import { WritableSignalStore, SignalStore } from '@mmstack/primitives';
5
5
 
6
6
  /** What the worker advertised in its `ready` handshake. */
7
7
  type WorkerManifest = {
@@ -117,16 +117,21 @@ type WorkerStoreOptions<T> = {
117
117
  /** The write path — present only on OWNED (writable) subtrees; absent on published (read-only) ones. */
118
118
  type WorkerStoreWrite<T> = {
119
119
  /**
120
- * Route a write to the OWNER: run `recipe` against a writable draft of the current replica, diff
121
- * it to ops, ship them. Resolves once the owner's authoritative batch carrying this write has been
122
- * applied to THIS replica (honest async the value is not applied locally first; for optimistic
123
- * UI, fork the replica and reconcile on resolve). Rejects if the owner reports a write error.
120
+ * Route a write to the OWNER: `recipe` applies optimistically to the store, its diff ships as a
121
+ * stamped op envelope, and the owner folds it in and echoes it back. Resolves once the owner has
122
+ * acknowledged it (its echo reconciles this replica). The owner can override with a higher-epoch
123
+ * correction that wins the merge. To hide the value until the owner confirms it, fork the store
124
+ * and reveal on resolve. Rejects if the worker is disconnected or the store is read-only.
124
125
  */
125
126
  write(recipe: (draft: WritableSignalStore<T>) => void): Promise<void>;
126
127
  };
127
128
  type WorkerStoreRef<T, W extends boolean = true> = {
128
- /** The live, READ-ONLY replica — deep per-leaf reads, mirrored from the worker-owned subtree. */
129
- readonly store: SignalStore<T>;
129
+ /**
130
+ * The live store. For an OWNED subtree it is a full {@link WritableSignalStore}: writes apply
131
+ * optimistically and route to the owner, and other op-log readers (`meshSync`, `persist`) can
132
+ * attach to it. For a PUBLISHED subtree it is a read-only {@link SignalStore} mirror.
133
+ */
134
+ readonly store: W extends true ? WritableSignalStore<T> : SignalStore<T>;
130
135
  /** The replica root value (undefined before hydration). */
131
136
  readonly value: Signal<T | undefined>;
132
137
  readonly status: Signal<ResourceStatus>;
@@ -138,11 +143,13 @@ type WorkerStoreRef<T, W extends boolean = true> = {
138
143
  destroy(): void;
139
144
  } & (W extends true ? WorkerStoreWrite<T> : object);
140
145
  /**
141
- * A live READ-ONLY replica of a store subtree OWNED by the worker. Subscribes over the
142
- * {@link WorkerRef}, hydrates from the owner's snapshot, then applies each authoritative op batch
143
- * into a main-thread `store` one `set` per batch, so a batch of N ops is a single notification
144
- * wave. Satisfies `ResourceLike`/`UseSource`, so it participates in transition scopes and nests in
145
- * `latest()`/`use()`. Writes are not local: they route to the owner (added in the next step).
146
+ * A live replica of a store subtree OWNED by the worker. It runs its own `opSync` over the worker
147
+ * transport: it hydrates from the owner's checkpoint, then folds each owner (or peer) envelope into
148
+ * a main-thread `store` convergently. For an owned subtree the store is WRITABLE: a write applies
149
+ * optimistically and its stamped envelope routes to the owner (which folds it and echoes it back),
150
+ * and any op-log reader `meshSync`, `persist` — can attach to the same store, so a persisted,
151
+ * meshed, worker-owned graph is just extra readers. Satisfies `ResourceLike`/`UseSource`, so it
152
+ * participates in transition scopes and nests in `latest()`/`use()`.
146
153
  */
147
154
  declare function workerStore<M extends WorkerSchema, K extends StoreKeys<M>>(worker: WorkerRef<M>, key: K, opt?: WorkerStoreOptions<StoreValueOf<M, K> & object>): WorkerStoreRef<StoreValueOf<M, K> & object, IsWritableKey<M, K>>;
148
155
  declare function workerStore<T extends object>(worker: WorkerRef, key: string, opt?: WorkerStoreOptions<T>): WorkerStoreRef<T, true>;