beachside 0.8.0 → 0.9.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/README.md +12 -4
- package/dist/file-transfer.d.ts +6 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +209 -31
- package/dist/resource.d.ts +21 -2
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -6,17 +6,25 @@ Install `beachside @beachside/daytona` (or `beachside @beachside/e2b`).
|
|
|
6
6
|
|
|
7
7
|
```ts
|
|
8
8
|
import { Beachside } from "beachside";
|
|
9
|
-
import {
|
|
9
|
+
import { daytonaAdapter } from "@beachside/daytona";
|
|
10
10
|
|
|
11
|
-
const beachside = await Beachside.connect(
|
|
11
|
+
const beachside = await Beachside.connect({
|
|
12
|
+
adapter: daytonaAdapter,
|
|
13
|
+
credentials: { apiKey: process.env.DAYTONA_API_KEY! },
|
|
14
|
+
config: { target: "us" },
|
|
15
|
+
});
|
|
12
16
|
await beachside.close();
|
|
13
17
|
```
|
|
14
18
|
|
|
15
19
|
```ts
|
|
16
20
|
import { Beachside, Image } from "beachside";
|
|
17
|
-
import {
|
|
21
|
+
import { daytonaAdapter } from "@beachside/daytona";
|
|
18
22
|
|
|
19
|
-
const beachside = await Beachside.connect(
|
|
23
|
+
const beachside = await Beachside.connect({
|
|
24
|
+
adapter: daytonaAdapter,
|
|
25
|
+
credentials: { apiKey: process.env.DAYTONA_API_KEY! },
|
|
26
|
+
config: { target: "us" },
|
|
27
|
+
});
|
|
20
28
|
try {
|
|
21
29
|
const box = await beachside.sandboxes.create({ environment: Image.prepared("your-snapshot-id") });
|
|
22
30
|
try {
|
package/dist/file-transfer.d.ts
CHANGED
|
@@ -22,7 +22,13 @@ export declare class FilesystemError extends BeachsideError {
|
|
|
22
22
|
}
|
|
23
23
|
export declare function filesystemError(error: unknown): Error;
|
|
24
24
|
export declare function byteLimit(value: number | undefined, defaultValue?: number): number | undefined;
|
|
25
|
+
/**
|
|
26
|
+
* The effective limit of one buffered adapter read or write: the caller's optional `maxBytes`
|
|
27
|
+
* guard, bounded by the adapter's own buffered limit. Larger files use the adapter's streams.
|
|
28
|
+
*/
|
|
25
29
|
export declare function bufferedLimit(value: number | undefined, adapterLimit: number): number;
|
|
30
|
+
/** The error for a file larger than an adapter without file streams can move in one call. */
|
|
31
|
+
export declare function adapterFileLimit(adapterLimit: number, code: "CAPACITY" | "OUTPUT_CAPACITY"): BeachsideError;
|
|
26
32
|
export declare function streamFile(read: (ctx: ReadContext) => Promise<ReadableStream<Uint8Array>>, closed: AbortSignal, options: FileStreamOptions, defaults: TransferPolicy): AsyncIterable<Uint8Array>;
|
|
27
33
|
export declare function writeStream(write: (bytes: AsyncIterable<Uint8Array>, ctx: FileTransferContext) => Promise<{
|
|
28
34
|
bytesWritten: number;
|
package/dist/index.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ export declare const Beachside: {
|
|
|
6
6
|
connect: typeof connectDirect;
|
|
7
7
|
};
|
|
8
8
|
export { Image, outputText, BeachsideError, UnsupportedFeatureError, OutcomeUnknownError, WaitAbortedError, NonzeroExitError, NoExitCodeError, } from "./resource.js";
|
|
9
|
-
export type { ImageInput, PreparedImage, ImageBuildResult, CreateInput, NetworkPolicy, ExecInput, ExecOutput, OutputPreview, DirectBeachsideClient, DirectSandboxHandle, SandboxHandle, SnapshotHandle, ImageHandle, VolumeHandle, ReadOptions, OperationHandle, RecoveryReference, BeachsideClient, } from "./resource.js";
|
|
9
|
+
export type { ImageInput, PreparedImage, ImageBuildResult, CreateInput, NetworkPolicy, ExecInput, ExecStdinStream, ExecOutput, OutputPreview, DirectBeachsideClient, DirectSandboxHandle, SandboxHandle, SnapshotHandle, ImageHandle, VolumeHandle, ReadOptions, OperationHandle, RecoveryReference, BeachsideClient, } from "./resource.js";
|
|
10
10
|
export { ResourceReference, ResourceKind, validateResourceReference, assertResourceScope, assertResourceIdentity, SnapshotRequest, SnapshotRequirements, Preserve, } from "@beachside/adapter";
|
|
11
11
|
export type { Support, Capabilities, DirectCapabilities, OperationOutcome, SnapshotPlan, SnapshotProfile, SandboxState, SandboxReference, SuspendResult, ResumeResult, RenewRequest, RenewResult, Preview, Fact, Deadline, ResourceMinimums, SandboxResources, CreatePlan, } from "@beachside/adapter";
|
|
12
12
|
export { AdapterSnapshot, AdapterVolume, AdapterImage } from "./resources.js";
|
package/dist/index.js
CHANGED
|
@@ -26,6 +26,97 @@ var Image = {
|
|
|
26
26
|
return { kind: "oci", value };
|
|
27
27
|
}
|
|
28
28
|
};
|
|
29
|
+
function isStdinStream(value) {
|
|
30
|
+
return value instanceof ReadableStream || value !== null && typeof value === "object" && !(value instanceof Uint8Array) && Symbol.asyncIterator in value;
|
|
31
|
+
}
|
|
32
|
+
function stdinNeedsStream(stdin) {
|
|
33
|
+
if (stdin === undefined)
|
|
34
|
+
return false;
|
|
35
|
+
if (isStdinStream(stdin))
|
|
36
|
+
return true;
|
|
37
|
+
if (stdin instanceof Uint8Array)
|
|
38
|
+
return stdin.byteLength > MAX_EXEC_STDIN_BYTES;
|
|
39
|
+
return stdin.length * 3 > MAX_EXEC_STDIN_BYTES && utf8Bytes(stdin) > MAX_EXEC_STDIN_BYTES;
|
|
40
|
+
}
|
|
41
|
+
function utf8Bytes(text) {
|
|
42
|
+
return new TextEncoder().encode(text).byteLength;
|
|
43
|
+
}
|
|
44
|
+
function streamedStdin(stdin) {
|
|
45
|
+
if (stdin === undefined || !stdinNeedsStream(stdin))
|
|
46
|
+
return;
|
|
47
|
+
if (stdin instanceof ReadableStream)
|
|
48
|
+
return readableSource(stdin);
|
|
49
|
+
if (isStdinStream(stdin))
|
|
50
|
+
return iterableSource(stdin);
|
|
51
|
+
let bytes = stdin instanceof Uint8Array ? Uint8Array.from(stdin) : new TextEncoder().encode(stdin);
|
|
52
|
+
return {
|
|
53
|
+
async next() {
|
|
54
|
+
const chunk = bytes;
|
|
55
|
+
bytes = null;
|
|
56
|
+
return chunk;
|
|
57
|
+
},
|
|
58
|
+
cancel() {
|
|
59
|
+
bytes = null;
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function stdinChunk(value) {
|
|
64
|
+
if (!(value instanceof Uint8Array))
|
|
65
|
+
throw new BeachsideError("INVALID_ARGUMENT", "Expected stdin stream byte chunks");
|
|
66
|
+
return value;
|
|
67
|
+
}
|
|
68
|
+
function readableSource(stream) {
|
|
69
|
+
let reader;
|
|
70
|
+
let cancelled = false;
|
|
71
|
+
return {
|
|
72
|
+
async next() {
|
|
73
|
+
if (cancelled)
|
|
74
|
+
return null;
|
|
75
|
+
reader ??= stream.getReader();
|
|
76
|
+
const part = await reader.read();
|
|
77
|
+
if (!part.done)
|
|
78
|
+
return stdinChunk(part.value);
|
|
79
|
+
cancelled = true;
|
|
80
|
+
reader.releaseLock();
|
|
81
|
+
return null;
|
|
82
|
+
},
|
|
83
|
+
cancel() {
|
|
84
|
+
if (cancelled)
|
|
85
|
+
return;
|
|
86
|
+
cancelled = true;
|
|
87
|
+
if (!reader) {
|
|
88
|
+
stream.cancel().catch(() => {
|
|
89
|
+
return;
|
|
90
|
+
});
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
const owned = reader;
|
|
94
|
+
owned.cancel().catch(() => {
|
|
95
|
+
return;
|
|
96
|
+
}).finally(() => owned.releaseLock());
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
function iterableSource(iterable) {
|
|
101
|
+
const iterator = iterable[Symbol.asyncIterator]();
|
|
102
|
+
let cancelled = false;
|
|
103
|
+
return {
|
|
104
|
+
async next() {
|
|
105
|
+
if (cancelled)
|
|
106
|
+
return null;
|
|
107
|
+
const part = await iterator.next();
|
|
108
|
+
return part.done ? null : stdinChunk(part.value);
|
|
109
|
+
},
|
|
110
|
+
cancel() {
|
|
111
|
+
if (cancelled)
|
|
112
|
+
return;
|
|
113
|
+
cancelled = true;
|
|
114
|
+
Promise.resolve().then(() => iterator.return?.()).catch(() => {
|
|
115
|
+
return;
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
}
|
|
29
120
|
var ExecStdinInput = z.union([
|
|
30
121
|
z.string().max(MAX_EXEC_STDIN_BYTES).transform((value) => ({ kind: "text", value })),
|
|
31
122
|
z.instanceof(Uint8Array).refine((value) => value.byteLength <= MAX_EXEC_STDIN_BYTES).transform((value) => ({ kind: "bytes", value }))
|
|
@@ -229,6 +320,8 @@ function validateExec(input, options = {}) {
|
|
|
229
320
|
if (options.allowStdin === false && !isArgumentArray(input) && request?.stdin !== undefined)
|
|
230
321
|
throw new BeachsideError("INVALID_ARGUMENT", "stdin is available only for finite exec input");
|
|
231
322
|
let stdin;
|
|
323
|
+
if (request?.stdin !== undefined && isStdinStream(request.stdin))
|
|
324
|
+
throw new BeachsideError("INVALID_ARGUMENT", "Streamed stdin is available through exec(), not as finite input");
|
|
232
325
|
if (request?.stdin !== undefined) {
|
|
233
326
|
const parsedInput = ExecStdinInput.safeParse(request.stdin);
|
|
234
327
|
if (!parsedInput.success)
|
|
@@ -418,10 +511,12 @@ class FilesystemError extends BeachsideError {
|
|
|
418
511
|
}
|
|
419
512
|
}
|
|
420
513
|
function filesystemError(error) {
|
|
421
|
-
if (error instanceof
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
514
|
+
if (error instanceof AdapterError) {
|
|
515
|
+
const mapped = error instanceof AdapterFilesystemError ? new FilesystemError(error.code, error.message, error.details) : new BeachsideError(error.code, error.message);
|
|
516
|
+
if (error.cause !== undefined)
|
|
517
|
+
mapped.cause = error.cause;
|
|
518
|
+
return mapped;
|
|
519
|
+
}
|
|
425
520
|
return error instanceof Error ? error : new Error("Filesystem operation failed", { cause: error });
|
|
426
521
|
}
|
|
427
522
|
function byteLimit(value, defaultValue) {
|
|
@@ -431,10 +526,12 @@ function byteLimit(value, defaultValue) {
|
|
|
431
526
|
return limit;
|
|
432
527
|
}
|
|
433
528
|
function bufferedLimit(value, adapterLimit) {
|
|
434
|
-
const limit = byteLimit(value
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
529
|
+
const limit = byteLimit(value);
|
|
530
|
+
const adapter = byteLimit(adapterLimit);
|
|
531
|
+
return limit === undefined ? adapter : Math.min(limit, adapter);
|
|
532
|
+
}
|
|
533
|
+
function adapterFileLimit(adapterLimit, code) {
|
|
534
|
+
return new BeachsideError(code, `File exceeds this adapter's ${adapterLimit}-byte file limit; it has no file streams`, code === "OUTPUT_CAPACITY" ? "unknown" : "none");
|
|
438
535
|
}
|
|
439
536
|
|
|
440
537
|
class Transfer {
|
|
@@ -892,7 +989,7 @@ function certifiedRecoveryAvailable(reference) {
|
|
|
892
989
|
// package.json
|
|
893
990
|
var package_default = {
|
|
894
991
|
name: "beachside",
|
|
895
|
-
version: "0.
|
|
992
|
+
version: "0.9.0",
|
|
896
993
|
private: false,
|
|
897
994
|
type: "module",
|
|
898
995
|
files: [
|
|
@@ -2597,9 +2694,15 @@ async function callbackExec(client, sandbox, controlActive, input, options) {
|
|
|
2597
2694
|
const callback = options.onOutput;
|
|
2598
2695
|
if (typeof callback !== "function")
|
|
2599
2696
|
throw new BeachsideError("INVALID_ARGUMENT", "Expected execution output callback");
|
|
2600
|
-
const
|
|
2601
|
-
|
|
2697
|
+
const object = Array.isArray(input) || !("command" in input) ? undefined : input;
|
|
2698
|
+
const streamed = streamedStdin(object?.stdin);
|
|
2699
|
+
const request = validateExec(streamed && object ? { ...object, stdin: undefined } : input);
|
|
2700
|
+
const deadlineSeconds = object?.deadlineSeconds;
|
|
2701
|
+
const localDeadline = streamed ? deadlineSeconds ?? request.deadlineSeconds : undefined;
|
|
2702
|
+
if (!streamed && deadlineSeconds !== undefined)
|
|
2602
2703
|
throw new BeachsideError("UNSUPPORTED", "Callback execution cannot map deadlineSeconds on the process transport; use signal to bound local observation");
|
|
2704
|
+
if (streamed && client.session.processes?.supports?.stdin !== "bytes")
|
|
2705
|
+
throw new BeachsideError("UNSUPPORTED", "Streamed stdin and stdin over 1 MiB need incremental process stdin, which this adapter lacks");
|
|
2603
2706
|
const maxOutputBytes = client.session.supports.exec?.maxOutputBytes;
|
|
2604
2707
|
if (maxOutputBytes !== undefined && request.maxOutputBytes > maxOutputBytes)
|
|
2605
2708
|
throw new BeachsideError("UNSUPPORTED", "Requested execution output limit is unsupported");
|
|
@@ -2610,7 +2713,7 @@ async function callbackExec(client, sandbox, controlActive, input, options) {
|
|
|
2610
2713
|
cwd: request.cwd,
|
|
2611
2714
|
env: request.env,
|
|
2612
2715
|
output: { mode: "stream" },
|
|
2613
|
-
stdin: request.stdin === undefined ? "closed" : "pipe"
|
|
2716
|
+
stdin: request.stdin === undefined && !streamed ? "closed" : "pipe"
|
|
2614
2717
|
}, options, controlActive, { maxBytes: request.maxOutputBytes });
|
|
2615
2718
|
const observation = new AbortController;
|
|
2616
2719
|
const signal = AbortSignal.any([
|
|
@@ -2654,20 +2757,58 @@ async function callbackExec(client, sandbox, controlActive, input, options) {
|
|
|
2654
2757
|
await raceAbort(Promise.resolve().then(invoke), signal);
|
|
2655
2758
|
}
|
|
2656
2759
|
})().catch(stop);
|
|
2760
|
+
const source = streamed ?? (request.stdin === undefined ? undefined : (() => {
|
|
2761
|
+
let bytes = request.stdin;
|
|
2762
|
+
return {
|
|
2763
|
+
async next() {
|
|
2764
|
+
const chunk = bytes;
|
|
2765
|
+
bytes = null;
|
|
2766
|
+
return chunk;
|
|
2767
|
+
},
|
|
2768
|
+
cancel() {
|
|
2769
|
+
bytes = null;
|
|
2770
|
+
}
|
|
2771
|
+
};
|
|
2772
|
+
})());
|
|
2773
|
+
let delivered = false;
|
|
2657
2774
|
const delivery = (async () => {
|
|
2658
|
-
if (
|
|
2659
|
-
|
|
2775
|
+
if (source === undefined)
|
|
2776
|
+
return;
|
|
2777
|
+
for (let chunk = await source.next();chunk; chunk = await source.next())
|
|
2778
|
+
for (let offset = 0;offset < chunk.length; offset += 65536) {
|
|
2660
2779
|
if (signal.aborted)
|
|
2661
2780
|
throw signal.reason;
|
|
2662
|
-
|
|
2781
|
+
if (streamed && exit)
|
|
2782
|
+
return;
|
|
2783
|
+
await process.write(chunk.subarray(offset, offset + 65536), { signal });
|
|
2663
2784
|
}
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
}
|
|
2668
|
-
})().
|
|
2785
|
+
if (signal.aborted)
|
|
2786
|
+
throw signal.reason;
|
|
2787
|
+
delivered = true;
|
|
2788
|
+
await process.closeStdin({ signal });
|
|
2789
|
+
})().then(() => {
|
|
2790
|
+
return;
|
|
2791
|
+
}, (error) => {
|
|
2792
|
+
if (streamed && exit)
|
|
2793
|
+
return;
|
|
2794
|
+
stop(error);
|
|
2795
|
+
});
|
|
2796
|
+
const deadline = localDeadline === undefined ? undefined : setTimeout(() => {
|
|
2797
|
+
initiatingFailure ??= {
|
|
2798
|
+
error: new BeachsideError("TIMEOUT", `Execution exceeded deadlineSeconds (${localDeadline}); termination was requested`, "possible")
|
|
2799
|
+
};
|
|
2800
|
+
observation.abort();
|
|
2801
|
+
process.terminate({ signal: AbortSignal.timeout(30000) }).catch(() => {
|
|
2802
|
+
return;
|
|
2803
|
+
});
|
|
2804
|
+
}, localDeadline * 1000);
|
|
2669
2805
|
try {
|
|
2670
|
-
await Promise.all([
|
|
2806
|
+
await Promise.all([
|
|
2807
|
+
streamed ? Promise.race([delivery, waiting]) : delivery,
|
|
2808
|
+
output,
|
|
2809
|
+
waiting,
|
|
2810
|
+
raceAbort(capture, signal)
|
|
2811
|
+
]);
|
|
2671
2812
|
if (captured.exitCode !== exit.exitCode)
|
|
2672
2813
|
throw new BeachsideError("INVALID_RESPONSE", "Execution capture conflicts with confirmed exit", "possible");
|
|
2673
2814
|
return checkExec(captured);
|
|
@@ -2691,6 +2832,9 @@ async function callbackExec(client, sandbox, controlActive, input, options) {
|
|
|
2691
2832
|
context2.output = captured;
|
|
2692
2833
|
throw context2;
|
|
2693
2834
|
} finally {
|
|
2835
|
+
clearTimeout(deadline);
|
|
2836
|
+
if (!delivered)
|
|
2837
|
+
source?.cancel();
|
|
2694
2838
|
await process.detach();
|
|
2695
2839
|
}
|
|
2696
2840
|
}
|
|
@@ -3292,7 +3436,7 @@ function fileOptions(options) {
|
|
|
3292
3436
|
function fileReadLimit(maxBytes) {
|
|
3293
3437
|
if (!Number.isSafeInteger(maxBytes) || maxBytes < 0)
|
|
3294
3438
|
throw new BeachsideError("INVALID_ARGUMENT", "Adapter file limit is invalid", "none");
|
|
3295
|
-
return
|
|
3439
|
+
return maxBytes;
|
|
3296
3440
|
}
|
|
3297
3441
|
function abortWaiting(ref, reason, outcome) {
|
|
3298
3442
|
throw new WaitAbortedError(ref, reason, outcome ?? acknowledgedLifecycleOutcome(ref));
|
|
@@ -3948,10 +4092,13 @@ class AdapterSandbox {
|
|
|
3948
4092
|
});
|
|
3949
4093
|
}
|
|
3950
4094
|
async exec(input, options = {}) {
|
|
3951
|
-
|
|
4095
|
+
const streamed = !Array.isArray(input) && "command" in input && stdinNeedsStream(input.stdin);
|
|
4096
|
+
if (options.onOutput !== undefined || streamed) {
|
|
3952
4097
|
this.client.ensureOpen();
|
|
3953
4098
|
const control = processControl(this.client, this.id);
|
|
3954
|
-
return callbackExec(this.client, sandboxInput(this.id, this.reference), () => control.active, input, options)
|
|
4099
|
+
return callbackExec(this.client, sandboxInput(this.id, this.reference), () => control.active, input, options.onOutput ? options : { ...options, onOutput: () => {
|
|
4100
|
+
return;
|
|
4101
|
+
} });
|
|
3955
4102
|
}
|
|
3956
4103
|
return waitFor(this.client.telemetry, await internalMethod(this.submitExec)(input, options), options);
|
|
3957
4104
|
}
|
|
@@ -4140,12 +4287,38 @@ class AdapterSandbox {
|
|
|
4140
4287
|
}
|
|
4141
4288
|
async readFile(path, options = {}) {
|
|
4142
4289
|
this.client.ensureOpen();
|
|
4143
|
-
|
|
4290
|
+
const files = this.client.session.files;
|
|
4291
|
+
if (!files?.read)
|
|
4144
4292
|
unsupported("readFile");
|
|
4145
4293
|
validateFilePath(path);
|
|
4146
|
-
const
|
|
4147
|
-
const
|
|
4148
|
-
|
|
4294
|
+
const guard = byteLimit(options.maxBytes);
|
|
4295
|
+
const maxBytes = bufferedLimit(guard, files.maxBytes);
|
|
4296
|
+
const read = files.read.bind(files);
|
|
4297
|
+
try {
|
|
4298
|
+
return await readFileBytes((context2) => read({ sandbox: sandboxInput(this.id, this.reference), path, maxBytes }, context2), maxBytes, this.client.signal, options);
|
|
4299
|
+
} catch (error) {
|
|
4300
|
+
const adapterBound = guard === undefined || guard > files.maxBytes;
|
|
4301
|
+
if (!(error instanceof BeachsideError) || error.code !== "OUTPUT_CAPACITY" || !adapterBound)
|
|
4302
|
+
throw error;
|
|
4303
|
+
if (!files.readStream)
|
|
4304
|
+
throw adapterFileLimit(files.maxBytes, "OUTPUT_CAPACITY");
|
|
4305
|
+
}
|
|
4306
|
+
const chunks = [];
|
|
4307
|
+
let total = 0;
|
|
4308
|
+
for await (const chunk of this.readFileStream(path, {
|
|
4309
|
+
signal: options.signal,
|
|
4310
|
+
maxBytes: guard
|
|
4311
|
+
})) {
|
|
4312
|
+
chunks.push(chunk);
|
|
4313
|
+
total += chunk.byteLength;
|
|
4314
|
+
}
|
|
4315
|
+
const bytes = new Uint8Array(total);
|
|
4316
|
+
let offset = 0;
|
|
4317
|
+
for (const chunk of chunks) {
|
|
4318
|
+
bytes.set(chunk, offset);
|
|
4319
|
+
offset += chunk.byteLength;
|
|
4320
|
+
}
|
|
4321
|
+
return bytes;
|
|
4149
4322
|
}
|
|
4150
4323
|
async writeFile(path, bytes, options = {}) {
|
|
4151
4324
|
if (!this.supports("writeFile"))
|
|
@@ -4153,10 +4326,13 @@ class AdapterSandbox {
|
|
|
4153
4326
|
validateFilePath(path);
|
|
4154
4327
|
if (!(bytes instanceof Uint8Array))
|
|
4155
4328
|
throw new BeachsideError("INVALID_ARGUMENT", "Expected byte buffer");
|
|
4156
|
-
const
|
|
4157
|
-
|
|
4329
|
+
const files = this.client.session.files;
|
|
4330
|
+
const guard = byteLimit(options.maxBytes);
|
|
4331
|
+
if (guard !== undefined && bytes.length > guard)
|
|
4158
4332
|
throw new BeachsideError("CAPACITY", "File exceeds byte limit");
|
|
4159
|
-
if (bytes.length >
|
|
4333
|
+
if (bytes.length > files.maxBytes && !files.writeStream)
|
|
4334
|
+
throw adapterFileLimit(files.maxBytes, "CAPACITY");
|
|
4335
|
+
if ((bytes.length > 1048576 || bytes.length > files.maxBytes) && files.writeStream) {
|
|
4160
4336
|
const payload2 = Uint8Array.from(bytes);
|
|
4161
4337
|
async function* chunks() {
|
|
4162
4338
|
yield payload2;
|
|
@@ -5030,6 +5206,8 @@ class AdapterDirectClient {
|
|
|
5030
5206
|
}
|
|
5031
5207
|
}
|
|
5032
5208
|
async function connectDirect(options, observability = {}) {
|
|
5209
|
+
if (options instanceof Function || "adapter" in options && options.adapter instanceof Function)
|
|
5210
|
+
throw new BeachsideError("INVALID_ARGUMENT", "Expected an adapter definition, got a function: pass the definition (`adapter: vercelAdapter`) or call the provider factory (`Beachside.connect(vercel({ ... }))`)", "none");
|
|
5033
5211
|
const cleanup = z8.strictObject({ storage: z8.enum(["require-durable", "allow-unconfirmed"]).optional() }).optional().safeParse(("bound" in options ? observability : options).cleanup);
|
|
5034
5212
|
if (!cleanup.success)
|
|
5035
5213
|
throw new BeachsideError("INVALID_ARGUMENT", "Invalid cleanup configuration", "none");
|
package/dist/resource.d.ts
CHANGED
|
@@ -58,12 +58,31 @@ export type ExecInput = {
|
|
|
58
58
|
command: ExecCommand;
|
|
59
59
|
cwd?: string;
|
|
60
60
|
env?: Record<string, string>;
|
|
61
|
-
/**
|
|
62
|
-
|
|
61
|
+
/**
|
|
62
|
+
* Command input, ending with guest EOF. Strings encode as UTF-8. A stream (`AsyncIterable` or
|
|
63
|
+
* `ReadableStream` of bytes) is sent as it arrives, with no size limit, while the command runs;
|
|
64
|
+
* streams and input over 1 MiB need an adapter with incremental process stdin.
|
|
65
|
+
*/
|
|
66
|
+
stdin?: string | Uint8Array | ExecStdinStream;
|
|
63
67
|
/** Provider-specific execution/observation limit (default 300), not a total wait budget. Use the call signal to bound local waiting. */
|
|
64
68
|
deadlineSeconds?: number;
|
|
65
69
|
maxOutputBytes?: number;
|
|
66
70
|
};
|
|
71
|
+
/** Streamed command input: any async iterable or web stream of byte chunks. */
|
|
72
|
+
export type ExecStdinStream = AsyncIterable<Uint8Array> | ReadableStream<Uint8Array>;
|
|
73
|
+
export declare function isStdinStream(value: ExecInput["stdin"]): value is ExecStdinStream;
|
|
74
|
+
/** Whether stdin must travel incrementally: a stream, or bytes or text over the finite bound. */
|
|
75
|
+
export declare function stdinNeedsStream(stdin: ExecInput["stdin"]): boolean;
|
|
76
|
+
/**
|
|
77
|
+
* Input that must travel incrementally, read one chunk at a time. `cancel` stops the source when
|
|
78
|
+
* the command no longer needs it, releasing a web stream's reader even while a read is pending.
|
|
79
|
+
*/
|
|
80
|
+
export type StdinSource = {
|
|
81
|
+
next(): Promise<Uint8Array | null>;
|
|
82
|
+
cancel(): void;
|
|
83
|
+
};
|
|
84
|
+
/** The streamed source for `stdin`, or undefined for finite input that fits the bound. */
|
|
85
|
+
export declare function streamedStdin(stdin: ExecInput["stdin"]): StdinSource | undefined;
|
|
67
86
|
export type ValidatedExecInput = Required<Pick<ExecInput, "command" | "deadlineSeconds" | "maxOutputBytes">> & Omit<ExecInput, "stdin"> & {
|
|
68
87
|
stdin?: Uint8Array;
|
|
69
88
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "beachside",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -19,14 +19,14 @@
|
|
|
19
19
|
"build": "rm -rf dist && bun build src/index.ts --target node --format esm --splitting --outdir dist --external @beachside/adapter --external zod --external @opentelemetry/api && tsc -p tsconfig.json --noEmit false --declaration --emitDeclarationOnly --outDir dist && bun ../../scripts/rewrite-declarations.mjs"
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@beachside/adapter": "0.
|
|
22
|
+
"@beachside/adapter": "0.9.0",
|
|
23
23
|
"zod": "4.6.5",
|
|
24
24
|
"@opentelemetry/api": "1.9.1"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"@beachside-internal/provider-fake": "0.0.0",
|
|
28
|
-
"@beachside/daytona": "0.
|
|
29
|
-
"@beachside/e2b": "0.
|
|
28
|
+
"@beachside/daytona": "0.9.0",
|
|
29
|
+
"@beachside/e2b": "0.9.0"
|
|
30
30
|
},
|
|
31
31
|
"license": "Apache-2.0",
|
|
32
32
|
"repository": {
|