@phpsandbox/sdk 0.0.29 → 0.0.32

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.
@@ -336,6 +336,7 @@ var PHPSandbox = (() => {
336
336
  NotebookSecretApi: () => NotebookSecretApi,
337
337
  NotebookSecrets: () => NotebookSecrets,
338
338
  NotebookState: () => NotebookState,
339
+ NotebookUnavailableError: () => NotebookUnavailableError,
339
340
  PHPSandbox: () => PHPSandbox,
340
341
  PHPSandboxError: () => PHPSandboxError,
341
342
  PromiseTimeoutError: () => PromiseTimeoutError,
@@ -344,6 +345,8 @@ var PHPSandbox = (() => {
344
345
  Transport: () => Transport,
345
346
  createBeacon: () => createBeacon,
346
347
  isBeaconSupported: () => isBeaconSupported,
348
+ notebookBuiltinServices: () => notebookBuiltinServices,
349
+ notebookKnownServices: () => notebookKnownServices,
347
350
  once: () => once,
348
351
  timeout: () => timeout
349
352
  });
@@ -614,6 +617,13 @@ var PHPSandbox = (() => {
614
617
  return new _SendTimeoutError(error.message, error.time);
615
618
  }
616
619
  };
620
+ var NotebookUnavailableError = class extends Error {
621
+ constructor(message = "Notebook is no longer available", raw = {}) {
622
+ super(message);
623
+ this.raw = raw;
624
+ this.name = "NotebookUnavailableError";
625
+ }
626
+ };
617
627
 
618
628
  // src/utils/promise.ts
619
629
  var timeout = (prom, time) => {
@@ -1061,6 +1071,108 @@ var PHPSandbox = (() => {
1061
1071
  }
1062
1072
  };
1063
1073
 
1074
+ // src/services.ts
1075
+ var notebookBuiltinServices = ["redis"];
1076
+ var notebookKnownServices = ["start", "nginx", ...notebookBuiltinServices];
1077
+ var Services = class {
1078
+ constructor(okra) {
1079
+ this.okra = okra;
1080
+ }
1081
+ list() {
1082
+ return this.okra.invoke("service.list");
1083
+ }
1084
+ run(name, command) {
1085
+ return this.okra.invoke("service.run", {
1086
+ name,
1087
+ command
1088
+ });
1089
+ }
1090
+ stop(name) {
1091
+ return this.okra.invoke("service.stop", { name });
1092
+ }
1093
+ onLog(id, handler) {
1094
+ return this.okra.listen(`service.log.${id}`, handler);
1095
+ }
1096
+ listen(event, handler) {
1097
+ return this.okra.listen(event, handler);
1098
+ }
1099
+ logs(name, options = {}) {
1100
+ if (options.follow) {
1101
+ return this.followLogs(name, options);
1102
+ }
1103
+ return this.okra.invoke("service.logs", {
1104
+ name,
1105
+ tail: options.tail
1106
+ }).then((response) => {
1107
+ if (!("output" in response)) {
1108
+ throw new Error(`Unexpected service.logs response for ${name}`);
1109
+ }
1110
+ return response.output;
1111
+ });
1112
+ }
1113
+ followLogs(name, options) {
1114
+ const id = options.id || nanoid();
1115
+ const disposables = /* @__PURE__ */ new Set();
1116
+ let controller = null;
1117
+ let closed = false;
1118
+ const dispose = () => {
1119
+ for (const disposable of disposables) {
1120
+ disposable.dispose();
1121
+ }
1122
+ disposables.clear();
1123
+ };
1124
+ const close = () => {
1125
+ if (closed) {
1126
+ return;
1127
+ }
1128
+ closed = true;
1129
+ dispose();
1130
+ if (controller) {
1131
+ try {
1132
+ controller.close();
1133
+ } catch {
1134
+ }
1135
+ }
1136
+ };
1137
+ const fail = (error) => {
1138
+ if (closed) {
1139
+ return;
1140
+ }
1141
+ closed = true;
1142
+ dispose();
1143
+ if (controller) {
1144
+ controller.error(error);
1145
+ }
1146
+ };
1147
+ const stop = once(() => {
1148
+ close();
1149
+ return this.okra.invoke("service.stop-logs", { id });
1150
+ });
1151
+ const output = new ReadableStream({
1152
+ start: (_controller) => {
1153
+ controller = _controller;
1154
+ disposables.add(
1155
+ this.onLog(id, (data) => {
1156
+ controller?.enqueue(data.output);
1157
+ })
1158
+ );
1159
+ void this.okra.invoke("service.logs", {
1160
+ name,
1161
+ tail: options.tail,
1162
+ follow: true,
1163
+ id
1164
+ }).catch((error) => {
1165
+ fail(error);
1166
+ });
1167
+ },
1168
+ cancel: () => {
1169
+ void stop();
1170
+ }
1171
+ });
1172
+ return { id, output, stop };
1173
+ }
1174
+ };
1175
+
1064
1176
  // node_modules/@msgpack/msgpack/dist.esm/utils/utf8.mjs
1065
1177
  function utf8Count(str) {
1066
1178
  const strLength = str.length;
@@ -3084,6 +3196,7 @@ var PHPSandbox = (() => {
3084
3196
  this.closed = false;
3085
3197
  this.disposables = new NamedDisposable();
3086
3198
  this.connectPromise = null;
3199
+ this.terminalError = null;
3087
3200
  this.pingIntervalStarted = false;
3088
3201
  // Connection health monitoring
3089
3202
  this.connectionStats = {
@@ -3115,7 +3228,7 @@ var PHPSandbox = (() => {
3115
3228
  const startClosed = options.startClosed !== false;
3116
3229
  this.rws = new reconnecting_websocket_mjs_default(this.url.toString(), [], {
3117
3230
  WebSocket: globalThis.WebSocket ?? browser_default,
3118
- connectionTimeout: options.connectionTimeout ?? 1e3,
3231
+ connectionTimeout: options.connectionTimeout,
3119
3232
  maxReconnectionDelay: 2e3,
3120
3233
  minReconnectionDelay: 200,
3121
3234
  maxEnqueuedMessages: 0,
@@ -3158,6 +3271,9 @@ var PHPSandbox = (() => {
3158
3271
  * Ensure the websocket is open without requiring an application-level roundtrip.
3159
3272
  */
3160
3273
  connect() {
3274
+ if (this.terminalError) {
3275
+ return Promise.reject(this.terminalError);
3276
+ }
3161
3277
  return __privateMethod(this, _Transport_instances, connect_fn).call(this);
3162
3278
  }
3163
3279
  id() {
@@ -3214,7 +3330,12 @@ var PHPSandbox = (() => {
3214
3330
  return;
3215
3331
  }
3216
3332
  if (event === "Events.BootError" /* BootError */) {
3333
+ const error = new NotebookUnavailableError(
3334
+ data?.message || "Notebook is no longer available",
3335
+ data || {}
3336
+ );
3217
3337
  this.log("error", "Boot error received", { data });
3338
+ this.terminate(error, 4e3, error.message);
3218
3339
  return;
3219
3340
  }
3220
3341
  if (event === "response" /* Response */) {
@@ -3273,6 +3394,11 @@ var PHPSandbox = (() => {
3273
3394
  listen(event, listener, _context) {
3274
3395
  return this.eventEmitter.listen(event, listener);
3275
3396
  }
3397
+ onDidBootError(listener) {
3398
+ this.eventEmitter.listen("transport.boot_error", (event) => {
3399
+ listener(event.error);
3400
+ });
3401
+ }
3276
3402
  removeListener(event, listener) {
3277
3403
  this.eventEmitter.removeListener(event, listener);
3278
3404
  }
@@ -3299,6 +3425,9 @@ var PHPSandbox = (() => {
3299
3425
  get isClosed() {
3300
3426
  return this.closed;
3301
3427
  }
3428
+ getTerminalError() {
3429
+ return this.terminalError;
3430
+ }
3302
3431
  get status() {
3303
3432
  return {
3304
3433
  0: "CONNECTING",
@@ -3308,6 +3437,9 @@ var PHPSandbox = (() => {
3308
3437
  }[this.rws.readyState];
3309
3438
  }
3310
3439
  async call(action, data = {}, options = {}) {
3440
+ if (this.terminalError) {
3441
+ throw this.terminalError;
3442
+ }
3311
3443
  if (this.isRateLimited()) {
3312
3444
  throw new RateLimitError("Rate limit exceeded - too many requests");
3313
3445
  }
@@ -3324,6 +3456,10 @@ var PHPSandbox = (() => {
3324
3456
  this.eventEmitter.removeListener(errorEvent);
3325
3457
  };
3326
3458
  const handler = async (resolve, reject) => {
3459
+ if (this.terminalError) {
3460
+ reject(this.terminalError);
3461
+ return;
3462
+ }
3327
3463
  const abortError = new DOMException("Request aborted", "AbortError");
3328
3464
  if (options.abortSignal?.aborted) {
3329
3465
  reject(abortError);
@@ -3375,7 +3511,7 @@ var PHPSandbox = (() => {
3375
3511
  reject(new RateLimitError(_ev.reason || "Rate limit exceeded", _ev));
3376
3512
  return;
3377
3513
  }
3378
- reject(new Error(`Connection lost to the notebook during request: ${_ev.reason || "Unknown reason"}`));
3514
+ reject(this.terminalError || new Error(`Connection lost to the notebook during request: ${_ev.reason || "Unknown reason"}`));
3379
3515
  };
3380
3516
  this.rws.addEventListener("close", closeHandler);
3381
3517
  this.rws.addEventListener("error", closeHandler);
@@ -3411,7 +3547,7 @@ var PHPSandbox = (() => {
3411
3547
  try {
3412
3548
  return await sender();
3413
3549
  } catch (e) {
3414
- if (e instanceof ErrorEvent || e instanceof RateLimitError || e instanceof InvalidConfigurationError || e instanceof InvalidMessageError || e instanceof DOMException) {
3550
+ if (e instanceof ErrorEvent || e instanceof RateLimitError || e instanceof InvalidConfigurationError || e instanceof InvalidMessageError || e instanceof NotebookUnavailableError || e instanceof DOMException) {
3415
3551
  this.log("debug", "Non-retryable error, bailing", {
3416
3552
  error: e.message,
3417
3553
  attempt
@@ -3463,6 +3599,9 @@ var PHPSandbox = (() => {
3463
3599
  * This preserves all event listeners and state.
3464
3600
  */
3465
3601
  reconnect() {
3602
+ if (this.terminalError) {
3603
+ throw this.terminalError;
3604
+ }
3466
3605
  if (this.closed) {
3467
3606
  throw new Error("Cannot reconnect a closed transport. The transport has been permanently closed.");
3468
3607
  }
@@ -3477,6 +3616,16 @@ var PHPSandbox = (() => {
3477
3616
  }
3478
3617
  this.close();
3479
3618
  }
3619
+ terminate(error, code = 4e3, reason = error.message) {
3620
+ this.terminalError = error;
3621
+ if (error instanceof NotebookUnavailableError) {
3622
+ this.eventEmitter.emit("transport.boot_error", {
3623
+ error,
3624
+ timestamp: Date.now()
3625
+ });
3626
+ }
3627
+ this.close(code, reason);
3628
+ }
3480
3629
  close(code, reason) {
3481
3630
  if (this.closed) {
3482
3631
  return;
@@ -3484,9 +3633,9 @@ var PHPSandbox = (() => {
3484
3633
  this.log("info", "Closing transport connection", { code, reason });
3485
3634
  this.connectPromise = null;
3486
3635
  const queuedCount = this.messageQueue.length;
3636
+ const closeError = this.terminalError || new Error("Connection closed while message was queued");
3487
3637
  this.messageQueue.forEach((msg) => {
3488
- console.log("Rejecting queued message due to connection close");
3489
- msg.reject(new Error("Connection closed while message was queued"));
3638
+ msg.reject(closeError);
3490
3639
  });
3491
3640
  this.messageQueue = [];
3492
3641
  if (queuedCount > 0) {
@@ -3574,6 +3723,9 @@ var PHPSandbox = (() => {
3574
3723
  this.log("error", "Connection closed due to policy violation");
3575
3724
  this.clearOldQueuedMessages();
3576
3725
  return "stop";
3726
+ case 4e3:
3727
+ this.log("info", "Connection closed due to terminal notebook error");
3728
+ return "stop";
3577
3729
  default:
3578
3730
  this.log("warn", `Unknown close code: ${code}, will reconnect`);
3579
3731
  return "reconnect";
@@ -3789,6 +3941,9 @@ var PHPSandbox = (() => {
3789
3941
  * by caching the connection promise.
3790
3942
  */
3791
3943
  connect_fn = function() {
3944
+ if (this.terminalError) {
3945
+ return Promise.reject(this.terminalError);
3946
+ }
3792
3947
  if (this.isConnected) {
3793
3948
  return Promise.resolve();
3794
3949
  }
@@ -4835,9 +4990,14 @@ var PHPSandbox = (() => {
4835
4990
  this.laravel = new Lravel(this);
4836
4991
  this.shell = new Shell(this);
4837
4992
  this.git = new Git(this);
4993
+ this.services = new Services(this);
4838
4994
  this.secrets = new NotebookSecrets(client, this.data.id);
4839
4995
  }
4840
4996
  async ready() {
4997
+ const terminalError = this.socket.getTerminalError();
4998
+ if (terminalError) {
4999
+ throw terminalError;
5000
+ }
4841
5001
  const ready = async () => {
4842
5002
  if (this.client.options.startClosed && !this.socket.isConnected) {
4843
5003
  await this.socket.connect();
@@ -4850,7 +5010,9 @@ var PHPSandbox = (() => {
4850
5010
  return this.client.notebook.fork(this.data.id);
4851
5011
  }
4852
5012
  delete() {
4853
- return this.client.notebook.delete(this.data.id);
5013
+ return this.client.notebook.delete(this.data.id).then(() => {
5014
+ this.socket.terminate(new NotebookUnavailableError("Notebook has been deleted.", { id: this.data.id }));
5015
+ });
4854
5016
  }
4855
5017
  stop() {
4856
5018
  return this.container.stop();
@@ -4874,6 +5036,10 @@ var PHPSandbox = (() => {
4874
5036
  !this.socket.isClosed && this.socket.disconnect();
4875
5037
  }
4876
5038
  connected() {
5039
+ const terminalError = this.socket.getTerminalError();
5040
+ if (terminalError) {
5041
+ return Promise.reject(terminalError);
5042
+ }
4877
5043
  if (this.socket.isConnected) {
4878
5044
  return Promise.resolve(this);
4879
5045
  }
@@ -4881,18 +5047,24 @@ var PHPSandbox = (() => {
4881
5047
  try {
4882
5048
  this.socket.onDidConnect(() => resolve(this));
4883
5049
  this.socket.onDidClose(() => reject(new Error("Connection closed")));
5050
+ this.socket.onDidBootError((error) => reject(error));
4884
5051
  } catch (e) {
4885
5052
  reject(e);
4886
5053
  }
4887
5054
  });
4888
5055
  }
4889
5056
  whenConnected() {
5057
+ const terminalError = this.socket.getTerminalError();
5058
+ if (terminalError) {
5059
+ return Promise.reject(terminalError);
5060
+ }
4890
5061
  if (this.socket.isConnected) {
4891
5062
  return Promise.resolve(this);
4892
5063
  }
4893
5064
  return new Promise((resolve, reject) => {
4894
5065
  try {
4895
5066
  this.socket.onDidConnect(() => resolve(this));
5067
+ this.socket.onDidBootError((error) => reject(error));
4896
5068
  } catch (e) {
4897
5069
  reject(e);
4898
5070
  }
@@ -4909,6 +5081,11 @@ var PHPSandbox = (() => {
4909
5081
  this.socket.emit("okra.disconnected");
4910
5082
  this.initialized = false;
4911
5083
  });
5084
+ this.socket.onDidBootError((error) => {
5085
+ this.socket.emit("okra.boot_error", error);
5086
+ this.socket.emit("okra.disconnected");
5087
+ this.initialized = false;
5088
+ });
4912
5089
  }
4913
5090
  onDidConnect(handler) {
4914
5091
  this.socket.removeListener("okra.connected", handler);
@@ -4921,6 +5098,11 @@ var PHPSandbox = (() => {
4921
5098
  this.disposables.push(disposable);
4922
5099
  return disposable;
4923
5100
  }
5101
+ onDidBootError(handler) {
5102
+ const disposable = this.socket.listen("okra.boot_error", handler);
5103
+ this.disposables.push(disposable);
5104
+ return disposable;
5105
+ }
4924
5106
  update() {
4925
5107
  return this.invoke("notebook.update");
4926
5108
  }
@@ -4946,6 +5128,7 @@ var PHPSandbox = (() => {
4946
5128
  init_fn = function() {
4947
5129
  __privateSet(this, _initPromise, new Promise((resolve, reject) => {
4948
5130
  const initializationListener = this.onDidInitialize((result) => {
5131
+ bootErrorListener.dispose();
4949
5132
  initializationListener.dispose();
4950
5133
  this.initialized = result;
4951
5134
  if (result.type === "error") {
@@ -4954,6 +5137,11 @@ var PHPSandbox = (() => {
4954
5137
  }
4955
5138
  resolve(result);
4956
5139
  });
5140
+ const bootErrorListener = this.onDidBootError((error) => {
5141
+ initializationListener.dispose();
5142
+ bootErrorListener.dispose();
5143
+ reject(error);
5144
+ });
4957
5145
  }));
4958
5146
  return __privateGet(this, _initPromise);
4959
5147
  };