@phpsandbox/sdk 0.0.32 → 0.0.34
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 +14 -0
- package/dist/browser/phpsandbox-sdk.esm.js +146 -10
- package/dist/browser/phpsandbox-sdk.esm.js.map +3 -3
- package/dist/browser/phpsandbox-sdk.esm.min.js +2 -2
- package/dist/browser/phpsandbox-sdk.esm.min.js.map +4 -4
- package/dist/browser/phpsandbox-sdk.iife.js +146 -10
- package/dist/browser/phpsandbox-sdk.iife.js.map +3 -3
- package/dist/browser/phpsandbox-sdk.iife.min.js +2 -2
- package/dist/browser/phpsandbox-sdk.iife.min.js.map +4 -4
- package/dist/index.d.ts +14 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/socket/index.d.ts +3 -2
- package/dist/socket/index.d.ts.map +1 -1
- package/dist/socket/index.js +42 -9
- package/dist/socket/index.js.map +1 -1
- package/dist/terminal.d.ts.map +1 -1
- package/dist/terminal.js +16 -3
- package/dist/terminal.js.map +1 -1
- package/dist/utils/websocket.d.ts +2 -0
- package/dist/utils/websocket.d.ts.map +1 -0
- package/dist/utils/websocket.js +87 -0
- package/dist/utils/websocket.js.map +1 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -79,12 +79,20 @@ await fetched.ready();
|
|
|
79
79
|
|
|
80
80
|
const forked = await created.fork();
|
|
81
81
|
await forked.delete();
|
|
82
|
+
|
|
83
|
+
const persistent = await client.notebook.create('laravel', {
|
|
84
|
+
title: 'Persistent Laravel',
|
|
85
|
+
persistent: true,
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
console.log(persistent.data.policy);
|
|
82
89
|
```
|
|
83
90
|
|
|
84
91
|
Notes:
|
|
85
92
|
|
|
86
93
|
- `create()` and `fork()` initialize automatically.
|
|
87
94
|
- `open()` and `get()` return a notebook instance; call `await notebook.ready()` before using tools.
|
|
95
|
+
- API notebook creates are ephemeral by default. `persistent: true` requires an entitled account.
|
|
88
96
|
|
|
89
97
|
## Services At A Glance
|
|
90
98
|
|
|
@@ -103,6 +111,12 @@ Each `NotebookInstance` exposes service clients:
|
|
|
103
111
|
- `notebook.log` (`Log`)
|
|
104
112
|
- `notebook.services` (`Services`)
|
|
105
113
|
|
|
114
|
+
Notes:
|
|
115
|
+
|
|
116
|
+
- `terminal.spawn()` and `shell.exec()` are part of the current okra websocket action set. `terminal.start` currently is not.
|
|
117
|
+
- `notebook.repl.eval()` is part of the current okra websocket action set. `repl.start` is currently not.
|
|
118
|
+
- `notebook.laravel` is exposed by the SDK, but the current okra websocket action set does not expose Laravel maintenance actions.
|
|
119
|
+
|
|
106
120
|
## Common Operations
|
|
107
121
|
|
|
108
122
|
### Files
|
|
@@ -639,19 +639,30 @@ var Terminal = class {
|
|
|
639
639
|
},
|
|
640
640
|
close: dispose
|
|
641
641
|
});
|
|
642
|
+
let controller = null;
|
|
642
643
|
const output = new ReadableStream({
|
|
643
|
-
start: (
|
|
644
|
+
start: (_controller) => {
|
|
645
|
+
controller = _controller;
|
|
644
646
|
disposables.add(
|
|
645
647
|
this.listen(`terminal.output.${id}`, (data) => {
|
|
646
|
-
controller
|
|
648
|
+
controller?.enqueue(data.output);
|
|
647
649
|
})
|
|
648
650
|
);
|
|
649
651
|
},
|
|
650
|
-
cancel:
|
|
652
|
+
cancel: () => {
|
|
653
|
+
controller = null;
|
|
654
|
+
dispose();
|
|
655
|
+
}
|
|
651
656
|
});
|
|
652
657
|
const exit = new Promise((resolve) => {
|
|
653
658
|
disposables.add(
|
|
654
659
|
this.listen(`terminal.close.${id}`, (data) => {
|
|
660
|
+
if (controller) {
|
|
661
|
+
try {
|
|
662
|
+
controller.close();
|
|
663
|
+
} catch {
|
|
664
|
+
}
|
|
665
|
+
}
|
|
655
666
|
dispose();
|
|
656
667
|
resolve(data.exitCode);
|
|
657
668
|
})
|
|
@@ -3091,6 +3102,89 @@ var ReconnectingWebSocket = (
|
|
|
3091
3102
|
);
|
|
3092
3103
|
var reconnecting_websocket_mjs_default = ReconnectingWebSocket;
|
|
3093
3104
|
|
|
3105
|
+
// src/utils/websocket.ts
|
|
3106
|
+
var getReadyStateLabel = (state) => {
|
|
3107
|
+
if (typeof state !== "number") {
|
|
3108
|
+
return void 0;
|
|
3109
|
+
}
|
|
3110
|
+
return {
|
|
3111
|
+
0: "CONNECTING",
|
|
3112
|
+
1: "OPEN",
|
|
3113
|
+
2: "CLOSING",
|
|
3114
|
+
3: "CLOSED"
|
|
3115
|
+
}[state] ?? `STATE_${state}`;
|
|
3116
|
+
};
|
|
3117
|
+
var sanitizeWebSocketUrl = (value) => {
|
|
3118
|
+
try {
|
|
3119
|
+
const url = new URL(value);
|
|
3120
|
+
return `${url.origin}${url.pathname}`;
|
|
3121
|
+
} catch {
|
|
3122
|
+
return value;
|
|
3123
|
+
}
|
|
3124
|
+
};
|
|
3125
|
+
var extractNestedErrorMessage = (value) => {
|
|
3126
|
+
if (value instanceof Error) {
|
|
3127
|
+
return value.message ? `${value.name}: ${value.message}` : value.name;
|
|
3128
|
+
}
|
|
3129
|
+
if (typeof value === "string") {
|
|
3130
|
+
return value;
|
|
3131
|
+
}
|
|
3132
|
+
if (!value || typeof value !== "object") {
|
|
3133
|
+
return void 0;
|
|
3134
|
+
}
|
|
3135
|
+
const record = value;
|
|
3136
|
+
const message = typeof record.message === "string" && record.message ? record.message : typeof record.reason === "string" && record.reason ? record.reason : void 0;
|
|
3137
|
+
if (!message) {
|
|
3138
|
+
return void 0;
|
|
3139
|
+
}
|
|
3140
|
+
const name = typeof record.name === "string" && record.name ? record.name : void 0;
|
|
3141
|
+
return name ? `${name}: ${message}` : message;
|
|
3142
|
+
};
|
|
3143
|
+
var describeWebSocketEvent = (event) => {
|
|
3144
|
+
if (event instanceof Error) {
|
|
3145
|
+
return event.message ? `${event.name}: ${event.message}` : event.name;
|
|
3146
|
+
}
|
|
3147
|
+
if (typeof event === "string") {
|
|
3148
|
+
return event;
|
|
3149
|
+
}
|
|
3150
|
+
if (!event || typeof event !== "object") {
|
|
3151
|
+
return String(event);
|
|
3152
|
+
}
|
|
3153
|
+
const record = event;
|
|
3154
|
+
const parts = [];
|
|
3155
|
+
const topLevelMessage = extractNestedErrorMessage(record);
|
|
3156
|
+
if (topLevelMessage) {
|
|
3157
|
+
parts.push(topLevelMessage);
|
|
3158
|
+
}
|
|
3159
|
+
const nestedErrorMessage = extractNestedErrorMessage(record.error);
|
|
3160
|
+
if (nestedErrorMessage && nestedErrorMessage !== topLevelMessage) {
|
|
3161
|
+
parts.push(`error=${nestedErrorMessage}`);
|
|
3162
|
+
}
|
|
3163
|
+
if (typeof record.type === "string" && record.type) {
|
|
3164
|
+
parts.push(`type=${record.type}`);
|
|
3165
|
+
}
|
|
3166
|
+
if (typeof record.code === "number") {
|
|
3167
|
+
parts.push(`code=${record.code}`);
|
|
3168
|
+
}
|
|
3169
|
+
if (typeof record.reason === "string" && record.reason && record.reason !== topLevelMessage) {
|
|
3170
|
+
parts.push(`reason=${record.reason}`);
|
|
3171
|
+
}
|
|
3172
|
+
if (typeof record.wasClean === "boolean") {
|
|
3173
|
+
parts.push(`wasClean=${record.wasClean}`);
|
|
3174
|
+
}
|
|
3175
|
+
const target = record.target ?? record.currentTarget;
|
|
3176
|
+
if (target && typeof target === "object") {
|
|
3177
|
+
if (typeof target.url === "string" && target.url) {
|
|
3178
|
+
parts.push(`url=${sanitizeWebSocketUrl(target.url)}`);
|
|
3179
|
+
}
|
|
3180
|
+
const readyState = getReadyStateLabel(target.readyState);
|
|
3181
|
+
if (readyState) {
|
|
3182
|
+
parts.push(`readyState=${readyState}`);
|
|
3183
|
+
}
|
|
3184
|
+
}
|
|
3185
|
+
return parts.length > 0 ? parts.join(" | ") : "Unknown websocket event";
|
|
3186
|
+
};
|
|
3187
|
+
|
|
3094
3188
|
// node_modules/isomorphic-ws/browser.js
|
|
3095
3189
|
var ws = null;
|
|
3096
3190
|
if (typeof WebSocket !== "undefined") {
|
|
@@ -3128,6 +3222,13 @@ var NamedDisposable = class {
|
|
|
3128
3222
|
};
|
|
3129
3223
|
|
|
3130
3224
|
// src/socket/index.ts
|
|
3225
|
+
var ConnectionFailedError = class extends Error {
|
|
3226
|
+
constructor(message, originalError) {
|
|
3227
|
+
super(message);
|
|
3228
|
+
this.originalError = originalError;
|
|
3229
|
+
this.name = "ConnectionFailedError";
|
|
3230
|
+
}
|
|
3231
|
+
};
|
|
3131
3232
|
var InvalidMessageError = class extends Error {
|
|
3132
3233
|
constructor(message, data) {
|
|
3133
3234
|
super(message);
|
|
@@ -3182,7 +3283,7 @@ var Transport = class {
|
|
|
3182
3283
|
this.connectionStats.connectionStartTime = Date.now();
|
|
3183
3284
|
const startClosed = options.startClosed !== false;
|
|
3184
3285
|
this.rws = new reconnecting_websocket_mjs_default(this.url.toString(), [], {
|
|
3185
|
-
WebSocket: globalThis.WebSocket ?? browser_default,
|
|
3286
|
+
WebSocket: options.webSocket ?? globalThis.WebSocket ?? browser_default,
|
|
3186
3287
|
connectionTimeout: options.connectionTimeout,
|
|
3187
3288
|
maxReconnectionDelay: 2e3,
|
|
3188
3289
|
minReconnectionDelay: 200,
|
|
@@ -3237,7 +3338,19 @@ var Transport = class {
|
|
|
3237
3338
|
async registerWatchers() {
|
|
3238
3339
|
const onMessage = (ev) => {
|
|
3239
3340
|
if (!(ev.data instanceof Blob)) {
|
|
3240
|
-
|
|
3341
|
+
const error = new Error("Unexpected message type: " + typeof ev.data);
|
|
3342
|
+
this.connectionStats.totalErrors++;
|
|
3343
|
+
this.log("error", "Unexpected WebSocket message type", {
|
|
3344
|
+
error: error.message,
|
|
3345
|
+
messageType: typeof ev.data
|
|
3346
|
+
});
|
|
3347
|
+
this.eventEmitter.emit("transport.error", {
|
|
3348
|
+
type: "message_type_error",
|
|
3349
|
+
error,
|
|
3350
|
+
rawMessage: ev.data,
|
|
3351
|
+
timestamp: Date.now()
|
|
3352
|
+
});
|
|
3353
|
+
return;
|
|
3241
3354
|
}
|
|
3242
3355
|
ev.data.arrayBuffer().then((buffer) => {
|
|
3243
3356
|
if (buffer.byteLength === 0) {
|
|
@@ -3245,7 +3358,19 @@ var Transport = class {
|
|
|
3245
3358
|
return;
|
|
3246
3359
|
}
|
|
3247
3360
|
try {
|
|
3248
|
-
this.handleRawMessage(decode(buffer))
|
|
3361
|
+
void this.handleRawMessage(decode(buffer)).catch((error) => {
|
|
3362
|
+
this.connectionStats.totalErrors++;
|
|
3363
|
+
this.log("error", "Failed to process WebSocket frame", {
|
|
3364
|
+
error: error instanceof Error ? error.message : String(error),
|
|
3365
|
+
byteLength: buffer.byteLength
|
|
3366
|
+
});
|
|
3367
|
+
this.eventEmitter.emit("transport.error", {
|
|
3368
|
+
type: "message_handle_error",
|
|
3369
|
+
error,
|
|
3370
|
+
rawMessage: buffer,
|
|
3371
|
+
timestamp: Date.now()
|
|
3372
|
+
});
|
|
3373
|
+
});
|
|
3249
3374
|
} catch (error) {
|
|
3250
3375
|
this.connectionStats.totalErrors++;
|
|
3251
3376
|
this.log("error", "Failed to decode WebSocket frame", {
|
|
@@ -3269,6 +3394,10 @@ var Transport = class {
|
|
|
3269
3394
|
});
|
|
3270
3395
|
}
|
|
3271
3396
|
async handleRawMessage(ev) {
|
|
3397
|
+
if (this.closed) {
|
|
3398
|
+
this.log("debug", "Ignoring message received after transport was closed");
|
|
3399
|
+
return;
|
|
3400
|
+
}
|
|
3272
3401
|
if (typeof ev !== "object" || ev === null) {
|
|
3273
3402
|
this.log("debug", "Received invalid message format", { ev });
|
|
3274
3403
|
return;
|
|
@@ -3466,7 +3595,8 @@ var Transport = class {
|
|
|
3466
3595
|
reject(new RateLimitError(_ev.reason || "Rate limit exceeded", _ev));
|
|
3467
3596
|
return;
|
|
3468
3597
|
}
|
|
3469
|
-
|
|
3598
|
+
const detail = describeWebSocketEvent(_ev);
|
|
3599
|
+
reject(this.terminalError || new ConnectionFailedError(`Connection lost to the notebook during request: ${detail}`, _ev));
|
|
3470
3600
|
};
|
|
3471
3601
|
this.rws.addEventListener("close", closeHandler);
|
|
3472
3602
|
this.rws.addEventListener("error", closeHandler);
|
|
@@ -3654,8 +3784,10 @@ var Transport = class {
|
|
|
3654
3784
|
});
|
|
3655
3785
|
this.rws.addEventListener("error", (event) => {
|
|
3656
3786
|
this.connectionStats.totalErrors++;
|
|
3787
|
+
const detail = describeWebSocketEvent(event);
|
|
3657
3788
|
this.log("error", "Connection error", {
|
|
3658
|
-
error:
|
|
3789
|
+
error: detail,
|
|
3790
|
+
rawError: event,
|
|
3659
3791
|
totalErrors: this.connectionStats.totalErrors
|
|
3660
3792
|
});
|
|
3661
3793
|
});
|
|
@@ -3928,7 +4060,8 @@ connect_fn = function() {
|
|
|
3928
4060
|
this.rws.removeEventListener("error", errorHandler);
|
|
3929
4061
|
clearTimeout(timeoutId);
|
|
3930
4062
|
this.connectPromise = null;
|
|
3931
|
-
|
|
4063
|
+
const detail = describeWebSocketEvent(error);
|
|
4064
|
+
reject(new ConnectionFailedError(`WebSocket connection failed: ${detail}`, error));
|
|
3932
4065
|
};
|
|
3933
4066
|
timeoutId = setTimeout(() => {
|
|
3934
4067
|
this.rws.removeEventListener("open", openHandler);
|
|
@@ -4930,7 +5063,8 @@ var NotebookInstance = class {
|
|
|
4930
5063
|
this.emitter = EventManager.createInstance();
|
|
4931
5064
|
this.socket = new Transport(data.okraUrl, this.emitter, {
|
|
4932
5065
|
debug: client.options.debug,
|
|
4933
|
-
startClosed: client.options.startClosed
|
|
5066
|
+
startClosed: client.options.startClosed,
|
|
5067
|
+
webSocket: client.options.webSocket
|
|
4934
5068
|
});
|
|
4935
5069
|
this.watchConnection();
|
|
4936
5070
|
__privateSet(this, _initPromise, __privateMethod(this, _NotebookInstance_instances, init_fn).call(this));
|
|
@@ -5098,6 +5232,8 @@ init_fn = function() {
|
|
|
5098
5232
|
reject(error);
|
|
5099
5233
|
});
|
|
5100
5234
|
}));
|
|
5235
|
+
void __privateGet(this, _initPromise).catch(() => {
|
|
5236
|
+
});
|
|
5101
5237
|
return __privateGet(this, _initPromise);
|
|
5102
5238
|
};
|
|
5103
5239
|
var NotebookSecrets = class {
|