@earendil-works/chord 0.87.0 → 0.99.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 (99) hide show
  1. package/README.md +80 -27
  2. package/dist/api.d.ts +6 -1
  3. package/dist/api.d.ts.map +1 -1
  4. package/dist/api.js +8 -3
  5. package/dist/api.js.map +1 -1
  6. package/dist/bundler.d.ts.map +1 -1
  7. package/dist/context/index.d.ts.map +1 -1
  8. package/dist/context/index.js.map +1 -1
  9. package/dist/delta/apply-immutable-trusted.d.ts +4 -0
  10. package/dist/delta/apply-immutable-trusted.d.ts.map +1 -0
  11. package/dist/delta/apply-immutable-trusted.js +127 -0
  12. package/dist/delta/apply-immutable-trusted.js.map +1 -0
  13. package/dist/{state → delta}/diff.d.ts +1 -1
  14. package/dist/delta/diff.d.ts.map +1 -0
  15. package/dist/delta/diff.js +433 -0
  16. package/dist/delta/diff.js.map +1 -0
  17. package/dist/delta/draft.d.ts +5 -0
  18. package/dist/delta/draft.d.ts.map +1 -0
  19. package/dist/delta/draft.js +2 -0
  20. package/dist/delta/draft.js.map +1 -0
  21. package/dist/delta/index.d.ts +11 -21
  22. package/dist/delta/index.d.ts.map +1 -1
  23. package/dist/delta/index.js +43 -1341
  24. package/dist/delta/index.js.map +1 -1
  25. package/dist/delta/revision-validator.d.ts +7 -0
  26. package/dist/delta/revision-validator.d.ts.map +1 -0
  27. package/dist/delta/revision-validator.js +71 -0
  28. package/dist/delta/revision-validator.js.map +1 -0
  29. package/dist/delta/tracker.d.ts +26 -0
  30. package/dist/delta/tracker.d.ts.map +1 -0
  31. package/dist/delta/tracker.js +2069 -0
  32. package/dist/delta/tracker.js.map +1 -0
  33. package/dist/facets/host.d.ts.map +1 -1
  34. package/dist/facets/host.js.map +1 -1
  35. package/dist/facets/loader.d.ts.map +1 -1
  36. package/dist/facets/loader.js.map +1 -1
  37. package/dist/index.d.ts +3 -3
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js +1 -1
  40. package/dist/index.js.map +1 -1
  41. package/dist/json.d.ts +6 -0
  42. package/dist/json.d.ts.map +1 -1
  43. package/dist/json.js +73 -6
  44. package/dist/json.js.map +1 -1
  45. package/dist/node/bundle-loader.d.ts.map +1 -1
  46. package/dist/node/bundle-loader.js.map +1 -1
  47. package/dist/node/bundle.d.ts.map +1 -1
  48. package/dist/node/bundle.js.map +1 -1
  49. package/dist/node/manifest.d.ts.map +1 -1
  50. package/dist/node/package.d.ts.map +1 -1
  51. package/dist/node/package.js.map +1 -1
  52. package/dist/node.d.ts.map +1 -1
  53. package/dist/services/consumer.d.ts.map +1 -1
  54. package/dist/services/consumer.js +49 -1
  55. package/dist/services/consumer.js.map +1 -1
  56. package/dist/services/errors.d.ts.map +1 -1
  57. package/dist/services/errors.js.map +1 -1
  58. package/dist/services/handle.d.ts.map +1 -1
  59. package/dist/services/handle.js +3 -1
  60. package/dist/services/handle.js.map +1 -1
  61. package/dist/services/instances.d.ts +1 -0
  62. package/dist/services/instances.d.ts.map +1 -1
  63. package/dist/services/instances.js +3 -0
  64. package/dist/services/instances.js.map +1 -1
  65. package/dist/services/loopback.d.ts.map +1 -1
  66. package/dist/services/loopback.js.map +1 -1
  67. package/dist/services/provider.d.ts.map +1 -1
  68. package/dist/services/provider.js +48 -29
  69. package/dist/services/provider.js.map +1 -1
  70. package/dist/services/state-codec.d.ts.map +1 -1
  71. package/dist/services/state-codec.js +18 -0
  72. package/dist/services/state-codec.js.map +1 -1
  73. package/dist/services/state-internals.d.ts +5 -2
  74. package/dist/services/state-internals.d.ts.map +1 -1
  75. package/dist/services/state-internals.js.map +1 -1
  76. package/dist/services/state.d.ts +8 -3
  77. package/dist/services/state.d.ts.map +1 -1
  78. package/dist/services/state.js +288 -88
  79. package/dist/services/state.js.map +1 -1
  80. package/dist/services/wire.d.ts +3 -0
  81. package/dist/services/wire.d.ts.map +1 -1
  82. package/dist/services/wire.js +13 -0
  83. package/dist/services/wire.js.map +1 -1
  84. package/dist/types.d.ts +74 -8
  85. package/dist/types.d.ts.map +1 -1
  86. package/dist/types.js.map +1 -1
  87. package/package.json +3 -3
  88. package/src/delta/README.md +143 -360
  89. package/dist/state/diff.d.ts.map +0 -1
  90. package/dist/state/diff.js +0 -259
  91. package/dist/state/diff.js.map +0 -1
  92. package/dist/state/draft.d.ts +0 -13
  93. package/dist/state/draft.d.ts.map +0 -1
  94. package/dist/state/draft.js +0 -540
  95. package/dist/state/draft.js.map +0 -1
  96. package/dist/state/value.d.ts +0 -9
  97. package/dist/state/value.d.ts.map +0 -1
  98. package/dist/state/value.js +0 -175
  99. package/dist/state/value.js.map +0 -1
package/README.md CHANGED
@@ -32,12 +32,12 @@ The design has a few connected pieces:
32
32
  stable facade while a provider disconnects or is replaced.
33
33
 
34
34
  - **Replicated state** exposes authoritative state to local and remote
35
- connected consumers. Producers publish atomic copy-on-write transactions with
35
+ connected consumers. Producers publish atomic overlay transactions with
36
36
  `change(context, callback)`; consumers receive complete immutable values. Draft
37
- proxies exist only during the callback and are revoked afterward. Chord compares
38
- the previous and next immutable revisions to produce one decoded operation batch,
39
- while each remote client/state stream owns independent path-codec state. Replicas
40
- become unready on disconnect or replacement until they are rehydrated.
37
+ proxies exist only during the callback and become unusable afterward. Preparation
38
+ materializes a structurally shared immutable candidate and one exact decoded
39
+ operation batch, while each remote client/state stream owns independent path-codec
40
+ state. Replicas become unready on disconnect or replacement until rehydrated.
41
41
 
42
42
  - **Delta tracking** records and coalesces operations over tracked plain JSON.
43
43
  It preserves common string and array operations, supports durable base
@@ -87,28 +87,60 @@ close, or fresh hydration. Applications may place these values inside any
87
87
  routing, request, response, or event envelope; Chord does not prescribe that
88
88
  outer protocol.
89
89
 
90
+ Provider subscriptions atomically capture a snapshot and buffer subsequent
91
+ updates until activation. Activation and reentrant publication use the same FIFO.
92
+ The provider retains at most 100 pending updates per subscription, excluding the
93
+ running delivery. Adding update 101 replaces the entire pending queue with
94
+ `{ type: "reset", snapshot }`: a current full subscription snapshot whose state
95
+ members contain `[["r", value]]` and their new sequence baselines. It also captures
96
+ current instance membership, so discarded spawn/close/replacement events cannot
97
+ leave stale instances behind. Live keyed generations retain their existing handles.
98
+ The reset carries the context of the publication that triggered overflow.
99
+
100
+ Consumers and codecs must handle this explicit reset before accepting subsequent
101
+ ordinary deltas. A root operation in an ordinary `state` update does **not** permit
102
+ a sequence gap. Resets restart the subscription's path dictionaries, and later
103
+ deltas must be contiguous from each reset baseline. Transport adapters must
104
+ preserve snapshot/reset/update order; they must not drop encoded delta batches or
105
+ assume their own asynchronous queues are bounded by the provider's queue.
106
+
90
107
  ## Tracking JSON deltas
91
108
 
92
- Import the standalone delta primitive from `@earendil-works/chord/delta`:
109
+ Import the standalone transactional tracker from `@earendil-works/chord/delta`:
93
110
 
94
111
  ```ts
95
- import { apply, track } from "@earendil-works/chord/delta";
112
+ import { applyImmutable, track } from "@earendil-works/chord/delta";
96
113
 
97
- const changes = track({ output: "", count: 0 });
98
- changes.flush(); // opening base batch
99
- changes.state.output += "done\n";
100
- changes.state.count += 1;
114
+ const tracker = track({ output: "", count: 0 });
115
+ const change = tracker.beginChange();
116
+ change.state.output += "done\n";
117
+ change.state.count += 1;
118
+ const prepared = change.prepare();
101
119
 
102
- const ops = changes.flush();
103
- const replica = apply({ output: "", count: 0 }, ops);
120
+ tracker.adopt(prepared);
121
+ const replica = applyImmutable(prepared.base, prepared.ops);
104
122
  ```
105
123
 
106
- The first flush is always a complete base batch. Later flushes contain path-based
107
- changes. `applyImmutable()` applies those batches while preserving prior replica
108
- revisions. Replicated state instead uses transaction-scoped copy-on-write drafts:
124
+ `tracker.value` is always the latest adopted immutable revision. Preparation does
125
+ not change authority; adoption validates the preparation and swaps the root pointer.
126
+ Assigned containers are copied by value and unchanged subtrees may be shared between
127
+ revisions.
128
+
129
+ The tracker uses trusted immutable ownership rather than defensive copying or
130
+ freezing. `track(initial)`, `prepareReplace(value)`, `replicatedState(initial)`, and
131
+ `replace(context, value)` take ownership of alias-free strict-JSON roots without
132
+ walking them. Callers must not mutate transferred or published data. Values placed
133
+ through drafts are already copied, so that walk also rejects non-strict JSON before
134
+ the draft changes. Published values are not frozen. In-process loopback consumers
135
+ may share their containers with the provider; mutating a consumed value violates
136
+ the contract and can corrupt authority. Clone or serialize at any mutable trust
137
+ boundary.
138
+
139
+ Replicated state provides the same model through a callback:
109
140
 
110
141
  ```ts
111
- const status = env.replicatedState({ output: "", count: 0 });
142
+ const initial = { output: "", count: 0 };
143
+ const status = env.replicatedState(initial); // transfers ownership of initial
112
144
  status.change(context, (draft) => {
113
145
  draft.output += "done\n";
114
146
  draft.count += 1;
@@ -116,16 +148,37 @@ status.change(context, (draft) => {
116
148
  ```
117
149
 
118
150
  A successful `change()` publishes exactly one atomic revision. If its callback
119
- throws, the original value and sequence remain unchanged. Draft handles are revoked
120
- when the callback returns and assigned containers are copied by value. Unchanged
121
- subtrees are shared between immutable revisions. Chord derives string append and
122
- front-truncate operations, array splices and permutations, sets, and deletes from
123
- the two revisions; a large delta falls back to a complete snapshot. Remote
124
- connection plumbing encodes each batch independently for every client/state
125
- pairing. `replace(context, value)` publishes a detached complete value directly.
126
-
127
- The standalone [Delta guide](src/delta/README.md) documents the lower-level mutable
128
- tracker, which remains available separately from replicated state.
151
+ throws, the original value and sequence remain unchanged. Draft handles become
152
+ unusable when the callback returns. Chord emits string append and front-truncate
153
+ operations, array splices and permutations, sets, and deletes; large edit sets may
154
+ fold into a complete replacement. Remote connection plumbing encodes each batch
155
+ independently for every client/state pairing.
156
+
157
+ ### Public state subscriptions
158
+
159
+ `state.subscribe(async (value, context, delivery) => { ... })` serializes callbacks
160
+ independently for each subscription. Hydration starts immediately when a value is
161
+ available, and its returned promise must settle before updates start. Synchronous
162
+ callbacks still run synchronously. Read the captured `value` inside an asynchronous
163
+ callback: `state.value` may already refer to a later revision.
164
+
165
+ Each subscription retains at most 100 pending complete-value deliveries, excluding
166
+ the running callback. On overflow, only the newest pending value, context, and
167
+ delivery metadata are retained; a not-yet-started initial hydration is preserved.
168
+ Thus public delivery sequences may skip. This is a frame-count policy, not a byte
169
+ limit. Internal exact-operation subscriptions remain synchronous and receive every
170
+ revision, independently of slow public callbacks.
171
+
172
+ The returned unsubscribe function immediately discards pending work and prevents
173
+ new callbacks. It neither aborts nor waits for the running callback. Synchronous
174
+ throws and promise rejections are observed, reported, and do not stop other
175
+ subscriptions or subsequent callbacks. Attached sources and remote bindings use
176
+ their `onError` handler; local mutable states (and attached sources without a
177
+ handler) report failures by throwing in a microtask. Unsubscribing does not hide a
178
+ later rejection from the running callback.
179
+
180
+ The standalone [Delta guide](src/delta/README.md) defines the complete ownership,
181
+ lifecycle, operation, and replica contracts.
129
182
 
130
183
  ## Bundling and loading facets
131
184
 
package/dist/api.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { Facet, FacetHost, FacetLoader, FacetOptions, MutableReplicatedState, RemoteServiceBinding, RemoteServiceBindingOptions, RemoteServiceContract, Service } from "./types.ts";
1
+ import type { AttachedReplicatedState, Facet, FacetHost, FacetLoader, FacetOptions, MutableReplicatedState, RemoteServiceBinding, RemoteServiceBindingOptions, RemoteServiceContract, ReplicatedStateSource, ReplicatedStateSourceOptions, Service } from "./types.ts";
2
2
  /** Create an active host for one complete set of facets. */
3
3
  export declare function createFacetHost(options: FacetOptions): Promise<FacetHost>;
4
4
  export declare function createStaticFacetLoader(facets: readonly Facet[]): FacetLoader;
@@ -11,5 +11,10 @@ export declare function defineService<T>(id: string, ...options: [RemoteServiceC
11
11
  readonly local?: false;
12
12
  }]): Service<T>;
13
13
  export declare function createRemoteServiceBinding(options: RemoteServiceBindingOptions): RemoteServiceBinding;
14
+ export declare function replicatedState<T>(source: ReplicatedStateSource<T>, options?: ReplicatedStateSourceOptions): AttachedReplicatedState<T>;
15
+ /**
16
+ * Create authoritative state by taking immutable ownership of an alias-free strict-JSON root.
17
+ * The caller must not mutate `initial` after this call.
18
+ */
14
19
  export declare function replicatedState<T extends object>(initial: T): MutableReplicatedState<T>;
15
20
  //# sourceMappingURL=api.d.ts.map
package/dist/api.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACX,KAAK,EACL,SAAS,EACT,WAAW,EACX,YAAY,EAEZ,sBAAsB,EACtB,oBAAoB,EACpB,2BAA2B,EAC3B,qBAAqB,EACrB,OAAO,EACP,MAAM,YAAY,CAAC;AAEpB,4DAA4D;AAC5D,wBAAsB,eAAe,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,SAAS,CAAC,CAQ/E;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,GAAG,WAAW,CAO7E;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,SAAS,WAAW,EAAE,GAAG,WAAW,CA0BhF;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,KAAK,GAAG,KAAK,CAE/C;AAED,wBAAgB,aAAa,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE;IAAE,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAA;CAAE,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;AAC5F,wBAAgB,aAAa,CAAC,CAAC,EAC9B,EAAE,EAAE,MAAM,EACV,GAAG,OAAO,EAAE,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,GACnD,SAAS,CAAC,OAAO,EAAE,KAAK,CAAC,GACzB,SAAS,CAAC,OAAO,CAAC,EAAE;IAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,CAAA;CAAE,CAAC,GAChD,OAAO,CAAC,CAAC,CAAC,CAAC;AAQd,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,2BAA2B,GAAG,oBAAoB,CAErG;AAED,wBAAgB,eAAe,CAAC,CAAC,SAAS,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,sBAAsB,CAAC,CAAC,CAAC,CAEvF","sourcesContent":["import { FacetKernel } from \"./facets/host.ts\";\nimport { disposeLoadedFacets } from \"./facets/loader.ts\";\nimport { RemoteServiceBindingImpl } from \"./services/consumer.ts\";\nimport { MutableReplicatedStateImpl } from \"./services/state.ts\";\nimport type {\n\tFacet,\n\tFacetHost,\n\tFacetLoader,\n\tFacetOptions,\n\tLoadedFacets,\n\tMutableReplicatedState,\n\tRemoteServiceBinding,\n\tRemoteServiceBindingOptions,\n\tRemoteServiceContract,\n\tService,\n} from \"./types.ts\";\n\n/** Create an active host for one complete set of facets. */\nexport async function createFacetHost(options: FacetOptions): Promise<FacetHost> {\n\tconst kernel = new FacetKernel(options);\n\tawait kernel.activate();\n\treturn Object.freeze({\n\t\tservices: kernel.provider,\n\t\treload: (facets: readonly Facet[]) => kernel.reload(facets),\n\t\tdispose: () => kernel.dispose(),\n\t});\n}\n\nexport function createStaticFacetLoader(facets: readonly Facet[]): FacetLoader {\n\tconst loadedFacets = Object.freeze([...facets]);\n\treturn {\n\t\tasync load() {\n\t\t\treturn { facets: loadedFacets, async dispose() {} };\n\t\t},\n\t};\n}\n\nexport function combineFacetLoaders(loaders: readonly FacetLoader[]): FacetLoader {\n\treturn {\n\t\tasync load() {\n\t\t\tconst loaded: LoadedFacets[] = [];\n\t\t\ttry {\n\t\t\t\tfor (const loader of loaders) loaded.push(await loader.load());\n\t\t\t} catch (error) {\n\t\t\t\tconst cleanupErrors = await disposeLoadedFacets(loaded.reverse());\n\t\t\t\tif (cleanupErrors.length > 0) {\n\t\t\t\t\tthrow new AggregateError([error, ...cleanupErrors], \"Facet loading and cleanup failed\");\n\t\t\t\t}\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\tlet disposed = false;\n\t\t\treturn {\n\t\t\t\tfacets: Object.freeze(loaded.flatMap(({ facets }) => facets)),\n\t\t\t\tasync dispose() {\n\t\t\t\t\tif (disposed) return;\n\t\t\t\t\tdisposed = true;\n\t\t\t\t\tconst errors = await disposeLoadedFacets([...loaded].reverse());\n\t\t\t\t\tif (errors.length === 1) throw errors[0];\n\t\t\t\t\tif (errors.length > 1) throw new AggregateError(errors, \"Failed to dispose loaded facets\");\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\t};\n}\n\nexport function defineFacet(facet: Facet): Facet {\n\treturn facet;\n}\n\nexport function defineService<T>(id: string, options: { readonly local: true }): Service<T>;\nexport function defineService<T>(\n\tid: string,\n\t...options: [RemoteServiceContract<T>] extends [never]\n\t\t? readonly [options: never]\n\t\t: readonly [options?: { readonly local?: false }]\n): Service<T>;\nexport function defineService(id: string, options?: { readonly local?: boolean }): Service<unknown> {\n\tif (id.length === 0) throw new TypeError(\"Service ID must not be empty\");\n\t// TODO: check if the reserved namespace should be part of Chord.\n\tif (id.startsWith(\"$chord.\")) throw new TypeError(\"Service IDs beginning with $chord. are reserved\");\n\treturn Object.freeze({ id, local: options?.local ?? false });\n}\n\nexport function createRemoteServiceBinding(options: RemoteServiceBindingOptions): RemoteServiceBinding {\n\treturn new RemoteServiceBindingImpl(options);\n}\n\nexport function replicatedState<T extends object>(initial: T): MutableReplicatedState<T> {\n\treturn new MutableReplicatedStateImpl(initial);\n}\n"]}
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACX,uBAAuB,EACvB,KAAK,EACL,SAAS,EACT,WAAW,EACX,YAAY,EAEZ,sBAAsB,EACtB,oBAAoB,EACpB,2BAA2B,EAC3B,qBAAqB,EACrB,qBAAqB,EACrB,4BAA4B,EAC5B,OAAO,EACP,MAAM,YAAY,CAAC;AAEpB,4DAA4D;AAC5D,wBAAsB,eAAe,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,SAAS,CAAC,CAQ/E;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,GAAG,WAAW,CAO7E;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,SAAS,WAAW,EAAE,GAAG,WAAW,CA0BhF;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,KAAK,GAAG,KAAK,CAE/C;AAED,wBAAgB,aAAa,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE;IAAE,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAA;CAAE,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;AAC5F,wBAAgB,aAAa,CAAC,CAAC,EAC9B,EAAE,EAAE,MAAM,EACV,GAAG,OAAO,EAAE,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,GACnD,SAAS,CAAC,OAAO,EAAE,KAAK,CAAC,GACzB,SAAS,CAAC,OAAO,CAAC,EAAE;IAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,CAAA;CAAE,CAAC,GAChD,OAAO,CAAC,CAAC,CAAC,CAAC;AAQd,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,2BAA2B,GAAG,oBAAoB,CAErG;AAED,wBAAgB,eAAe,CAAC,CAAC,EAChC,MAAM,EAAE,qBAAqB,CAAC,CAAC,CAAC,EAChC,OAAO,CAAC,EAAE,4BAA4B,GACpC,uBAAuB,CAAC,CAAC,CAAC,CAAC;AAC9B;;;GAGG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,sBAAsB,CAAC,CAAC,CAAC,CAAC"}
package/dist/api.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { FacetKernel } from "./facets/host.js";
2
2
  import { disposeLoadedFacets } from "./facets/loader.js";
3
3
  import { RemoteServiceBindingImpl } from "./services/consumer.js";
4
- import { MutableReplicatedStateImpl } from "./services/state.js";
4
+ import { attachReplicatedStateSource, MutableReplicatedStateImpl } from "./services/state.js";
5
5
  /** Create an active host for one complete set of facets. */
6
6
  export async function createFacetHost(options) {
7
7
  const kernel = new FacetKernel(options);
@@ -66,7 +66,12 @@ export function defineService(id, options) {
66
66
  export function createRemoteServiceBinding(options) {
67
67
  return new RemoteServiceBindingImpl(options);
68
68
  }
69
- export function replicatedState(initial) {
70
- return new MutableReplicatedStateImpl(initial);
69
+ export function replicatedState(initialOrSource, options) {
70
+ if (isReplicatedStateSource(initialOrSource))
71
+ return attachReplicatedStateSource(initialOrSource, options);
72
+ return new MutableReplicatedStateImpl(initialOrSource);
73
+ }
74
+ function isReplicatedStateSource(value) {
75
+ return typeof value.attach === "function";
71
76
  }
72
77
  //# sourceMappingURL=api.js.map
package/dist/api.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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","sourcesContent":["import { FacetKernel } from \"./facets/host.ts\";\nimport { disposeLoadedFacets } from \"./facets/loader.ts\";\nimport { RemoteServiceBindingImpl } from \"./services/consumer.ts\";\nimport { MutableReplicatedStateImpl } from \"./services/state.ts\";\nimport type {\n\tFacet,\n\tFacetHost,\n\tFacetLoader,\n\tFacetOptions,\n\tLoadedFacets,\n\tMutableReplicatedState,\n\tRemoteServiceBinding,\n\tRemoteServiceBindingOptions,\n\tRemoteServiceContract,\n\tService,\n} from \"./types.ts\";\n\n/** Create an active host for one complete set of facets. */\nexport async function createFacetHost(options: FacetOptions): Promise<FacetHost> {\n\tconst kernel = new FacetKernel(options);\n\tawait kernel.activate();\n\treturn Object.freeze({\n\t\tservices: kernel.provider,\n\t\treload: (facets: readonly Facet[]) => kernel.reload(facets),\n\t\tdispose: () => kernel.dispose(),\n\t});\n}\n\nexport function createStaticFacetLoader(facets: readonly Facet[]): FacetLoader {\n\tconst loadedFacets = Object.freeze([...facets]);\n\treturn {\n\t\tasync load() {\n\t\t\treturn { facets: loadedFacets, async dispose() {} };\n\t\t},\n\t};\n}\n\nexport function combineFacetLoaders(loaders: readonly FacetLoader[]): FacetLoader {\n\treturn {\n\t\tasync load() {\n\t\t\tconst loaded: LoadedFacets[] = [];\n\t\t\ttry {\n\t\t\t\tfor (const loader of loaders) loaded.push(await loader.load());\n\t\t\t} catch (error) {\n\t\t\t\tconst cleanupErrors = await disposeLoadedFacets(loaded.reverse());\n\t\t\t\tif (cleanupErrors.length > 0) {\n\t\t\t\t\tthrow new AggregateError([error, ...cleanupErrors], \"Facet loading and cleanup failed\");\n\t\t\t\t}\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\tlet disposed = false;\n\t\t\treturn {\n\t\t\t\tfacets: Object.freeze(loaded.flatMap(({ facets }) => facets)),\n\t\t\t\tasync dispose() {\n\t\t\t\t\tif (disposed) return;\n\t\t\t\t\tdisposed = true;\n\t\t\t\t\tconst errors = await disposeLoadedFacets([...loaded].reverse());\n\t\t\t\t\tif (errors.length === 1) throw errors[0];\n\t\t\t\t\tif (errors.length > 1) throw new AggregateError(errors, \"Failed to dispose loaded facets\");\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\t};\n}\n\nexport function defineFacet(facet: Facet): Facet {\n\treturn facet;\n}\n\nexport function defineService<T>(id: string, options: { readonly local: true }): Service<T>;\nexport function defineService<T>(\n\tid: string,\n\t...options: [RemoteServiceContract<T>] extends [never]\n\t\t? readonly [options: never]\n\t\t: readonly [options?: { readonly local?: false }]\n): Service<T>;\nexport function defineService(id: string, options?: { readonly local?: boolean }): Service<unknown> {\n\tif (id.length === 0) throw new TypeError(\"Service ID must not be empty\");\n\t// TODO: check if the reserved namespace should be part of Chord.\n\tif (id.startsWith(\"$chord.\")) throw new TypeError(\"Service IDs beginning with $chord. are reserved\");\n\treturn Object.freeze({ id, local: options?.local ?? false });\n}\n\nexport function createRemoteServiceBinding(options: RemoteServiceBindingOptions): RemoteServiceBinding {\n\treturn new RemoteServiceBindingImpl(options);\n}\n\nexport function replicatedState<T extends object>(initial: T): MutableReplicatedState<T> {\n\treturn new MutableReplicatedStateImpl(initial);\n}\n"]}
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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","sourcesContent":["import { FacetKernel } from \"./facets/host.ts\";\nimport { disposeLoadedFacets } from \"./facets/loader.ts\";\nimport { RemoteServiceBindingImpl } from \"./services/consumer.ts\";\nimport { attachReplicatedStateSource, MutableReplicatedStateImpl } from \"./services/state.ts\";\nimport type {\n\tAttachedReplicatedState,\n\tFacet,\n\tFacetHost,\n\tFacetLoader,\n\tFacetOptions,\n\tLoadedFacets,\n\tMutableReplicatedState,\n\tRemoteServiceBinding,\n\tRemoteServiceBindingOptions,\n\tRemoteServiceContract,\n\tReplicatedStateSource,\n\tReplicatedStateSourceOptions,\n\tService,\n} from \"./types.ts\";\n\n/** Create an active host for one complete set of facets. */\nexport async function createFacetHost(options: FacetOptions): Promise<FacetHost> {\n\tconst kernel = new FacetKernel(options);\n\tawait kernel.activate();\n\treturn Object.freeze({\n\t\tservices: kernel.provider,\n\t\treload: (facets: readonly Facet[]) => kernel.reload(facets),\n\t\tdispose: () => kernel.dispose(),\n\t});\n}\n\nexport function createStaticFacetLoader(facets: readonly Facet[]): FacetLoader {\n\tconst loadedFacets = Object.freeze([...facets]);\n\treturn {\n\t\tasync load() {\n\t\t\treturn { facets: loadedFacets, async dispose() {} };\n\t\t},\n\t};\n}\n\nexport function combineFacetLoaders(loaders: readonly FacetLoader[]): FacetLoader {\n\treturn {\n\t\tasync load() {\n\t\t\tconst loaded: LoadedFacets[] = [];\n\t\t\ttry {\n\t\t\t\tfor (const loader of loaders) loaded.push(await loader.load());\n\t\t\t} catch (error) {\n\t\t\t\tconst cleanupErrors = await disposeLoadedFacets(loaded.reverse());\n\t\t\t\tif (cleanupErrors.length > 0) {\n\t\t\t\t\tthrow new AggregateError([error, ...cleanupErrors], \"Facet loading and cleanup failed\");\n\t\t\t\t}\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\tlet disposed = false;\n\t\t\treturn {\n\t\t\t\tfacets: Object.freeze(loaded.flatMap(({ facets }) => facets)),\n\t\t\t\tasync dispose() {\n\t\t\t\t\tif (disposed) return;\n\t\t\t\t\tdisposed = true;\n\t\t\t\t\tconst errors = await disposeLoadedFacets([...loaded].reverse());\n\t\t\t\t\tif (errors.length === 1) throw errors[0];\n\t\t\t\t\tif (errors.length > 1) throw new AggregateError(errors, \"Failed to dispose loaded facets\");\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\t};\n}\n\nexport function defineFacet(facet: Facet): Facet {\n\treturn facet;\n}\n\nexport function defineService<T>(id: string, options: { readonly local: true }): Service<T>;\nexport function defineService<T>(\n\tid: string,\n\t...options: [RemoteServiceContract<T>] extends [never]\n\t\t? readonly [options: never]\n\t\t: readonly [options?: { readonly local?: false }]\n): Service<T>;\nexport function defineService(id: string, options?: { readonly local?: boolean }): Service<unknown> {\n\tif (id.length === 0) throw new TypeError(\"Service ID must not be empty\");\n\t// TODO: check if the reserved namespace should be part of Chord.\n\tif (id.startsWith(\"$chord.\")) throw new TypeError(\"Service IDs beginning with $chord. are reserved\");\n\treturn Object.freeze({ id, local: options?.local ?? false });\n}\n\nexport function createRemoteServiceBinding(options: RemoteServiceBindingOptions): RemoteServiceBinding {\n\treturn new RemoteServiceBindingImpl(options);\n}\n\nexport function replicatedState<T>(\n\tsource: ReplicatedStateSource<T>,\n\toptions?: ReplicatedStateSourceOptions,\n): AttachedReplicatedState<T>;\n/**\n * Create authoritative state by taking immutable ownership of an alias-free strict-JSON root.\n * The caller must not mutate `initial` after this call.\n */\nexport function replicatedState<T extends object>(initial: T): MutableReplicatedState<T>;\nexport function replicatedState(\n\tinitialOrSource: object | ReplicatedStateSource<unknown>,\n\toptions?: ReplicatedStateSourceOptions,\n): MutableReplicatedState<object> | AttachedReplicatedState<unknown> {\n\tif (isReplicatedStateSource(initialOrSource)) return attachReplicatedStateSource(initialOrSource, options);\n\treturn new MutableReplicatedStateImpl(initialOrSource);\n}\n\nfunction isReplicatedStateSource(value: object): value is ReplicatedStateSource<unknown> {\n\treturn typeof (value as { readonly attach?: unknown }).attach === \"function\";\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"bundler.d.ts","sourceRoot":"","sources":["../src/bundler.ts"],"names":[],"mappings":"AAAA,YAAY,EACX,mBAAmB,EACnB,kBAAkB,EAClB,mBAAmB,GACnB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,YAAY,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAChF,YAAY,EAAE,yBAAyB,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAC7F,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC","sourcesContent":["export type {\n\tBundleFacetsOptions,\n\tBundleFacetsResult,\n\tFacetBundlePlatform,\n} from \"./node/bundle.ts\";\nexport { bundleFacets } from \"./node/bundle.ts\";\nexport type { FacetBundleEntry, FacetBundleManifest } from \"./node/manifest.ts\";\nexport type { BundleFacetPackageOptions, BundleFacetPackageResult } from \"./node/package.ts\";\nexport { bundleFacetPackage } from \"./node/package.ts\";\n"]}
1
+ {"version":3,"file":"bundler.d.ts","sourceRoot":"","sources":["../src/bundler.ts"],"names":[],"mappings":"AAAA,YAAY,EACX,mBAAmB,EACnB,kBAAkB,EAClB,mBAAmB,GACnB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,YAAY,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAChF,YAAY,EAAE,yBAAyB,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAC7F,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/context/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAsDvD,eAAO,MAAM,kBAAkB,EAAE,OAA0D,CAAC;AAC5F,eAAO,MAAM,YAAY,EAAE,OAAoD,CAAC;AAEhF,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,WAAW,EAAE,MAAM,GAAG,UAAU,CAAC,CAAC,CAAC,CAEtE;AAED,oEAAoE;AACpE,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,GAAG,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,CAE1F;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAI9E;AAED,6GAA6G;AAC7G,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAE5D;AAED,yDAAyD;AACzD,wBAAgB,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG;IAC7C,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;CAC5C,CAMA;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,CAkBrF","sourcesContent":["import type { Context, ContextKey } from \"../types.ts\";\n\nconst ABORT_SIGNAL_CONTEXT_KEY: ContextKey<AbortSignal | undefined> = Object.freeze({\n\ttoken: Symbol(\"chord.abortSignal\"),\n});\n\nabstract class BaseContext implements Context {\n\tabstract value<T>(key: ContextKey<T>): T | undefined;\n\tabstract toString(): string;\n\n\tget abortSignal(): AbortSignal | undefined {\n\t\treturn this.value(ABORT_SIGNAL_CONTEXT_KEY);\n\t}\n}\n\nclass EmptyContext extends BaseContext {\n\treadonly #name: string;\n\n\tconstructor(name: string) {\n\t\tsuper();\n\t\tthis.#name = name;\n\t}\n\n\tvalue<T>(_key: ContextKey<T>): T | undefined {\n\t\treturn undefined;\n\t}\n\n\ttoString(): string {\n\t\treturn this.#name;\n\t}\n}\n\nclass ContextValue<T> extends BaseContext {\n\treadonly #parent: Context;\n\treadonly #key: ContextKey<T>;\n\treadonly #value: T;\n\n\tconstructor(parent: Context, key: ContextKey<T>, value: T) {\n\t\tsuper();\n\t\tthis.#parent = parent;\n\t\tthis.#key = key;\n\t\tthis.#value = value;\n\t}\n\n\tvalue<Value>(key: ContextKey<Value>): Value | undefined {\n\t\tif (key.token === this.#key.token) return this.#value as unknown as Value;\n\t\treturn this.#parent.value(key);\n\t}\n\n\ttoString(): string {\n\t\treturn `${this.#parent}.WithValue(${this.#key.token.description ?? \"anonymous\"})`;\n\t}\n}\n\nexport const BACKGROUND_CONTEXT: Context = new EmptyContext(\"[Context BACKGROUND_CONTEXT]\");\nexport const TODO_CONTEXT: Context = new EmptyContext(\"[Context TODO_CONTEXT]\");\n\nexport function createContextKey<T>(description: string): ContextKey<T> {\n\treturn Object.freeze({ token: Symbol(description) });\n}\n\n/** Derive a context containing one additional or replaced value. */\nexport function withContextValue<T>(key: ContextKey<T>, value: T, parent: Context): Context {\n\treturn new ContextValue(parent, key, value);\n}\n\n/**\n * Derive a context cancelled by either the parent signal or the supplied signal.\n * The parent context remains unchanged.\n */\nexport function withAbortSignal(signal: AbortSignal, context: Context): Context {\n\tconst parentSignal = context.abortSignal;\n\tconst combined = parentSignal === undefined ? signal : AbortSignal.any([parentSignal, signal]);\n\treturn withContextValue(ABORT_SIGNAL_CONTEXT_KEY, combined, context);\n}\n\n/** Derive a context retaining all values except caller cancellation. Intended for mandatory cleanup only. */\nexport function withoutAbortSignal(context: Context): Context {\n\treturn withContextValue(ABORT_SIGNAL_CONTEXT_KEY, undefined, context);\n}\n\n/** Derive an independently cancellable child context. */\nexport function withCancel(context: Context): {\n\treadonly context: Context;\n\treadonly cancel: (reason?: unknown) => void;\n} {\n\tconst controller = new AbortController();\n\treturn {\n\t\tcontext: withAbortSignal(controller.signal, context),\n\t\tcancel: (reason?: unknown) => controller.abort(reason),\n\t};\n}\n\n/**\n * Observe a promise until it settles or the invocation is cancelled.\n * Cancellation rejects only this waiter; it does not cancel the underlying promise.\n */\nexport function awaitWithContext<T>(promise: Promise<T>, context: Context): Promise<T> {\n\tconst signal = context.abortSignal;\n\tif (signal === undefined) return promise;\n\tif (signal.aborted) return Promise.reject(abortError(signal));\n\treturn new Promise<T>((resolve, reject) => {\n\t\tconst onAbort = (): void => reject(abortError(signal));\n\t\tsignal.addEventListener(\"abort\", onAbort, { once: true });\n\t\tvoid promise.then(\n\t\t\t(value) => {\n\t\t\t\tsignal.removeEventListener(\"abort\", onAbort);\n\t\t\t\tresolve(value);\n\t\t\t},\n\t\t\t(error: unknown) => {\n\t\t\t\tsignal.removeEventListener(\"abort\", onAbort);\n\t\t\t\treject(error);\n\t\t\t},\n\t\t);\n\t});\n}\n\nfunction abortError(signal: AbortSignal): Error {\n\tconst reason: unknown = signal.reason;\n\treturn reason instanceof Error ? reason : new DOMException(\"The operation was aborted\", \"AbortError\");\n}\n"]}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/context/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAsDvD,eAAO,MAAM,kBAAkB,EAAE,OAA0D,CAAC;AAC5F,eAAO,MAAM,YAAY,EAAE,OAAoD,CAAC;AAEhF,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,WAAW,EAAE,MAAM,GAAG,UAAU,CAAC,CAAC,CAAC,CAEtE;AAED,oEAAoE;AACpE,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,GAAG,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,CAE1F;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAI9E;AAED,6GAA6G;AAC7G,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAE5D;AAED,yDAAyD;AACzD,wBAAgB,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG;IAC7C,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;CAC5C,CAMA;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,CAkBrF"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/context/index.ts"],"names":[],"mappings":"AAEA,MAAM,wBAAwB,GAAwC,MAAM,CAAC,MAAM,CAAC;IACnF,KAAK,EAAE,MAAM,CAAC,mBAAmB,CAAC;CAClC,CAAC,CAAC;AAEH,MAAe,WAAW;IAIzB,IAAI,WAAW,GAA4B;QAC1C,OAAO,IAAI,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAC;IAAA,CAC5C;CACD;AAED,MAAM,YAAa,SAAQ,WAAW;IAC5B,KAAK,CAAS;IAEvB,YAAY,IAAY,EAAE;QACzB,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;IAAA,CAClB;IAED,KAAK,CAAI,IAAmB,EAAiB;QAC5C,OAAO,SAAS,CAAC;IAAA,CACjB;IAED,QAAQ,GAAW;QAClB,OAAO,IAAI,CAAC,KAAK,CAAC;IAAA,CAClB;CACD;AAED,MAAM,YAAgB,SAAQ,WAAW;IAC/B,OAAO,CAAU;IACjB,IAAI,CAAgB;IACpB,MAAM,CAAI;IAEnB,YAAY,MAAe,EAAE,GAAkB,EAAE,KAAQ,EAAE;QAC1D,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;QACtB,IAAI,CAAC,IAAI,GAAG,GAAG,CAAC;QAChB,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;IAAA,CACpB;IAED,KAAK,CAAQ,GAAsB,EAAqB;QACvD,IAAI,GAAG,CAAC,KAAK,KAAK,IAAI,CAAC,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAC,MAA0B,CAAC;QAC1E,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAAA,CAC/B;IAED,QAAQ,GAAW;QAClB,OAAO,GAAG,IAAI,CAAC,OAAO,cAAc,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,IAAI,WAAW,GAAG,CAAC;IAAA,CAClF;CACD;AAED,MAAM,CAAC,MAAM,kBAAkB,GAAY,IAAI,YAAY,CAAC,8BAA8B,CAAC,CAAC;AAC5F,MAAM,CAAC,MAAM,YAAY,GAAY,IAAI,YAAY,CAAC,wBAAwB,CAAC,CAAC;AAEhF,MAAM,UAAU,gBAAgB,CAAI,WAAmB,EAAiB;IACvE,OAAO,MAAM,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;AAAA,CACrD;AAED,oEAAoE;AACpE,MAAM,UAAU,gBAAgB,CAAI,GAAkB,EAAE,KAAQ,EAAE,MAAe,EAAW;IAC3F,OAAO,IAAI,YAAY,CAAC,MAAM,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;AAAA,CAC5C;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,MAAmB,EAAE,OAAgB,EAAW;IAC/E,MAAM,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC;IACzC,MAAM,QAAQ,GAAG,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC;IAC/F,OAAO,gBAAgB,CAAC,wBAAwB,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;AAAA,CACrE;AAED,6GAA6G;AAC7G,MAAM,UAAU,kBAAkB,CAAC,OAAgB,EAAW;IAC7D,OAAO,gBAAgB,CAAC,wBAAwB,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;AAAA,CACtE;AAED,yDAAyD;AACzD,MAAM,UAAU,UAAU,CAAC,OAAgB,EAGzC;IACD,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,OAAO;QACN,OAAO,EAAE,eAAe,CAAC,UAAU,CAAC,MAAM,EAAE,OAAO,CAAC;QACpD,MAAM,EAAE,CAAC,MAAgB,EAAE,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC;KACtD,CAAC;AAAA,CACF;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAI,OAAmB,EAAE,OAAgB,EAAc;IACtF,MAAM,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IACnC,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,OAAO,CAAC;IACzC,IAAI,MAAM,CAAC,OAAO;QAAE,OAAO,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;IAC9D,OAAO,IAAI,OAAO,CAAI,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC;QAC1C,MAAM,OAAO,GAAG,GAAS,EAAE,CAAC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;QACvD,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1D,KAAK,OAAO,CAAC,IAAI,CAChB,CAAC,KAAK,EAAE,EAAE,CAAC;YACV,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAC7C,OAAO,CAAC,KAAK,CAAC,CAAC;QAAA,CACf,EACD,CAAC,KAAc,EAAE,EAAE,CAAC;YACnB,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAC7C,MAAM,CAAC,KAAK,CAAC,CAAC;QAAA,CACd,CACD,CAAC;IAAA,CACF,CAAC,CAAC;AAAA,CACH;AAED,SAAS,UAAU,CAAC,MAAmB,EAAS;IAC/C,MAAM,MAAM,GAAY,MAAM,CAAC,MAAM,CAAC;IACtC,OAAO,MAAM,YAAY,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,YAAY,CAAC,2BAA2B,EAAE,YAAY,CAAC,CAAC;AAAA,CACtG","sourcesContent":["import type { Context, ContextKey } from \"../types.ts\";\n\nconst ABORT_SIGNAL_CONTEXT_KEY: ContextKey<AbortSignal | undefined> = Object.freeze({\n\ttoken: Symbol(\"chord.abortSignal\"),\n});\n\nabstract class BaseContext implements Context {\n\tabstract value<T>(key: ContextKey<T>): T | undefined;\n\tabstract toString(): string;\n\n\tget abortSignal(): AbortSignal | undefined {\n\t\treturn this.value(ABORT_SIGNAL_CONTEXT_KEY);\n\t}\n}\n\nclass EmptyContext extends BaseContext {\n\treadonly #name: string;\n\n\tconstructor(name: string) {\n\t\tsuper();\n\t\tthis.#name = name;\n\t}\n\n\tvalue<T>(_key: ContextKey<T>): T | undefined {\n\t\treturn undefined;\n\t}\n\n\ttoString(): string {\n\t\treturn this.#name;\n\t}\n}\n\nclass ContextValue<T> extends BaseContext {\n\treadonly #parent: Context;\n\treadonly #key: ContextKey<T>;\n\treadonly #value: T;\n\n\tconstructor(parent: Context, key: ContextKey<T>, value: T) {\n\t\tsuper();\n\t\tthis.#parent = parent;\n\t\tthis.#key = key;\n\t\tthis.#value = value;\n\t}\n\n\tvalue<Value>(key: ContextKey<Value>): Value | undefined {\n\t\tif (key.token === this.#key.token) return this.#value as unknown as Value;\n\t\treturn this.#parent.value(key);\n\t}\n\n\ttoString(): string {\n\t\treturn `${this.#parent}.WithValue(${this.#key.token.description ?? \"anonymous\"})`;\n\t}\n}\n\nexport const BACKGROUND_CONTEXT: Context = new EmptyContext(\"[Context BACKGROUND_CONTEXT]\");\nexport const TODO_CONTEXT: Context = new EmptyContext(\"[Context TODO_CONTEXT]\");\n\nexport function createContextKey<T>(description: string): ContextKey<T> {\n\treturn Object.freeze({ token: Symbol(description) });\n}\n\n/** Derive a context containing one additional or replaced value. */\nexport function withContextValue<T>(key: ContextKey<T>, value: T, parent: Context): Context {\n\treturn new ContextValue(parent, key, value);\n}\n\n/**\n * Derive a context cancelled by either the parent signal or the supplied signal.\n * The parent context remains unchanged.\n */\nexport function withAbortSignal(signal: AbortSignal, context: Context): Context {\n\tconst parentSignal = context.abortSignal;\n\tconst combined = parentSignal === undefined ? signal : AbortSignal.any([parentSignal, signal]);\n\treturn withContextValue(ABORT_SIGNAL_CONTEXT_KEY, combined, context);\n}\n\n/** Derive a context retaining all values except caller cancellation. Intended for mandatory cleanup only. */\nexport function withoutAbortSignal(context: Context): Context {\n\treturn withContextValue(ABORT_SIGNAL_CONTEXT_KEY, undefined, context);\n}\n\n/** Derive an independently cancellable child context. */\nexport function withCancel(context: Context): {\n\treadonly context: Context;\n\treadonly cancel: (reason?: unknown) => void;\n} {\n\tconst controller = new AbortController();\n\treturn {\n\t\tcontext: withAbortSignal(controller.signal, context),\n\t\tcancel: (reason?: unknown) => controller.abort(reason),\n\t};\n}\n\n/**\n * Observe a promise until it settles or the invocation is cancelled.\n * Cancellation rejects only this waiter; it does not cancel the underlying promise.\n */\nexport function awaitWithContext<T>(promise: Promise<T>, context: Context): Promise<T> {\n\tconst signal = context.abortSignal;\n\tif (signal === undefined) return promise;\n\tif (signal.aborted) return Promise.reject(abortError(signal));\n\treturn new Promise<T>((resolve, reject) => {\n\t\tconst onAbort = (): void => reject(abortError(signal));\n\t\tsignal.addEventListener(\"abort\", onAbort, { once: true });\n\t\tvoid promise.then(\n\t\t\t(value) => {\n\t\t\t\tsignal.removeEventListener(\"abort\", onAbort);\n\t\t\t\tresolve(value);\n\t\t\t},\n\t\t\t(error: unknown) => {\n\t\t\t\tsignal.removeEventListener(\"abort\", onAbort);\n\t\t\t\treject(error);\n\t\t\t},\n\t\t);\n\t});\n}\n\nfunction abortError(signal: AbortSignal): Error {\n\tconst reason: unknown = signal.reason;\n\treturn reason instanceof Error ? reason : new DOMException(\"The operation was aborted\", \"AbortError\");\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/context/index.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Context, ContextKey } from \"../types.ts\";\n\nconst ABORT_SIGNAL_CONTEXT_KEY: ContextKey<AbortSignal | undefined> = Object.freeze({\n\ttoken: Symbol(\"chord.abortSignal\"),\n});\n\nabstract class BaseContext implements Context {\n\tabstract value<T>(key: ContextKey<T>): T | undefined;\n\tabstract toString(): string;\n\n\tget abortSignal(): AbortSignal | undefined {\n\t\treturn this.value(ABORT_SIGNAL_CONTEXT_KEY);\n\t}\n}\n\nclass EmptyContext extends BaseContext {\n\treadonly #name: string;\n\n\tconstructor(name: string) {\n\t\tsuper();\n\t\tthis.#name = name;\n\t}\n\n\tvalue<T>(_key: ContextKey<T>): T | undefined {\n\t\treturn undefined;\n\t}\n\n\ttoString(): string {\n\t\treturn this.#name;\n\t}\n}\n\nclass ContextValue<T> extends BaseContext {\n\treadonly #parent: Context;\n\treadonly #key: ContextKey<T>;\n\treadonly #value: T;\n\n\tconstructor(parent: Context, key: ContextKey<T>, value: T) {\n\t\tsuper();\n\t\tthis.#parent = parent;\n\t\tthis.#key = key;\n\t\tthis.#value = value;\n\t}\n\n\tvalue<Value>(key: ContextKey<Value>): Value | undefined {\n\t\tif (key.token === this.#key.token) return this.#value as unknown as Value;\n\t\treturn this.#parent.value(key);\n\t}\n\n\ttoString(): string {\n\t\treturn `${this.#parent}.WithValue(${this.#key.token.description ?? \"anonymous\"})`;\n\t}\n}\n\nexport const BACKGROUND_CONTEXT: Context = new EmptyContext(\"[Context BACKGROUND_CONTEXT]\");\nexport const TODO_CONTEXT: Context = new EmptyContext(\"[Context TODO_CONTEXT]\");\n\nexport function createContextKey<T>(description: string): ContextKey<T> {\n\treturn Object.freeze({ token: Symbol(description) });\n}\n\n/** Derive a context containing one additional or replaced value. */\nexport function withContextValue<T>(key: ContextKey<T>, value: T, parent: Context): Context {\n\treturn new ContextValue(parent, key, value);\n}\n\n/**\n * Derive a context cancelled by either the parent signal or the supplied signal.\n * The parent context remains unchanged.\n */\nexport function withAbortSignal(signal: AbortSignal, context: Context): Context {\n\tconst parentSignal = context.abortSignal;\n\tconst combined = parentSignal === undefined ? signal : AbortSignal.any([parentSignal, signal]);\n\treturn withContextValue(ABORT_SIGNAL_CONTEXT_KEY, combined, context);\n}\n\n/** Derive a context retaining all values except caller cancellation. Intended for mandatory cleanup only. */\nexport function withoutAbortSignal(context: Context): Context {\n\treturn withContextValue(ABORT_SIGNAL_CONTEXT_KEY, undefined, context);\n}\n\n/** Derive an independently cancellable child context. */\nexport function withCancel(context: Context): {\n\treadonly context: Context;\n\treadonly cancel: (reason?: unknown) => void;\n} {\n\tconst controller = new AbortController();\n\treturn {\n\t\tcontext: withAbortSignal(controller.signal, context),\n\t\tcancel: (reason?: unknown) => controller.abort(reason),\n\t};\n}\n\n/**\n * Observe a promise until it settles or the invocation is cancelled.\n * Cancellation rejects only this waiter; it does not cancel the underlying promise.\n */\nexport function awaitWithContext<T>(promise: Promise<T>, context: Context): Promise<T> {\n\tconst signal = context.abortSignal;\n\tif (signal === undefined) return promise;\n\tif (signal.aborted) return Promise.reject(abortError(signal));\n\treturn new Promise<T>((resolve, reject) => {\n\t\tconst onAbort = (): void => reject(abortError(signal));\n\t\tsignal.addEventListener(\"abort\", onAbort, { once: true });\n\t\tvoid promise.then(\n\t\t\t(value) => {\n\t\t\t\tsignal.removeEventListener(\"abort\", onAbort);\n\t\t\t\tresolve(value);\n\t\t\t},\n\t\t\t(error: unknown) => {\n\t\t\t\tsignal.removeEventListener(\"abort\", onAbort);\n\t\t\t\treject(error);\n\t\t\t},\n\t\t);\n\t});\n}\n\nfunction abortError(signal: AbortSignal): Error {\n\tconst reason: unknown = signal.reason;\n\treturn reason instanceof Error ? reason : new DOMException(\"The operation was aborted\", \"AbortError\");\n}\n"]}
@@ -0,0 +1,4 @@
1
+ import type { Op } from "./index.ts";
2
+ /** Apply self-produced trusted operations while copying each touched container once. */
3
+ export declare function applyImmutableTrusted<T>(target: T, operations: readonly Op[]): T;
4
+ //# sourceMappingURL=apply-immutable-trusted.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apply-immutable-trusted.d.ts","sourceRoot":"","sources":["../../src/delta/apply-immutable-trusted.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,EAAE,EAAa,MAAM,YAAY,CAAC;AAYhD,wFAAwF;AACxF,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,UAAU,EAAE,SAAS,EAAE,EAAE,GAAG,CAAC,CA8ChF"}
@@ -0,0 +1,127 @@
1
+ const DATA_DESCRIPTOR = {
2
+ value: undefined,
3
+ writable: true,
4
+ enumerable: true,
5
+ configurable: true,
6
+ };
7
+ /** Apply self-produced trusted operations while copying each touched container once. */
8
+ export function applyImmutableTrusted(target, operations) {
9
+ let root = target;
10
+ const owned = new WeakSet();
11
+ for (const operation of operations) {
12
+ if (operation[0] === "r") {
13
+ root = operation[1];
14
+ continue;
15
+ }
16
+ if (operation[0] === "p" || operation[0] === "m") {
17
+ const copied = copyPath(root, operation[1], operation[1].length, owned);
18
+ root = copied.root;
19
+ if (!Array.isArray(copied.target))
20
+ throw new TypeError("Trusted array operation target is not an array");
21
+ if (operation[0] === "p")
22
+ spliceTrusted(copied.target, operation[2], operation[3], operation[4]);
23
+ else
24
+ permuteTrusted(copied.target, operation[2]);
25
+ continue;
26
+ }
27
+ const path = operation[1];
28
+ const copied = copyPath(root, path, path.length - 1, owned);
29
+ root = copied.root;
30
+ const key = path[path.length - 1];
31
+ switch (operation[0]) {
32
+ case "s":
33
+ defineData(copied.target, key, operation[2]);
34
+ break;
35
+ case "d":
36
+ if (Array.isArray(copied.target)) {
37
+ if (typeof key !== "number")
38
+ throw new TypeError("Trusted array deletion key is not numeric");
39
+ spliceTrusted(copied.target, key, 1, []);
40
+ }
41
+ else
42
+ Reflect.deleteProperty(copied.target, key);
43
+ break;
44
+ case "a": {
45
+ const current = read(copied.target, key);
46
+ if (typeof current !== "string")
47
+ throw new TypeError("Trusted append target is not a string");
48
+ defineData(copied.target, key, `${current}${operation[2]}`);
49
+ break;
50
+ }
51
+ case "t": {
52
+ const current = read(copied.target, key);
53
+ if (typeof current !== "string")
54
+ throw new TypeError("Trusted truncate target is not a string");
55
+ defineData(copied.target, key, current.slice(operation[2]));
56
+ break;
57
+ }
58
+ }
59
+ }
60
+ return root;
61
+ }
62
+ function copyPath(root, path, length, owned) {
63
+ if (!isContainer(root))
64
+ throw new TypeError("Trusted operation root is not a container");
65
+ let copiedRoot = root;
66
+ if (!owned.has(root)) {
67
+ copiedRoot = shallowCopy(root);
68
+ owned.add(copiedRoot);
69
+ }
70
+ let destination = copiedRoot;
71
+ for (let index = 0; index < length; index++) {
72
+ const segment = path[index];
73
+ const child = read(destination, segment);
74
+ if (!isContainer(child))
75
+ throw new TypeError("Trusted operation path is not a container");
76
+ if (owned.has(child)) {
77
+ destination = child;
78
+ continue;
79
+ }
80
+ const copy = shallowCopy(child);
81
+ defineData(destination, segment, copy);
82
+ owned.add(copy);
83
+ destination = copy;
84
+ }
85
+ return { root: copiedRoot, target: destination };
86
+ }
87
+ function shallowCopy(value) {
88
+ if (Array.isArray(value))
89
+ return Array.from(value);
90
+ const result = Object.create(Object.getPrototypeOf(value) === null ? null : Object.prototype);
91
+ for (const key of Object.keys(value))
92
+ defineData(result, key, value[key]);
93
+ return result;
94
+ }
95
+ function read(target, key) {
96
+ return target[key];
97
+ }
98
+ function defineData(target, key, value) {
99
+ DATA_DESCRIPTOR.value = value;
100
+ Object.defineProperty(target, key, DATA_DESCRIPTOR);
101
+ DATA_DESCRIPTOR.value = undefined;
102
+ }
103
+ function spliceTrusted(target, start, remove, items) {
104
+ const oldLength = target.length;
105
+ const delta = items.length - remove;
106
+ if (delta > 0) {
107
+ target.length = oldLength + delta;
108
+ target.copyWithin(start + items.length, start + remove, oldLength);
109
+ }
110
+ else if (delta < 0) {
111
+ target.copyWithin(start + items.length, start + remove, oldLength);
112
+ target.length = oldLength + delta;
113
+ }
114
+ for (let index = 0; index < items.length; index++)
115
+ defineData(target, start + index, items[index]);
116
+ }
117
+ function permuteTrusted(target, permutation) {
118
+ if (target.length !== permutation.length)
119
+ throw new TypeError("Trusted permutation length mismatch");
120
+ const previous = target.slice();
121
+ for (let index = 0; index < permutation.length; index++)
122
+ target[index] = previous[permutation[index]];
123
+ }
124
+ function isContainer(value) {
125
+ return value !== null && typeof value === "object";
126
+ }
127
+ //# sourceMappingURL=apply-immutable-trusted.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apply-immutable-trusted.js","sourceRoot":"","sources":["../../src/delta/apply-immutable-trusted.ts"],"names":[],"mappings":"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","sourcesContent":["import type { JsonValue } from \"../types.ts\";\nimport type { Op, Path, Seg } from \"./index.ts\";\n\ntype JsonContainer = JsonValue[] | Record<string, JsonValue>;\ntype CopiedPath = { root: JsonValue; target: JsonContainer };\n\nconst DATA_DESCRIPTOR: PropertyDescriptor = {\n\tvalue: undefined,\n\twritable: true,\n\tenumerable: true,\n\tconfigurable: true,\n};\n\n/** Apply self-produced trusted operations while copying each touched container once. */\nexport function applyImmutableTrusted<T>(target: T, operations: readonly Op[]): T {\n\tlet root = target as unknown as JsonValue;\n\tconst owned = new WeakSet<object>();\n\tfor (const operation of operations) {\n\t\tif (operation[0] === \"r\") {\n\t\t\troot = operation[1];\n\t\t\tcontinue;\n\t\t}\n\t\tif (operation[0] === \"p\" || operation[0] === \"m\") {\n\t\t\tconst copied = copyPath(root, operation[1], operation[1].length, owned);\n\t\t\troot = copied.root;\n\t\t\tif (!Array.isArray(copied.target)) throw new TypeError(\"Trusted array operation target is not an array\");\n\t\t\tif (operation[0] === \"p\") spliceTrusted(copied.target, operation[2], operation[3], operation[4]);\n\t\t\telse permuteTrusted(copied.target, operation[2]);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst path = operation[1];\n\t\tconst copied = copyPath(root, path, path.length - 1, owned);\n\t\troot = copied.root;\n\t\tconst key = path[path.length - 1]!;\n\t\tswitch (operation[0]) {\n\t\t\tcase \"s\":\n\t\t\t\tdefineData(copied.target, key, operation[2]);\n\t\t\t\tbreak;\n\t\t\tcase \"d\":\n\t\t\t\tif (Array.isArray(copied.target)) {\n\t\t\t\t\tif (typeof key !== \"number\") throw new TypeError(\"Trusted array deletion key is not numeric\");\n\t\t\t\t\tspliceTrusted(copied.target, key, 1, []);\n\t\t\t\t} else Reflect.deleteProperty(copied.target, key);\n\t\t\t\tbreak;\n\t\t\tcase \"a\": {\n\t\t\t\tconst current = read(copied.target, key);\n\t\t\t\tif (typeof current !== \"string\") throw new TypeError(\"Trusted append target is not a string\");\n\t\t\t\tdefineData(copied.target, key, `${current}${operation[2]}`);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"t\": {\n\t\t\t\tconst current = read(copied.target, key);\n\t\t\t\tif (typeof current !== \"string\") throw new TypeError(\"Trusted truncate target is not a string\");\n\t\t\t\tdefineData(copied.target, key, current.slice(operation[2]));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\treturn root as unknown as T;\n}\n\nfunction copyPath(root: JsonValue, path: Path, length: number, owned: WeakSet<object>): CopiedPath {\n\tif (!isContainer(root)) throw new TypeError(\"Trusted operation root is not a container\");\n\tlet copiedRoot = root;\n\tif (!owned.has(root)) {\n\t\tcopiedRoot = shallowCopy(root);\n\t\towned.add(copiedRoot);\n\t}\n\tlet destination = copiedRoot as JsonContainer;\n\tfor (let index = 0; index < length; index++) {\n\t\tconst segment = path[index]!;\n\t\tconst child = read(destination, segment);\n\t\tif (!isContainer(child)) throw new TypeError(\"Trusted operation path is not a container\");\n\t\tif (owned.has(child)) {\n\t\t\tdestination = child;\n\t\t\tcontinue;\n\t\t}\n\t\tconst copy = shallowCopy(child);\n\t\tdefineData(destination, segment, copy);\n\t\towned.add(copy);\n\t\tdestination = copy;\n\t}\n\treturn { root: copiedRoot, target: destination };\n}\n\nfunction shallowCopy(value: JsonContainer): JsonContainer {\n\tif (Array.isArray(value)) return Array.from(value);\n\tconst result = Object.create(Object.getPrototypeOf(value) === null ? null : Object.prototype) as Record<\n\t\tstring,\n\t\tJsonValue\n\t>;\n\tfor (const key of Object.keys(value)) defineData(result, key, value[key]!);\n\treturn result;\n}\n\nfunction read(target: JsonContainer, key: Seg): JsonValue {\n\treturn (target as Record<Seg, JsonValue>)[key]!;\n}\n\nfunction defineData(target: JsonContainer, key: Seg, value: JsonValue): void {\n\tDATA_DESCRIPTOR.value = value;\n\tObject.defineProperty(target, key, DATA_DESCRIPTOR);\n\tDATA_DESCRIPTOR.value = undefined;\n}\n\nfunction spliceTrusted(target: JsonValue[], start: number, remove: number, items: readonly JsonValue[]): void {\n\tconst oldLength = target.length;\n\tconst delta = items.length - remove;\n\tif (delta > 0) {\n\t\ttarget.length = oldLength + delta;\n\t\ttarget.copyWithin(start + items.length, start + remove, oldLength);\n\t} else if (delta < 0) {\n\t\ttarget.copyWithin(start + items.length, start + remove, oldLength);\n\t\ttarget.length = oldLength + delta;\n\t}\n\tfor (let index = 0; index < items.length; index++) defineData(target, start + index, items[index]!);\n}\n\nfunction permuteTrusted(target: JsonValue[], permutation: readonly number[]): void {\n\tif (target.length !== permutation.length) throw new TypeError(\"Trusted permutation length mismatch\");\n\tconst previous = target.slice();\n\tfor (let index = 0; index < permutation.length; index++) target[index] = previous[permutation[index]!]!;\n}\n\nfunction isContainer(value: unknown): value is JsonContainer {\n\treturn value !== null && typeof value === \"object\";\n}\n"]}
@@ -1,5 +1,5 @@
1
- import { type Op } from "../delta/index.ts";
2
1
  import type { JsonValue } from "../types.ts";
2
+ import { type Op } from "./index.ts";
3
3
  /** Compute a compact operation batch from two immutable JSON revisions. */
4
4
  export declare function diffRevisions(before: JsonValue, after: JsonValue): Op[];
5
5
  //# sourceMappingURL=diff.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../../src/delta/diff.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAqB,KAAK,EAAE,EAAmD,MAAM,YAAY,CAAC;AA8fzG,2EAA2E;AAC3E,wBAAgB,aAAa,CAAC,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,GAAG,EAAE,EAAE,CAUvE"}