@phpsandbox/sdk 0.1.1 → 0.1.2

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.
@@ -29,7 +29,7 @@ var PHPSandbox = (() => {
29
29
  var index_exports = {};
30
30
  __export(index_exports, {
31
31
  ApiError: () => ApiError,
32
- Client: () => Client2,
32
+ Client: () => Client,
33
33
  CommandError: () => CommandError,
34
34
  ComposerCredentials: () => ComposerCredentials,
35
35
  ErrorEvent: () => ErrorEvent,
@@ -43,7 +43,7 @@ var PHPSandbox = (() => {
43
43
  NotebookApi: () => NotebookApi,
44
44
  NotebookComposerCredentialApi: () => NotebookComposerCredentialApi,
45
45
  NotebookInitError: () => NotebookInitError,
46
- NotebookInstance: () => NotebookInstance2,
46
+ NotebookInstance: () => NotebookInstance,
47
47
  NotebookMail: () => NotebookMail,
48
48
  NotebookMailApi: () => NotebookMailApi,
49
49
  NotebookPreview: () => NotebookPreview,
@@ -55,6 +55,7 @@ var PHPSandbox = (() => {
55
55
  NotebookUnavailableError: () => NotebookUnavailableError,
56
56
  PHPSandbox: () => PHPSandbox,
57
57
  PHPSandboxError: () => PHPSandboxError,
58
+ ProcessReadable: () => ProcessReadable,
58
59
  ProcessResult: () => ProcessResult,
59
60
  PromiseTimeoutError: () => PromiseTimeoutError,
60
61
  PublicationApi: () => PublicationApi,
@@ -89,6 +90,7 @@ var PHPSandbox = (() => {
89
90
  PublishStreamSsePhaseEventSchema: () => PublishStreamSsePhaseEventSchema,
90
91
  PublishStreamSseResultEventSchema: () => PublishStreamSseResultEventSchema,
91
92
  RateLimitError: () => RateLimitError,
93
+ RuntimeTelemetry: () => RuntimeTelemetry,
92
94
  SendTimeoutError: () => SendTimeoutError,
93
95
  ServerApi: () => ServerApi,
94
96
  ServerInstance: () => ServerInstance,
@@ -97,7 +99,8 @@ var PHPSandbox = (() => {
97
99
  notebookBuiltinServices: () => notebookBuiltinServices,
98
100
  notebookKnownServices: () => notebookKnownServices,
99
101
  once: () => once,
100
- timeout: () => timeout
102
+ timeout: () => timeout,
103
+ watchRuntimeTelemetry: () => watchRuntimeTelemetry
101
104
  });
102
105
 
103
106
  // src/types.ts
@@ -197,25 +200,119 @@ var PHPSandbox = (() => {
197
200
  }
198
201
  });
199
202
  }
200
- info(path) {
201
- return this.okra.invoke("fs.info", { path });
203
+ async write(path, contents, options = {}) {
204
+ const resolvedOptions = {
205
+ overwrite: true,
206
+ create: true,
207
+ unlock: false,
208
+ atomic: false,
209
+ ...options
210
+ };
211
+ const source = typeof contents === "string" ? new TextEncoder().encode(contents) : contents;
212
+ if (this.okra.runtimeTransport === "rest") {
213
+ const bytes = source instanceof Uint8Array ? source : await collectBytes(source);
214
+ await this.okra.invoke("fs.writeFile", {
215
+ path,
216
+ contents: bytes,
217
+ options: resolvedOptions
218
+ }).catch((error51) => this.handleError(error51));
219
+ return;
220
+ }
221
+ const id = nanoid();
222
+ const completion = this.okra.invoke("fs.writeStream", { id, path, options: resolvedOptions });
223
+ void completion.catch(() => {
224
+ });
225
+ try {
226
+ for await (const chunk of toAsyncIterable(source)) {
227
+ const written = await this.okra.invoke("fs.writeChunk", { id, contents: chunk });
228
+ if (!written) {
229
+ throw new Error(`File write stream ${id} is not available`);
230
+ }
231
+ }
232
+ const ended = await this.okra.invoke("fs.endWrite", { id });
233
+ if (!ended) {
234
+ throw new Error(`File write stream ${id} could not be completed`);
235
+ }
236
+ await completion;
237
+ } catch (error51) {
238
+ await this.okra.invoke("fs.abortWrite", { id }).catch(() => false);
239
+ await completion.catch(() => false);
240
+ throw error51;
241
+ }
202
242
  }
203
- async write(path, contents, options = { overwrite: true, create: true, unlock: false, atomic: false }) {
204
- if (typeof contents === "string") {
205
- return this.okra.invoke("fs.write", { path, contents });
243
+ async read(path) {
244
+ try {
245
+ const content = await this.okra.invoke("fs.readFile", { path });
246
+ return content instanceof Uint8Array ? content : new TextEncoder().encode(content);
247
+ } catch (error51) {
248
+ this.handleError(error51);
206
249
  }
207
- const bytes = contents instanceof Uint8Array ? contents : await collectBytes(contents);
208
- await this.writeFile(path, bytes, options);
209
- return true;
210
250
  }
211
- read(path) {
251
+ async readText(path) {
252
+ return new TextDecoder().decode(await this.read(path));
253
+ }
254
+ async readLines(path, range) {
255
+ try {
256
+ const result = await this.okra.invoke("fs.readFile", {
257
+ path,
258
+ lineRange: {
259
+ lineStart: range.start,
260
+ lineEnd: range.end
261
+ }
262
+ });
263
+ if (typeof result === "string" || result instanceof Uint8Array) {
264
+ throw new Error(`Unexpected ranged file response for ${path}`);
265
+ }
266
+ return {
267
+ start: result.lineStart,
268
+ end: result.lineEnd,
269
+ content: result.content,
270
+ ...result.error === null ? {} : { warning: result.error }
271
+ };
272
+ } catch (error51) {
273
+ this.handleError(error51);
274
+ }
275
+ }
276
+ readStream(path) {
277
+ if (this.okra.runtimeTransport === "rest") {
278
+ return new ReadableStream({
279
+ start: async (controller) => {
280
+ try {
281
+ controller.enqueue(await this.read(path));
282
+ controller.close();
283
+ } catch (error51) {
284
+ controller.error(error51);
285
+ }
286
+ }
287
+ });
288
+ }
289
+ const id = nanoid();
290
+ let disposable = null;
291
+ let closed = false;
212
292
  return new ReadableStream({
213
- start: async (controller) => {
214
- try {
215
- controller.enqueue(await this.readFile(path));
216
- controller.close();
217
- } catch (error51) {
218
- controller.error(error51);
293
+ start: (controller) => {
294
+ disposable = this.okra.listen(`fs.read.${id}`, (event) => {
295
+ controller.enqueue(event.data);
296
+ });
297
+ void this.okra.invoke("fs.readStream", { id, path }).then(() => {
298
+ if (!closed) {
299
+ closed = true;
300
+ disposable?.dispose();
301
+ controller.close();
302
+ }
303
+ }).catch((error51) => {
304
+ if (!closed) {
305
+ closed = true;
306
+ disposable?.dispose();
307
+ controller.error(error51);
308
+ }
309
+ });
310
+ },
311
+ cancel: async () => {
312
+ if (!closed) {
313
+ closed = true;
314
+ disposable?.dispose();
315
+ await this.okra.invoke("fs.cancelRead", { id }).catch(() => false);
219
316
  }
220
317
  }
221
318
  });
@@ -297,53 +394,38 @@ var PHPSandbox = (() => {
297
394
  const disposable = this.okra.listen(`fs.text.search.${sid}`, localOnMatch);
298
395
  return this.okra.invoke("fs.textSearch", { query, options: { ...defaultOptions, ...options } }).finally(() => disposable.dispose());
299
396
  }
300
- mkdir(path) {
301
- return this.okra.invoke("fs.mkdir", { path });
302
- }
303
- move(from, to) {
304
- return this.okra.invoke("fs.move", { from, to });
305
- }
306
- remove(path, type) {
307
- return this.okra.invoke("fs.remove", { path, type });
308
- }
309
- async readFile(path, lineRange) {
310
- try {
311
- const content = await this.okra.invoke("fs.readFile", { path, lineRange });
312
- if (content instanceof Uint8Array) {
313
- return content;
314
- }
315
- if (typeof content === "string") {
316
- return new TextEncoder().encode(content);
317
- }
318
- return content;
319
- } catch (e) {
320
- this.handleError(e);
321
- }
322
- }
323
- tail(path, lines = 10) {
324
- return this.okra.invoke("fs.tail", { path, lines }).catch((e) => this.handleError(e));
325
- }
326
- writeFile(path, contents, options) {
327
- return this.okra.invoke("fs.writeFile", {
328
- path,
329
- contents,
330
- options
331
- }).catch((e) => this.handleError(e));
397
+ tail(path, options = {}) {
398
+ return this.okra.invoke("fs.tail", { path, lines: options.lines ?? 10 }).catch((e) => this.handleError(e));
332
399
  }
333
400
  stat(path) {
334
401
  return this.okra.invoke("fs.stat", { path }).catch((e) => this.handleError(e));
335
402
  }
336
- rename(from, to, options) {
337
- return this.okra.invoke("fs.rename", { from, to, options }).catch((e) => this.handleError(e));
403
+ move(from, to, options = {}) {
404
+ return this.okra.invoke("fs.rename", {
405
+ from,
406
+ to,
407
+ options: { overwrite: false, ...options }
408
+ }).catch((e) => this.handleError(e));
338
409
  }
339
- delete(path, options) {
340
- return this.okra.invoke("fs.delete", { path, options }).catch((e) => this.handleError(e));
410
+ remove(path, options = {}) {
411
+ return this.okra.invoke("fs.delete", {
412
+ path,
413
+ options: { recursive: false, useTrash: false, atomic: false, ...options }
414
+ }).catch((e) => this.handleError(e));
341
415
  }
342
- copy(source, destination, options) {
343
- return this.okra.invoke("fs.copy", { source, destination, options }).catch((e) => this.handleError(e));
416
+ copy(source, destination, options = {}) {
417
+ return this.okra.invoke("fs.copy", {
418
+ source,
419
+ destination,
420
+ options: { overwrite: false, ...options }
421
+ }).catch((e) => this.handleError(e));
344
422
  }
345
- readDirectory(path, include = [], exclude = []) {
346
- return this.okra.invoke("fs.readDirectory", { path, include, exclude }).catch((e) => this.handleError(e));
423
+ list(path, options = {}) {
424
+ return this.okra.invoke("fs.readDirectory", {
425
+ path,
426
+ include: options.include ?? [],
427
+ exclude: options.exclude ?? []
428
+ }).catch((e) => this.handleError(e));
347
429
  }
348
430
  createDirectory(path) {
349
431
  return this.okra.invoke("fs.createDirectory", { path }).catch((e) => this.handleError(e));
@@ -372,17 +454,17 @@ var PHPSandbox = (() => {
372
454
  exists(path) {
373
455
  return this.stat(path).then(() => true).catch(() => false);
374
456
  }
375
- async download(chunk, exclude, include) {
457
+ async download(options = {}) {
376
458
  const id = nanoid();
377
- const stream = !!chunk;
459
+ const stream = !!options.onChunk;
378
460
  const chunks = [];
379
461
  const disposable = this.okra.listen(
380
462
  `fs.download.${id}`,
381
- stream ? chunk : (data) => {
463
+ stream ? options.onChunk : (data) => {
382
464
  chunks.push(data);
383
465
  }
384
466
  );
385
- return this.okra.invoke("fs.download", { id, exclude, include }).then(() => new Blob(chunks, { type: "application/octet-stream" })).finally(() => disposable.dispose());
467
+ return this.okra.invoke("fs.download", { id, exclude: options.exclude, include: options.include }).then(() => new Blob(chunks, { type: "application/octet-stream" })).finally(() => disposable.dispose());
386
468
  }
387
469
  tree(path = "/") {
388
470
  return this.okra.invoke("fs.tree", { path });
@@ -394,6 +476,13 @@ var PHPSandbox = (() => {
394
476
  throw e;
395
477
  }
396
478
  };
479
+ async function* toAsyncIterable(contents) {
480
+ if (contents instanceof Uint8Array) {
481
+ yield contents;
482
+ return;
483
+ }
484
+ yield* contents;
485
+ }
397
486
  async function collectBytes(stream) {
398
487
  const chunks = [];
399
488
  let length = 0;
@@ -454,30 +543,35 @@ var PHPSandbox = (() => {
454
543
  // src/terminal.ts
455
544
  var TERMINAL_INPUT_FLUSH_DELAY_MS = 8;
456
545
  var terminalInputFlushPattern = /[\x00-\x1f\x7f]/;
457
- var Terminal = class {
546
+ var Terminals = class {
458
547
  constructor(okra) {
459
548
  this.okra = okra;
460
549
  }
461
550
  list() {
462
551
  return this.okra.invoke("terminal.list");
463
552
  }
464
- create(input) {
465
- return this.okra.invoke("terminal.create", input);
466
- }
467
- onStarted(handler) {
468
- return this.okra.listen("terminal.started", handler);
469
- }
470
- resize(id, size) {
471
- return this.okra.invoke("terminal.resize", { id, width: size[0], height: size[1] });
472
- }
473
- onOutput(id, handler) {
474
- return this.okra.listen(`terminal.output.${id}`, handler);
475
- }
476
- input(id, input) {
477
- return Promise.resolve(this.okra.send("terminal.input", { id, input }));
478
- }
479
- listen(event, handler) {
480
- return this.okra.listen(event, handler);
553
+ async create(options = {}) {
554
+ const id = options.id ?? nanoid();
555
+ const handle = this.processHandle(id, options.abortSignal);
556
+ const command = typeof options.command === "string" && options.args?.length ? [options.command, ...options.args] : options.command;
557
+ const { abortSignal, args: _args, ...input } = options;
558
+ try {
559
+ const task = await this.okra.invoke(
560
+ "terminal.create",
561
+ {
562
+ ...input,
563
+ id,
564
+ kind: options.kind ?? "bash",
565
+ size: options.size ?? [80, 24],
566
+ command
567
+ },
568
+ { abortSignal }
569
+ );
570
+ return { ...handle.process, ...task };
571
+ } catch (error51) {
572
+ handle.dispose();
573
+ throw error51;
574
+ }
481
575
  }
482
576
  async attach(id, opts) {
483
577
  const handle = this.processHandle(id, opts?.abortSignal);
@@ -503,31 +597,6 @@ var PHPSandbox = (() => {
503
597
  throw error51;
504
598
  }
505
599
  }
506
- async spawn(command, args, opts) {
507
- const id = opts?.id || nanoid();
508
- const handle = this.processHandle(id, opts?.abortSignal);
509
- const { abortSignal, ...otherOpts } = opts ?? {};
510
- try {
511
- const result = await this.okra.invoke(
512
- "terminal.spawn",
513
- {
514
- command: [command, ...args],
515
- opts: {
516
- id,
517
- ...otherOpts
518
- }
519
- },
520
- { abortSignal }
521
- );
522
- return {
523
- ...handle.process,
524
- ...result
525
- };
526
- } catch (error51) {
527
- handle.dispose();
528
- throw error51;
529
- }
530
- }
531
600
  processHandle(id, abortSignal) {
532
601
  const disposables = /* @__PURE__ */ new Set();
533
602
  let abortHandler;
@@ -553,7 +622,7 @@ var PHPSandbox = (() => {
553
622
  }
554
623
  const input2 = pendingInput;
555
624
  pendingInput = "";
556
- void this.input(id, input2);
625
+ void this.okra.send("terminal.input", { id, input: input2 });
557
626
  };
558
627
  const scheduleInputFlush = () => {
559
628
  if (inputFlushTimer) {
@@ -561,7 +630,8 @@ var PHPSandbox = (() => {
561
630
  }
562
631
  inputFlushTimer = setTimeout(flushInput, TERMINAL_INPUT_FLUSH_DELAY_MS);
563
632
  };
564
- const queueInput = (chunk) => {
633
+ const queueInput = (value) => {
634
+ const chunk = typeof value === "string" ? value : new TextDecoder().decode(value);
565
635
  pendingInput += chunk;
566
636
  if (terminalInputFlushPattern.test(chunk)) {
567
637
  flushInput();
@@ -583,7 +653,7 @@ var PHPSandbox = (() => {
583
653
  start: (_controller) => {
584
654
  controller = _controller;
585
655
  disposables.add(
586
- this.listen(`terminal.output.${id}`, (data) => {
656
+ this.okra.listen(`terminal.output.${id}`, (data) => {
587
657
  controller?.enqueue(data.output);
588
658
  })
589
659
  );
@@ -595,7 +665,7 @@ var PHPSandbox = (() => {
595
665
  });
596
666
  const completion = new Promise((resolve) => {
597
667
  disposables.add(
598
- this.listen(`terminal.close.${id}`, (data) => {
668
+ this.okra.listen(`terminal.close.${id}`, (data) => {
599
669
  if (controller) {
600
670
  try {
601
671
  controller.close();
@@ -660,37 +730,9 @@ var PHPSandbox = (() => {
660
730
  state() {
661
731
  return this.okra.invoke("container.state");
662
732
  }
663
- openedPorts() {
664
- return this.okra.invoke("container.opened-ports");
665
- }
666
733
  setPhp(version2) {
667
734
  return this.okra.invoke("container.set-php", { version: version2 });
668
735
  }
669
- enableTelemetry(features) {
670
- return this.okra.invoke("container.stream-telemetry", { features: Array.from(features) });
671
- }
672
- stopTelemetry() {
673
- return this.okra.invoke("container.stop-telemetry");
674
- }
675
- listen(event, handler) {
676
- return this.okra.listen(event, handler);
677
- }
678
- onPort(handler) {
679
- let ports = [];
680
- return this.listen("container.ports", (newPorts) => {
681
- for (const port of newPorts) {
682
- if (!ports.some((p) => p.port === port.port)) {
683
- handler(port, "open");
684
- }
685
- }
686
- for (const port of ports) {
687
- if (!newPorts.some((p) => p.port === port.port)) {
688
- handler(port, "close");
689
- }
690
- }
691
- ports = newPorts;
692
- });
693
- }
694
736
  };
695
737
 
696
738
  // src/auth.ts
@@ -777,76 +819,6 @@ var PHPSandbox = (() => {
777
819
  }
778
820
  };
779
821
 
780
- // src/composer.ts
781
- var Composer = class {
782
- constructor(okra, client) {
783
- this.okra = okra;
784
- this.credentials = new ComposerCredentials(client, okra.data.id);
785
- }
786
- invoke(command, args = {}, options = {}) {
787
- return this.okra.invoke("composer.invoke", { command, args, options });
788
- }
789
- dumpAutoload() {
790
- return this.okra.invoke("composer.dump-autoload");
791
- }
792
- install(options = {}) {
793
- return this.invoke("install", {}, options);
794
- }
795
- update(args, options = {}) {
796
- return this.invoke("update", args, options);
797
- }
798
- require(packages, options = {}) {
799
- return this.invoke("require", packages, options);
800
- }
801
- remove(args, options = {}) {
802
- return this.invoke("remove", args, options);
803
- }
804
- stream(fn) {
805
- return this.okra.listen("composer.log", fn);
806
- }
807
- packages() {
808
- return this.okra.invoke("composer.packages");
809
- }
810
- };
811
-
812
- // src/log.ts
813
- var Log = class {
814
- constructor(okra) {
815
- this.okra = okra;
816
- }
817
- stream(fn) {
818
- return this.okra.listen("notebook.log", fn);
819
- }
820
- listen(event, handler) {
821
- return this.okra.listen(event, handler);
822
- }
823
- };
824
-
825
- // src/repl.ts
826
- var Repl = class {
827
- constructor(okra) {
828
- this.okra = okra;
829
- }
830
- eval(code, args = "", repl = { prependWith: "", appendWith: "" }) {
831
- return this.okra.invoke("repl.eval", { code, args, repl });
832
- }
833
- write(input) {
834
- return this.okra.invoke("repl.write", { input });
835
- }
836
- stop() {
837
- return this.okra.invoke("repl.stop");
838
- }
839
- resize(cols, rows) {
840
- return this.okra.invoke("repl.resize", { cols, rows });
841
- }
842
- listen(event, handler) {
843
- return this.okra.listen(event, handler);
844
- }
845
- onOutput(handler) {
846
- return this.okra.listen("repl.output", handler);
847
- }
848
- };
849
-
850
822
  // src/shell.ts
851
823
  function resolveRunInput(command, argsOrOpts = [], opts) {
852
824
  const args = Array.isArray(argsOrOpts) ? argsOrOpts : [];
@@ -876,7 +848,7 @@ var PHPSandbox = (() => {
876
848
  return this;
877
849
  }
878
850
  };
879
- var OutputStream = class {
851
+ var ProcessReadable = class {
880
852
  constructor() {
881
853
  this.controller = null;
882
854
  this.closed = false;
@@ -916,9 +888,9 @@ var PHPSandbox = (() => {
916
888
  constructor(id, okra, command, opts) {
917
889
  this.id = id;
918
890
  this.okra = okra;
919
- const output = new OutputStream();
920
- const stdout = new OutputStream();
921
- const stderr = new OutputStream();
891
+ const output = new ProcessReadable();
892
+ const stdout = new ProcessReadable();
893
+ const stderr = new ProcessReadable();
922
894
  this.output = output.stream;
923
895
  this.stdout = stdout.stream;
924
896
  this.stderr = stderr.stream;
@@ -987,6 +959,264 @@ var PHPSandbox = (() => {
987
959
  }
988
960
  };
989
961
 
962
+ // src/composer.ts
963
+ var ComposerProcess = class {
964
+ constructor(id, okra, command, args, options) {
965
+ this.id = id;
966
+ this.okra = okra;
967
+ const output = new ProcessReadable();
968
+ const stdout = new ProcessReadable();
969
+ const stderr = new ProcessReadable();
970
+ this.output = output.stream;
971
+ this.stdout = stdout.stream;
972
+ this.stderr = stderr.stream;
973
+ const encoder = new TextEncoder();
974
+ let subscription = this.okra.listen(`composer.output.${this.id}`, (event) => {
975
+ const data = encoder.encode(event.output);
976
+ const source = event.source ?? "output";
977
+ output.enqueue({ source, data });
978
+ if (source === "stderr") {
979
+ stderr.enqueue(data);
980
+ } else {
981
+ stdout.enqueue(data);
982
+ }
983
+ });
984
+ const finish = () => {
985
+ subscription?.dispose();
986
+ subscription = null;
987
+ output.close();
988
+ stdout.close();
989
+ stderr.close();
990
+ };
991
+ const fail = (error51) => {
992
+ subscription?.dispose();
993
+ subscription = null;
994
+ output.fail(error51);
995
+ stdout.fail(error51);
996
+ stderr.fail(error51);
997
+ };
998
+ this.completion = this.okra.invoke("composer.invoke", { id, command, args, options }).then((result) => {
999
+ finish();
1000
+ return new ProcessResult(result.output, result.stdout, result.stderr, result.exitCode);
1001
+ }).catch((error51) => {
1002
+ fail(error51);
1003
+ throw error51;
1004
+ });
1005
+ }
1006
+ wait() {
1007
+ return this.completion;
1008
+ }
1009
+ async kill() {
1010
+ await this.okra.invoke("composer.kill", { id: this.id });
1011
+ }
1012
+ };
1013
+ var Composer = class {
1014
+ constructor(okra, client) {
1015
+ this.okra = okra;
1016
+ this.credentials = new ComposerCredentials(client, okra.data.id);
1017
+ }
1018
+ run(command, args = {}, options = {}) {
1019
+ return new ComposerProcess(nanoid(), this.okra, command, args, options);
1020
+ }
1021
+ dumpAutoload(options = {}) {
1022
+ return this.run("dump-autoload", {}, options);
1023
+ }
1024
+ install(options = {}) {
1025
+ return this.run("install", {}, options);
1026
+ }
1027
+ update(args, options = {}) {
1028
+ return this.run("update", args, options);
1029
+ }
1030
+ require(packages, options = {}) {
1031
+ return this.run("require", packages, options);
1032
+ }
1033
+ remove(args, options = {}) {
1034
+ return this.run("remove", args, options);
1035
+ }
1036
+ packages() {
1037
+ return this.okra.invoke("composer.packages");
1038
+ }
1039
+ };
1040
+
1041
+ // src/runtime-telemetry.ts
1042
+ var RuntimeTelemetry = class {
1043
+ constructor(okra) {
1044
+ this.okra = okra;
1045
+ this.references = /* @__PURE__ */ new Map();
1046
+ this.synchronization = Promise.resolve();
1047
+ if (okra.runtimeTransport === "realtime") {
1048
+ okra.onDidConnect(() => {
1049
+ if (this.references.size > 0) {
1050
+ void this.synchronize();
1051
+ }
1052
+ });
1053
+ }
1054
+ }
1055
+ async acquire(feature) {
1056
+ this.references.set(feature, (this.references.get(feature) ?? 0) + 1);
1057
+ try {
1058
+ await this.synchronize();
1059
+ } catch (error51) {
1060
+ this.removeReference(feature);
1061
+ throw error51;
1062
+ }
1063
+ let released = false;
1064
+ return async () => {
1065
+ if (released) {
1066
+ return;
1067
+ }
1068
+ released = true;
1069
+ this.removeReference(feature);
1070
+ await this.synchronize();
1071
+ };
1072
+ }
1073
+ removeReference(feature) {
1074
+ const references = this.references.get(feature) ?? 0;
1075
+ if (references <= 1) {
1076
+ this.references.delete(feature);
1077
+ } else {
1078
+ this.references.set(feature, references - 1);
1079
+ }
1080
+ }
1081
+ synchronize() {
1082
+ this.synchronization = this.synchronization.catch(() => {
1083
+ }).then(async () => {
1084
+ const features = Array.from(this.references.keys());
1085
+ if (features.length === 0) {
1086
+ await this.okra.invoke("container.stop-telemetry");
1087
+ } else {
1088
+ await this.okra.invoke("container.stream-telemetry", { features });
1089
+ }
1090
+ });
1091
+ return this.synchronization;
1092
+ }
1093
+ };
1094
+ function watchRuntimeTelemetry(okra, telemetry, feature, event, transform2, options = {}) {
1095
+ let subscription = null;
1096
+ let release = null;
1097
+ let closed = false;
1098
+ let abort = null;
1099
+ const stop = async () => {
1100
+ if (closed) {
1101
+ return;
1102
+ }
1103
+ closed = true;
1104
+ subscription?.dispose();
1105
+ subscription = null;
1106
+ if (abort && options.signal) {
1107
+ options.signal.removeEventListener("abort", abort);
1108
+ }
1109
+ abort = null;
1110
+ await release?.();
1111
+ release = null;
1112
+ };
1113
+ return new ReadableStream({
1114
+ start(controller) {
1115
+ subscription = okra.listen(event, (data) => {
1116
+ if (!closed) {
1117
+ controller.enqueue(transform2(data));
1118
+ }
1119
+ });
1120
+ if (options.signal) {
1121
+ abort = () => {
1122
+ void stop().finally(() => controller.error(options.signal?.reason ?? new Error("Aborted")));
1123
+ };
1124
+ if (options.signal.aborted) {
1125
+ abort();
1126
+ return;
1127
+ }
1128
+ options.signal.addEventListener("abort", abort, { once: true });
1129
+ }
1130
+ void telemetry.acquire(feature).then(async (releaseTelemetry) => {
1131
+ if (closed) {
1132
+ await releaseTelemetry();
1133
+ return;
1134
+ }
1135
+ release = releaseTelemetry;
1136
+ }).catch((error51) => {
1137
+ if (!closed) {
1138
+ closed = true;
1139
+ subscription?.dispose();
1140
+ if (abort && options.signal) {
1141
+ options.signal.removeEventListener("abort", abort);
1142
+ }
1143
+ abort = null;
1144
+ controller.error(error51);
1145
+ }
1146
+ });
1147
+ },
1148
+ cancel: stop
1149
+ });
1150
+ }
1151
+
1152
+ // src/log.ts
1153
+ var Logs = class {
1154
+ constructor(okra, telemetry) {
1155
+ this.okra = okra;
1156
+ this.telemetry = telemetry;
1157
+ }
1158
+ follow(options = {}) {
1159
+ return watchRuntimeTelemetry(this.okra, this.telemetry, "logs", "notebook.log", (message) => ({
1160
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1161
+ source: "runtime",
1162
+ message
1163
+ }), options);
1164
+ }
1165
+ };
1166
+
1167
+ // src/metrics.ts
1168
+ var Metrics = class {
1169
+ constructor(okra, telemetry) {
1170
+ this.okra = okra;
1171
+ this.telemetry = telemetry;
1172
+ }
1173
+ current() {
1174
+ return this.okra.invoke("container.stats");
1175
+ }
1176
+ watch(options = {}) {
1177
+ return watchRuntimeTelemetry(this.okra, this.telemetry, "stats", "container.stats", (stats) => stats, options);
1178
+ }
1179
+ };
1180
+
1181
+ // src/ports.ts
1182
+ var Ports = class {
1183
+ constructor(okra, telemetry) {
1184
+ this.okra = okra;
1185
+ this.telemetry = telemetry;
1186
+ }
1187
+ list() {
1188
+ return this.okra.invoke("container.opened-ports");
1189
+ }
1190
+ watch(options = {}) {
1191
+ return watchRuntimeTelemetry(this.okra, this.telemetry, "ports", "container.ports", (ports) => ports, options);
1192
+ }
1193
+ };
1194
+
1195
+ // src/repl.ts
1196
+ var Repl = class {
1197
+ constructor(okra) {
1198
+ this.okra = okra;
1199
+ }
1200
+ eval(code, args = "", repl = { prependWith: "", appendWith: "" }) {
1201
+ return this.okra.invoke("repl.eval", { code, args, repl });
1202
+ }
1203
+ write(input) {
1204
+ return this.okra.invoke("repl.write", { input });
1205
+ }
1206
+ stop() {
1207
+ return this.okra.invoke("repl.stop");
1208
+ }
1209
+ resize(cols, rows) {
1210
+ return this.okra.invoke("repl.resize", { cols, rows });
1211
+ }
1212
+ listen(event, handler) {
1213
+ return this.okra.listen(event, handler);
1214
+ }
1215
+ onOutput(handler) {
1216
+ return this.okra.listen("repl.output", handler);
1217
+ }
1218
+ };
1219
+
990
1220
  // src/services.ts
991
1221
  var notebookBuiltinServices = ["redis"];
992
1222
  var notebookKnownServices = ["start", "nginx", ...notebookBuiltinServices];
@@ -1006,12 +1236,6 @@ var PHPSandbox = (() => {
1006
1236
  stop(name) {
1007
1237
  return this.okra.invoke("service.stop", { name });
1008
1238
  }
1009
- onLog(id, handler) {
1010
- return this.okra.listen(`service.log.${id}`, handler);
1011
- }
1012
- listen(event, handler) {
1013
- return this.okra.listen(event, handler);
1014
- }
1015
1239
  logs(name, options = {}) {
1016
1240
  if (options.follow) {
1017
1241
  return this.followLogs(name, options);
@@ -1064,11 +1288,11 @@ var PHPSandbox = (() => {
1064
1288
  close();
1065
1289
  return this.okra.invoke("service.stop-logs", { id });
1066
1290
  });
1067
- const output = new ReadableStream({
1291
+ return new ReadableStream({
1068
1292
  start: (_controller) => {
1069
1293
  controller = _controller;
1070
1294
  disposables.add(
1071
- this.onLog(id, (data) => {
1295
+ this.okra.listen(`service.log.${id}`, (data) => {
1072
1296
  controller?.enqueue(data.output);
1073
1297
  })
1074
1298
  );
@@ -1082,10 +1306,9 @@ var PHPSandbox = (() => {
1082
1306
  });
1083
1307
  },
1084
1308
  cancel: () => {
1085
- void stop();
1309
+ return stop().then(() => void 0);
1086
1310
  }
1087
1311
  });
1088
- return { id, output, stop };
1089
1312
  }
1090
1313
  };
1091
1314
 
@@ -19213,6 +19436,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19213
19436
  "container.stop": { method: "POST", path: "/runtime/container/stop" },
19214
19437
  "container.set-php": { method: "PUT", path: "/runtime/container/php" },
19215
19438
  "container.opened-ports": { method: "GET", path: "/runtime/container/ports" },
19439
+ "container.stats": { method: "GET", path: "/runtime/container/stats" },
19216
19440
  "terminal.list": { method: "GET", path: "/runtime/terminals" },
19217
19441
  "shell.exec": { method: "POST", path: "/runtime/executions" },
19218
19442
  "fs.info": { method: "POST", path: "/runtime/files/info" },
@@ -19238,10 +19462,6 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19238
19462
  "service.logs": { method: "POST", path: "/runtime/services/logs" },
19239
19463
  "composer.invoke": { method: "POST", path: "/runtime/composer/executions" },
19240
19464
  "composer.packages": { method: "GET", path: "/runtime/composer/packages" },
19241
- "composer.dump-autoload": {
19242
- method: "POST",
19243
- path: "/runtime/composer/autoload"
19244
- },
19245
19465
  "git.checkpoint": { method: "POST", path: "/runtime/git/checkpoints" },
19246
19466
  "git.checkout": { method: "POST", path: "/runtime/git/checkouts" },
19247
19467
  "git.log": { method: "POST", path: "/runtime/git/log" },
@@ -19485,7 +19705,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19485
19705
  }
19486
19706
  async create(template, input = {}, init = true) {
19487
19707
  const response = await this.client.post("/notebook", { template, ...input });
19488
- const instance = new NotebookInstance2(response.data, this.client);
19708
+ const instance = new NotebookInstance(response.data, this.client);
19489
19709
  if (!init) {
19490
19710
  return instance;
19491
19711
  }
@@ -19493,25 +19713,25 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19493
19713
  }
19494
19714
  async get(id) {
19495
19715
  const response = await this.client.get(`/notebook/${id}`);
19496
- return new NotebookInstance2(response.data, this.client);
19716
+ return new NotebookInstance(response.data, this.client);
19497
19717
  }
19498
19718
  async update(id, input = {}) {
19499
19719
  const response = await this.client.put(`/notebook/${id}`, input);
19500
- return new NotebookInstance2(response.data, this.client);
19720
+ return new NotebookInstance(response.data, this.client);
19501
19721
  }
19502
19722
  async destroy(id) {
19503
19723
  await this.client.delete(`/notebook/${id}`);
19504
19724
  }
19505
19725
  async fork(id, input = {}) {
19506
19726
  const response = await this.client.post(`/notebook/${id}/fork`, input);
19507
- return new NotebookInstance2(response.data, this.client);
19727
+ return new NotebookInstance(response.data, this.client);
19508
19728
  }
19509
19729
  async gitCredentials(id) {
19510
19730
  const response = await this.client.post(`/notebook/${id}/git/credentials`);
19511
19731
  return response.data;
19512
19732
  }
19513
19733
  openFromData(data) {
19514
- return this.init(new NotebookInstance2(data, this.client));
19734
+ return this.init(new NotebookInstance(data, this.client));
19515
19735
  }
19516
19736
  async init(instance) {
19517
19737
  await instance.ready();
@@ -19526,7 +19746,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19526
19746
  baseUrl.hash = "";
19527
19747
  return baseUrl.toString();
19528
19748
  }
19529
- var Client2 = class {
19749
+ var Client = class {
19530
19750
  constructor(token, url2 = "https://api.phpsandbox.io/v1", options = {}) {
19531
19751
  this.runtimeTransportValue = "realtime";
19532
19752
  this.fetch = globalThis.fetch;
@@ -19610,7 +19830,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19610
19830
  return { data: body === "" ? void 0 : JSON.parse(body) };
19611
19831
  }
19612
19832
  };
19613
- var PHPSandbox = class _PHPSandbox extends Client2 {
19833
+ var PHPSandbox = class _PHPSandbox extends Client {
19614
19834
  static realtime(token, url2 = "https://api.phpsandbox.io/v1", options = {}) {
19615
19835
  return new _PHPSandbox(token, url2, options);
19616
19836
  }
@@ -19718,7 +19938,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19718
19938
  }
19719
19939
  };
19720
19940
  var _initPromise, _NotebookInstance_instances, init_fn;
19721
- var NotebookInstance2 = class {
19941
+ var NotebookInstance = class {
19722
19942
  constructor(data, client) {
19723
19943
  this.data = data;
19724
19944
  this.client = client;
@@ -19753,15 +19973,18 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19753
19973
  this.watchConnection();
19754
19974
  __privateSet(this, _initPromise, __privateMethod(this, _NotebookInstance_instances, init_fn).call(this));
19755
19975
  }
19756
- this.file = new Filesystem(
19976
+ this.files = new Filesystem(
19757
19977
  this,
19758
19978
  this.socket === null ? null : (handler) => this.onDidConnect(handler)
19759
19979
  );
19760
- this.terminal = new Terminal(this);
19980
+ this.terminals = new Terminals(this);
19761
19981
  this.auth = new Auth(this);
19762
19982
  this.lsp = new Lsp(this);
19763
19983
  this.composer = new Composer(this, client);
19764
- this.log = new Log(this);
19984
+ const telemetry = new RuntimeTelemetry(this);
19985
+ this.logs = new Logs(this, telemetry);
19986
+ this.metrics = new Metrics(this, telemetry);
19987
+ this.ports = new Ports(this, telemetry);
19765
19988
  this.repl = new Repl(this);
19766
19989
  this.container = new Container(this);
19767
19990
  this.shell = new Shell(this);
@@ -19807,6 +20030,12 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19807
20030
  }
19808
20031
  return this.shell.run(command, argsOrOpts);
19809
20032
  }
20033
+ exec(command, argsOrOpts = [], opts) {
20034
+ if (Array.isArray(argsOrOpts)) {
20035
+ return this.run(command, argsOrOpts, opts).wait();
20036
+ }
20037
+ return this.run(command, argsOrOpts).wait();
20038
+ }
19810
20039
  gitCredentials() {
19811
20040
  return this.client.notebook.gitCredentials(this.data.id);
19812
20041
  }
@@ -19897,9 +20126,6 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19897
20126
  const socket = this.realtimeSocket();
19898
20127
  socket.onDidConnect(() => {
19899
20128
  socket.emit("okra.connected");
19900
- if (this.client.options.telemetry) {
19901
- this.container.enableTelemetry(this.client.options.telemetry);
19902
- }
19903
20129
  });
19904
20130
  socket.onDidClose(() => {
19905
20131
  socket.emit("okra.disconnected");