@phpsandbox/sdk 0.0.28 → 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,
@@ -574,8 +575,7 @@ var PHPSandbox = (() => {
574
575
  };
575
576
  const subscription = new FilesystemSubscription(path, this.okra, wrappedDisposable);
576
577
  this.watches.set(path, { options, path, onDidChange });
577
- this.okra.invoke("fs.watch", { path, options });
578
- return subscription;
578
+ return this.okra.invoke("fs.watch", { path, options }).then(() => subscription);
579
579
  }
580
580
  exists(path) {
581
581
  return this.stat(path).then(() => true).catch(() => false);
@@ -615,6 +615,13 @@ var PHPSandbox = (() => {
615
615
  return new _SendTimeoutError(error.message, error.time);
616
616
  }
617
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
+ };
618
625
 
619
626
  // src/utils/promise.ts
620
627
  var timeout = (prom, time) => {
@@ -1062,6 +1069,103 @@ var PHPSandbox = (() => {
1062
1069
  }
1063
1070
  };
1064
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
+
1065
1169
  // node_modules/@msgpack/msgpack/dist.esm/utils/utf8.mjs
1066
1170
  function utf8Count(str) {
1067
1171
  const strLength = str.length;
@@ -3085,6 +3189,7 @@ var PHPSandbox = (() => {
3085
3189
  this.closed = false;
3086
3190
  this.disposables = new NamedDisposable();
3087
3191
  this.connectPromise = null;
3192
+ this.terminalError = null;
3088
3193
  this.pingIntervalStarted = false;
3089
3194
  // Connection health monitoring
3090
3195
  this.connectionStats = {
@@ -3116,7 +3221,7 @@ var PHPSandbox = (() => {
3116
3221
  const startClosed = options.startClosed !== false;
3117
3222
  this.rws = new reconnecting_websocket_mjs_default(this.url.toString(), [], {
3118
3223
  WebSocket: globalThis.WebSocket ?? browser_default,
3119
- connectionTimeout: options.connectionTimeout ?? 1e3,
3224
+ connectionTimeout: options.connectionTimeout,
3120
3225
  maxReconnectionDelay: 2e3,
3121
3226
  minReconnectionDelay: 200,
3122
3227
  maxEnqueuedMessages: 0,
@@ -3159,6 +3264,9 @@ var PHPSandbox = (() => {
3159
3264
  * Ensure the websocket is open without requiring an application-level roundtrip.
3160
3265
  */
3161
3266
  connect() {
3267
+ if (this.terminalError) {
3268
+ return Promise.reject(this.terminalError);
3269
+ }
3162
3270
  return __privateMethod(this, _Transport_instances, connect_fn).call(this);
3163
3271
  }
3164
3272
  id() {
@@ -3215,7 +3323,12 @@ var PHPSandbox = (() => {
3215
3323
  return;
3216
3324
  }
3217
3325
  if (event === "Events.BootError" /* BootError */) {
3326
+ const error = new NotebookUnavailableError(
3327
+ data?.message || "Notebook is no longer available",
3328
+ data || {}
3329
+ );
3218
3330
  this.log("error", "Boot error received", { data });
3331
+ this.terminate(error, 4e3, error.message);
3219
3332
  return;
3220
3333
  }
3221
3334
  if (event === "response" /* Response */) {
@@ -3274,6 +3387,11 @@ var PHPSandbox = (() => {
3274
3387
  listen(event, listener, _context) {
3275
3388
  return this.eventEmitter.listen(event, listener);
3276
3389
  }
3390
+ onDidBootError(listener) {
3391
+ this.eventEmitter.listen("transport.boot_error", (event) => {
3392
+ listener(event.error);
3393
+ });
3394
+ }
3277
3395
  removeListener(event, listener) {
3278
3396
  this.eventEmitter.removeListener(event, listener);
3279
3397
  }
@@ -3300,6 +3418,9 @@ var PHPSandbox = (() => {
3300
3418
  get isClosed() {
3301
3419
  return this.closed;
3302
3420
  }
3421
+ getTerminalError() {
3422
+ return this.terminalError;
3423
+ }
3303
3424
  get status() {
3304
3425
  return {
3305
3426
  0: "CONNECTING",
@@ -3309,6 +3430,9 @@ var PHPSandbox = (() => {
3309
3430
  }[this.rws.readyState];
3310
3431
  }
3311
3432
  async call(action, data = {}, options = {}) {
3433
+ if (this.terminalError) {
3434
+ throw this.terminalError;
3435
+ }
3312
3436
  if (this.isRateLimited()) {
3313
3437
  throw new RateLimitError("Rate limit exceeded - too many requests");
3314
3438
  }
@@ -3325,6 +3449,10 @@ var PHPSandbox = (() => {
3325
3449
  this.eventEmitter.removeListener(errorEvent);
3326
3450
  };
3327
3451
  const handler = async (resolve, reject) => {
3452
+ if (this.terminalError) {
3453
+ reject(this.terminalError);
3454
+ return;
3455
+ }
3328
3456
  const abortError = new DOMException("Request aborted", "AbortError");
3329
3457
  if (options.abortSignal?.aborted) {
3330
3458
  reject(abortError);
@@ -3376,7 +3504,7 @@ var PHPSandbox = (() => {
3376
3504
  reject(new RateLimitError(_ev.reason || "Rate limit exceeded", _ev));
3377
3505
  return;
3378
3506
  }
3379
- 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"}`));
3380
3508
  };
3381
3509
  this.rws.addEventListener("close", closeHandler);
3382
3510
  this.rws.addEventListener("error", closeHandler);
@@ -3412,7 +3540,7 @@ var PHPSandbox = (() => {
3412
3540
  try {
3413
3541
  return await sender();
3414
3542
  } catch (e) {
3415
- 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) {
3416
3544
  this.log("debug", "Non-retryable error, bailing", {
3417
3545
  error: e.message,
3418
3546
  attempt
@@ -3464,6 +3592,9 @@ var PHPSandbox = (() => {
3464
3592
  * This preserves all event listeners and state.
3465
3593
  */
3466
3594
  reconnect() {
3595
+ if (this.terminalError) {
3596
+ throw this.terminalError;
3597
+ }
3467
3598
  if (this.closed) {
3468
3599
  throw new Error("Cannot reconnect a closed transport. The transport has been permanently closed.");
3469
3600
  }
@@ -3478,6 +3609,16 @@ var PHPSandbox = (() => {
3478
3609
  }
3479
3610
  this.close();
3480
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
+ }
3481
3622
  close(code, reason) {
3482
3623
  if (this.closed) {
3483
3624
  return;
@@ -3485,9 +3626,9 @@ var PHPSandbox = (() => {
3485
3626
  this.log("info", "Closing transport connection", { code, reason });
3486
3627
  this.connectPromise = null;
3487
3628
  const queuedCount = this.messageQueue.length;
3629
+ const closeError = this.terminalError || new Error("Connection closed while message was queued");
3488
3630
  this.messageQueue.forEach((msg) => {
3489
- console.log("Rejecting queued message due to connection close");
3490
- msg.reject(new Error("Connection closed while message was queued"));
3631
+ msg.reject(closeError);
3491
3632
  });
3492
3633
  this.messageQueue = [];
3493
3634
  if (queuedCount > 0) {
@@ -3575,6 +3716,9 @@ var PHPSandbox = (() => {
3575
3716
  this.log("error", "Connection closed due to policy violation");
3576
3717
  this.clearOldQueuedMessages();
3577
3718
  return "stop";
3719
+ case 4e3:
3720
+ this.log("info", "Connection closed due to terminal notebook error");
3721
+ return "stop";
3578
3722
  default:
3579
3723
  this.log("warn", `Unknown close code: ${code}, will reconnect`);
3580
3724
  return "reconnect";
@@ -3790,6 +3934,9 @@ var PHPSandbox = (() => {
3790
3934
  * by caching the connection promise.
3791
3935
  */
3792
3936
  connect_fn = function() {
3937
+ if (this.terminalError) {
3938
+ return Promise.reject(this.terminalError);
3939
+ }
3793
3940
  if (this.isConnected) {
3794
3941
  return Promise.resolve();
3795
3942
  }
@@ -4836,9 +4983,14 @@ var PHPSandbox = (() => {
4836
4983
  this.laravel = new Lravel(this);
4837
4984
  this.shell = new Shell(this);
4838
4985
  this.git = new Git(this);
4986
+ this.services = new Services(this);
4839
4987
  this.secrets = new NotebookSecrets(client, this.data.id);
4840
4988
  }
4841
4989
  async ready() {
4990
+ const terminalError = this.socket.getTerminalError();
4991
+ if (terminalError) {
4992
+ throw terminalError;
4993
+ }
4842
4994
  const ready = async () => {
4843
4995
  if (this.client.options.startClosed && !this.socket.isConnected) {
4844
4996
  await this.socket.connect();
@@ -4851,7 +5003,9 @@ var PHPSandbox = (() => {
4851
5003
  return this.client.notebook.fork(this.data.id);
4852
5004
  }
4853
5005
  delete() {
4854
- 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
+ });
4855
5009
  }
4856
5010
  stop() {
4857
5011
  return this.container.stop();
@@ -4875,6 +5029,10 @@ var PHPSandbox = (() => {
4875
5029
  !this.socket.isClosed && this.socket.disconnect();
4876
5030
  }
4877
5031
  connected() {
5032
+ const terminalError = this.socket.getTerminalError();
5033
+ if (terminalError) {
5034
+ return Promise.reject(terminalError);
5035
+ }
4878
5036
  if (this.socket.isConnected) {
4879
5037
  return Promise.resolve(this);
4880
5038
  }
@@ -4882,18 +5040,24 @@ var PHPSandbox = (() => {
4882
5040
  try {
4883
5041
  this.socket.onDidConnect(() => resolve(this));
4884
5042
  this.socket.onDidClose(() => reject(new Error("Connection closed")));
5043
+ this.socket.onDidBootError((error) => reject(error));
4885
5044
  } catch (e) {
4886
5045
  reject(e);
4887
5046
  }
4888
5047
  });
4889
5048
  }
4890
5049
  whenConnected() {
5050
+ const terminalError = this.socket.getTerminalError();
5051
+ if (terminalError) {
5052
+ return Promise.reject(terminalError);
5053
+ }
4891
5054
  if (this.socket.isConnected) {
4892
5055
  return Promise.resolve(this);
4893
5056
  }
4894
5057
  return new Promise((resolve, reject) => {
4895
5058
  try {
4896
5059
  this.socket.onDidConnect(() => resolve(this));
5060
+ this.socket.onDidBootError((error) => reject(error));
4897
5061
  } catch (e) {
4898
5062
  reject(e);
4899
5063
  }
@@ -4910,6 +5074,11 @@ var PHPSandbox = (() => {
4910
5074
  this.socket.emit("okra.disconnected");
4911
5075
  this.initialized = false;
4912
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
+ });
4913
5082
  }
4914
5083
  onDidConnect(handler) {
4915
5084
  this.socket.removeListener("okra.connected", handler);
@@ -4922,6 +5091,11 @@ var PHPSandbox = (() => {
4922
5091
  this.disposables.push(disposable);
4923
5092
  return disposable;
4924
5093
  }
5094
+ onDidBootError(handler) {
5095
+ const disposable = this.socket.listen("okra.boot_error", handler);
5096
+ this.disposables.push(disposable);
5097
+ return disposable;
5098
+ }
4925
5099
  update() {
4926
5100
  return this.invoke("notebook.update");
4927
5101
  }
@@ -4947,6 +5121,7 @@ var PHPSandbox = (() => {
4947
5121
  init_fn = function() {
4948
5122
  __privateSet(this, _initPromise, new Promise((resolve, reject) => {
4949
5123
  const initializationListener = this.onDidInitialize((result) => {
5124
+ bootErrorListener.dispose();
4950
5125
  initializationListener.dispose();
4951
5126
  this.initialized = result;
4952
5127
  if (result.type === "error") {
@@ -4955,6 +5130,11 @@ var PHPSandbox = (() => {
4955
5130
  }
4956
5131
  resolve(result);
4957
5132
  });
5133
+ const bootErrorListener = this.onDidBootError((error) => {
5134
+ initializationListener.dispose();
5135
+ bootErrorListener.dispose();
5136
+ reject(error);
5137
+ });
4958
5138
  }));
4959
5139
  return __privateGet(this, _initPromise);
4960
5140
  };