effect-cf 0.29.0 → 0.31.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 +20 -0
- package/README.md +103 -8
- package/dist/{Artifacts-rq3hUKQE.mjs → Artifacts-CSIk1Oml.mjs} +4 -128
- package/dist/Binding-pfilNJmP.mjs +129 -0
- package/dist/ComputerArtifacts.mjs +1 -1
- package/dist/ComputerWorkspace.d.mts +1 -1
- package/dist/ComputerWorkspace.mjs +1 -1
- package/dist/ContainerNamespace-CFjiLw8b.d.mts +155 -0
- package/dist/{DurableObjectState-DnnLoHV-.mjs → DurableObjectState-CxS5Y2hf.mjs} +10 -2
- package/dist/{DurableObjectState-OupNMMUl.d.mts → DurableObjectState-DVIgf0sA.d.mts} +7 -1
- package/dist/Sandbox.d.mts +222 -0
- package/dist/Sandbox.mjs +361 -0
- package/dist/Vitest.d.mts +9 -9
- package/dist/Vitest.mjs +5 -5
- package/dist/{WorkflowBinding-DUZtKCVM.d.mts → WorkflowBinding-C9KRmfDU.d.mts} +6 -3
- package/dist/index.d.mts +62 -199
- package/dist/index.mjs +15 -6
- package/package.json +15 -5
- package/src/DurableObjectStorage.ts +40 -1
- package/src/Images.ts +60 -9
- package/src/Sandbox.ts +1094 -0
- package/src/Vitest.ts +9 -9
- package/src/Worker.ts +2 -0
- package/src/WorkflowBinding.ts +10 -2
- package/src/internal/WorkerContext.ts +1 -0
package/dist/Sandbox.mjs
ADDED
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
import { a as WorkerEnvironment, t as causeMessage } from "./ErrorMessage-BM0K-Nik.mjs";
|
|
2
|
+
import { r as layer$1 } from "./Binding-pfilNJmP.mjs";
|
|
3
|
+
import { Context, Data, Effect, Option, Predicate, Stream } from "effect";
|
|
4
|
+
//#region src/Sandbox.ts
|
|
5
|
+
const expectedSandboxNamespace = "Sandbox Durable Object namespace binding with idFromName() and get()";
|
|
6
|
+
/** Failure raised when invoking a sandbox operation. */
|
|
7
|
+
var SandboxOperationError = class extends Data.TaggedError("SandboxOperationError") {
|
|
8
|
+
get message() {
|
|
9
|
+
return `Sandbox ${this.operation} failed for binding "${this.binding}" instance "${this.instance}": ${causeMessage(this.cause)}`;
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
const trySandboxPromise = (definition, instance, operation, evaluate) => Effect.tryPromise({
|
|
13
|
+
try: evaluate,
|
|
14
|
+
catch: (cause) => new SandboxOperationError({
|
|
15
|
+
binding: definition.binding,
|
|
16
|
+
instance,
|
|
17
|
+
operation,
|
|
18
|
+
cause
|
|
19
|
+
})
|
|
20
|
+
});
|
|
21
|
+
const sandboxStream = (definition, instance, operation, acquire) => Stream.unwrap(Effect.map(trySandboxPromise(definition, instance, operation, acquire), (readable) => Stream.fromReadableStream({
|
|
22
|
+
evaluate: () => readable,
|
|
23
|
+
onError: (cause) => new SandboxOperationError({
|
|
24
|
+
binding: definition.binding,
|
|
25
|
+
instance,
|
|
26
|
+
operation,
|
|
27
|
+
cause
|
|
28
|
+
})
|
|
29
|
+
})));
|
|
30
|
+
const decodeSseData = (payload) => {
|
|
31
|
+
if (payload === "[DONE]" || payload.trim() === "") return [];
|
|
32
|
+
try {
|
|
33
|
+
return [JSON.parse(payload)];
|
|
34
|
+
} catch {
|
|
35
|
+
return [];
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Decodes the sandbox runtime's `data:`-framed Server-Sent Events byte stream
|
|
40
|
+
* into JSON event values, mirroring `parseSSEStream` from
|
|
41
|
+
* `@cloudflare/sandbox` without requiring the package at runtime.
|
|
42
|
+
*/
|
|
43
|
+
const decodeSseEvents = (bytes) => bytes.pipe(Stream.decodeText(), Stream.splitLines, Stream.mapAccum(() => [], (pending, line) => {
|
|
44
|
+
if (line === "") return pending.length > 0 ? [[], decodeSseData(pending.join("\n"))] : [pending, []];
|
|
45
|
+
if (line.startsWith("data:")) {
|
|
46
|
+
const value = line.startsWith("data: ") ? line.slice(6) : line.slice(5);
|
|
47
|
+
return [[...pending, value], []];
|
|
48
|
+
}
|
|
49
|
+
return [pending, []];
|
|
50
|
+
}, { onHalt: (pending) => pending.length > 0 ? decodeSseData(pending.join("\n")) : [] }));
|
|
51
|
+
const makeProcessHandle = (definition, instance, process) => {
|
|
52
|
+
const spanOptions = (operation) => ({ attributes: {
|
|
53
|
+
binding: definition.binding,
|
|
54
|
+
instance,
|
|
55
|
+
processId: process.id,
|
|
56
|
+
operation
|
|
57
|
+
} });
|
|
58
|
+
return {
|
|
59
|
+
id: process.id,
|
|
60
|
+
pid: process.pid,
|
|
61
|
+
rawUnsafe: Effect.succeed(process),
|
|
62
|
+
status: trySandboxPromise(definition, instance, "process.status", () => process.status()).pipe(Effect.withSpan("Sandbox.process.status", spanOptions("process.status"))),
|
|
63
|
+
exitCode: trySandboxPromise(definition, instance, "process.exitCode", () => process.exitCode).pipe(Effect.withSpan("Sandbox.process.exitCode", spanOptions("process.exitCode"))),
|
|
64
|
+
logs: (options) => sandboxStream(definition, instance, "process.logs", (signal) => process.logs({
|
|
65
|
+
...options,
|
|
66
|
+
signal
|
|
67
|
+
})),
|
|
68
|
+
waitForLog: Effect.fn("Sandbox.process.waitForLog", spanOptions("process.waitForLog"))(function* (pattern, options) {
|
|
69
|
+
return yield* trySandboxPromise(definition, instance, "process.waitForLog", (signal) => process.waitForLog(pattern, {
|
|
70
|
+
...options,
|
|
71
|
+
signal
|
|
72
|
+
}));
|
|
73
|
+
}),
|
|
74
|
+
waitForExit: Effect.fn("Sandbox.process.waitForExit", spanOptions("process.waitForExit"))(function* (options) {
|
|
75
|
+
return yield* trySandboxPromise(definition, instance, "process.waitForExit", (signal) => process.waitForExit({
|
|
76
|
+
...options,
|
|
77
|
+
signal
|
|
78
|
+
}));
|
|
79
|
+
}),
|
|
80
|
+
output: Effect.fn("Sandbox.process.output", spanOptions("process.output"))(function* (options) {
|
|
81
|
+
return yield* trySandboxPromise(definition, instance, "process.output", (signal) => process.output({
|
|
82
|
+
...options,
|
|
83
|
+
signal
|
|
84
|
+
}));
|
|
85
|
+
}),
|
|
86
|
+
outputText: Effect.fn("Sandbox.process.outputText", spanOptions("process.outputText"))(function* (options) {
|
|
87
|
+
return yield* trySandboxPromise(definition, instance, "process.outputText", (signal) => process.output({
|
|
88
|
+
...options,
|
|
89
|
+
encoding: "utf8",
|
|
90
|
+
signal
|
|
91
|
+
}));
|
|
92
|
+
}),
|
|
93
|
+
waitForPort: Effect.fn("Sandbox.process.waitForPort", spanOptions("process.waitForPort"))(function* (port, options) {
|
|
94
|
+
return yield* trySandboxPromise(definition, instance, "process.waitForPort", (signal) => process.waitForPort(port, {
|
|
95
|
+
...options,
|
|
96
|
+
signal
|
|
97
|
+
}));
|
|
98
|
+
}),
|
|
99
|
+
kill: Effect.fn("Sandbox.process.kill", spanOptions("process.kill"))(function* (signal) {
|
|
100
|
+
return yield* trySandboxPromise(definition, instance, "process.kill", () => process.kill(signal));
|
|
101
|
+
})
|
|
102
|
+
};
|
|
103
|
+
};
|
|
104
|
+
const makeTerminalHandle = (definition, instance, terminal) => {
|
|
105
|
+
const spanOptions = (operation) => ({ attributes: {
|
|
106
|
+
binding: definition.binding,
|
|
107
|
+
instance,
|
|
108
|
+
terminalId: terminal.id,
|
|
109
|
+
operation
|
|
110
|
+
} });
|
|
111
|
+
return {
|
|
112
|
+
id: terminal.id,
|
|
113
|
+
rawUnsafe: Effect.succeed(terminal),
|
|
114
|
+
snapshot: trySandboxPromise(definition, instance, "terminal.snapshot", () => terminal.getSnapshot()).pipe(Effect.withSpan("Sandbox.terminal.snapshot", spanOptions("terminal.snapshot"))),
|
|
115
|
+
write: Effect.fn("Sandbox.terminal.write", spanOptions("terminal.write"))(function* (data) {
|
|
116
|
+
return yield* trySandboxPromise(definition, instance, "terminal.write", () => terminal.write(data));
|
|
117
|
+
}),
|
|
118
|
+
resize: Effect.fn("Sandbox.terminal.resize", spanOptions("terminal.resize"))(function* (cols, rows) {
|
|
119
|
+
return yield* trySandboxPromise(definition, instance, "terminal.resize", () => terminal.resize(cols, rows));
|
|
120
|
+
}),
|
|
121
|
+
output: (options) => sandboxStream(definition, instance, "terminal.output", (signal) => terminal.output({
|
|
122
|
+
...options,
|
|
123
|
+
signal
|
|
124
|
+
})),
|
|
125
|
+
waitForExit: Effect.fn("Sandbox.terminal.waitForExit", spanOptions("terminal.waitForExit"))(function* (options) {
|
|
126
|
+
return yield* trySandboxPromise(definition, instance, "terminal.waitForExit", (signal) => terminal.waitForExit({
|
|
127
|
+
...options,
|
|
128
|
+
signal
|
|
129
|
+
}));
|
|
130
|
+
}),
|
|
131
|
+
interrupt: trySandboxPromise(definition, instance, "terminal.interrupt", () => terminal.interrupt()).pipe(Effect.withSpan("Sandbox.terminal.interrupt", spanOptions("terminal.interrupt"))),
|
|
132
|
+
terminate: trySandboxPromise(definition, instance, "terminal.terminate", () => terminal.terminate()).pipe(Effect.withSpan("Sandbox.terminal.terminate", spanOptions("terminal.terminate"))),
|
|
133
|
+
connect: Effect.fn("Sandbox.terminal.connect", spanOptions("terminal.connect"))(function* (request, options) {
|
|
134
|
+
return yield* trySandboxPromise(definition, instance, "terminal.connect", () => terminal.connect(request, options));
|
|
135
|
+
})
|
|
136
|
+
};
|
|
137
|
+
};
|
|
138
|
+
/**
|
|
139
|
+
* Wraps an already-created sandbox client (the value returned by
|
|
140
|
+
* `getSandbox()` from `@cloudflare/sandbox`) into an Effect client.
|
|
141
|
+
*
|
|
142
|
+
* Prefer {@link Tag}/{@link make} with a layer for application code; this
|
|
143
|
+
* entry point exists for advanced composition and testing.
|
|
144
|
+
*/
|
|
145
|
+
const fromSandboxClient = (client, definition, instance) => {
|
|
146
|
+
const spanOptions = (operation) => ({ attributes: {
|
|
147
|
+
binding: definition.binding,
|
|
148
|
+
instance,
|
|
149
|
+
operation
|
|
150
|
+
} });
|
|
151
|
+
return {
|
|
152
|
+
rawUnsafe: Effect.succeed(client),
|
|
153
|
+
exec: Effect.fn("Sandbox.exec", spanOptions("exec"))(function* (command, options) {
|
|
154
|
+
const process = yield* trySandboxPromise(definition, instance, "exec", () => client.exec(command, options));
|
|
155
|
+
return makeProcessHandle(definition, instance, process);
|
|
156
|
+
}),
|
|
157
|
+
getProcess: Effect.fn("Sandbox.getProcess", spanOptions("getProcess"))(function* (id) {
|
|
158
|
+
const process = yield* trySandboxPromise(definition, instance, "getProcess", () => client.getProcess(id));
|
|
159
|
+
return Option.map(Option.fromNullishOr(process), (found) => makeProcessHandle(definition, instance, found));
|
|
160
|
+
}),
|
|
161
|
+
listProcesses: trySandboxPromise(definition, instance, "listProcesses", () => client.listProcesses()).pipe(Effect.withSpan("Sandbox.listProcesses", spanOptions("listProcesses"))),
|
|
162
|
+
writeFile: Effect.fn("Sandbox.writeFile", spanOptions("writeFile"))(function* (path, content, options) {
|
|
163
|
+
yield* Effect.annotateCurrentSpan("path", path);
|
|
164
|
+
return yield* trySandboxPromise(definition, instance, "writeFile", () => client.writeFile(path, content, options));
|
|
165
|
+
}),
|
|
166
|
+
readFile: Effect.fn("Sandbox.readFile", spanOptions("readFile"))(function* (path, options) {
|
|
167
|
+
yield* Effect.annotateCurrentSpan("path", path);
|
|
168
|
+
return yield* trySandboxPromise(definition, instance, "readFile", () => client.readFile(path, options));
|
|
169
|
+
}),
|
|
170
|
+
readFileStream: (path) => sandboxStream(definition, instance, "readFileStream", () => client.readFileStream(path)),
|
|
171
|
+
mkdir: Effect.fn("Sandbox.mkdir", spanOptions("mkdir"))(function* (path, options) {
|
|
172
|
+
yield* Effect.annotateCurrentSpan("path", path);
|
|
173
|
+
return yield* trySandboxPromise(definition, instance, "mkdir", () => client.mkdir(path, options));
|
|
174
|
+
}),
|
|
175
|
+
deleteFile: Effect.fn("Sandbox.deleteFile", spanOptions("deleteFile"))(function* (path) {
|
|
176
|
+
yield* Effect.annotateCurrentSpan("path", path);
|
|
177
|
+
return yield* trySandboxPromise(definition, instance, "deleteFile", () => client.deleteFile(path));
|
|
178
|
+
}),
|
|
179
|
+
renameFile: Effect.fn("Sandbox.renameFile", spanOptions("renameFile"))(function* (oldPath, newPath) {
|
|
180
|
+
yield* Effect.annotateCurrentSpan({
|
|
181
|
+
oldPath,
|
|
182
|
+
newPath
|
|
183
|
+
});
|
|
184
|
+
return yield* trySandboxPromise(definition, instance, "renameFile", () => client.renameFile(oldPath, newPath));
|
|
185
|
+
}),
|
|
186
|
+
moveFile: Effect.fn("Sandbox.moveFile", spanOptions("moveFile"))(function* (sourcePath, destinationPath) {
|
|
187
|
+
yield* Effect.annotateCurrentSpan({
|
|
188
|
+
sourcePath,
|
|
189
|
+
destinationPath
|
|
190
|
+
});
|
|
191
|
+
return yield* trySandboxPromise(definition, instance, "moveFile", () => client.moveFile(sourcePath, destinationPath));
|
|
192
|
+
}),
|
|
193
|
+
listFiles: Effect.fn("Sandbox.listFiles", spanOptions("listFiles"))(function* (path, options) {
|
|
194
|
+
yield* Effect.annotateCurrentSpan("path", path);
|
|
195
|
+
return yield* trySandboxPromise(definition, instance, "listFiles", () => client.listFiles(path, options));
|
|
196
|
+
}),
|
|
197
|
+
exists: Effect.fn("Sandbox.exists", spanOptions("exists"))(function* (path) {
|
|
198
|
+
yield* Effect.annotateCurrentSpan("path", path);
|
|
199
|
+
return yield* trySandboxPromise(definition, instance, "exists", () => client.exists(path));
|
|
200
|
+
}),
|
|
201
|
+
watch: (path, options) => decodeSseEvents(sandboxStream(definition, instance, "watch", () => client.watch(path, options))),
|
|
202
|
+
checkChanges: Effect.fn("Sandbox.checkChanges", spanOptions("checkChanges"))(function* (path, options) {
|
|
203
|
+
yield* Effect.annotateCurrentSpan("path", path);
|
|
204
|
+
return yield* trySandboxPromise(definition, instance, "checkChanges", () => client.checkChanges(path, options));
|
|
205
|
+
}),
|
|
206
|
+
setEnvVars: Effect.fn("Sandbox.setEnvVars", spanOptions("setEnvVars"))(function* (envVars) {
|
|
207
|
+
return yield* trySandboxPromise(definition, instance, "setEnvVars", () => client.setEnvVars(envVars));
|
|
208
|
+
}),
|
|
209
|
+
exposePort: Effect.fn("Sandbox.exposePort", spanOptions("exposePort"))(function* (port, options) {
|
|
210
|
+
yield* Effect.annotateCurrentSpan("port", port);
|
|
211
|
+
return yield* trySandboxPromise(definition, instance, "exposePort", () => client.exposePort(port, options));
|
|
212
|
+
}),
|
|
213
|
+
unexposePort: Effect.fn("Sandbox.unexposePort", spanOptions("unexposePort"))(function* (port) {
|
|
214
|
+
yield* Effect.annotateCurrentSpan("port", port);
|
|
215
|
+
return yield* trySandboxPromise(definition, instance, "unexposePort", () => client.unexposePort(port));
|
|
216
|
+
}),
|
|
217
|
+
getExposedPorts: Effect.fn("Sandbox.getExposedPorts", spanOptions("getExposedPorts"))(function* (hostname) {
|
|
218
|
+
return yield* trySandboxPromise(definition, instance, "getExposedPorts", () => client.getExposedPorts(hostname));
|
|
219
|
+
}),
|
|
220
|
+
isPortExposed: Effect.fn("Sandbox.isPortExposed", spanOptions("isPortExposed"))(function* (port) {
|
|
221
|
+
yield* Effect.annotateCurrentSpan("port", port);
|
|
222
|
+
return yield* trySandboxPromise(definition, instance, "isPortExposed", () => client.isPortExposed(port));
|
|
223
|
+
}),
|
|
224
|
+
validatePortToken: Effect.fn("Sandbox.validatePortToken", spanOptions("validatePortToken"))(function* (port, token) {
|
|
225
|
+
yield* Effect.annotateCurrentSpan("port", port);
|
|
226
|
+
return yield* trySandboxPromise(definition, instance, "validatePortToken", () => client.validatePortToken(port, token));
|
|
227
|
+
}),
|
|
228
|
+
containerFetch: Effect.fn("Sandbox.containerFetch", spanOptions("containerFetch"))(function* (requestOrUrl, port) {
|
|
229
|
+
return yield* trySandboxPromise(definition, instance, "containerFetch", () => client.containerFetch(requestOrUrl, port));
|
|
230
|
+
}),
|
|
231
|
+
wsConnect: Effect.fn("Sandbox.wsConnect", spanOptions("wsConnect"))(function* (request, port) {
|
|
232
|
+
yield* Effect.annotateCurrentSpan("port", port);
|
|
233
|
+
return yield* trySandboxPromise(definition, instance, "wsConnect", () => client.wsConnect(request, port));
|
|
234
|
+
}),
|
|
235
|
+
mountBucket: Effect.fn("Sandbox.mountBucket", spanOptions("mountBucket"))(function* (bucket, mountPath, options) {
|
|
236
|
+
yield* Effect.annotateCurrentSpan({
|
|
237
|
+
bucket,
|
|
238
|
+
mountPath
|
|
239
|
+
});
|
|
240
|
+
return yield* trySandboxPromise(definition, instance, "mountBucket", () => client.mountBucket(bucket, mountPath, options));
|
|
241
|
+
}),
|
|
242
|
+
unmountBucket: Effect.fn("Sandbox.unmountBucket", spanOptions("unmountBucket"))(function* (mountPath) {
|
|
243
|
+
yield* Effect.annotateCurrentSpan("mountPath", mountPath);
|
|
244
|
+
return yield* trySandboxPromise(definition, instance, "unmountBucket", () => client.unmountBucket(mountPath));
|
|
245
|
+
}),
|
|
246
|
+
createBackup: Effect.fn("Sandbox.createBackup", spanOptions("createBackup"))(function* (options) {
|
|
247
|
+
yield* Effect.annotateCurrentSpan("dir", options.dir);
|
|
248
|
+
return yield* trySandboxPromise(definition, instance, "createBackup", () => client.createBackup(options));
|
|
249
|
+
}),
|
|
250
|
+
restoreBackup: Effect.fn("Sandbox.restoreBackup", spanOptions("restoreBackup"))(function* (backup) {
|
|
251
|
+
yield* Effect.annotateCurrentSpan({
|
|
252
|
+
backupId: backup.id,
|
|
253
|
+
dir: backup.dir
|
|
254
|
+
});
|
|
255
|
+
return yield* trySandboxPromise(definition, instance, "restoreBackup", () => client.restoreBackup(backup));
|
|
256
|
+
}),
|
|
257
|
+
createTerminal: Effect.fn("Sandbox.createTerminal", spanOptions("createTerminal"))(function* (options) {
|
|
258
|
+
const terminal = yield* trySandboxPromise(definition, instance, "createTerminal", () => client.createTerminal(options));
|
|
259
|
+
return makeTerminalHandle(definition, instance, terminal);
|
|
260
|
+
}),
|
|
261
|
+
getTerminal: Effect.fn("Sandbox.getTerminal", spanOptions("getTerminal"))(function* (id) {
|
|
262
|
+
const terminal = yield* trySandboxPromise(definition, instance, "getTerminal", () => client.getTerminal(id));
|
|
263
|
+
return Option.map(Option.fromNullishOr(terminal), (found) => makeTerminalHandle(definition, instance, found));
|
|
264
|
+
}),
|
|
265
|
+
listTerminals: trySandboxPromise(definition, instance, "listTerminals", () => client.listTerminals()).pipe(Effect.map((terminals) => terminals.map((terminal) => makeTerminalHandle(definition, instance, terminal))), Effect.withSpan("Sandbox.listTerminals", spanOptions("listTerminals"))),
|
|
266
|
+
tunnels: {
|
|
267
|
+
get: Effect.fn("Sandbox.tunnels.get", spanOptions("tunnels.get"))(function* (port, options) {
|
|
268
|
+
yield* Effect.annotateCurrentSpan("port", port);
|
|
269
|
+
return yield* trySandboxPromise(definition, instance, "tunnels.get", () => client.tunnels.get(port, options));
|
|
270
|
+
}),
|
|
271
|
+
list: trySandboxPromise(definition, instance, "tunnels.list", () => client.tunnels.list()).pipe(Effect.withSpan("Sandbox.tunnels.list", spanOptions("tunnels.list"))),
|
|
272
|
+
destroy: Effect.fn("Sandbox.tunnels.destroy", spanOptions("tunnels.destroy"))(function* (portOrInfo) {
|
|
273
|
+
return yield* trySandboxPromise(definition, instance, "tunnels.destroy", () => client.tunnels.destroy(portOrInfo));
|
|
274
|
+
})
|
|
275
|
+
},
|
|
276
|
+
start: Effect.fn("Sandbox.start", spanOptions("start"))(function* (options) {
|
|
277
|
+
return yield* trySandboxPromise(definition, instance, "start", () => client.start(options));
|
|
278
|
+
}),
|
|
279
|
+
stop: Effect.fn("Sandbox.stop", spanOptions("stop"))(function* (signal) {
|
|
280
|
+
return yield* trySandboxPromise(definition, instance, "stop", () => client.stop(signal));
|
|
281
|
+
}),
|
|
282
|
+
destroy: trySandboxPromise(definition, instance, "destroy", () => client.destroy()).pipe(Effect.withSpan("Sandbox.destroy", spanOptions("destroy")))
|
|
283
|
+
};
|
|
284
|
+
};
|
|
285
|
+
/**
|
|
286
|
+
* Lazily imports `@cloudflare/sandbox`.
|
|
287
|
+
*
|
|
288
|
+
* The package is an optional peer dependency: loading it on first use keeps
|
|
289
|
+
* this module importable (and the rest of `effect-cf` bundleable) in
|
|
290
|
+
* applications that never touch sandboxes.
|
|
291
|
+
*/
|
|
292
|
+
const importSandboxModule = (definition, instance) => Effect.tryPromise({
|
|
293
|
+
try: () => import("@cloudflare/sandbox"),
|
|
294
|
+
catch: (cause) => new SandboxOperationError({
|
|
295
|
+
binding: definition.binding,
|
|
296
|
+
instance,
|
|
297
|
+
operation: "import",
|
|
298
|
+
cause
|
|
299
|
+
})
|
|
300
|
+
});
|
|
301
|
+
const isSandboxNamespaceResource = (value) => Predicate.hasProperty(value, "idFromName") && Predicate.isFunction(value.idFromName) && Predicate.hasProperty(value, "get") && Predicate.isFunction(value.get);
|
|
302
|
+
const makeClient = (definition) => (namespace) => {
|
|
303
|
+
return {
|
|
304
|
+
definition,
|
|
305
|
+
get: Effect.fn("Sandbox.get", { attributes: {
|
|
306
|
+
binding: definition.binding,
|
|
307
|
+
operation: "get"
|
|
308
|
+
} })(function* (name, options) {
|
|
309
|
+
yield* Effect.annotateCurrentSpan("instance", name);
|
|
310
|
+
const sandboxModule = yield* importSandboxModule(definition, name);
|
|
311
|
+
const nativeNamespace = namespace;
|
|
312
|
+
const client = yield* Effect.try({
|
|
313
|
+
try: () => sandboxModule.getSandbox(nativeNamespace, name, options),
|
|
314
|
+
catch: (cause) => new SandboxOperationError({
|
|
315
|
+
binding: definition.binding,
|
|
316
|
+
instance: name,
|
|
317
|
+
operation: "get",
|
|
318
|
+
cause
|
|
319
|
+
})
|
|
320
|
+
});
|
|
321
|
+
return fromSandboxClient(client, definition, name);
|
|
322
|
+
}),
|
|
323
|
+
rawUnsafe: Effect.succeed(namespace)
|
|
324
|
+
};
|
|
325
|
+
};
|
|
326
|
+
const layer = (tag, definition) => layer$1(tag, definition.binding, (value) => isSandboxNamespaceResource(value), makeClient(definition), { expected: expectedSandboxNamespace });
|
|
327
|
+
const make = (id) => Tag()(id);
|
|
328
|
+
/** Creates a typed Effect service for a Cloudflare Sandbox namespace. */
|
|
329
|
+
const Tag = () => (id) => {
|
|
330
|
+
const tag = Context.Service()(`effect-cf/Sandbox/${id}`);
|
|
331
|
+
const makeLayer = (definition) => layer(tag, definition);
|
|
332
|
+
const get = Effect.fnUntraced(function* (name, options) {
|
|
333
|
+
return yield* (yield* tag).get(name, options);
|
|
334
|
+
});
|
|
335
|
+
const rawUnsafe = Effect.flatMap(tag, (namespace) => namespace.rawUnsafe);
|
|
336
|
+
return Object.assign(tag, {
|
|
337
|
+
id,
|
|
338
|
+
layer: makeLayer,
|
|
339
|
+
get,
|
|
340
|
+
rawUnsafe
|
|
341
|
+
});
|
|
342
|
+
};
|
|
343
|
+
const Sandbox = Tag;
|
|
344
|
+
/**
|
|
345
|
+
* Routes sandbox preview-URL requests (`https://<port>-<sandbox-id>-<token>.<host>`)
|
|
346
|
+
* to the owning sandbox, mirroring `proxyToSandbox` from `@cloudflare/sandbox`.
|
|
347
|
+
*
|
|
348
|
+
* Call this before any other routing in a Worker `fetch` handler; it resolves
|
|
349
|
+
* to `Option.none()` for requests that are not preview-URL traffic.
|
|
350
|
+
*/
|
|
351
|
+
const proxyToSandbox = Effect.fn("Sandbox.proxyToSandbox")(function* (request, options) {
|
|
352
|
+
const binding = options?.binding ?? "Sandbox";
|
|
353
|
+
const definition = { binding };
|
|
354
|
+
const env = yield* WorkerEnvironment;
|
|
355
|
+
const resource = Predicate.hasProperty(env, binding) ? env[binding] : void 0;
|
|
356
|
+
const sandboxModule = yield* importSandboxModule(definition, "preview");
|
|
357
|
+
const response = yield* trySandboxPromise(definition, "preview", "proxyToSandbox", () => sandboxModule.proxyToSandbox(request, { Sandbox: resource }));
|
|
358
|
+
return Option.fromNullishOr(response);
|
|
359
|
+
});
|
|
360
|
+
//#endregion
|
|
361
|
+
export { Sandbox, SandboxOperationError, Tag, fromSandboxClient, isSandboxNamespaceResource, layer, make, makeClient, proxyToSandbox };
|
package/dist/Vitest.d.mts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { c as WorkerEnvironment, s as WorkerEnv } from "./Binding-xJJJPmiD.mjs";
|
|
2
|
-
import { n as DurableObjectStateService, t as DurableObjectState } from "./DurableObjectState-
|
|
3
|
-
import { o as WorkflowInstanceStatusName } from "./WorkflowBinding-
|
|
2
|
+
import { n as DurableObjectStateService, t as DurableObjectState } from "./DurableObjectState-DVIgf0sA.mjs";
|
|
3
|
+
import { o as WorkflowInstanceStatusName } from "./WorkflowBinding-C9KRmfDU.mjs";
|
|
4
4
|
import { Effect, Layer, Schema } from "effect";
|
|
5
5
|
import { DurableObject } from "cloudflare:workers";
|
|
6
6
|
import { adminSecretsStore as adminSecretsStore$1, getQueueResult, introspectWorkflow as introspectWorkflow$1, introspectWorkflowInstance as introspectWorkflowInstance$1 } from "cloudflare:test";
|
|
7
7
|
//#region src/Vitest.d.ts
|
|
8
8
|
/**
|
|
9
|
-
* Provides the Workers
|
|
9
|
+
* Provides the Workers test environment as both {@link WorkerEnvironment} and
|
|
10
10
|
* the active Effect `ConfigProvider`.
|
|
11
11
|
*/
|
|
12
12
|
declare const layer: Layer.Layer<WorkerEnvironment>;
|
|
@@ -54,7 +54,7 @@ type PagesEventContextInitData<Context> = Context extends globalThis.EventContex
|
|
|
54
54
|
} : {
|
|
55
55
|
readonly data: Data;
|
|
56
56
|
} : never;
|
|
57
|
-
/** Initialization accepted by
|
|
57
|
+
/** Initialization accepted by the Workers Vitest plugin for a Pages Function. */
|
|
58
58
|
type PagesEventContextInit<Function extends AnyPagesFunction> = PagesEventContextInitBase & PagesEventContextInitParams<Parameters<Function>[0]> & PagesEventContextInitData<Parameters<Function>[0]>;
|
|
59
59
|
/**
|
|
60
60
|
* Invokes a Pages Function with a typed event context and drains its
|
|
@@ -64,7 +64,7 @@ declare const pages: <Function extends AnyPagesFunction>(handler: Function, init
|
|
|
64
64
|
interface QueueWorker {
|
|
65
65
|
queue(batch: globalThis.MessageBatch): Promise<void>;
|
|
66
66
|
}
|
|
67
|
-
/** Input message accepted by
|
|
67
|
+
/** Input message accepted by the Workers Vitest plugin's `createMessageBatch` helper. */
|
|
68
68
|
type QueueMessage<Body> = {
|
|
69
69
|
readonly id: string;
|
|
70
70
|
readonly timestamp: Date;
|
|
@@ -74,7 +74,7 @@ type QueueMessage<Body> = {
|
|
|
74
74
|
} | {
|
|
75
75
|
readonly serializedBody: ArrayBuffer | ArrayBufferView;
|
|
76
76
|
});
|
|
77
|
-
/** Acknowledgement and retry result returned by
|
|
77
|
+
/** Acknowledgement and retry result returned by the Workers Vitest plugin. */
|
|
78
78
|
type QueueResult = Awaited<ReturnType<typeof getQueueResult>>;
|
|
79
79
|
/** Result of invoking a queue consumer through {@link queue}. */
|
|
80
80
|
interface QueueRunResult<Body> {
|
|
@@ -83,7 +83,7 @@ interface QueueRunResult<Body> {
|
|
|
83
83
|
}
|
|
84
84
|
/**
|
|
85
85
|
* Invokes an Effect-backed queue consumer, drains its `waitUntil` work, and
|
|
86
|
-
* returns
|
|
86
|
+
* returns the Workers Vitest plugin's acknowledgement and retry result.
|
|
87
87
|
*/
|
|
88
88
|
declare const queue: <Body, Instance extends QueueWorker>(Worker: WorkerConstructor<Instance>, queueName: string, messages: readonly QueueMessage<Body>[], workerEnv?: Cloudflare.Env | undefined) => Effect.Effect<QueueRunResult<Body>, never, never>;
|
|
89
89
|
/**
|
|
@@ -110,7 +110,7 @@ declare const reset: () => Effect.Effect<void, never, never>;
|
|
|
110
110
|
declare const abortAllDurableObjects: () => Effect.Effect<void, never, never>;
|
|
111
111
|
/** Gracefully evicts all running Durable Objects while preserving durable storage. */
|
|
112
112
|
declare const evictAllDurableObjects: (options?: DurableObjectEvictionOptions | undefined) => Effect.Effect<void, never, never>;
|
|
113
|
-
/** A D1 migration accepted by
|
|
113
|
+
/** A D1 migration accepted by the Workers Vitest plugin. */
|
|
114
114
|
interface D1Migration {
|
|
115
115
|
readonly name: string;
|
|
116
116
|
readonly queries: ReadonlyArray<string>;
|
|
@@ -125,7 +125,7 @@ interface SecretsStoreSecret {
|
|
|
125
125
|
readonly uuid: string;
|
|
126
126
|
};
|
|
127
127
|
}
|
|
128
|
-
/** Effect-native wrapper around
|
|
128
|
+
/** Effect-native wrapper around the Workers Vitest plugin's Secrets Store admin API. */
|
|
129
129
|
interface SecretsStoreAdmin {
|
|
130
130
|
readonly raw: NativeSecretsStoreAdmin;
|
|
131
131
|
readonly create: (value: string) => Effect.Effect<string>;
|
package/dist/Vitest.mjs
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { a as WorkerEnvironment, i as WorkerConfig } from "./ErrorMessage-BM0K-Nik.mjs";
|
|
2
|
-
import { r as fromDurableObjectState, t as DurableObjectState } from "./DurableObjectState-
|
|
2
|
+
import { r as fromDurableObjectState, t as DurableObjectState } from "./DurableObjectState-CxS5Y2hf.mjs";
|
|
3
3
|
import { ConfigProvider, Effect, Exit, Layer } from "effect";
|
|
4
4
|
import { env } from "cloudflare:workers";
|
|
5
5
|
import { abortAllDurableObjects as abortAllDurableObjects$1, adminSecretsStore as adminSecretsStore$1, applyD1Migrations as applyD1Migrations$1, createExecutionContext, createMessageBatch, createPagesEventContext, createScheduledController, evictAllDurableObjects as evictAllDurableObjects$1, evictDurableObject as evictDurableObject$1, getQueueResult, introspectWorkflow as introspectWorkflow$1, introspectWorkflowInstance as introspectWorkflowInstance$1, listDurableObjectIds as listDurableObjectIds$1, reset as reset$1, runDurableObjectAlarm as runDurableObjectAlarm$1, runInDurableObject as runInDurableObject$1, waitOnExecutionContext } from "cloudflare:test";
|
|
6
6
|
//#region src/Vitest.ts
|
|
7
7
|
/**
|
|
8
|
-
* Effect-native helpers for tests running in Cloudflare's Vitest
|
|
8
|
+
* Effect-native helpers for tests running in Cloudflare's Workers Vitest plugin.
|
|
9
9
|
*
|
|
10
10
|
* Import this module through the `effect-cf/vitest` subpath. It is intended to
|
|
11
11
|
* run inside the Workers test runtime, where `cloudflare:test` is available.
|
|
12
12
|
*/
|
|
13
13
|
const currentEnv = env;
|
|
14
14
|
/**
|
|
15
|
-
* Provides the Workers
|
|
15
|
+
* Provides the Workers test environment as both {@link WorkerEnvironment} and
|
|
16
16
|
* the active Effect `ConfigProvider`.
|
|
17
17
|
*/
|
|
18
18
|
const layer = Layer.mergeAll(Layer.succeed(WorkerEnvironment, currentEnv), ConfigProvider.layer(WorkerConfig.providerFromEnv(currentEnv)));
|
|
@@ -50,7 +50,7 @@ const pages = Effect.fn("effect-cf/vitest/pages")(function* (handler, init) {
|
|
|
50
50
|
});
|
|
51
51
|
/**
|
|
52
52
|
* Invokes an Effect-backed queue consumer, drains its `waitUntil` work, and
|
|
53
|
-
* returns
|
|
53
|
+
* returns the Workers Vitest plugin's acknowledgement and retry result.
|
|
54
54
|
*/
|
|
55
55
|
const queue = Effect.fn("effect-cf/vitest/queue")(function* (Worker, queueName, messages, workerEnv = currentEnv) {
|
|
56
56
|
return yield* withExecutionContext(Effect.fnUntraced(function* (context) {
|
|
@@ -146,7 +146,7 @@ const runWorkflowModifier = Effect.fnUntraced(function* (register, use) {
|
|
|
146
146
|
if (callbackExit !== void 0 && Exit.isFailure(callbackExit)) return callbackExit;
|
|
147
147
|
throw cause;
|
|
148
148
|
}
|
|
149
|
-
if (callbackExit === void 0) throw new Error("
|
|
149
|
+
if (callbackExit === void 0) throw new Error("The Workers Vitest plugin did not invoke the Workflow modifier callback");
|
|
150
150
|
return callbackExit;
|
|
151
151
|
});
|
|
152
152
|
});
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { c as WorkerEnvironment, r as BindingValidationError, t as BindingNotFoundError } from "./Binding-xJJJPmiD.mjs";
|
|
2
|
-
import { x as ServiceFreeSchema } from "./DurableObjectState-
|
|
2
|
+
import { x as ServiceFreeSchema } from "./DurableObjectState-DVIgf0sA.mjs";
|
|
3
3
|
import { Context, Effect, Option, Schema } from "effect";
|
|
4
4
|
declare namespace WorkflowBinding_d_exports {
|
|
5
|
-
export { WorkflowBindingClient, WorkflowBindingDefinition, WorkflowInstance, WorkflowInstanceCreateBatchOptions, WorkflowInstanceCreateOptions, WorkflowInstanceEvent, WorkflowInstanceRestartOptions, WorkflowInstanceStatus, WorkflowInstanceStatusName, WorkflowOperationError, WorkflowResultDecodeError, isWorkflow, layer, makeClient };
|
|
5
|
+
export { WorkflowBindingClient, WorkflowBindingDefinition, WorkflowInstance, WorkflowInstanceCreateBatchOptions, WorkflowInstanceCreateOptions, WorkflowInstanceEvent, WorkflowInstanceLocationHint, WorkflowInstanceRestartOptions, WorkflowInstanceStatus, WorkflowInstanceStatusName, WorkflowInstanceTerminateOptions, WorkflowOperationError, WorkflowResultDecodeError, isWorkflow, layer, makeClient };
|
|
6
6
|
}
|
|
7
7
|
type WorkflowInstanceCreateOptions<Payload> = Omit<CloudflareWorkflowInstanceCreateOptions<Payload>, "params">;
|
|
8
8
|
type WorkflowInstanceCreateBatchOptions<Payload, EncodedPayload = unknown> = ReadonlyArray<{
|
|
9
9
|
readonly payload: Payload;
|
|
10
10
|
} & WorkflowInstanceCreateOptions<EncodedPayload>>;
|
|
11
11
|
type WorkflowInstanceRestartOptions = CloudflareWorkflowInstanceRestartOptions;
|
|
12
|
+
type WorkflowInstanceTerminateOptions = CloudflareWorkflowInstanceTerminateOptions;
|
|
13
|
+
type WorkflowInstanceLocationHint = CloudflareWorkflowInstanceLocationHint;
|
|
12
14
|
type WorkflowInstanceStatusName = CloudflareInstanceStatus["status"];
|
|
13
15
|
interface WorkflowInstanceStatus<Result> {
|
|
14
16
|
readonly status: WorkflowInstanceStatusName;
|
|
@@ -23,8 +25,9 @@ interface WorkflowInstance<Result> {
|
|
|
23
25
|
readonly id: string;
|
|
24
26
|
readonly pause: Effect.Effect<void, WorkflowOperationError>;
|
|
25
27
|
readonly resume: Effect.Effect<void, WorkflowOperationError>;
|
|
26
|
-
readonly terminate: Effect.Effect<void, WorkflowOperationError>;
|
|
28
|
+
readonly terminate: (options?: WorkflowInstanceTerminateOptions) => Effect.Effect<void, WorkflowOperationError>;
|
|
27
29
|
readonly restart: (options?: WorkflowInstanceRestartOptions) => Effect.Effect<void, WorkflowOperationError>;
|
|
30
|
+
readonly delete: Effect.Effect<void, WorkflowOperationError>;
|
|
28
31
|
readonly status: Effect.Effect<WorkflowInstanceStatus<Result>, WorkflowOperationError | WorkflowResultDecodeError>;
|
|
29
32
|
readonly sendEvent: (event: WorkflowInstanceEvent) => Effect.Effect<void, WorkflowOperationError>;
|
|
30
33
|
}
|