@phpsandbox/sdk 0.0.29 → 0.0.30

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,
@@ -614,6 +615,13 @@ var PHPSandbox = (() => {
614
615
  return new _SendTimeoutError(error.message, error.time);
615
616
  }
616
617
  };
618
+ var NotebookUnavailableError = class extends Error {
619
+ constructor(message = "Notebook is no longer available", raw = {}) {
620
+ super(message);
621
+ this.raw = raw;
622
+ this.name = "NotebookUnavailableError";
623
+ }
624
+ };
617
625
 
618
626
  // src/utils/promise.ts
619
627
  var timeout = (prom, time) => {
@@ -1061,6 +1069,103 @@ var PHPSandbox = (() => {
1061
1069
  }
1062
1070
  };
1063
1071
 
1072
+ // src/services.ts
1073
+ var Services = class {
1074
+ constructor(okra) {
1075
+ this.okra = okra;
1076
+ }
1077
+ list() {
1078
+ return this.okra.invoke("service.list");
1079
+ }
1080
+ run(name) {
1081
+ return this.okra.invoke("service.run", { name });
1082
+ }
1083
+ stop(name) {
1084
+ return this.okra.invoke("service.stop", { name });
1085
+ }
1086
+ onLog(id, handler) {
1087
+ return this.okra.listen(`service.log.${id}`, handler);
1088
+ }
1089
+ listen(event, handler) {
1090
+ return this.okra.listen(event, handler);
1091
+ }
1092
+ logs(name, options = {}) {
1093
+ if (options.follow) {
1094
+ return this.followLogs(name, options);
1095
+ }
1096
+ return this.okra.invoke("service.logs", {
1097
+ name,
1098
+ tail: options.tail
1099
+ }).then((response) => {
1100
+ if (!("output" in response)) {
1101
+ throw new Error(`Unexpected service.logs response for ${name}`);
1102
+ }
1103
+ return response.output;
1104
+ });
1105
+ }
1106
+ followLogs(name, options) {
1107
+ const id = options.id || nanoid();
1108
+ const disposables = /* @__PURE__ */ new Set();
1109
+ let controller = null;
1110
+ let closed = false;
1111
+ const dispose = () => {
1112
+ for (const disposable of disposables) {
1113
+ disposable.dispose();
1114
+ }
1115
+ disposables.clear();
1116
+ };
1117
+ const close = () => {
1118
+ if (closed) {
1119
+ return;
1120
+ }
1121
+ closed = true;
1122
+ dispose();
1123
+ if (controller) {
1124
+ try {
1125
+ controller.close();
1126
+ } catch {
1127
+ }
1128
+ }
1129
+ };
1130
+ const fail = (error) => {
1131
+ if (closed) {
1132
+ return;
1133
+ }
1134
+ closed = true;
1135
+ dispose();
1136
+ if (controller) {
1137
+ controller.error(error);
1138
+ }
1139
+ };
1140
+ const stop = once(() => {
1141
+ close();
1142
+ return this.okra.invoke("service.stop-logs", { id });
1143
+ });
1144
+ const output = new ReadableStream({
1145
+ start: (_controller) => {
1146
+ controller = _controller;
1147
+ disposables.add(
1148
+ this.onLog(id, (data) => {
1149
+ controller?.enqueue(data.output);
1150
+ })
1151
+ );
1152
+ void this.okra.invoke("service.logs", {
1153
+ name,
1154
+ tail: options.tail,
1155
+ follow: true,
1156
+ id
1157
+ }).catch((error) => {
1158
+ fail(error);
1159
+ });
1160
+ },
1161
+ cancel: () => {
1162
+ void stop();
1163
+ }
1164
+ });
1165
+ return { id, output, stop };
1166
+ }
1167
+ };
1168
+
1064
1169
  // node_modules/@msgpack/msgpack/dist.esm/utils/utf8.mjs
1065
1170
  function utf8Count(str) {
1066
1171
  const strLength = str.length;
@@ -3084,6 +3189,7 @@ var PHPSandbox = (() => {
3084
3189
  this.closed = false;
3085
3190
  this.disposables = new NamedDisposable();
3086
3191
  this.connectPromise = null;
3192
+ this.terminalError = null;
3087
3193
  this.pingIntervalStarted = false;
3088
3194
  // Connection health monitoring
3089
3195
  this.connectionStats = {
@@ -3115,7 +3221,7 @@ var PHPSandbox = (() => {
3115
3221
  const startClosed = options.startClosed !== false;
3116
3222
  this.rws = new reconnecting_websocket_mjs_default(this.url.toString(), [], {
3117
3223
  WebSocket: globalThis.WebSocket ?? browser_default,
3118
- connectionTimeout: options.connectionTimeout ?? 1e3,
3224
+ connectionTimeout: options.connectionTimeout,
3119
3225
  maxReconnectionDelay: 2e3,
3120
3226
  minReconnectionDelay: 200,
3121
3227
  maxEnqueuedMessages: 0,
@@ -3158,6 +3264,9 @@ var PHPSandbox = (() => {
3158
3264
  * Ensure the websocket is open without requiring an application-level roundtrip.
3159
3265
  */
3160
3266
  connect() {
3267
+ if (this.terminalError) {
3268
+ return Promise.reject(this.terminalError);
3269
+ }
3161
3270
  return __privateMethod(this, _Transport_instances, connect_fn).call(this);
3162
3271
  }
3163
3272
  id() {
@@ -3214,7 +3323,12 @@ var PHPSandbox = (() => {
3214
3323
  return;
3215
3324
  }
3216
3325
  if (event === "Events.BootError" /* BootError */) {
3326
+ const error = new NotebookUnavailableError(
3327
+ data?.message || "Notebook is no longer available",
3328
+ data || {}
3329
+ );
3217
3330
  this.log("error", "Boot error received", { data });
3331
+ this.terminate(error, 4e3, error.message);
3218
3332
  return;
3219
3333
  }
3220
3334
  if (event === "response" /* Response */) {
@@ -3273,6 +3387,11 @@ var PHPSandbox = (() => {
3273
3387
  listen(event, listener, _context) {
3274
3388
  return this.eventEmitter.listen(event, listener);
3275
3389
  }
3390
+ onDidBootError(listener) {
3391
+ this.eventEmitter.listen("transport.boot_error", (event) => {
3392
+ listener(event.error);
3393
+ });
3394
+ }
3276
3395
  removeListener(event, listener) {
3277
3396
  this.eventEmitter.removeListener(event, listener);
3278
3397
  }
@@ -3299,6 +3418,9 @@ var PHPSandbox = (() => {
3299
3418
  get isClosed() {
3300
3419
  return this.closed;
3301
3420
  }
3421
+ getTerminalError() {
3422
+ return this.terminalError;
3423
+ }
3302
3424
  get status() {
3303
3425
  return {
3304
3426
  0: "CONNECTING",
@@ -3308,6 +3430,9 @@ var PHPSandbox = (() => {
3308
3430
  }[this.rws.readyState];
3309
3431
  }
3310
3432
  async call(action, data = {}, options = {}) {
3433
+ if (this.terminalError) {
3434
+ throw this.terminalError;
3435
+ }
3311
3436
  if (this.isRateLimited()) {
3312
3437
  throw new RateLimitError("Rate limit exceeded - too many requests");
3313
3438
  }
@@ -3324,6 +3449,10 @@ var PHPSandbox = (() => {
3324
3449
  this.eventEmitter.removeListener(errorEvent);
3325
3450
  };
3326
3451
  const handler = async (resolve, reject) => {
3452
+ if (this.terminalError) {
3453
+ reject(this.terminalError);
3454
+ return;
3455
+ }
3327
3456
  const abortError = new DOMException("Request aborted", "AbortError");
3328
3457
  if (options.abortSignal?.aborted) {
3329
3458
  reject(abortError);
@@ -3375,7 +3504,7 @@ var PHPSandbox = (() => {
3375
3504
  reject(new RateLimitError(_ev.reason || "Rate limit exceeded", _ev));
3376
3505
  return;
3377
3506
  }
3378
- reject(new Error(`Connection lost to the notebook during request: ${_ev.reason || "Unknown reason"}`));
3507
+ reject(this.terminalError || new Error(`Connection lost to the notebook during request: ${_ev.reason || "Unknown reason"}`));
3379
3508
  };
3380
3509
  this.rws.addEventListener("close", closeHandler);
3381
3510
  this.rws.addEventListener("error", closeHandler);
@@ -3411,7 +3540,7 @@ var PHPSandbox = (() => {
3411
3540
  try {
3412
3541
  return await sender();
3413
3542
  } catch (e) {
3414
- if (e instanceof ErrorEvent || e instanceof RateLimitError || e instanceof InvalidConfigurationError || e instanceof InvalidMessageError || e instanceof DOMException) {
3543
+ if (e instanceof ErrorEvent || e instanceof RateLimitError || e instanceof InvalidConfigurationError || e instanceof InvalidMessageError || e instanceof NotebookUnavailableError || e instanceof DOMException) {
3415
3544
  this.log("debug", "Non-retryable error, bailing", {
3416
3545
  error: e.message,
3417
3546
  attempt
@@ -3463,6 +3592,9 @@ var PHPSandbox = (() => {
3463
3592
  * This preserves all event listeners and state.
3464
3593
  */
3465
3594
  reconnect() {
3595
+ if (this.terminalError) {
3596
+ throw this.terminalError;
3597
+ }
3466
3598
  if (this.closed) {
3467
3599
  throw new Error("Cannot reconnect a closed transport. The transport has been permanently closed.");
3468
3600
  }
@@ -3477,6 +3609,16 @@ var PHPSandbox = (() => {
3477
3609
  }
3478
3610
  this.close();
3479
3611
  }
3612
+ terminate(error, code = 4e3, reason = error.message) {
3613
+ this.terminalError = error;
3614
+ if (error instanceof NotebookUnavailableError) {
3615
+ this.eventEmitter.emit("transport.boot_error", {
3616
+ error,
3617
+ timestamp: Date.now()
3618
+ });
3619
+ }
3620
+ this.close(code, reason);
3621
+ }
3480
3622
  close(code, reason) {
3481
3623
  if (this.closed) {
3482
3624
  return;
@@ -3484,9 +3626,9 @@ var PHPSandbox = (() => {
3484
3626
  this.log("info", "Closing transport connection", { code, reason });
3485
3627
  this.connectPromise = null;
3486
3628
  const queuedCount = this.messageQueue.length;
3629
+ const closeError = this.terminalError || new Error("Connection closed while message was queued");
3487
3630
  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"));
3631
+ msg.reject(closeError);
3490
3632
  });
3491
3633
  this.messageQueue = [];
3492
3634
  if (queuedCount > 0) {
@@ -3574,6 +3716,9 @@ var PHPSandbox = (() => {
3574
3716
  this.log("error", "Connection closed due to policy violation");
3575
3717
  this.clearOldQueuedMessages();
3576
3718
  return "stop";
3719
+ case 4e3:
3720
+ this.log("info", "Connection closed due to terminal notebook error");
3721
+ return "stop";
3577
3722
  default:
3578
3723
  this.log("warn", `Unknown close code: ${code}, will reconnect`);
3579
3724
  return "reconnect";
@@ -3789,6 +3934,9 @@ var PHPSandbox = (() => {
3789
3934
  * by caching the connection promise.
3790
3935
  */
3791
3936
  connect_fn = function() {
3937
+ if (this.terminalError) {
3938
+ return Promise.reject(this.terminalError);
3939
+ }
3792
3940
  if (this.isConnected) {
3793
3941
  return Promise.resolve();
3794
3942
  }
@@ -4835,9 +4983,14 @@ var PHPSandbox = (() => {
4835
4983
  this.laravel = new Lravel(this);
4836
4984
  this.shell = new Shell(this);
4837
4985
  this.git = new Git(this);
4986
+ this.services = new Services(this);
4838
4987
  this.secrets = new NotebookSecrets(client, this.data.id);
4839
4988
  }
4840
4989
  async ready() {
4990
+ const terminalError = this.socket.getTerminalError();
4991
+ if (terminalError) {
4992
+ throw terminalError;
4993
+ }
4841
4994
  const ready = async () => {
4842
4995
  if (this.client.options.startClosed && !this.socket.isConnected) {
4843
4996
  await this.socket.connect();
@@ -4850,7 +5003,9 @@ var PHPSandbox = (() => {
4850
5003
  return this.client.notebook.fork(this.data.id);
4851
5004
  }
4852
5005
  delete() {
4853
- return this.client.notebook.delete(this.data.id);
5006
+ return this.client.notebook.delete(this.data.id).then(() => {
5007
+ this.socket.terminate(new NotebookUnavailableError("Notebook has been deleted.", { id: this.data.id }));
5008
+ });
4854
5009
  }
4855
5010
  stop() {
4856
5011
  return this.container.stop();
@@ -4874,6 +5029,10 @@ var PHPSandbox = (() => {
4874
5029
  !this.socket.isClosed && this.socket.disconnect();
4875
5030
  }
4876
5031
  connected() {
5032
+ const terminalError = this.socket.getTerminalError();
5033
+ if (terminalError) {
5034
+ return Promise.reject(terminalError);
5035
+ }
4877
5036
  if (this.socket.isConnected) {
4878
5037
  return Promise.resolve(this);
4879
5038
  }
@@ -4881,18 +5040,24 @@ var PHPSandbox = (() => {
4881
5040
  try {
4882
5041
  this.socket.onDidConnect(() => resolve(this));
4883
5042
  this.socket.onDidClose(() => reject(new Error("Connection closed")));
5043
+ this.socket.onDidBootError((error) => reject(error));
4884
5044
  } catch (e) {
4885
5045
  reject(e);
4886
5046
  }
4887
5047
  });
4888
5048
  }
4889
5049
  whenConnected() {
5050
+ const terminalError = this.socket.getTerminalError();
5051
+ if (terminalError) {
5052
+ return Promise.reject(terminalError);
5053
+ }
4890
5054
  if (this.socket.isConnected) {
4891
5055
  return Promise.resolve(this);
4892
5056
  }
4893
5057
  return new Promise((resolve, reject) => {
4894
5058
  try {
4895
5059
  this.socket.onDidConnect(() => resolve(this));
5060
+ this.socket.onDidBootError((error) => reject(error));
4896
5061
  } catch (e) {
4897
5062
  reject(e);
4898
5063
  }
@@ -4909,6 +5074,11 @@ var PHPSandbox = (() => {
4909
5074
  this.socket.emit("okra.disconnected");
4910
5075
  this.initialized = false;
4911
5076
  });
5077
+ this.socket.onDidBootError((error) => {
5078
+ this.socket.emit("okra.boot_error", error);
5079
+ this.socket.emit("okra.disconnected");
5080
+ this.initialized = false;
5081
+ });
4912
5082
  }
4913
5083
  onDidConnect(handler) {
4914
5084
  this.socket.removeListener("okra.connected", handler);
@@ -4921,6 +5091,11 @@ var PHPSandbox = (() => {
4921
5091
  this.disposables.push(disposable);
4922
5092
  return disposable;
4923
5093
  }
5094
+ onDidBootError(handler) {
5095
+ const disposable = this.socket.listen("okra.boot_error", handler);
5096
+ this.disposables.push(disposable);
5097
+ return disposable;
5098
+ }
4924
5099
  update() {
4925
5100
  return this.invoke("notebook.update");
4926
5101
  }
@@ -4946,6 +5121,7 @@ var PHPSandbox = (() => {
4946
5121
  init_fn = function() {
4947
5122
  __privateSet(this, _initPromise, new Promise((resolve, reject) => {
4948
5123
  const initializationListener = this.onDidInitialize((result) => {
5124
+ bootErrorListener.dispose();
4949
5125
  initializationListener.dispose();
4950
5126
  this.initialized = result;
4951
5127
  if (result.type === "error") {
@@ -4954,6 +5130,11 @@ var PHPSandbox = (() => {
4954
5130
  }
4955
5131
  resolve(result);
4956
5132
  });
5133
+ const bootErrorListener = this.onDidBootError((error) => {
5134
+ initializationListener.dispose();
5135
+ bootErrorListener.dispose();
5136
+ reject(error);
5137
+ });
4957
5138
  }));
4958
5139
  return __privateGet(this, _initPromise);
4959
5140
  };