@phpsandbox/sdk 0.6.0-canary.2 → 0.6.0-canary.4
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 +29 -0
- package/dist/browser/phpsandbox-sdk.esm.js +174 -18
- package/dist/browser/phpsandbox-sdk.esm.min.js +9 -9
- package/dist/browser/phpsandbox-sdk.iife.js +174 -18
- package/dist/browser/phpsandbox-sdk.iife.min.js +9 -9
- package/dist/feedback.d.ts +19 -4
- package/dist/feedback.js +17 -5
- package/dist/filesystem.d.ts +2 -2
- package/dist/filesystem.js +4 -13
- package/dist/index.d.ts +14 -3
- package/dist/index.js +23 -4
- package/dist/runtime/rest.d.ts +7 -0
- package/dist/runtime/rest.js +122 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -209,6 +209,32 @@ const [hasMore, matches] = await notebook.files.search(
|
|
|
209
209
|
);
|
|
210
210
|
```
|
|
211
211
|
|
|
212
|
+
`files.download()` returns a `ReadableStream<Uint8Array>` containing the notebook ZIP.
|
|
213
|
+
Read it incrementally or pipe it to a writable stream:
|
|
214
|
+
|
|
215
|
+
```ts
|
|
216
|
+
const controller = new AbortController();
|
|
217
|
+
const archive = notebook.files.download({
|
|
218
|
+
include: ['src/**'],
|
|
219
|
+
exclude: ['vendor/**'],
|
|
220
|
+
signal: controller.signal,
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
for await (const chunk of archive) {
|
|
224
|
+
await consume(chunk);
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Downloads use HTTP for both realtime and REST clients and require the runtime's
|
|
229
|
+
`POST /api/v1/notebooks/{notebook}/runtime/files/download` endpoint. Reading starts
|
|
230
|
+
the request; cancelling the stream, aborting its signal, or disposing the notebook
|
|
231
|
+
stops the transfer. Failures reject stream reads and are not retried after bytes
|
|
232
|
+
have started arriving.
|
|
233
|
+
|
|
234
|
+
**Breaking change in 0.6.0-canary.4:** downloads no longer return a `Blob` or accept
|
|
235
|
+
`onChunk`. If you need a Blob, explicitly collect the archive with
|
|
236
|
+
`await new Response(notebook.files.download()).blob()`; this buffers the ZIP in memory.
|
|
237
|
+
|
|
212
238
|
### Terminal
|
|
213
239
|
|
|
214
240
|
```ts
|
|
@@ -378,6 +404,9 @@ await notebook.mail.disable();
|
|
|
378
404
|
### Feedback
|
|
379
405
|
|
|
380
406
|
```ts
|
|
407
|
+
// Feedback uses the same API token as the rest of the SDK.
|
|
408
|
+
// You can also address it directly with client.feedback.forProject(projectId)
|
|
409
|
+
// or client.feedback.forNotebook(notebookId), without fetching a notebook.
|
|
381
410
|
await notebook.feedback.configure({
|
|
382
411
|
widget: {
|
|
383
412
|
targets: ['preview', 'publication'],
|
|
@@ -16372,17 +16372,14 @@ var Filesystem = class {
|
|
|
16372
16372
|
exists(path) {
|
|
16373
16373
|
return this.stat(path).then(() => true).catch(() => false);
|
|
16374
16374
|
}
|
|
16375
|
-
|
|
16376
|
-
|
|
16377
|
-
|
|
16378
|
-
|
|
16379
|
-
|
|
16380
|
-
|
|
16381
|
-
stream ? options.onChunk : (data) => {
|
|
16382
|
-
chunks.push(data);
|
|
16375
|
+
download(options = {}) {
|
|
16376
|
+
return this.okra.stream(
|
|
16377
|
+
"fs.download",
|
|
16378
|
+
{ include: options.include, exclude: options.exclude },
|
|
16379
|
+
{
|
|
16380
|
+
abortSignal: options.signal
|
|
16383
16381
|
}
|
|
16384
16382
|
);
|
|
16385
|
-
return this.okra.invoke("fs.download", { id, exclude: options.exclude, include: options.include }).then(() => new Blob(chunks, { type: "application/octet-stream" })).finally(() => disposable.dispose());
|
|
16386
16383
|
}
|
|
16387
16384
|
tree(path = "/") {
|
|
16388
16385
|
return this.okra.invoke("fs.tree", { path });
|
|
@@ -20343,6 +20340,7 @@ var restActionRoutes = {
|
|
|
20343
20340
|
"shell.exec": { method: "POST", path: "/runtime/executions" },
|
|
20344
20341
|
"fs.readFile": { method: "GET", path: "/runtime/files/content" },
|
|
20345
20342
|
"fs.writeFile": { method: "PUT", path: "/runtime/files/content" },
|
|
20343
|
+
"fs.download": { method: "POST", path: "/runtime/files/download" },
|
|
20346
20344
|
"fs.createDirectory": { method: "PUT", path: "/runtime/directories" },
|
|
20347
20345
|
"fs.readDirectory": { method: "POST", path: "/runtime/directories/read" },
|
|
20348
20346
|
"fs.stat": { method: "POST", path: "/runtime/files/stat" },
|
|
@@ -20379,6 +20377,7 @@ var RestRuntimeInvoker = class {
|
|
|
20379
20377
|
this.fetch = fetch;
|
|
20380
20378
|
this.refresh = refresh;
|
|
20381
20379
|
this.refreshPromise = null;
|
|
20380
|
+
this.streams = /* @__PURE__ */ new Set();
|
|
20382
20381
|
this.notebookId = data.id;
|
|
20383
20382
|
this.configure(data.runtimeUrl);
|
|
20384
20383
|
}
|
|
@@ -20411,6 +20410,127 @@ var RestRuntimeInvoker = class {
|
|
|
20411
20410
|
send() {
|
|
20412
20411
|
throw new PHPSandboxError("Fire-and-forget actions require the realtime transport.");
|
|
20413
20412
|
}
|
|
20413
|
+
dispose() {
|
|
20414
|
+
for (const stream of this.streams) {
|
|
20415
|
+
stream.abort(new DOMException("Notebook disposed", "AbortError"));
|
|
20416
|
+
}
|
|
20417
|
+
}
|
|
20418
|
+
stream(action, data, options = {}) {
|
|
20419
|
+
const cancellation = new AbortController();
|
|
20420
|
+
this.streams.add(cancellation);
|
|
20421
|
+
const abort = runtimeAbort({ ...options, abortSignal: cancellation.signal });
|
|
20422
|
+
const abortFromCaller = () => cancellation.abort(options.abortSignal?.reason);
|
|
20423
|
+
let reader;
|
|
20424
|
+
let closed = false;
|
|
20425
|
+
let onAbort = () => {
|
|
20426
|
+
};
|
|
20427
|
+
const dispose = () => {
|
|
20428
|
+
this.streams.delete(cancellation);
|
|
20429
|
+
options.abortSignal?.removeEventListener("abort", abortFromCaller);
|
|
20430
|
+
abort.signal.removeEventListener("abort", onAbort);
|
|
20431
|
+
abort.dispose();
|
|
20432
|
+
};
|
|
20433
|
+
const cancelReader = async (reason) => {
|
|
20434
|
+
const activeReader = reader;
|
|
20435
|
+
reader = void 0;
|
|
20436
|
+
if (activeReader !== void 0) {
|
|
20437
|
+
await activeReader.cancel(reason).catch(() => void 0);
|
|
20438
|
+
activeReader.releaseLock();
|
|
20439
|
+
}
|
|
20440
|
+
};
|
|
20441
|
+
return new ReadableStream(
|
|
20442
|
+
{
|
|
20443
|
+
start: (controller) => {
|
|
20444
|
+
onAbort = () => {
|
|
20445
|
+
if (closed) {
|
|
20446
|
+
return;
|
|
20447
|
+
}
|
|
20448
|
+
closed = true;
|
|
20449
|
+
dispose();
|
|
20450
|
+
controller.error(abort.signal.reason);
|
|
20451
|
+
void cancelReader(abort.signal.reason);
|
|
20452
|
+
};
|
|
20453
|
+
abort.signal.addEventListener("abort", onAbort, { once: true });
|
|
20454
|
+
options.abortSignal?.addEventListener("abort", abortFromCaller, { once: true });
|
|
20455
|
+
if (options.abortSignal?.aborted) {
|
|
20456
|
+
abortFromCaller();
|
|
20457
|
+
}
|
|
20458
|
+
},
|
|
20459
|
+
pull: async (controller) => {
|
|
20460
|
+
try {
|
|
20461
|
+
if (reader === void 0) {
|
|
20462
|
+
const response = await this.streamResponse(action, data, abort.signal);
|
|
20463
|
+
if (closed) {
|
|
20464
|
+
await response.body?.cancel();
|
|
20465
|
+
return;
|
|
20466
|
+
}
|
|
20467
|
+
if (response.body === null) {
|
|
20468
|
+
throw new TransportError("PHPSandbox runtime returned an empty download.", "InvalidResponse", response);
|
|
20469
|
+
}
|
|
20470
|
+
reader = response.body.getReader();
|
|
20471
|
+
}
|
|
20472
|
+
const result = await reader.read();
|
|
20473
|
+
if (closed) {
|
|
20474
|
+
return;
|
|
20475
|
+
}
|
|
20476
|
+
if (result.done) {
|
|
20477
|
+
closed = true;
|
|
20478
|
+
dispose();
|
|
20479
|
+
reader.releaseLock();
|
|
20480
|
+
reader = void 0;
|
|
20481
|
+
controller.close();
|
|
20482
|
+
} else {
|
|
20483
|
+
controller.enqueue(result.value);
|
|
20484
|
+
}
|
|
20485
|
+
} catch (error51) {
|
|
20486
|
+
if (!closed) {
|
|
20487
|
+
closed = true;
|
|
20488
|
+
cancellation.abort(error51);
|
|
20489
|
+
dispose();
|
|
20490
|
+
await cancelReader(error51);
|
|
20491
|
+
controller.error(error51);
|
|
20492
|
+
}
|
|
20493
|
+
}
|
|
20494
|
+
},
|
|
20495
|
+
cancel: async (reason) => {
|
|
20496
|
+
closed = true;
|
|
20497
|
+
cancellation.abort(reason);
|
|
20498
|
+
dispose();
|
|
20499
|
+
await cancelReader(reason);
|
|
20500
|
+
}
|
|
20501
|
+
},
|
|
20502
|
+
{ highWaterMark: 0 }
|
|
20503
|
+
);
|
|
20504
|
+
}
|
|
20505
|
+
async streamResponse(action, data, signal) {
|
|
20506
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
20507
|
+
signal.throwIfAborted();
|
|
20508
|
+
const response = await runtimeFetch(this.fetch, this.request(action, data, signal));
|
|
20509
|
+
if (signal.aborted) {
|
|
20510
|
+
await response.body?.cancel();
|
|
20511
|
+
signal.throwIfAborted();
|
|
20512
|
+
}
|
|
20513
|
+
if (response.status === 401 && attempt === 0 && this.refresh !== void 0) {
|
|
20514
|
+
await response.body?.cancel();
|
|
20515
|
+
await this.refreshAuthorization();
|
|
20516
|
+
continue;
|
|
20517
|
+
}
|
|
20518
|
+
if (!response.ok) {
|
|
20519
|
+
const payload = await runtimeActionResponse(response);
|
|
20520
|
+
throw remoteError("runtime", payload.error, response);
|
|
20521
|
+
}
|
|
20522
|
+
if (!response.headers.get("content-type")?.toLowerCase().startsWith("application/zip")) {
|
|
20523
|
+
await response.body?.cancel();
|
|
20524
|
+
throw new TransportError("PHPSandbox runtime returned an invalid download.", "InvalidResponse", response);
|
|
20525
|
+
}
|
|
20526
|
+
return response;
|
|
20527
|
+
}
|
|
20528
|
+
throw new RemoteError("Notebook download could not be authorized.", {
|
|
20529
|
+
source: "runtime",
|
|
20530
|
+
status: 401,
|
|
20531
|
+
code: "AuthenticationRequired"
|
|
20532
|
+
});
|
|
20533
|
+
}
|
|
20414
20534
|
async invokeRaw(action, data, options) {
|
|
20415
20535
|
const abort = runtimeAbort(options);
|
|
20416
20536
|
try {
|
|
@@ -20443,7 +20563,7 @@ var RestRuntimeInvoker = class {
|
|
|
20443
20563
|
const url2 = new URL(this.actionUrl);
|
|
20444
20564
|
url2.pathname += route.path;
|
|
20445
20565
|
const headers = {
|
|
20446
|
-
Accept: "application/json, application/octet-stream",
|
|
20566
|
+
Accept: action === "fs.download" ? "application/zip" : "application/json, application/octet-stream",
|
|
20447
20567
|
Authorization: `Bearer ${this.ticket}`
|
|
20448
20568
|
};
|
|
20449
20569
|
const body = runtimeActionRequestBody(action, data, url2, headers);
|
|
@@ -20560,9 +20680,9 @@ function runtimeAbort(options) {
|
|
|
20560
20680
|
|
|
20561
20681
|
// src/feedback.ts
|
|
20562
20682
|
var Feedback = class {
|
|
20563
|
-
constructor(client,
|
|
20683
|
+
constructor(client, identifier) {
|
|
20564
20684
|
this.client = client;
|
|
20565
|
-
this.
|
|
20685
|
+
this.identifier = identifier;
|
|
20566
20686
|
}
|
|
20567
20687
|
async configure(input) {
|
|
20568
20688
|
const response = await this.client.put(this.path("/configuration"), input);
|
|
@@ -20572,8 +20692,8 @@ var Feedback = class {
|
|
|
20572
20692
|
const response = await this.client.get(this.path("/configuration"));
|
|
20573
20693
|
return response.data;
|
|
20574
20694
|
}
|
|
20575
|
-
async submit(input) {
|
|
20576
|
-
const response = await this.client.post(this.path(), input);
|
|
20695
|
+
async submit(input, options = {}) {
|
|
20696
|
+
const response = await this.client.post(this.path(), input, options);
|
|
20577
20697
|
return response.data;
|
|
20578
20698
|
}
|
|
20579
20699
|
async list(options = {}) {
|
|
@@ -20594,7 +20714,18 @@ var Feedback = class {
|
|
|
20594
20714
|
return response.data;
|
|
20595
20715
|
}
|
|
20596
20716
|
path(suffix = "") {
|
|
20597
|
-
return `/
|
|
20717
|
+
return `/platform/projects/${encodeURIComponent(this.identifier)}/feedback${suffix}`;
|
|
20718
|
+
}
|
|
20719
|
+
};
|
|
20720
|
+
var FeedbackApi = class {
|
|
20721
|
+
constructor(client) {
|
|
20722
|
+
this.client = client;
|
|
20723
|
+
}
|
|
20724
|
+
forProject(projectId) {
|
|
20725
|
+
return new Feedback(this.client, projectId);
|
|
20726
|
+
}
|
|
20727
|
+
forNotebook(notebookId) {
|
|
20728
|
+
return new Feedback(this.client, notebookId);
|
|
20598
20729
|
}
|
|
20599
20730
|
};
|
|
20600
20731
|
|
|
@@ -20755,6 +20886,7 @@ var ClientImplementation = class {
|
|
|
20755
20886
|
this.integrations = new IntegrationApi(this);
|
|
20756
20887
|
this.payments = new PaymentsApi(this);
|
|
20757
20888
|
this.customers = new CustomersApi(this);
|
|
20889
|
+
this.feedback = new FeedbackApi(this);
|
|
20758
20890
|
this.options = { ...options };
|
|
20759
20891
|
this.runtimeTransportValue = runtimeTransport;
|
|
20760
20892
|
this.baseUrl = normalizeApiBaseUrl(url2);
|
|
@@ -20773,8 +20905,13 @@ var ClientImplementation = class {
|
|
|
20773
20905
|
get(path) {
|
|
20774
20906
|
return this.requireData(this.makeRequest("GET", path));
|
|
20775
20907
|
}
|
|
20776
|
-
post(path, body) {
|
|
20777
|
-
return this.requireData(
|
|
20908
|
+
post(path, body, options = {}) {
|
|
20909
|
+
return this.requireData(
|
|
20910
|
+
this.makeRequest("POST", path, {
|
|
20911
|
+
body: body === void 0 ? void 0 : JSON.stringify(body),
|
|
20912
|
+
headers: options.idempotencyKey === void 0 ? void 0 : { "Idempotency-Key": options.idempotencyKey }
|
|
20913
|
+
})
|
|
20914
|
+
);
|
|
20778
20915
|
}
|
|
20779
20916
|
delete(path) {
|
|
20780
20917
|
return this.makeRequest("DELETE", path);
|
|
@@ -20827,11 +20964,15 @@ var ClientImplementation = class {
|
|
|
20827
20964
|
return { ...response, data: response.data };
|
|
20828
20965
|
}
|
|
20829
20966
|
async makeRequest(method, path, init) {
|
|
20967
|
+
const headers = new Headers(init?.headers);
|
|
20968
|
+
for (const [name, value] of Object.entries(this.headers)) {
|
|
20969
|
+
headers.set(name, value);
|
|
20970
|
+
}
|
|
20830
20971
|
const response = await this.fetchResponse(
|
|
20831
20972
|
authenticatedRequest(new URL(path.replace(/^\//, ""), this.baseUrl), {
|
|
20832
20973
|
method,
|
|
20833
20974
|
...init,
|
|
20834
|
-
headers
|
|
20975
|
+
headers
|
|
20835
20976
|
})
|
|
20836
20977
|
);
|
|
20837
20978
|
if (!response.ok) {
|
|
@@ -20897,6 +21038,7 @@ var PHPSandbox = class _PHPSandbox {
|
|
|
20897
21038
|
this.integrations = client.integrations;
|
|
20898
21039
|
this.payments = client.payments;
|
|
20899
21040
|
this.customers = client.customers;
|
|
21041
|
+
this.feedback = client.feedback;
|
|
20900
21042
|
}
|
|
20901
21043
|
async whoami() {
|
|
20902
21044
|
const { data } = await this.client.get("/auth/whoami");
|
|
@@ -20926,6 +21068,7 @@ var _NotebookInstance = class _NotebookInstance {
|
|
|
20926
21068
|
this.data = data;
|
|
20927
21069
|
this.client = client;
|
|
20928
21070
|
__privateAdd(this, _NotebookInstance_instances);
|
|
21071
|
+
this.downloadInvoker = null;
|
|
20929
21072
|
this.initialized = false;
|
|
20930
21073
|
__privateAdd(this, _initPromise, null);
|
|
20931
21074
|
this.disposables = [];
|
|
@@ -21047,12 +21190,23 @@ var _NotebookInstance = class _NotebookInstance {
|
|
|
21047
21190
|
}
|
|
21048
21191
|
return this.realtimeSocket().send(action, data || {});
|
|
21049
21192
|
}
|
|
21193
|
+
/** Binary downloads use HTTP independently of the selected runtime transport. */
|
|
21194
|
+
stream(action, data = {}, options = {}) {
|
|
21195
|
+
const runtimeUrlProvider = this.client.options.runtimeUrlProvider;
|
|
21196
|
+
this.downloadInvoker ?? (this.downloadInvoker = this.restInvoker ?? new RestRuntimeInvoker(
|
|
21197
|
+
this.data,
|
|
21198
|
+
this.client.options.fetch ?? globalThis.fetch,
|
|
21199
|
+
runtimeUrlProvider === void 0 ? void 0 : () => runtimeUrlProvider(this.data.id)
|
|
21200
|
+
));
|
|
21201
|
+
return this.downloadInvoker.stream(action, data, options);
|
|
21202
|
+
}
|
|
21050
21203
|
listen(event, handler) {
|
|
21051
21204
|
const disposable = this.emitter.listen(event, handler);
|
|
21052
21205
|
this.disposables.push(disposable);
|
|
21053
21206
|
return disposable;
|
|
21054
21207
|
}
|
|
21055
21208
|
dispose() {
|
|
21209
|
+
this.downloadInvoker?.dispose();
|
|
21056
21210
|
this.disposables.forEach((d) => d.dispose());
|
|
21057
21211
|
if (this.socket !== null && !this.socket.isClosed) {
|
|
21058
21212
|
this.socket.disconnect();
|
|
@@ -21309,6 +21463,8 @@ export {
|
|
|
21309
21463
|
CustomerRuntime,
|
|
21310
21464
|
CustomersApi,
|
|
21311
21465
|
DEFAULT_PAYMENT_CALLBACK_PATH,
|
|
21466
|
+
Feedback,
|
|
21467
|
+
FeedbackApi,
|
|
21312
21468
|
FileChangeFilter,
|
|
21313
21469
|
FileChangeType,
|
|
21314
21470
|
FileType,
|