@effect-agent/platform-cloudflare 0.1.0-beta.9 → 0.1.0-beta.91
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/Alarm.d.mts +188 -0
- package/dist/Alarm.mjs +451 -0
- package/dist/Alarm.mjs.map +1 -0
- package/dist/BrowserRestCapture.d.mts +35 -0
- package/dist/BrowserRestCapture.mjs +238 -0
- package/dist/BrowserRestCapture.mjs.map +1 -0
- package/dist/BrowserRestCrawl.d.mts +17 -0
- package/dist/BrowserRestCrawl.mjs +244 -0
- package/dist/BrowserRestCrawl.mjs.map +1 -0
- package/dist/CloudflareAiGateway.d.mts +64 -0
- package/dist/CloudflareAiGateway.mjs +70 -0
- package/dist/CloudflareAiGateway.mjs.map +1 -0
- package/dist/CloudflareBindings.d.mts +115 -0
- package/dist/CloudflareBindings.mjs +113 -0
- package/dist/CloudflareBindings.mjs.map +1 -0
- package/dist/CloudflareBrowser-DV6kya1H.mjs +495 -0
- package/dist/CloudflareBrowser-DV6kya1H.mjs.map +1 -0
- package/dist/CloudflareBrowser-Doe1M7R5.d.mts +80 -0
- package/dist/CloudflareBrowser.d.mts +2 -0
- package/dist/CloudflareBrowser.mjs +2 -0
- package/dist/CloudflareCodeMode.d.mts +44 -0
- package/dist/CloudflareCodeMode.mjs +616 -0
- package/dist/CloudflareCodeMode.mjs.map +1 -0
- package/dist/CloudflareConfig-f3CqTel1.d.mts +101 -0
- package/dist/CloudflareConfig.d.mts +2 -0
- package/dist/CloudflareConfig.mjs +122 -0
- package/dist/CloudflareConfig.mjs.map +1 -0
- package/dist/CloudflareMemory.d.mts +142 -0
- package/dist/CloudflareMemory.mjs +202 -0
- package/dist/CloudflareMemory.mjs.map +1 -0
- package/dist/CloudflareScheduling.d.mts +52 -0
- package/dist/CloudflareScheduling.mjs +396 -0
- package/dist/CloudflareScheduling.mjs.map +1 -0
- package/dist/CloudflareSubscriptions.d.mts +87 -0
- package/dist/CloudflareSubscriptions.mjs +530 -0
- package/dist/CloudflareSubscriptions.mjs.map +1 -0
- package/dist/CloudflareThreadClient.d.mts +1496 -0
- package/dist/CloudflareThreadClient.mjs +382 -0
- package/dist/CloudflareThreadClient.mjs.map +1 -0
- package/dist/InteractiveBrowser-DUE_m12-.d.mts +161 -0
- package/dist/InteractiveBrowser-DupDtfw2.mjs +1169 -0
- package/dist/InteractiveBrowser-DupDtfw2.mjs.map +1 -0
- package/dist/InteractiveBrowser.d.mts +2 -0
- package/dist/InteractiveBrowser.mjs +2 -0
- package/dist/ProtectedBrowser.d.mts +123 -0
- package/dist/ProtectedBrowser.mjs +1135 -0
- package/dist/ProtectedBrowser.mjs.map +1 -0
- package/dist/ThreadObject-CIbmXZCY.d.mts +812 -0
- package/dist/ThreadObject-DvRG0dDz.mjs +835 -0
- package/dist/ThreadObject-DvRG0dDz.mjs.map +1 -0
- package/dist/ThreadObject.d.mts +2 -0
- package/dist/ThreadObject.mjs +3 -0
- package/dist/WakeScheduler.d.mts +23 -0
- package/dist/WakeScheduler.mjs +57 -0
- package/dist/WakeScheduler.mjs.map +1 -0
- package/dist/boundary-BguazVkh.mjs +42 -0
- package/dist/boundary-BguazVkh.mjs.map +1 -0
- package/dist/index.d.mts +13 -1548
- package/dist/index.mjs +13 -1636
- package/dist/prepared-admission-DU0_T-ev.mjs +73 -0
- package/dist/prepared-admission-DU0_T-ev.mjs.map +1 -0
- package/dist/rolldown-runtime-D7D4PA-g.mjs +13 -0
- package/package.json +1 -53
- package/src/Alarm.ts +973 -0
- package/src/BrowserRestCapture.ts +477 -0
- package/src/BrowserRestCrawl.ts +540 -0
- package/src/CloudflareAiGateway.ts +170 -0
- package/src/CloudflareBindings.ts +199 -0
- package/src/CloudflareBrowser.ts +15 -0
- package/src/{code-mode-executor.ts → CloudflareCodeMode.ts} +372 -204
- package/src/{config.ts → CloudflareConfig.ts} +9 -8
- package/src/CloudflareMemory.ts +420 -0
- package/src/CloudflareScheduling.ts +747 -0
- package/src/CloudflareSubscriptions.ts +1041 -0
- package/src/CloudflareThreadClient.ts +809 -0
- package/src/InteractiveBrowser.ts +2354 -0
- package/src/ProtectedBrowser.ts +9 -0
- package/src/ThreadObject.ts +1179 -0
- package/src/{wake-scheduler.ts → WakeScheduler.ts} +32 -26
- package/src/index.ts +12 -23
- package/src/internal/boundary.ts +55 -0
- package/src/internal/browser-quick-action.ts +857 -0
- package/src/internal/browser-session-lifecycle.ts +160 -0
- package/src/internal/layers.ts +751 -0
- package/src/internal/message-delivery.ts +152 -0
- package/src/internal/prepared-admission.ts +116 -0
- package/src/internal/progress-wait.ts +121 -0
- package/src/internal/transport.ts +44 -0
- package/src/protected-browser/binding.ts +313 -0
- package/src/protected-browser/host.ts +344 -0
- package/src/protected-browser/inspect-frame.ts +125 -0
- package/src/protected-browser/native.ts +445 -0
- package/src/protected-browser/policy.ts +821 -0
- package/dist/index.mjs.map +0 -1
- package/src/alarm.ts +0 -334
- package/src/bindings.ts +0 -145
- package/src/client.ts +0 -646
- package/src/conversation-object.ts +0 -768
- package/src/layers.ts +0 -376
- package/src/transport.ts +0 -38
|
@@ -0,0 +1,530 @@
|
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
|
|
2
|
+
import { CloudflareThreadClient } from "./CloudflareThreadClient.mjs";
|
|
3
|
+
import { t as cloudflarePreparedInputAdmissionLayer } from "./prepared-admission-DU0_T-ev.mjs";
|
|
4
|
+
import { Cause, Clock, Context, DateTime, Effect, Layer, Schema } from "effect";
|
|
5
|
+
import { Principal } from "effect-agent/submission-ledger";
|
|
6
|
+
import { PersistedJson } from "effect-agent/records";
|
|
7
|
+
import { DurableObject, DurableObjectAlarm, DurableObjectState } from "effect-cf";
|
|
8
|
+
import { BrowserCrypto } from "@effect/platform-browser";
|
|
9
|
+
import { SqliteClient } from "@effect/sql-sqlite-do";
|
|
10
|
+
import { EventAcknowledgement, EventSourceVersion, SourcePartition, SubscriptionConfiguration, SubscriptionDeliveryKey, SubscriptionDeliverySnapshot, SubscriptionError, SubscriptionFailpointError, SubscriptionKey, SubscriptionScope, SubscriptionSnapshot, SubscriptionSourceError, defaultSubscriptionLimits } from "effect-agent/subscription";
|
|
11
|
+
import { DoSubscriptionAlarmControl, DoSubscriptionTransaction, doSubscriptionStoreLayer } from "@effect-agent/storage-cloudflare/do-subscription-store";
|
|
12
|
+
import "effect-agent/event-source";
|
|
13
|
+
import "effect-agent/subscription-input";
|
|
14
|
+
import { SubscriptionDriver, SubscriptionIntake, Subscriptions } from "effect-agent/subscriptions";
|
|
15
|
+
//#region src/CloudflareSubscriptions.ts
|
|
16
|
+
var CloudflareSubscriptions_exports = /* @__PURE__ */ __exportAll({
|
|
17
|
+
CloudflareSubscriptionConfigError: () => CloudflareSubscriptionConfigError,
|
|
18
|
+
CloudflareSubscriptionsClient: () => CloudflareSubscriptionsClient,
|
|
19
|
+
SubscriptionAlarmExtensionError: () => SubscriptionAlarmExtensionError,
|
|
20
|
+
SubscriptionAlarmProtocolError: () => SubscriptionAlarmProtocolError,
|
|
21
|
+
SubscriptionPartitionAlarmExtension: () => SubscriptionPartitionAlarmExtension,
|
|
22
|
+
SubscriptionPartitionIdentity: () => SubscriptionPartitionIdentity,
|
|
23
|
+
SubscriptionPartitionNamespace: () => SubscriptionPartitionNamespace,
|
|
24
|
+
SubscriptionPartitionProtocolError: () => SubscriptionPartitionProtocolError,
|
|
25
|
+
makeSubscriptionPartitionAlarmHandler: () => makeSubscriptionPartitionAlarmHandler,
|
|
26
|
+
makeSubscriptionPartitionObjectClass: () => makeSubscriptionPartitionObjectClass,
|
|
27
|
+
sourcePartitionName: () => sourcePartitionName,
|
|
28
|
+
validateCloudflareSubscriptionLimits: () => validateCloudflareSubscriptionLimits
|
|
29
|
+
});
|
|
30
|
+
const SUBSCRIPTION_ALARM_TAG = "effect-agent/SubscriptionPartitionWake";
|
|
31
|
+
const SUBSCRIPTION_ALARM_ID = "driver";
|
|
32
|
+
const MAX_ALARM_WALL_MILLIS = 72e4;
|
|
33
|
+
const MAX_ALARMS_PER_INVOCATION = 16;
|
|
34
|
+
const MAX_ANCILLARY_ALARM_MILLIS = 3e4;
|
|
35
|
+
const SubscriptionAlarmPayload = Schema.Struct({
|
|
36
|
+
schemaVersion: Schema.Literal(1),
|
|
37
|
+
generation: Schema.Int.check(Schema.isGreaterThan(0))
|
|
38
|
+
});
|
|
39
|
+
var SubscriptionAlarmProtocolError = class extends Schema.TaggedError()("SubscriptionAlarmProtocolError", { message: Schema.String }) {};
|
|
40
|
+
/** Bounded host diagnostic; credentials and provider responses do not belong in alarm failures. */
|
|
41
|
+
var SubscriptionAlarmExtensionError = class extends Schema.TaggedError()("SubscriptionAlarmExtensionError", { code: Schema.NonEmptyString.check(Schema.isMaxLength(128)) }) {};
|
|
42
|
+
/** Host-only handlers; the framework reserves its namespace and rejects every unknown tag. */
|
|
43
|
+
const SubscriptionPartitionAlarmExtension = Context.Reference("@effect-agent/platform-cloudflare/SubscriptionPartitionAlarmExtension", { defaultValue: () => ({ handlers: [] }) });
|
|
44
|
+
/** Capture host services once, deferring native partition services to invocation.
|
|
45
|
+
* Each invocation owns its codec/handler Scope and timeout.
|
|
46
|
+
* Callback failures stay typed. Defects and interruption reach the native alarm multiplexer.
|
|
47
|
+
* The host owns durable idempotency, prearming and external-effect uncertainty.
|
|
48
|
+
*/
|
|
49
|
+
const makeSubscriptionPartitionAlarmHandler = Effect.fn("makeSubscriptionPartitionAlarmHandler")(function* (options) {
|
|
50
|
+
if (options.tag.length === 0 || options.tag.length > 128 || options.tag.startsWith("effect-agent/") || !Number.isSafeInteger(options.timeoutMillis) || options.timeoutMillis < 1 || options.timeoutMillis > MAX_ANCILLARY_ALARM_MILLIS) return yield* SubscriptionAlarmProtocolError.make({ message: "Invalid ancillary alarm tag or timeout" });
|
|
51
|
+
const services = (yield* Effect.context()).pipe(Context.omit(Subscriptions, SubscriptionIntake, SubscriptionDriver));
|
|
52
|
+
return {
|
|
53
|
+
tag: options.tag,
|
|
54
|
+
handle: (event) => Effect.gen(function* () {
|
|
55
|
+
if (event.tag !== options.tag) return yield* SubscriptionAlarmProtocolError.make({ message: "Ancillary alarm tag mismatch" });
|
|
56
|
+
const payload = yield* Schema.decodeEffect(options.payload)(event.payload).pipe(Effect.mapError(() => SubscriptionAlarmProtocolError.make({ message: "Invalid ancillary alarm payload" })));
|
|
57
|
+
yield* options.handle({
|
|
58
|
+
...event,
|
|
59
|
+
payload
|
|
60
|
+
});
|
|
61
|
+
}).pipe(Effect.scoped, Effect.timeoutOrElse({
|
|
62
|
+
duration: options.timeoutMillis,
|
|
63
|
+
orElse: () => SubscriptionAlarmExtensionError.make({ code: "timeout" })
|
|
64
|
+
}), Effect.provideContext(services))
|
|
65
|
+
};
|
|
66
|
+
});
|
|
67
|
+
var SubscriptionPartitionProtocolError = class extends Schema.TaggedError()("SubscriptionPartitionProtocolError", { message: Schema.String.check(Schema.isMaxLength(4096)) }) {};
|
|
68
|
+
var CloudflareSubscriptionConfigError = class extends Schema.TaggedError()("CloudflareSubscriptionConfigError", { message: Schema.String }) {};
|
|
69
|
+
/** Reject limits whose four bounded phases could exceed the safe Durable Object alarm budget. */
|
|
70
|
+
const validateCloudflareSubscriptionLimits = (limits, options = {}) => {
|
|
71
|
+
return 4 * Math.ceil(limits.batchSize / limits.concurrency) * limits.operationTimeoutMillis + (options.ancillaryAlarms === true ? MAX_ALARMS_PER_INVOCATION * MAX_ANCILLARY_ALARM_MILLIS : 0) <= MAX_ALARM_WALL_MILLIS ? Effect.void : Effect.fail(CloudflareSubscriptionConfigError.make({ message: "Subscription limits can exceed the bounded Durable Object alarm wall budget" }));
|
|
72
|
+
};
|
|
73
|
+
const protocolMessage = (message) => message.length <= 4096 ? message : `${message.slice(0, 4093)}...`;
|
|
74
|
+
const SubscribeRequest = Schema.TaggedStruct("Subscribe", {
|
|
75
|
+
schemaVersion: Schema.Literal(1),
|
|
76
|
+
scope: Schema.Struct({ ...SubscriptionScope.fields }),
|
|
77
|
+
options: Schema.Struct({
|
|
78
|
+
subscriptionId: SubscriptionKey.fields.subscriptionId,
|
|
79
|
+
source: EventSourceVersion,
|
|
80
|
+
parameters: PersistedJson,
|
|
81
|
+
context: PersistedJson,
|
|
82
|
+
mode: Schema.Literals(["once", "continuous"]),
|
|
83
|
+
expiresAtMillis: Schema.NullOr(Schema.Number),
|
|
84
|
+
destination: SubscriptionConfiguration.fields.destination,
|
|
85
|
+
deliveryPrincipal: SubscriptionConfiguration.fields.deliveryPrincipal,
|
|
86
|
+
admissionGroup: SubscriptionConfiguration.fields.admissionGroup,
|
|
87
|
+
admissionFence: SubscriptionConfiguration.fields.admissionFence,
|
|
88
|
+
agentId: SubscriptionConfiguration.fields.agentId,
|
|
89
|
+
definitions: SubscriptionConfiguration.fields.definitions
|
|
90
|
+
})
|
|
91
|
+
});
|
|
92
|
+
const UpdateSubscriptionRequest = Schema.TaggedStruct("UpdateSubscription", {
|
|
93
|
+
schemaVersion: Schema.Literal(1),
|
|
94
|
+
scope: SubscribeRequest.fields.scope,
|
|
95
|
+
key: SubscriptionKey,
|
|
96
|
+
expectedRevision: Schema.Int.check(Schema.isGreaterThan(0)),
|
|
97
|
+
options: SubscribeRequest.fields.options.mapFields(({ subscriptionId: _, ...fields }) => fields)
|
|
98
|
+
});
|
|
99
|
+
const SubscriptionStateRequest = Schema.Struct({
|
|
100
|
+
_tag: Schema.Literals([
|
|
101
|
+
"PauseSubscription",
|
|
102
|
+
"ResumeSubscription",
|
|
103
|
+
"RecoverSubscription"
|
|
104
|
+
]),
|
|
105
|
+
schemaVersion: Schema.Literal(1),
|
|
106
|
+
scope: SubscribeRequest.fields.scope,
|
|
107
|
+
key: SubscriptionKey,
|
|
108
|
+
expectedRevision: Schema.Int.check(Schema.isGreaterThan(0))
|
|
109
|
+
});
|
|
110
|
+
const ListSubscriptionsRequest = Schema.TaggedStruct("ListSubscriptions", {
|
|
111
|
+
schemaVersion: Schema.Literal(1),
|
|
112
|
+
scope: SubscribeRequest.fields.scope,
|
|
113
|
+
after: Schema.optionalKey(Schema.Natural),
|
|
114
|
+
limit: Schema.optionalKey(Schema.Int.check(Schema.isGreaterThan(0)))
|
|
115
|
+
});
|
|
116
|
+
const CancelSubscriptionRequest = Schema.TaggedStruct("CancelSubscription", {
|
|
117
|
+
expectedRevision: Schema.optionalKey(Schema.Int.check(Schema.isGreaterThan(0))),
|
|
118
|
+
schemaVersion: Schema.Literal(1),
|
|
119
|
+
scope: SubscribeRequest.fields.scope,
|
|
120
|
+
key: SubscriptionKey
|
|
121
|
+
});
|
|
122
|
+
const GetSubscriptionRequest = Schema.TaggedStruct("GetSubscription", {
|
|
123
|
+
schemaVersion: Schema.Literal(1),
|
|
124
|
+
scope: SubscriptionScope,
|
|
125
|
+
key: SubscriptionKey
|
|
126
|
+
});
|
|
127
|
+
const RecoverDeliveryRequest = Schema.TaggedStruct("RecoverDelivery", {
|
|
128
|
+
expectedGeneration: Schema.Natural,
|
|
129
|
+
schemaVersion: Schema.Literal(1),
|
|
130
|
+
scope: SubscribeRequest.fields.scope,
|
|
131
|
+
key: SubscriptionDeliveryKey
|
|
132
|
+
});
|
|
133
|
+
const ListDeliveriesRequest = Schema.TaggedStruct("ListDeliveries", {
|
|
134
|
+
schemaVersion: Schema.Literal(1),
|
|
135
|
+
scope: SubscribeRequest.fields.scope,
|
|
136
|
+
key: SubscriptionKey,
|
|
137
|
+
after: Schema.optionalKey(Schema.String),
|
|
138
|
+
limit: Schema.optionalKey(Schema.Int.check(Schema.isGreaterThan(0)))
|
|
139
|
+
});
|
|
140
|
+
const AcceptRequest = Schema.TaggedStruct("Accept", {
|
|
141
|
+
schemaVersion: Schema.Literal(1),
|
|
142
|
+
partition: SourcePartition,
|
|
143
|
+
principal: Principal,
|
|
144
|
+
source: EventSourceVersion,
|
|
145
|
+
payload: PersistedJson
|
|
146
|
+
});
|
|
147
|
+
const StatusRequest = Schema.TaggedStruct("Status", {
|
|
148
|
+
schemaVersion: Schema.Literal(1),
|
|
149
|
+
partition: SourcePartition,
|
|
150
|
+
principal: Principal,
|
|
151
|
+
source: EventSourceVersion,
|
|
152
|
+
eventId: Schema.String
|
|
153
|
+
});
|
|
154
|
+
const SubscriptionPartitionRequest = Schema.Union([
|
|
155
|
+
SubscribeRequest,
|
|
156
|
+
GetSubscriptionRequest,
|
|
157
|
+
UpdateSubscriptionRequest,
|
|
158
|
+
SubscriptionStateRequest,
|
|
159
|
+
ListSubscriptionsRequest,
|
|
160
|
+
CancelSubscriptionRequest,
|
|
161
|
+
ListDeliveriesRequest,
|
|
162
|
+
RecoverDeliveryRequest,
|
|
163
|
+
AcceptRequest,
|
|
164
|
+
StatusRequest
|
|
165
|
+
]);
|
|
166
|
+
const SubscriptionPage = Schema.Struct({
|
|
167
|
+
items: Schema.Array(SubscriptionSnapshot),
|
|
168
|
+
next: Schema.NullOr(Schema.Natural)
|
|
169
|
+
});
|
|
170
|
+
const DeliveryPage = Schema.Struct({
|
|
171
|
+
items: Schema.Array(SubscriptionDeliverySnapshot),
|
|
172
|
+
next: Schema.NullOr(Schema.String)
|
|
173
|
+
});
|
|
174
|
+
const IntakeStatus = Schema.Struct({
|
|
175
|
+
...EventAcknowledgement.fields,
|
|
176
|
+
routingComplete: Schema.Boolean,
|
|
177
|
+
routingFailure: Schema.NullOr(Schema.String),
|
|
178
|
+
nextAttemptAtMillis: Schema.Number
|
|
179
|
+
});
|
|
180
|
+
const SubscriptionPartitionFailure = Schema.Union([
|
|
181
|
+
SubscriptionError,
|
|
182
|
+
SubscriptionSourceError,
|
|
183
|
+
SubscriptionFailpointError,
|
|
184
|
+
SubscriptionPartitionProtocolError
|
|
185
|
+
]);
|
|
186
|
+
const SubscriptionPartitionResponse = Schema.Union([
|
|
187
|
+
Schema.TaggedStruct("Snapshot", { value: SubscriptionSnapshot }),
|
|
188
|
+
Schema.TaggedStruct("SubscriptionPage", { value: SubscriptionPage }),
|
|
189
|
+
Schema.TaggedStruct("DeliveryPage", { value: DeliveryPage }),
|
|
190
|
+
Schema.TaggedStruct("DeliverySnapshot", { value: SubscriptionDeliverySnapshot }),
|
|
191
|
+
Schema.TaggedStruct("Acknowledgement", { value: EventAcknowledgement }),
|
|
192
|
+
Schema.TaggedStruct("Status", { value: IntakeStatus }),
|
|
193
|
+
Schema.TaggedStruct("Failed", { failure: SubscriptionPartitionFailure })
|
|
194
|
+
]);
|
|
195
|
+
const decodeRequest = Schema.decodeUnknownEffect(SubscriptionPartitionRequest);
|
|
196
|
+
const encodeRequest = Schema.encodeEffect(SubscriptionPartitionRequest);
|
|
197
|
+
const decodeResponse = Schema.decodeUnknownEffect(SubscriptionPartitionResponse);
|
|
198
|
+
const encodeResponse = Schema.encodeEffect(SubscriptionPartitionResponse);
|
|
199
|
+
const protocolFailure = (message) => ({
|
|
200
|
+
_tag: "Failed",
|
|
201
|
+
failure: SubscriptionPartitionProtocolError.make({ message: protocolMessage(message) })
|
|
202
|
+
});
|
|
203
|
+
const sourcePartitionName = (partition) => JSON.stringify([partition.tenantId, partition.address]);
|
|
204
|
+
var SubscriptionPartitionNamespace = class extends Context.Service()("@effect-agent/platform-cloudflare/SubscriptionPartitionNamespace") {};
|
|
205
|
+
const storageFailure = (code) => SubscriptionError.make({
|
|
206
|
+
reason: "storage",
|
|
207
|
+
code
|
|
208
|
+
});
|
|
209
|
+
const corruptFailure = (code) => SubscriptionError.make({
|
|
210
|
+
reason: "corrupt",
|
|
211
|
+
code
|
|
212
|
+
});
|
|
213
|
+
/** Worker-side management and trusted intake, routed to one fresh partition stub per call. */
|
|
214
|
+
var CloudflareSubscriptionsClient = class {
|
|
215
|
+
static layer(partition) {
|
|
216
|
+
return Layer.effectContext(Effect.gen(function* () {
|
|
217
|
+
const { namespace } = yield* SubscriptionPartitionNamespace;
|
|
218
|
+
const call = Effect.fn("CloudflareSubscriptionsClient.call")(function* (addressedPartition, request) {
|
|
219
|
+
if (!samePartition(partition, addressedPartition)) return yield* SubscriptionError.make({
|
|
220
|
+
reason: "unauthorized",
|
|
221
|
+
code: "partition"
|
|
222
|
+
});
|
|
223
|
+
const encoded = yield* encodeRequest(request).pipe(Effect.mapError(() => corruptFailure("subscription-partition-protocol")));
|
|
224
|
+
const raw = yield* Effect.tryPromise({
|
|
225
|
+
try: () => namespace.get(namespace.idFromName(sourcePartitionName(partition))).subscription(encoded),
|
|
226
|
+
catch: () => storageFailure("call-subscription-partition")
|
|
227
|
+
});
|
|
228
|
+
return yield* decodeResponse(raw).pipe(Effect.mapError(() => corruptFailure("subscription-partition-protocol")));
|
|
229
|
+
});
|
|
230
|
+
const failed = (response) => response._tag === "Failed" ? response.failure : SubscriptionPartitionProtocolError.make({ message: "Unexpected subscription response" });
|
|
231
|
+
const asProtocolError = () => corruptFailure("subscription-partition-protocol");
|
|
232
|
+
const changeState = Effect.fn("CloudflareSubscriptions.changeState")(function* (scope, key, expectedRevision, _tag) {
|
|
233
|
+
const response = yield* call(scope.partition, {
|
|
234
|
+
_tag,
|
|
235
|
+
schemaVersion: 1,
|
|
236
|
+
scope,
|
|
237
|
+
key,
|
|
238
|
+
expectedRevision
|
|
239
|
+
});
|
|
240
|
+
if (response._tag === "Snapshot") return response.value;
|
|
241
|
+
const failure = failed(response);
|
|
242
|
+
return yield* failure._tag === "SubscriptionError" || failure._tag === "SubscriptionFailpointError" ? failure : asProtocolError();
|
|
243
|
+
});
|
|
244
|
+
const subscriptions = Subscriptions.of({
|
|
245
|
+
recoverSubscription: (scope, key, revision) => changeState(scope, key, revision, "RecoverSubscription"),
|
|
246
|
+
getSubscription: (scope, key) => Effect.gen(function* () {
|
|
247
|
+
const response = yield* call(scope.partition, {
|
|
248
|
+
_tag: "GetSubscription",
|
|
249
|
+
schemaVersion: 1,
|
|
250
|
+
scope,
|
|
251
|
+
key
|
|
252
|
+
});
|
|
253
|
+
if (response._tag === "Snapshot") return response.value;
|
|
254
|
+
const failure = failed(response);
|
|
255
|
+
return yield* failure._tag === "SubscriptionError" ? failure : asProtocolError();
|
|
256
|
+
}),
|
|
257
|
+
recoverDelivery: (scope, key, expectedGeneration) => Effect.gen(function* () {
|
|
258
|
+
const response = yield* call(scope.partition, {
|
|
259
|
+
_tag: "RecoverDelivery",
|
|
260
|
+
expectedGeneration,
|
|
261
|
+
schemaVersion: 1,
|
|
262
|
+
scope,
|
|
263
|
+
key
|
|
264
|
+
});
|
|
265
|
+
if (response._tag === "DeliverySnapshot") return response.value;
|
|
266
|
+
const failure = failed(response);
|
|
267
|
+
return yield* failure._tag === "SubscriptionError" || failure._tag === "SubscriptionFailpointError" ? failure : asProtocolError();
|
|
268
|
+
}),
|
|
269
|
+
updateSubscription: (scope, key, expectedRevision, options) => Effect.gen(function* () {
|
|
270
|
+
const response = yield* call(scope.partition, {
|
|
271
|
+
_tag: "UpdateSubscription",
|
|
272
|
+
schemaVersion: 1,
|
|
273
|
+
scope,
|
|
274
|
+
key,
|
|
275
|
+
expectedRevision,
|
|
276
|
+
options
|
|
277
|
+
});
|
|
278
|
+
if (response._tag === "Snapshot") return response.value;
|
|
279
|
+
const failure = failed(response);
|
|
280
|
+
return yield* failure._tag === "SubscriptionError" || failure._tag === "SubscriptionFailpointError" || failure._tag === "SubscriptionSourceError" ? failure : asProtocolError();
|
|
281
|
+
}),
|
|
282
|
+
pauseSubscription: (scope, key, revision) => changeState(scope, key, revision, "PauseSubscription"),
|
|
283
|
+
resumeSubscription: (scope, key, revision) => changeState(scope, key, revision, "ResumeSubscription"),
|
|
284
|
+
subscribe: (scope, options) => Effect.gen(function* () {
|
|
285
|
+
const response = yield* call(scope.partition, {
|
|
286
|
+
_tag: "Subscribe",
|
|
287
|
+
schemaVersion: 1,
|
|
288
|
+
scope,
|
|
289
|
+
options
|
|
290
|
+
});
|
|
291
|
+
if (response._tag === "Snapshot") return response.value;
|
|
292
|
+
const failure = failed(response);
|
|
293
|
+
if (failure._tag === "SubscriptionError" || failure._tag === "SubscriptionSourceError" || failure._tag === "SubscriptionFailpointError") return yield* failure;
|
|
294
|
+
return yield* asProtocolError();
|
|
295
|
+
}),
|
|
296
|
+
listSubscriptions: (scope, after, limit) => Effect.gen(function* () {
|
|
297
|
+
const response = yield* call(scope.partition, {
|
|
298
|
+
_tag: "ListSubscriptions",
|
|
299
|
+
schemaVersion: 1,
|
|
300
|
+
scope,
|
|
301
|
+
...after === void 0 ? {} : { after },
|
|
302
|
+
...limit === void 0 ? {} : { limit }
|
|
303
|
+
});
|
|
304
|
+
if (response._tag === "SubscriptionPage") return response.value;
|
|
305
|
+
const failure = failed(response);
|
|
306
|
+
return yield* failure._tag === "SubscriptionError" ? failure : asProtocolError();
|
|
307
|
+
}),
|
|
308
|
+
cancelSubscription: (scope, key, expectedRevision) => Effect.gen(function* () {
|
|
309
|
+
const response = yield* call(scope.partition, {
|
|
310
|
+
_tag: "CancelSubscription",
|
|
311
|
+
...expectedRevision === void 0 ? {} : { expectedRevision },
|
|
312
|
+
schemaVersion: 1,
|
|
313
|
+
scope,
|
|
314
|
+
key
|
|
315
|
+
});
|
|
316
|
+
if (response._tag === "Snapshot") return response.value;
|
|
317
|
+
const failure = failed(response);
|
|
318
|
+
if (failure._tag === "SubscriptionError" || failure._tag === "SubscriptionFailpointError") return yield* failure;
|
|
319
|
+
return yield* asProtocolError();
|
|
320
|
+
}),
|
|
321
|
+
listDeliveries: (scope, key, after, limit) => Effect.gen(function* () {
|
|
322
|
+
const response = yield* call(scope.partition, {
|
|
323
|
+
_tag: "ListDeliveries",
|
|
324
|
+
schemaVersion: 1,
|
|
325
|
+
scope,
|
|
326
|
+
key,
|
|
327
|
+
...after === void 0 ? {} : { after },
|
|
328
|
+
...limit === void 0 ? {} : { limit }
|
|
329
|
+
});
|
|
330
|
+
if (response._tag === "DeliveryPage") return response.value;
|
|
331
|
+
const failure = failed(response);
|
|
332
|
+
return yield* failure._tag === "SubscriptionError" ? failure : asProtocolError();
|
|
333
|
+
})
|
|
334
|
+
});
|
|
335
|
+
const intake = SubscriptionIntake.of({
|
|
336
|
+
accept: (principal, source, payload) => Effect.gen(function* () {
|
|
337
|
+
const request = yield* Schema.decodeUnknownEffect(AcceptRequest)({
|
|
338
|
+
_tag: "Accept",
|
|
339
|
+
schemaVersion: 1,
|
|
340
|
+
partition,
|
|
341
|
+
principal,
|
|
342
|
+
source,
|
|
343
|
+
payload
|
|
344
|
+
}).pipe(Effect.mapError(() => SubscriptionError.make({
|
|
345
|
+
reason: "validation",
|
|
346
|
+
code: "event-payload"
|
|
347
|
+
})));
|
|
348
|
+
const response = yield* call(request.partition, request);
|
|
349
|
+
if (response._tag === "Acknowledgement") return response.value;
|
|
350
|
+
const failure = failed(response);
|
|
351
|
+
if (failure._tag === "SubscriptionError" || failure._tag === "SubscriptionSourceError" || failure._tag === "SubscriptionFailpointError") return yield* failure;
|
|
352
|
+
return yield* asProtocolError();
|
|
353
|
+
}),
|
|
354
|
+
status: (principal, source, eventId) => Effect.gen(function* () {
|
|
355
|
+
const response = yield* call(partition, {
|
|
356
|
+
_tag: "Status",
|
|
357
|
+
schemaVersion: 1,
|
|
358
|
+
partition,
|
|
359
|
+
principal,
|
|
360
|
+
source,
|
|
361
|
+
eventId
|
|
362
|
+
});
|
|
363
|
+
if (response._tag === "Status") return response.value;
|
|
364
|
+
const failure = failed(response);
|
|
365
|
+
return yield* failure._tag === "SubscriptionError" ? failure : asProtocolError();
|
|
366
|
+
})
|
|
367
|
+
});
|
|
368
|
+
return Context.make(Subscriptions, subscriptions).pipe(Context.add(SubscriptionIntake, intake));
|
|
369
|
+
}));
|
|
370
|
+
}
|
|
371
|
+
};
|
|
372
|
+
var SubscriptionPartitionIdentity = class extends Context.Service()("@effect-agent/platform-cloudflare/SubscriptionPartitionIdentity") {};
|
|
373
|
+
const decodePartitionName = Effect.fn("decodeSubscriptionPartitionName")(function* (name) {
|
|
374
|
+
if (name === null || name === void 0) return yield* SubscriptionPartitionProtocolError.make({ message: "Subscription Partition objects require an idFromName identity" });
|
|
375
|
+
const tuple = yield* Schema.decodeEffect(Schema.fromJsonString(Schema.Tuple([Schema.String, Schema.String])))(name).pipe(Effect.mapError(() => SubscriptionPartitionProtocolError.make({ message: "Subscription Partition name is malformed" })));
|
|
376
|
+
return yield* Schema.decodeEffect(SourcePartition)({
|
|
377
|
+
tenantId: tuple[0],
|
|
378
|
+
address: tuple[1]
|
|
379
|
+
}).pipe(Effect.mapError(() => SubscriptionPartitionProtocolError.make({ message: "Subscription Partition identity is invalid" })));
|
|
380
|
+
});
|
|
381
|
+
const samePartition = Schema.toEquivalence(SourcePartition);
|
|
382
|
+
const requestPartition = (request) => request._tag === "Accept" || request._tag === "Status" ? request.partition : request.scope.partition;
|
|
383
|
+
const handleRequest = Effect.fn("SubscriptionPartition.handleRequest")(function* (encoded) {
|
|
384
|
+
const decoded = yield* decodeRequest(encoded).pipe(Effect.result);
|
|
385
|
+
if (decoded._tag === "Failure") return yield* encodeResponse(protocolFailure("The subscription request could not be decoded")).pipe(Effect.orDie);
|
|
386
|
+
const request = decoded.success;
|
|
387
|
+
const { partition } = yield* SubscriptionPartitionIdentity;
|
|
388
|
+
if (!samePartition(partition, requestPartition(request))) return yield* encodeResponse(protocolFailure("The request partition does not match the addressed object")).pipe(Effect.orDie);
|
|
389
|
+
const subscriptions = yield* Subscriptions;
|
|
390
|
+
const intake = yield* SubscriptionIntake;
|
|
391
|
+
const response = yield* Effect.gen(function* () {
|
|
392
|
+
switch (request._tag) {
|
|
393
|
+
case "GetSubscription": return {
|
|
394
|
+
_tag: "Snapshot",
|
|
395
|
+
value: yield* subscriptions.getSubscription(request.scope, request.key)
|
|
396
|
+
};
|
|
397
|
+
case "Subscribe": return {
|
|
398
|
+
_tag: "Snapshot",
|
|
399
|
+
value: yield* subscriptions.subscribe(request.scope, request.options)
|
|
400
|
+
};
|
|
401
|
+
case "UpdateSubscription": return {
|
|
402
|
+
_tag: "Snapshot",
|
|
403
|
+
value: yield* subscriptions.updateSubscription(request.scope, request.key, request.expectedRevision, request.options)
|
|
404
|
+
};
|
|
405
|
+
case "RecoverSubscription": return {
|
|
406
|
+
_tag: "Snapshot",
|
|
407
|
+
value: yield* subscriptions.recoverSubscription(request.scope, request.key, request.expectedRevision)
|
|
408
|
+
};
|
|
409
|
+
case "PauseSubscription": return {
|
|
410
|
+
_tag: "Snapshot",
|
|
411
|
+
value: yield* subscriptions.pauseSubscription(request.scope, request.key, request.expectedRevision)
|
|
412
|
+
};
|
|
413
|
+
case "ResumeSubscription": return {
|
|
414
|
+
_tag: "Snapshot",
|
|
415
|
+
value: yield* subscriptions.resumeSubscription(request.scope, request.key, request.expectedRevision)
|
|
416
|
+
};
|
|
417
|
+
case "ListSubscriptions": return {
|
|
418
|
+
_tag: "SubscriptionPage",
|
|
419
|
+
value: yield* subscriptions.listSubscriptions(request.scope, request.after, request.limit)
|
|
420
|
+
};
|
|
421
|
+
case "CancelSubscription": return {
|
|
422
|
+
_tag: "Snapshot",
|
|
423
|
+
value: yield* subscriptions.cancelSubscription(request.scope, request.key, request.expectedRevision)
|
|
424
|
+
};
|
|
425
|
+
case "RecoverDelivery": return {
|
|
426
|
+
_tag: "DeliverySnapshot",
|
|
427
|
+
value: yield* subscriptions.recoverDelivery(request.scope, request.key, request.expectedGeneration)
|
|
428
|
+
};
|
|
429
|
+
case "ListDeliveries": return {
|
|
430
|
+
_tag: "DeliveryPage",
|
|
431
|
+
value: yield* subscriptions.listDeliveries(request.scope, request.key, request.after, request.limit)
|
|
432
|
+
};
|
|
433
|
+
case "Accept": return {
|
|
434
|
+
_tag: "Acknowledgement",
|
|
435
|
+
value: yield* intake.accept(request.principal, request.source, request.payload)
|
|
436
|
+
};
|
|
437
|
+
case "Status": return {
|
|
438
|
+
_tag: "Status",
|
|
439
|
+
value: yield* intake.status(request.principal, request.source, request.eventId)
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
}).pipe(Effect.catch((failure) => Schema.is(SubscriptionPartitionFailure)(failure) ? Effect.succeed({
|
|
443
|
+
_tag: "Failed",
|
|
444
|
+
failure
|
|
445
|
+
}) : Effect.succeed(protocolFailure("The subscription operation failed outside its contract"))));
|
|
446
|
+
return yield* encodeResponse(response).pipe(Effect.orDie);
|
|
447
|
+
});
|
|
448
|
+
const alarmStorageError = (code) => () => storageFailure(code);
|
|
449
|
+
const transactionLayer = Layer.effect(DoSubscriptionTransaction, Effect.gen(function* () {
|
|
450
|
+
const alarms = yield* DurableObjectAlarm.DurableObjectAlarm;
|
|
451
|
+
return DoSubscriptionTransaction.of({ run: (body) => Effect.gen(function* () {
|
|
452
|
+
const nowMillis = yield* Clock.currentTimeMillis;
|
|
453
|
+
return yield* alarms.transaction((transaction) => body((replacement) => replacement.deadlineAtMillis === null ? transaction.cancelAlarm({
|
|
454
|
+
id: SUBSCRIPTION_ALARM_ID,
|
|
455
|
+
tag: SUBSCRIPTION_ALARM_TAG
|
|
456
|
+
}).pipe(Effect.mapError(alarmStorageError("cancel-subscription-alarm"))) : Effect.fromOption(DateTime.make(Math.max(replacement.deadlineAtMillis, nowMillis + 1))).pipe(Effect.mapError(() => corruptFailure("subscription-alarm-deadline")), Effect.flatMap((runAt) => transaction.scheduleAlarm({
|
|
457
|
+
id: SUBSCRIPTION_ALARM_ID,
|
|
458
|
+
tag: SUBSCRIPTION_ALARM_TAG,
|
|
459
|
+
runAt,
|
|
460
|
+
payload: {
|
|
461
|
+
schemaVersion: 1,
|
|
462
|
+
generation: replacement.generation
|
|
463
|
+
}
|
|
464
|
+
}).pipe(Effect.mapError(alarmStorageError("schedule-subscription-alarm"))))))).pipe(Effect.catchTag("StorageOperationError", () => storageFailure("commit-subscription-transaction")));
|
|
465
|
+
}) });
|
|
466
|
+
}));
|
|
467
|
+
const alarmHandler = (limits) => DurableObjectAlarm.processDue((event) => Effect.gen(function* () {
|
|
468
|
+
if (event.tag !== SUBSCRIPTION_ALARM_TAG) {
|
|
469
|
+
const { handlers } = yield* SubscriptionPartitionAlarmExtension;
|
|
470
|
+
const matches = handlers.filter((handler) => handler.tag === event.tag);
|
|
471
|
+
const handler = matches[0];
|
|
472
|
+
if (event.tag.startsWith("effect-agent/") || matches.length !== 1 || handler === void 0) return yield* SubscriptionAlarmProtocolError.make({ message: "Unknown or ambiguous ancillary alarm tag" });
|
|
473
|
+
return yield* handler.handle(event).pipe(Effect.scoped, Effect.timeoutOrElse({
|
|
474
|
+
duration: MAX_ANCILLARY_ALARM_MILLIS,
|
|
475
|
+
orElse: () => SubscriptionAlarmExtensionError.make({ code: "timeout" })
|
|
476
|
+
}));
|
|
477
|
+
}
|
|
478
|
+
if (event.id !== SUBSCRIPTION_ALARM_ID) return yield* SubscriptionAlarmProtocolError.make({ message: `Unsupported Subscription Partition alarm ${event.tag}/${event.id}` });
|
|
479
|
+
yield* Schema.decodeUnknownEffect(SubscriptionAlarmPayload)(event.payload).pipe(Effect.mapError(() => SubscriptionAlarmProtocolError.make({ message: "Unsupported subscription alarm payload" })));
|
|
480
|
+
const driver = yield* SubscriptionDriver;
|
|
481
|
+
const alarmControl = yield* DoSubscriptionAlarmControl;
|
|
482
|
+
yield* alarmControl.prearm((yield* Clock.currentTimeMillis) + limits.retryMillis);
|
|
483
|
+
if ((yield* driver.runDue.pipe(Effect.catchCauseIf((cause) => !Cause.hasInterrupts(cause), (cause) => Clock.currentTimeMillis.pipe(Effect.flatMap((time) => alarmControl.prearm(time + limits.retryMillis)), Effect.andThen(Effect.failCause(cause)))))).failed > 0) yield* alarmControl.prearm((yield* Clock.currentTimeMillis) + limits.retryMillis);
|
|
484
|
+
else yield* alarmControl.reconcile;
|
|
485
|
+
}), {
|
|
486
|
+
mode: "isolated",
|
|
487
|
+
limit: MAX_ALARMS_PER_INVOCATION,
|
|
488
|
+
retryFailedAfter: limits.retryMillis,
|
|
489
|
+
onFailure: () => Effect.logWarning("Subscription partition alarm retained for retry")
|
|
490
|
+
}).pipe(Effect.asVoid);
|
|
491
|
+
/**
|
|
492
|
+
* Build one source-addressed SQLite Durable Object. The host Layer binds the permitted source
|
|
493
|
+
* catalog and authority. It must not retain cleanup-scoped resources across RPC events.
|
|
494
|
+
*/
|
|
495
|
+
const makeSubscriptionPartitionObjectClass = (host, limits = defaultSubscriptionLimits) => {
|
|
496
|
+
const cloudflareLimitsLayer = Layer.effectDiscard(validateCloudflareSubscriptionLimits(limits));
|
|
497
|
+
const identityLayer = Layer.effect(SubscriptionPartitionIdentity, Effect.gen(function* () {
|
|
498
|
+
const state = yield* DurableObjectState.DurableObjectState;
|
|
499
|
+
return SubscriptionPartitionIdentity.of({ partition: yield* decodePartitionName(state.raw.id.name) });
|
|
500
|
+
}));
|
|
501
|
+
const sqlLayer = Layer.unwrap(Effect.map(DurableObjectState.DurableObjectState, (state) => SqliteClient.layer({ storage: state.raw.storage })));
|
|
502
|
+
const partitionStore = Layer.unwrap(Effect.map(SubscriptionPartitionIdentity, ({ partition }) => doSubscriptionStoreLayer(partition).pipe(Layer.provide(transactionLayer), Layer.provide(sqlLayer))));
|
|
503
|
+
const application = Layer.mergeAll(Subscriptions.layer(limits), SubscriptionIntake.layer(limits), SubscriptionDriver.layer(limits), cloudflareLimitsLayer, Layer.effect(SubscriptionPartitionAlarmExtension, Effect.gen(function* () {
|
|
504
|
+
const extension = yield* SubscriptionPartitionAlarmExtension;
|
|
505
|
+
const tags = extension.handlers.map((handler) => handler.tag);
|
|
506
|
+
if (tags.length > MAX_ALARMS_PER_INVOCATION || new Set(tags).size !== tags.length || tags.some((tag) => tag.length === 0 || tag.length > 128 || tag.startsWith("effect-agent/"))) return yield* SubscriptionAlarmProtocolError.make({ message: "Invalid ancillary alarm handler registry" });
|
|
507
|
+
yield* validateCloudflareSubscriptionLimits(limits, { ancillaryAlarms: tags.length > 0 });
|
|
508
|
+
return extension;
|
|
509
|
+
}))).pipe(Layer.provideMerge(partitionStore), Layer.provide(cloudflarePreparedInputAdmissionLayer.pipe(Layer.provide(CloudflareThreadClient.layer))), Layer.provide(BrowserCrypto.layer), Layer.provideMerge(DurableObjectAlarm.DurableObjectAlarm.layer), Layer.provide(host), Layer.provideMerge(identityLayer));
|
|
510
|
+
const runtime = Layer.effectContext(Effect.gen(function* () {
|
|
511
|
+
const state = yield* DurableObjectState.DurableObjectState;
|
|
512
|
+
const scope = yield* Effect.scope;
|
|
513
|
+
return yield* state.blockConcurrencyWhile(Layer.buildWithScope(application, scope));
|
|
514
|
+
}));
|
|
515
|
+
const Base = DurableObject.make(runtime, {
|
|
516
|
+
initialize: Effect.void,
|
|
517
|
+
rpc: { subscription: (encoded) => handleRequest(encoded) },
|
|
518
|
+
alarms: alarmHandler(limits)
|
|
519
|
+
});
|
|
520
|
+
class SubscriptionPartitionObject extends Base {
|
|
521
|
+
alarm(alarmInfo) {
|
|
522
|
+
return super.alarm?.(alarmInfo);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
return SubscriptionPartitionObject;
|
|
526
|
+
};
|
|
527
|
+
//#endregion
|
|
528
|
+
export { CloudflareSubscriptionConfigError, CloudflareSubscriptionsClient, SubscriptionAlarmExtensionError, SubscriptionAlarmProtocolError, SubscriptionPartitionAlarmExtension, SubscriptionPartitionIdentity, SubscriptionPartitionNamespace, SubscriptionPartitionProtocolError, makeSubscriptionPartitionAlarmHandler, makeSubscriptionPartitionObjectClass, sourcePartitionName, CloudflareSubscriptions_exports as t, validateCloudflareSubscriptionLimits };
|
|
529
|
+
|
|
530
|
+
//# sourceMappingURL=CloudflareSubscriptions.mjs.map
|