@ghchinoy/litflow 0.2.8 → 0.3.0

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 (158) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/dist/breadboard/data/common.d.ts +35 -0
  3. package/dist/breadboard/engine/loader/capability.d.ts +21 -0
  4. package/dist/breadboard/engine/loader/loader.d.ts +29 -0
  5. package/dist/breadboard/engine/loader/resolve-graph-urls.d.ts +16 -0
  6. package/dist/breadboard/engine/runtime/bubble.d.ts +23 -0
  7. package/dist/breadboard/engine/runtime/graph-based-node-handler.d.ts +16 -0
  8. package/dist/breadboard/engine/runtime/handler.d.ts +27 -0
  9. package/dist/breadboard/engine/runtime/harness/events.d.ts +145 -0
  10. package/dist/breadboard/engine/runtime/harness/local.d.ts +10 -0
  11. package/dist/breadboard/engine/runtime/harness/plan-runner.d.ts +25 -0
  12. package/dist/breadboard/engine/runtime/run/invoke-graph.d.ts +12 -0
  13. package/dist/breadboard/engine/runtime/run/node-invoker.d.ts +12 -0
  14. package/dist/breadboard/engine/runtime/run/run-graph.d.ts +12 -0
  15. package/dist/breadboard/engine/runtime/run.d.ts +29 -0
  16. package/dist/breadboard/engine/runtime/sandbox/capabilities-manager.d.ts +15 -0
  17. package/dist/breadboard/engine/runtime/sandbox/file-system-handler-factory.d.ts +15 -0
  18. package/dist/breadboard/engine/runtime/sandbox/invoke-describer.d.ts +10 -0
  19. package/dist/breadboard/engine/runtime/static/create-plan.d.ts +14 -0
  20. package/dist/breadboard/engine/runtime/static/orchestrator.d.ts +72 -0
  21. package/dist/breadboard/engine/runtime/traversal/index.d.ts +20 -0
  22. package/dist/breadboard/engine/runtime/traversal/iterator.d.ts +12 -0
  23. package/dist/breadboard/engine/runtime/traversal/machine.d.ts +14 -0
  24. package/dist/breadboard/engine/runtime/traversal/representation.d.ts +27 -0
  25. package/dist/breadboard/engine/runtime/traversal/result.d.ts +24 -0
  26. package/dist/breadboard/engine/runtime/traversal/state.d.ts +34 -0
  27. package/dist/breadboard/lit-flow-runner.d.ts +13 -0
  28. package/dist/breadboard/lit-flow-runner.test.d.ts +1 -0
  29. package/dist/breadboard/runner.d.ts +13 -0
  30. package/dist/index.d.ts +2 -0
  31. package/dist/lit-chiclet.d.ts +9 -0
  32. package/dist/lit-schema-node.d.ts +13 -0
  33. package/dist/lit-schema-node.test.d.ts +1 -0
  34. package/dist/litflow.js +708 -442
  35. package/dist/litflow.js.map +1 -1
  36. package/package.json +15 -3
  37. package/src/breadboard/data/common.ts +450 -0
  38. package/src/breadboard/data/file-system.ts +54 -0
  39. package/src/breadboard/data/inline-all-content.ts +126 -0
  40. package/src/breadboard/data/recent-boards.ts +118 -0
  41. package/src/breadboard/data/save-outputs-as-file.ts +104 -0
  42. package/src/breadboard/engine/add-run-module.ts +168 -0
  43. package/src/breadboard/engine/editor/blank.ts +65 -0
  44. package/src/breadboard/engine/editor/edge.ts +27 -0
  45. package/src/breadboard/engine/editor/events.ts +64 -0
  46. package/src/breadboard/engine/editor/graph.ts +383 -0
  47. package/src/breadboard/engine/editor/history.ts +98 -0
  48. package/src/breadboard/engine/editor/index.ts +8 -0
  49. package/src/breadboard/engine/editor/operations/add-asset.ts +45 -0
  50. package/src/breadboard/engine/editor/operations/add-edge.ts +142 -0
  51. package/src/breadboard/engine/editor/operations/add-graph.ts +47 -0
  52. package/src/breadboard/engine/editor/operations/add-module.ts +64 -0
  53. package/src/breadboard/engine/editor/operations/add-node.ts +86 -0
  54. package/src/breadboard/engine/editor/operations/change-asset-metadata.ts +70 -0
  55. package/src/breadboard/engine/editor/operations/change-configuration.ts +82 -0
  56. package/src/breadboard/engine/editor/operations/change-edge-metadata.ts +58 -0
  57. package/src/breadboard/engine/editor/operations/change-edge.ts +111 -0
  58. package/src/breadboard/engine/editor/operations/change-graph-metadata.ts +52 -0
  59. package/src/breadboard/engine/editor/operations/change-metadata.ts +92 -0
  60. package/src/breadboard/engine/editor/operations/change-module.ts +64 -0
  61. package/src/breadboard/engine/editor/operations/error.ts +21 -0
  62. package/src/breadboard/engine/editor/operations/remove-asset.ts +48 -0
  63. package/src/breadboard/engine/editor/operations/remove-edge.ts +89 -0
  64. package/src/breadboard/engine/editor/operations/remove-graph.ts +49 -0
  65. package/src/breadboard/engine/editor/operations/remove-integration.ts +54 -0
  66. package/src/breadboard/engine/editor/operations/remove-module.ts +69 -0
  67. package/src/breadboard/engine/editor/operations/remove-node.ts +86 -0
  68. package/src/breadboard/engine/editor/operations/replace-graph.ts +52 -0
  69. package/src/breadboard/engine/editor/operations/toggle-export.ts +72 -0
  70. package/src/breadboard/engine/editor/operations/upsert-integration.ts +43 -0
  71. package/src/breadboard/engine/editor/selection.ts +58 -0
  72. package/src/breadboard/engine/editor/transforms/configure-sidewire.ts +73 -0
  73. package/src/breadboard/engine/editor/transforms/isolate-selection.ts +54 -0
  74. package/src/breadboard/engine/editor/transforms/merge-graph.ts +58 -0
  75. package/src/breadboard/engine/editor/transforms/move-to-graph.ts +102 -0
  76. package/src/breadboard/engine/editor/transforms/move-to-new-graph.ts +72 -0
  77. package/src/breadboard/engine/editor/transforms/sidewire-to-new-graph.ts +82 -0
  78. package/src/breadboard/engine/file-system/blob-transform.ts +44 -0
  79. package/src/breadboard/engine/file-system/composed-peristent-backend.ts +140 -0
  80. package/src/breadboard/engine/file-system/ephemeral-blob-store.ts +46 -0
  81. package/src/breadboard/engine/file-system/in-memory-blob-store.ts +87 -0
  82. package/src/breadboard/engine/file-system/index.ts +723 -0
  83. package/src/breadboard/engine/file-system/partial-persistent-backend.ts +109 -0
  84. package/src/breadboard/engine/file-system/path.ts +125 -0
  85. package/src/breadboard/engine/file-system/persistent-file.ts +66 -0
  86. package/src/breadboard/engine/file-system/readable-stream-file.ts +61 -0
  87. package/src/breadboard/engine/file-system/stub-file-system.ts +47 -0
  88. package/src/breadboard/engine/file-system/utils.ts +40 -0
  89. package/src/breadboard/engine/inspector/graph/bubbled-node.ts +162 -0
  90. package/src/breadboard/engine/inspector/graph/describe-cache.ts +78 -0
  91. package/src/breadboard/engine/inspector/graph/describe-type-cache.ts +48 -0
  92. package/src/breadboard/engine/inspector/graph/edge-cache.ts +118 -0
  93. package/src/breadboard/engine/inspector/graph/edge.ts +133 -0
  94. package/src/breadboard/engine/inspector/graph/event.ts +35 -0
  95. package/src/breadboard/engine/inspector/graph/exports-describer.ts +45 -0
  96. package/src/breadboard/engine/inspector/graph/graph-cache.ts +54 -0
  97. package/src/breadboard/engine/inspector/graph/graph-describer-manager.ts +338 -0
  98. package/src/breadboard/engine/inspector/graph/graph-descriptor-handle.ts +73 -0
  99. package/src/breadboard/engine/inspector/graph/graph-node-type.ts +111 -0
  100. package/src/breadboard/engine/inspector/graph/graph-queries.ts +256 -0
  101. package/src/breadboard/engine/inspector/graph/graph.ts +163 -0
  102. package/src/breadboard/engine/inspector/graph/inspectable-asset.ts +36 -0
  103. package/src/breadboard/engine/inspector/graph/kits.ts +208 -0
  104. package/src/breadboard/engine/inspector/graph/module.ts +69 -0
  105. package/src/breadboard/engine/inspector/graph/mutable-graph.ts +150 -0
  106. package/src/breadboard/engine/inspector/graph/node-cache.ts +123 -0
  107. package/src/breadboard/engine/inspector/graph/node-describer-manager.ts +279 -0
  108. package/src/breadboard/engine/inspector/graph/node-type-describer-manager.ts +122 -0
  109. package/src/breadboard/engine/inspector/graph/node.ts +149 -0
  110. package/src/breadboard/engine/inspector/graph/port-cache.ts +80 -0
  111. package/src/breadboard/engine/inspector/graph/ports.ts +292 -0
  112. package/src/breadboard/engine/inspector/graph/schemas.ts +131 -0
  113. package/src/breadboard/engine/inspector/graph/virtual-node.ts +184 -0
  114. package/src/breadboard/engine/inspector/graph-store.ts +629 -0
  115. package/src/breadboard/engine/inspector/index.ts +13 -0
  116. package/src/breadboard/engine/inspector/utils.ts +20 -0
  117. package/src/breadboard/engine/loader/capability.ts +184 -0
  118. package/src/breadboard/engine/loader/index.ts +14 -0
  119. package/src/breadboard/engine/loader/loader.ts +244 -0
  120. package/src/breadboard/engine/loader/resolve-graph-urls.ts +111 -0
  121. package/src/breadboard/engine/runtime/bubble.ts +269 -0
  122. package/src/breadboard/engine/runtime/graph-based-node-handler.ts +174 -0
  123. package/src/breadboard/engine/runtime/handler.ts +166 -0
  124. package/src/breadboard/engine/runtime/harness/diagnostics.ts +22 -0
  125. package/src/breadboard/engine/runtime/harness/events.ts +217 -0
  126. package/src/breadboard/engine/runtime/harness/index.ts +14 -0
  127. package/src/breadboard/engine/runtime/harness/local.ts +48 -0
  128. package/src/breadboard/engine/runtime/harness/plan-runner.ts +759 -0
  129. package/src/breadboard/engine/runtime/index.ts +8 -0
  130. package/src/breadboard/engine/runtime/legacy.ts +28 -0
  131. package/src/breadboard/engine/runtime/run/invoke-graph.ts +79 -0
  132. package/src/breadboard/engine/runtime/run/node-invoker.ts +137 -0
  133. package/src/breadboard/engine/runtime/run/run-graph.ts +186 -0
  134. package/src/breadboard/engine/runtime/run.ts +111 -0
  135. package/src/breadboard/engine/runtime/sandbox/capabilities-manager.ts +253 -0
  136. package/src/breadboard/engine/runtime/sandbox/file-system-handler-factory.ts +53 -0
  137. package/src/breadboard/engine/runtime/sandbox/invoke-describer.ts +125 -0
  138. package/src/breadboard/engine/runtime/static/condense.ts +155 -0
  139. package/src/breadboard/engine/runtime/static/create-plan.ts +134 -0
  140. package/src/breadboard/engine/runtime/static/nodes-to-subgraph.ts +168 -0
  141. package/src/breadboard/engine/runtime/static/orchestrator.ts +664 -0
  142. package/src/breadboard/engine/runtime/static/types.ts +77 -0
  143. package/src/breadboard/engine/runtime/traversal/index.ts +58 -0
  144. package/src/breadboard/engine/runtime/traversal/iterator.ts +124 -0
  145. package/src/breadboard/engine/runtime/traversal/machine.ts +58 -0
  146. package/src/breadboard/engine/runtime/traversal/representation.ts +115 -0
  147. package/src/breadboard/engine/runtime/traversal/result.ts +72 -0
  148. package/src/breadboard/engine/runtime/traversal/state.ts +115 -0
  149. package/src/breadboard/engine/telemetry.ts +121 -0
  150. package/src/breadboard/engine/types.ts +32 -0
  151. package/src/breadboard/lit-flow-runner.test.ts +44 -0
  152. package/src/breadboard/lit-flow-runner.ts +98 -0
  153. package/src/breadboard/runner.ts +80 -0
  154. package/src/index.ts +2 -0
  155. package/src/lit-chiclet.ts +69 -0
  156. package/src/lit-flow.ts +17 -7
  157. package/src/lit-schema-node.test.ts +65 -0
  158. package/src/lit-schema-node.ts +194 -0
@@ -0,0 +1,723 @@
1
+ // @ts-nocheck
2
+ /**
3
+ * @license
4
+ * Copyright 2024 Google LLC
5
+ * SPDX-License-Identifier: Apache-2.0
6
+ */
7
+
8
+ import type { LLMContent } from "@breadboard-ai/types";
9
+ import type {
10
+ FileSystem,
11
+ FileSystemFile,
12
+ FileSystemPath,
13
+ FileSystemQueryArguments,
14
+ FileSystemQueryEntry,
15
+ FileSystemQueryResult,
16
+ FileSystemReadArguments,
17
+ FileSystemEntry,
18
+ FileSystemReadResult,
19
+ FileSystemWriteArguments,
20
+ FileSystemWriteResult,
21
+ OuterFileSystems,
22
+ Outcome,
23
+ FileMap,
24
+ PersistentBackend,
25
+ FileSystemBlobStore,
26
+ CreateRunFileSystemArgs,
27
+ CreateModuleFileSystemArgs,
28
+ FileSystemWriteStreamArguments,
29
+ } from "@breadboard-ai/types";
30
+ import { Path } from "./path.js";
31
+ import { noStreams } from "./utils.js";
32
+ import { PersistentFile } from "./persistent-file.js";
33
+ import { InMemoryBlobStore } from "./in-memory-blob-store.js";
34
+ import { transformBlobs } from "./blob-transform.js";
35
+ import { baseURLFromString } from "../loader/loader.js";
36
+ import { ReadableStreamFile } from "./readable-stream-file.js";
37
+ import { err, ok } from "@breadboard-ai/utils";
38
+
39
+ export { FileSystemImpl, createFileSystem };
40
+
41
+ function createFileSystem(
42
+ args: Omit<
43
+ OuterFileSystems,
44
+ "graphUrl" | "blobs" | "session" | "run" | "assets"
45
+ >
46
+ ): FileSystem {
47
+ return new FileSystemImpl({ ...args, graphUrl: "" });
48
+ }
49
+
50
+ class StreamFile implements FileSystemFile {
51
+ readonly data = [];
52
+ readable: ReadableStream<LLMContent[]>;
53
+ writer: WritableStreamDefaultWriter<LLMContent[]> | null;
54
+
55
+ constructor() {
56
+ const { writable, readable } = new TransformStream<
57
+ LLMContent[],
58
+ LLMContent[]
59
+ >();
60
+ this.readable = readable;
61
+ this.writer = writable.getWriter();
62
+ }
63
+
64
+ async read(
65
+ _inflate: boolean,
66
+ start: number = 0
67
+ ): Promise<FileSystemReadResult> {
68
+ if (start !== 0) {
69
+ return err(`Reading partial streams is not supported.`);
70
+ }
71
+
72
+ const reader = this.readable.getReader();
73
+ try {
74
+ const { value, done } = await reader.read();
75
+ return { data: value, done };
76
+ } catch (e) {
77
+ return err(`Unable to read stream: ${(e as Error).message}`);
78
+ } finally {
79
+ reader.releaseLock();
80
+ }
81
+ }
82
+
83
+ async append(
84
+ data: LLMContent[],
85
+ done: boolean,
86
+ receipt = false
87
+ ): Promise<Outcome<void>> {
88
+ if (!this.writer) {
89
+ return err(`Unable to write to a closed stream`);
90
+ }
91
+ if (done) {
92
+ this.writer.close();
93
+ this.writer = null;
94
+ } else if (receipt) {
95
+ await this.writer.write(data);
96
+ } else {
97
+ this.writer.write(data);
98
+ }
99
+ }
100
+
101
+ async delete() {
102
+ if (!this.writer) return;
103
+
104
+ this.writer.close().catch(() => {
105
+ // eat errors
106
+ });
107
+ this.writer = null;
108
+ }
109
+
110
+ copy(): Outcome<FileSystemFile> {
111
+ // create a tee: A an B
112
+ // reassign the original stream to A
113
+ // return B
114
+ return err("Copy/move of stream files is not yet implemented");
115
+ }
116
+
117
+ queryEntry(path: FileSystemPath): FileSystemQueryEntry {
118
+ return { path, length: 0, stream: true };
119
+ }
120
+ }
121
+
122
+ class SimpleFile implements FileSystemFile {
123
+ constructor(public readonly data: LLMContent[]) {}
124
+
125
+ async read(
126
+ _inflate: boolean,
127
+ start: number = 0
128
+ ): Promise<FileSystemReadResult> {
129
+ if (start >= this.data.length) {
130
+ return err(`Length of file is lesser than start "${start}"`);
131
+ }
132
+ return {
133
+ data: this.data.slice(start),
134
+ last: this.data.length - 1,
135
+ };
136
+ }
137
+
138
+ async append(data: LLMContent[], done: boolean, receipt = false) {
139
+ const checkForStreams = noStreams(done, receipt);
140
+ if (!ok(checkForStreams)) {
141
+ return checkForStreams;
142
+ }
143
+ this.data.push(...data);
144
+ }
145
+
146
+ async delete() {}
147
+
148
+ copy(): Outcome<FileSystemFile> {
149
+ return new SimpleFile(this.data);
150
+ }
151
+
152
+ queryEntry(path: FileSystemPath): FileSystemQueryEntry {
153
+ return { path, length: this.data.length, stream: false };
154
+ }
155
+
156
+ static fromEntry(entry: FileSystemEntry): SimpleFile {
157
+ return new SimpleFile(entry.data);
158
+ }
159
+
160
+ static fromEntries(
161
+ entries: FileSystemEntry[]
162
+ ): Map<FileSystemPath, SimpleFile> {
163
+ return new Map(
164
+ entries.map((entry) => [entry.path, SimpleFile.fromEntry(entry)])
165
+ );
166
+ }
167
+ }
168
+
169
+ class FileSystemImpl implements FileSystem {
170
+ #graphUrl: string;
171
+
172
+ #local: PersistentBackend;
173
+ #mnt: PersistentBackend;
174
+ #env: FileMap;
175
+ #assets: FileMap;
176
+
177
+ #blobs: FileSystemBlobStore;
178
+ #ownsBlobs: boolean;
179
+
180
+ #session: FileMap;
181
+ #ownsSession: boolean;
182
+
183
+ #run: FileMap;
184
+ #ownsRun: boolean;
185
+
186
+ #tmp: FileMap;
187
+
188
+ constructor(outer: Partial<OuterFileSystems>) {
189
+ this.#graphUrl = getMainGraphUrl(outer.graphUrl);
190
+ if (!outer.local) {
191
+ throw new Error("Must supply persistent backend for file system to work");
192
+ }
193
+ if (!outer.mnt) {
194
+ throw new Error(
195
+ "Must supply `mnt` persistent backend for file system to work"
196
+ );
197
+ }
198
+ this.#mnt = outer.mnt;
199
+ this.#local = outer.local;
200
+ this.#env = SimpleFile.fromEntries(outer.env || []);
201
+ this.#assets = SimpleFile.fromEntries(outer.assets || []);
202
+
203
+ this.#ownsBlobs = !outer.blobs;
204
+ this.#blobs = outer.blobs ? outer.blobs : new InMemoryBlobStore();
205
+
206
+ this.#ownsSession = !outer.session;
207
+ this.#session = outer.session ? outer.session : new Map();
208
+
209
+ this.#ownsRun = !outer.run;
210
+ this.#run = outer.run ? outer.run : new Map();
211
+
212
+ this.#tmp = new Map();
213
+ }
214
+
215
+ async query({
216
+ path,
217
+ }: FileSystemQueryArguments): Promise<FileSystemQueryResult> {
218
+ const parsedPath = Path.create(path);
219
+ if (!ok(parsedPath)) {
220
+ return parsedPath;
221
+ }
222
+
223
+ if (parsedPath.persistent) {
224
+ return this.#local.query(this.#graphUrl, path);
225
+ } else if (parsedPath.mounted) {
226
+ return this.#mnt.query(this.#graphUrl, path);
227
+ } else {
228
+ const map = this.#getFileMap(parsedPath);
229
+ if (!ok(map)) {
230
+ return map;
231
+ }
232
+ return {
233
+ entries: this.#startsWith(map, path),
234
+ };
235
+ }
236
+ }
237
+
238
+ async read({
239
+ path,
240
+ start,
241
+ inflate = false,
242
+ }: FileSystemReadArguments): Promise<FileSystemReadResult> {
243
+ const parsedPath = Path.create(path);
244
+ if (!ok(parsedPath)) {
245
+ return parsedPath;
246
+ }
247
+
248
+ let file: FileSystemFile | undefined;
249
+
250
+ if (parsedPath.persistent) {
251
+ file = new PersistentFile(this.#graphUrl, path, this.#local);
252
+ } else if (parsedPath.mounted) {
253
+ // Treat it as if it's persistent file, but read from `mnt`.
254
+ file = new PersistentFile(this.#graphUrl, path, this.#mnt);
255
+ } else {
256
+ const map = this.#getFileMap(parsedPath);
257
+ if (!ok(map)) {
258
+ return map;
259
+ }
260
+ file = map.get(path);
261
+ if (!file) {
262
+ return err(`File not found: "${path}"`);
263
+ }
264
+ }
265
+
266
+ const result = await file.read(inflate, start);
267
+ if (!ok(result)) {
268
+ return result;
269
+ }
270
+
271
+ if (!parsedPath.persistent) {
272
+ if ("done" in result) {
273
+ // Handle end of stream.
274
+ const { done } = result;
275
+ if (done) {
276
+ // We are done with the stream, delete the file.
277
+ this.#deleteFile(path);
278
+ }
279
+ } else if (inflate && result.data) {
280
+ // Handle inflating ephemeral data.
281
+ const inflating = await transformBlobs(path, result.data, [
282
+ this.#blobs.inflator(),
283
+ ]);
284
+ if (!ok(inflating)) {
285
+ return inflating;
286
+ }
287
+ return { data: inflating, last: result.last };
288
+ }
289
+ } else {
290
+ return file.read(inflate, start);
291
+ }
292
+ return result;
293
+ }
294
+
295
+ async write(args: FileSystemWriteArguments): Promise<FileSystemWriteResult> {
296
+ const { path } = args;
297
+ // 1) Let's do path validation
298
+ const parsedPath = Path.create(path);
299
+ if (!ok(parsedPath)) {
300
+ return parsedPath;
301
+ }
302
+ if (!parsedPath.writable) {
303
+ return err(`Destination "${path}" is not writable`);
304
+ }
305
+
306
+ // 2) Handle copy/move case
307
+ if ("source" in args) {
308
+ const { source, move } = args;
309
+ const sourcePath = Path.create(source);
310
+ if (!ok(sourcePath)) {
311
+ return sourcePath;
312
+ }
313
+
314
+ if (parsedPath.mounted) {
315
+ return err(`Copying/moving files to mounted is not yet implemented`);
316
+ }
317
+
318
+ if (parsedPath.persistent) {
319
+ if (sourcePath.persistent) {
320
+ // a) Persistent -> Persistent
321
+ if (move) {
322
+ const moving = await this.#local.move(this.#graphUrl, source, path);
323
+ if (!ok(moving)) {
324
+ return moving;
325
+ }
326
+ } else {
327
+ const copying = await this.#local.copy(
328
+ this.#graphUrl,
329
+ source,
330
+ path
331
+ );
332
+ if (!ok(copying)) {
333
+ return copying;
334
+ }
335
+ }
336
+ return;
337
+ } else if (sourcePath.mounted) {
338
+ // b) Mounted to persistent
339
+ return err(
340
+ `Copying/moving files from mounted to persistent is not yet implemented`
341
+ );
342
+ } else {
343
+ // c) Ephemeral -> Persistent
344
+ const sourceMap = this.#getFileMap(sourcePath);
345
+ if (!ok(sourceMap)) {
346
+ return sourceMap;
347
+ }
348
+ const file = sourceMap.get(source);
349
+ if (!file) {
350
+ return err(`Source file not found: "${source}"`);
351
+ }
352
+ const writing = await this.#local.write(
353
+ this.#graphUrl,
354
+ path,
355
+ file.data
356
+ );
357
+ if (!ok(writing)) {
358
+ return writing;
359
+ }
360
+ if (move) {
361
+ sourceMap.delete(source);
362
+ }
363
+ return;
364
+ }
365
+ }
366
+
367
+ if (sourcePath.persistent) {
368
+ // d) Persistent -> Ephemeral
369
+ const sourceFile = new PersistentFile(
370
+ this.#graphUrl,
371
+ source,
372
+ this.#local
373
+ );
374
+ const sourceContents = await sourceFile.read(false);
375
+ if (!ok(sourceContents)) {
376
+ return sourceContents;
377
+ }
378
+
379
+ const destinationMap = this.#getFileMap(parsedPath);
380
+ if (!ok(destinationMap)) {
381
+ return destinationMap;
382
+ }
383
+ if (!sourceContents.data) {
384
+ return err(`Source file "${path}" is empty`);
385
+ }
386
+ destinationMap.set(path, new SimpleFile(sourceContents.data));
387
+ return this.#local.delete(this.#graphUrl, source, false);
388
+ }
389
+
390
+ if (sourcePath.mounted) {
391
+ return err(
392
+ `Copying/moving files from mounted to persistent is not yet implemented`
393
+ );
394
+ }
395
+
396
+ // e) Ephemeral -> Ephemeral
397
+ const sourceMap = this.#getFileMap(sourcePath);
398
+ if (!ok(sourceMap)) {
399
+ return sourceMap;
400
+ }
401
+ const file = sourceMap.get(source);
402
+ if (!file) {
403
+ return err(`Source file not found: "${source}"`);
404
+ }
405
+ const copy = file.copy();
406
+ if (!ok(copy)) {
407
+ return copy;
408
+ }
409
+ const destinationMap = this.#getFileMap(parsedPath);
410
+ if (!ok(destinationMap)) {
411
+ return destinationMap;
412
+ }
413
+ destinationMap.set(path, copy);
414
+ if (move) {
415
+ sourceMap.delete(source);
416
+ }
417
+ return;
418
+ }
419
+
420
+ // 3) Handle stream case
421
+ if ("stream" in args && args.stream) {
422
+ if (parsedPath.persistent) {
423
+ return err(
424
+ `Creating streams in "${parsedPath.root}" is not yet supported`
425
+ );
426
+ }
427
+
428
+ if (parsedPath.mounted) {
429
+ return err(
430
+ `Creating streams in "${parsedPath.root}" is not yet supported`
431
+ );
432
+ }
433
+
434
+ const map = this.#getFileMap(parsedPath);
435
+ if (!ok(map)) {
436
+ return map;
437
+ }
438
+
439
+ let file = map.get(path);
440
+ const { done } = args;
441
+ if (done) {
442
+ if (!file) {
443
+ return err(`Can't close stream on a non-existent file "${path}"`);
444
+ }
445
+ // Handle end of stream.
446
+ return file.append([], true);
447
+ } else {
448
+ if (!file) {
449
+ file = new StreamFile();
450
+ map.set(path, file);
451
+ }
452
+ const { receipt, data } = args;
453
+ const deflated = await transformBlobs(path, data, [
454
+ this.#blobs.deflator(),
455
+ ]);
456
+ if (!ok(deflated)) {
457
+ return deflated;
458
+ }
459
+ return file.append(deflated, false, receipt);
460
+ }
461
+ }
462
+
463
+ // 4) Handle delete case
464
+ if ("delete" in args) {
465
+ if (parsedPath.dir) {
466
+ await this.#deleteDir(path);
467
+ } else {
468
+ await this.#deleteFile(path);
469
+ }
470
+ return;
471
+ }
472
+
473
+ if (parsedPath.dir) {
474
+ return err(`Can't write data to a directory: "${path}"`);
475
+ }
476
+
477
+ const { data, append } = args;
478
+
479
+ // 5) Handle append case
480
+ if (append) {
481
+ if (parsedPath.persistent) {
482
+ return this.#local.append(this.#graphUrl, path, data);
483
+ }
484
+
485
+ if (parsedPath.mounted) {
486
+ return this.#mnt.append(this.#graphUrl, path, data);
487
+ }
488
+
489
+ const map = this.#getFileMap(parsedPath);
490
+ if (!ok(map)) {
491
+ return map;
492
+ }
493
+
494
+ const file = map.get(path);
495
+ if (file) {
496
+ const deflated = await transformBlobs(path, data, [
497
+ this.#blobs.deflator(),
498
+ ]);
499
+ if (!ok(deflated)) {
500
+ return deflated;
501
+ }
502
+ return file.append(deflated, false);
503
+ }
504
+ }
505
+
506
+ // 6) otherwise, fall through to create a new file
507
+ if (parsedPath.persistent) {
508
+ return this.#local.write(this.#graphUrl, path, data);
509
+ } else if (parsedPath.mounted) {
510
+ return this.#mnt.write(this.#graphUrl, path, data);
511
+ } else {
512
+ const deflated = await transformBlobs(path, data, [
513
+ this.#blobs.deflator(),
514
+ ]);
515
+ if (!ok(deflated)) {
516
+ return deflated;
517
+ }
518
+ const file = new SimpleFile(deflated);
519
+ const map = this.#getFileMap(parsedPath);
520
+ if (!ok(map)) {
521
+ return map;
522
+ }
523
+
524
+ map.set(path, file);
525
+ }
526
+ }
527
+
528
+ #getFileMap(parsedPath: Path): Outcome<FileMap> {
529
+ const { root } = parsedPath;
530
+ switch (root) {
531
+ case "local":
532
+ return err(`Querying "${parsedPath.root}" is not yet implemented.`);
533
+ case "env":
534
+ return this.#env;
535
+ case "assets":
536
+ return this.#assets;
537
+ case "session":
538
+ return this.#session;
539
+ case "run":
540
+ return this.#run;
541
+ case "tmp":
542
+ return this.#tmp;
543
+ default:
544
+ return err(`Unknown root "${root}"`);
545
+ }
546
+ }
547
+
548
+ #startsWith(map: FileMap, prefix: FileSystemPath): FileSystemQueryEntry[] {
549
+ const results: FileSystemQueryEntry[] = [];
550
+ for (const [path, file] of map.entries()) {
551
+ if (path.startsWith(prefix)) {
552
+ results.push(file.queryEntry(path));
553
+ }
554
+ }
555
+ return results;
556
+ }
557
+
558
+ #deleteFile(path: FileSystemPath) {
559
+ const parsedPath = Path.create(path);
560
+ if (!ok(parsedPath)) {
561
+ return parsedPath;
562
+ }
563
+ if (parsedPath.persistent) {
564
+ return this.#local.delete(this.#graphUrl, path, false);
565
+ }
566
+
567
+ const map = this.#getFileMap(parsedPath);
568
+ if (!ok(map)) {
569
+ return map;
570
+ }
571
+
572
+ const file = map.get(path);
573
+ if (!file) return;
574
+ map.delete(path);
575
+ // Async, but it's okay to not await here, because we don't need to wait
576
+ // for cleanup to complete.
577
+ file.delete().catch(() => {
578
+ // Eat the errors.
579
+ });
580
+ }
581
+
582
+ async #deleteDir(path: FileSystemPath) {
583
+ const parsedPath = Path.create(path);
584
+ if (!ok(parsedPath)) {
585
+ return parsedPath;
586
+ }
587
+
588
+ if (parsedPath.persistent) {
589
+ await this.#local.delete(this.#graphUrl, path, parsedPath.dir);
590
+ return;
591
+ }
592
+
593
+ const map = this.#getFileMap(parsedPath);
594
+ if (!ok(map)) {
595
+ return map;
596
+ }
597
+
598
+ const entries = this.#startsWith(map, path);
599
+ for (const entry of entries) {
600
+ this.#deleteFile(entry.path);
601
+ }
602
+ }
603
+
604
+ async addStream({
605
+ path,
606
+ stream,
607
+ }: FileSystemWriteStreamArguments): Promise<Outcome<void>> {
608
+ const parsedPath = Path.create(path);
609
+ if (!ok(parsedPath)) return parsedPath;
610
+
611
+ if (!parsedPath.writable) {
612
+ return err(`Can't write streams to path "${path}"`);
613
+ }
614
+ if (parsedPath.root === "local") {
615
+ return err(`Can't write streams to "/${parsedPath.root}/*"`);
616
+ }
617
+
618
+ const map = this.#getFileMap(parsedPath);
619
+ if (!ok(map)) return map;
620
+
621
+ const file = new ReadableStreamFile(stream);
622
+
623
+ map.set(path, file);
624
+ }
625
+
626
+ async close(): Promise<void> {
627
+ if (this.#ownsSession) {
628
+ await deleteAll(this.#session);
629
+ }
630
+ if (this.#ownsRun) {
631
+ await deleteAll(this.#run);
632
+ }
633
+ await deleteAll(this.#tmp);
634
+
635
+ async function deleteAll(map: FileMap) {
636
+ await Promise.all(
637
+ [...map.values()].map((file) => {
638
+ return file.delete();
639
+ })
640
+ );
641
+ }
642
+
643
+ if (this.#ownsBlobs) {
644
+ await this.#blobs.close();
645
+ }
646
+ }
647
+
648
+ async onEndRun(): Promise<void> {
649
+ return this.#mnt.onEndRun?.();
650
+ }
651
+
652
+ env(): FileSystemEntry[] {
653
+ return [...this.#env.entries()].map(([path, file]) => {
654
+ return {
655
+ path,
656
+ data: file.data,
657
+ };
658
+ });
659
+ }
660
+
661
+ createModuleFileSystem(args: CreateModuleFileSystemArgs): FileSystem {
662
+ const { graphUrl, env } = args;
663
+ return new FileSystemImpl({
664
+ graphUrl,
665
+ local: this.#local,
666
+ mnt: this.#mnt,
667
+ env: env || mapToEntries(this.#env),
668
+ assets: mapToEntries(this.#assets),
669
+ blobs: this.#blobs,
670
+ session: this.#session,
671
+ run: this.#run,
672
+ });
673
+ }
674
+
675
+ updateRunFileSystem(args: CreateRunFileSystemArgs): FileSystem {
676
+ const { graphUrl, env, assets } = args;
677
+ return new FileSystemImpl({
678
+ graphUrl,
679
+ local: this.#local,
680
+ mnt: this.#mnt,
681
+ env: env || mapToEntries(this.#env),
682
+ assets: assets || mapToEntries(this.#assets),
683
+ blobs: this.#blobs,
684
+ session: this.#session,
685
+ run: this.#run,
686
+ });
687
+ }
688
+
689
+ createRunFileSystem(args: CreateRunFileSystemArgs): FileSystem {
690
+ const { graphUrl, env, assets } = args;
691
+ return new FileSystemImpl({
692
+ graphUrl,
693
+ local: this.#local,
694
+ mnt: this.#mnt,
695
+ env: env || mapToEntries(this.#env),
696
+ assets: assets || mapToEntries(this.#assets),
697
+ blobs: this.#blobs,
698
+ session: this.#session,
699
+ });
700
+ }
701
+ }
702
+
703
+ function mapToEntries(map: FileMap): FileSystemEntry[] {
704
+ return [...map.entries()].map(([path, file]) => ({
705
+ path,
706
+ data: file.data,
707
+ }));
708
+ }
709
+
710
+ /**
711
+ * Tries its darndest to strip out the module/subgraph ids
712
+ * and return the main graph URL
713
+ * @param url
714
+ */
715
+ function getMainGraphUrl(url: string | undefined): string {
716
+ if (!url) return "";
717
+
718
+ try {
719
+ return baseURLFromString(url)?.href || "";
720
+ } catch {
721
+ return "";
722
+ }
723
+ }