@yielded/agent-storage-cloudflare 0.1.0-beta.174 → 0.1.0-beta.176

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 (41) hide show
  1. package/dist/DoMemoryStore.mjs +1 -1
  2. package/dist/DoMessageDeliveryStore.mjs +1 -1
  3. package/dist/DoStorageFailpoint.d.mts +3 -1
  4. package/dist/DoStorageFailpoint.mjs +7 -2
  5. package/dist/DoStorageFailpoint.mjs.map +1 -1
  6. package/dist/{DoStorageVersion-NKnQU3eR.d.mts → DoStorageVersion-Mc7CXMdN.d.mts} +2 -2
  7. package/dist/DoStorageVersion.d.mts +1 -1
  8. package/dist/DoStorageVersion.mjs +2 -2
  9. package/dist/DoSubmissionLedger.d.mts +1 -1
  10. package/dist/DoSubmissionLedger.mjs +194 -98
  11. package/dist/DoSubmissionLedger.mjs.map +1 -1
  12. package/dist/{DoThreadStore-Bd4fTns3.d.mts → DoThreadStore-BzYJjm4O.d.mts} +18 -8
  13. package/dist/{DoThreadStore-DyzOZDXK.mjs → DoThreadStore-DG0h5J6u.mjs} +25 -19
  14. package/dist/DoThreadStore-DG0h5J6u.mjs.map +1 -0
  15. package/dist/DoThreadStore.d.mts +2 -2
  16. package/dist/DoThreadStore.mjs +3 -2
  17. package/dist/PortProtocol.d.mts +7201 -7201
  18. package/dist/PortRouting.mjs +21 -11
  19. package/dist/PortRouting.mjs.map +1 -1
  20. package/dist/{do-journal-ZMAzzENZ.mjs → do-journal-ySC3eXNv.mjs} +144 -45
  21. package/dist/do-journal-ySC3eXNv.mjs.map +1 -0
  22. package/dist/index.d.mts +2 -2
  23. package/dist/index.mjs +1 -1
  24. package/dist/{migrations-C3N9GN6A.mjs → migrations-CAO_akhD.mjs} +45 -22
  25. package/dist/migrations-CAO_akhD.mjs.map +1 -0
  26. package/dist/{owned-state-DcFh0YNo.mjs → owned-state-BgGOA2Ak.mjs} +4 -3
  27. package/dist/owned-state-BgGOA2Ak.mjs.map +1 -0
  28. package/package.json +1 -1
  29. package/src/DoStorageFailpoint.ts +4 -0
  30. package/src/DoSubmissionLedger.ts +430 -228
  31. package/src/DoThreadStore.ts +25 -44
  32. package/src/PortRouting.ts +57 -31
  33. package/src/internal/do-journal.ts +318 -70
  34. package/src/internal/migrations.ts +52 -10
  35. package/src/internal/owned-state.ts +12 -5
  36. package/src/internal/prompt-checkpoint.ts +6 -3
  37. package/src/internal/storage-span.ts +14 -10
  38. package/dist/DoThreadStore-DyzOZDXK.mjs.map +0 -1
  39. package/dist/do-journal-ZMAzzENZ.mjs.map +0 -1
  40. package/dist/migrations-C3N9GN6A.mjs.map +0 -1
  41. package/dist/owned-state-DcFh0YNo.mjs.map +0 -1
@@ -18,7 +18,6 @@ import { ThreadImport } from "@yielded/agent/thread-import";
18
18
  import {
19
19
  type ThreadExportRequest,
20
20
  AppendConflict,
21
- AppendResult,
22
21
  CheckpointRejected,
23
22
  ThreadCheckpoint,
24
23
  ThreadMaterialization,
@@ -104,6 +103,11 @@ const isDoCheckpointConflict = Schema.is(DoCheckpointConflict);
104
103
  const ThreadCheckpointJson = Schema.fromJsonString(ThreadCheckpoint);
105
104
  const decodeCanonicalRecord = decodeExportRecordJson;
106
105
 
106
+ const decodeThreadMaterializationType = Schema.decodeEffect(Schema.toType(ThreadMaterialization));
107
+ const decodeThreadReadRequestType = Schema.decodeEffect(Schema.toType(ThreadReadRequest));
108
+ const decodeThreadTailRequestType = Schema.decodeEffect(Schema.toType(ThreadTailRequest));
109
+ const decodeStorageConfig = Schema.decodeEffect(DoStorageConfigValue);
110
+
107
111
  const storeError = (
108
112
  operation: string,
109
113
  error: { readonly message: string; readonly diagnostic?: ThreadStoreDiagnostic },
@@ -126,19 +130,6 @@ const schemaStoreError = (operation: string, error: Schema.SchemaError) =>
126
130
  }),
127
131
  });
128
132
 
129
- const makeOffset = (
130
- threadId: ThreadMaterialization["threadId"],
131
- sequence: number,
132
- ): Effect.Effect<ObservationOffset, ThreadStoreError> =>
133
- Schema.decodeEffect(CanonicalSequence)(sequence).pipe(
134
- Effect.flatMap((validatedSequence) =>
135
- Schema.decodeEffect(ObservationOffset)(
136
- `${DO_OFFSET_PREFIX}${encodeURIComponent(threadId)}:${validatedSequence}`,
137
- ),
138
- ),
139
- Effect.mapError((error) => schemaStoreError("encode observation offset", error)),
140
- );
141
-
142
133
  const parseOffset = Effect.fnUntraced(function* (
143
134
  threadId: ThreadMaterialization["threadId"],
144
135
  offset: ObservationOffset | undefined,
@@ -179,8 +170,8 @@ const encodeCheckpoint = (checkpoint: ThreadCheckpoint): Effect.Effect<string, T
179
170
  const envelopes = new WeakMap<object, CanonicalRecordEnvelope>();
180
171
 
181
172
  const decodeEnvelope = Effect.fnUntraced(function* (row: {
182
- readonly batch_id: string;
183
- readonly thread_id: string;
173
+ readonly batch_id: CanonicalRecordEnvelope["batchId"];
174
+ readonly thread_id: CanonicalRecordEnvelope["threadId"];
184
175
  readonly record_json: string;
185
176
  readonly sequence: CanonicalSequence;
186
177
  }) {
@@ -213,15 +204,11 @@ const decodeEnvelope = Effect.fnUntraced(function* (row: {
213
204
  ),
214
205
  );
215
206
 
216
- const threadId = yield* Schema.decodeEffect(CanonicalRecordEnvelope.fields.threadId)(
217
- row.thread_id,
218
- ).pipe(Effect.mapError((error) => schemaStoreError("decode thread identity", error)));
207
+ const { thread_id: threadId, batch_id: batchId } = row;
219
208
 
220
- const offset = yield* makeOffset(threadId, row.sequence);
221
-
222
- const batchId = yield* Schema.decodeEffect(CanonicalRecordEnvelope.fields.batchId)(
223
- row.batch_id,
224
- ).pipe(Effect.mapError((error) => schemaStoreError("decode batch identity", error)));
209
+ const offset = ObservationOffset.make(
210
+ `${DO_OFFSET_PREFIX}${encodeURIComponent(threadId)}:${row.sequence}`,
211
+ );
225
212
 
226
213
  const envelope = CanonicalRecordEnvelope.make(
227
214
  { threadId, batchId, sequence: row.sequence, offset, record },
@@ -317,9 +304,9 @@ const makeServices = Effect.fnUntraced(function* () {
317
304
  const materialize: ThreadStore["Service"]["materialize"] = Effect.fnUntraced(function* (
318
305
  request: ThreadMaterialization,
319
306
  ) {
320
- const validated = yield* Schema.decodeEffect(Schema.toType(ThreadMaterialization))(
321
- request,
322
- ).pipe(Effect.mapError((error) => schemaStoreError("validate materialization", error)));
307
+ const validated = yield* decodeThreadMaterializationType(request).pipe(
308
+ Effect.mapError((error) => schemaStoreError("validate materialization", error)),
309
+ );
323
310
 
324
311
  const now = yield* Clock.currentTimeMillis;
325
312
 
@@ -362,11 +349,6 @@ const makeServices = Effect.fnUntraced(function* () {
362
349
 
363
350
  return storeError("append canonical batch", error);
364
351
  }),
365
- Effect.flatMap((result) =>
366
- Schema.decodeEffect(AppendResult)(result).pipe(
367
- Effect.mapError((error) => schemaStoreError("decode append result", error)),
368
- ),
369
- ),
370
352
  );
371
353
 
372
354
  yield* hitFailpoint("append:after");
@@ -391,7 +373,7 @@ const makeServices = Effect.fnUntraced(function* () {
391
373
  });
392
374
 
393
375
  const readEffect = Effect.fnUntraced(function* (request: ThreadReadRequest) {
394
- const validated = yield* Schema.decodeEffect(Schema.toType(ThreadReadRequest))(request).pipe(
376
+ const validated = yield* decodeThreadReadRequestType(request).pipe(
395
377
  Effect.mapError((error) => schemaStoreError("validate thread read", error)),
396
378
  );
397
379
 
@@ -503,7 +485,7 @@ const makeServices = Effect.fnUntraced(function* () {
503
485
  const inspectTail: ThreadStore["Service"]["inspectTail"] = Effect.fnUntraced(function* (
504
486
  request: ThreadTailRequest,
505
487
  ) {
506
- const validated = yield* Schema.decodeEffect(Schema.toType(ThreadTailRequest))(request).pipe(
488
+ const validated = yield* decodeThreadTailRequestType(request).pipe(
507
489
  Effect.mapError((error) => schemaStoreError("validate tail inspection", error)),
508
490
  );
509
491
 
@@ -602,15 +584,7 @@ const makeServices = Effect.fnUntraced(function* () {
602
584
  snapshot: journal.state.read,
603
585
  tail: (threadId) =>
604
586
  journal.getThread(threadId).pipe(
605
- Effect.flatMap((rows) =>
606
- Schema.decodeEffect(
607
- Schema.UndefinedOr(
608
- Schema.Struct({
609
- tail_sequence: CanonicalSequence,
610
- }),
611
- ),
612
- )(rows[0]),
613
- ),
587
+ Effect.map((rows) => rows[0]),
614
588
  Effect.mapError((error) => storeError("native thread tail", error)),
615
589
  ),
616
590
  }),
@@ -659,6 +633,13 @@ export const threadStoreLayer: Layer.Layer<
659
633
  DoStorageConfig | DoStorageFailpoint | SqlClientService.SqlClient | Crypto.Crypto
660
634
  > = ThreadReader.layer().pipe(Layer.provideMerge(Layer.effectContext(makeServices())));
661
635
 
636
+ /**
637
+ * Share one initialized journal between local storage ports in one Object build. Provide
638
+ * this beneath both ports and above their common SQL, owner, configuration, failpoint,
639
+ * progress and lifecycle providers. Standalone ports initialize their own journal.
640
+ */
641
+ export { initializedDoJournalLayer as journalLayer } from "./internal/do-journal.ts";
642
+
662
643
  /**
663
644
  * Validated Durable Object storage configuration Layer with the documented defaults applied.
664
645
  * Shared by the ThreadStore and SubmissionLedger convenience layers so their defaults
@@ -668,7 +649,7 @@ export const storageConfigLayer = (
668
649
  options: DoStorageOptions,
669
650
  ): Layer.Layer<DoStorageConfig, DoStorageError> =>
670
651
  Layer.effect(DoStorageConfig)(
671
- Schema.decodeEffect(DoStorageConfigValue)({
652
+ decodeStorageConfig({
672
653
  observationPollInterval: options.observationPollInterval ?? 25,
673
654
  maxStoredValueBytes: options.maxStoredValueBytes ?? DEFAULT_MAX_STORED_VALUE_BYTES,
674
655
  verifyOnOpen: options.verifyOnOpen ?? false,
@@ -236,35 +236,36 @@ const LOCAL: RouteTarget = { _tag: "local" };
236
236
  * Identities beyond the ledger's 1,024-character row bound fail typed: the minting side
237
237
  * refused them at admission, so they cannot name any stored row anywhere.
238
238
  */
239
- const routableSubmissionTarget = (
240
- ownsThread: RoutedPortOptions["ownsThread"],
241
- ): ((operation: string, submissionId: string) => Effect.Effect<RouteTarget, LedgerError>) =>
242
- Effect.fnUntraced(function* (
243
- operation: string,
244
- submissionId: string,
245
- ): Effect.fn.Return<RouteTarget, LedgerError> {
246
- if (submissionId.length > MAX_ROUTABLE_SUBMISSION_ID_LENGTH) {
247
- return yield* LedgerError.make({
248
- operation,
249
- message:
250
- `A Submission identity of ${submissionId.length} characters exceeds the ` +
251
- `${MAX_ROUTABLE_SUBMISSION_ID_LENGTH}-character routable identity bound; admission ` +
252
- "refuses such identities, so it cannot name any stored row.",
253
- });
254
- }
255
- const separator = submissionId.indexOf(":");
256
-
257
- if (separator === -1) return LOCAL;
258
- if (!UUID_HEAD_PATTERN.test(submissionId.slice(0, separator))) return LOCAL;
259
- const tail = submissionId.slice(separator + 1);
260
-
261
- return yield* decodeThreadId(tail).pipe(
262
- Effect.map((threadId): RouteTarget =>
263
- ownsThread(threadId) ? LOCAL : { _tag: "foreign", threadId },
264
- ),
265
- Effect.orElseSucceed(() => LOCAL),
266
- );
267
- });
239
+ const routableSubmissionTarget =
240
+ (
241
+ ownsThread: RoutedPortOptions["ownsThread"],
242
+ ): ((operation: string, submissionId: string) => Effect.Effect<RouteTarget, LedgerError>) =>
243
+ (operation, submissionId) =>
244
+ Effect.suspend(() => {
245
+ if (submissionId.length > MAX_ROUTABLE_SUBMISSION_ID_LENGTH) {
246
+ return Effect.fail(
247
+ LedgerError.make({
248
+ operation,
249
+ message:
250
+ `A Submission identity of ${submissionId.length} characters exceeds the ` +
251
+ `${MAX_ROUTABLE_SUBMISSION_ID_LENGTH}-character routable identity bound; admission ` +
252
+ "refuses such identities, so it cannot name any stored row.",
253
+ }),
254
+ );
255
+ }
256
+ const separator = submissionId.indexOf(":");
257
+
258
+ if (separator === -1 || !UUID_HEAD_PATTERN.test(submissionId.slice(0, separator)))
259
+ return Effect.succeed(LOCAL);
260
+
261
+ return decodeThreadId(submissionId.slice(separator + 1)).pipe(
262
+ Effect.match({
263
+ onFailure: () => LOCAL,
264
+ onSuccess: (threadId): RouteTarget =>
265
+ ownsThread(threadId) ? LOCAL : { _tag: "foreign", threadId },
266
+ }),
267
+ );
268
+ });
268
269
 
269
270
  /**
270
271
  * Read a Submission only from this physical owner. Encoded foreign identities fail before
@@ -570,9 +571,9 @@ const makeRoutedLedgerServices = Effect.fnUntraced(function* (options: RoutedPor
570
571
  const routed = SubmissionLedger.of({
571
572
  capabilities: local.capabilities,
572
573
 
573
- admit: (request) =>
574
+ admit: (request, admissionOptions) =>
574
575
  options.ownsThread(request.threadId)
575
- ? local.admit(request)
576
+ ? local.admit(request, admissionOptions)
576
577
  : foreignLedgerCall(
577
578
  "ledger admit",
578
579
  request.threadId,
@@ -730,6 +731,23 @@ const makeRoutedLedgerServices = Effect.fnUntraced(function* (options: RoutedPor
730
731
  ? local.claimJoining(request)
731
732
  : Effect.fail(crossThreadLedgerError("ledger claim joining", request.threadId)),
732
733
 
734
+ ...(local.appendInput === undefined
735
+ ? {}
736
+ : {
737
+ appendInput: (
738
+ submissionId: SubmissionSnapshot["submissionId"],
739
+ request: Parameters<NonNullable<SubmissionLedger["Service"]["appendInput"]>>[1],
740
+ ) =>
741
+ options.ownsThread(request.threadId)
742
+ ? local.appendInput!(submissionId, request)
743
+ : Effect.fail(
744
+ ThreadStoreError.make({
745
+ operation: "append original input",
746
+ message: "Original input publication is lane-local",
747
+ }),
748
+ ),
749
+ }),
750
+
733
751
  markInputApplied: (request) =>
734
752
  submissionTarget("ledger mark input applied", request.submissionId).pipe(
735
753
  Effect.flatMap((target) =>
@@ -824,6 +842,7 @@ const makeRoutedLedgerServices = Effect.fnUntraced(function* (options: RoutedPor
824
842
 
825
843
  // The local scan IS the whole worklist: one Thread per Object (durability §5).
826
844
  scanNonterminal: local.scanNonterminal,
845
+ ...(local.listNonterminal === undefined ? {} : { listNonterminal: local.listNonterminal }),
827
846
 
828
847
  readAbortIntent: (request) =>
829
848
  submissionTarget("ledger read abort intent", request.submissionId).pipe(
@@ -1046,6 +1065,13 @@ const makeRoutedStoreServices = Effect.fnUntraced(function* (options: RoutedPort
1046
1065
  ).pipe(Effect.map((reply) => reply.range)),
1047
1066
  },
1048
1067
  work: {
1068
+ isOnlyFreshAdmission: (request) => {
1069
+ const prove = local.work?.isOnlyFreshAdmission;
1070
+
1071
+ return prove !== undefined && options.ownsThread(request.threadId)
1072
+ ? prove(request)
1073
+ : Effect.succeed(false);
1074
+ },
1049
1075
  threads: (request) =>
1050
1076
  Effect.flatMap(CurrentNativeSource, (nativeSource) =>
1051
1077
  nativeSource && local.work !== undefined