effect-cf 0.49.0 → 0.51.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +26 -0
- package/README.md +6 -2
- package/dist/ContainerFiles.d.mts +66 -0
- package/dist/ContainerFiles.mjs +66 -0
- package/dist/ContainerNamespace.d.mts +3 -1
- package/dist/DurableObjectAlarm.d.mts +68 -15
- package/dist/DurableObjectAlarm.mjs +261 -110
- package/dist/DurableObjectContainer.d.mts +133 -0
- package/dist/DurableObjectContainer.mjs +179 -0
- package/dist/DurableObjectRpcWebSocket.d.mts +2 -2
- package/dist/DurableObjectRpcWebSocket.mjs +2 -6
- package/dist/DurableObjectStorage.d.mts +2 -9
- package/dist/DurableObjectStorage.mjs +1 -14
- package/dist/DurableObjectWebSocket.d.mts +10 -2
- package/dist/DurableObjectWebSocket.mjs +18 -2
- package/dist/RpcTracing.mjs +1 -1
- package/dist/index.d.mts +3 -1
- package/dist/index.mjs +3 -1
- package/package.json +4 -3
- package/src/ContainerFiles.ts +162 -0
- package/src/ContainerNamespace.ts +3 -1
- package/src/DurableObjectAlarm.ts +536 -202
- package/src/DurableObjectContainer.ts +388 -0
- package/src/DurableObjectRpcWebSocket.ts +4 -14
- package/src/DurableObjectStorage.ts +2 -59
- package/src/DurableObjectWebSocket.ts +24 -0
- package/src/Sandbox.ts +4 -0
- package/src/index.ts +2 -0
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { __exportAll } from "./_virtual/_rolldown/runtime.mjs";
|
|
2
|
+
import { causeMessage } from "./internal/ErrorMessage.mjs";
|
|
3
|
+
import { DurableObjectState } from "./DurableObjectState.mjs";
|
|
4
|
+
import * as Context from "effect/Context";
|
|
5
|
+
import * as Data from "effect/Data";
|
|
6
|
+
import * as Effect from "effect/Effect";
|
|
7
|
+
import * as Layer from "effect/Layer";
|
|
8
|
+
import * as Option from "effect/Option";
|
|
9
|
+
import * as Stream from "effect/Stream";
|
|
10
|
+
import * as Duration from "effect/Duration";
|
|
11
|
+
//#region src/DurableObjectContainer.ts
|
|
12
|
+
var DurableObjectContainer_exports = /* @__PURE__ */ __exportAll({
|
|
13
|
+
ContainerError: () => ContainerError,
|
|
14
|
+
ContainerNotConfiguredError: () => ContainerNotConfiguredError,
|
|
15
|
+
ContainerReadinessTimeoutError: () => ContainerReadinessTimeoutError,
|
|
16
|
+
DurableObjectContainer: () => DurableObjectContainer,
|
|
17
|
+
fromContainer: () => fromContainer,
|
|
18
|
+
layer: () => layer
|
|
19
|
+
});
|
|
20
|
+
var ContainerNotConfiguredError = class extends Data.TaggedError("ContainerNotConfiguredError") {
|
|
21
|
+
get message() {
|
|
22
|
+
return `No container is configured for Durable Object "${this.durableObjectId}"`;
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
var ContainerError = class extends Data.TaggedError("ContainerError") {
|
|
26
|
+
get message() {
|
|
27
|
+
return `Durable Object container ${this.operation} failed: ${causeMessage(this.cause)}`;
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
var ContainerReadinessTimeoutError = class extends Data.TaggedError("ContainerReadinessTimeoutError") {
|
|
31
|
+
get message() {
|
|
32
|
+
return `Container port ${this.port} was not ready within ${Duration.toMillis(this.timeout)}ms`;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
var DurableObjectContainer = class extends Context.Service()("effect-cf/DurableObjectContainer") {};
|
|
36
|
+
const attempt = (operation, evaluate) => Effect.try({
|
|
37
|
+
try: evaluate,
|
|
38
|
+
catch: (cause) => new ContainerError({
|
|
39
|
+
operation,
|
|
40
|
+
cause
|
|
41
|
+
})
|
|
42
|
+
});
|
|
43
|
+
const attemptPromise = (operation, evaluate) => Effect.tryPromise({
|
|
44
|
+
try: evaluate,
|
|
45
|
+
catch: (cause) => new ContainerError({
|
|
46
|
+
operation,
|
|
47
|
+
cause
|
|
48
|
+
})
|
|
49
|
+
});
|
|
50
|
+
const processStream = (stream, operation) => stream === null || stream === void 0 ? Stream.empty : Stream.fromReadableStream({
|
|
51
|
+
evaluate: () => stream,
|
|
52
|
+
onError: (cause) => new ContainerError({
|
|
53
|
+
operation,
|
|
54
|
+
cause
|
|
55
|
+
})
|
|
56
|
+
});
|
|
57
|
+
const fromProcess = (process) => {
|
|
58
|
+
let exited = false;
|
|
59
|
+
const onExit = () => {
|
|
60
|
+
exited = true;
|
|
61
|
+
};
|
|
62
|
+
process.exitCode.then(onExit, onExit);
|
|
63
|
+
const stdout = processStream(process.stdout, "exec.stdout");
|
|
64
|
+
const stderr = processStream(process.stderr, "exec.stderr");
|
|
65
|
+
const output = attemptPromise("exec.output", () => process.output());
|
|
66
|
+
return {
|
|
67
|
+
raw: process,
|
|
68
|
+
pid: process.pid,
|
|
69
|
+
isPty: process.isPty,
|
|
70
|
+
stdin: process.stdin ?? null,
|
|
71
|
+
stdout,
|
|
72
|
+
stderr,
|
|
73
|
+
logs: Stream.merge(Stream.map(stdout, (data) => ({
|
|
74
|
+
stream: "stdout",
|
|
75
|
+
data
|
|
76
|
+
})), Stream.map(stderr, (data) => ({
|
|
77
|
+
stream: "stderr",
|
|
78
|
+
data
|
|
79
|
+
}))),
|
|
80
|
+
exitCode: attemptPromise("exec.exitCode", () => process.exitCode),
|
|
81
|
+
output,
|
|
82
|
+
outputText: Effect.map(output, (result) => ({
|
|
83
|
+
stdout: new TextDecoder().decode(result.stdout),
|
|
84
|
+
stderr: new TextDecoder().decode(result.stderr),
|
|
85
|
+
exitCode: result.exitCode
|
|
86
|
+
})),
|
|
87
|
+
kill: (signal) => attemptPromise("exec.kill", async () => {
|
|
88
|
+
await Promise.resolve();
|
|
89
|
+
if (!exited) process.kill(signal);
|
|
90
|
+
}),
|
|
91
|
+
resize: (cols, rows) => attempt("exec.resize", () => process.resize(cols, rows))
|
|
92
|
+
};
|
|
93
|
+
};
|
|
94
|
+
const fromTcpPort = (port) => {
|
|
95
|
+
const fetch = (input, init) => attemptPromise("getTcpPort.fetch", (signal) => {
|
|
96
|
+
const callerSignal = init?.signal !== void 0 ? init.signal : input instanceof Request ? input.signal : void 0;
|
|
97
|
+
return port.fetch(input, {
|
|
98
|
+
...init,
|
|
99
|
+
signal: callerSignal === null || callerSignal === void 0 ? signal : AbortSignal.any([callerSignal, signal])
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
return {
|
|
103
|
+
raw: port,
|
|
104
|
+
fetch,
|
|
105
|
+
proxy: (request) => attempt("getTcpPort.proxy", () => {
|
|
106
|
+
const url = new URL(request.url);
|
|
107
|
+
url.protocol = "http:";
|
|
108
|
+
return new Request(url, request);
|
|
109
|
+
}).pipe(Effect.flatMap((forwarded) => fetch(forwarded))),
|
|
110
|
+
connect: (...args) => attempt("getTcpPort.connect", () => port.connect(...args))
|
|
111
|
+
};
|
|
112
|
+
};
|
|
113
|
+
/** Wraps `ctx.container` without starting it or taking ownership of its lifecycle. */
|
|
114
|
+
const fromContainer = (container) => {
|
|
115
|
+
const exec = (command, options) => attemptPromise("exec", (signal) => container.exec([...command], {
|
|
116
|
+
...options,
|
|
117
|
+
signal
|
|
118
|
+
})).pipe(Effect.map(fromProcess));
|
|
119
|
+
const waitForHttp = Effect.fnUntraced(function* (portNumber, options = {}) {
|
|
120
|
+
const timeout = yield* attempt("waitForHttp", () => Duration.fromInputUnsafe(options.timeout ?? "30 seconds"));
|
|
121
|
+
let lastFailure;
|
|
122
|
+
const probe = attemptPromise("waitForHttp", async (signal) => {
|
|
123
|
+
const response = await container.getTcpPort(portNumber).fetch(new URL(options.path ?? "/", "http://container"), { signal });
|
|
124
|
+
await response.body?.cancel();
|
|
125
|
+
if (options.status !== void 0 && (response.status < options.status.min || response.status > options.status.max)) throw new Error(`HTTP readiness probe returned status ${response.status}`);
|
|
126
|
+
});
|
|
127
|
+
return yield* Effect.gen(function* () {
|
|
128
|
+
while (true) {
|
|
129
|
+
if (!(yield* attempt("running", () => container.running))) return yield* new ContainerError({
|
|
130
|
+
operation: "waitForHttp",
|
|
131
|
+
cause: /* @__PURE__ */ new Error("Container stopped before the port became ready")
|
|
132
|
+
});
|
|
133
|
+
if (yield* probe.pipe(Effect.timeout(options.attemptTimeout ?? "1 second"), Effect.match({
|
|
134
|
+
onSuccess: () => true,
|
|
135
|
+
onFailure: (error) => {
|
|
136
|
+
lastFailure = error;
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
}))) return;
|
|
140
|
+
yield* Effect.sleep(options.interval ?? "100 millis");
|
|
141
|
+
}
|
|
142
|
+
}).pipe(Effect.timeoutOrElse({
|
|
143
|
+
duration: timeout,
|
|
144
|
+
orElse: () => Effect.fail(new ContainerReadinessTimeoutError({
|
|
145
|
+
port: portNumber,
|
|
146
|
+
timeout,
|
|
147
|
+
cause: lastFailure
|
|
148
|
+
}))
|
|
149
|
+
}));
|
|
150
|
+
});
|
|
151
|
+
return {
|
|
152
|
+
raw: container,
|
|
153
|
+
running: attempt("running", () => container.running),
|
|
154
|
+
images: attempt("images", () => container.images),
|
|
155
|
+
inspect: attemptPromise("inspect", () => container.inspect()).pipe(Effect.map(Option.fromNullishOr)),
|
|
156
|
+
start: (options) => attempt("start", () => container.start(options)),
|
|
157
|
+
monitor: attemptPromise("monitor", () => container.monitor()),
|
|
158
|
+
destroy: (reason) => attemptPromise("destroy", () => container.destroy(reason)),
|
|
159
|
+
signal: (signal) => attempt("signal", () => container.signal(signal)),
|
|
160
|
+
setInactivityTimeout: (duration) => attemptPromise("setInactivityTimeout", () => container.setInactivityTimeout(Duration.toMillis(duration))),
|
|
161
|
+
getTcpPort: (port) => attempt("getTcpPort", () => fromTcpPort(container.getTcpPort(port))),
|
|
162
|
+
waitForHttp,
|
|
163
|
+
exec,
|
|
164
|
+
execScoped: (command, options) => Effect.acquireRelease(exec(command, options), (process) => process.kill(9).pipe(Effect.orDie), { interruptible: true }),
|
|
165
|
+
snapshotContainer: (options = {}) => attemptPromise("snapshotContainer", () => container.snapshotContainer(options)),
|
|
166
|
+
interceptOutboundHttp: (address, binding) => attemptPromise("interceptOutboundHttp", () => container.interceptOutboundHttp(address, binding)),
|
|
167
|
+
interceptAllOutboundHttp: (binding) => attemptPromise("interceptAllOutboundHttp", () => container.interceptAllOutboundHttp(binding)),
|
|
168
|
+
interceptOutboundHttps: (address, binding) => attemptPromise("interceptOutboundHttps", () => container.interceptOutboundHttps(address, binding))
|
|
169
|
+
};
|
|
170
|
+
};
|
|
171
|
+
/** Supplies the container attached to the current Durable Object, without owning it. */
|
|
172
|
+
const layer = Layer.effect(DurableObjectContainer, Effect.gen(function* () {
|
|
173
|
+
const state = yield* DurableObjectState;
|
|
174
|
+
const container = state.raw.container;
|
|
175
|
+
if (container === void 0) return yield* new ContainerNotConfiguredError({ durableObjectId: state.id.toString() });
|
|
176
|
+
return fromContainer(container);
|
|
177
|
+
}));
|
|
178
|
+
//#endregion
|
|
179
|
+
export { ContainerError, ContainerNotConfiguredError, ContainerReadinessTimeoutError, DurableObjectContainer, DurableObjectContainer_exports, fromContainer, layer };
|
|
@@ -64,8 +64,8 @@ interface LayerOptions {
|
|
|
64
64
|
readonly attachmentKey?: string | undefined;
|
|
65
65
|
/**
|
|
66
66
|
* Heartbeat behavior. `"auto-response"` (the default) installs Cloudflare's
|
|
67
|
-
* hibernation-safe text Ping/Pong response
|
|
68
|
-
*
|
|
67
|
+
* hibernation-safe text Ping/Pong response for the lifetime of RPC sockets,
|
|
68
|
+
* including pending operations. Keepalives never dispatch application code.
|
|
69
69
|
* `"passthrough"` leaves the Durable Object's existing auto-response
|
|
70
70
|
* configuration unchanged and makes the application responsible for waking a
|
|
71
71
|
* lost pending operation.
|
|
@@ -70,7 +70,6 @@ const layer = (options = {}) => Layer.effectContext(Effect.gen(function* () {
|
|
|
70
70
|
const declarationsByTag = /* @__PURE__ */ new Map();
|
|
71
71
|
const replayRequests = [];
|
|
72
72
|
let nextClientId = 0;
|
|
73
|
-
let activeNonResumableRequestCount = 0;
|
|
74
73
|
let writeRequest;
|
|
75
74
|
for (const declaration of declarations) {
|
|
76
75
|
if (declaration.id.length === 0 || declaration.rpcTag.length === 0) return yield* Effect.die(/* @__PURE__ */ new Error("Resumable RPC stream declaration ids and tags must be non-empty"));
|
|
@@ -137,7 +136,7 @@ const layer = (options = {}) => Layer.effectContext(Effect.gen(function* () {
|
|
|
137
136
|
return connection;
|
|
138
137
|
};
|
|
139
138
|
const heartbeat = yield* makeHeartbeatController(durableObjectState, serialization, heartbeatPolicy);
|
|
140
|
-
const syncHeartbeat = () => heartbeat.setEnabled(connectionsBySocket.size > 0
|
|
139
|
+
const syncHeartbeat = () => heartbeat.setEnabled(connectionsBySocket.size > 0);
|
|
141
140
|
const persistConnection = Effect.fn("DurableObjectRpcWebSocket.persistConnection")(function* (connection) {
|
|
142
141
|
return yield* Effect.try({
|
|
143
142
|
try: () => {
|
|
@@ -171,7 +170,6 @@ const layer = (options = {}) => Layer.effectContext(Effect.gen(function* () {
|
|
|
171
170
|
if (connection === void 0) return;
|
|
172
171
|
connectionsBySocket.delete(socket.raw);
|
|
173
172
|
connectionsById.delete(connection.id);
|
|
174
|
-
activeNonResumableRequestCount -= connection.nonResumableRequestIds.size;
|
|
175
173
|
connection.requestIds.clear();
|
|
176
174
|
connection.nonResumableRequestIds.clear();
|
|
177
175
|
connection.subscriptionsByRequestId.clear();
|
|
@@ -210,7 +208,6 @@ const layer = (options = {}) => Layer.effectContext(Effect.gen(function* () {
|
|
|
210
208
|
} else {
|
|
211
209
|
connection.requestIds.add(request.id);
|
|
212
210
|
connection.nonResumableRequestIds.add(request.id);
|
|
213
|
-
activeNonResumableRequestCount++;
|
|
214
211
|
}
|
|
215
212
|
if (!(yield* persistOrReset(connection))) return false;
|
|
216
213
|
yield* syncHeartbeat();
|
|
@@ -218,7 +215,7 @@ const layer = (options = {}) => Layer.effectContext(Effect.gen(function* () {
|
|
|
218
215
|
});
|
|
219
216
|
const completeRequest = Effect.fn("DurableObjectRpcWebSocket.completeRequest")(function* (connection, requestId, preserveAckFilter = false) {
|
|
220
217
|
if (connection.requestIds.delete(requestId)) {
|
|
221
|
-
|
|
218
|
+
connection.nonResumableRequestIds.delete(requestId);
|
|
222
219
|
const subscription = removeSubscription(connection, requestId);
|
|
223
220
|
connection.currentPendingRequestIds.delete(requestId);
|
|
224
221
|
if (preserveAckFilter && subscription !== void 0) connection.suppressedAckRequestIds.add(requestId);
|
|
@@ -229,7 +226,6 @@ const layer = (options = {}) => Layer.effectContext(Effect.gen(function* () {
|
|
|
229
226
|
});
|
|
230
227
|
const completeAllRequests = Effect.fn("DurableObjectRpcWebSocket.completeAllRequests")(function* (connection) {
|
|
231
228
|
const hadRequests = connection.requestIds.size > 0;
|
|
232
|
-
if (connection.requestIds.size > 0) activeNonResumableRequestCount -= connection.nonResumableRequestIds.size;
|
|
233
229
|
connection.requestIds.clear();
|
|
234
230
|
connection.nonResumableRequestIds.clear();
|
|
235
231
|
connection.subscriptionsByRequestId.clear();
|
|
@@ -2,7 +2,6 @@ import * as Effect from "effect/Effect";
|
|
|
2
2
|
import * as S from "effect/Schema";
|
|
3
3
|
import * as Option from "effect/Option";
|
|
4
4
|
import * as Cause from "effect/Cause";
|
|
5
|
-
import * as Duration from "effect/Duration";
|
|
6
5
|
declare namespace DurableObjectStorage_d_exports {
|
|
7
6
|
export { DurableObjectStorage, DurableObjectTransaction, SchemaBackedSyncKvStorage, SqlCursor, SqlStorage, SqlStorageValue, StorageOperationError, SyncKvDefinition, SyncKvStorage, fromDurableObjectStorage };
|
|
8
7
|
}
|
|
@@ -69,23 +68,17 @@ interface DurableObjectTransaction {
|
|
|
69
68
|
delete(keys: Array<string>, options?: globalThis.DurableObjectPutOptions): StorageEffect<number>;
|
|
70
69
|
rollback(): StorageEffect<void>;
|
|
71
70
|
getAlarm(options?: globalThis.DurableObjectGetAlarmOptions): StorageEffect<number | null>;
|
|
72
|
-
setAlarm(scheduledTime: number | Date, options?: globalThis.DurableObjectSetAlarmOptions): StorageEffect<void>;
|
|
73
|
-
setAlarmAfter(delay: Duration.Input, options?: globalThis.DurableObjectSetAlarmOptions): StorageEffect<void>;
|
|
74
|
-
deleteAlarm(options?: globalThis.DurableObjectSetAlarmOptions): StorageEffect<void>;
|
|
75
71
|
}
|
|
76
72
|
interface DurableObjectStorage {
|
|
77
73
|
get<T = unknown>(key: string, options?: globalThis.DurableObjectGetOptions): StorageEffect<T | undefined>;
|
|
78
74
|
put<T>(key: string, value: T, options?: globalThis.DurableObjectPutOptions): StorageEffect<void>;
|
|
79
75
|
delete(key: string, options?: globalThis.DurableObjectPutOptions): StorageEffect<boolean>;
|
|
80
76
|
/**
|
|
81
|
-
* Deletes all stored data
|
|
82
|
-
*
|
|
77
|
+
* Deletes all stored data, including logical schedules. Use the alarm scheduler
|
|
78
|
+
* to cancel individual alarms without deleting application data.
|
|
83
79
|
*/
|
|
84
80
|
deleteAll(options?: globalThis.DurableObjectPutOptions): StorageEffect<void>;
|
|
85
81
|
getAlarm(options?: globalThis.DurableObjectGetAlarmOptions): StorageEffect<number | null>;
|
|
86
|
-
setAlarm(scheduledTime: number | Date, options?: globalThis.DurableObjectSetAlarmOptions): StorageEffect<void>;
|
|
87
|
-
setAlarmAfter(delay: Duration.Input, options?: globalThis.DurableObjectSetAlarmOptions): StorageEffect<void>;
|
|
88
|
-
deleteAlarm(options?: globalThis.DurableObjectSetAlarmOptions): StorageEffect<void>;
|
|
89
82
|
/**
|
|
90
83
|
* SQLite-backed Durable Object storage only.
|
|
91
84
|
*
|
|
@@ -9,8 +9,6 @@ import * as Option from "effect/Option";
|
|
|
9
9
|
import * as Result from "effect/Result";
|
|
10
10
|
import * as Cause from "effect/Cause";
|
|
11
11
|
import * as Exit from "effect/Exit";
|
|
12
|
-
import * as Clock from "effect/Clock";
|
|
13
|
-
import * as Duration from "effect/Duration";
|
|
14
12
|
import * as Fiber from "effect/Fiber";
|
|
15
13
|
//#region src/DurableObjectStorage.ts
|
|
16
14
|
var DurableObjectStorage_exports = /* @__PURE__ */ __exportAll({
|
|
@@ -34,11 +32,6 @@ const tryStoragePromise = (operation, evaluate) => Effect.tryPromise({
|
|
|
34
32
|
try: evaluate,
|
|
35
33
|
catch: (cause) => storageError(operation, cause)
|
|
36
34
|
});
|
|
37
|
-
const setAlarmAfter = Effect.fn("DurableObjectStorage.setAlarmAfter")(function* (operation, setAlarm, delay, options) {
|
|
38
|
-
const now = yield* Clock.currentTimeMillis;
|
|
39
|
-
const delayMillis = Math.ceil(Duration.toMillis(delay));
|
|
40
|
-
yield* tryStoragePromise(operation, () => setAlarm(now + delayMillis, options));
|
|
41
|
-
});
|
|
42
35
|
const fromSqlCursor = (cursor) => ({
|
|
43
36
|
next: () => tryStorageSync("sql.next", () => cursor.next()),
|
|
44
37
|
toArray: () => tryStorageSync("sql.toArray", () => cursor.toArray()),
|
|
@@ -98,10 +91,7 @@ const fromDurableObjectTransaction = (txn) => ({
|
|
|
98
91
|
put: (keyOrEntries, valueOrOptions, maybeOptions) => tryStoragePromise("transaction.put", () => Predicate.isString(keyOrEntries) ? txn.put(keyOrEntries, valueOrOptions, maybeOptions) : txn.put(keyOrEntries, valueOrOptions)),
|
|
99
92
|
delete: (keyOrKeys, options) => Array.isArray(keyOrKeys) ? tryStoragePromise("transaction.delete", () => txn.delete(keyOrKeys, options)) : tryStoragePromise("transaction.delete", () => txn.delete(keyOrKeys, options)),
|
|
100
93
|
rollback: () => tryStorageSync("transaction.rollback", () => txn.rollback()),
|
|
101
|
-
getAlarm: (options) => tryStoragePromise("transaction.getAlarm", () => txn.getAlarm(options))
|
|
102
|
-
setAlarm: (scheduledTime, options) => tryStoragePromise("transaction.setAlarm", () => txn.setAlarm(scheduledTime, options)),
|
|
103
|
-
setAlarmAfter: (delay, options) => setAlarmAfter("transaction.setAlarm", (scheduledTime, options) => txn.setAlarm(scheduledTime, options), delay, options),
|
|
104
|
-
deleteAlarm: (options) => tryStoragePromise("transaction.deleteAlarm", () => txn.deleteAlarm(options))
|
|
94
|
+
getAlarm: (options) => tryStoragePromise("transaction.getAlarm", () => txn.getAlarm(options))
|
|
105
95
|
});
|
|
106
96
|
const fromDurableObjectStorage = (storage) => ({
|
|
107
97
|
get: (key, options) => tryStoragePromise("get", () => storage.get(key, options)),
|
|
@@ -109,9 +99,6 @@ const fromDurableObjectStorage = (storage) => ({
|
|
|
109
99
|
delete: (key, options) => tryStoragePromise("delete", () => storage.delete(key, options)),
|
|
110
100
|
deleteAll: (options) => tryStoragePromise("deleteAll", () => storage.deleteAll(options)),
|
|
111
101
|
getAlarm: (options) => tryStoragePromise("getAlarm", () => storage.getAlarm(options)),
|
|
112
|
-
setAlarm: (scheduledTime, options) => tryStoragePromise("setAlarm", () => storage.setAlarm(scheduledTime, options)),
|
|
113
|
-
setAlarmAfter: (delay, options) => setAlarmAfter("setAlarm", (scheduledTime, options) => storage.setAlarm(scheduledTime, options), delay, options),
|
|
114
|
-
deleteAlarm: (options) => tryStoragePromise("deleteAlarm", () => storage.deleteAlarm(options)),
|
|
115
102
|
transactionSync: (closure) => Effect.context().pipe(Effect.flatMap((context) => Effect.suspend(() => {
|
|
116
103
|
let aborted;
|
|
117
104
|
let escapedFiber;
|
|
@@ -3,8 +3,16 @@ import * as Effect from "effect/Effect";
|
|
|
3
3
|
import * as S from "effect/Schema";
|
|
4
4
|
import * as Option from "effect/Option";
|
|
5
5
|
declare namespace DurableObjectWebSocket_d_exports {
|
|
6
|
-
export { AcceptUpgradeOptions, AcceptedUpgrade, AttachmentInvalidPolicy, AttachmentRehydrateOptions, DurableWebSocket, DurableWebSocketAttachment, DurableWebSocketAttachmentError, DurableWebSocketCloseError, DurableWebSocketHandlers, DurableWebSocketSendData, DurableWebSocketSendError, RehydratedDurableWebSocket, acceptUpgrade, attachment, fromWebSocket, handlers };
|
|
6
|
+
export { AcceptUpgradeOptions, AcceptedUpgrade, AttachmentInvalidPolicy, AttachmentRehydrateOptions, DEFAULT_KEEPALIVE_REQUEST, DEFAULT_KEEPALIVE_RESPONSE, DurableWebSocket, DurableWebSocketAttachment, DurableWebSocketAttachmentError, DurableWebSocketCloseError, DurableWebSocketHandlers, DurableWebSocketSendData, DurableWebSocketSendError, RehydratedDurableWebSocket, acceptUpgrade, attachment, fromWebSocket, handlers, installKeepalive };
|
|
7
7
|
}
|
|
8
|
+
declare const DEFAULT_KEEPALIVE_REQUEST = "effect-cf:ping";
|
|
9
|
+
declare const DEFAULT_KEEPALIVE_RESPONSE = "effect-cf:pong";
|
|
10
|
+
/**
|
|
11
|
+
* Registers exact text keepalives with Cloudflare so they never invoke the object.
|
|
12
|
+
* The pair applies to all hibernating sockets on this object; install in its constructor.
|
|
13
|
+
* Clients send the request text and consume the response outside their application protocol.
|
|
14
|
+
*/
|
|
15
|
+
declare const installKeepalive: (request?: any, response?: any) => Effect.Effect<undefined, never, DurableObjectState>;
|
|
8
16
|
type DurableWebSocketSendData = string | ArrayBuffer | ArrayBufferView;
|
|
9
17
|
declare const DurableWebSocketSendError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
10
18
|
readonly _tag: "DurableWebSocketSendError";
|
|
@@ -80,4 +88,4 @@ declare const handlers: <R = never, E = unknown>(options: DurableWebSocketHandle
|
|
|
80
88
|
webSocketError: ((socket: DurableWebSocket, cause: unknown) => Effect.Effect<void, E, R>) | undefined;
|
|
81
89
|
};
|
|
82
90
|
//#endregion
|
|
83
|
-
export { AcceptUpgradeOptions, AcceptedUpgrade, AttachmentInvalidPolicy, AttachmentRehydrateOptions, DurableObjectWebSocket_d_exports, DurableWebSocket, DurableWebSocketAttachment, DurableWebSocketAttachmentError, DurableWebSocketCloseError, DurableWebSocketHandlers, DurableWebSocketSendData, DurableWebSocketSendError, RehydratedDurableWebSocket, acceptUpgrade, attachment, fromWebSocket, handlers };
|
|
91
|
+
export { AcceptUpgradeOptions, AcceptedUpgrade, AttachmentInvalidPolicy, AttachmentRehydrateOptions, DEFAULT_KEEPALIVE_REQUEST, DEFAULT_KEEPALIVE_RESPONSE, DurableObjectWebSocket_d_exports, DurableWebSocket, DurableWebSocketAttachment, DurableWebSocketAttachmentError, DurableWebSocketCloseError, DurableWebSocketHandlers, DurableWebSocketSendData, DurableWebSocketSendError, RehydratedDurableWebSocket, acceptUpgrade, attachment, fromWebSocket, handlers, installKeepalive };
|
|
@@ -8,13 +8,29 @@ import * as Option from "effect/Option";
|
|
|
8
8
|
import * as Result from "effect/Result";
|
|
9
9
|
//#region src/DurableObjectWebSocket.ts
|
|
10
10
|
var DurableObjectWebSocket_exports = /* @__PURE__ */ __exportAll({
|
|
11
|
+
DEFAULT_KEEPALIVE_REQUEST: () => DEFAULT_KEEPALIVE_REQUEST,
|
|
12
|
+
DEFAULT_KEEPALIVE_RESPONSE: () => DEFAULT_KEEPALIVE_RESPONSE,
|
|
11
13
|
DurableWebSocketAttachmentError: () => DurableWebSocketAttachmentError,
|
|
12
14
|
DurableWebSocketCloseError: () => DurableWebSocketCloseError,
|
|
13
15
|
DurableWebSocketSendError: () => DurableWebSocketSendError,
|
|
14
16
|
acceptUpgrade: () => acceptUpgrade,
|
|
15
17
|
attachment: () => attachment,
|
|
16
18
|
fromWebSocket: () => fromWebSocket,
|
|
17
|
-
handlers: () => handlers
|
|
19
|
+
handlers: () => handlers,
|
|
20
|
+
installKeepalive: () => installKeepalive
|
|
21
|
+
});
|
|
22
|
+
const DEFAULT_KEEPALIVE_REQUEST = "effect-cf:ping";
|
|
23
|
+
const DEFAULT_KEEPALIVE_RESPONSE = "effect-cf:pong";
|
|
24
|
+
/**
|
|
25
|
+
* Registers exact text keepalives with Cloudflare so they never invoke the object.
|
|
26
|
+
* The pair applies to all hibernating sockets on this object; install in its constructor.
|
|
27
|
+
* Clients send the request text and consume the response outside their application protocol.
|
|
28
|
+
*/
|
|
29
|
+
const installKeepalive = Effect.fn("DurableObjectWebSocket.installKeepalive")(function* (request = DEFAULT_KEEPALIVE_REQUEST, response = DEFAULT_KEEPALIVE_RESPONSE) {
|
|
30
|
+
const state = yield* DurableObjectState;
|
|
31
|
+
const configured = yield* state.getWebSocketAutoResponse;
|
|
32
|
+
if (configured !== null && (configured.request !== request || configured.response !== response)) return yield* Effect.die(/* @__PURE__ */ new Error("Durable Object already has a different keepalive protocol"));
|
|
33
|
+
yield* state.setWebSocketAutoResponse(new WebSocketRequestResponsePair(request, response));
|
|
18
34
|
});
|
|
19
35
|
var DurableWebSocketSendError = class extends Data.TaggedError("DurableWebSocketSendError") {
|
|
20
36
|
get message() {
|
|
@@ -117,4 +133,4 @@ const handlers = (options) => ({
|
|
|
117
133
|
webSocketError: options.error
|
|
118
134
|
});
|
|
119
135
|
//#endregion
|
|
120
|
-
export { DurableObjectWebSocket_exports, DurableWebSocketAttachmentError, DurableWebSocketCloseError, DurableWebSocketSendError, acceptUpgrade, attachment, fromWebSocket, handlers };
|
|
136
|
+
export { DEFAULT_KEEPALIVE_REQUEST, DEFAULT_KEEPALIVE_RESPONSE, DurableObjectWebSocket_exports, DurableWebSocketAttachmentError, DurableWebSocketCloseError, DurableWebSocketSendError, acceptUpgrade, attachment, fromWebSocket, handlers, installKeepalive };
|
package/dist/RpcTracing.mjs
CHANGED
|
@@ -5,9 +5,9 @@ import * as Schema from "effect/Schema";
|
|
|
5
5
|
import * as Option from "effect/Option";
|
|
6
6
|
import * as Cause from "effect/Cause";
|
|
7
7
|
import * as Exit from "effect/Exit";
|
|
8
|
-
import * as Clock from "effect/Clock";
|
|
9
8
|
import * as Tracer from "effect/Tracer";
|
|
10
9
|
import * as References from "effect/References";
|
|
10
|
+
import * as Clock from "effect/Clock";
|
|
11
11
|
//#region src/RpcTracing.ts
|
|
12
12
|
var RpcTracing_exports = /* @__PURE__ */ __exportAll({
|
|
13
13
|
RpcTraceContext: () => RpcTraceContext,
|
package/dist/index.d.mts
CHANGED
|
@@ -24,6 +24,8 @@ import { BrowserRendering_d_exports } from "./BrowserRendering.mjs";
|
|
|
24
24
|
import { Cache_d_exports } from "./Cache.mjs";
|
|
25
25
|
import { CloudflareOtlp_d_exports } from "./CloudflareOtlp.mjs";
|
|
26
26
|
import { CloudflareTracer_d_exports } from "./CloudflareTracer.mjs";
|
|
27
|
+
import { DurableObjectContainer_d_exports } from "./DurableObjectContainer.mjs";
|
|
28
|
+
import { ContainerFiles_d_exports } from "./ContainerFiles.mjs";
|
|
27
29
|
import { D1_d_exports } from "./D1.mjs";
|
|
28
30
|
import { DurableObjectAlarm_d_exports } from "./DurableObjectAlarm.mjs";
|
|
29
31
|
import { DurableObjectDefinition_d_exports } from "./DurableObjectDefinition.mjs";
|
|
@@ -40,4 +42,4 @@ import { Vectorize_d_exports } from "./Vectorize.mjs";
|
|
|
40
42
|
import { WebTransport_d_exports } from "./WebTransport.mjs";
|
|
41
43
|
import { WorkflowDefinition_d_exports } from "./WorkflowDefinition.mjs";
|
|
42
44
|
import { Workflow_d_exports } from "./Workflow.mjs";
|
|
43
|
-
export { AiGateway_d_exports as AiGateway, AnalyticsEngine_d_exports as AnalyticsEngine, Artifacts_d_exports as Artifacts, Binding_d_exports as Binding, BrowserRendering_d_exports as BrowserRendering, Cache_d_exports as Cache, CloudflareOtlp_d_exports as CloudflareOtlp, CloudflareTracer_d_exports as CloudflareTracer, ContainerNamespace_d_exports as ContainerNamespace, D1_d_exports as D1, DurableObject_d_exports as DurableObject, DurableObjectAlarm_d_exports as DurableObjectAlarm, DurableObjectDefinition_d_exports as DurableObjectDefinition, DurableObjectNamespace_d_exports as DurableObjectNamespace, DurableObjectRpcWebSocket_d_exports as DurableObjectRpcWebSocket, DurableObjectSqlite_d_exports as DurableObjectSqlite, DurableObjectState_d_exports as DurableObjectState, DurableObjectStorage_d_exports as DurableObjectStorage, DurableObjectWebSocket_d_exports as DurableObjectWebSocket, Email_d_exports as Email, Environment_d_exports as Environment, Hyperdrive_d_exports as Hyperdrive, Images_d_exports as Images, Kv_d_exports as Kv, Queue_d_exports as Queue, QueueBinding_d_exports as QueueBinding, QueueDefinition_d_exports as QueueDefinition, R2_d_exports as R2, Rpc_d_exports as Rpc, RpcDefinition_d_exports as RpcDefinition, RpcTargets_d_exports as RpcTargets, RpcTracing_d_exports as RpcTracing, RpcWebSocketClient_d_exports as RpcWebSocketClient, ServiceBinding_d_exports as ServiceBinding, Vectorize_d_exports as Vectorize, WebTransport_d_exports as WebTransport, Worker_d_exports as Worker, WorkerConfig, WorkerDefinition_d_exports as WorkerDefinition, type WorkerEnv, WorkerEnvironment, WorkersAi_d_exports as WorkersAi, Workflow_d_exports as Workflow, WorkflowBinding_d_exports as WorkflowBinding, WorkflowDefinition_d_exports as WorkflowDefinition };
|
|
45
|
+
export { AiGateway_d_exports as AiGateway, AnalyticsEngine_d_exports as AnalyticsEngine, Artifacts_d_exports as Artifacts, Binding_d_exports as Binding, BrowserRendering_d_exports as BrowserRendering, Cache_d_exports as Cache, CloudflareOtlp_d_exports as CloudflareOtlp, CloudflareTracer_d_exports as CloudflareTracer, ContainerFiles_d_exports as ContainerFiles, ContainerNamespace_d_exports as ContainerNamespace, D1_d_exports as D1, DurableObject_d_exports as DurableObject, DurableObjectAlarm_d_exports as DurableObjectAlarm, DurableObjectContainer_d_exports as DurableObjectContainer, DurableObjectDefinition_d_exports as DurableObjectDefinition, DurableObjectNamespace_d_exports as DurableObjectNamespace, DurableObjectRpcWebSocket_d_exports as DurableObjectRpcWebSocket, DurableObjectSqlite_d_exports as DurableObjectSqlite, DurableObjectState_d_exports as DurableObjectState, DurableObjectStorage_d_exports as DurableObjectStorage, DurableObjectWebSocket_d_exports as DurableObjectWebSocket, Email_d_exports as Email, Environment_d_exports as Environment, Hyperdrive_d_exports as Hyperdrive, Images_d_exports as Images, Kv_d_exports as Kv, Queue_d_exports as Queue, QueueBinding_d_exports as QueueBinding, QueueDefinition_d_exports as QueueDefinition, R2_d_exports as R2, Rpc_d_exports as Rpc, RpcDefinition_d_exports as RpcDefinition, RpcTargets_d_exports as RpcTargets, RpcTracing_d_exports as RpcTracing, RpcWebSocketClient_d_exports as RpcWebSocketClient, ServiceBinding_d_exports as ServiceBinding, Vectorize_d_exports as Vectorize, WebTransport_d_exports as WebTransport, Worker_d_exports as Worker, WorkerConfig, WorkerDefinition_d_exports as WorkerDefinition, type WorkerEnv, WorkerEnvironment, WorkersAi_d_exports as WorkersAi, Workflow_d_exports as Workflow, WorkflowBinding_d_exports as WorkflowBinding, WorkflowDefinition_d_exports as WorkflowDefinition };
|
package/dist/index.mjs
CHANGED
|
@@ -11,6 +11,8 @@ import { DurableObjectWebSocket_exports } from "./DurableObjectWebSocket.mjs";
|
|
|
11
11
|
import { DurableObjectState_exports } from "./DurableObjectState.mjs";
|
|
12
12
|
import { CloudflareOtlp_exports } from "./CloudflareOtlp.mjs";
|
|
13
13
|
import { CloudflareTracer_exports } from "./CloudflareTracer.mjs";
|
|
14
|
+
import { DurableObjectContainer_exports } from "./DurableObjectContainer.mjs";
|
|
15
|
+
import { ContainerFiles_exports } from "./ContainerFiles.mjs";
|
|
14
16
|
import { RpcTargets_exports } from "./RpcTargets.mjs";
|
|
15
17
|
import { ContainerNamespace_exports } from "./ContainerNamespace.mjs";
|
|
16
18
|
import { D1_exports } from "./D1.mjs";
|
|
@@ -40,4 +42,4 @@ import { WebTransport_exports } from "./WebTransport.mjs";
|
|
|
40
42
|
import { WorkflowBinding_exports } from "./WorkflowBinding.mjs";
|
|
41
43
|
import { WorkflowDefinition_exports } from "./WorkflowDefinition.mjs";
|
|
42
44
|
import { Workflow_exports } from "./Workflow.mjs";
|
|
43
|
-
export { AiGateway_exports as AiGateway, AnalyticsEngine_exports as AnalyticsEngine, Artifacts_exports as Artifacts, Binding_exports as Binding, BrowserRendering_exports as BrowserRendering, Cache_exports as Cache, CloudflareOtlp_exports as CloudflareOtlp, CloudflareTracer_exports as CloudflareTracer, ContainerNamespace_exports as ContainerNamespace, D1_exports as D1, DurableObject_exports as DurableObject, DurableObjectAlarm_exports as DurableObjectAlarm, DurableObjectDefinition_exports as DurableObjectDefinition, DurableObjectNamespace_exports as DurableObjectNamespace, DurableObjectRpcWebSocket_exports as DurableObjectRpcWebSocket, DurableObjectSqlite_exports as DurableObjectSqlite, DurableObjectState_exports as DurableObjectState, DurableObjectStorage_exports as DurableObjectStorage, DurableObjectWebSocket_exports as DurableObjectWebSocket, Email_exports as Email, Environment_exports as Environment, Hyperdrive_exports as Hyperdrive, Images_exports as Images, Kv_exports as Kv, Queue_exports as Queue, QueueBinding_exports as QueueBinding, QueueDefinition_exports as QueueDefinition, R2_exports as R2, Rpc_exports as Rpc, RpcDefinition_exports as RpcDefinition, RpcTargets_exports as RpcTargets, RpcTracing_exports as RpcTracing, RpcWebSocketClient_exports as RpcWebSocketClient, ServiceBinding_exports as ServiceBinding, Vectorize_exports as Vectorize, WebTransport_exports as WebTransport, Worker_exports as Worker, WorkerConfig, WorkerDefinition_exports as WorkerDefinition, WorkerEnvironment, WorkersAi_exports as WorkersAi, Workflow_exports as Workflow, WorkflowBinding_exports as WorkflowBinding, WorkflowDefinition_exports as WorkflowDefinition };
|
|
45
|
+
export { AiGateway_exports as AiGateway, AnalyticsEngine_exports as AnalyticsEngine, Artifacts_exports as Artifacts, Binding_exports as Binding, BrowserRendering_exports as BrowserRendering, Cache_exports as Cache, CloudflareOtlp_exports as CloudflareOtlp, CloudflareTracer_exports as CloudflareTracer, ContainerFiles_exports as ContainerFiles, ContainerNamespace_exports as ContainerNamespace, D1_exports as D1, DurableObject_exports as DurableObject, DurableObjectAlarm_exports as DurableObjectAlarm, DurableObjectContainer_exports as DurableObjectContainer, DurableObjectDefinition_exports as DurableObjectDefinition, DurableObjectNamespace_exports as DurableObjectNamespace, DurableObjectRpcWebSocket_exports as DurableObjectRpcWebSocket, DurableObjectSqlite_exports as DurableObjectSqlite, DurableObjectState_exports as DurableObjectState, DurableObjectStorage_exports as DurableObjectStorage, DurableObjectWebSocket_exports as DurableObjectWebSocket, Email_exports as Email, Environment_exports as Environment, Hyperdrive_exports as Hyperdrive, Images_exports as Images, Kv_exports as Kv, Queue_exports as Queue, QueueBinding_exports as QueueBinding, QueueDefinition_exports as QueueDefinition, R2_exports as R2, Rpc_exports as Rpc, RpcDefinition_exports as RpcDefinition, RpcTargets_exports as RpcTargets, RpcTracing_exports as RpcTracing, RpcWebSocketClient_exports as RpcWebSocketClient, ServiceBinding_exports as ServiceBinding, Vectorize_exports as Vectorize, WebTransport_exports as WebTransport, Worker_exports as Worker, WorkerConfig, WorkerDefinition_exports as WorkerDefinition, WorkerEnvironment, WorkersAi_exports as WorkersAi, Workflow_exports as Workflow, WorkflowBinding_exports as WorkflowBinding, WorkflowDefinition_exports as WorkflowDefinition };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "effect-cf",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.51.0",
|
|
4
4
|
"description": "Effect-native primitives for Cloudflare Workers and bindings.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -78,6 +78,7 @@
|
|
|
78
78
|
"@cloudflare/computer": "0.2.0",
|
|
79
79
|
"@cloudflare/containers": "catalog:",
|
|
80
80
|
"@cloudflare/sandbox": "0.13.0-next.738.2",
|
|
81
|
+
"@cloudflare/sandbox-v1": "npm:@cloudflare/sandbox@1.0.0",
|
|
81
82
|
"@cloudflare/vitest-plugin": "catalog:",
|
|
82
83
|
"@cloudflare/workers-types": "catalog:",
|
|
83
84
|
"@effect/platform-node": "catalog:",
|
|
@@ -96,9 +97,9 @@
|
|
|
96
97
|
},
|
|
97
98
|
"peerDependencies": {
|
|
98
99
|
"@cloudflare/computer": "^0.2.0",
|
|
99
|
-
"@cloudflare/sandbox": "^0.13.0-next.738.2",
|
|
100
|
+
"@cloudflare/sandbox": "^0.13.0-next.738.2 || ^1.0.0",
|
|
100
101
|
"@cloudflare/vitest-plugin": "^1.0.0",
|
|
101
|
-
"@cloudflare/workers-types": "^5.
|
|
102
|
+
"@cloudflare/workers-types": "^5.20260926.1",
|
|
102
103
|
"@effect/sql-d1": "^4.0.0-rc.115",
|
|
103
104
|
"@effect/sql-pg": "^4.0.0-rc.115",
|
|
104
105
|
"@effect/sql-sqlite-do": "^4.0.0-rc.115",
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import * as Stream from "effect/Stream";
|
|
3
|
+
|
|
4
|
+
import { ContainerError } from "./DurableObjectContainer";
|
|
5
|
+
|
|
6
|
+
export type FileContent =
|
|
7
|
+
| string
|
|
8
|
+
| ArrayBuffer
|
|
9
|
+
| ArrayBufferView
|
|
10
|
+
| Blob
|
|
11
|
+
| ReadableStream<Uint8Array>;
|
|
12
|
+
export type FileType =
|
|
13
|
+
| "file"
|
|
14
|
+
| "directory"
|
|
15
|
+
| "symlink"
|
|
16
|
+
| "blockDevice"
|
|
17
|
+
| "characterDevice"
|
|
18
|
+
| "fifo"
|
|
19
|
+
| "socket";
|
|
20
|
+
|
|
21
|
+
export interface FileOperationOptions {
|
|
22
|
+
readonly cwd?: string;
|
|
23
|
+
readonly user?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface MkdirOptions extends FileOperationOptions {
|
|
27
|
+
readonly recursive?: boolean;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface RemoveOptions extends FileOperationOptions {
|
|
31
|
+
readonly recursive?: boolean;
|
|
32
|
+
readonly force?: boolean;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface FileStat {
|
|
36
|
+
readonly type: FileType;
|
|
37
|
+
readonly size: bigint;
|
|
38
|
+
readonly mode: number;
|
|
39
|
+
readonly uid: number;
|
|
40
|
+
readonly gid: number;
|
|
41
|
+
readonly accessedAt: Date;
|
|
42
|
+
readonly modifiedAt: Date;
|
|
43
|
+
readonly changedAt: Date;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface DirectoryEntry {
|
|
47
|
+
readonly name: string;
|
|
48
|
+
readonly type: FileType;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
type WithSignal<A> = A & { readonly signal?: AbortSignal };
|
|
52
|
+
|
|
53
|
+
/** Structural contract of Sandbox SDK 1.x `Files`, without loading an SDK version. */
|
|
54
|
+
export interface FilesResource {
|
|
55
|
+
readFile(path: string, options?: WithSignal<FileOperationOptions>): Promise<Response>;
|
|
56
|
+
writeFile(
|
|
57
|
+
path: string,
|
|
58
|
+
content: FileContent,
|
|
59
|
+
options?: WithSignal<FileOperationOptions>,
|
|
60
|
+
): Promise<void>;
|
|
61
|
+
stat(path: string, options?: WithSignal<FileOperationOptions>): Promise<FileStat>;
|
|
62
|
+
lstat(path: string, options?: WithSignal<FileOperationOptions>): Promise<FileStat>;
|
|
63
|
+
readDirectory(
|
|
64
|
+
path: string,
|
|
65
|
+
options?: WithSignal<FileOperationOptions>,
|
|
66
|
+
): Promise<ReadonlyArray<DirectoryEntry>>;
|
|
67
|
+
mkdir(path: string, options?: WithSignal<MkdirOptions>): Promise<void>;
|
|
68
|
+
rename(
|
|
69
|
+
source: string,
|
|
70
|
+
destination: string,
|
|
71
|
+
options?: WithSignal<FileOperationOptions>,
|
|
72
|
+
): Promise<void>;
|
|
73
|
+
remove(path: string, options?: WithSignal<RemoveOptions>): Promise<void>;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface ContainerFiles {
|
|
77
|
+
readonly raw: FilesResource;
|
|
78
|
+
/** The caller consumes or cancels the response body. Late failures retain native SDK errors. */
|
|
79
|
+
readonly readFile: (
|
|
80
|
+
path: string,
|
|
81
|
+
options?: FileOperationOptions,
|
|
82
|
+
) => Effect.Effect<Response, ContainerError>;
|
|
83
|
+
/** Includes failures that arrive while reading the response body. */
|
|
84
|
+
readonly readFileStream: (
|
|
85
|
+
path: string,
|
|
86
|
+
options?: FileOperationOptions,
|
|
87
|
+
) => Stream.Stream<Uint8Array, ContainerError>;
|
|
88
|
+
readonly readFileString: (
|
|
89
|
+
path: string,
|
|
90
|
+
options?: FileOperationOptions,
|
|
91
|
+
) => Effect.Effect<string, ContainerError>;
|
|
92
|
+
readonly writeFile: (
|
|
93
|
+
path: string,
|
|
94
|
+
content: FileContent,
|
|
95
|
+
options?: FileOperationOptions,
|
|
96
|
+
) => Effect.Effect<void, ContainerError>;
|
|
97
|
+
readonly stat: (
|
|
98
|
+
path: string,
|
|
99
|
+
options?: FileOperationOptions,
|
|
100
|
+
) => Effect.Effect<FileStat, ContainerError>;
|
|
101
|
+
readonly lstat: (
|
|
102
|
+
path: string,
|
|
103
|
+
options?: FileOperationOptions,
|
|
104
|
+
) => Effect.Effect<FileStat, ContainerError>;
|
|
105
|
+
readonly readDirectory: (
|
|
106
|
+
path: string,
|
|
107
|
+
options?: FileOperationOptions,
|
|
108
|
+
) => Effect.Effect<ReadonlyArray<DirectoryEntry>, ContainerError>;
|
|
109
|
+
readonly mkdir: (path: string, options?: MkdirOptions) => Effect.Effect<void, ContainerError>;
|
|
110
|
+
readonly rename: (
|
|
111
|
+
source: string,
|
|
112
|
+
destination: string,
|
|
113
|
+
options?: FileOperationOptions,
|
|
114
|
+
) => Effect.Effect<void, ContainerError>;
|
|
115
|
+
readonly remove: (path: string, options?: RemoveOptions) => Effect.Effect<void, ContainerError>;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const attempt = <A>(operation: string, evaluate: (signal: AbortSignal) => Promise<A>) =>
|
|
119
|
+
Effect.tryPromise({
|
|
120
|
+
try: evaluate,
|
|
121
|
+
catch: (cause) => new ContainerError({ operation: `files.${operation}`, cause }),
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
/** Pass `new Files(container.raw)` from SDK 1.x. The image must supply its matching sandbox-shim. */
|
|
125
|
+
export const fromFiles = (files: FilesResource): ContainerFiles => {
|
|
126
|
+
const readFile = (path: string, options?: FileOperationOptions) =>
|
|
127
|
+
attempt("readFile", (signal) => files.readFile(path, { ...options, signal }));
|
|
128
|
+
const readFileStream = (path: string, options?: FileOperationOptions) =>
|
|
129
|
+
Stream.unwrap(
|
|
130
|
+
Effect.map(readFile(path, options), (response) => {
|
|
131
|
+
const body = response.body;
|
|
132
|
+
|
|
133
|
+
return body === null
|
|
134
|
+
? Stream.empty
|
|
135
|
+
: Stream.fromReadableStream({
|
|
136
|
+
evaluate: () => body,
|
|
137
|
+
onError: (cause) => new ContainerError({ operation: "files.readFile", cause }),
|
|
138
|
+
});
|
|
139
|
+
}),
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
return {
|
|
143
|
+
raw: files,
|
|
144
|
+
readFile,
|
|
145
|
+
readFileStream,
|
|
146
|
+
readFileString: (path, options) =>
|
|
147
|
+
readFileStream(path, options).pipe(Stream.decodeText(), Stream.mkString),
|
|
148
|
+
writeFile: (path, content, options) =>
|
|
149
|
+
attempt("writeFile", (signal) => files.writeFile(path, content, { ...options, signal })),
|
|
150
|
+
stat: (path, options) => attempt("stat", (signal) => files.stat(path, { ...options, signal })),
|
|
151
|
+
lstat: (path, options) =>
|
|
152
|
+
attempt("lstat", (signal) => files.lstat(path, { ...options, signal })),
|
|
153
|
+
readDirectory: (path, options) =>
|
|
154
|
+
attempt("readDirectory", (signal) => files.readDirectory(path, { ...options, signal })),
|
|
155
|
+
mkdir: (path, options) =>
|
|
156
|
+
attempt("mkdir", (signal) => files.mkdir(path, { ...options, signal })),
|
|
157
|
+
rename: (source, destination, options) =>
|
|
158
|
+
attempt("rename", (signal) => files.rename(source, destination, { ...options, signal })),
|
|
159
|
+
remove: (path, options) =>
|
|
160
|
+
attempt("remove", (signal) => files.remove(path, { ...options, signal })),
|
|
161
|
+
};
|
|
162
|
+
};
|
|
@@ -60,10 +60,12 @@ export const ContainerState = S.Union([
|
|
|
60
60
|
export type ContainerState = typeof ContainerState.Type;
|
|
61
61
|
|
|
62
62
|
/**
|
|
63
|
-
*
|
|
63
|
+
* RPC stub exposed by the legacy `@cloudflare/containers` Container class.
|
|
64
64
|
*
|
|
65
65
|
* This is structural so using Container bindings does not make
|
|
66
66
|
* `@cloudflare/containers` a runtime dependency of effect-cf.
|
|
67
|
+
* Use `DurableObjectContainer` inside a Durable Object for the native
|
|
68
|
+
* `ctx.container` API and the `durable_object` scheduling policy.
|
|
67
69
|
*/
|
|
68
70
|
export interface ContainerStub {
|
|
69
71
|
fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response>;
|