@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.
- package/README.md +59 -22
- package/dist/browser/phpsandbox-sdk.esm.js +465 -239
- package/dist/browser/phpsandbox-sdk.esm.min.js +24 -24
- package/dist/browser/phpsandbox-sdk.iife.js +465 -239
- package/dist/browser/phpsandbox-sdk.iife.min.js +24 -24
- package/dist/composer.d.ts +36 -23
- package/dist/composer.js +61 -11
- package/dist/container.d.ts +2 -6
- package/dist/container.js +0 -28
- package/dist/filesystem.d.ts +58 -18
- package/dist/filesystem.js +148 -63
- package/dist/index.d.ts +18 -9
- package/dist/index.js +21 -8
- package/dist/log.d.ts +11 -5
- package/dist/log.js +10 -7
- package/dist/metrics.d.ts +10 -0
- package/dist/metrics.js +13 -0
- package/dist/ports.d.ts +10 -0
- package/dist/ports.js +13 -0
- package/dist/runtime/rest.js +1 -4
- package/dist/runtime-telemetry.d.ts +15 -0
- package/dist/runtime-telemetry.js +111 -0
- package/dist/schemas/publications.d.ts +10 -10
- package/dist/services.d.ts +2 -9
- package/dist/services.js +3 -10
- package/dist/shell.d.ts +9 -0
- package/dist/shell.js +4 -4
- package/dist/terminal.d.ts +17 -11
- package/dist/terminal.js +27 -44
- package/docs/api-summary.md +28 -23
- package/docs/cdn-usage.md +2 -2
- package/docs/getting-started.md +13 -13
- package/examples/basic.ts +1 -1
- package/examples/git-merge-smoke.ts +1 -4
- package/examples/laravel-load-test.ts +67 -80
- package/examples/notebook-readonly-smoke.ts +30 -33
- package/package.json +1 -1
package/dist/composer.d.ts
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
|
-
import { Action, Client,
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
import { ComposerCredentials, type Action, type Client, type NotebookInstance } from './index.js';
|
|
2
|
+
import { ProcessResult, type ProcessOutputChunk, type ProcessOutputSource, type ProcessResultData } from './shell.js';
|
|
3
|
+
export interface ComposerEventData {
|
|
4
|
+
'composer.output': {
|
|
5
|
+
id: string;
|
|
6
|
+
output: string;
|
|
7
|
+
source: ProcessOutputSource;
|
|
8
|
+
};
|
|
8
9
|
}
|
|
10
|
+
type ComposerEventPattern = `${keyof ComposerEventData}.${string}`;
|
|
11
|
+
export type ComposerEvents = ComposerEventData & {
|
|
12
|
+
[K in ComposerEventPattern]: ComposerEventData['composer.output'];
|
|
13
|
+
};
|
|
9
14
|
export interface ComposerPackage {
|
|
10
15
|
name: string;
|
|
11
16
|
_installedVersion: string;
|
|
@@ -14,32 +19,40 @@ export interface ComposerPackage {
|
|
|
14
19
|
}
|
|
15
20
|
export interface ComposerActions {
|
|
16
21
|
'composer.invoke': Action<{
|
|
22
|
+
id: string;
|
|
17
23
|
command: ComposerCommand;
|
|
18
24
|
args: Argument;
|
|
19
25
|
options: Options;
|
|
20
|
-
},
|
|
21
|
-
'composer.
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}>;
|
|
26
|
+
}, ProcessResultData>;
|
|
27
|
+
'composer.kill': Action<{
|
|
28
|
+
id: string;
|
|
29
|
+
}, boolean>;
|
|
25
30
|
'composer.packages': Action<undefined, ComposerPackage[]>;
|
|
26
31
|
}
|
|
27
|
-
export type ComposerCommand = 'install' | 'update' | 'require' | 'remove' | 'search' | 'show';
|
|
32
|
+
export type ComposerCommand = 'install' | 'update' | 'require' | 'remove' | 'search' | 'show' | 'dump-autoload';
|
|
28
33
|
export type Argument<Name extends string = string, Type extends string | string[] = string | string[]> = Record<Name, Type>;
|
|
29
34
|
export type Options<Name extends string = string, Type extends string | string[] | boolean = string | string[] | boolean> = Record<Name, Type>;
|
|
35
|
+
export declare class ComposerProcess {
|
|
36
|
+
readonly id: string;
|
|
37
|
+
private readonly okra;
|
|
38
|
+
readonly output: ReadableStream<ProcessOutputChunk>;
|
|
39
|
+
readonly stdout: ReadableStream<Uint8Array>;
|
|
40
|
+
readonly stderr: ReadableStream<Uint8Array>;
|
|
41
|
+
private readonly completion;
|
|
42
|
+
constructor(id: string, okra: NotebookInstance, command: ComposerCommand, args: Argument, options: Options);
|
|
43
|
+
wait(): Promise<ProcessResult>;
|
|
44
|
+
kill(): Promise<void>;
|
|
45
|
+
}
|
|
30
46
|
export default class Composer {
|
|
31
47
|
protected okra: NotebookInstance;
|
|
32
48
|
readonly credentials: ComposerCredentials;
|
|
33
49
|
constructor(okra: NotebookInstance, client: Client);
|
|
34
|
-
|
|
35
|
-
dumpAutoload():
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
update(args: Argument<'packages'>, options?: Options): Promise<Result>;
|
|
41
|
-
require(packages: Argument<'packages'>, options?: Options): Promise<Result>;
|
|
42
|
-
remove(args: Argument<'packages'>, options?: Options): Promise<Result>;
|
|
43
|
-
stream(fn: (data: string) => void): Disposable;
|
|
50
|
+
run(command: ComposerCommand, args?: Argument, options?: Options): ComposerProcess;
|
|
51
|
+
dumpAutoload(options?: Options): ComposerProcess;
|
|
52
|
+
install(options?: Options): ComposerProcess;
|
|
53
|
+
update(args: Argument<'packages'>, options?: Options): ComposerProcess;
|
|
54
|
+
require(packages: Argument<'packages'>, options?: Options): ComposerProcess;
|
|
55
|
+
remove(args: Argument<'packages'>, options?: Options): ComposerProcess;
|
|
44
56
|
packages(): Promise<ComposerPackage[]>;
|
|
45
57
|
}
|
|
58
|
+
export {};
|
package/dist/composer.js
CHANGED
|
@@ -1,29 +1,79 @@
|
|
|
1
1
|
import { ComposerCredentials } from './index.js';
|
|
2
|
+
import { ProcessReadable, ProcessResult, } from './shell.js';
|
|
3
|
+
import { nanoid } from 'nanoid';
|
|
4
|
+
export class ComposerProcess {
|
|
5
|
+
constructor(id, okra, command, args, options) {
|
|
6
|
+
this.id = id;
|
|
7
|
+
this.okra = okra;
|
|
8
|
+
const output = new ProcessReadable();
|
|
9
|
+
const stdout = new ProcessReadable();
|
|
10
|
+
const stderr = new ProcessReadable();
|
|
11
|
+
this.output = output.stream;
|
|
12
|
+
this.stdout = stdout.stream;
|
|
13
|
+
this.stderr = stderr.stream;
|
|
14
|
+
const encoder = new TextEncoder();
|
|
15
|
+
let subscription = this.okra.listen(`composer.output.${this.id}`, (event) => {
|
|
16
|
+
const data = encoder.encode(event.output);
|
|
17
|
+
const source = event.source ?? 'output';
|
|
18
|
+
output.enqueue({ source, data });
|
|
19
|
+
if (source === 'stderr') {
|
|
20
|
+
stderr.enqueue(data);
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
stdout.enqueue(data);
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
const finish = () => {
|
|
27
|
+
subscription?.dispose();
|
|
28
|
+
subscription = null;
|
|
29
|
+
output.close();
|
|
30
|
+
stdout.close();
|
|
31
|
+
stderr.close();
|
|
32
|
+
};
|
|
33
|
+
const fail = (error) => {
|
|
34
|
+
subscription?.dispose();
|
|
35
|
+
subscription = null;
|
|
36
|
+
output.fail(error);
|
|
37
|
+
stdout.fail(error);
|
|
38
|
+
stderr.fail(error);
|
|
39
|
+
};
|
|
40
|
+
this.completion = this.okra.invoke('composer.invoke', { id, command, args, options }).then((result) => {
|
|
41
|
+
finish();
|
|
42
|
+
return new ProcessResult(result.output, result.stdout, result.stderr, result.exitCode);
|
|
43
|
+
}).catch((error) => {
|
|
44
|
+
fail(error);
|
|
45
|
+
throw error;
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
wait() {
|
|
49
|
+
return this.completion;
|
|
50
|
+
}
|
|
51
|
+
async kill() {
|
|
52
|
+
await this.okra.invoke('composer.kill', { id: this.id });
|
|
53
|
+
}
|
|
54
|
+
}
|
|
2
55
|
export default class Composer {
|
|
3
56
|
constructor(okra, client) {
|
|
4
57
|
this.okra = okra;
|
|
5
58
|
this.credentials = new ComposerCredentials(client, okra.data.id);
|
|
6
59
|
}
|
|
7
|
-
|
|
8
|
-
return this.okra
|
|
60
|
+
run(command, args = {}, options = {}) {
|
|
61
|
+
return new ComposerProcess(nanoid(), this.okra, command, args, options);
|
|
9
62
|
}
|
|
10
|
-
dumpAutoload() {
|
|
11
|
-
return this.
|
|
63
|
+
dumpAutoload(options = {}) {
|
|
64
|
+
return this.run('dump-autoload', {}, options);
|
|
12
65
|
}
|
|
13
66
|
install(options = {}) {
|
|
14
|
-
return this.
|
|
67
|
+
return this.run('install', {}, options);
|
|
15
68
|
}
|
|
16
69
|
update(args, options = {}) {
|
|
17
|
-
return this.
|
|
70
|
+
return this.run('update', args, options);
|
|
18
71
|
}
|
|
19
72
|
require(packages, options = {}) {
|
|
20
|
-
return this.
|
|
73
|
+
return this.run('require', packages, options);
|
|
21
74
|
}
|
|
22
75
|
remove(args, options = {}) {
|
|
23
|
-
return this.
|
|
24
|
-
}
|
|
25
|
-
stream(fn) {
|
|
26
|
-
return this.okra.listen('composer.log', fn);
|
|
76
|
+
return this.run('remove', args, options);
|
|
27
77
|
}
|
|
28
78
|
packages() {
|
|
29
79
|
return this.okra.invoke('composer.packages');
|
package/dist/container.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Action,
|
|
1
|
+
import { Action, NotebookInstance } from './index.js';
|
|
2
2
|
interface Stat {
|
|
3
3
|
usage: number;
|
|
4
4
|
limit: number;
|
|
@@ -38,6 +38,7 @@ export interface ContainerActions {
|
|
|
38
38
|
state: NotebookState;
|
|
39
39
|
}>;
|
|
40
40
|
'container.opened-ports': Action<{}, PortInfo[]>;
|
|
41
|
+
'container.stats': Action<{}, ContainerStats | null>;
|
|
41
42
|
'container.set-php': Action<{
|
|
42
43
|
version: string;
|
|
43
44
|
}, {
|
|
@@ -57,13 +58,8 @@ export default class Container {
|
|
|
57
58
|
state(): Promise<{
|
|
58
59
|
state: NotebookState;
|
|
59
60
|
}>;
|
|
60
|
-
openedPorts(): Promise<PortInfo[]>;
|
|
61
61
|
setPhp(version: string): Promise<{
|
|
62
62
|
version: string;
|
|
63
63
|
}>;
|
|
64
|
-
enableTelemetry(features: Set<TelemetryFeature>): Promise<void>;
|
|
65
|
-
stopTelemetry(): Promise<void>;
|
|
66
|
-
listen<T extends keyof ContainerEvents>(event: T, handler: (data: ContainerEvents[T]) => void): Disposable;
|
|
67
|
-
onPort(handler: (port: PortInfo, type: 'open' | 'close') => void): Disposable;
|
|
68
64
|
}
|
|
69
65
|
export {};
|
package/dist/container.js
CHANGED
|
@@ -24,35 +24,7 @@ export default class Container {
|
|
|
24
24
|
state() {
|
|
25
25
|
return this.okra.invoke('container.state');
|
|
26
26
|
}
|
|
27
|
-
openedPorts() {
|
|
28
|
-
return this.okra.invoke('container.opened-ports');
|
|
29
|
-
}
|
|
30
27
|
setPhp(version) {
|
|
31
28
|
return this.okra.invoke('container.set-php', { version });
|
|
32
29
|
}
|
|
33
|
-
enableTelemetry(features) {
|
|
34
|
-
return this.okra.invoke('container.stream-telemetry', { features: Array.from(features) });
|
|
35
|
-
}
|
|
36
|
-
stopTelemetry() {
|
|
37
|
-
return this.okra.invoke('container.stop-telemetry');
|
|
38
|
-
}
|
|
39
|
-
listen(event, handler) {
|
|
40
|
-
return this.okra.listen(event, handler);
|
|
41
|
-
}
|
|
42
|
-
onPort(handler) {
|
|
43
|
-
let ports = [];
|
|
44
|
-
return this.listen('container.ports', (newPorts) => {
|
|
45
|
-
for (const port of newPorts) {
|
|
46
|
-
if (!ports.some((p) => p.port === port.port)) {
|
|
47
|
-
handler(port, 'open');
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
for (const port of ports) {
|
|
51
|
-
if (!newPorts.some((p) => p.port === port.port)) {
|
|
52
|
-
handler(port, 'close');
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
ports = newPorts;
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
30
|
}
|
package/dist/filesystem.d.ts
CHANGED
|
@@ -60,6 +60,27 @@ export interface FileDeleteOptions {
|
|
|
60
60
|
postfix: string;
|
|
61
61
|
};
|
|
62
62
|
}
|
|
63
|
+
export type FileContents = string | Uint8Array | ReadableStream<Uint8Array> | AsyncIterable<Uint8Array>;
|
|
64
|
+
export interface FileListOptions {
|
|
65
|
+
include?: string[];
|
|
66
|
+
exclude?: string[];
|
|
67
|
+
}
|
|
68
|
+
export interface FileTailOptions {
|
|
69
|
+
lines?: number;
|
|
70
|
+
}
|
|
71
|
+
export interface FileLineRange {
|
|
72
|
+
start: number;
|
|
73
|
+
end: number;
|
|
74
|
+
}
|
|
75
|
+
export interface FileLineRangeResult extends FileLineRange {
|
|
76
|
+
content: string;
|
|
77
|
+
warning?: string;
|
|
78
|
+
}
|
|
79
|
+
export interface FileDownloadOptions {
|
|
80
|
+
onChunk?: (data: Uint8Array) => void;
|
|
81
|
+
exclude?: string[];
|
|
82
|
+
include?: string[];
|
|
83
|
+
}
|
|
63
84
|
export interface SearchOptions {
|
|
64
85
|
/**
|
|
65
86
|
* The maximum number of results to be returned.
|
|
@@ -375,6 +396,28 @@ export interface FilesystemActions {
|
|
|
375
396
|
contents: Uint8Array;
|
|
376
397
|
options: FileWriteOptions;
|
|
377
398
|
}, void>;
|
|
399
|
+
'fs.readStream': Action<{
|
|
400
|
+
id: string;
|
|
401
|
+
path: string;
|
|
402
|
+
}, boolean>;
|
|
403
|
+
'fs.cancelRead': Action<{
|
|
404
|
+
id: string;
|
|
405
|
+
}, boolean>;
|
|
406
|
+
'fs.writeStream': Action<{
|
|
407
|
+
id: string;
|
|
408
|
+
path: string;
|
|
409
|
+
options: FileWriteOptions;
|
|
410
|
+
}, boolean>;
|
|
411
|
+
'fs.writeChunk': Action<{
|
|
412
|
+
id: string;
|
|
413
|
+
contents: Uint8Array;
|
|
414
|
+
}, boolean>;
|
|
415
|
+
'fs.endWrite': Action<{
|
|
416
|
+
id: string;
|
|
417
|
+
}, boolean>;
|
|
418
|
+
'fs.abortWrite': Action<{
|
|
419
|
+
id: string;
|
|
420
|
+
}, boolean>;
|
|
378
421
|
'fs.stat': Action<{
|
|
379
422
|
path: string;
|
|
380
423
|
}, Stats>;
|
|
@@ -424,6 +467,10 @@ interface FilesystemEventData {
|
|
|
424
467
|
'fs.watch': FileChange;
|
|
425
468
|
'fs.download': Uint8Array;
|
|
426
469
|
'fs.text.search': TextSearchResult | false;
|
|
470
|
+
'fs.read': {
|
|
471
|
+
id: string;
|
|
472
|
+
data: Uint8Array;
|
|
473
|
+
};
|
|
427
474
|
}
|
|
428
475
|
type PrefixKey<K extends keyof FilesystemEventData> = `${K}.${string}`;
|
|
429
476
|
export type FilesystemEvents = FilesystemEventData & {
|
|
@@ -453,30 +500,23 @@ export declare class Filesystem {
|
|
|
453
500
|
private watches;
|
|
454
501
|
constructor(okra: NotebookInstance, registerReconnect?: ((handler: () => void) => Disposable) | null);
|
|
455
502
|
private registerReconnectHandler;
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
503
|
+
write(path: string, contents: FileContents, options?: Partial<FileWriteOptions>): Promise<void>;
|
|
504
|
+
read(path: string): Promise<Uint8Array>;
|
|
505
|
+
readText(path: string): Promise<string>;
|
|
506
|
+
readLines(path: string, range: FileLineRange): Promise<FileLineRangeResult>;
|
|
507
|
+
readStream(path: string): ReadableStream<Uint8Array>;
|
|
459
508
|
find(query: string, options?: Partial<FileSearchOptions>): Promise<FileResult[]>;
|
|
460
509
|
search(query: TextSearchQuery, options?: Partial<TextSearchOptions>, onMatch?: (result: TextSearchResult | false) => boolean | Promise<boolean> | void | Promise<void>): Promise<[boolean, TextSearchMatch[]]>;
|
|
461
|
-
|
|
462
|
-
move(from: string, to: string): Promise<boolean>;
|
|
463
|
-
remove(path: string, type: 'file' | 'directory'): Promise<boolean>;
|
|
464
|
-
readFile(path: string): Promise<Uint8Array>;
|
|
465
|
-
readFile(path: string, lineRange: {
|
|
466
|
-
lineStart: number;
|
|
467
|
-
lineEnd: number;
|
|
468
|
-
}): Promise<ReadFileRangeResult>;
|
|
469
|
-
tail(path: string, lines?: number): Promise<string>;
|
|
470
|
-
writeFile(path: string, contents: Uint8Array, options: FileWriteOptions): Promise<void>;
|
|
510
|
+
tail(path: string, options?: FileTailOptions): Promise<string>;
|
|
471
511
|
stat(path: string): Promise<Stats>;
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
copy(source: string, destination: string, options
|
|
475
|
-
|
|
512
|
+
move(from: string, to: string, options?: Partial<FileOverwriteOptions>): Promise<void>;
|
|
513
|
+
remove(path: string, options?: Partial<FileDeleteOptions>): Promise<void>;
|
|
514
|
+
copy(source: string, destination: string, options?: Partial<FileOverwriteOptions>): Promise<void>;
|
|
515
|
+
list(path: string, options?: FileListOptions): Promise<[string, FileType, number | null][]>;
|
|
476
516
|
createDirectory(path: string): Promise<void>;
|
|
477
517
|
watch(path: string, options: WatchOptions, onDidChange: (e: FileChange) => void): Promise<FilesystemSubscription>;
|
|
478
518
|
exists(path: string): Promise<boolean>;
|
|
479
|
-
download(
|
|
519
|
+
download(options?: FileDownloadOptions): Promise<Blob>;
|
|
480
520
|
tree(path?: string): Promise<string>;
|
|
481
521
|
protected handleError(e: unknown): never;
|
|
482
522
|
}
|
package/dist/filesystem.js
CHANGED
|
@@ -84,26 +84,122 @@ export class Filesystem {
|
|
|
84
84
|
}
|
|
85
85
|
});
|
|
86
86
|
}
|
|
87
|
-
|
|
88
|
-
|
|
87
|
+
async write(path, contents, options = {}) {
|
|
88
|
+
const resolvedOptions = {
|
|
89
|
+
overwrite: true,
|
|
90
|
+
create: true,
|
|
91
|
+
unlock: false,
|
|
92
|
+
atomic: false,
|
|
93
|
+
...options,
|
|
94
|
+
};
|
|
95
|
+
const source = typeof contents === 'string' ? new TextEncoder().encode(contents) : contents;
|
|
96
|
+
if (this.okra.runtimeTransport === 'rest') {
|
|
97
|
+
const bytes = source instanceof Uint8Array ? source : await collectBytes(source);
|
|
98
|
+
await this.okra.invoke('fs.writeFile', {
|
|
99
|
+
path,
|
|
100
|
+
contents: bytes,
|
|
101
|
+
options: resolvedOptions,
|
|
102
|
+
}).catch((error) => this.handleError(error));
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const id = nanoid();
|
|
106
|
+
const completion = this.okra.invoke('fs.writeStream', { id, path, options: resolvedOptions });
|
|
107
|
+
void completion.catch(() => { });
|
|
108
|
+
try {
|
|
109
|
+
for await (const chunk of toAsyncIterable(source)) {
|
|
110
|
+
const written = await this.okra.invoke('fs.writeChunk', { id, contents: chunk });
|
|
111
|
+
if (!written) {
|
|
112
|
+
throw new Error(`File write stream ${id} is not available`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
const ended = await this.okra.invoke('fs.endWrite', { id });
|
|
116
|
+
if (!ended) {
|
|
117
|
+
throw new Error(`File write stream ${id} could not be completed`);
|
|
118
|
+
}
|
|
119
|
+
await completion;
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
await this.okra.invoke('fs.abortWrite', { id }).catch(() => false);
|
|
123
|
+
await completion.catch(() => false);
|
|
124
|
+
throw error;
|
|
125
|
+
}
|
|
89
126
|
}
|
|
90
|
-
async
|
|
91
|
-
|
|
92
|
-
|
|
127
|
+
async read(path) {
|
|
128
|
+
try {
|
|
129
|
+
const content = await this.okra.invoke('fs.readFile', { path });
|
|
130
|
+
return content instanceof Uint8Array ? content : new TextEncoder().encode(content);
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
this.handleError(error);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
async readText(path) {
|
|
137
|
+
return new TextDecoder().decode(await this.read(path));
|
|
138
|
+
}
|
|
139
|
+
async readLines(path, range) {
|
|
140
|
+
try {
|
|
141
|
+
const result = await this.okra.invoke('fs.readFile', {
|
|
142
|
+
path,
|
|
143
|
+
lineRange: {
|
|
144
|
+
lineStart: range.start,
|
|
145
|
+
lineEnd: range.end,
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
if (typeof result === 'string' || result instanceof Uint8Array) {
|
|
149
|
+
throw new Error(`Unexpected ranged file response for ${path}`);
|
|
150
|
+
}
|
|
151
|
+
return {
|
|
152
|
+
start: result.lineStart,
|
|
153
|
+
end: result.lineEnd,
|
|
154
|
+
content: result.content,
|
|
155
|
+
...(result.error === null ? {} : { warning: result.error }),
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
this.handleError(error);
|
|
93
160
|
}
|
|
94
|
-
const bytes = contents instanceof Uint8Array ? contents : await collectBytes(contents);
|
|
95
|
-
await this.writeFile(path, bytes, options);
|
|
96
|
-
return true;
|
|
97
161
|
}
|
|
98
|
-
|
|
162
|
+
readStream(path) {
|
|
163
|
+
if (this.okra.runtimeTransport === 'rest') {
|
|
164
|
+
return new ReadableStream({
|
|
165
|
+
start: async (controller) => {
|
|
166
|
+
try {
|
|
167
|
+
controller.enqueue(await this.read(path));
|
|
168
|
+
controller.close();
|
|
169
|
+
}
|
|
170
|
+
catch (error) {
|
|
171
|
+
controller.error(error);
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
const id = nanoid();
|
|
177
|
+
let disposable = null;
|
|
178
|
+
let closed = false;
|
|
99
179
|
return new ReadableStream({
|
|
100
|
-
start:
|
|
101
|
-
|
|
102
|
-
controller.enqueue(
|
|
103
|
-
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
|
|
180
|
+
start: (controller) => {
|
|
181
|
+
disposable = this.okra.listen(`fs.read.${id}`, (event) => {
|
|
182
|
+
controller.enqueue(event.data);
|
|
183
|
+
});
|
|
184
|
+
void this.okra.invoke('fs.readStream', { id, path }).then(() => {
|
|
185
|
+
if (!closed) {
|
|
186
|
+
closed = true;
|
|
187
|
+
disposable?.dispose();
|
|
188
|
+
controller.close();
|
|
189
|
+
}
|
|
190
|
+
}).catch((error) => {
|
|
191
|
+
if (!closed) {
|
|
192
|
+
closed = true;
|
|
193
|
+
disposable?.dispose();
|
|
194
|
+
controller.error(error);
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
},
|
|
198
|
+
cancel: async () => {
|
|
199
|
+
if (!closed) {
|
|
200
|
+
closed = true;
|
|
201
|
+
disposable?.dispose();
|
|
202
|
+
await this.okra.invoke('fs.cancelRead', { id }).catch(() => false);
|
|
107
203
|
}
|
|
108
204
|
},
|
|
109
205
|
});
|
|
@@ -187,56 +283,38 @@ export class Filesystem {
|
|
|
187
283
|
.invoke('fs.textSearch', { query, options: { ...defaultOptions, ...options } })
|
|
188
284
|
.finally(() => disposable.dispose());
|
|
189
285
|
}
|
|
190
|
-
|
|
191
|
-
return this.okra.invoke('fs.
|
|
192
|
-
}
|
|
193
|
-
move(from, to) {
|
|
194
|
-
return this.okra.invoke('fs.move', { from, to });
|
|
195
|
-
}
|
|
196
|
-
remove(path, type) {
|
|
197
|
-
return this.okra.invoke('fs.remove', { path, type });
|
|
198
|
-
}
|
|
199
|
-
async readFile(path, lineRange) {
|
|
200
|
-
try {
|
|
201
|
-
const content = await this.okra.invoke('fs.readFile', { path, lineRange });
|
|
202
|
-
if (content instanceof Uint8Array) {
|
|
203
|
-
return content;
|
|
204
|
-
}
|
|
205
|
-
if (typeof content === 'string') {
|
|
206
|
-
return new TextEncoder().encode(content);
|
|
207
|
-
}
|
|
208
|
-
return content;
|
|
209
|
-
}
|
|
210
|
-
catch (e) {
|
|
211
|
-
this.handleError(e);
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
tail(path, lines = 10) {
|
|
215
|
-
return this.okra.invoke('fs.tail', { path, lines }).catch((e) => this.handleError(e));
|
|
216
|
-
}
|
|
217
|
-
writeFile(path, contents, options) {
|
|
218
|
-
return this.okra
|
|
219
|
-
.invoke('fs.writeFile', {
|
|
220
|
-
path,
|
|
221
|
-
contents,
|
|
222
|
-
options,
|
|
223
|
-
})
|
|
224
|
-
.catch((e) => this.handleError(e));
|
|
286
|
+
tail(path, options = {}) {
|
|
287
|
+
return this.okra.invoke('fs.tail', { path, lines: options.lines ?? 10 }).catch((e) => this.handleError(e));
|
|
225
288
|
}
|
|
226
289
|
stat(path) {
|
|
227
290
|
return this.okra.invoke('fs.stat', { path }).catch((e) => this.handleError(e));
|
|
228
291
|
}
|
|
229
|
-
|
|
230
|
-
return this.okra.invoke('fs.rename', {
|
|
292
|
+
move(from, to, options = {}) {
|
|
293
|
+
return this.okra.invoke('fs.rename', {
|
|
294
|
+
from,
|
|
295
|
+
to,
|
|
296
|
+
options: { overwrite: false, ...options },
|
|
297
|
+
}).catch((e) => this.handleError(e));
|
|
231
298
|
}
|
|
232
|
-
|
|
233
|
-
return this.okra.invoke('fs.delete', {
|
|
299
|
+
remove(path, options = {}) {
|
|
300
|
+
return this.okra.invoke('fs.delete', {
|
|
301
|
+
path,
|
|
302
|
+
options: { recursive: false, useTrash: false, atomic: false, ...options },
|
|
303
|
+
}).catch((e) => this.handleError(e));
|
|
234
304
|
}
|
|
235
|
-
copy(source, destination, options) {
|
|
236
|
-
return this.okra.invoke('fs.copy', {
|
|
305
|
+
copy(source, destination, options = {}) {
|
|
306
|
+
return this.okra.invoke('fs.copy', {
|
|
307
|
+
source,
|
|
308
|
+
destination,
|
|
309
|
+
options: { overwrite: false, ...options },
|
|
310
|
+
}).catch((e) => this.handleError(e));
|
|
237
311
|
}
|
|
238
|
-
|
|
239
|
-
return this.okra.invoke('fs.readDirectory', {
|
|
312
|
+
list(path, options = {}) {
|
|
313
|
+
return this.okra.invoke('fs.readDirectory', {
|
|
314
|
+
path,
|
|
315
|
+
include: options.include ?? [],
|
|
316
|
+
exclude: options.exclude ?? [],
|
|
317
|
+
}).catch((e) => this.handleError(e));
|
|
240
318
|
}
|
|
241
319
|
createDirectory(path) {
|
|
242
320
|
return this.okra.invoke('fs.createDirectory', { path }).catch((e) => this.handleError(e));
|
|
@@ -269,17 +347,17 @@ export class Filesystem {
|
|
|
269
347
|
.then(() => true)
|
|
270
348
|
.catch(() => false);
|
|
271
349
|
}
|
|
272
|
-
async download(
|
|
350
|
+
async download(options = {}) {
|
|
273
351
|
const id = nanoid();
|
|
274
|
-
const stream = !!
|
|
352
|
+
const stream = !!options.onChunk;
|
|
275
353
|
const chunks = [];
|
|
276
354
|
const disposable = this.okra.listen(`fs.download.${id}`, stream
|
|
277
|
-
?
|
|
355
|
+
? options.onChunk
|
|
278
356
|
: (data) => {
|
|
279
357
|
chunks.push(data);
|
|
280
358
|
});
|
|
281
359
|
return this.okra
|
|
282
|
-
.invoke('fs.download', { id, exclude, include })
|
|
360
|
+
.invoke('fs.download', { id, exclude: options.exclude, include: options.include })
|
|
283
361
|
.then(() => new Blob(chunks, { type: 'application/octet-stream' }))
|
|
284
362
|
.finally(() => disposable.dispose());
|
|
285
363
|
}
|
|
@@ -293,6 +371,13 @@ export class Filesystem {
|
|
|
293
371
|
throw e;
|
|
294
372
|
}
|
|
295
373
|
}
|
|
374
|
+
async function* toAsyncIterable(contents) {
|
|
375
|
+
if (contents instanceof Uint8Array) {
|
|
376
|
+
yield contents;
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
yield* contents;
|
|
380
|
+
}
|
|
296
381
|
async function collectBytes(stream) {
|
|
297
382
|
const chunks = [];
|
|
298
383
|
let length = 0;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { Filesystem, FilesystemActions, FilesystemEvents } from './filesystem.js';
|
|
2
|
-
import
|
|
3
|
-
import Container, { ContainerActions, ContainerEvents, PortInfo
|
|
2
|
+
import Terminals, { TerminalEvents, TerminalActions, type SpawnOptions } from './terminal.js';
|
|
3
|
+
import Container, { ContainerActions, ContainerEvents, PortInfo } from './container.js';
|
|
4
4
|
import Auth, { AuthActions } from './auth.js';
|
|
5
5
|
import Lsp, { LspActions, LspEvents } from './lsp.js';
|
|
6
6
|
import Composer, { ComposerActions, ComposerEvents } from './composer.js';
|
|
7
|
-
import
|
|
7
|
+
import Logs, { LogActions, LogEvents } from './log.js';
|
|
8
|
+
import Metrics from './metrics.js';
|
|
9
|
+
import Ports from './ports.js';
|
|
8
10
|
import Repl, { ReplActions, ReplEvents } from './repl.js';
|
|
9
|
-
import
|
|
11
|
+
import { ShellEvents, ShellActions, type ProcessResult, type ShellProcess } from './shell.js';
|
|
10
12
|
import Services, { ServiceActions, ServiceEvents } from './services.js';
|
|
11
13
|
import Config, { ConfigActions } from './config.js';
|
|
12
14
|
import { EventDispatcher } from './events/index.js';
|
|
@@ -19,6 +21,10 @@ export * from './types.js';
|
|
|
19
21
|
export * from './lsp.js';
|
|
20
22
|
export * from './filesystem.js';
|
|
21
23
|
export * from './container.js';
|
|
24
|
+
export * from './log.js';
|
|
25
|
+
export * from './metrics.js';
|
|
26
|
+
export * from './ports.js';
|
|
27
|
+
export * from './runtime-telemetry.js';
|
|
22
28
|
export * from './shell.js';
|
|
23
29
|
export * from './terminal.js';
|
|
24
30
|
export * from './git.js';
|
|
@@ -299,7 +305,6 @@ export declare class NotebookApi {
|
|
|
299
305
|
export interface PHPSandboxClientOptions {
|
|
300
306
|
debug?: boolean;
|
|
301
307
|
startClosed?: boolean;
|
|
302
|
-
telemetry?: Set<TelemetryFeature>;
|
|
303
308
|
fetch?: typeof globalThis.fetch;
|
|
304
309
|
webSocket?: unknown;
|
|
305
310
|
runtimeUrlProvider?: (notebookId: string) => Promise<string>;
|
|
@@ -384,15 +389,17 @@ export declare class NotebookInstance {
|
|
|
384
389
|
#private;
|
|
385
390
|
readonly data: NotebookData;
|
|
386
391
|
protected client: Client;
|
|
387
|
-
readonly
|
|
388
|
-
readonly
|
|
392
|
+
readonly files: Filesystem;
|
|
393
|
+
readonly terminals: Terminals;
|
|
389
394
|
readonly auth: Auth;
|
|
390
395
|
readonly lsp: Lsp;
|
|
391
396
|
readonly composer: Composer;
|
|
392
|
-
readonly
|
|
397
|
+
readonly logs: Logs;
|
|
398
|
+
readonly metrics: Metrics;
|
|
399
|
+
readonly ports: Ports;
|
|
393
400
|
readonly repl: Repl;
|
|
394
401
|
readonly container: Container;
|
|
395
|
-
readonly shell
|
|
402
|
+
private readonly shell;
|
|
396
403
|
readonly git: Git;
|
|
397
404
|
readonly services: Services;
|
|
398
405
|
readonly config: Config;
|
|
@@ -412,6 +419,8 @@ export declare class NotebookInstance {
|
|
|
412
419
|
publish(input: PublishInput): Promise<PublicationRun>;
|
|
413
420
|
run(command: string | string[], opts?: SpawnOptions): ShellProcess;
|
|
414
421
|
run(command: string, args: string[], opts?: SpawnOptions): ShellProcess;
|
|
422
|
+
exec(command: string | string[], opts?: SpawnOptions): Promise<ProcessResult>;
|
|
423
|
+
exec(command: string, args: string[], opts?: SpawnOptions): Promise<ProcessResult>;
|
|
415
424
|
gitCredentials(): Promise<GitCredentials>;
|
|
416
425
|
destroy(): Promise<void>;
|
|
417
426
|
stop(): Promise<void>;
|