@effect-agent/storage-cloudflare 0.1.0-beta.24 → 0.1.0-beta.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.mjs +117 -85
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/src/do-conversation-store.ts +20 -15
- package/src/do-journal.ts +3 -4
- package/src/do-ledger.ts +60 -29
- package/src/do-storage-failpoint.ts +1 -1
- package/src/routing.ts +74 -78
package/src/routing.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
AdmissionIndeterminate,
|
|
3
|
+
AdmissionConflict,
|
|
3
4
|
AppendConflict,
|
|
4
5
|
ChildAttachmentSnapshot,
|
|
5
6
|
ConversationMaterialization,
|
|
@@ -13,11 +14,10 @@ import {
|
|
|
13
14
|
SettlementConflict,
|
|
14
15
|
SubmissionLedger,
|
|
15
16
|
SubmissionLookupById,
|
|
16
|
-
type AdmissionConflict,
|
|
17
17
|
type SubmissionLookupByKey,
|
|
18
18
|
type SubmissionSnapshot,
|
|
19
19
|
} from "@effect-agent/session";
|
|
20
|
-
import { Context, Effect, Layer, Option, Schema, Stream } from "effect";
|
|
20
|
+
import { Context, Effect, Layer, Option, Predicate, Schema, Stream } from "effect";
|
|
21
21
|
|
|
22
22
|
import {
|
|
23
23
|
boundPortDiagnostic,
|
|
@@ -91,20 +91,34 @@ export class PortTransportError extends Schema.TaggedError<PortTransportError>()
|
|
|
91
91
|
},
|
|
92
92
|
) {}
|
|
93
93
|
|
|
94
|
+
const safeTransportDiagnostic = (cause: unknown): string => {
|
|
95
|
+
try {
|
|
96
|
+
const message = cause instanceof Error ? cause.message : cause;
|
|
97
|
+
return boundPortDiagnostic(typeof message === "string" ? message : String(message));
|
|
98
|
+
} catch {
|
|
99
|
+
return "[unavailable transport diagnostic]";
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const transportRetryableSignal = (cause: unknown): boolean | undefined => {
|
|
104
|
+
if (!Predicate.isObjectKeyword(cause)) return undefined;
|
|
105
|
+
try {
|
|
106
|
+
const signal = Reflect.get(cause, "retryable");
|
|
107
|
+
return typeof signal === "boolean" ? signal : undefined;
|
|
108
|
+
} catch {
|
|
109
|
+
return undefined;
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
|
|
94
113
|
/**
|
|
95
114
|
* Build a `PortTransportError` from an arbitrary thrown transport cause, preserving the
|
|
96
115
|
* platform stub's own `retryable` signal when present.
|
|
97
116
|
*/
|
|
98
117
|
export const portTransportFailure = (target: string, cause: unknown): PortTransportError => {
|
|
99
|
-
const
|
|
100
|
-
let retryable: boolean | undefined;
|
|
101
|
-
if (typeof cause === "object" && cause !== null && "retryable" in cause) {
|
|
102
|
-
const signal = cause.retryable;
|
|
103
|
-
if (typeof signal === "boolean") retryable = signal;
|
|
104
|
-
}
|
|
118
|
+
const retryable = transportRetryableSignal(cause);
|
|
105
119
|
return PortTransportError.make({
|
|
106
120
|
target,
|
|
107
|
-
message:
|
|
121
|
+
message: safeTransportDiagnostic(cause),
|
|
108
122
|
...(retryable === undefined ? {} : { retryable }),
|
|
109
123
|
cause,
|
|
110
124
|
});
|
|
@@ -181,26 +195,13 @@ const routableSubmissionTarget = (
|
|
|
181
195
|
);
|
|
182
196
|
});
|
|
183
197
|
|
|
184
|
-
const
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
const isAbortConflict = (failure: PortFailure): failure is SettlementConflict | JoinedToHost =>
|
|
192
|
-
failure._tag === "SettlementConflict" || failure._tag === "JoinedToHost";
|
|
193
|
-
const noExtraFailure = (_failure: PortFailure): _failure is never => false;
|
|
194
|
-
const isFenceRejected = (failure: PortFailure): failure is FenceRejected =>
|
|
195
|
-
failure._tag === "FenceRejected";
|
|
196
|
-
const isAppendFailure = (
|
|
197
|
-
failure: PortFailure,
|
|
198
|
-
): failure is ConversationNotMaterialized | AppendConflict | FenceRejected =>
|
|
199
|
-
failure._tag === "ConversationNotMaterialized" ||
|
|
200
|
-
failure._tag === "AppendConflict" ||
|
|
201
|
-
failure._tag === "FenceRejected";
|
|
202
|
-
const isNotMaterialized = (failure: PortFailure): failure is ConversationNotMaterialized =>
|
|
203
|
-
failure._tag === "ConversationNotMaterialized";
|
|
198
|
+
const NoAdditionalPortFailure = Schema.Never;
|
|
199
|
+
const AbortPortFailure = Schema.Union([SettlementConflict, JoinedToHost]);
|
|
200
|
+
const AppendPortFailure = Schema.Union([
|
|
201
|
+
ConversationNotMaterialized,
|
|
202
|
+
AppendConflict,
|
|
203
|
+
FenceRejected,
|
|
204
|
+
]);
|
|
204
205
|
|
|
205
206
|
/**
|
|
206
207
|
* The fail-fast refusal for any foreign operation OUTSIDE the closed route-capable subset
|
|
@@ -272,22 +273,19 @@ const makeRoutedLedgerServices = Effect.fn("DoPortRouting.makeRoutedLedgerServic
|
|
|
272
273
|
* any failure outside the operation's declared surface — including protocol anomalies — is
|
|
273
274
|
* folded into a `LedgerError` naming the anomaly instead of being erased or re-thrown raw.
|
|
274
275
|
*/
|
|
275
|
-
const foreignLedgerCall = <
|
|
276
|
+
const foreignLedgerCall = <ResultSchema extends Schema.Top, FailureSchema extends Schema.Top>(
|
|
276
277
|
operation: string,
|
|
277
278
|
target: ConversationId,
|
|
278
279
|
call: PortRequest,
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
): Effect.Effect<
|
|
282
|
-
|
|
280
|
+
resultSchema: ResultSchema,
|
|
281
|
+
failureSchema: FailureSchema,
|
|
282
|
+
): Effect.Effect<ResultSchema["Type"], FailureSchema["Type"] | LedgerError> => {
|
|
283
|
+
const isExpectedResult = Schema.is(resultSchema);
|
|
284
|
+
const isExpectedFailure = Schema.is(failureSchema);
|
|
285
|
+
return transportCall(target, call).pipe(
|
|
283
286
|
Effect.mapError(routeFailure(operation, target)),
|
|
284
287
|
Effect.flatMap(
|
|
285
|
-
(
|
|
286
|
-
response,
|
|
287
|
-
): Effect.Effect<
|
|
288
|
-
Extract<PortResult, { readonly _tag: Tag }>,
|
|
289
|
-
ExpectedFailure | LedgerError
|
|
290
|
-
> => {
|
|
288
|
+
(response): Effect.Effect<ResultSchema["Type"], FailureSchema["Type"] | LedgerError> => {
|
|
291
289
|
if (response._tag === "PortFailed") {
|
|
292
290
|
const failure = response.failure;
|
|
293
291
|
if (isExpectedFailure(failure)) return Effect.fail(failure);
|
|
@@ -304,13 +302,13 @@ const makeRoutedLedgerServices = Effect.fn("DoPortRouting.makeRoutedLedgerServic
|
|
|
304
302
|
);
|
|
305
303
|
}
|
|
306
304
|
const result = response.result;
|
|
307
|
-
if (!
|
|
305
|
+
if (!isExpectedResult(result)) {
|
|
308
306
|
return Effect.fail(
|
|
309
307
|
LedgerError.make({
|
|
310
308
|
operation,
|
|
311
309
|
message:
|
|
312
310
|
`The Conversation Object owning ${target} answered ${operation} with the ` +
|
|
313
|
-
`mismatched result ${result._tag}
|
|
311
|
+
`mismatched result ${result._tag}.`,
|
|
314
312
|
}),
|
|
315
313
|
);
|
|
316
314
|
}
|
|
@@ -321,6 +319,7 @@ const makeRoutedLedgerServices = Effect.fn("DoPortRouting.makeRoutedLedgerServic
|
|
|
321
319
|
attributes: { operation, target },
|
|
322
320
|
}),
|
|
323
321
|
);
|
|
322
|
+
};
|
|
324
323
|
|
|
325
324
|
/**
|
|
326
325
|
* Routed `resolveAdmission` — where the S2 tri-state becomes real (plan §1.3): when the
|
|
@@ -394,8 +393,8 @@ const makeRoutedLedgerServices = Effect.fn("DoPortRouting.makeRoutedLedgerServic
|
|
|
394
393
|
operation,
|
|
395
394
|
target,
|
|
396
395
|
LedgerLookupCall.make({ request: SubmissionLookupById.make({ submissionId }) }),
|
|
397
|
-
|
|
398
|
-
|
|
396
|
+
LedgerLookupResult,
|
|
397
|
+
NoAdditionalPortFailure,
|
|
399
398
|
).pipe(
|
|
400
399
|
Effect.map((result) =>
|
|
401
400
|
result.submission === undefined ? Option.none() : Option.some(result.submission),
|
|
@@ -460,8 +459,8 @@ const makeRoutedLedgerServices = Effect.fn("DoPortRouting.makeRoutedLedgerServic
|
|
|
460
459
|
"ledger admit",
|
|
461
460
|
request.conversationId,
|
|
462
461
|
LedgerAdmitCall.make({ request }),
|
|
463
|
-
|
|
464
|
-
|
|
462
|
+
LedgerAdmitResult,
|
|
463
|
+
AdmissionConflict,
|
|
465
464
|
).pipe(Effect.map((reply) => reply.result)),
|
|
466
465
|
|
|
467
466
|
markReady: (request) =>
|
|
@@ -473,8 +472,8 @@ const makeRoutedLedgerServices = Effect.fn("DoPortRouting.makeRoutedLedgerServic
|
|
|
473
472
|
"ledger mark ready",
|
|
474
473
|
target.conversationId,
|
|
475
474
|
LedgerMarkReadyCall.make({ request }),
|
|
476
|
-
|
|
477
|
-
|
|
475
|
+
LedgerMarkReadyResult,
|
|
476
|
+
NoAdditionalPortFailure,
|
|
478
477
|
).pipe(Effect.asVoid),
|
|
479
478
|
),
|
|
480
479
|
),
|
|
@@ -494,8 +493,8 @@ const makeRoutedLedgerServices = Effect.fn("DoPortRouting.makeRoutedLedgerServic
|
|
|
494
493
|
"ledger lookup",
|
|
495
494
|
request.conversationId,
|
|
496
495
|
LedgerLookupCall.make({ request }),
|
|
497
|
-
|
|
498
|
-
|
|
496
|
+
LedgerLookupResult,
|
|
497
|
+
NoAdditionalPortFailure,
|
|
499
498
|
).pipe(
|
|
500
499
|
Effect.map((result) =>
|
|
501
500
|
result.submission === undefined ? Option.none() : Option.some(result.submission),
|
|
@@ -516,8 +515,8 @@ const makeRoutedLedgerServices = Effect.fn("DoPortRouting.makeRoutedLedgerServic
|
|
|
516
515
|
"ledger request abort",
|
|
517
516
|
target.conversationId,
|
|
518
517
|
LedgerRequestAbortCall.make({ request }),
|
|
519
|
-
|
|
520
|
-
|
|
518
|
+
LedgerRequestAbortResult,
|
|
519
|
+
AbortPortFailure,
|
|
521
520
|
).pipe(Effect.map((reply) => reply.intent)),
|
|
522
521
|
),
|
|
523
522
|
),
|
|
@@ -531,8 +530,8 @@ const makeRoutedLedgerServices = Effect.fn("DoPortRouting.makeRoutedLedgerServic
|
|
|
531
530
|
"ledger record child settled",
|
|
532
531
|
target.conversationId,
|
|
533
532
|
LedgerRecordChildSettledCall.make({ request }),
|
|
534
|
-
|
|
535
|
-
|
|
533
|
+
LedgerRecordChildSettledResult,
|
|
534
|
+
NoAdditionalPortFailure,
|
|
536
535
|
).pipe(Effect.map((reply) => reply.outcome)),
|
|
537
536
|
),
|
|
538
537
|
),
|
|
@@ -732,25 +731,21 @@ const makeRoutedStoreServices = Effect.fn("DoPortRouting.makeRoutedStoreServices
|
|
|
732
731
|
});
|
|
733
732
|
|
|
734
733
|
/** The store twin of `foreignLedgerCall` with `ConversationStoreError` as the base error. */
|
|
735
|
-
const foreignStoreCall = <
|
|
734
|
+
const foreignStoreCall = <ResultSchema extends Schema.Top, FailureSchema extends Schema.Top>(
|
|
736
735
|
operation: string,
|
|
737
736
|
target: ConversationId,
|
|
738
737
|
call: PortRequest,
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
): Effect.Effect<
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
transportCall(target, call).pipe(
|
|
738
|
+
resultSchema: ResultSchema,
|
|
739
|
+
failureSchema: FailureSchema,
|
|
740
|
+
): Effect.Effect<ResultSchema["Type"], FailureSchema["Type"] | ConversationStoreError> => {
|
|
741
|
+
const isExpectedResult = Schema.is(resultSchema);
|
|
742
|
+
const isExpectedFailure = Schema.is(failureSchema);
|
|
743
|
+
return transportCall(target, call).pipe(
|
|
746
744
|
Effect.mapError(routeFailure(operation, target)),
|
|
747
745
|
Effect.flatMap(
|
|
748
746
|
(
|
|
749
747
|
response,
|
|
750
|
-
): Effect.Effect<
|
|
751
|
-
Extract<PortResult, { readonly _tag: Tag }>,
|
|
752
|
-
ExpectedFailure | ConversationStoreError
|
|
753
|
-
> => {
|
|
748
|
+
): Effect.Effect<ResultSchema["Type"], FailureSchema["Type"] | ConversationStoreError> => {
|
|
754
749
|
if (response._tag === "PortFailed") {
|
|
755
750
|
const failure = response.failure;
|
|
756
751
|
if (isExpectedFailure(failure)) return Effect.fail(failure);
|
|
@@ -767,13 +762,13 @@ const makeRoutedStoreServices = Effect.fn("DoPortRouting.makeRoutedStoreServices
|
|
|
767
762
|
);
|
|
768
763
|
}
|
|
769
764
|
const result = response.result;
|
|
770
|
-
if (!
|
|
765
|
+
if (!isExpectedResult(result)) {
|
|
771
766
|
return Effect.fail(
|
|
772
767
|
ConversationStoreError.make({
|
|
773
768
|
operation,
|
|
774
769
|
message:
|
|
775
770
|
`The Conversation Object owning ${target} answered ${operation} with the ` +
|
|
776
|
-
`mismatched result ${result._tag}
|
|
771
|
+
`mismatched result ${result._tag}.`,
|
|
777
772
|
}),
|
|
778
773
|
);
|
|
779
774
|
}
|
|
@@ -784,6 +779,7 @@ const makeRoutedStoreServices = Effect.fn("DoPortRouting.makeRoutedStoreServices
|
|
|
784
779
|
attributes: { operation, target },
|
|
785
780
|
}),
|
|
786
781
|
);
|
|
782
|
+
};
|
|
787
783
|
|
|
788
784
|
const routed = ConversationStore.of({
|
|
789
785
|
materialize: (request) =>
|
|
@@ -793,8 +789,8 @@ const makeRoutedStoreServices = Effect.fn("DoPortRouting.makeRoutedStoreServices
|
|
|
793
789
|
"conversation materialize",
|
|
794
790
|
request.conversationId,
|
|
795
791
|
StoreMaterializeCall.make({ request }),
|
|
796
|
-
|
|
797
|
-
|
|
792
|
+
StoreMaterializeResult,
|
|
793
|
+
FenceRejected,
|
|
798
794
|
).pipe(Effect.asVoid),
|
|
799
795
|
|
|
800
796
|
append: (request) =>
|
|
@@ -804,8 +800,8 @@ const makeRoutedStoreServices = Effect.fn("DoPortRouting.makeRoutedStoreServices
|
|
|
804
800
|
"conversation append",
|
|
805
801
|
request.conversationId,
|
|
806
802
|
StoreAppendCall.make({ request }),
|
|
807
|
-
|
|
808
|
-
|
|
803
|
+
StoreAppendResult,
|
|
804
|
+
AppendPortFailure,
|
|
809
805
|
).pipe(Effect.map((reply) => reply.result)),
|
|
810
806
|
|
|
811
807
|
read: (request) =>
|
|
@@ -816,8 +812,8 @@ const makeRoutedStoreServices = Effect.fn("DoPortRouting.makeRoutedStoreServices
|
|
|
816
812
|
"conversation read",
|
|
817
813
|
request.conversationId,
|
|
818
814
|
StoreReadPageCall.make({ request }),
|
|
819
|
-
|
|
820
|
-
|
|
815
|
+
StoreReadPageResult,
|
|
816
|
+
ConversationNotMaterialized,
|
|
821
817
|
).pipe(Effect.map((reply) => Stream.fromIterable(reply.records))),
|
|
822
818
|
),
|
|
823
819
|
|
|
@@ -828,8 +824,8 @@ const makeRoutedStoreServices = Effect.fn("DoPortRouting.makeRoutedStoreServices
|
|
|
828
824
|
"conversation inspect tail",
|
|
829
825
|
request.conversationId,
|
|
830
826
|
StoreInspectTailCall.make({ request }),
|
|
831
|
-
|
|
832
|
-
|
|
827
|
+
StoreInspectTailResult,
|
|
828
|
+
ConversationNotMaterialized,
|
|
833
829
|
).pipe(Effect.map((reply) => reply.tail)),
|
|
834
830
|
|
|
835
831
|
export: (request) =>
|
|
@@ -839,8 +835,8 @@ const makeRoutedStoreServices = Effect.fn("DoPortRouting.makeRoutedStoreServices
|
|
|
839
835
|
"conversation export",
|
|
840
836
|
request.conversationId,
|
|
841
837
|
StoreExportCall.make({ request }),
|
|
842
|
-
|
|
843
|
-
|
|
838
|
+
StoreExportResult,
|
|
839
|
+
ConversationNotMaterialized,
|
|
844
840
|
).pipe(Effect.map((reply) => reply.export)),
|
|
845
841
|
|
|
846
842
|
// Observation and checkpoints are lane-local by construction (plan §1.3): the closed
|