@phpsandbox/sdk 0.0.68 → 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.
Files changed (56) hide show
  1. package/README.md +66 -32
  2. package/dist/auth.d.ts +0 -1
  3. package/dist/auth.js +0 -3
  4. package/dist/beacon/index.d.ts +2 -0
  5. package/dist/beacon/index.js +5 -0
  6. package/dist/browser/phpsandbox-sdk.esm.js +842 -1320
  7. package/dist/browser/phpsandbox-sdk.esm.min.js +24 -24
  8. package/dist/browser/phpsandbox-sdk.iife.js +842 -1320
  9. package/dist/browser/phpsandbox-sdk.iife.min.js +23 -23
  10. package/dist/composer.d.ts +36 -23
  11. package/dist/composer.js +61 -11
  12. package/dist/container.d.ts +2 -6
  13. package/dist/container.js +0 -28
  14. package/dist/filesystem.d.ts +58 -17
  15. package/dist/filesystem.js +169 -51
  16. package/dist/index.d.ts +30 -26
  17. package/dist/index.js +37 -32
  18. package/dist/log.d.ts +11 -5
  19. package/dist/log.js +10 -7
  20. package/dist/metrics.d.ts +10 -0
  21. package/dist/metrics.js +13 -0
  22. package/dist/ports.d.ts +10 -0
  23. package/dist/ports.js +13 -0
  24. package/dist/publications.d.ts +222 -0
  25. package/dist/{deployments.js → publications.js} +64 -69
  26. package/dist/repl.d.ts +0 -2
  27. package/dist/repl.js +0 -3
  28. package/dist/runtime/rest.js +1 -4
  29. package/dist/runtime-telemetry.d.ts +15 -0
  30. package/dist/runtime-telemetry.js +111 -0
  31. package/dist/schemas/{deployments.d.ts → publications.d.ts} +76 -76
  32. package/dist/schemas/publications.js +133 -0
  33. package/dist/services.d.ts +2 -9
  34. package/dist/services.js +3 -10
  35. package/dist/shell.d.ts +52 -22
  36. package/dist/shell.js +103 -75
  37. package/dist/terminal.d.ts +18 -14
  38. package/dist/terminal.js +29 -49
  39. package/docs/api-summary.md +37 -29
  40. package/docs/cdn-usage.md +4 -4
  41. package/docs/getting-started.md +17 -17
  42. package/examples/README.md +8 -9
  43. package/examples/basic.ts +2 -2
  44. package/examples/git-merge-smoke.ts +3 -6
  45. package/examples/laravel-load-test.ts +80 -98
  46. package/examples/notebook-readonly-smoke.ts +38 -41
  47. package/examples/publication-provider-label.ts +11 -0
  48. package/examples/{deploy-cloudflare-container.ts → publish-cloudflare-container.ts} +11 -11
  49. package/examples/{deploy-laravel-cloud.ts → publish-laravel-cloud.ts} +11 -11
  50. package/examples/{deploy-ssh-agent.ts → publish-ssh-agent.ts} +15 -16
  51. package/package.json +7 -2
  52. package/dist/deployments.d.ts +0 -251
  53. package/dist/laravel.d.ts +0 -22
  54. package/dist/laravel.js +0 -11
  55. package/dist/schemas/deployments.js +0 -133
  56. package/examples/deployment-provider-label.ts +0 -11
@@ -1,11 +1,16 @@
1
- import { Action, Client, ComposerCredentials, Disposable, NotebookInstance } from './index.js';
2
- export interface ComposerEvents {
3
- 'composer.log': string;
4
- }
5
- export interface Result {
6
- exitCode: number;
7
- output: string;
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
- }, Result>;
21
- 'composer.dump-autoload': Action<undefined, {
22
- message: string;
23
- success: boolean;
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
- invoke(command: ComposerCommand, args?: Argument, options?: Options): Promise<Result>;
35
- dumpAutoload(): Promise<{
36
- message: string;
37
- success: boolean;
38
- }>;
39
- install(options?: Options): Promise<Result>;
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
- invoke(command, args = {}, options = {}) {
8
- return this.okra.invoke('composer.invoke', { command, args, options });
60
+ run(command, args = {}, options = {}) {
61
+ return new ComposerProcess(nanoid(), this.okra, command, args, options);
9
62
  }
10
- dumpAutoload() {
11
- return this.okra.invoke('composer.dump-autoload');
63
+ dumpAutoload(options = {}) {
64
+ return this.run('dump-autoload', {}, options);
12
65
  }
13
66
  install(options = {}) {
14
- return this.invoke('install', {}, options);
67
+ return this.run('install', {}, options);
15
68
  }
16
69
  update(args, options = {}) {
17
- return this.invoke('update', args, options);
70
+ return this.run('update', args, options);
18
71
  }
19
72
  require(packages, options = {}) {
20
- return this.invoke('require', packages, options);
73
+ return this.run('require', packages, options);
21
74
  }
22
75
  remove(args, options = {}) {
23
- return this.invoke('remove', args, options);
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');
@@ -1,4 +1,4 @@
1
- import { Action, Disposable, NotebookInstance } from './index.js';
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
  }
@@ -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,29 +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
- info(path: string): Promise<FileInfo>;
457
- write(path: string, contents: string): Promise<boolean>;
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>;
458
508
  find(query: string, options?: Partial<FileSearchOptions>): Promise<FileResult[]>;
459
509
  search(query: TextSearchQuery, options?: Partial<TextSearchOptions>, onMatch?: (result: TextSearchResult | false) => boolean | Promise<boolean> | void | Promise<void>): Promise<[boolean, TextSearchMatch[]]>;
460
- mkdir(path: string): Promise<boolean>;
461
- move(from: string, to: string): Promise<boolean>;
462
- remove(path: string, type: 'file' | 'directory'): Promise<boolean>;
463
- readFile(path: string): Promise<Uint8Array>;
464
- readFile(path: string, lineRange: {
465
- lineStart: number;
466
- lineEnd: number;
467
- }): Promise<ReadFileRangeResult>;
468
- tail(path: string, lines?: number): Promise<string>;
469
- writeFile(path: string, contents: Uint8Array, options: FileWriteOptions): Promise<void>;
510
+ tail(path: string, options?: FileTailOptions): Promise<string>;
470
511
  stat(path: string): Promise<Stats>;
471
- rename(from: string, to: string, options: FileOverwriteOptions): Promise<void>;
472
- delete(path: string, options: FileDeleteOptions): Promise<void>;
473
- copy(source: string, destination: string, options: FileOverwriteOptions): Promise<void>;
474
- readDirectory(path: string, include?: string[], exclude?: string[]): Promise<[string, FileType, number | null][]>;
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][]>;
475
516
  createDirectory(path: string): Promise<void>;
476
517
  watch(path: string, options: WatchOptions, onDidChange: (e: FileChange) => void): Promise<FilesystemSubscription>;
477
518
  exists(path: string): Promise<boolean>;
478
- download(chunk?: (data: Uint8Array) => void, exclude?: string[], include?: string[]): Promise<Blob>;
519
+ download(options?: FileDownloadOptions): Promise<Blob>;
479
520
  tree(path?: string): Promise<string>;
480
521
  protected handleError(e: unknown): never;
481
522
  }
@@ -84,11 +84,125 @@ export class Filesystem {
84
84
  }
85
85
  });
86
86
  }
87
- info(path) {
88
- return this.okra.invoke('fs.info', { path });
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
+ }
126
+ }
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);
160
+ }
89
161
  }
90
- write(path, contents) {
91
- return this.okra.invoke('fs.write', { path, contents: contents });
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;
179
+ return new ReadableStream({
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);
203
+ }
204
+ },
205
+ });
92
206
  }
93
207
  find(query, options = {
94
208
  useIgnoreFiles: false,
@@ -169,56 +283,38 @@ export class Filesystem {
169
283
  .invoke('fs.textSearch', { query, options: { ...defaultOptions, ...options } })
170
284
  .finally(() => disposable.dispose());
171
285
  }
172
- mkdir(path) {
173
- return this.okra.invoke('fs.mkdir', { path });
174
- }
175
- move(from, to) {
176
- return this.okra.invoke('fs.move', { from, to });
177
- }
178
- remove(path, type) {
179
- return this.okra.invoke('fs.remove', { path, type });
180
- }
181
- async readFile(path, lineRange) {
182
- try {
183
- const content = await this.okra.invoke('fs.readFile', { path, lineRange });
184
- if (content instanceof Uint8Array) {
185
- return content;
186
- }
187
- if (typeof content === 'string') {
188
- return new TextEncoder().encode(content);
189
- }
190
- return content;
191
- }
192
- catch (e) {
193
- this.handleError(e);
194
- }
195
- }
196
- tail(path, lines = 10) {
197
- return this.okra.invoke('fs.tail', { path, lines }).catch((e) => this.handleError(e));
198
- }
199
- writeFile(path, contents, options) {
200
- return this.okra
201
- .invoke('fs.writeFile', {
202
- path,
203
- contents,
204
- options,
205
- })
206
- .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));
207
288
  }
208
289
  stat(path) {
209
290
  return this.okra.invoke('fs.stat', { path }).catch((e) => this.handleError(e));
210
291
  }
211
- rename(from, to, options) {
212
- return this.okra.invoke('fs.rename', { from, to, options }).catch((e) => this.handleError(e));
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));
213
298
  }
214
- delete(path, options) {
215
- return this.okra.invoke('fs.delete', { path, options }).catch((e) => this.handleError(e));
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));
216
304
  }
217
- copy(source, destination, options) {
218
- return this.okra.invoke('fs.copy', { source, destination, options }).catch((e) => this.handleError(e));
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));
219
311
  }
220
- readDirectory(path, include = [], exclude = []) {
221
- return this.okra.invoke('fs.readDirectory', { path, include, exclude }).catch((e) => this.handleError(e));
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));
222
318
  }
223
319
  createDirectory(path) {
224
320
  return this.okra.invoke('fs.createDirectory', { path }).catch((e) => this.handleError(e));
@@ -251,17 +347,17 @@ export class Filesystem {
251
347
  .then(() => true)
252
348
  .catch(() => false);
253
349
  }
254
- async download(chunk, exclude, include) {
350
+ async download(options = {}) {
255
351
  const id = nanoid();
256
- const stream = !!chunk;
352
+ const stream = !!options.onChunk;
257
353
  const chunks = [];
258
354
  const disposable = this.okra.listen(`fs.download.${id}`, stream
259
- ? chunk
355
+ ? options.onChunk
260
356
  : (data) => {
261
357
  chunks.push(data);
262
358
  });
263
359
  return this.okra
264
- .invoke('fs.download', { id, exclude, include })
360
+ .invoke('fs.download', { id, exclude: options.exclude, include: options.include })
265
361
  .then(() => new Blob(chunks, { type: 'application/octet-stream' }))
266
362
  .finally(() => disposable.dispose());
267
363
  }
@@ -275,3 +371,25 @@ export class Filesystem {
275
371
  throw e;
276
372
  }
277
373
  }
374
+ async function* toAsyncIterable(contents) {
375
+ if (contents instanceof Uint8Array) {
376
+ yield contents;
377
+ return;
378
+ }
379
+ yield* contents;
380
+ }
381
+ async function collectBytes(stream) {
382
+ const chunks = [];
383
+ let length = 0;
384
+ for await (const chunk of stream) {
385
+ chunks.push(chunk);
386
+ length += chunk.byteLength;
387
+ }
388
+ const output = new Uint8Array(length);
389
+ let offset = 0;
390
+ for (const chunk of chunks) {
391
+ output.set(chunk, offset);
392
+ offset += chunk.byteLength;
393
+ }
394
+ return output;
395
+ }