@sandboxxjs/state 0.0.1

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/dist/index.js ADDED
@@ -0,0 +1,1091 @@
1
+ // src/errors.ts
2
+ class StateError extends Error {
3
+ constructor(message) {
4
+ super(message);
5
+ this.name = "StateError";
6
+ }
7
+ }
8
+
9
+ class FileSystemError extends StateError {
10
+ constructor(message) {
11
+ super(message);
12
+ this.name = "FileSystemError";
13
+ }
14
+ }
15
+
16
+ // src/StateFS.ts
17
+ class StateFS {
18
+ sandbox;
19
+ constructor(sandbox) {
20
+ this.sandbox = sandbox;
21
+ }
22
+ async read(path) {
23
+ const result = await this.sandbox.shell(`cat "${path}"`);
24
+ if (!result.success) {
25
+ throw new FileSystemError(`Failed to read file: ${path}`);
26
+ }
27
+ return result.stdout;
28
+ }
29
+ async write(path, data) {
30
+ const dir = path.substring(0, path.lastIndexOf("/"));
31
+ if (dir) {
32
+ await this.sandbox.shell(`mkdir -p "${dir}"`);
33
+ }
34
+ const result = await this.sandbox.shell(`cat > "${path}" << 'SANDBOX_EOF'
35
+ ${data}
36
+ SANDBOX_EOF`);
37
+ if (!result.success) {
38
+ throw new FileSystemError(`Failed to write file: ${path}`);
39
+ }
40
+ }
41
+ async exists(path) {
42
+ const result = await this.sandbox.shell(`test -e "${path}" && echo "yes" || echo "no"`);
43
+ return result.stdout.trim() === "yes";
44
+ }
45
+ async delete(path) {
46
+ await this.sandbox.shell(`rm -rf "${path}"`);
47
+ }
48
+ async list(path) {
49
+ const result = await this.sandbox.shell(`ls -1 "${path}" 2>/dev/null`);
50
+ if (!result.success || !result.stdout.trim()) {
51
+ return [];
52
+ }
53
+ return result.stdout.trim().split(`
54
+ `).filter(Boolean);
55
+ }
56
+ }
57
+ // src/StateEnv.ts
58
+ class StateEnv {
59
+ vars;
60
+ constructor(initial) {
61
+ this.vars = new Map(Object.entries(initial ?? {}));
62
+ }
63
+ get(key) {
64
+ return this.vars.get(key);
65
+ }
66
+ set(key, value) {
67
+ this.vars.set(key, value);
68
+ }
69
+ has(key) {
70
+ return this.vars.has(key);
71
+ }
72
+ delete(key) {
73
+ this.vars.delete(key);
74
+ }
75
+ keys() {
76
+ return [...this.vars.keys()];
77
+ }
78
+ all() {
79
+ return Object.fromEntries(this.vars);
80
+ }
81
+ }
82
+ // src/StateStorage.ts
83
+ class StateStorage {
84
+ data;
85
+ constructor(initial) {
86
+ this.data = new Map(Object.entries(initial ?? {}));
87
+ }
88
+ getItem(key) {
89
+ return this.data.get(key) ?? null;
90
+ }
91
+ setItem(key, value) {
92
+ this.data.set(key, value);
93
+ }
94
+ removeItem(key) {
95
+ this.data.delete(key);
96
+ }
97
+ clear() {
98
+ this.data.clear();
99
+ }
100
+ keys() {
101
+ return [...this.data.keys()];
102
+ }
103
+ }
104
+ // src/StateLog.ts
105
+ class StateLog {
106
+ entries = [];
107
+ fs = {
108
+ write: (path, data) => {
109
+ this.entries.push({ op: "fs.write", args: { path, data } });
110
+ return this;
111
+ },
112
+ delete: (path) => {
113
+ this.entries.push({ op: "fs.delete", args: { path } });
114
+ return this;
115
+ }
116
+ };
117
+ env = {
118
+ set: (key, value) => {
119
+ this.entries.push({ op: "env.set", args: { key, value } });
120
+ return this;
121
+ },
122
+ delete: (key) => {
123
+ this.entries.push({ op: "env.delete", args: { key } });
124
+ return this;
125
+ }
126
+ };
127
+ storage = {
128
+ set: (key, value) => {
129
+ this.entries.push({ op: "storage.set", args: { key, value } });
130
+ return this;
131
+ },
132
+ delete: (key) => {
133
+ this.entries.push({ op: "storage.delete", args: { key } });
134
+ return this;
135
+ },
136
+ clear: () => {
137
+ this.entries.push({ op: "storage.clear", args: {} });
138
+ return this;
139
+ }
140
+ };
141
+ recordEntry(op, args) {
142
+ this.entries.push({ op, args });
143
+ return this;
144
+ }
145
+ getEntries() {
146
+ return [...this.entries];
147
+ }
148
+ toJSON() {
149
+ return JSON.stringify(this.entries);
150
+ }
151
+ static fromJSON(json) {
152
+ const log = new StateLog;
153
+ log.entries = JSON.parse(json);
154
+ return log;
155
+ }
156
+ static fromEntries(entries) {
157
+ const log = new StateLog;
158
+ log.entries = [...entries];
159
+ return log;
160
+ }
161
+ compact() {
162
+ const fsState = new Map;
163
+ const envState = new Map;
164
+ const storageState = new Map;
165
+ let storageClear = null;
166
+ for (const entry of this.entries) {
167
+ const { op, args } = entry;
168
+ if (op === "fs.write") {
169
+ fsState.set(args.path, entry);
170
+ } else if (op === "fs.delete") {
171
+ fsState.set(args.path, entry);
172
+ } else if (op === "env.set") {
173
+ envState.set(args.key, entry);
174
+ } else if (op === "env.delete") {
175
+ envState.set(args.key, entry);
176
+ } else if (op === "storage.set") {
177
+ storageState.set(args.key, entry);
178
+ } else if (op === "storage.delete") {
179
+ storageState.set(args.key, entry);
180
+ } else if (op === "storage.clear") {
181
+ storageClear = entry;
182
+ storageState.clear();
183
+ }
184
+ }
185
+ const compactedEntries = [];
186
+ for (const entry of fsState.values()) {
187
+ compactedEntries.push(entry);
188
+ }
189
+ for (const entry of envState.values()) {
190
+ compactedEntries.push(entry);
191
+ }
192
+ if (storageClear) {
193
+ compactedEntries.push(storageClear);
194
+ }
195
+ for (const entry of storageState.values()) {
196
+ compactedEntries.push(entry);
197
+ }
198
+ return StateLog.fromEntries(compactedEntries);
199
+ }
200
+ }
201
+ function buildStateLog() {
202
+ return new StateLog;
203
+ }
204
+ function loadStateLog(json) {
205
+ return StateLog.fromJSON(json);
206
+ }
207
+ // src/opRegistry.ts
208
+ var opRegistry = {
209
+ "fs.write": {
210
+ namespace: "fs",
211
+ method: "write",
212
+ args: ["path", "data"],
213
+ replay: async (target, args) => {
214
+ await target.fs.write(args.path, args.data);
215
+ }
216
+ },
217
+ "fs.delete": {
218
+ namespace: "fs",
219
+ method: "delete",
220
+ args: ["path"],
221
+ replay: async (target, args) => {
222
+ await target.fs.delete(args.path);
223
+ }
224
+ },
225
+ "env.set": {
226
+ namespace: "env",
227
+ method: "set",
228
+ args: ["key", "value"],
229
+ replay: (target, args) => {
230
+ target.env.set(args.key, args.value);
231
+ }
232
+ },
233
+ "env.delete": {
234
+ namespace: "env",
235
+ method: "delete",
236
+ args: ["key"],
237
+ replay: (target, args) => {
238
+ target.env.delete(args.key);
239
+ }
240
+ },
241
+ "storage.set": {
242
+ namespace: "storage",
243
+ method: "setItem",
244
+ args: ["key", "value"],
245
+ replay: (target, args) => {
246
+ target.storage.setItem(args.key, args.value);
247
+ }
248
+ },
249
+ "storage.delete": {
250
+ namespace: "storage",
251
+ method: "removeItem",
252
+ args: ["key"],
253
+ replay: (target, args) => {
254
+ target.storage.removeItem(args.key);
255
+ }
256
+ },
257
+ "storage.clear": {
258
+ namespace: "storage",
259
+ method: "clear",
260
+ args: [],
261
+ replay: (target) => {
262
+ target.storage.clear();
263
+ }
264
+ }
265
+ };
266
+ function findOp(namespace, method) {
267
+ for (const [op, config] of Object.entries(opRegistry)) {
268
+ if (config.namespace === namespace && config.method === method) {
269
+ return op;
270
+ }
271
+ }
272
+ return;
273
+ }
274
+ function argsToEntry(op, methodArgs) {
275
+ const config = opRegistry[op];
276
+ if (!config)
277
+ return {};
278
+ const entry = {};
279
+ config.args.forEach((name, index) => {
280
+ entry[name] = methodArgs[index];
281
+ });
282
+ return entry;
283
+ }
284
+
285
+ // src/replayStateLog.ts
286
+ async function replayStateLog(log, target) {
287
+ for (const entry of log.getEntries()) {
288
+ const config = opRegistry[entry.op];
289
+ if (config) {
290
+ await config.replay(target, entry.args);
291
+ }
292
+ }
293
+ }
294
+ // src/createState.ts
295
+ function createRecordingProxy(target, namespace, log) {
296
+ return new Proxy(target, {
297
+ get(obj, prop) {
298
+ const value = obj[prop];
299
+ if (typeof value !== "function") {
300
+ return value;
301
+ }
302
+ const method = prop;
303
+ const op = findOp(namespace, method);
304
+ if (!op) {
305
+ return value.bind(obj);
306
+ }
307
+ return (...args) => {
308
+ const result = value.apply(obj, args);
309
+ const record = () => {
310
+ const entryArgs = argsToEntry(op, args);
311
+ log.recordEntry(op, entryArgs);
312
+ };
313
+ if (result instanceof Promise) {
314
+ return result.then((res) => {
315
+ record();
316
+ return res;
317
+ });
318
+ } else {
319
+ record();
320
+ return result;
321
+ }
322
+ };
323
+ }
324
+ });
325
+ }
326
+ function createState(options) {
327
+ const { sandbox, env, enableRecord } = options;
328
+ const baseFS = new StateFS(sandbox);
329
+ const baseEnv = new StateEnv(env);
330
+ const baseStorage = new StateStorage;
331
+ if (!enableRecord) {
332
+ return {
333
+ fs: baseFS,
334
+ env: baseEnv,
335
+ storage: baseStorage
336
+ };
337
+ }
338
+ const stateLog = buildStateLog();
339
+ return {
340
+ fs: createRecordingProxy(baseFS, "fs", stateLog),
341
+ env: createRecordingProxy(baseEnv, "env", stateLog),
342
+ storage: createRecordingProxy(baseStorage, "storage", stateLog),
343
+ stateLog
344
+ };
345
+ }
346
+ // ../../node_modules/.bun/resourcexjs@0.4.0/node_modules/resourcexjs/dist/index.js
347
+ import { readFile, writeFile, readdir, mkdir, rm, access, stat as fsStat } from "node:fs/promises";
348
+ import { resolve, dirname } from "node:path";
349
+ import { homedir } from "node:os";
350
+ import { join } from "node:path";
351
+ import { readFile as readFile2, writeFile as writeFile2, readdir as readdir2, access as access2, unlink, mkdir as mkdir2, stat, rm as rm2 } from "node:fs/promises";
352
+ import { join as join2 } from "node:path";
353
+
354
+ class ResourceXError extends Error {
355
+ constructor(message, options) {
356
+ super(message, options);
357
+ this.name = "ResourceXError";
358
+ }
359
+ }
360
+
361
+ class ParseError extends ResourceXError {
362
+ url;
363
+ constructor(message, url) {
364
+ super(message);
365
+ this.url = url;
366
+ this.name = "ParseError";
367
+ }
368
+ }
369
+
370
+ class TransportError extends ResourceXError {
371
+ transport;
372
+ constructor(message, transport, options) {
373
+ super(message, options);
374
+ this.transport = transport;
375
+ this.name = "TransportError";
376
+ }
377
+ }
378
+
379
+ class SemanticError extends ResourceXError {
380
+ semantic;
381
+ constructor(message, semantic, options) {
382
+ super(message, options);
383
+ this.semantic = semantic;
384
+ this.name = "SemanticError";
385
+ }
386
+ }
387
+ function parseARP(url) {
388
+ if (!url.startsWith("arp:")) {
389
+ throw new ParseError(`Invalid ARP URL: must start with "arp:"`, url);
390
+ }
391
+ const content = url.substring(4);
392
+ const separatorIndex = content.indexOf("://");
393
+ if (separatorIndex === -1) {
394
+ throw new ParseError(`Invalid ARP URL: missing "://"`, url);
395
+ }
396
+ const typePart = content.substring(0, separatorIndex);
397
+ const location = content.substring(separatorIndex + 3);
398
+ const colonIndex = typePart.indexOf(":");
399
+ if (colonIndex === -1) {
400
+ throw new ParseError(`Invalid ARP URL: must have exactly 2 types (semantic:transport)`, url);
401
+ }
402
+ const semantic = typePart.substring(0, colonIndex);
403
+ const transport = typePart.substring(colonIndex + 1);
404
+ if (!semantic) {
405
+ throw new ParseError(`Invalid ARP URL: semantic type cannot be empty`, url);
406
+ }
407
+ if (!transport) {
408
+ throw new ParseError(`Invalid ARP URL: transport type cannot be empty`, url);
409
+ }
410
+ if (!location) {
411
+ throw new ParseError(`Invalid ARP URL: location cannot be empty`, url);
412
+ }
413
+ return { semantic, transport, location };
414
+ }
415
+
416
+ class HttpTransportHandler {
417
+ name;
418
+ protocol;
419
+ capabilities = {
420
+ canRead: true,
421
+ canWrite: false,
422
+ canList: false,
423
+ canDelete: false,
424
+ canStat: false
425
+ };
426
+ constructor(protocol = "https") {
427
+ this.protocol = protocol;
428
+ this.name = protocol;
429
+ }
430
+ async read(location) {
431
+ const url = `${this.protocol}://${location}`;
432
+ try {
433
+ const response = await fetch(url);
434
+ if (!response.ok) {
435
+ throw new TransportError(`HTTP ${response.status}: ${response.statusText} - ${url}`, this.name);
436
+ }
437
+ const arrayBuffer = await response.arrayBuffer();
438
+ return Buffer.from(arrayBuffer);
439
+ } catch (error) {
440
+ if (error instanceof TransportError) {
441
+ throw error;
442
+ }
443
+ throw new TransportError(`Network error: ${url}`, this.name, {
444
+ cause: error
445
+ });
446
+ }
447
+ }
448
+ }
449
+ var httpsHandler = new HttpTransportHandler("https");
450
+ var httpHandler = new HttpTransportHandler("http");
451
+
452
+ class FileTransportHandler {
453
+ name = "file";
454
+ capabilities = {
455
+ canRead: true,
456
+ canWrite: true,
457
+ canList: true,
458
+ canDelete: true,
459
+ canStat: true
460
+ };
461
+ resolvePath(location) {
462
+ return resolve(process.cwd(), location);
463
+ }
464
+ async read(location) {
465
+ const filePath = this.resolvePath(location);
466
+ try {
467
+ return await readFile(filePath);
468
+ } catch (error) {
469
+ const err = error;
470
+ throw new TransportError(`File read error: ${err.code} - ${filePath}`, this.name, {
471
+ cause: err
472
+ });
473
+ }
474
+ }
475
+ async write(location, content) {
476
+ const filePath = this.resolvePath(location);
477
+ try {
478
+ await mkdir(dirname(filePath), { recursive: true });
479
+ await writeFile(filePath, content);
480
+ } catch (error) {
481
+ const err = error;
482
+ throw new TransportError(`File write error: ${err.code} - ${filePath}`, this.name, {
483
+ cause: err
484
+ });
485
+ }
486
+ }
487
+ async list(location) {
488
+ const dirPath = this.resolvePath(location);
489
+ try {
490
+ return await readdir(dirPath);
491
+ } catch (error) {
492
+ const err = error;
493
+ throw new TransportError(`Directory list error: ${err.code} - ${dirPath}`, this.name, {
494
+ cause: err
495
+ });
496
+ }
497
+ }
498
+ async mkdir(location) {
499
+ const dirPath = this.resolvePath(location);
500
+ try {
501
+ await mkdir(dirPath, { recursive: true });
502
+ } catch (error) {
503
+ const err = error;
504
+ throw new TransportError(`Directory create error: ${err.code} - ${dirPath}`, this.name, {
505
+ cause: err
506
+ });
507
+ }
508
+ }
509
+ async exists(location) {
510
+ const filePath = this.resolvePath(location);
511
+ try {
512
+ await access(filePath);
513
+ return true;
514
+ } catch {
515
+ return false;
516
+ }
517
+ }
518
+ async stat(location) {
519
+ const filePath = this.resolvePath(location);
520
+ try {
521
+ const stats = await fsStat(filePath);
522
+ return {
523
+ size: stats.size,
524
+ modifiedAt: stats.mtime,
525
+ isDirectory: stats.isDirectory()
526
+ };
527
+ } catch (error) {
528
+ const err = error;
529
+ throw new TransportError(`File stat error: ${err.code} - ${filePath}`, this.name, {
530
+ cause: err
531
+ });
532
+ }
533
+ }
534
+ async delete(location) {
535
+ const filePath = this.resolvePath(location);
536
+ try {
537
+ await rm(filePath, { recursive: true });
538
+ } catch (error) {
539
+ const err = error;
540
+ throw new TransportError(`File delete error: ${err.code} - ${filePath}`, this.name, {
541
+ cause: err
542
+ });
543
+ }
544
+ }
545
+ }
546
+ var fileHandler = new FileTransportHandler;
547
+ function deepracticeHandler(config = {}) {
548
+ const parentDir = config.parentDir || homedir();
549
+ const baseDir = join(parentDir, ".deepractice");
550
+ function resolvePath(location) {
551
+ return join(baseDir, location);
552
+ }
553
+ return {
554
+ name: "deepractice",
555
+ capabilities: {
556
+ canRead: true,
557
+ canWrite: true,
558
+ canList: true,
559
+ canDelete: true,
560
+ canStat: true
561
+ },
562
+ async read(location) {
563
+ const fullPath = resolvePath(location);
564
+ try {
565
+ return await readFile2(fullPath);
566
+ } catch (error) {
567
+ throw new TransportError(`Failed to read from deepractice: ${error.message}`, "deepractice", { cause: error });
568
+ }
569
+ },
570
+ async write(location, content) {
571
+ const fullPath = resolvePath(location);
572
+ try {
573
+ await mkdir2(join(fullPath, ".."), { recursive: true });
574
+ await writeFile2(fullPath, content);
575
+ } catch (error) {
576
+ throw new TransportError(`Failed to write to deepractice: ${error.message}`, "deepractice", { cause: error });
577
+ }
578
+ },
579
+ async list(location) {
580
+ const fullPath = resolvePath(location);
581
+ try {
582
+ return await readdir2(fullPath);
583
+ } catch (error) {
584
+ throw new TransportError(`Failed to list deepractice directory: ${error.message}`, "deepractice", { cause: error });
585
+ }
586
+ },
587
+ async exists(location) {
588
+ const fullPath = resolvePath(location);
589
+ try {
590
+ await access2(fullPath);
591
+ return true;
592
+ } catch {
593
+ return false;
594
+ }
595
+ },
596
+ async stat(location) {
597
+ const fullPath = resolvePath(location);
598
+ try {
599
+ const stats = await stat(fullPath);
600
+ return {
601
+ size: stats.size,
602
+ isDirectory: stats.isDirectory(),
603
+ modifiedAt: stats.mtime
604
+ };
605
+ } catch (error) {
606
+ throw new TransportError(`Failed to stat deepractice resource: ${error.message}`, "deepractice", { cause: error });
607
+ }
608
+ },
609
+ async delete(location) {
610
+ const fullPath = resolvePath(location);
611
+ try {
612
+ const stats = await stat(fullPath);
613
+ if (stats.isDirectory()) {
614
+ await rm2(fullPath, { recursive: true, force: true });
615
+ } else {
616
+ await unlink(fullPath);
617
+ }
618
+ } catch (error) {
619
+ throw new TransportError(`Failed to delete from deepractice: ${error.message}`, "deepractice", { cause: error });
620
+ }
621
+ }
622
+ };
623
+ }
624
+ var handlers = new Map([
625
+ ["https", httpsHandler],
626
+ ["http", httpHandler],
627
+ ["file", fileHandler]
628
+ ]);
629
+ function getTransportHandler(name) {
630
+ const handler = handlers.get(name);
631
+ if (!handler) {
632
+ throw new TransportError(`Unsupported transport type: ${name}`, name);
633
+ }
634
+ return handler;
635
+ }
636
+ function registerTransportHandler(handler) {
637
+ handlers.set(handler.name, handler);
638
+ }
639
+
640
+ class TextSemanticHandler {
641
+ name = "text";
642
+ async resolve(transport, location, context) {
643
+ const buffer = await transport.read(location);
644
+ const text = buffer.toString("utf-8");
645
+ const meta = {
646
+ url: context.url,
647
+ semantic: context.semantic,
648
+ transport: context.transport,
649
+ location: context.location,
650
+ size: buffer.length,
651
+ encoding: "utf-8",
652
+ mimeType: "text/plain",
653
+ resolvedAt: context.timestamp.toISOString()
654
+ };
655
+ return {
656
+ type: "text",
657
+ content: text,
658
+ meta
659
+ };
660
+ }
661
+ async deposit(transport, location, data, _context) {
662
+ if (!transport.write) {
663
+ throw new SemanticError(`Transport "${transport.name}" does not support write operation`, this.name);
664
+ }
665
+ const buffer = Buffer.from(data, "utf-8");
666
+ await transport.write(location, buffer);
667
+ }
668
+ async exists(transport, location, _context) {
669
+ if (transport.exists) {
670
+ return transport.exists(location);
671
+ }
672
+ try {
673
+ await transport.read(location);
674
+ return true;
675
+ } catch {
676
+ return false;
677
+ }
678
+ }
679
+ async delete(transport, location, _context) {
680
+ if (!transport.delete) {
681
+ throw new SemanticError(`Transport "${transport.name}" does not support delete operation`, this.name);
682
+ }
683
+ await transport.delete(location);
684
+ }
685
+ }
686
+ var textHandler = new TextSemanticHandler;
687
+ function toBuffer(data) {
688
+ if (Buffer.isBuffer(data)) {
689
+ return data;
690
+ }
691
+ if (data instanceof Uint8Array) {
692
+ return Buffer.from(data);
693
+ }
694
+ if (data instanceof ArrayBuffer) {
695
+ return Buffer.from(data);
696
+ }
697
+ if (Array.isArray(data)) {
698
+ return Buffer.from(data);
699
+ }
700
+ throw new SemanticError(`Unsupported binary input type`, "binary");
701
+ }
702
+
703
+ class BinarySemanticHandler {
704
+ name = "binary";
705
+ async resolve(transport, location, context) {
706
+ const buffer = await transport.read(location);
707
+ const meta = {
708
+ url: context.url,
709
+ semantic: context.semantic,
710
+ transport: context.transport,
711
+ location: context.location,
712
+ size: buffer.length,
713
+ resolvedAt: context.timestamp.toISOString()
714
+ };
715
+ return {
716
+ type: "binary",
717
+ content: buffer,
718
+ meta
719
+ };
720
+ }
721
+ async deposit(transport, location, data, _context) {
722
+ if (!transport.write) {
723
+ throw new SemanticError(`Transport "${transport.name}" does not support write operation`, this.name);
724
+ }
725
+ const buffer = toBuffer(data);
726
+ await transport.write(location, buffer);
727
+ }
728
+ async exists(transport, location, _context) {
729
+ if (transport.exists) {
730
+ return transport.exists(location);
731
+ }
732
+ try {
733
+ await transport.read(location);
734
+ return true;
735
+ } catch {
736
+ return false;
737
+ }
738
+ }
739
+ async delete(transport, location, _context) {
740
+ if (!transport.delete) {
741
+ throw new SemanticError(`Transport "${transport.name}" does not support delete operation`, this.name);
742
+ }
743
+ await transport.delete(location);
744
+ }
745
+ }
746
+ var binaryHandler = new BinarySemanticHandler;
747
+ var handlers2 = new Map([
748
+ ["text", textHandler],
749
+ ["binary", binaryHandler]
750
+ ]);
751
+ function getSemanticHandler(name) {
752
+ const handler = handlers2.get(name);
753
+ if (!handler) {
754
+ throw new SemanticError(`Unsupported semantic type: ${name}`, name);
755
+ }
756
+ return handler;
757
+ }
758
+ function registerSemanticHandler(handler) {
759
+ handlers2.set(handler.name, handler);
760
+ }
761
+ function createContext(url, semantic, transport, location) {
762
+ return {
763
+ url,
764
+ semantic,
765
+ transport,
766
+ location,
767
+ timestamp: new Date
768
+ };
769
+ }
770
+ async function resolve2(url) {
771
+ const parsed = parseARP(url);
772
+ const transport = getTransportHandler(parsed.transport);
773
+ const semantic = getSemanticHandler(parsed.semantic);
774
+ const context = createContext(url, parsed.semantic, parsed.transport, parsed.location);
775
+ return semantic.resolve(transport, parsed.location, context);
776
+ }
777
+ async function deposit(url, data) {
778
+ const parsed = parseARP(url);
779
+ const transport = getTransportHandler(parsed.transport);
780
+ const semantic = getSemanticHandler(parsed.semantic);
781
+ if (!semantic.deposit) {
782
+ throw new SemanticError(`Semantic "${semantic.name}" does not support deposit operation`, parsed.semantic);
783
+ }
784
+ const context = createContext(url, parsed.semantic, parsed.transport, parsed.location);
785
+ await semantic.deposit(transport, parsed.location, data, context);
786
+ }
787
+ async function resourceExists(url) {
788
+ const parsed = parseARP(url);
789
+ const transport = getTransportHandler(parsed.transport);
790
+ const semantic = getSemanticHandler(parsed.semantic);
791
+ const context = createContext(url, parsed.semantic, parsed.transport, parsed.location);
792
+ if (semantic.exists) {
793
+ return semantic.exists(transport, parsed.location, context);
794
+ }
795
+ if (transport.exists) {
796
+ return transport.exists(parsed.location);
797
+ }
798
+ try {
799
+ await transport.read(parsed.location);
800
+ return true;
801
+ } catch {
802
+ return false;
803
+ }
804
+ }
805
+ async function resourceDelete(url) {
806
+ const parsed = parseARP(url);
807
+ const transport = getTransportHandler(parsed.transport);
808
+ const semantic = getSemanticHandler(parsed.semantic);
809
+ const context = createContext(url, parsed.semantic, parsed.transport, parsed.location);
810
+ if (semantic.delete) {
811
+ return semantic.delete(transport, parsed.location, context);
812
+ }
813
+ if (!transport.delete) {
814
+ throw new SemanticError(`Neither semantic "${semantic.name}" nor transport "${transport.name}" supports delete operation`, parsed.semantic);
815
+ }
816
+ await transport.delete(parsed.location);
817
+ }
818
+ function createResourceRegistry() {
819
+ const registry = new Map;
820
+ return {
821
+ register(definition) {
822
+ if (!/^[a-z][a-z0-9-]*$/.test(definition.name)) {
823
+ throw new ParseError(`Invalid resource name: "${definition.name}". Must start with lowercase letter and contain only lowercase letters, numbers, and hyphens.`, definition.name);
824
+ }
825
+ getSemanticHandler(definition.semantic);
826
+ getTransportHandler(definition.transport);
827
+ registry.set(definition.name, definition);
828
+ },
829
+ get(name) {
830
+ return registry.get(name);
831
+ },
832
+ has(name) {
833
+ return registry.has(name);
834
+ },
835
+ clear() {
836
+ registry.clear();
837
+ }
838
+ };
839
+ }
840
+
841
+ class ResourceX {
842
+ timeout;
843
+ alias;
844
+ resourceRegistry;
845
+ constructor(config = {}) {
846
+ this.timeout = config.timeout;
847
+ this.alias = config.alias || "@";
848
+ this.resourceRegistry = createResourceRegistry();
849
+ if (config.transports) {
850
+ for (const handler of config.transports) {
851
+ registerTransportHandler(handler);
852
+ }
853
+ }
854
+ if (config.semantics) {
855
+ for (const handler of config.semantics) {
856
+ registerSemanticHandler(handler);
857
+ }
858
+ }
859
+ if (config.resources) {
860
+ for (const resource of config.resources) {
861
+ this.resourceRegistry.register(resource);
862
+ }
863
+ }
864
+ }
865
+ parseURL(url) {
866
+ let content;
867
+ if (url.startsWith("arp:")) {
868
+ content = url.substring(4);
869
+ } else if (url.startsWith(this.alias)) {
870
+ content = url.substring(this.alias.length);
871
+ } else {
872
+ throw new ParseError(`Invalid URL prefix: must start with "arp:" or "${this.alias}"`, url);
873
+ }
874
+ const separatorIndex = content.indexOf("://");
875
+ if (separatorIndex === -1) {
876
+ throw new ParseError(`Invalid URL format: missing "://"`, url);
877
+ }
878
+ const beforeSeparator = content.substring(0, separatorIndex);
879
+ const location = content.substring(separatorIndex + 3);
880
+ const colonCount = (beforeSeparator.match(/:/g) || []).length;
881
+ if (colonCount === 1) {
882
+ const parts = beforeSeparator.split(":");
883
+ if (parts.length !== 2) {
884
+ throw new ParseError(`Invalid ARP URL format`, url);
885
+ }
886
+ const [semantic, transport] = parts;
887
+ if (!semantic || !transport || !location) {
888
+ throw new ParseError(`Invalid ARP URL: semantic, transport, and location are required`, url);
889
+ }
890
+ const arpUrl = `arp:${semantic}:${transport}://${location}`;
891
+ return {
892
+ arpUrl,
893
+ parsed: { semantic, transport, location }
894
+ };
895
+ }
896
+ if (colonCount === 0) {
897
+ const name = beforeSeparator;
898
+ if (!name || !location) {
899
+ throw new ParseError(`Invalid Resource URL: name and location are required`, url);
900
+ }
901
+ const definition = this.resourceRegistry.get(name);
902
+ if (!definition) {
903
+ throw new ParseError(`Unknown resource: "${name}"`, url);
904
+ }
905
+ const fullLocation = definition.basePath ? join2(definition.basePath, location) : location;
906
+ const arpUrl = `arp:${definition.semantic}:${definition.transport}://${fullLocation}`;
907
+ return {
908
+ arpUrl,
909
+ parsed: {
910
+ semantic: definition.semantic,
911
+ transport: definition.transport,
912
+ location: fullLocation
913
+ }
914
+ };
915
+ }
916
+ throw new ParseError(`Invalid URL format: unexpected colon count in "${beforeSeparator}"`, url);
917
+ }
918
+ parse(url) {
919
+ return this.parseURL(url).parsed;
920
+ }
921
+ async resolve(url) {
922
+ const { arpUrl } = this.parseURL(url);
923
+ return resolve2(arpUrl);
924
+ }
925
+ async deposit(url, data) {
926
+ const { arpUrl } = this.parseURL(url);
927
+ return deposit(arpUrl, data);
928
+ }
929
+ async exists(url) {
930
+ const { arpUrl } = this.parseURL(url);
931
+ return resourceExists(arpUrl);
932
+ }
933
+ async delete(url) {
934
+ const { arpUrl } = this.parseURL(url);
935
+ return resourceDelete(arpUrl);
936
+ }
937
+ }
938
+ function createResourceX(config) {
939
+ return new ResourceX(config);
940
+ }
941
+
942
+ // src/StateStore.ts
943
+ class MemoryStateStore {
944
+ logs = new Map;
945
+ blobs = new Map;
946
+ async saveLog(key, data) {
947
+ this.logs.set(key, data);
948
+ }
949
+ async loadLog(key) {
950
+ return this.logs.get(key) ?? null;
951
+ }
952
+ async deleteLog(key) {
953
+ this.logs.delete(key);
954
+ }
955
+ async listLogs() {
956
+ return [...this.logs.keys()];
957
+ }
958
+ async saveBlob(ref, data) {
959
+ this.blobs.set(ref, data);
960
+ }
961
+ async loadBlob(ref) {
962
+ return this.blobs.get(ref) ?? null;
963
+ }
964
+ async deleteBlob(ref) {
965
+ this.blobs.delete(ref);
966
+ }
967
+ }
968
+
969
+ class ResourceXStateStore {
970
+ rx;
971
+ constructor() {
972
+ this.rx = createResourceX({
973
+ transports: [deepracticeHandler()]
974
+ });
975
+ }
976
+ logUrl(key) {
977
+ return `@text:deepractice://sandbox/logs/${key}.json`;
978
+ }
979
+ blobUrl(ref) {
980
+ return `@binary:deepractice://sandbox/blobs/${ref}`;
981
+ }
982
+ async saveLog(key, data) {
983
+ await this.rx.deposit(this.logUrl(key), data);
984
+ }
985
+ async loadLog(key) {
986
+ try {
987
+ const exists = await this.rx.exists(this.logUrl(key));
988
+ if (!exists)
989
+ return null;
990
+ const resource = await this.rx.resolve(this.logUrl(key));
991
+ return resource.content;
992
+ } catch {
993
+ return null;
994
+ }
995
+ }
996
+ async deleteLog(key) {
997
+ try {
998
+ const exists = await this.rx.exists(this.logUrl(key));
999
+ if (exists) {
1000
+ await this.rx.delete(this.logUrl(key));
1001
+ }
1002
+ } catch {}
1003
+ }
1004
+ async listLogs() {
1005
+ return [];
1006
+ }
1007
+ async saveBlob(ref, data) {
1008
+ await this.rx.deposit(this.blobUrl(ref), data);
1009
+ }
1010
+ async loadBlob(ref) {
1011
+ try {
1012
+ const exists = await this.rx.exists(this.blobUrl(ref));
1013
+ if (!exists)
1014
+ return null;
1015
+ const resource = await this.rx.resolve(this.blobUrl(ref));
1016
+ return resource.content;
1017
+ } catch {
1018
+ return null;
1019
+ }
1020
+ }
1021
+ async deleteBlob(ref) {
1022
+ try {
1023
+ const exists = await this.rx.exists(this.blobUrl(ref));
1024
+ if (exists) {
1025
+ await this.rx.delete(this.blobUrl(ref));
1026
+ }
1027
+ } catch {}
1028
+ }
1029
+ }
1030
+ function createStateStore(options) {
1031
+ if (options.type === "memory") {
1032
+ return new MemoryStateStore;
1033
+ }
1034
+ if (options.type === "resourcex") {
1035
+ return new ResourceXStateStore;
1036
+ }
1037
+ throw new Error(`StateStore type "${options.type}" not implemented`);
1038
+ }
1039
+ // src/StateAssets.ts
1040
+ import { createHash } from "crypto";
1041
+ function generateRef(data) {
1042
+ const hash = createHash("sha256").update(data).digest("hex");
1043
+ return `sha256-${hash}`;
1044
+ }
1045
+
1046
+ class StateAssetsImpl {
1047
+ sandbox;
1048
+ store;
1049
+ uploadedPaths = new Set;
1050
+ constructor(options) {
1051
+ this.sandbox = options.sandbox;
1052
+ this.store = options.store;
1053
+ }
1054
+ async uploadBuffer(data, remotePath) {
1055
+ const ref = generateRef(data);
1056
+ await this.sandbox.upload(data, remotePath);
1057
+ await this.store.saveBlob(ref, data);
1058
+ this.uploadedPaths.add(remotePath);
1059
+ return ref;
1060
+ }
1061
+ async downloadBuffer(remotePath, options) {
1062
+ const data = await this.sandbox.download(remotePath);
1063
+ if (options?.persist) {
1064
+ const ref = generateRef(data);
1065
+ await this.store.saveBlob(ref, data);
1066
+ return ref;
1067
+ }
1068
+ return data;
1069
+ }
1070
+ list() {
1071
+ return [...this.uploadedPaths];
1072
+ }
1073
+ }
1074
+ function createStateAssets(options) {
1075
+ return new StateAssetsImpl(options);
1076
+ }
1077
+ export {
1078
+ replayStateLog,
1079
+ loadStateLog,
1080
+ createStateStore,
1081
+ createStateAssets,
1082
+ createState,
1083
+ buildStateLog,
1084
+ StateStorage,
1085
+ StateFS,
1086
+ StateError,
1087
+ StateEnv,
1088
+ FileSystemError
1089
+ };
1090
+
1091
+ //# debugId=0AA8C1D6A9548D7A64756E2164756E21