@elaraai/e3-types 1.0.79 → 1.0.80

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 (153) hide show
  1. package/README.md +1 -1
  2. package/dist/src/api.d.ts +569 -36
  3. package/dist/src/api.d.ts.map +1 -1
  4. package/dist/src/api.js +257 -32
  5. package/dist/src/api.js.map +1 -1
  6. package/dist/src/collection-manifest.d.ts +37 -0
  7. package/dist/src/collection-manifest.d.ts.map +1 -0
  8. package/dist/src/collection-manifest.js +37 -0
  9. package/dist/src/collection-manifest.js.map +1 -0
  10. package/dist/src/dataflow.d.ts +264 -19
  11. package/dist/src/dataflow.d.ts.map +1 -1
  12. package/dist/src/dataflow.js +156 -15
  13. package/dist/src/dataflow.js.map +1 -1
  14. package/dist/src/dataflow.spec.js +184 -27
  15. package/dist/src/dataflow.spec.js.map +1 -1
  16. package/dist/src/dataset-blob.d.ts +75 -15
  17. package/dist/src/dataset-blob.d.ts.map +1 -1
  18. package/dist/src/dataset-blob.js +95 -28
  19. package/dist/src/dataset-blob.js.map +1 -1
  20. package/dist/src/dataset-blob.spec.d.ts +6 -0
  21. package/dist/src/dataset-blob.spec.d.ts.map +1 -0
  22. package/dist/src/dataset-blob.spec.js +92 -0
  23. package/dist/src/dataset-blob.spec.js.map +1 -0
  24. package/dist/src/dataset-ref.d.ts +5 -4
  25. package/dist/src/dataset-ref.d.ts.map +1 -1
  26. package/dist/src/dataset-ref.js +5 -4
  27. package/dist/src/dataset-ref.js.map +1 -1
  28. package/dist/src/environment.d.ts +3 -2
  29. package/dist/src/environment.d.ts.map +1 -1
  30. package/dist/src/environment.js +3 -2
  31. package/dist/src/environment.js.map +1 -1
  32. package/dist/src/execution.d.ts +112 -18
  33. package/dist/src/execution.d.ts.map +1 -1
  34. package/dist/src/execution.js +174 -45
  35. package/dist/src/execution.js.map +1 -1
  36. package/dist/src/execution.spec.d.ts +6 -0
  37. package/dist/src/execution.spec.d.ts.map +1 -0
  38. package/dist/src/execution.spec.js +108 -0
  39. package/dist/src/execution.spec.js.map +1 -0
  40. package/dist/src/function.d.ts +14 -12
  41. package/dist/src/function.d.ts.map +1 -1
  42. package/dist/src/function.js +17 -32
  43. package/dist/src/function.js.map +1 -1
  44. package/dist/src/index.d.ts +16 -12
  45. package/dist/src/index.d.ts.map +1 -1
  46. package/dist/src/index.js +25 -20
  47. package/dist/src/index.js.map +1 -1
  48. package/dist/src/lock.d.ts +68 -22
  49. package/dist/src/lock.d.ts.map +1 -1
  50. package/dist/src/lock.js +33 -22
  51. package/dist/src/lock.js.map +1 -1
  52. package/dist/src/names.d.ts +21 -0
  53. package/dist/src/names.d.ts.map +1 -0
  54. package/dist/src/names.js +31 -0
  55. package/dist/src/names.js.map +1 -0
  56. package/dist/src/package.d.ts +11 -78
  57. package/dist/src/package.d.ts.map +1 -1
  58. package/dist/src/package.js +13 -66
  59. package/dist/src/package.js.map +1 -1
  60. package/dist/src/record.d.ts +582 -18
  61. package/dist/src/record.d.ts.map +1 -1
  62. package/dist/src/record.js +405 -7
  63. package/dist/src/record.js.map +1 -1
  64. package/dist/src/{stream.spec.d.ts → record.spec.d.ts} +1 -1
  65. package/dist/src/record.spec.d.ts.map +1 -0
  66. package/dist/src/record.spec.js +143 -0
  67. package/dist/src/record.spec.js.map +1 -0
  68. package/dist/src/release.d.ts +36 -0
  69. package/dist/src/release.d.ts.map +1 -0
  70. package/dist/src/release.js +71 -0
  71. package/dist/src/release.js.map +1 -0
  72. package/dist/src/release.spec.d.ts +6 -0
  73. package/dist/src/release.spec.d.ts.map +1 -0
  74. package/dist/src/release.spec.js +42 -0
  75. package/dist/src/release.spec.js.map +1 -0
  76. package/dist/src/repository.d.ts +64 -0
  77. package/dist/src/repository.d.ts.map +1 -0
  78. package/dist/src/repository.js +49 -0
  79. package/dist/src/repository.js.map +1 -0
  80. package/dist/src/runner.d.ts +16 -62
  81. package/dist/src/runner.d.ts.map +1 -1
  82. package/dist/src/runner.js +16 -79
  83. package/dist/src/runner.js.map +1 -1
  84. package/dist/src/runner.spec.js +11 -85
  85. package/dist/src/runner.spec.js.map +1 -1
  86. package/dist/src/structure.d.ts +0 -57
  87. package/dist/src/structure.d.ts.map +1 -1
  88. package/dist/src/structure.js +0 -3
  89. package/dist/src/structure.js.map +1 -1
  90. package/dist/src/task.d.ts +210 -318
  91. package/dist/src/task.d.ts.map +1 -1
  92. package/dist/src/task.js +117 -472
  93. package/dist/src/task.js.map +1 -1
  94. package/dist/src/task.spec.js +62 -154
  95. package/dist/src/task.spec.js.map +1 -1
  96. package/dist/src/transfer.d.ts +16 -22
  97. package/dist/src/transfer.d.ts.map +1 -1
  98. package/dist/src/transfer.js +16 -22
  99. package/dist/src/transfer.js.map +1 -1
  100. package/dist/src/transfer.spec.js +4 -34
  101. package/dist/src/transfer.spec.js.map +1 -1
  102. package/dist/src/unit-plan.d.ts +130 -0
  103. package/dist/src/unit-plan.d.ts.map +1 -0
  104. package/dist/src/unit-plan.js +107 -0
  105. package/dist/src/unit-plan.js.map +1 -0
  106. package/dist/src/unit-plan.spec.d.ts +6 -0
  107. package/dist/src/unit-plan.spec.d.ts.map +1 -0
  108. package/dist/src/unit-plan.spec.js +29 -0
  109. package/dist/src/unit-plan.spec.js.map +1 -0
  110. package/dist/src/workspace.d.ts +22 -11
  111. package/dist/src/workspace.d.ts.map +1 -1
  112. package/dist/src/workspace.js +10 -11
  113. package/dist/src/workspace.js.map +1 -1
  114. package/package.json +3 -3
  115. package/src/api.ts +276 -32
  116. package/src/collection-manifest.ts +53 -0
  117. package/src/dataflow.spec.ts +223 -31
  118. package/src/dataflow.ts +183 -26
  119. package/src/dataset-blob.spec.ts +117 -0
  120. package/src/dataset-blob.ts +157 -21
  121. package/src/dataset-ref.ts +5 -4
  122. package/src/environment.ts +3 -2
  123. package/src/execution.spec.ts +114 -0
  124. package/src/execution.ts +175 -51
  125. package/src/function.ts +19 -32
  126. package/src/index.ts +122 -39
  127. package/src/lock.ts +39 -22
  128. package/src/names.ts +39 -0
  129. package/src/package.ts +15 -71
  130. package/src/record.spec.ts +195 -0
  131. package/src/record.ts +489 -7
  132. package/src/release.spec.ts +47 -0
  133. package/src/release.ts +69 -0
  134. package/src/repository.ts +67 -0
  135. package/src/runner.spec.ts +11 -94
  136. package/src/runner.ts +16 -80
  137. package/src/structure.ts +0 -8
  138. package/src/task.spec.ts +67 -188
  139. package/src/task.ts +140 -522
  140. package/src/transfer.spec.ts +4 -51
  141. package/src/transfer.ts +16 -22
  142. package/src/unit-plan.spec.ts +31 -0
  143. package/src/unit-plan.ts +121 -0
  144. package/src/workspace.ts +13 -11
  145. package/dist/src/stream.d.ts +0 -97
  146. package/dist/src/stream.d.ts.map +0 -1
  147. package/dist/src/stream.js +0 -134
  148. package/dist/src/stream.js.map +0 -1
  149. package/dist/src/stream.spec.d.ts.map +0 -1
  150. package/dist/src/stream.spec.js +0 -60
  151. package/dist/src/stream.spec.js.map +0 -1
  152. package/src/stream.spec.ts +0 -88
  153. package/src/stream.ts +0 -170
@@ -10,12 +10,13 @@
10
10
  * a content-addressed commit object, so a record's history is a git-style chain
11
11
  * — the audit trail of who changed what, when, superseding which state.
12
12
  *
13
- * The record's current state is an ordinary `value` dataset ref (its `hash`
14
- * points at the plain state blob), so every existing read path — task inputs,
15
- * `e3 get`, the UI `Data.bind` read — works on records unchanged. Only the
16
- * write protocol differs.
13
+ * The record's current state is an ordinary `value` dataset ref. Its `hash`
14
+ * names the state or, once the record declares an index, a `$record` state
15
+ * naming it, and every read path resolves it, so task inputs, `e3 get` and the
16
+ * UI `Data.bind` read work on records unchanged. Only the write protocol
17
+ * differs.
17
18
  */
18
- import { StructType, StringType, OptionType, DateTimeType, ArrayType, DictType, EastTypeType } from '@elaraai/east';
19
+ import { ArrayType, BooleanType, DateTimeType, DictType, EastTypeType, FunctionType, NullType, OptionType, PatchType, StringType, StructType, VariantType, decodeBeast2For, dictPatchOpsType, setPatchOpsType, toEastTypeValue, } from '@elaraai/east';
19
20
  import { RunnerType } from './runner.js';
20
21
  /**
21
22
  * A single commit in a record's history.
@@ -38,6 +39,61 @@ export const RecordCommitType = StructType({
38
39
  actor: StringType,
39
40
  /** Commit wall-clock time. */
40
41
  at: DateTimeType,
42
+ /** The mutation delta this commit applied: one sorted collection of the
43
+ * primary and index changes, addressed by key. History then shows WHAT
44
+ * changed without diffing two states, and an undo can be synthesised from
45
+ * it. No state depends on it — every state past and present reads
46
+ * identically if every delta were deleted — but it is a collection like any
47
+ * other, a manifest naming segment objects, so a collector walks it as a
48
+ * value rather than marking it as a leaf the way it marks `args`. */
49
+ delta: OptionType(StringType),
50
+ });
51
+ const decodeCurrentCommit = decodeBeast2For(RecordCommitType);
52
+ /**
53
+ * Decode a `RecordCommit`.
54
+ *
55
+ * @remarks
56
+ * Stored state, so it changes by hard cutover: a repository an older e3 wrote
57
+ * is re-created rather than read, and this says so.
58
+ *
59
+ * @param data - the stored bytes
60
+ * @returns the commit
61
+ * @throws {Error} When the bytes are not a current commit — one an older e3
62
+ * wrote, whose repository is re-created.
63
+ */
64
+ export function decodeRecordCommit(data) {
65
+ try {
66
+ return decodeCurrentCommit(data);
67
+ }
68
+ catch (err) {
69
+ throw new Error(`the record commit does not decode: an older e3 wrote this repository — re-create it: deploy again and import its data again ` +
70
+ `(${err instanceof Error ? err.message : String(err)})`);
71
+ }
72
+ }
73
+ /**
74
+ * How a mutation says what it changed.
75
+ *
76
+ * - `reduce` — `(State, …Args) => State`, the original surface. Its body and
77
+ * its diff see the whole state, so its RUNNER cost stays O(state); only its
78
+ * write, on a Dict or Set record, is O(touched).
79
+ * - `edit` — `(State, …Args, Edit) => Null`, the lazy write: the body reads the
80
+ * state it is given, lazily, and writes through an `edit` capability, so the
81
+ * body reads only the entries it touches and the commit rewrites only the
82
+ * segments they live in. The state reaches the runner as its manifest, the
83
+ * segments linked, so nothing on the way reads it whole either.
84
+ * - `patch` — no body; the argument is `PatchType(State)`. What an interactive
85
+ * edit from a view sends. On a record with no index a patch of per-key
86
+ * changes runs nothing at all, the only write whose cost is independent of
87
+ * the record's size.
88
+ *
89
+ * @remarks
90
+ * A variant, so a mutation object decodes only with one of these: a form this
91
+ * e3 does not know is refused where the object is read, not run as another.
92
+ */
93
+ export const MutationFormType = VariantType({
94
+ edit: NullType,
95
+ patch: NullType,
96
+ reduce: NullType,
41
97
  });
42
98
  /**
43
99
  * A mutation: the write half of the function machinery (CQRS — `e3.function`
@@ -51,17 +107,359 @@ export const MutationObjectType = StructType({
51
107
  bodyIr: StringType,
52
108
  /** The EXTRA positional parameter types after the state parameter. */
53
109
  argTypes: ArrayType(EastTypeType),
54
- /** Author-chosen runtime; resolved to argv by runnerToArgv. */
110
+ /** Author-chosen runtime the mutation's unit runs on. */
55
111
  runner: RunnerType,
112
+ /** Which write form this is — see {@link MutationFormType}. */
113
+ form: MutationFormType,
114
+ /** Hash of the generated program's IR bundle: the one thing that actually
115
+ * runs, whatever the form. For the `patch` form `bodyIr` names it too,
116
+ * there being no author body. */
117
+ programIr: StringType,
56
118
  });
119
+ const decodeCurrentMutation = decodeBeast2For(MutationObjectType);
120
+ /**
121
+ * Decode a `MutationObject`.
122
+ *
123
+ * @remarks
124
+ * A package-borne wire, so it changes by hard cutover: a package exported by
125
+ * an older SDK is re-exported with the current one, and this says so.
126
+ *
127
+ * @param data - the stored bytes
128
+ * @returns the mutation object
129
+ * @throws {Error} When the bytes are not a current mutation object — a package
130
+ * exported by an older SDK, which is re-exported with the current one.
131
+ */
132
+ export function decodeMutationObject(data) {
133
+ try {
134
+ return decodeCurrentMutation(data);
135
+ }
136
+ catch (err) {
137
+ throw new Error(`the mutation object does not decode: the package was exported by an older e3 SDK — re-export it with the current one ` +
138
+ `(${err instanceof Error ? err.message : String(err)})`);
139
+ }
140
+ }
141
+ /**
142
+ * A secondary index over a record: an East function of an entry, and the
143
+ * collection it builds.
144
+ *
145
+ * An index is a second canonical collection whose sort order IS the query
146
+ * order, stored as a segment manifest exactly like the primary and maintained
147
+ * inside the same commit — so a view by an attribute of the row, or by a
148
+ * related entity a row names many of, is a page rather than a scan.
149
+ *
150
+ * The functions are pure for the reason a reducer is: they run again on every
151
+ * commit and in every bulk build, and a maintained index must equal a rebuilt
152
+ * one to the byte.
153
+ */
154
+ export const RecordIndexObjectType = StructType({
155
+ /** Hash of the encoded EastIR bundle: `(K, V) -> IK`, or `-> Set<IK>` when
156
+ * {@link multi}. */
157
+ keyIr: StringType,
158
+ /** Declared with `keys` (a Set return) rather than `key`: one entry per
159
+ * element, so a row that names five resources appears under five keys. */
160
+ multi: BooleanType,
161
+ /** Hash of the covering projection's IR bundle, `(K, V) -> P`; none when the
162
+ * index carries no value and `P` is Null. */
163
+ valueIr: OptionType(StringType),
164
+ /** `IK` — the index key an entry sorts under. */
165
+ keyType: EastTypeType,
166
+ /** `P` — the covering projection's type; Null when there is none. */
167
+ valueType: EastTypeType,
168
+ /** Hash of the generated build program's IR bundle: `(piece, emit) =>
169
+ * Null`, which emits the index entries of a piece of the primary, in any
170
+ * order. */
171
+ buildIr: StringType,
172
+ /** Author-chosen runtime the index's functions run on. */
173
+ runner: RunnerType,
174
+ });
175
+ /**
176
+ * How a migration says what it changes.
177
+ *
178
+ * - `value` — `(Old) => New`, the whole state: one unit, whose runner opens the
179
+ * state lazily, so it costs what the body reads.
180
+ * - `rows` — a Dict's rows, `(K, V1) => V2` with the keys unchanged, or an
181
+ * Array's elements, `(T1) => T2` in order: a task split over the state, a
182
+ * piece at a time.
183
+ * - `rekey` — a Dict's entries, `(K1, V1) => { key: K2, value: V2 }`, or a
184
+ * Set's elements, `(T1) => T2`: the same split task, whose pieces' outputs
185
+ * merge by key. Two rows landing on one key fail, naming it; two elements
186
+ * landing on one are one element.
187
+ *
188
+ * @remarks
189
+ * A variant, so a migration object decodes only with one of these: a deploy
190
+ * never runs a step of a form it does not know as another.
191
+ */
192
+ export const MigrationFormType = VariantType({
193
+ rekey: NullType,
194
+ rows: NullType,
195
+ value: NullType,
196
+ });
197
+ /**
198
+ * A migration: one step of a record's declared chain, which a deploy runs over
199
+ * a workspace's state that has not applied it.
200
+ *
201
+ * @remarks
202
+ * A step changes the record's type, or keeps the type and changes its values.
203
+ * The chain is the record object's `migrations`, in order, and a workspace's
204
+ * record ref names the steps it has applied by name, never by this object's
205
+ * hash: the hash covers the step's IR, which carries source locations and
206
+ * changes with the SDK that exported it, so an unchanged step would read as
207
+ * edited.
208
+ */
209
+ export const MigrationObjectType = StructType({
210
+ /** Which form this is — see {@link MigrationFormType}. */
211
+ form: MigrationFormType,
212
+ /** The record's type before the step. */
213
+ from: EastTypeType,
214
+ /** The record's type after it. */
215
+ to: EastTypeType,
216
+ /** Hash of the author's function's IR bundle, which a `value` step runs. */
217
+ bodyIr: StringType,
218
+ /** Hash of the generated program's IR bundle, `(piece, emit) => Null`,
219
+ * which a `rows` or `rekey` step runs over each piece of the state; empty
220
+ * for a `value` step. */
221
+ programIr: StringType,
222
+ /** Author-chosen runtime the step runs on. */
223
+ runner: RunnerType,
224
+ });
225
+ const decodeCurrentMigration = decodeBeast2For(MigrationObjectType);
226
+ /**
227
+ * Decode a `MigrationObject`.
228
+ *
229
+ * @remarks
230
+ * A package-borne wire, so it changes by hard cutover: a package exported by
231
+ * an older SDK is re-exported with the current one, and this says so.
232
+ *
233
+ * @param data - the stored bytes
234
+ * @returns the migration object
235
+ * @throws {Error} When the bytes are not a current migration object — a
236
+ * package exported by an older SDK, which is re-exported with the current
237
+ * one.
238
+ */
239
+ export function decodeMigrationObject(data) {
240
+ try {
241
+ return decodeCurrentMigration(data);
242
+ }
243
+ catch (err) {
244
+ throw new Error(`the migration object does not decode: the package was exported by an older e3 SDK — re-export it with the current one ` +
245
+ `(${err instanceof Error ? err.message : String(err)})`);
246
+ }
247
+ }
57
248
  /**
58
249
  * Record object stored in the object store, referenced by name from
59
- * `PackageObject.records`. Carries the record's dataset path and its mutations.
250
+ * `PackageObject.records`. Carries the record's dataset path, its mutations,
251
+ * its secondary indexes and its migration chain.
60
252
  */
61
253
  export const RecordObjectType = StructType({
62
254
  /** refPath of the record's dataset, e.g. `records/orders`. */
63
255
  path: StringType,
64
256
  /** Mutations by name -> MutationObject hash. */
65
257
  mutations: DictType(StringType, StringType),
258
+ /** Secondary indexes by name -> RecordIndexObject hash. */
259
+ indexes: DictType(StringType, StringType),
260
+ /** The declared migration chain, in order: each step's name, an identifier
261
+ * unique on the record, and its MigrationObject hash. Empty when the
262
+ * record declares none. */
263
+ migrations: ArrayType(StructType({ name: StringType, migration: StringType })),
264
+ });
265
+ const decodeCurrentRecord = decodeBeast2For(RecordObjectType);
266
+ /**
267
+ * Decode a `RecordObject`.
268
+ *
269
+ * @remarks
270
+ * A package-borne wire, so it changes by hard cutover: a package exported by
271
+ * an older SDK is re-exported with the current one, and this says so.
272
+ *
273
+ * @param data - the stored bytes
274
+ * @returns the record object
275
+ * @throws {Error} When the bytes are not a current record object — a package
276
+ * exported by an older SDK, which is re-exported with the current one.
277
+ */
278
+ export function decodeRecordObject(data) {
279
+ try {
280
+ return decodeCurrentRecord(data);
281
+ }
282
+ catch (err) {
283
+ throw new Error(`the record object does not decode: the package was exported by an older e3 SDK — re-export it with the current one ` +
284
+ `(${err instanceof Error ? err.message : String(err)})`);
285
+ }
286
+ }
287
+ /** The `kind` tag a record's state object carries once the record has an
288
+ * index: what tells a reader that the ref names a table of manifests rather
289
+ * than one of them. */
290
+ export const RECORD_STATE_KIND = '$record';
291
+ /**
292
+ * A record's state when it has secondary indexes: one small object naming the
293
+ * primary's manifest and every index's.
294
+ *
295
+ * @remarks
296
+ * One state object, one commit, one conditional ref write — so the primary and
297
+ * every index are consistent at every commit, history carries them, reading at
298
+ * an older commit resolves them, compaction keeps them by copying one hash,
299
+ * and the collector walks them. A record with no index keeps the plain
300
+ * manifest, so this is additive: the door that resolves a record's state
301
+ * accepts either.
302
+ */
303
+ export const RecordStateType = StructType({
304
+ /** Always {@link RECORD_STATE_KIND}. */
305
+ kind: StringType,
306
+ /** CollectionManifest hash of the primary. */
307
+ primary: StringType,
308
+ /** Index name -> the manifest it is held in and the index object it was
309
+ * built under. Deploy compares the second against the package's to decide
310
+ * whether an index is built, dropped or kept. */
311
+ indexes: DictType(StringType, StructType({
312
+ /** CollectionManifest hash of the index collection. */
313
+ manifest: StringType,
314
+ /** RecordIndexObject hash the index was built under. */
315
+ index: StringType,
316
+ })),
66
317
  });
318
+ /** The record state's field names, in wire order — read from the type itself
319
+ * so the recognizer and the type cannot drift. */
320
+ const RECORD_STATE_FIELDS = toEastTypeValue(RecordStateType).value.map((f) => f.name);
321
+ /**
322
+ * Whether a decoded object is a record's `$record` state.
323
+ *
324
+ * @remarks
325
+ * Matched on the exact field set plus the tag, as every object recognizer is:
326
+ * a record's own state is an arbitrary user struct flowing through the same
327
+ * dispatch, and misreading one as a state table would probe its fields as
328
+ * object hashes.
329
+ *
330
+ * @param typeValue - the object's decoded root type
331
+ * @param value - the decoded value, when the tag is to be checked too
332
+ * @returns whether the object is a record state
333
+ */
334
+ export function isRecordStateType(typeValue, value) {
335
+ if (typeValue.type !== 'Struct')
336
+ return false;
337
+ const fields = typeValue.value;
338
+ if (fields.length !== RECORD_STATE_FIELDS.length
339
+ || !fields.every((f, i) => f.name === RECORD_STATE_FIELDS[i]))
340
+ return false;
341
+ return value === undefined || value?.kind === RECORD_STATE_KIND;
342
+ }
343
+ /**
344
+ * The collection an index is held in: `Dict<{ik, k}, P>`.
345
+ *
346
+ * @remarks
347
+ * `ik` first, so every entry with one index key is a contiguous run — ordered
348
+ * by primary key inside it — and a RANGE of index keys is one contiguous run
349
+ * too. `k` makes the entry unique and gives the run its inner order.
350
+ *
351
+ * @param keyType - the record's primary key type, `K`
352
+ * @param indexKeyType - the index key the function computes, `IK`
353
+ * @param valueType - the covering projection's type, `P`
354
+ * @returns the index collection's East type
355
+ */
356
+ export function indexCollectionType(keyType, indexKeyType, valueType) {
357
+ return DictType(StructType({ ik: indexKeyType, k: keyType }), valueType);
358
+ }
359
+ /**
360
+ * The op type of one target's changes: what one touched key carries.
361
+ *
362
+ * @remarks
363
+ * Exactly the op type of that target's own `PatchType`, which is what makes
364
+ * applying a delta's run for one target literally
365
+ * `applyFor(targetType)(segment, variant('patch', ops))` — `ConflictError` and
366
+ * all — rather than a second apply implementation that could disagree with the
367
+ * first.
368
+ *
369
+ * @param collectionType - the target's collection type (a Dict or a Set)
370
+ * @returns the op variant for one key of it
371
+ * @throws {Error} When the type is not a Dict or a Set — the kinds whose
372
+ * patches are sparse and addressed by key.
373
+ */
374
+ export function patchOpsType(collectionType) {
375
+ const collection = collectionType;
376
+ if (collection.type === 'Dict')
377
+ return dictPatchOpsType(collection.value);
378
+ if (collection.type === 'Set')
379
+ return setPatchOpsType(collection.key);
380
+ throw new Error(`a mutation delta addresses Dict and Set targets by key; this one holds ${collection.type}`);
381
+ }
382
+ /**
383
+ * The case of a mutation delta that says the write went stale: its key says
384
+ * what went stale, in words, and its op is Null.
385
+ *
386
+ * @remarks
387
+ * A write reaches the record two ways — the program emits a delta and the
388
+ * engine applies it, or the engine applies the client's patch directly — and
389
+ * both meet the same situation: an op that disagrees with the state it lands
390
+ * on. The apply reports it as a conflict, which a caller retries. A program
391
+ * that meets it — a patch whose `before` the record no longer holds, an
392
+ * update of a row that no longer matches, a delete of a key the record does
393
+ * not hold — emits one of these rather than failing, which a caller would not
394
+ * retry. The name sorts before every target's, since index names are
395
+ * identifiers, so a stale delta says so in its first entry.
396
+ */
397
+ export const DELTA_CONFLICT = '$conflict';
398
+ /**
399
+ * The type of a mutation delta: every target's changes in ONE sorted
400
+ * collection.
401
+ *
402
+ * @remarks
403
+ * One variant case per target — `primary` plus one per declared index, which
404
+ * is why `primary` is a reserved index name — and {@link DELTA_CONFLICT}, which
405
+ * sorts first. Canonical order puts every target's ops in one contiguous run,
406
+ * in that target's own key order, so the apply takes the delta one target at
407
+ * a time, a segment at a time, and a delta is a pageable collection like any
408
+ * other.
409
+ *
410
+ * Derived on demand and never stored as a type: a delta blob is
411
+ * self-describing, and deriving it means it cannot drift from the record's
412
+ * declarations.
413
+ *
414
+ * @param targets - the primary and each index, in canonical case order
415
+ * @returns `Dict<DeltaKey, DeltaOp>`
416
+ */
417
+ export function mutationDeltaType(targets) {
418
+ const keys = { [DELTA_CONFLICT]: StringType };
419
+ const ops = { [DELTA_CONFLICT]: NullType };
420
+ for (const target of targets) {
421
+ keys[target.name] = target.keyType;
422
+ ops[target.name] = patchOpsType(target.collectionType);
423
+ }
424
+ return DictType(VariantType(keys), VariantType(ops));
425
+ }
426
+ export function editTypeOf(recordType) {
427
+ const dict = recordType;
428
+ if (dict.type !== 'Dict') {
429
+ throw new Error(`an edit mutation writes a Dict record; this one holds ${dict.type}`);
430
+ }
431
+ return StructType({
432
+ set: FunctionType([dict.key, dict.value], NullType),
433
+ delete: FunctionType([dict.key], NullType),
434
+ update: FunctionType([dict.key, PatchType(dict.value)], NullType),
435
+ });
436
+ }
437
+ /**
438
+ * One window of an index read: an ORDERED array, never a dict.
439
+ *
440
+ * @remarks
441
+ * A Dict value re-sorts by its own key, which would throw away the index order
442
+ * the page was asked for — so the wire type of an index window is positional,
443
+ * in index order, carrying every key the client may need. Whatever renders an
444
+ * index-ordered view consumes this shape; it is a contract the data layer sets
445
+ * and the UI meets, not the reverse.
446
+ *
447
+ * @param keyType - the record's primary key type, `K`
448
+ * @param indexKeyType - the index key, `IK`
449
+ * @param valueType - the covering projection's type, `P`
450
+ * @param rowType - the record's row type, `V`
451
+ * @returns the window's East type
452
+ */
453
+ export function indexWindowType(keyType, indexKeyType, valueType, rowType) {
454
+ return ArrayType(StructType({
455
+ /** The index key. */
456
+ ik: indexKeyType,
457
+ /** The primary key, so a client can address the row it came from. */
458
+ key: keyType,
459
+ /** The covering projection; Null when the index carries none. */
460
+ value: valueType,
461
+ /** The primary row — `some(...)` when the read asked for a join. */
462
+ row: OptionType(rowType),
463
+ }));
464
+ }
67
465
  //# sourceMappingURL=record.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"record.js","sourceRoot":"","sources":["../../src/record.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,YAAY,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY,EAAe,MAAM,eAAe,CAAC;AACjI,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,UAAU,CAAC;IACzC,yDAAyD;IACzD,MAAM,EAAE,UAAU,CAAC,UAAU,CAAC;IAC9B,6EAA6E;IAC7E,KAAK,EAAE,UAAU;IACjB,4EAA4E;IAC5E,QAAQ,EAAE,UAAU;IACpB,iFAAiF;IACjF,IAAI,EAAE,UAAU,CAAC,UAAU,CAAC;IAC5B,2EAA2E;IAC3E,KAAK,EAAE,UAAU;IACjB,8BAA8B;IAC9B,EAAE,EAAE,YAAY;CACjB,CAAC,CAAC;AAIH;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,UAAU,CAAC;IAC3C,kFAAkF;IAClF,MAAM,EAAE,UAAU;IAClB,sEAAsE;IACtE,QAAQ,EAAE,SAAS,CAAC,YAAY,CAAC;IACjC,+DAA+D;IAC/D,MAAM,EAAE,UAAU;CACnB,CAAC,CAAC;AAIH;;;GAGG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,UAAU,CAAC;IACzC,8DAA8D;IAC9D,IAAI,EAAE,UAAU;IAChB,gDAAgD;IAChD,SAAS,EAAE,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;CAC5C,CAAC,CAAC"}
1
+ {"version":3,"file":"record.js","sourceRoot":"","sources":["../../src/record.ts"],"names":[],"mappings":"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"}
@@ -3,4 +3,4 @@
3
3
  * Dual-licensed under AGPL-3.0 and commercial license. See LICENSE for details.
4
4
  */
5
5
  export {};
6
- //# sourceMappingURL=stream.spec.d.ts.map
6
+ //# sourceMappingURL=record.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"record.spec.d.ts","sourceRoot":"","sources":["../../src/record.spec.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Copyright (c) 2025 Elara AI Pty Ltd
3
+ * Dual-licensed under AGPL-3.0 and commercial license. See LICENSE for details.
4
+ */
5
+ /**
6
+ * The record wire types' decoders: each reads the current shape, and refuses
7
+ * an older one naming the fix — a commit an older e3 wrote, whose repository
8
+ * is re-created, and a mutation, migration or record object an older SDK
9
+ * exported, whose package is re-exported.
10
+ */
11
+ import { describe, it } from 'node:test';
12
+ import assert from 'node:assert/strict';
13
+ import { DateTimeType, DictType, IntegerType, OptionType, StringType, StructType, ArrayType, EastTypeType, encodeBeast2For, isTypeValueEqual, some, toEastTypeValue, variant } from '@elaraai/east';
14
+ import { RunnerType } from './runner.js';
15
+ import { MigrationObjectType, MutationObjectType, RecordCommitType, RecordObjectType, decodeMigrationObject, decodeMutationObject, decodeRecordCommit, decodeRecordObject, } from './record.js';
16
+ describe('decodeRecordCommit', () => {
17
+ const commit = {
18
+ parent: some('a'.repeat(64)),
19
+ state: 'b'.repeat(64),
20
+ mutation: 'increment',
21
+ args: some('c'.repeat(64)),
22
+ actor: 'cli:test',
23
+ at: new Date(0),
24
+ delta: some('d'.repeat(64)),
25
+ };
26
+ it('round-trips a current commit', () => {
27
+ assert.deepEqual(decodeRecordCommit(encodeBeast2For(RecordCommitType)(commit)), commit);
28
+ });
29
+ it('refuses a commit an older e3 wrote before deltas existed, naming the fix', () => {
30
+ const PreDeltaCommitType = StructType({
31
+ parent: OptionType(StringType), state: StringType, mutation: StringType,
32
+ args: OptionType(StringType), actor: StringType, at: DateTimeType,
33
+ });
34
+ const { delta: _delta, ...older } = commit;
35
+ assert.throws(() => decodeRecordCommit(encodeBeast2For(PreDeltaCommitType)(older)), /^Error: the record commit does not decode: an older e3 wrote this repository — re-create it: deploy again and import its data again \(/);
36
+ });
37
+ it('refuses bytes of no commit shape', () => {
38
+ assert.throws(() => decodeRecordCommit(encodeBeast2For(IntegerType)(7n)), /the record commit does not decode/);
39
+ });
40
+ });
41
+ describe('decodeMutationObject', () => {
42
+ const mutation = {
43
+ bodyIr: 'a'.repeat(64),
44
+ argTypes: [toEastTypeValue(IntegerType)],
45
+ runner: variant('east_node', { platforms: ['@elaraai/east-node-std'] }),
46
+ form: variant('edit', null),
47
+ programIr: 'b'.repeat(64),
48
+ };
49
+ /** A decoded mutation with its arg types checked off separately: a decoded
50
+ * type value carries no memoized identity, so only the East comparison
51
+ * says whether two of them are the same type. */
52
+ function read(bytes) {
53
+ const decoded = decodeMutationObject(bytes);
54
+ assert.equal(decoded.argTypes.length, 1);
55
+ assert.ok(isTypeValueEqual(decoded.argTypes[0], toEastTypeValue(IntegerType)));
56
+ const { argTypes: _argTypes, ...rest } = decoded;
57
+ return rest;
58
+ }
59
+ it('round-trips a current mutation', () => {
60
+ const { argTypes: _argTypes, ...expected } = mutation;
61
+ assert.deepEqual(read(encodeBeast2For(MutationObjectType)(mutation)), expected);
62
+ });
63
+ it('refuses a mutation an older SDK exported before the delta, naming the fix', () => {
64
+ const PreDeltaMutationType = StructType({
65
+ bodyIr: StringType, argTypes: ArrayType(EastTypeType), runner: RunnerType,
66
+ });
67
+ const { form: _form, programIr: _programIr, argTypes: _argTypes, ...older } = mutation;
68
+ assert.throws(() => decodeMutationObject(encodeBeast2For(PreDeltaMutationType)({ ...older, argTypes: mutation.argTypes })), /^Error: the mutation object does not decode: the package was exported by an older e3 SDK — re-export it with the current one \(/);
69
+ });
70
+ it('refuses a mutation whose form is a String, as an older SDK wrote it, whatever the string', () => {
71
+ // A String form decoded as any string, and a form e3 does not know ran as
72
+ // whichever form its checks fell through to.
73
+ const StringFormMutationType = StructType({
74
+ bodyIr: StringType, argTypes: ArrayType(EastTypeType), runner: RunnerType, form: StringType, programIr: StringType,
75
+ });
76
+ for (const form of ['edit', 'bogus']) {
77
+ assert.throws(() => decodeMutationObject(encodeBeast2For(StringFormMutationType)({ ...mutation, form })), /^Error: the mutation object does not decode: the package was exported by an older e3 SDK — re-export it with the current one \(/, form);
78
+ }
79
+ });
80
+ });
81
+ describe('decodeMigrationObject', () => {
82
+ const RowV1Type = StructType({ name: StringType });
83
+ const RowV2Type = StructType({ name: StringType, shift: IntegerType });
84
+ const migration = {
85
+ form: variant('rows', null),
86
+ from: toEastTypeValue(DictType(StringType, RowV1Type)),
87
+ to: toEastTypeValue(DictType(StringType, RowV2Type)),
88
+ bodyIr: 'a'.repeat(64),
89
+ programIr: 'b'.repeat(64),
90
+ runner: variant('east_node', { platforms: ['@elaraai/east-node-std'] }),
91
+ };
92
+ it('round-trips a current migration, its types among it', () => {
93
+ const decoded = decodeMigrationObject(encodeBeast2For(MigrationObjectType)(migration));
94
+ // A decoded type value carries no memoized identity, so only the East
95
+ // comparison says whether two of them are the same type.
96
+ assert.ok(isTypeValueEqual(decoded.from, migration.from));
97
+ assert.ok(isTypeValueEqual(decoded.to, migration.to));
98
+ assert.ok(!isTypeValueEqual(decoded.from, decoded.to));
99
+ const { from: _from, to: _to, ...rest } = decoded;
100
+ const { from: _f, to: _t, ...expected } = migration;
101
+ assert.deepEqual(rest, expected);
102
+ });
103
+ it('refuses the bytes of another object, naming the re-export', () => {
104
+ const mutation = {
105
+ bodyIr: 'a'.repeat(64), argTypes: [], runner: migration.runner, form: variant('reduce', null), programIr: '',
106
+ };
107
+ assert.throws(() => decodeMigrationObject(encodeBeast2For(MutationObjectType)(mutation)), /^Error: the migration object does not decode: the package was exported by an older e3 SDK — re-export it with the current one \(/);
108
+ });
109
+ it('refuses a migration whose form is a String, as an older SDK wrote it, whatever the string', () => {
110
+ // A deploy ran a String form it did not know as a split step.
111
+ const StringFormMigrationType = StructType({
112
+ form: StringType, from: EastTypeType, to: EastTypeType, bodyIr: StringType, programIr: StringType, runner: RunnerType,
113
+ });
114
+ for (const form of ['rows', 'bogus']) {
115
+ assert.throws(() => decodeMigrationObject(encodeBeast2For(StringFormMigrationType)({ ...migration, form })), /^Error: the migration object does not decode: the package was exported by an older e3 SDK — re-export it with the current one \(/, form);
116
+ }
117
+ });
118
+ });
119
+ describe('decodeRecordObject', () => {
120
+ const record = {
121
+ path: 'records/orders',
122
+ mutations: new Map([['place', 'a'.repeat(64)]]),
123
+ indexes: new Map([['by_status', 'b'.repeat(64)]]),
124
+ migrations: [{ name: 'add_shift', migration: 'c'.repeat(64) }, { name: 'widen_row', migration: 'd'.repeat(64) }],
125
+ };
126
+ /** A decoded record object with its dicts as entry lists: a decoded Dict is
127
+ * a SortedMap, which is the same mapping and not the same container. */
128
+ const read = (bytes) => {
129
+ const decoded = decodeRecordObject(bytes);
130
+ return { path: decoded.path, mutations: [...decoded.mutations], indexes: [...decoded.indexes], migrations: decoded.migrations };
131
+ };
132
+ it('round-trips a current record object, its migrations in their order', () => {
133
+ assert.deepEqual(read(encodeBeast2For(RecordObjectType)(record)), { path: record.path, mutations: [...record.mutations], indexes: [...record.indexes], migrations: record.migrations });
134
+ });
135
+ it('refuses a record object an older SDK exported before migrations, naming the fix', () => {
136
+ const IndexesEraRecordType = StructType({
137
+ path: StringType, mutations: DictType(StringType, StringType), indexes: DictType(StringType, StringType),
138
+ });
139
+ const { migrations: _migrations, ...older } = record;
140
+ assert.throws(() => decodeRecordObject(encodeBeast2For(IndexesEraRecordType)(older)), /^Error: the record object does not decode: the package was exported by an older e3 SDK — re-export it with the current one \(/);
141
+ });
142
+ });
143
+ //# sourceMappingURL=record.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"record.spec.js","sourceRoot":"","sources":["../../src/record.spec.ts"],"names":[],"mappings":"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"}