@effect-agent/storage-cloudflare 0.1.0-beta.12 → 0.1.0-beta.120
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/DoMemoryStore.d.mts +29 -0
- package/dist/DoMemoryStore.mjs +55 -0
- package/dist/DoMemoryStore.mjs.map +1 -0
- package/dist/DoMessageDeliveryStore.d.mts +17 -0
- package/dist/DoMessageDeliveryStore.mjs +32 -0
- package/dist/DoMessageDeliveryStore.mjs.map +1 -0
- package/dist/DoScheduleStore.d.mts +36 -0
- package/dist/DoScheduleStore.mjs +388 -0
- package/dist/DoScheduleStore.mjs.map +1 -0
- package/dist/DoStorageConfig.d.mts +45 -0
- package/dist/DoStorageConfig.mjs +52 -0
- package/dist/DoStorageConfig.mjs.map +1 -0
- package/dist/DoStorageError-DjpuLHmX.d.mts +101 -0
- package/dist/DoStorageError.d.mts +2 -0
- package/dist/DoStorageError.mjs +167 -0
- package/dist/DoStorageError.mjs.map +1 -0
- package/dist/DoStorageFailpoint.d.mts +17 -0
- package/dist/DoStorageFailpoint.mjs +14 -0
- package/dist/DoStorageFailpoint.mjs.map +1 -0
- package/dist/DoStorageFailpointTesting.d.mts +33 -0
- package/dist/DoStorageFailpointTesting.mjs +35 -0
- package/dist/DoStorageFailpointTesting.mjs.map +1 -0
- package/dist/DoStorageVersion-DJwZzqel.d.mts +12 -0
- package/dist/DoStorageVersion.d.mts +2 -0
- package/dist/DoStorageVersion.mjs +8 -0
- package/dist/DoStorageVersion.mjs.map +1 -0
- package/dist/DoSubmissionLedger.d.mts +23 -0
- package/dist/DoSubmissionLedger.mjs +1939 -0
- package/dist/DoSubmissionLedger.mjs.map +1 -0
- package/dist/DoSubscriptionStore.d.mts +25 -0
- package/dist/DoSubscriptionStore.mjs +183 -0
- package/dist/DoSubscriptionStore.mjs.map +1 -0
- package/dist/DoThreadStore.d.mts +59 -0
- package/dist/DoThreadStore.mjs +499 -0
- package/dist/DoThreadStore.mjs.map +1 -0
- package/dist/MemoryProtocol.d.mts +38160 -0
- package/dist/MemoryProtocol.mjs +219 -0
- package/dist/MemoryProtocol.mjs.map +1 -0
- package/dist/PortProtocol.d.mts +5521 -0
- package/dist/PortProtocol.mjs +206 -0
- package/dist/PortProtocol.mjs.map +1 -0
- package/dist/PortRouting.d.mts +86 -0
- package/dist/PortRouting.mjs +461 -0
- package/dist/PortRouting.mjs.map +1 -0
- package/dist/do-journal-D65Iuln6.mjs +1119 -0
- package/dist/do-journal-D65Iuln6.mjs.map +1 -0
- package/dist/index.d.mts +14 -1525
- package/dist/index.mjs +14 -3695
- package/dist/migrations-Br6QqMIf.mjs +306 -0
- package/dist/migrations-Br6QqMIf.mjs.map +1 -0
- package/dist/rolldown-runtime-D7D4PA-g.mjs +13 -0
- package/package.json +1 -45
- package/src/DoMemoryStore.ts +56 -0
- package/src/DoMessageDeliveryStore.ts +53 -0
- package/src/DoScheduleStore.ts +622 -0
- package/src/{errors.ts → DoStorageError.ts} +13 -3
- package/src/DoStorageFailpoint.ts +20 -0
- package/src/{do-storage-failpoint.ts → DoStorageFailpointTesting.ts} +5 -18
- package/src/DoStorageVersion.ts +2 -0
- package/src/{do-ledger.ts → DoSubmissionLedger.ts} +1119 -262
- package/src/DoSubscriptionStore.ts +329 -0
- package/src/DoThreadStore.ts +1064 -0
- package/src/MemoryProtocol.ts +351 -0
- package/src/{port-protocol.ts → PortProtocol.ts} +103 -38
- package/src/{routing.ts → PortRouting.ts} +440 -264
- package/src/index.ts +13 -32
- package/src/internal/do-journal.ts +1789 -0
- package/src/internal/message-delivery-schema.ts +24 -0
- package/src/{migrations.ts → internal/migrations.ts} +72 -35
- package/src/internal/recovery-checkpoint-schema.ts +17 -0
- package/dist/index.mjs.map +0 -1
- package/src/do-conversation-store.ts +0 -850
- package/src/do-journal.ts +0 -1078
- /package/src/{do-storage-config.ts → DoStorageConfig.ts} +0 -0
|
@@ -1,25 +1,31 @@
|
|
|
1
|
+
import { Context, Effect, Layer, Option, Predicate, Schema, Stream } from "effect";
|
|
2
|
+
import { MessageDeliveryStore, MessageDeliveryError } from "effect-agent/message-delivery";
|
|
1
3
|
import {
|
|
2
4
|
AdmissionIndeterminate,
|
|
3
|
-
|
|
5
|
+
AdmissionConflict,
|
|
6
|
+
AdmissionPolicyError,
|
|
4
7
|
ChildAttachmentSnapshot,
|
|
5
|
-
ConversationMaterialization,
|
|
6
|
-
ConversationNotMaterialized,
|
|
7
|
-
ConversationStore,
|
|
8
|
-
ConversationStoreError,
|
|
9
|
-
FenceRejected,
|
|
10
8
|
JoinedToHost,
|
|
11
9
|
LedgerError,
|
|
12
10
|
RecoverySnapshot,
|
|
13
11
|
SettlementConflict,
|
|
14
12
|
SubmissionLedger,
|
|
15
13
|
SubmissionLookupById,
|
|
16
|
-
type AdmissionConflict,
|
|
17
14
|
type SubmissionLookupByKey,
|
|
18
15
|
type SubmissionSnapshot,
|
|
19
|
-
} from "
|
|
20
|
-
import {
|
|
16
|
+
} from "effect-agent/submission-ledger";
|
|
17
|
+
import {
|
|
18
|
+
AppendConflict,
|
|
19
|
+
ThreadMaterialization,
|
|
20
|
+
ThreadNotMaterialized,
|
|
21
|
+
ThreadStore,
|
|
22
|
+
ThreadStoreError,
|
|
23
|
+
FenceRejected,
|
|
24
|
+
} from "effect-agent/thread-store";
|
|
21
25
|
|
|
22
26
|
import {
|
|
27
|
+
MessageDeliveryListCall,
|
|
28
|
+
MessageDeliveryListResult,
|
|
23
29
|
boundPortDiagnostic,
|
|
24
30
|
decodePortRequest,
|
|
25
31
|
decodePortResponse,
|
|
@@ -34,6 +40,10 @@ import {
|
|
|
34
40
|
LedgerRecordChildSettledCall,
|
|
35
41
|
LedgerRecordChildSettledResult,
|
|
36
42
|
LedgerRequestAbortCall,
|
|
43
|
+
LedgerStopWorkerCall,
|
|
44
|
+
LedgerInspectWorkerCall,
|
|
45
|
+
LedgerInspectWorkerResult,
|
|
46
|
+
LedgerStopWorkerResult,
|
|
37
47
|
LedgerRequestAbortResult,
|
|
38
48
|
LedgerResolveAdmissionCall,
|
|
39
49
|
LedgerResolveAdmissionResult,
|
|
@@ -42,6 +52,8 @@ import {
|
|
|
42
52
|
PortSucceeded,
|
|
43
53
|
StoreAppendCall,
|
|
44
54
|
StoreAppendResult,
|
|
55
|
+
StoreCountPeerMessagesCall,
|
|
56
|
+
StoreCountPeerMessagesResult,
|
|
45
57
|
StoreExportCall,
|
|
46
58
|
StoreExportResult,
|
|
47
59
|
StoreInspectTailCall,
|
|
@@ -55,13 +67,13 @@ import {
|
|
|
55
67
|
type PortRequestEnvelope,
|
|
56
68
|
type PortResponse,
|
|
57
69
|
type PortResult,
|
|
58
|
-
} from "./
|
|
70
|
+
} from "./PortProtocol.ts";
|
|
59
71
|
|
|
60
|
-
type
|
|
72
|
+
type ThreadId = ThreadMaterialization["threadId"];
|
|
61
73
|
type SubmissionId = SubmissionSnapshot["submissionId"];
|
|
62
74
|
|
|
63
|
-
const
|
|
64
|
-
const
|
|
75
|
+
const ThreadIdSchema = ThreadMaterialization.fields.threadId;
|
|
76
|
+
const decodeThreadId = Schema.decodeUnknownEffect(ThreadIdSchema);
|
|
65
77
|
|
|
66
78
|
/**
|
|
67
79
|
* The ledger row bound routable Submission identities must respect (mirrors the local
|
|
@@ -76,7 +88,7 @@ const UUID_HEAD_PATTERN =
|
|
|
76
88
|
|
|
77
89
|
/**
|
|
78
90
|
* A transport could not deliver a port request to (or an answer from) the owning
|
|
79
|
-
*
|
|
91
|
+
* Thread's Durable Object. `retryable` carries the platform's own stub signal when one
|
|
80
92
|
* exists. This error never crosses the wire — it is the CALLER-side evidence that the
|
|
81
93
|
* authority was unreachable, which is exactly the case `AdmissionIndeterminate` was
|
|
82
94
|
* specified for (SUB-031).
|
|
@@ -91,20 +103,37 @@ export class PortTransportError extends Schema.TaggedError<PortTransportError>()
|
|
|
91
103
|
},
|
|
92
104
|
) {}
|
|
93
105
|
|
|
106
|
+
const safeTransportDiagnostic = (cause: unknown): string => {
|
|
107
|
+
try {
|
|
108
|
+
const message = cause instanceof Error ? cause.message : cause;
|
|
109
|
+
|
|
110
|
+
return boundPortDiagnostic(typeof message === "string" ? message : String(message));
|
|
111
|
+
} catch {
|
|
112
|
+
return "[unavailable transport diagnostic]";
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const transportRetryableSignal = (cause: unknown): boolean | undefined => {
|
|
117
|
+
if (!Predicate.isObjectKeyword(cause)) return undefined;
|
|
118
|
+
try {
|
|
119
|
+
const signal = Reflect.get(cause, "retryable");
|
|
120
|
+
|
|
121
|
+
return typeof signal === "boolean" ? signal : undefined;
|
|
122
|
+
} catch {
|
|
123
|
+
return undefined;
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
|
|
94
127
|
/**
|
|
95
128
|
* Build a `PortTransportError` from an arbitrary thrown transport cause, preserving the
|
|
96
129
|
* platform stub's own `retryable` signal when present.
|
|
97
130
|
*/
|
|
98
131
|
export const portTransportFailure = (target: string, cause: unknown): PortTransportError => {
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
if (typeof cause === "object" && cause !== null && "retryable" in cause) {
|
|
102
|
-
const signal = cause.retryable;
|
|
103
|
-
if (typeof signal === "boolean") retryable = signal;
|
|
104
|
-
}
|
|
132
|
+
const retryable = transportRetryableSignal(cause);
|
|
133
|
+
|
|
105
134
|
return PortTransportError.make({
|
|
106
135
|
target,
|
|
107
|
-
message:
|
|
136
|
+
message: safeTransportDiagnostic(cause),
|
|
108
137
|
...(retryable === undefined ? {} : { retryable }),
|
|
109
138
|
cause,
|
|
110
139
|
});
|
|
@@ -112,42 +141,42 @@ export const portTransportFailure = (target: string, cause: unknown): PortTransp
|
|
|
112
141
|
|
|
113
142
|
/**
|
|
114
143
|
* Delivery of Schema-encoded port envelopes to the Durable Object that owns a FOREIGN
|
|
115
|
-
*
|
|
144
|
+
* Thread (plan §1.3, D-P6-3). The shipped implementation (platform-cloudflare, WP3)
|
|
116
145
|
* calls the owner's `portCall` over native Durable Object JS RPC via
|
|
117
|
-
* `namespace.idFromName(
|
|
146
|
+
* `namespace.idFromName(threadId)`; the protocol is transport-agnostic and any carrier
|
|
118
147
|
* that moves the encoded envelopes verbatim satisfies this service. Implementations MUST
|
|
119
148
|
* surface every delivery problem as `PortTransportError` and must never fabricate an answer.
|
|
120
149
|
*/
|
|
121
|
-
export class
|
|
122
|
-
|
|
150
|
+
export class ThreadPortTransport extends Context.Service<
|
|
151
|
+
ThreadPortTransport,
|
|
123
152
|
{
|
|
124
153
|
readonly call: (
|
|
125
|
-
|
|
154
|
+
threadId: ThreadId,
|
|
126
155
|
request: PortRequestEnvelope,
|
|
127
156
|
) => Effect.Effect<unknown, PortTransportError>;
|
|
128
157
|
}
|
|
129
|
-
>()("@effect-agent/storage-cloudflare/
|
|
158
|
+
>()("@effect-agent/storage-cloudflare/ThreadPortTransport") {}
|
|
130
159
|
|
|
131
160
|
/** Construction options shared by both routed port Layers. */
|
|
132
161
|
export interface RoutedPortOptions {
|
|
133
162
|
/**
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
*
|
|
163
|
+
* Decide whether this physical owner stores the addressed logical Thread. Local
|
|
164
|
+
* requests use the same SQLite facets; foreign requests use the transport or fail
|
|
165
|
+
* fast typed. The predicate determines placement, never caller authorization.
|
|
137
166
|
*/
|
|
138
|
-
readonly
|
|
167
|
+
readonly ownsThread: (threadId: ThreadId) => boolean;
|
|
139
168
|
}
|
|
140
169
|
|
|
141
170
|
/** Where one port request must execute. */
|
|
142
171
|
type RouteTarget =
|
|
143
172
|
| { readonly _tag: "local" }
|
|
144
|
-
| { readonly _tag: "foreign"; readonly
|
|
173
|
+
| { readonly _tag: "foreign"; readonly threadId: ThreadId };
|
|
145
174
|
|
|
146
175
|
const LOCAL: RouteTarget = { _tag: "local" };
|
|
147
176
|
|
|
148
177
|
/**
|
|
149
|
-
* Parse a DC-minted routable Submission identity — `{uuidv7}:{
|
|
150
|
-
* FIRST `:` because the
|
|
178
|
+
* Parse a DC-minted routable Submission identity — `{uuidv7}:{threadId}`, split at the
|
|
179
|
+
* FIRST `:` because the Thread tail may itself contain colons (D-P6-5). This adapter
|
|
151
180
|
* minted the format at admission and is the ONLY component that parses it; identities that do
|
|
152
181
|
* not carry the minted shape (no separator, non-UUID head, empty tail) fall back to the local
|
|
153
182
|
* facet, which is the only authority this Object can consult without inventing an owner.
|
|
@@ -155,7 +184,7 @@ const LOCAL: RouteTarget = { _tag: "local" };
|
|
|
155
184
|
* refused them at admission, so they cannot name any stored row anywhere.
|
|
156
185
|
*/
|
|
157
186
|
const routableSubmissionTarget = (
|
|
158
|
-
|
|
187
|
+
ownsThread: RoutedPortOptions["ownsThread"],
|
|
159
188
|
): ((operation: string, submissionId: string) => Effect.Effect<RouteTarget, LedgerError>) =>
|
|
160
189
|
Effect.fn("DoPortRouting.routableSubmissionTarget")(function* (
|
|
161
190
|
operation: string,
|
|
@@ -171,63 +200,49 @@ const routableSubmissionTarget = (
|
|
|
171
200
|
});
|
|
172
201
|
}
|
|
173
202
|
const separator = submissionId.indexOf(":");
|
|
203
|
+
|
|
174
204
|
if (separator === -1) return LOCAL;
|
|
175
205
|
if (!UUID_HEAD_PATTERN.test(submissionId.slice(0, separator))) return LOCAL;
|
|
176
206
|
const tail = submissionId.slice(separator + 1);
|
|
177
|
-
|
|
178
|
-
return yield*
|
|
179
|
-
Effect.map((
|
|
207
|
+
|
|
208
|
+
return yield* decodeThreadId(tail).pipe(
|
|
209
|
+
Effect.map((threadId): RouteTarget =>
|
|
210
|
+
ownsThread(threadId) ? LOCAL : { _tag: "foreign", threadId },
|
|
211
|
+
),
|
|
180
212
|
Effect.orElseSucceed(() => LOCAL),
|
|
181
213
|
);
|
|
182
214
|
});
|
|
183
215
|
|
|
184
|
-
const
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
result._tag === tag;
|
|
188
|
-
|
|
189
|
-
const isAdmitConflict = (failure: PortFailure): failure is AdmissionConflict =>
|
|
190
|
-
failure._tag === "AdmissionConflict";
|
|
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";
|
|
216
|
+
const NoAdditionalPortFailure = Schema.Never;
|
|
217
|
+
const AbortPortFailure = Schema.Union([SettlementConflict, JoinedToHost]);
|
|
218
|
+
const AppendPortFailure = Schema.Union([ThreadNotMaterialized, AppendConflict, FenceRejected]);
|
|
204
219
|
|
|
205
220
|
/**
|
|
206
221
|
* The fail-fast refusal for any foreign operation OUTSIDE the closed route-capable subset
|
|
207
222
|
* (plan §1.3): honesty over accidental distribution.
|
|
208
223
|
*/
|
|
209
|
-
const
|
|
224
|
+
const crossThreadLedgerError = (operation: string, target: string): LedgerError =>
|
|
210
225
|
LedgerError.make({
|
|
211
226
|
operation,
|
|
212
227
|
message:
|
|
213
|
-
`${operation} addressed to foreign
|
|
228
|
+
`${operation} addressed to foreign Thread ${target} is not route-capable; the ` +
|
|
214
229
|
"closed cross-Object subset is admit, markReady, lookup, resolveAdmission, " +
|
|
215
230
|
"requestAbort, and recordChildSettled. Every other ledger operation is lane-local by " +
|
|
216
|
-
"construction and must execute inside the owning
|
|
231
|
+
"construction and must execute inside the owning Thread's Durable Object.",
|
|
217
232
|
});
|
|
218
233
|
|
|
219
|
-
const
|
|
220
|
-
|
|
234
|
+
const crossThreadStoreError = (operation: string, target: string): ThreadStoreError =>
|
|
235
|
+
ThreadStoreError.make({
|
|
221
236
|
operation,
|
|
222
237
|
message:
|
|
223
|
-
`${operation} addressed to foreign
|
|
238
|
+
`${operation} addressed to foreign Thread ${target} is not route-capable; the ` +
|
|
224
239
|
"closed cross-Object subset is materialize, append, read (paged), inspectTail, and " +
|
|
225
240
|
"export. Observation and checkpoints are lane-local by construction and must execute " +
|
|
226
|
-
"inside the owning
|
|
241
|
+
"inside the owning Thread's Durable Object.",
|
|
227
242
|
});
|
|
228
243
|
|
|
229
|
-
const makeTransportCall = (transport:
|
|
230
|
-
Effect.fn("DoPortRouting.transportCall")(function* (target:
|
|
244
|
+
const makeTransportCall = (transport: ThreadPortTransport["Service"]) =>
|
|
245
|
+
Effect.fn("DoPortRouting.transportCall")(function* (target: ThreadId, call: PortRequest) {
|
|
231
246
|
const encoded = yield* encodePortRequest(call).pipe(
|
|
232
247
|
Effect.mapError((error) =>
|
|
233
248
|
PortProtocolError.make({
|
|
@@ -235,7 +250,9 @@ const makeTransportCall = (transport: ConversationPortTransport["Service"]) =>
|
|
|
235
250
|
}),
|
|
236
251
|
),
|
|
237
252
|
);
|
|
253
|
+
|
|
238
254
|
const raw = yield* transport.call(target, encoded);
|
|
255
|
+
|
|
239
256
|
return yield* decodePortResponse(raw).pipe(
|
|
240
257
|
Effect.mapError((error) =>
|
|
241
258
|
PortProtocolError.make({
|
|
@@ -251,9 +268,9 @@ const makeRoutedLedgerServices = Effect.fn("DoPortRouting.makeRoutedLedgerServic
|
|
|
251
268
|
options: RoutedPortOptions,
|
|
252
269
|
) {
|
|
253
270
|
const local = yield* SubmissionLedger;
|
|
254
|
-
const transport = yield*
|
|
271
|
+
const transport = yield* ThreadPortTransport;
|
|
255
272
|
const transportCall: TransportCall = makeTransportCall(transport);
|
|
256
|
-
const submissionTarget = routableSubmissionTarget(options.
|
|
273
|
+
const submissionTarget = routableSubmissionTarget(options.ownsThread);
|
|
257
274
|
|
|
258
275
|
const routeFailure =
|
|
259
276
|
(operation: string, target: string) =>
|
|
@@ -261,7 +278,7 @@ const makeRoutedLedgerServices = Effect.fn("DoPortRouting.makeRoutedLedgerServic
|
|
|
261
278
|
LedgerError.make({
|
|
262
279
|
operation,
|
|
263
280
|
message: boundPortDiagnostic(
|
|
264
|
-
`Routed ${operation} to the
|
|
281
|
+
`Routed ${operation} to the Thread Object owning ${target} failed: ${error.message}`,
|
|
265
282
|
),
|
|
266
283
|
cause: error,
|
|
267
284
|
});
|
|
@@ -272,48 +289,50 @@ const makeRoutedLedgerServices = Effect.fn("DoPortRouting.makeRoutedLedgerServic
|
|
|
272
289
|
* any failure outside the operation's declared surface — including protocol anomalies — is
|
|
273
290
|
* folded into a `LedgerError` naming the anomaly instead of being erased or re-thrown raw.
|
|
274
291
|
*/
|
|
275
|
-
const foreignLedgerCall = <
|
|
292
|
+
const foreignLedgerCall = <ResultSchema extends Schema.Top, FailureSchema extends Schema.Top>(
|
|
276
293
|
operation: string,
|
|
277
|
-
target:
|
|
294
|
+
target: ThreadId,
|
|
278
295
|
call: PortRequest,
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
): Effect.Effect<
|
|
282
|
-
|
|
296
|
+
resultSchema: ResultSchema,
|
|
297
|
+
failureSchema: FailureSchema,
|
|
298
|
+
): Effect.Effect<ResultSchema["Type"], FailureSchema["Type"] | LedgerError> => {
|
|
299
|
+
const isExpectedResult = Schema.is(resultSchema);
|
|
300
|
+
const isExpectedFailure = Schema.is(failureSchema);
|
|
301
|
+
|
|
302
|
+
return transportCall(target, call).pipe(
|
|
283
303
|
Effect.mapError(routeFailure(operation, target)),
|
|
284
304
|
Effect.flatMap(
|
|
285
|
-
(
|
|
286
|
-
response,
|
|
287
|
-
): Effect.Effect<
|
|
288
|
-
Extract<PortResult, { readonly _tag: Tag }>,
|
|
289
|
-
ExpectedFailure | LedgerError
|
|
290
|
-
> => {
|
|
305
|
+
(response): Effect.Effect<ResultSchema["Type"], FailureSchema["Type"] | LedgerError> => {
|
|
291
306
|
if (response._tag === "PortFailed") {
|
|
292
307
|
const failure = response.failure;
|
|
308
|
+
|
|
293
309
|
if (isExpectedFailure(failure)) return Effect.fail(failure);
|
|
294
310
|
if (failure._tag === "LedgerError") return Effect.fail(failure);
|
|
311
|
+
|
|
295
312
|
return Effect.fail(
|
|
296
313
|
LedgerError.make({
|
|
297
314
|
operation,
|
|
298
315
|
message: boundPortDiagnostic(
|
|
299
|
-
`The
|
|
300
|
-
`out-of-contract failure ${failure._tag}
|
|
316
|
+
`The Thread Object owning ${target} answered ${operation} with the ` +
|
|
317
|
+
`out-of-contract failure ${failure._tag}`,
|
|
301
318
|
),
|
|
302
319
|
cause: failure,
|
|
303
320
|
}),
|
|
304
321
|
);
|
|
305
322
|
}
|
|
306
323
|
const result = response.result;
|
|
307
|
-
|
|
324
|
+
|
|
325
|
+
if (!isExpectedResult(result)) {
|
|
308
326
|
return Effect.fail(
|
|
309
327
|
LedgerError.make({
|
|
310
328
|
operation,
|
|
311
329
|
message:
|
|
312
|
-
`The
|
|
313
|
-
`mismatched result ${result._tag}
|
|
330
|
+
`The Thread Object owning ${target} answered ${operation} with the ` +
|
|
331
|
+
`mismatched result ${result._tag}.`,
|
|
314
332
|
}),
|
|
315
333
|
);
|
|
316
334
|
}
|
|
335
|
+
|
|
317
336
|
return Effect.succeed(result);
|
|
318
337
|
},
|
|
319
338
|
),
|
|
@@ -321,6 +340,7 @@ const makeRoutedLedgerServices = Effect.fn("DoPortRouting.makeRoutedLedgerServic
|
|
|
321
340
|
attributes: { operation, target },
|
|
322
341
|
}),
|
|
323
342
|
);
|
|
343
|
+
};
|
|
324
344
|
|
|
325
345
|
/**
|
|
326
346
|
* Routed `resolveAdmission` — where the S2 tri-state becomes real (plan §1.3): when the
|
|
@@ -331,7 +351,7 @@ const makeRoutedLedgerServices = Effect.fn("DoPortRouting.makeRoutedLedgerServic
|
|
|
331
351
|
* reached and reported its own storage failure.
|
|
332
352
|
*/
|
|
333
353
|
const resolveForeignAdmission = (
|
|
334
|
-
target:
|
|
354
|
+
target: ThreadId,
|
|
335
355
|
request: SubmissionLookupByKey,
|
|
336
356
|
): Effect.Effect<
|
|
337
357
|
| AdmissionIndeterminate
|
|
@@ -342,10 +362,11 @@ const makeRoutedLedgerServices = Effect.fn("DoPortRouting.makeRoutedLedgerServic
|
|
|
342
362
|
Effect.flatMap((response) => {
|
|
343
363
|
if (response._tag === "PortFailed") {
|
|
344
364
|
if (response.failure._tag === "LedgerError") return Effect.fail(response.failure);
|
|
365
|
+
|
|
345
366
|
return Effect.succeed(
|
|
346
367
|
AdmissionIndeterminate.make({
|
|
347
368
|
reason: boundPortDiagnostic(
|
|
348
|
-
`The
|
|
369
|
+
`The Thread Object owning ${target} answered resolveAdmission with the ` +
|
|
349
370
|
`out-of-contract failure ${response.failure._tag}: ${response.failure.message}`,
|
|
350
371
|
),
|
|
351
372
|
}),
|
|
@@ -355,12 +376,13 @@ const makeRoutedLedgerServices = Effect.fn("DoPortRouting.makeRoutedLedgerServic
|
|
|
355
376
|
return Effect.succeed(
|
|
356
377
|
AdmissionIndeterminate.make({
|
|
357
378
|
reason: boundPortDiagnostic(
|
|
358
|
-
`The
|
|
379
|
+
`The Thread Object owning ${target} answered resolveAdmission with the ` +
|
|
359
380
|
`mismatched result ${response.result._tag}.`,
|
|
360
381
|
),
|
|
361
382
|
}),
|
|
362
383
|
);
|
|
363
384
|
}
|
|
385
|
+
|
|
364
386
|
return Effect.succeed(response.result.resolution);
|
|
365
387
|
}),
|
|
366
388
|
Effect.catchTags({
|
|
@@ -368,7 +390,7 @@ const makeRoutedLedgerServices = Effect.fn("DoPortRouting.makeRoutedLedgerServic
|
|
|
368
390
|
Effect.succeed(
|
|
369
391
|
AdmissionIndeterminate.make({
|
|
370
392
|
reason: boundPortDiagnostic(
|
|
371
|
-
`The
|
|
393
|
+
`The Thread Object owning ${target} is unreachable: ${error.message}`,
|
|
372
394
|
),
|
|
373
395
|
}),
|
|
374
396
|
),
|
|
@@ -376,7 +398,7 @@ const makeRoutedLedgerServices = Effect.fn("DoPortRouting.makeRoutedLedgerServic
|
|
|
376
398
|
Effect.succeed(
|
|
377
399
|
AdmissionIndeterminate.make({
|
|
378
400
|
reason: boundPortDiagnostic(
|
|
379
|
-
`The answer of the
|
|
401
|
+
`The answer of the Thread Object owning ${target} could not be ` +
|
|
380
402
|
`understood: ${error.message}`,
|
|
381
403
|
),
|
|
382
404
|
}),
|
|
@@ -387,15 +409,15 @@ const makeRoutedLedgerServices = Effect.fn("DoPortRouting.makeRoutedLedgerServic
|
|
|
387
409
|
|
|
388
410
|
const foreignLookupById = (
|
|
389
411
|
operation: string,
|
|
390
|
-
target:
|
|
412
|
+
target: ThreadId,
|
|
391
413
|
submissionId: SubmissionId,
|
|
392
414
|
): Effect.Effect<Option.Option<SubmissionSnapshot>, LedgerError> =>
|
|
393
415
|
foreignLedgerCall(
|
|
394
416
|
operation,
|
|
395
417
|
target,
|
|
396
418
|
LedgerLookupCall.make({ request: SubmissionLookupById.make({ submissionId }) }),
|
|
397
|
-
|
|
398
|
-
|
|
419
|
+
LedgerLookupResult,
|
|
420
|
+
NoAdditionalPortFailure,
|
|
399
421
|
).pipe(
|
|
400
422
|
Effect.map((result) =>
|
|
401
423
|
result.submission === undefined ? Option.none() : Option.some(result.submission),
|
|
@@ -413,18 +435,24 @@ const makeRoutedLedgerServices = Effect.fn("DoPortRouting.makeRoutedLedgerServic
|
|
|
413
435
|
snapshot: RecoverySnapshot,
|
|
414
436
|
): Effect.fn.Return<RecoverySnapshot, LedgerError> {
|
|
415
437
|
const operation = "ledger load recovery snapshot";
|
|
438
|
+
|
|
416
439
|
const attachments = new Map(
|
|
417
440
|
snapshot.childAttachments.map((attachment) => [attachment.childSubmissionId, attachment]),
|
|
418
441
|
);
|
|
442
|
+
|
|
419
443
|
let enriched = false;
|
|
444
|
+
|
|
420
445
|
for (const reservation of snapshot.childReservations) {
|
|
421
446
|
const childSubmissionId = reservation.childSubmissionId;
|
|
447
|
+
|
|
422
448
|
if (childSubmissionId === undefined || attachments.has(childSubmissionId)) continue;
|
|
423
449
|
const target = yield* submissionTarget(operation, childSubmissionId);
|
|
450
|
+
|
|
424
451
|
// A local or opaque child identity was already answered authoritatively by the local
|
|
425
452
|
// facet; absence there means the child admission never committed.
|
|
426
453
|
if (target._tag !== "foreign") continue;
|
|
427
|
-
const child = yield* foreignLookupById(operation, target.
|
|
454
|
+
const child = yield* foreignLookupById(operation, target.threadId, childSubmissionId);
|
|
455
|
+
|
|
428
456
|
if (Option.isNone(child)) continue;
|
|
429
457
|
attachments.set(
|
|
430
458
|
childSubmissionId,
|
|
@@ -442,11 +470,14 @@ const makeRoutedLedgerServices = Effect.fn("DoPortRouting.makeRoutedLedgerServic
|
|
|
442
470
|
if (!enriched) return snapshot;
|
|
443
471
|
// Rebuild in reservation (parent Tool Call) order, the order the local facet documents.
|
|
444
472
|
const ordered: Array<ChildAttachmentSnapshot> = [];
|
|
473
|
+
|
|
445
474
|
for (const reservation of snapshot.childReservations) {
|
|
446
475
|
if (reservation.childSubmissionId === undefined) continue;
|
|
447
476
|
const attachment = attachments.get(reservation.childSubmissionId);
|
|
477
|
+
|
|
448
478
|
if (attachment !== undefined) ordered.push(attachment);
|
|
449
479
|
}
|
|
480
|
+
|
|
450
481
|
return RecoverySnapshot.make({ ...snapshot, childAttachments: ordered });
|
|
451
482
|
});
|
|
452
483
|
|
|
@@ -454,14 +485,14 @@ const makeRoutedLedgerServices = Effect.fn("DoPortRouting.makeRoutedLedgerServic
|
|
|
454
485
|
capabilities: local.capabilities,
|
|
455
486
|
|
|
456
487
|
admit: (request) =>
|
|
457
|
-
request.
|
|
488
|
+
options.ownsThread(request.threadId)
|
|
458
489
|
? local.admit(request)
|
|
459
490
|
: foreignLedgerCall(
|
|
460
491
|
"ledger admit",
|
|
461
|
-
request.
|
|
492
|
+
request.threadId,
|
|
462
493
|
LedgerAdmitCall.make({ request }),
|
|
463
|
-
|
|
464
|
-
|
|
494
|
+
LedgerAdmitResult,
|
|
495
|
+
Schema.Union([AdmissionConflict, AdmissionPolicyError]),
|
|
465
496
|
).pipe(Effect.map((reply) => reply.result)),
|
|
466
497
|
|
|
467
498
|
markReady: (request) =>
|
|
@@ -471,10 +502,10 @@ const makeRoutedLedgerServices = Effect.fn("DoPortRouting.makeRoutedLedgerServic
|
|
|
471
502
|
? local.markReady(request)
|
|
472
503
|
: foreignLedgerCall(
|
|
473
504
|
"ledger mark ready",
|
|
474
|
-
target.
|
|
505
|
+
target.threadId,
|
|
475
506
|
LedgerMarkReadyCall.make({ request }),
|
|
476
|
-
|
|
477
|
-
|
|
507
|
+
LedgerMarkReadyResult,
|
|
508
|
+
NoAdditionalPortFailure,
|
|
478
509
|
).pipe(Effect.asVoid),
|
|
479
510
|
),
|
|
480
511
|
),
|
|
@@ -485,17 +516,17 @@ const makeRoutedLedgerServices = Effect.fn("DoPortRouting.makeRoutedLedgerServic
|
|
|
485
516
|
Effect.flatMap((target) =>
|
|
486
517
|
target._tag === "local"
|
|
487
518
|
? local.lookup(request)
|
|
488
|
-
: foreignLookupById("ledger lookup", target.
|
|
519
|
+
: foreignLookupById("ledger lookup", target.threadId, request.submissionId),
|
|
489
520
|
),
|
|
490
521
|
)
|
|
491
|
-
: request.
|
|
522
|
+
: options.ownsThread(request.threadId)
|
|
492
523
|
? local.lookup(request)
|
|
493
524
|
: foreignLedgerCall(
|
|
494
525
|
"ledger lookup",
|
|
495
|
-
request.
|
|
526
|
+
request.threadId,
|
|
496
527
|
LedgerLookupCall.make({ request }),
|
|
497
|
-
|
|
498
|
-
|
|
528
|
+
LedgerLookupResult,
|
|
529
|
+
NoAdditionalPortFailure,
|
|
499
530
|
).pipe(
|
|
500
531
|
Effect.map((result) =>
|
|
501
532
|
result.submission === undefined ? Option.none() : Option.some(result.submission),
|
|
@@ -503,9 +534,42 @@ const makeRoutedLedgerServices = Effect.fn("DoPortRouting.makeRoutedLedgerServic
|
|
|
503
534
|
),
|
|
504
535
|
|
|
505
536
|
resolveAdmission: (request) =>
|
|
506
|
-
request.
|
|
537
|
+
options.ownsThread(request.threadId)
|
|
507
538
|
? local.resolveAdmission(request)
|
|
508
|
-
: resolveForeignAdmission(request.
|
|
539
|
+
: resolveForeignAdmission(request.threadId, request),
|
|
540
|
+
|
|
541
|
+
inspectWorker: (threadId) =>
|
|
542
|
+
options.ownsThread(threadId)
|
|
543
|
+
? local.inspectWorker === undefined
|
|
544
|
+
? Effect.fail(
|
|
545
|
+
LedgerError.make({
|
|
546
|
+
operation: "inspectWorker",
|
|
547
|
+
message: "Worker inspection unavailable",
|
|
548
|
+
}),
|
|
549
|
+
)
|
|
550
|
+
: local.inspectWorker(threadId)
|
|
551
|
+
: foreignLedgerCall(
|
|
552
|
+
"inspect worker",
|
|
553
|
+
threadId,
|
|
554
|
+
LedgerInspectWorkerCall.make({ request: { threadId } }),
|
|
555
|
+
LedgerInspectWorkerResult,
|
|
556
|
+
NoAdditionalPortFailure,
|
|
557
|
+
).pipe(Effect.map((reply) => reply.state)),
|
|
558
|
+
|
|
559
|
+
stopWorker: (request) =>
|
|
560
|
+
options.ownsThread(request.threadId)
|
|
561
|
+
? local.stopWorker === undefined
|
|
562
|
+
? Effect.fail(
|
|
563
|
+
LedgerError.make({ operation: "stopWorker", message: "Worker stop unavailable" }),
|
|
564
|
+
)
|
|
565
|
+
: local.stopWorker(request)
|
|
566
|
+
: foreignLedgerCall(
|
|
567
|
+
"ledger stop worker",
|
|
568
|
+
request.threadId,
|
|
569
|
+
LedgerStopWorkerCall.make({ request }),
|
|
570
|
+
LedgerStopWorkerResult,
|
|
571
|
+
NoAdditionalPortFailure,
|
|
572
|
+
).pipe(Effect.map((reply) => reply.owned)),
|
|
509
573
|
|
|
510
574
|
requestAbort: (request) =>
|
|
511
575
|
submissionTarget("ledger request abort", request.submissionId).pipe(
|
|
@@ -514,10 +578,10 @@ const makeRoutedLedgerServices = Effect.fn("DoPortRouting.makeRoutedLedgerServic
|
|
|
514
578
|
? local.requestAbort(request)
|
|
515
579
|
: foreignLedgerCall(
|
|
516
580
|
"ledger request abort",
|
|
517
|
-
target.
|
|
581
|
+
target.threadId,
|
|
518
582
|
LedgerRequestAbortCall.make({ request }),
|
|
519
|
-
|
|
520
|
-
|
|
583
|
+
LedgerRequestAbortResult,
|
|
584
|
+
AbortPortFailure,
|
|
521
585
|
).pipe(Effect.map((reply) => reply.intent)),
|
|
522
586
|
),
|
|
523
587
|
),
|
|
@@ -529,10 +593,10 @@ const makeRoutedLedgerServices = Effect.fn("DoPortRouting.makeRoutedLedgerServic
|
|
|
529
593
|
? local.recordChildSettled(request)
|
|
530
594
|
: foreignLedgerCall(
|
|
531
595
|
"ledger record child settled",
|
|
532
|
-
target.
|
|
596
|
+
target.threadId,
|
|
533
597
|
LedgerRecordChildSettledCall.make({ request }),
|
|
534
|
-
|
|
535
|
-
|
|
598
|
+
LedgerRecordChildSettledResult,
|
|
599
|
+
NoAdditionalPortFailure,
|
|
536
600
|
).pipe(Effect.map((reply) => reply.outcome)),
|
|
537
601
|
),
|
|
538
602
|
),
|
|
@@ -540,23 +604,21 @@ const makeRoutedLedgerServices = Effect.fn("DoPortRouting.makeRoutedLedgerServic
|
|
|
540
604
|
// Every operation below is lane-local by construction (plan §1.3): a foreign address is
|
|
541
605
|
// an out-of-contract call and fails fast typed instead of being quietly distributed.
|
|
542
606
|
claim: (request) =>
|
|
543
|
-
request.
|
|
607
|
+
options.ownsThread(request.threadId)
|
|
544
608
|
? local.claim(request)
|
|
545
|
-
: Effect.fail(
|
|
609
|
+
: Effect.fail(crossThreadLedgerError("ledger claim", request.threadId)),
|
|
546
610
|
|
|
547
611
|
claimJoining: (request) =>
|
|
548
|
-
request.
|
|
612
|
+
options.ownsThread(request.threadId)
|
|
549
613
|
? local.claimJoining(request)
|
|
550
|
-
: Effect.fail(
|
|
614
|
+
: Effect.fail(crossThreadLedgerError("ledger claim joining", request.threadId)),
|
|
551
615
|
|
|
552
616
|
renewOwnership: (request) =>
|
|
553
617
|
submissionTarget("ledger renew ownership", request.submissionId).pipe(
|
|
554
618
|
Effect.flatMap((target) =>
|
|
555
619
|
target._tag === "local"
|
|
556
620
|
? local.renewOwnership(request)
|
|
557
|
-
: Effect.fail(
|
|
558
|
-
crossConversationLedgerError("ledger renew ownership", target.conversationId),
|
|
559
|
-
),
|
|
621
|
+
: Effect.fail(crossThreadLedgerError("ledger renew ownership", target.threadId)),
|
|
560
622
|
),
|
|
561
623
|
),
|
|
562
624
|
|
|
@@ -565,9 +627,7 @@ const makeRoutedLedgerServices = Effect.fn("DoPortRouting.makeRoutedLedgerServic
|
|
|
565
627
|
Effect.flatMap((target) =>
|
|
566
628
|
target._tag === "local"
|
|
567
629
|
? local.releaseOwnership(request)
|
|
568
|
-
: Effect.fail(
|
|
569
|
-
crossConversationLedgerError("ledger release ownership", target.conversationId),
|
|
570
|
-
),
|
|
630
|
+
: Effect.fail(crossThreadLedgerError("ledger release ownership", target.threadId)),
|
|
571
631
|
),
|
|
572
632
|
),
|
|
573
633
|
|
|
@@ -576,9 +636,7 @@ const makeRoutedLedgerServices = Effect.fn("DoPortRouting.makeRoutedLedgerServic
|
|
|
576
636
|
Effect.flatMap((target) =>
|
|
577
637
|
target._tag === "local"
|
|
578
638
|
? local.markInputApplied(request)
|
|
579
|
-
: Effect.fail(
|
|
580
|
-
crossConversationLedgerError("ledger mark input applied", target.conversationId),
|
|
581
|
-
),
|
|
639
|
+
: Effect.fail(crossThreadLedgerError("ledger mark input applied", target.threadId)),
|
|
582
640
|
),
|
|
583
641
|
),
|
|
584
642
|
|
|
@@ -587,9 +645,7 @@ const makeRoutedLedgerServices = Effect.fn("DoPortRouting.makeRoutedLedgerServic
|
|
|
587
645
|
Effect.flatMap((target) =>
|
|
588
646
|
target._tag === "local"
|
|
589
647
|
? local.reserveSettlement(request)
|
|
590
|
-
: Effect.fail(
|
|
591
|
-
crossConversationLedgerError("ledger reserve settlement", target.conversationId),
|
|
592
|
-
),
|
|
648
|
+
: Effect.fail(crossThreadLedgerError("ledger reserve settlement", target.threadId)),
|
|
593
649
|
),
|
|
594
650
|
),
|
|
595
651
|
|
|
@@ -598,9 +654,7 @@ const makeRoutedLedgerServices = Effect.fn("DoPortRouting.makeRoutedLedgerServic
|
|
|
598
654
|
Effect.flatMap((target) =>
|
|
599
655
|
target._tag === "local"
|
|
600
656
|
? local.finalizeSettlement(request)
|
|
601
|
-
: Effect.fail(
|
|
602
|
-
crossConversationLedgerError("ledger finalize settlement", target.conversationId),
|
|
603
|
-
),
|
|
657
|
+
: Effect.fail(crossThreadLedgerError("ledger finalize settlement", target.threadId)),
|
|
604
658
|
),
|
|
605
659
|
),
|
|
606
660
|
|
|
@@ -609,9 +663,7 @@ const makeRoutedLedgerServices = Effect.fn("DoPortRouting.makeRoutedLedgerServic
|
|
|
609
663
|
Effect.flatMap((target) =>
|
|
610
664
|
target._tag === "local"
|
|
611
665
|
? local.markJoined(request)
|
|
612
|
-
: Effect.fail(
|
|
613
|
-
crossConversationLedgerError("ledger mark joined", target.conversationId),
|
|
614
|
-
),
|
|
666
|
+
: Effect.fail(crossThreadLedgerError("ledger mark joined", target.threadId)),
|
|
615
667
|
),
|
|
616
668
|
),
|
|
617
669
|
|
|
@@ -620,9 +672,7 @@ const makeRoutedLedgerServices = Effect.fn("DoPortRouting.makeRoutedLedgerServic
|
|
|
620
672
|
Effect.flatMap((target) =>
|
|
621
673
|
target._tag === "local"
|
|
622
674
|
? local.revertJoining(request)
|
|
623
|
-
: Effect.fail(
|
|
624
|
-
crossConversationLedgerError("ledger revert joining", target.conversationId),
|
|
625
|
-
),
|
|
675
|
+
: Effect.fail(crossThreadLedgerError("ledger revert joining", target.threadId)),
|
|
626
676
|
),
|
|
627
677
|
),
|
|
628
678
|
|
|
@@ -631,7 +681,7 @@ const makeRoutedLedgerServices = Effect.fn("DoPortRouting.makeRoutedLedgerServic
|
|
|
631
681
|
Effect.flatMap((target) =>
|
|
632
682
|
target._tag === "local"
|
|
633
683
|
? local.suspend(request)
|
|
634
|
-
: Effect.fail(
|
|
684
|
+
: Effect.fail(crossThreadLedgerError("ledger suspend", target.threadId)),
|
|
635
685
|
),
|
|
636
686
|
),
|
|
637
687
|
|
|
@@ -641,10 +691,7 @@ const makeRoutedLedgerServices = Effect.fn("DoPortRouting.makeRoutedLedgerServic
|
|
|
641
691
|
target._tag === "local"
|
|
642
692
|
? local.recordApprovalDecision(command)
|
|
643
693
|
: Effect.fail(
|
|
644
|
-
|
|
645
|
-
"ledger record approval decision",
|
|
646
|
-
target.conversationId,
|
|
647
|
-
),
|
|
694
|
+
crossThreadLedgerError("ledger record approval decision", target.threadId),
|
|
648
695
|
),
|
|
649
696
|
),
|
|
650
697
|
),
|
|
@@ -654,9 +701,7 @@ const makeRoutedLedgerServices = Effect.fn("DoPortRouting.makeRoutedLedgerServic
|
|
|
654
701
|
Effect.flatMap((target) =>
|
|
655
702
|
target._tag === "local"
|
|
656
703
|
? local.markUnknown(request)
|
|
657
|
-
: Effect.fail(
|
|
658
|
-
crossConversationLedgerError("ledger mark unknown", target.conversationId),
|
|
659
|
-
),
|
|
704
|
+
: Effect.fail(crossThreadLedgerError("ledger mark unknown", target.threadId)),
|
|
660
705
|
),
|
|
661
706
|
),
|
|
662
707
|
|
|
@@ -666,10 +711,7 @@ const makeRoutedLedgerServices = Effect.fn("DoPortRouting.makeRoutedLedgerServic
|
|
|
666
711
|
target._tag === "local"
|
|
667
712
|
? local.recordUnknownResolution(command)
|
|
668
713
|
: Effect.fail(
|
|
669
|
-
|
|
670
|
-
"ledger record unknown resolution",
|
|
671
|
-
target.conversationId,
|
|
672
|
-
),
|
|
714
|
+
crossThreadLedgerError("ledger record unknown resolution", target.threadId),
|
|
673
715
|
),
|
|
674
716
|
),
|
|
675
717
|
),
|
|
@@ -679,33 +721,35 @@ const makeRoutedLedgerServices = Effect.fn("DoPortRouting.makeRoutedLedgerServic
|
|
|
679
721
|
Effect.flatMap((target) =>
|
|
680
722
|
target._tag === "local"
|
|
681
723
|
? local.reserveChildBudget(request)
|
|
682
|
-
: Effect.fail(
|
|
683
|
-
crossConversationLedgerError("ledger reserve child budget", target.conversationId),
|
|
684
|
-
),
|
|
724
|
+
: Effect.fail(crossThreadLedgerError("ledger reserve child budget", target.threadId)),
|
|
685
725
|
),
|
|
686
726
|
),
|
|
687
727
|
|
|
688
|
-
// Reservation identities carry no
|
|
728
|
+
// Reservation identities carry no Thread address; the reservation row lives in the
|
|
689
729
|
// parent's own Object and these transitions are parent-lane-local by construction, so
|
|
690
730
|
// they always execute on the local facet (which fails typed for an unknown row).
|
|
691
731
|
attachChildToReservation: local.attachChildToReservation,
|
|
692
732
|
beginChildBudgetRelease: local.beginChildBudgetRelease,
|
|
693
733
|
releaseChildBudget: local.releaseChildBudget,
|
|
694
734
|
|
|
695
|
-
// The local scan IS the whole worklist: one
|
|
735
|
+
// The local scan IS the whole worklist: one Thread per Object (durability §5).
|
|
696
736
|
scanNonterminal: local.scanNonterminal,
|
|
697
737
|
|
|
738
|
+
readAbortIntent: (request) =>
|
|
739
|
+
submissionTarget("ledger read abort intent", request.submissionId).pipe(
|
|
740
|
+
Effect.flatMap((target) =>
|
|
741
|
+
target._tag === "local"
|
|
742
|
+
? local.readAbortIntent(request)
|
|
743
|
+
: Effect.fail(crossThreadLedgerError("ledger read abort intent", target.threadId)),
|
|
744
|
+
),
|
|
745
|
+
),
|
|
746
|
+
|
|
698
747
|
loadRecoverySnapshot: (request) =>
|
|
699
748
|
submissionTarget("ledger load recovery snapshot", request.submissionId).pipe(
|
|
700
749
|
Effect.flatMap((target) =>
|
|
701
750
|
target._tag === "local"
|
|
702
751
|
? local.loadRecoverySnapshot(request).pipe(Effect.flatMap(enrichChildAttachments))
|
|
703
|
-
: Effect.fail(
|
|
704
|
-
crossConversationLedgerError(
|
|
705
|
-
"ledger load recovery snapshot",
|
|
706
|
-
target.conversationId,
|
|
707
|
-
),
|
|
708
|
-
),
|
|
752
|
+
: Effect.fail(crossThreadLedgerError("ledger load recovery snapshot", target.threadId)),
|
|
709
753
|
),
|
|
710
754
|
),
|
|
711
755
|
});
|
|
@@ -716,67 +760,70 @@ const makeRoutedLedgerServices = Effect.fn("DoPortRouting.makeRoutedLedgerServic
|
|
|
716
760
|
const makeRoutedStoreServices = Effect.fn("DoPortRouting.makeRoutedStoreServices")(function* (
|
|
717
761
|
options: RoutedPortOptions,
|
|
718
762
|
) {
|
|
719
|
-
const local = yield*
|
|
720
|
-
const
|
|
763
|
+
const local = yield* ThreadStore;
|
|
764
|
+
const checkpoints = local.checkpoints;
|
|
765
|
+
const recoveryCheckpoints = local.recoveryCheckpoints;
|
|
766
|
+
const transport = yield* ThreadPortTransport;
|
|
721
767
|
const transportCall: TransportCall = makeTransportCall(transport);
|
|
722
768
|
|
|
723
769
|
const routeFailure =
|
|
724
770
|
(operation: string, target: string) =>
|
|
725
|
-
(error: PortTransportError | PortProtocolError):
|
|
726
|
-
|
|
771
|
+
(error: PortTransportError | PortProtocolError): ThreadStoreError =>
|
|
772
|
+
ThreadStoreError.make({
|
|
727
773
|
operation,
|
|
728
774
|
message: boundPortDiagnostic(
|
|
729
|
-
`Routed ${operation} to the
|
|
775
|
+
`Routed ${operation} to the Thread Object owning ${target} failed: ${error.message}`,
|
|
730
776
|
),
|
|
731
777
|
cause: error,
|
|
732
778
|
});
|
|
733
779
|
|
|
734
|
-
/** The store twin of `foreignLedgerCall` with `
|
|
735
|
-
const foreignStoreCall = <
|
|
780
|
+
/** The store twin of `foreignLedgerCall` with `ThreadStoreError` as the base error. */
|
|
781
|
+
const foreignStoreCall = <ResultSchema extends Schema.Top, FailureSchema extends Schema.Top>(
|
|
736
782
|
operation: string,
|
|
737
|
-
target:
|
|
783
|
+
target: ThreadId,
|
|
738
784
|
call: PortRequest,
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
): Effect.Effect<
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
transportCall(target, call).pipe(
|
|
785
|
+
resultSchema: ResultSchema,
|
|
786
|
+
failureSchema: FailureSchema,
|
|
787
|
+
): Effect.Effect<ResultSchema["Type"], FailureSchema["Type"] | ThreadStoreError> => {
|
|
788
|
+
const isExpectedResult = Schema.is(resultSchema);
|
|
789
|
+
const isExpectedFailure = Schema.is(failureSchema);
|
|
790
|
+
|
|
791
|
+
return transportCall(target, call).pipe(
|
|
746
792
|
Effect.mapError(routeFailure(operation, target)),
|
|
747
793
|
Effect.flatMap(
|
|
748
794
|
(
|
|
749
795
|
response,
|
|
750
|
-
): Effect.Effect<
|
|
751
|
-
Extract<PortResult, { readonly _tag: Tag }>,
|
|
752
|
-
ExpectedFailure | ConversationStoreError
|
|
753
|
-
> => {
|
|
796
|
+
): Effect.Effect<ResultSchema["Type"], FailureSchema["Type"] | ThreadStoreError> => {
|
|
754
797
|
if (response._tag === "PortFailed") {
|
|
755
798
|
const failure = response.failure;
|
|
799
|
+
|
|
756
800
|
if (isExpectedFailure(failure)) return Effect.fail(failure);
|
|
757
|
-
if (failure._tag === "
|
|
801
|
+
if (failure._tag === "ThreadStoreError") return Effect.fail(failure);
|
|
802
|
+
|
|
758
803
|
return Effect.fail(
|
|
759
|
-
|
|
804
|
+
ThreadStoreError.make({
|
|
760
805
|
operation,
|
|
761
806
|
message: boundPortDiagnostic(
|
|
762
|
-
`The
|
|
763
|
-
`out-of-contract failure ${failure._tag}
|
|
807
|
+
`The Thread Object owning ${target} answered ${operation} with the ` +
|
|
808
|
+
`out-of-contract failure ${failure._tag}`,
|
|
764
809
|
),
|
|
765
810
|
cause: failure,
|
|
766
811
|
}),
|
|
767
812
|
);
|
|
768
813
|
}
|
|
769
814
|
const result = response.result;
|
|
770
|
-
|
|
815
|
+
|
|
816
|
+
if (!isExpectedResult(result)) {
|
|
771
817
|
return Effect.fail(
|
|
772
|
-
|
|
818
|
+
ThreadStoreError.make({
|
|
773
819
|
operation,
|
|
774
820
|
message:
|
|
775
|
-
`The
|
|
776
|
-
`mismatched result ${result._tag}
|
|
821
|
+
`The Thread Object owning ${target} answered ${operation} with the ` +
|
|
822
|
+
`mismatched result ${result._tag}.`,
|
|
777
823
|
}),
|
|
778
824
|
);
|
|
779
825
|
}
|
|
826
|
+
|
|
780
827
|
return Effect.succeed(result);
|
|
781
828
|
},
|
|
782
829
|
),
|
|
@@ -784,120 +831,192 @@ const makeRoutedStoreServices = Effect.fn("DoPortRouting.makeRoutedStoreServices
|
|
|
784
831
|
attributes: { operation, target },
|
|
785
832
|
}),
|
|
786
833
|
);
|
|
834
|
+
};
|
|
835
|
+
|
|
836
|
+
const routed = ThreadStore.of({
|
|
837
|
+
countPeerMessages: (request) =>
|
|
838
|
+
options.ownsThread(request.threadId)
|
|
839
|
+
? local.countPeerMessages === undefined
|
|
840
|
+
? Effect.fail(crossThreadStoreError("peer count unavailable", request.threadId))
|
|
841
|
+
: local.countPeerMessages(request)
|
|
842
|
+
: foreignStoreCall(
|
|
843
|
+
"thread countPeerMessages",
|
|
844
|
+
request.threadId,
|
|
845
|
+
StoreCountPeerMessagesCall.make({ request }),
|
|
846
|
+
StoreCountPeerMessagesResult,
|
|
847
|
+
ThreadNotMaterialized,
|
|
848
|
+
).pipe(Effect.map((reply) => reply.count)),
|
|
787
849
|
|
|
788
|
-
const routed = ConversationStore.of({
|
|
789
850
|
materialize: (request) =>
|
|
790
|
-
request.
|
|
851
|
+
options.ownsThread(request.threadId)
|
|
791
852
|
? local.materialize(request)
|
|
792
853
|
: foreignStoreCall(
|
|
793
|
-
"
|
|
794
|
-
request.
|
|
854
|
+
"thread materialize",
|
|
855
|
+
request.threadId,
|
|
795
856
|
StoreMaterializeCall.make({ request }),
|
|
796
|
-
|
|
797
|
-
|
|
857
|
+
StoreMaterializeResult,
|
|
858
|
+
FenceRejected,
|
|
798
859
|
).pipe(Effect.asVoid),
|
|
799
860
|
|
|
800
861
|
append: (request) =>
|
|
801
|
-
request.
|
|
862
|
+
options.ownsThread(request.threadId)
|
|
802
863
|
? local.append(request)
|
|
803
864
|
: foreignStoreCall(
|
|
804
|
-
"
|
|
805
|
-
request.
|
|
865
|
+
"thread append",
|
|
866
|
+
request.threadId,
|
|
806
867
|
StoreAppendCall.make({ request }),
|
|
807
|
-
|
|
808
|
-
|
|
868
|
+
StoreAppendResult,
|
|
869
|
+
AppendPortFailure,
|
|
809
870
|
).pipe(Effect.map((reply) => reply.result)),
|
|
810
871
|
|
|
811
872
|
read: (request) =>
|
|
812
|
-
request.
|
|
873
|
+
options.ownsThread(request.threadId)
|
|
813
874
|
? local.read(request)
|
|
814
875
|
: Stream.unwrap(
|
|
815
876
|
foreignStoreCall(
|
|
816
|
-
"
|
|
817
|
-
request.
|
|
877
|
+
"thread read",
|
|
878
|
+
request.threadId,
|
|
818
879
|
StoreReadPageCall.make({ request }),
|
|
819
|
-
|
|
820
|
-
|
|
880
|
+
StoreReadPageResult,
|
|
881
|
+
ThreadNotMaterialized,
|
|
821
882
|
).pipe(Effect.map((reply) => Stream.fromIterable(reply.records))),
|
|
822
883
|
),
|
|
823
884
|
|
|
824
885
|
inspectTail: (request) =>
|
|
825
|
-
request.
|
|
886
|
+
options.ownsThread(request.threadId)
|
|
826
887
|
? local.inspectTail(request)
|
|
827
888
|
: foreignStoreCall(
|
|
828
|
-
"
|
|
829
|
-
request.
|
|
889
|
+
"thread inspect tail",
|
|
890
|
+
request.threadId,
|
|
830
891
|
StoreInspectTailCall.make({ request }),
|
|
831
|
-
|
|
832
|
-
|
|
892
|
+
StoreInspectTailResult,
|
|
893
|
+
ThreadNotMaterialized,
|
|
833
894
|
).pipe(Effect.map((reply) => reply.tail)),
|
|
834
895
|
|
|
835
896
|
export: (request) =>
|
|
836
|
-
request.
|
|
897
|
+
options.ownsThread(request.threadId)
|
|
837
898
|
? local.export(request)
|
|
838
899
|
: foreignStoreCall(
|
|
839
|
-
"
|
|
840
|
-
request.
|
|
900
|
+
"thread export",
|
|
901
|
+
request.threadId,
|
|
841
902
|
StoreExportCall.make({ request }),
|
|
842
|
-
|
|
843
|
-
|
|
903
|
+
StoreExportResult,
|
|
904
|
+
ThreadNotMaterialized,
|
|
844
905
|
).pipe(Effect.map((reply) => reply.export)),
|
|
845
906
|
|
|
846
|
-
// Observation and checkpoints are lane-local
|
|
847
|
-
//
|
|
848
|
-
//
|
|
907
|
+
// Observation and checkpoints are lane-local: the closed route-capable store subset
|
|
908
|
+
// is materialize/append/read/inspectTail/export. Recovery cache misses can fall back
|
|
909
|
+
// to those canonical reads, including when the cache belongs to a foreign Object.
|
|
849
910
|
observe: (request) =>
|
|
850
|
-
request.
|
|
911
|
+
options.ownsThread(request.threadId)
|
|
851
912
|
? local.observe(request)
|
|
852
|
-
: Stream.unwrap(
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
913
|
+
: Stream.unwrap(Effect.fail(crossThreadStoreError("thread observe", request.threadId))),
|
|
914
|
+
|
|
915
|
+
...(recoveryCheckpoints === undefined
|
|
916
|
+
? {}
|
|
917
|
+
: {
|
|
918
|
+
recoveryCheckpoints: {
|
|
919
|
+
save: (request) =>
|
|
920
|
+
options.ownsThread(request.checkpoint.threadId)
|
|
921
|
+
? recoveryCheckpoints.save(request)
|
|
922
|
+
: Effect.fail(
|
|
923
|
+
crossThreadStoreError(
|
|
924
|
+
"thread save recovery checkpoint",
|
|
925
|
+
request.checkpoint.threadId,
|
|
926
|
+
),
|
|
927
|
+
),
|
|
928
|
+
load: (request) =>
|
|
929
|
+
options.ownsThread(request.threadId)
|
|
930
|
+
? recoveryCheckpoints.load(request)
|
|
931
|
+
: Effect.succeed(Option.none()),
|
|
932
|
+
},
|
|
933
|
+
}),
|
|
934
|
+
...(checkpoints === undefined
|
|
935
|
+
? {}
|
|
936
|
+
: {
|
|
937
|
+
checkpoints: {
|
|
938
|
+
save: (request) =>
|
|
939
|
+
options.ownsThread(request.checkpoint.threadId)
|
|
940
|
+
? checkpoints.save(request)
|
|
941
|
+
: Effect.fail(
|
|
942
|
+
crossThreadStoreError("thread save checkpoint", request.checkpoint.threadId),
|
|
943
|
+
),
|
|
944
|
+
load: (request) =>
|
|
945
|
+
options.ownsThread(request.threadId)
|
|
946
|
+
? checkpoints.load(request)
|
|
947
|
+
: Effect.fail(crossThreadStoreError("thread load checkpoint", request.threadId)),
|
|
948
|
+
},
|
|
949
|
+
}),
|
|
874
950
|
});
|
|
875
951
|
|
|
876
|
-
return Context.make(
|
|
952
|
+
return Context.make(ThreadStore, routed);
|
|
877
953
|
});
|
|
878
954
|
|
|
879
955
|
/**
|
|
880
956
|
* Routing decorator over the LOCAL `SubmissionLedger` facet (plan §1.3): a request addressing
|
|
881
|
-
* this Object's
|
|
882
|
-
*
|
|
957
|
+
* this Object's Thread executes locally; a route-capable request addressing another
|
|
958
|
+
* Thread is Schema-encoded onto the `ThreadPortTransport` and executed by the
|
|
883
959
|
* owning Object's local facet; any other foreign request fails fast typed. Provide the WP1
|
|
884
960
|
* local facet (`submissionLedgerLayer`/`ledgerLayer`) and a transport to close it.
|
|
885
961
|
*/
|
|
886
962
|
export const routedSubmissionLedgerLayer = (
|
|
887
963
|
options: RoutedPortOptions,
|
|
888
|
-
): Layer.Layer<SubmissionLedger, never, SubmissionLedger |
|
|
964
|
+
): Layer.Layer<SubmissionLedger, never, SubmissionLedger | ThreadPortTransport> =>
|
|
889
965
|
Layer.effectContext(makeRoutedLedgerServices(options));
|
|
890
966
|
|
|
891
967
|
/**
|
|
892
|
-
* Routing decorator over the LOCAL `
|
|
968
|
+
* Routing decorator over the LOCAL `ThreadStore` facet (plan §1.3): this-thread
|
|
893
969
|
* requests execute locally; foreign materialize/append/read/inspectTail/export travel the
|
|
894
|
-
* transport
|
|
970
|
+
* transport. Foreign recovery-cache loads return none for canonical replay; other foreign
|
|
971
|
+
* checkpoint operations and observation fail fast typed.
|
|
895
972
|
*/
|
|
896
|
-
export const
|
|
973
|
+
export const routedThreadStoreLayer = (
|
|
897
974
|
options: RoutedPortOptions,
|
|
898
|
-
): Layer.Layer<
|
|
975
|
+
): Layer.Layer<ThreadStore, never, ThreadStore | ThreadPortTransport> =>
|
|
899
976
|
Layer.effectContext(makeRoutedStoreServices(options));
|
|
900
977
|
|
|
978
|
+
/** Route the existing owner-scoped list; delivery mutations remain source local. */
|
|
979
|
+
export const routedMessageDeliveryStoreLayer = (options: RoutedPortOptions) =>
|
|
980
|
+
Layer.effect(
|
|
981
|
+
MessageDeliveryStore,
|
|
982
|
+
Effect.gen(function* () {
|
|
983
|
+
const local = yield* MessageDeliveryStore;
|
|
984
|
+
const call = makeTransportCall(yield* ThreadPortTransport);
|
|
985
|
+
|
|
986
|
+
return MessageDeliveryStore.of({
|
|
987
|
+
...local,
|
|
988
|
+
list: (request) =>
|
|
989
|
+
options.ownsThread(request.ownerThreadId)
|
|
990
|
+
? local.list(request)
|
|
991
|
+
: call(request.ownerThreadId, MessageDeliveryListCall.make({ request })).pipe(
|
|
992
|
+
Effect.mapError(() =>
|
|
993
|
+
MessageDeliveryError.make({
|
|
994
|
+
reason: "storage",
|
|
995
|
+
operation: "route delivery list",
|
|
996
|
+
}),
|
|
997
|
+
),
|
|
998
|
+
Effect.flatMap((response) => {
|
|
999
|
+
if (
|
|
1000
|
+
response._tag === "PortSucceeded" &&
|
|
1001
|
+
response.result._tag === "MessageDeliveryListResult"
|
|
1002
|
+
)
|
|
1003
|
+
return Effect.succeed(response.result.page);
|
|
1004
|
+
|
|
1005
|
+
return Effect.fail(
|
|
1006
|
+
response._tag === "PortFailed" &&
|
|
1007
|
+
response.failure._tag === "MessageDeliveryError"
|
|
1008
|
+
? response.failure
|
|
1009
|
+
: MessageDeliveryError.make({
|
|
1010
|
+
reason: "storage",
|
|
1011
|
+
operation: "route delivery list",
|
|
1012
|
+
}),
|
|
1013
|
+
);
|
|
1014
|
+
}),
|
|
1015
|
+
),
|
|
1016
|
+
});
|
|
1017
|
+
}),
|
|
1018
|
+
);
|
|
1019
|
+
|
|
901
1020
|
// ---------------------------------------------------------------------------
|
|
902
1021
|
// Owner-side execution
|
|
903
1022
|
// ---------------------------------------------------------------------------
|
|
@@ -915,16 +1034,37 @@ const capture = <Failure extends PortFailure>(
|
|
|
915
1034
|
* Execute one decoded port request against THIS Object's LOCAL facets — the owner-side half
|
|
916
1035
|
* of the routed ports (plan §1.3). Callers must provide the WP1 local facets, never the
|
|
917
1036
|
* routed decorators: the routing layer already established that this Object owns the
|
|
918
|
-
* addressed
|
|
1037
|
+
* addressed Thread, and re-routing here could bounce a request between Objects.
|
|
919
1038
|
* Failures never escape — every typed port failure becomes a `PortFailed` envelope that
|
|
920
1039
|
* re-decodes on the caller side.
|
|
921
1040
|
*/
|
|
922
1041
|
export const executePortRequest = Effect.fn("DoPortRouting.executePortRequest")(function* (
|
|
923
1042
|
request: PortRequest,
|
|
924
|
-
): Effect.fn.Return<PortResponse, never, SubmissionLedger |
|
|
1043
|
+
): Effect.fn.Return<PortResponse, never, SubmissionLedger | ThreadStore | MessageDeliveryStore> {
|
|
925
1044
|
switch (request._tag) {
|
|
1045
|
+
case "MessageDeliveryList": {
|
|
1046
|
+
const store = yield* MessageDeliveryStore;
|
|
1047
|
+
|
|
1048
|
+
return yield* capture(
|
|
1049
|
+
store
|
|
1050
|
+
.list(request.request)
|
|
1051
|
+
.pipe(Effect.map((page) => MessageDeliveryListResult.make({ page }))),
|
|
1052
|
+
);
|
|
1053
|
+
}
|
|
1054
|
+
case "StoreCountPeerMessages": {
|
|
1055
|
+
const store = yield* ThreadStore;
|
|
1056
|
+
|
|
1057
|
+
return yield* capture(
|
|
1058
|
+
store.countPeerMessages === undefined
|
|
1059
|
+
? Effect.fail(crossThreadStoreError("native read unavailable", request.request.threadId))
|
|
1060
|
+
: store
|
|
1061
|
+
.countPeerMessages(request.request)
|
|
1062
|
+
.pipe(Effect.map((count) => StoreCountPeerMessagesResult.make({ count }))),
|
|
1063
|
+
);
|
|
1064
|
+
}
|
|
926
1065
|
case "LedgerAdmit": {
|
|
927
1066
|
const ledger = yield* SubmissionLedger;
|
|
1067
|
+
|
|
928
1068
|
return yield* capture(
|
|
929
1069
|
ledger
|
|
930
1070
|
.admit(request.request)
|
|
@@ -933,12 +1073,14 @@ export const executePortRequest = Effect.fn("DoPortRouting.executePortRequest")(
|
|
|
933
1073
|
}
|
|
934
1074
|
case "LedgerMarkReady": {
|
|
935
1075
|
const ledger = yield* SubmissionLedger;
|
|
1076
|
+
|
|
936
1077
|
return yield* capture(
|
|
937
1078
|
ledger.markReady(request.request).pipe(Effect.map(() => LedgerMarkReadyResult.make({}))),
|
|
938
1079
|
);
|
|
939
1080
|
}
|
|
940
1081
|
case "LedgerLookup": {
|
|
941
1082
|
const ledger = yield* SubmissionLedger;
|
|
1083
|
+
|
|
942
1084
|
return yield* capture(
|
|
943
1085
|
ledger
|
|
944
1086
|
.lookup(request.request)
|
|
@@ -953,14 +1095,45 @@ export const executePortRequest = Effect.fn("DoPortRouting.executePortRequest")(
|
|
|
953
1095
|
}
|
|
954
1096
|
case "LedgerResolveAdmission": {
|
|
955
1097
|
const ledger = yield* SubmissionLedger;
|
|
1098
|
+
|
|
956
1099
|
return yield* capture(
|
|
957
1100
|
ledger
|
|
958
1101
|
.resolveAdmission(request.request)
|
|
959
1102
|
.pipe(Effect.map((resolution) => LedgerResolveAdmissionResult.make({ resolution }))),
|
|
960
1103
|
);
|
|
961
1104
|
}
|
|
1105
|
+
case "LedgerInspectWorker": {
|
|
1106
|
+
const ledger = yield* SubmissionLedger;
|
|
1107
|
+
|
|
1108
|
+
return yield* capture(
|
|
1109
|
+
ledger.inspectWorker === undefined
|
|
1110
|
+
? Effect.fail(
|
|
1111
|
+
LedgerError.make({
|
|
1112
|
+
operation: "inspectWorker",
|
|
1113
|
+
message: "Worker inspection unavailable",
|
|
1114
|
+
}),
|
|
1115
|
+
)
|
|
1116
|
+
: ledger
|
|
1117
|
+
.inspectWorker(request.request.threadId)
|
|
1118
|
+
.pipe(Effect.map((state) => LedgerInspectWorkerResult.make({ state }))),
|
|
1119
|
+
);
|
|
1120
|
+
}
|
|
1121
|
+
case "LedgerStopWorker": {
|
|
1122
|
+
const ledger = yield* SubmissionLedger;
|
|
1123
|
+
|
|
1124
|
+
return yield* capture(
|
|
1125
|
+
ledger.stopWorker === undefined
|
|
1126
|
+
? Effect.fail(
|
|
1127
|
+
LedgerError.make({ operation: "stopWorker", message: "Worker stop unavailable" }),
|
|
1128
|
+
)
|
|
1129
|
+
: ledger
|
|
1130
|
+
.stopWorker(request.request)
|
|
1131
|
+
.pipe(Effect.map((owned) => LedgerStopWorkerResult.make({ owned }))),
|
|
1132
|
+
);
|
|
1133
|
+
}
|
|
962
1134
|
case "LedgerRequestAbort": {
|
|
963
1135
|
const ledger = yield* SubmissionLedger;
|
|
1136
|
+
|
|
964
1137
|
return yield* capture(
|
|
965
1138
|
ledger
|
|
966
1139
|
.requestAbort(request.request)
|
|
@@ -969,6 +1142,7 @@ export const executePortRequest = Effect.fn("DoPortRouting.executePortRequest")(
|
|
|
969
1142
|
}
|
|
970
1143
|
case "LedgerRecordChildSettled": {
|
|
971
1144
|
const ledger = yield* SubmissionLedger;
|
|
1145
|
+
|
|
972
1146
|
return yield* capture(
|
|
973
1147
|
ledger
|
|
974
1148
|
.recordChildSettled(request.request)
|
|
@@ -976,13 +1150,15 @@ export const executePortRequest = Effect.fn("DoPortRouting.executePortRequest")(
|
|
|
976
1150
|
);
|
|
977
1151
|
}
|
|
978
1152
|
case "StoreMaterialize": {
|
|
979
|
-
const store = yield*
|
|
1153
|
+
const store = yield* ThreadStore;
|
|
1154
|
+
|
|
980
1155
|
return yield* capture(
|
|
981
1156
|
store.materialize(request.request).pipe(Effect.map(() => StoreMaterializeResult.make({}))),
|
|
982
1157
|
);
|
|
983
1158
|
}
|
|
984
1159
|
case "StoreAppend": {
|
|
985
|
-
const store = yield*
|
|
1160
|
+
const store = yield* ThreadStore;
|
|
1161
|
+
|
|
986
1162
|
return yield* capture(
|
|
987
1163
|
store
|
|
988
1164
|
.append(request.request)
|
|
@@ -990,7 +1166,8 @@ export const executePortRequest = Effect.fn("DoPortRouting.executePortRequest")(
|
|
|
990
1166
|
);
|
|
991
1167
|
}
|
|
992
1168
|
case "StoreReadPage": {
|
|
993
|
-
const store = yield*
|
|
1169
|
+
const store = yield* ThreadStore;
|
|
1170
|
+
|
|
994
1171
|
return yield* capture(
|
|
995
1172
|
store.read(request.request).pipe(
|
|
996
1173
|
Stream.runCollect,
|
|
@@ -999,7 +1176,8 @@ export const executePortRequest = Effect.fn("DoPortRouting.executePortRequest")(
|
|
|
999
1176
|
);
|
|
1000
1177
|
}
|
|
1001
1178
|
case "StoreInspectTail": {
|
|
1002
|
-
const store = yield*
|
|
1179
|
+
const store = yield* ThreadStore;
|
|
1180
|
+
|
|
1003
1181
|
return yield* capture(
|
|
1004
1182
|
store
|
|
1005
1183
|
.inspectTail(request.request)
|
|
@@ -1007,15 +1185,12 @@ export const executePortRequest = Effect.fn("DoPortRouting.executePortRequest")(
|
|
|
1007
1185
|
);
|
|
1008
1186
|
}
|
|
1009
1187
|
case "StoreExport": {
|
|
1010
|
-
const store = yield*
|
|
1188
|
+
const store = yield* ThreadStore;
|
|
1189
|
+
|
|
1011
1190
|
return yield* capture(
|
|
1012
1191
|
store
|
|
1013
1192
|
.export(request.request)
|
|
1014
|
-
.pipe(
|
|
1015
|
-
Effect.map((conversationExport) =>
|
|
1016
|
-
StoreExportResult.make({ export: conversationExport }),
|
|
1017
|
-
),
|
|
1018
|
-
),
|
|
1193
|
+
.pipe(Effect.map((threadExport) => StoreExportResult.make({ export: threadExport }))),
|
|
1019
1194
|
);
|
|
1020
1195
|
}
|
|
1021
1196
|
}
|
|
@@ -1041,7 +1216,7 @@ const encodedProtocolFailure = (message: string): unknown => ({
|
|
|
1041
1216
|
export const handleEncodedPortRequest = Effect.fn("DoPortRouting.handleEncodedPortRequest")(
|
|
1042
1217
|
function* (
|
|
1043
1218
|
encoded: unknown,
|
|
1044
|
-
): Effect.fn.Return<unknown, never, SubmissionLedger |
|
|
1219
|
+
): Effect.fn.Return<unknown, never, SubmissionLedger | ThreadStore | MessageDeliveryStore> {
|
|
1045
1220
|
const response = yield* decodePortRequest(encoded).pipe(
|
|
1046
1221
|
Effect.flatMap(executePortRequest),
|
|
1047
1222
|
Effect.catch((error) =>
|
|
@@ -1056,6 +1231,7 @@ export const handleEncodedPortRequest = Effect.fn("DoPortRouting.handleEncodedPo
|
|
|
1056
1231
|
),
|
|
1057
1232
|
),
|
|
1058
1233
|
);
|
|
1234
|
+
|
|
1059
1235
|
return yield* encodePortResponse(response).pipe(
|
|
1060
1236
|
Effect.catch((error) =>
|
|
1061
1237
|
Effect.succeed(
|