@effect-agent/storage-cloudflare 0.0.1-beta.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.
@@ -0,0 +1,319 @@
1
+ import {
2
+ AbortCommand,
3
+ AbortIntent,
4
+ AdmissionConflict,
5
+ AdmissionRequest,
6
+ AdmissionResolution,
7
+ AdmissionResult,
8
+ AppendConflict,
9
+ AppendResult,
10
+ CanonicalRecordEnvelope,
11
+ ChildSettledNotification,
12
+ ChildSettledOutcome,
13
+ ConversationExport,
14
+ ConversationExportRequest,
15
+ ConversationMaterialization,
16
+ ConversationNotMaterialized,
17
+ ConversationRead,
18
+ ConversationStoreError,
19
+ ConversationTail,
20
+ ConversationTailRequest,
21
+ FenceRejected,
22
+ FencedAppendRequest,
23
+ JoinedToHost,
24
+ LedgerError,
25
+ MarkReadyRequest,
26
+ SettlementConflict,
27
+ SubmissionLookup,
28
+ SubmissionLookupByKey,
29
+ SubmissionSnapshot,
30
+ } from "@effect-agent/session";
31
+ import { Schema } from "effect";
32
+
33
+ /**
34
+ * The cross-Durable-Object port protocol (plan §1.3, D-P6-3): Schema request/response/error
35
+ * envelopes for the CLOSED route-capable subset of the session ports. One Conversation's
36
+ * Durable Object executes another Conversation's request against its OWN local facets; the
37
+ * envelopes here are the only values that cross the Object boundary, and they are
38
+ * transport-agnostic — native Durable Object JS RPC is the shipped carrier, fetch-with-JSON
39
+ * the documented fallback, and both move the same Schema-encoded JSON.
40
+ *
41
+ * The closed subset is exactly the set of operations the durable coordinator performs against
42
+ * a FOREIGN Conversation (parent/child establishment, status checks, abort propagation,
43
+ * child-settlement notification, and the child-conversation store operations used by
44
+ * establishment, `verifySettledChild`, and result projection):
45
+ *
46
+ * - ledger: `admit`, `markReady`, `lookup`, `resolveAdmission`, `requestAbort`,
47
+ * `recordChildSettled`;
48
+ * - store: `materialize`, `append`, `read` (one page), `inspectTail`, `export`.
49
+ *
50
+ * Every other port operation is lane-local by construction and is NOT given an envelope:
51
+ * honesty over accidental distribution — the routing layer fails such calls fast and typed
52
+ * instead of quietly widening the distributed surface.
53
+ *
54
+ * Failures cross the boundary as the `PortFailure` union and re-decode on the caller side to
55
+ * the SAME tagged error types the local facet would have produced, so routed calls keep
56
+ * error-tag fidelity. `cause` chains inside `LedgerError`/`ConversationStoreError` travel as
57
+ * Schema defects and do not claim instance fidelity across Objects (plan §2.8).
58
+ */
59
+
60
+ /** Ceiling for protocol diagnostic strings; matches `AdmissionIndeterminate.reason`. */
61
+ export const MAX_PORT_DIAGNOSTIC_LENGTH = 4_096;
62
+
63
+ const BoundedDiagnostic = Schema.String.check(Schema.isMaxLength(MAX_PORT_DIAGNOSTIC_LENGTH));
64
+
65
+ /** Truncate a diagnostic string to the protocol's bounded diagnostic length. */
66
+ export const boundPortDiagnostic = (value: string): string =>
67
+ value.length > MAX_PORT_DIAGNOSTIC_LENGTH
68
+ ? `${value.slice(0, MAX_PORT_DIAGNOSTIC_LENGTH - 3)}...`
69
+ : value;
70
+
71
+ /**
72
+ * The envelope itself could not be honored: the receiving Object could not decode the
73
+ * request, or a response could not be encoded/decoded. It never carries port semantics —
74
+ * callers fold it into the operation's base error (`LedgerError`/`ConversationStoreError`),
75
+ * except `resolveAdmission`, which folds it into `AdmissionIndeterminate` because a
76
+ * non-answer is never proof of absence (SUB-031).
77
+ */
78
+ export class PortProtocolError extends Schema.TaggedError<PortProtocolError>()(
79
+ "PortProtocolError",
80
+ {
81
+ message: BoundedDiagnostic,
82
+ },
83
+ ) {}
84
+
85
+ // ---------------------------------------------------------------------------
86
+ // Requests
87
+ // ---------------------------------------------------------------------------
88
+
89
+ /** Routed `SubmissionLedger.admit` — child establishment admits INTO the owning Object. */
90
+ export class LedgerAdmitCall extends Schema.TaggedClass<LedgerAdmitCall>(
91
+ "@effect-agent/storage-cloudflare/LedgerAdmitCall",
92
+ )("LedgerAdmit", {
93
+ request: AdmissionRequest,
94
+ }) {}
95
+
96
+ /** Routed `SubmissionLedger.markReady` for a Submission owned by another Object. */
97
+ export class LedgerMarkReadyCall extends Schema.TaggedClass<LedgerMarkReadyCall>(
98
+ "@effect-agent/storage-cloudflare/LedgerMarkReadyCall",
99
+ )("LedgerMarkReady", {
100
+ request: MarkReadyRequest,
101
+ }) {}
102
+
103
+ /** Routed `SubmissionLedger.lookup` (by identity or scoped idempotency key). */
104
+ export class LedgerLookupCall extends Schema.TaggedClass<LedgerLookupCall>(
105
+ "@effect-agent/storage-cloudflare/LedgerLookupCall",
106
+ )("LedgerLookup", {
107
+ request: SubmissionLookup,
108
+ }) {}
109
+
110
+ /** Routed `SubmissionLedger.resolveAdmission` — the SUB-031 tri-state authority call. */
111
+ export class LedgerResolveAdmissionCall extends Schema.TaggedClass<LedgerResolveAdmissionCall>(
112
+ "@effect-agent/storage-cloudflare/LedgerResolveAdmissionCall",
113
+ )("LedgerResolveAdmission", {
114
+ request: SubmissionLookupByKey,
115
+ }) {}
116
+
117
+ /** Routed `SubmissionLedger.requestAbort` — abort propagation across Objects. */
118
+ export class LedgerRequestAbortCall extends Schema.TaggedClass<LedgerRequestAbortCall>(
119
+ "@effect-agent/storage-cloudflare/LedgerRequestAbortCall",
120
+ )("LedgerRequestAbort", {
121
+ request: AbortCommand,
122
+ }) {}
123
+
124
+ /** Routed `SubmissionLedger.recordChildSettled` — the child→parent durable notification. */
125
+ export class LedgerRecordChildSettledCall extends Schema.TaggedClass<LedgerRecordChildSettledCall>(
126
+ "@effect-agent/storage-cloudflare/LedgerRecordChildSettledCall",
127
+ )("LedgerRecordChildSettled", {
128
+ request: ChildSettledNotification,
129
+ }) {}
130
+
131
+ /** Routed `ConversationStore.materialize` against the owning Object. */
132
+ export class StoreMaterializeCall extends Schema.TaggedClass<StoreMaterializeCall>(
133
+ "@effect-agent/storage-cloudflare/StoreMaterializeCall",
134
+ )("StoreMaterialize", {
135
+ request: ConversationMaterialization,
136
+ }) {}
137
+
138
+ /** Routed `ConversationStore.append` against the owning Object. */
139
+ export class StoreAppendCall extends Schema.TaggedClass<StoreAppendCall>(
140
+ "@effect-agent/storage-cloudflare/StoreAppendCall",
141
+ )("StoreAppend", {
142
+ request: FencedAppendRequest,
143
+ }) {}
144
+
145
+ /** Routed one-page `ConversationStore.read`; the page bound is the request's own `limit`. */
146
+ export class StoreReadPageCall extends Schema.TaggedClass<StoreReadPageCall>(
147
+ "@effect-agent/storage-cloudflare/StoreReadPageCall",
148
+ )("StoreReadPage", {
149
+ request: ConversationRead,
150
+ }) {}
151
+
152
+ /** Routed `ConversationStore.inspectTail` against the owning Object. */
153
+ export class StoreInspectTailCall extends Schema.TaggedClass<StoreInspectTailCall>(
154
+ "@effect-agent/storage-cloudflare/StoreInspectTailCall",
155
+ )("StoreInspectTail", {
156
+ request: ConversationTailRequest,
157
+ }) {}
158
+
159
+ /** Routed `ConversationStore.export` against the owning Object. */
160
+ export class StoreExportCall extends Schema.TaggedClass<StoreExportCall>(
161
+ "@effect-agent/storage-cloudflare/StoreExportCall",
162
+ )("StoreExport", {
163
+ request: ConversationExportRequest,
164
+ }) {}
165
+
166
+ /** Every request that may cross a Durable Object boundary — the CLOSED route-capable subset. */
167
+ export const PortRequest = Schema.Union([
168
+ LedgerAdmitCall,
169
+ LedgerMarkReadyCall,
170
+ LedgerLookupCall,
171
+ LedgerResolveAdmissionCall,
172
+ LedgerRequestAbortCall,
173
+ LedgerRecordChildSettledCall,
174
+ StoreMaterializeCall,
175
+ StoreAppendCall,
176
+ StoreReadPageCall,
177
+ StoreInspectTailCall,
178
+ StoreExportCall,
179
+ ]);
180
+ export type PortRequest = typeof PortRequest.Type;
181
+
182
+ /** The wire form of one port request (what a transport actually carries). */
183
+ export type PortRequestEnvelope = typeof PortRequest.Encoded;
184
+
185
+ // ---------------------------------------------------------------------------
186
+ // Results
187
+ // ---------------------------------------------------------------------------
188
+
189
+ export class LedgerAdmitResult extends Schema.TaggedClass<LedgerAdmitResult>(
190
+ "@effect-agent/storage-cloudflare/LedgerAdmitResult",
191
+ )("LedgerAdmitResult", {
192
+ result: AdmissionResult,
193
+ }) {}
194
+
195
+ export class LedgerMarkReadyResult extends Schema.TaggedClass<LedgerMarkReadyResult>(
196
+ "@effect-agent/storage-cloudflare/LedgerMarkReadyResult",
197
+ )("LedgerMarkReadyResult", {}) {}
198
+
199
+ /** `submission` is absent exactly when the lookup answered `Option.none`. */
200
+ export class LedgerLookupResult extends Schema.TaggedClass<LedgerLookupResult>(
201
+ "@effect-agent/storage-cloudflare/LedgerLookupResult",
202
+ )("LedgerLookupResult", {
203
+ submission: Schema.optionalKey(SubmissionSnapshot),
204
+ }) {}
205
+
206
+ export class LedgerResolveAdmissionResult extends Schema.TaggedClass<LedgerResolveAdmissionResult>(
207
+ "@effect-agent/storage-cloudflare/LedgerResolveAdmissionResult",
208
+ )("LedgerResolveAdmissionResult", {
209
+ resolution: AdmissionResolution,
210
+ }) {}
211
+
212
+ export class LedgerRequestAbortResult extends Schema.TaggedClass<LedgerRequestAbortResult>(
213
+ "@effect-agent/storage-cloudflare/LedgerRequestAbortResult",
214
+ )("LedgerRequestAbortResult", {
215
+ intent: AbortIntent,
216
+ }) {}
217
+
218
+ export class LedgerRecordChildSettledResult extends Schema.TaggedClass<LedgerRecordChildSettledResult>(
219
+ "@effect-agent/storage-cloudflare/LedgerRecordChildSettledResult",
220
+ )("LedgerRecordChildSettledResult", {
221
+ outcome: ChildSettledOutcome,
222
+ }) {}
223
+
224
+ export class StoreMaterializeResult extends Schema.TaggedClass<StoreMaterializeResult>(
225
+ "@effect-agent/storage-cloudflare/StoreMaterializeResult",
226
+ )("StoreMaterializeResult", {}) {}
227
+
228
+ export class StoreAppendResult extends Schema.TaggedClass<StoreAppendResult>(
229
+ "@effect-agent/storage-cloudflare/StoreAppendResult",
230
+ )("StoreAppendResult", {
231
+ result: AppendResult,
232
+ }) {}
233
+
234
+ /** One page of canonical records, bounded by the request's `limit` (≤ 1,024). */
235
+ export class StoreReadPageResult extends Schema.TaggedClass<StoreReadPageResult>(
236
+ "@effect-agent/storage-cloudflare/StoreReadPageResult",
237
+ )("StoreReadPageResult", {
238
+ records: Schema.Array(CanonicalRecordEnvelope).check(Schema.isMaxLength(1_024)),
239
+ }) {}
240
+
241
+ export class StoreInspectTailResult extends Schema.TaggedClass<StoreInspectTailResult>(
242
+ "@effect-agent/storage-cloudflare/StoreInspectTailResult",
243
+ )("StoreInspectTailResult", {
244
+ tail: ConversationTail,
245
+ }) {}
246
+
247
+ export class StoreExportResult extends Schema.TaggedClass<StoreExportResult>(
248
+ "@effect-agent/storage-cloudflare/StoreExportResult",
249
+ )("StoreExportResult", {
250
+ export: ConversationExport,
251
+ }) {}
252
+
253
+ /** Every successful routed result. Callers narrow by the tag their request implies. */
254
+ export const PortResult = Schema.Union([
255
+ LedgerAdmitResult,
256
+ LedgerMarkReadyResult,
257
+ LedgerLookupResult,
258
+ LedgerResolveAdmissionResult,
259
+ LedgerRequestAbortResult,
260
+ LedgerRecordChildSettledResult,
261
+ StoreMaterializeResult,
262
+ StoreAppendResult,
263
+ StoreReadPageResult,
264
+ StoreInspectTailResult,
265
+ StoreExportResult,
266
+ ]);
267
+ export type PortResult = typeof PortResult.Type;
268
+
269
+ // ---------------------------------------------------------------------------
270
+ // Failures and the response envelope
271
+ // ---------------------------------------------------------------------------
272
+
273
+ /**
274
+ * Every typed failure a route-capable operation can produce on its owning Object, plus the
275
+ * protocol's own `PortProtocolError`. Members re-decode to the SAME tagged classes the
276
+ * session ports declare, so a routed caller observes identical error tags and fields.
277
+ */
278
+ export const PortFailure = Schema.Union([
279
+ AdmissionConflict,
280
+ SettlementConflict,
281
+ JoinedToHost,
282
+ LedgerError,
283
+ ConversationStoreError,
284
+ ConversationNotMaterialized,
285
+ AppendConflict,
286
+ FenceRejected,
287
+ PortProtocolError,
288
+ ]);
289
+ export type PortFailure = typeof PortFailure.Type;
290
+
291
+ /** The routed operation succeeded on its owning Object. */
292
+ export class PortSucceeded extends Schema.TaggedClass<PortSucceeded>(
293
+ "@effect-agent/storage-cloudflare/PortSucceeded",
294
+ )("PortSucceeded", {
295
+ result: PortResult,
296
+ }) {}
297
+
298
+ /** The routed operation failed TYPED on its owning Object; the failure re-decodes verbatim. */
299
+ export class PortFailed extends Schema.TaggedClass<PortFailed>(
300
+ "@effect-agent/storage-cloudflare/PortFailed",
301
+ )("PortFailed", {
302
+ failure: PortFailure,
303
+ }) {}
304
+
305
+ /** The uniform answer of one `portCall`: op-specific success or a re-decodable typed failure. */
306
+ export const PortResponse = Schema.Union([PortSucceeded, PortFailed]);
307
+ export type PortResponse = typeof PortResponse.Type;
308
+
309
+ /** The wire form of one port response (what a transport actually carries). */
310
+ export type PortResponseEnvelope = typeof PortResponse.Encoded;
311
+
312
+ // ---------------------------------------------------------------------------
313
+ // Codecs
314
+ // ---------------------------------------------------------------------------
315
+
316
+ export const encodePortRequest = Schema.encodeEffect(PortRequest);
317
+ export const decodePortRequest = Schema.decodeUnknownEffect(PortRequest);
318
+ export const encodePortResponse = Schema.encodeEffect(PortResponse);
319
+ export const decodePortResponse = Schema.decodeUnknownEffect(PortResponse);