@openfairygui/backend 0.2.0-alpha.2 → 0.2.0-alpha.20

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 CHANGED
@@ -12,6 +12,7 @@ It owns:
12
12
  - revisioned request handling
13
13
  - coordinated but non-atomic save semantics
14
14
  - browser-safe project sessions
15
+ - browser-safe async project storage adapter
15
16
  - adapter-backed file sessions and backend-local advisory locking
16
17
  - capability discovery
17
18
  - transport-neutral bootstrap
@@ -31,8 +32,16 @@ It also provides:
31
32
 
32
33
  It does **not** redefine transaction grammar or expose `Document`.
33
34
  It also does **not** implement MCP or any transport-specific wire protocol.
34
- The root `@openfairygui/backend` entrypoint is browser-safe: file-backed sessions require an injected
35
- `BackendFileSystem`, while the default Node filesystem/runtime lives under `@openfairygui/backend/node`.
35
+ The root `@openfairygui/backend` entrypoint is browser-safe: pure authoring sessions can run in memory,
36
+ and browser editors can inject an async storage adapter for OPFS, IndexedDB, ZIP-backed virtual filesystems,
37
+ or File System Access API bridges. Storage adapters must implement `unlink()` so resource rename/move/remove
38
+ can clean up stale source files. The default Node filesystem/runtime lives under `@openfairygui/backend/node`.
39
+ File-backed `openSession` hydrates primary resource bytes so browser-safe transactions can rename/move
40
+ assets or add/replace/remove binary resources. `saveSession` writes replacement bytes before it removes
41
+ stale source files, preserving the prior file when a write fails.
42
+ It also compares the source project with a UAM round trip through `ProjectWriter`; sessions with
43
+ unrepresented persisted properties expose `uamFidelity: 'unsupported'`, and write attempts fail with
44
+ `uam_fidelity_unsupported`. Transactions, saves, and materialization are serialized per session.
36
45
 
37
46
  ## Relationship to other packages
38
47
 
@@ -58,6 +67,43 @@ const applied = await runtime.applyTransaction({
58
67
  });
59
68
  ```
60
69
 
70
+ Browser-safe project session with injected storage:
71
+
72
+ ```ts
73
+ import { BackendRuntime, createBackendStorageFileSystem } from '@openfairygui/backend';
74
+
75
+ const fileSystem = createBackendStorageFileSystem({
76
+ async readFile(filePath) { return storage.readText(filePath); },
77
+ async readFileRaw(filePath) { return storage.readBytes(filePath); },
78
+ async writeFile(filePath, content) { await storage.writeText(filePath, content); },
79
+ async writeFileRaw(filePath, data) { await storage.writeBytes(filePath, data); },
80
+ async mkdir(dirPath) { await storage.mkdir(dirPath); },
81
+ async readdir(dirPath) { return storage.readdir(dirPath); },
82
+ async exists(filePath) { return storage.exists(filePath); },
83
+ async unlink(filePath) { await storage.remove(filePath); },
84
+ });
85
+
86
+ const runtime = new BackendRuntime();
87
+ const opened = runtime.openProjectSession({
88
+ project: uamProject,
89
+ storage: {
90
+ fileSystem,
91
+ fairyPath: 'Project.fairy',
92
+ },
93
+ });
94
+ if (!opened.ok) throw new Error(opened.error.message);
95
+
96
+ const bootstrapped = await runtime.materializeSession({
97
+ sessionId: opened.data.sessionId,
98
+ expectedRevision: opened.data.revision,
99
+ mode: 'fullProject',
100
+ reason: 'workspace_bootstrap',
101
+ });
102
+ if (!bootstrapped.ok) throw new Error(bootstrapped.error.message);
103
+
104
+ console.log(bootstrapped.data.writtenPaths);
105
+ ```
106
+
61
107
  Node file-backed session:
62
108
 
63
109
  ```ts
package/dist/index.cjs CHANGED
@@ -1,6 +1,151 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_runtime = require("./runtime-BL9Pkfc7.cjs");
2
+ const require_runtime = require("./runtime-D3bShb1X.cjs");
3
+ //#region src/storage.ts
4
+ var StorageFileStat = class {
5
+ constructor(kind) {
6
+ this.kind = kind;
7
+ }
8
+ isFile() {
9
+ return this.kind === "file";
10
+ }
11
+ isDirectory() {
12
+ return this.kind === "directory";
13
+ }
14
+ };
15
+ function createPathError(code, message) {
16
+ const error = new Error(message);
17
+ error.code = code;
18
+ return error;
19
+ }
20
+ function normalizeStoragePath(value) {
21
+ const normalized = value.replace(/\\/g, "/").replace(/\/+/g, "/");
22
+ const absolute = normalized.startsWith("/");
23
+ const rawSegments = normalized.split("/").filter((segment) => segment.length > 0);
24
+ const segments = [];
25
+ for (const segment of rawSegments) {
26
+ if (segment === ".") continue;
27
+ if (segment === "..") {
28
+ if (segments.length > 0) segments.pop();
29
+ continue;
30
+ }
31
+ segments.push(segment);
32
+ }
33
+ const joined = segments.join("/");
34
+ if (absolute) return joined ? `/${joined}` : "/";
35
+ return joined || ".";
36
+ }
37
+ function joinStoragePath(...paths) {
38
+ return normalizeStoragePath(paths.filter((part) => part.length > 0).join("/"));
39
+ }
40
+ function dirnameStoragePath(filePath) {
41
+ const normalized = normalizeStoragePath(filePath);
42
+ if (normalized === "/" || normalized === ".") return ".";
43
+ const absolute = normalized.startsWith("/");
44
+ const parts = normalized.split("/").filter((part) => part.length > 0);
45
+ parts.pop();
46
+ if (parts.length === 0) return absolute ? "/" : ".";
47
+ return `${absolute ? "/" : ""}${parts.join("/")}`;
48
+ }
49
+ function statFromLike(stat) {
50
+ if (typeof stat.isFile === "function" && typeof stat.isDirectory === "function") return stat;
51
+ const kind = stat.kind ?? stat.type;
52
+ if (kind === "file" || kind === "directory") return new StorageFileStat(kind);
53
+ throw createPathError("EINVAL", "Storage stat must provide kind/type or isFile()/isDirectory().");
54
+ }
55
+ async function inferStat(storage, filePath) {
56
+ if (storage.stat) return statFromLike(await storage.stat(filePath));
57
+ try {
58
+ await storage.readdir(filePath);
59
+ return new StorageFileStat("directory");
60
+ } catch {}
61
+ try {
62
+ await storage.readFileRaw(filePath);
63
+ return new StorageFileStat("file");
64
+ } catch {
65
+ try {
66
+ await storage.readFile(filePath);
67
+ return new StorageFileStat("file");
68
+ } catch {
69
+ throw createPathError("ENOENT", `Storage path not found: ${filePath}`);
70
+ }
71
+ }
72
+ }
73
+ function createBackendStorageFileSystem(storage) {
74
+ if (typeof storage.unlink !== "function") throw createPathError("ENOTSUP", "Storage adapter must provide unlink() for project resource lifecycle writes.");
75
+ const lockedPaths = /* @__PURE__ */ new Set();
76
+ const fileSystem = {
77
+ stat(filePath) {
78
+ return inferStat(storage, fileSystem.resolve(filePath));
79
+ },
80
+ readdir(dirPath) {
81
+ return storage.readdir(fileSystem.resolve(dirPath));
82
+ },
83
+ readFile(filePath) {
84
+ return storage.readFile(fileSystem.resolve(filePath));
85
+ },
86
+ readFileRaw(filePath) {
87
+ return storage.readFileRaw(fileSystem.resolve(filePath));
88
+ },
89
+ writeFile(filePath, content) {
90
+ return storage.writeFile(fileSystem.resolve(filePath), content);
91
+ },
92
+ writeFileRaw(filePath, data) {
93
+ return storage.writeFileRaw(fileSystem.resolve(filePath), data);
94
+ },
95
+ mkdir(dirPath, options) {
96
+ return storage.mkdir(fileSystem.resolve(dirPath), options);
97
+ },
98
+ async exists(filePath) {
99
+ if (storage.exists) return storage.exists(fileSystem.resolve(filePath));
100
+ try {
101
+ await fileSystem.stat(filePath);
102
+ return true;
103
+ } catch {
104
+ return false;
105
+ }
106
+ },
107
+ resolvePath(filePath) {
108
+ const resolved = fileSystem.resolve(filePath);
109
+ return storage.resolvePath ? storage.resolvePath(resolved) : Promise.resolve(resolved);
110
+ },
111
+ async openExclusive(filePath) {
112
+ const resolved = fileSystem.resolve(filePath);
113
+ if (storage.openExclusive) return storage.openExclusive(resolved);
114
+ if (lockedPaths.has(resolved) || await fileSystem.exists(resolved)) throw createPathError("EEXIST", `Storage path already exists: ${resolved}`);
115
+ lockedPaths.add(resolved);
116
+ let closed = false;
117
+ return {
118
+ async writeFile(content) {
119
+ if (closed) throw createPathError("EBADF", `Storage lock handle is closed: ${resolved}`);
120
+ await fileSystem.mkdir(fileSystem.dirname(resolved), { recursive: true });
121
+ await storage.writeFile(resolved, content);
122
+ },
123
+ async close() {
124
+ closed = true;
125
+ lockedPaths.delete(resolved);
126
+ }
127
+ };
128
+ },
129
+ unlink(filePath) {
130
+ const resolved = fileSystem.resolve(filePath);
131
+ lockedPaths.delete(resolved);
132
+ return storage.unlink(resolved);
133
+ },
134
+ join(...paths) {
135
+ return storage.join ? storage.join(...paths) : joinStoragePath(...paths);
136
+ },
137
+ dirname(filePath) {
138
+ return storage.dirname ? storage.dirname(filePath) : dirnameStoragePath(filePath);
139
+ },
140
+ resolve(...paths) {
141
+ return storage.resolve ? storage.resolve(...paths) : joinStoragePath(...paths);
142
+ }
143
+ };
144
+ return fileSystem;
145
+ }
146
+ //#endregion
3
147
  exports.BACKEND_CAPABILITY_SCHEMA_VERSION = require_runtime.BACKEND_CAPABILITY_SCHEMA_VERSION;
4
148
  exports.BACKEND_COMPATIBILITY_POLICY = require_runtime.BACKEND_COMPATIBILITY_POLICY;
5
149
  exports.BACKEND_CONTRACT_VERSION = require_runtime.BACKEND_CONTRACT_VERSION;
6
150
  exports.BackendRuntime = require_runtime.BackendRuntime;
151
+ exports.createBackendStorageFileSystem = createBackendStorageFileSystem;
package/dist/index.d.cts CHANGED
@@ -1,2 +1,33 @@
1
- import { A as BackendSuccess, B as OpenProjectSessionInput, C as BackendJobProgress, D as BackendRuntime, E as BackendResult, F as GetEventsInput, G as BACKEND_CAPABILITY_SCHEMA_VERSION, H as SavePartialFailureError, I as GetEventsSnapshot, J as BackendDiagnostic, K as BACKEND_COMPATIBILITY_POLICY, L as GetJobInput, M as CancelJobInput, N as EventCursorInvalidError, O as BackendRuntimeOptions, P as GetCacheSnapshotInput, R as InProcessLockConflictError, S as BackendJobNotFoundError, T as BackendJobStatus, U as SessionNotFoundError, V as RefreshCacheInput, W as SessionStaleWriteError, X as BackendResponseMeta, Y as BackendMessage, Z as BackendStage, _ as BackendJobErrors, a as BackendCacheSnapshot, b as BackendJobListStatusFilter, c as BackendCapabilityUnavailableError, d as BackendEventKind, f as BackendFailure, g as BackendHostAdapter, h as BackendFileSystem, i as BackendCacheEntry, j as CacheRefreshFailedError, k as BackendSessionSnapshot, l as BackendError, m as BackendFileStat, n as ApplySessionTransactionInput, o as BackendCapabilities, p as BackendFileHandle, q as BACKEND_CONTRACT_VERSION, r as BackendArtifactBridgeCapability, s as BackendCapabilityManifest, t as AdvisoryLockConflictError, u as BackendEvent, v as BackendJobKind, w as BackendJobSnapshot, x as BackendJobNotCancellableError, y as BackendJobListSnapshot, z as ListJobsInput } from "./runtime-qcSlD_9-.cjs";
2
- export { type AdvisoryLockConflictError, type ApplySessionTransactionInput, BACKEND_CAPABILITY_SCHEMA_VERSION, BACKEND_COMPATIBILITY_POLICY, BACKEND_CONTRACT_VERSION, type BackendArtifactBridgeCapability, type BackendCacheEntry, type BackendCacheSnapshot, type BackendCapabilities, type BackendCapabilityManifest, type BackendCapabilityUnavailableError, type BackendDiagnostic, type BackendError, type BackendEvent, type BackendEventKind, type BackendFailure, type BackendFileHandle, type BackendFileStat, type BackendFileSystem, type BackendHostAdapter, type BackendJobErrors, type BackendJobKind, type BackendJobListSnapshot, type BackendJobListStatusFilter, type BackendJobNotCancellableError, type BackendJobNotFoundError, type BackendJobProgress, type BackendJobSnapshot, type BackendJobStatus, type BackendMessage, type BackendResponseMeta, type BackendResult, BackendRuntime, type BackendRuntimeOptions, type BackendSessionSnapshot, type BackendStage, type BackendSuccess, type CacheRefreshFailedError, type CancelJobInput, type EventCursorInvalidError, type GetCacheSnapshotInput, type GetEventsInput, type GetEventsSnapshot, type GetJobInput, type InProcessLockConflictError, type ListJobsInput, type OpenProjectSessionInput, type RefreshCacheInput, type SavePartialFailureError, type SessionNotFoundError, type SessionStaleWriteError };
1
+ import { $ as BACKEND_COMPATIBILITY_POLICY, A as BackendSessionSnapshot, B as ListJobsInput, C as BackendJobNotFoundError, D as BackendProjectSessionStorage, E as BackendJobStatus, F as GetCacheSnapshotInput, G as OpenProjectSessionInput, H as MaterializeSessionSnapshot, I as GetEventsInput, J as SaveSessionInput, K as RefreshCacheInput, L as GetEventsSnapshot, M as CacheRefreshFailedError, N as CancelJobInput, O as BackendResult, P as EventCursorInvalidError, Q as BACKEND_CAPABILITY_SCHEMA_VERSION, R as GetJobInput, S as BackendJobNotCancellableError, T as BackendJobSnapshot, U as MaterializeValidationFailedError, V as MaterializeSessionInput, W as MaterializeWriteFailedError, X as SessionStaleWriteError, Y as SessionNotFoundError, Z as UamFidelityUnsupportedError, _ as BackendHostAdapter, a as BackendCacheEntry, b as BackendJobListSnapshot, c as BackendCapabilityManifest, d as BackendEvent, et as BACKEND_CONTRACT_VERSION, f as BackendEventKind, g as BackendFileSystem, h as BackendFileStat, i as BackendArtifactBridgeCapability, it as BackendStage, j as BackendSuccess, k as BackendRuntimeOptions, l as BackendCapabilityUnavailableError, m as BackendFileHandle, n as AdvisoryLockConflictError, nt as BackendMessage, o as BackendCacheSnapshot, p as BackendFailure, q as SavePartialFailureError, r as ApplySessionTransactionInput, rt as BackendResponseMeta, s as BackendCapabilities, t as BackendRuntime, tt as BackendDiagnostic, u as BackendError, v as BackendJobErrors, w as BackendJobProgress, x as BackendJobListStatusFilter, y as BackendJobKind, z as InProcessLockConflictError } from "./runtime-BB_3Ws5y.cjs";
2
+ import { FileSystem } from "@openfairygui/core/project-io";
3
+
4
+ //#region src/storage.d.ts
5
+ type StorageStatKind = 'file' | 'directory';
6
+ interface BackendStorageStatLike {
7
+ kind?: StorageStatKind;
8
+ type?: StorageStatKind;
9
+ isFile?(): boolean;
10
+ isDirectory?(): boolean;
11
+ }
12
+ interface BackendAsyncStorageAdapter {
13
+ readFile(filePath: string): Promise<string>;
14
+ readFileRaw(filePath: string): Promise<Uint8Array>;
15
+ writeFile(filePath: string, content: string): Promise<void>;
16
+ writeFileRaw(filePath: string, data: Uint8Array): Promise<void>;
17
+ mkdir(dirPath: string, options?: {
18
+ recursive?: boolean;
19
+ }): Promise<void>;
20
+ readdir(dirPath: string): Promise<string[]>;
21
+ exists?(filePath: string): Promise<boolean>;
22
+ stat?(filePath: string): Promise<BackendStorageStatLike>;
23
+ resolvePath?(filePath: string): Promise<string>;
24
+ openExclusive?(filePath: string): Promise<BackendFileHandle>;
25
+ unlink(filePath: string): Promise<void>;
26
+ join?(...paths: string[]): string;
27
+ dirname?(filePath: string): string;
28
+ resolve?(...paths: string[]): string;
29
+ }
30
+ type BackendStorageFileSystem = BackendFileSystem & FileSystem;
31
+ declare function createBackendStorageFileSystem(storage: BackendAsyncStorageAdapter): BackendStorageFileSystem;
32
+ //#endregion
33
+ export { type AdvisoryLockConflictError, type ApplySessionTransactionInput, BACKEND_CAPABILITY_SCHEMA_VERSION, BACKEND_COMPATIBILITY_POLICY, BACKEND_CONTRACT_VERSION, type BackendArtifactBridgeCapability, type BackendAsyncStorageAdapter, type BackendCacheEntry, type BackendCacheSnapshot, type BackendCapabilities, type BackendCapabilityManifest, type BackendCapabilityUnavailableError, type BackendDiagnostic, type BackendError, type BackendEvent, type BackendEventKind, type BackendFailure, type BackendFileHandle, type BackendFileStat, type BackendFileSystem, type BackendHostAdapter, type BackendJobErrors, type BackendJobKind, type BackendJobListSnapshot, type BackendJobListStatusFilter, type BackendJobNotCancellableError, type BackendJobNotFoundError, type BackendJobProgress, type BackendJobSnapshot, type BackendJobStatus, type BackendMessage, type BackendProjectSessionStorage, type BackendResponseMeta, type BackendResult, BackendRuntime, type BackendRuntimeOptions, type BackendSessionSnapshot, type BackendStage, type BackendStorageFileSystem, type BackendStorageStatLike, type BackendSuccess, type CacheRefreshFailedError, type CancelJobInput, type EventCursorInvalidError, type GetCacheSnapshotInput, type GetEventsInput, type GetEventsSnapshot, type GetJobInput, type InProcessLockConflictError, type ListJobsInput, type MaterializeSessionInput, type MaterializeSessionSnapshot, type MaterializeValidationFailedError, type MaterializeWriteFailedError, type OpenProjectSessionInput, type RefreshCacheInput, type SavePartialFailureError, type SaveSessionInput, type SessionNotFoundError, type SessionStaleWriteError, type UamFidelityUnsupportedError, createBackendStorageFileSystem };
package/dist/index.d.mts CHANGED
@@ -1,2 +1,33 @@
1
- import { A as BackendSuccess, B as OpenProjectSessionInput, C as BackendJobProgress, D as BackendRuntime, E as BackendResult, F as GetEventsInput, G as BACKEND_CAPABILITY_SCHEMA_VERSION, H as SavePartialFailureError, I as GetEventsSnapshot, J as BackendDiagnostic, K as BACKEND_COMPATIBILITY_POLICY, L as GetJobInput, M as CancelJobInput, N as EventCursorInvalidError, O as BackendRuntimeOptions, P as GetCacheSnapshotInput, R as InProcessLockConflictError, S as BackendJobNotFoundError, T as BackendJobStatus, U as SessionNotFoundError, V as RefreshCacheInput, W as SessionStaleWriteError, X as BackendResponseMeta, Y as BackendMessage, Z as BackendStage, _ as BackendJobErrors, a as BackendCacheSnapshot, b as BackendJobListStatusFilter, c as BackendCapabilityUnavailableError, d as BackendEventKind, f as BackendFailure, g as BackendHostAdapter, h as BackendFileSystem, i as BackendCacheEntry, j as CacheRefreshFailedError, k as BackendSessionSnapshot, l as BackendError, m as BackendFileStat, n as ApplySessionTransactionInput, o as BackendCapabilities, p as BackendFileHandle, q as BACKEND_CONTRACT_VERSION, r as BackendArtifactBridgeCapability, s as BackendCapabilityManifest, t as AdvisoryLockConflictError, u as BackendEvent, v as BackendJobKind, w as BackendJobSnapshot, x as BackendJobNotCancellableError, y as BackendJobListSnapshot, z as ListJobsInput } from "./runtime-OO1sHuCl.mjs";
2
- export { type AdvisoryLockConflictError, type ApplySessionTransactionInput, BACKEND_CAPABILITY_SCHEMA_VERSION, BACKEND_COMPATIBILITY_POLICY, BACKEND_CONTRACT_VERSION, type BackendArtifactBridgeCapability, type BackendCacheEntry, type BackendCacheSnapshot, type BackendCapabilities, type BackendCapabilityManifest, type BackendCapabilityUnavailableError, type BackendDiagnostic, type BackendError, type BackendEvent, type BackendEventKind, type BackendFailure, type BackendFileHandle, type BackendFileStat, type BackendFileSystem, type BackendHostAdapter, type BackendJobErrors, type BackendJobKind, type BackendJobListSnapshot, type BackendJobListStatusFilter, type BackendJobNotCancellableError, type BackendJobNotFoundError, type BackendJobProgress, type BackendJobSnapshot, type BackendJobStatus, type BackendMessage, type BackendResponseMeta, type BackendResult, BackendRuntime, type BackendRuntimeOptions, type BackendSessionSnapshot, type BackendStage, type BackendSuccess, type CacheRefreshFailedError, type CancelJobInput, type EventCursorInvalidError, type GetCacheSnapshotInput, type GetEventsInput, type GetEventsSnapshot, type GetJobInput, type InProcessLockConflictError, type ListJobsInput, type OpenProjectSessionInput, type RefreshCacheInput, type SavePartialFailureError, type SessionNotFoundError, type SessionStaleWriteError };
1
+ import { $ as BACKEND_COMPATIBILITY_POLICY, A as BackendSessionSnapshot, B as ListJobsInput, C as BackendJobNotFoundError, D as BackendProjectSessionStorage, E as BackendJobStatus, F as GetCacheSnapshotInput, G as OpenProjectSessionInput, H as MaterializeSessionSnapshot, I as GetEventsInput, J as SaveSessionInput, K as RefreshCacheInput, L as GetEventsSnapshot, M as CacheRefreshFailedError, N as CancelJobInput, O as BackendResult, P as EventCursorInvalidError, Q as BACKEND_CAPABILITY_SCHEMA_VERSION, R as GetJobInput, S as BackendJobNotCancellableError, T as BackendJobSnapshot, U as MaterializeValidationFailedError, V as MaterializeSessionInput, W as MaterializeWriteFailedError, X as SessionStaleWriteError, Y as SessionNotFoundError, Z as UamFidelityUnsupportedError, _ as BackendHostAdapter, a as BackendCacheEntry, b as BackendJobListSnapshot, c as BackendCapabilityManifest, d as BackendEvent, et as BACKEND_CONTRACT_VERSION, f as BackendEventKind, g as BackendFileSystem, h as BackendFileStat, i as BackendArtifactBridgeCapability, it as BackendStage, j as BackendSuccess, k as BackendRuntimeOptions, l as BackendCapabilityUnavailableError, m as BackendFileHandle, n as AdvisoryLockConflictError, nt as BackendMessage, o as BackendCacheSnapshot, p as BackendFailure, q as SavePartialFailureError, r as ApplySessionTransactionInput, rt as BackendResponseMeta, s as BackendCapabilities, t as BackendRuntime, tt as BackendDiagnostic, u as BackendError, v as BackendJobErrors, w as BackendJobProgress, x as BackendJobListStatusFilter, y as BackendJobKind, z as InProcessLockConflictError } from "./runtime-B4SwHTRC.mjs";
2
+ import { FileSystem } from "@openfairygui/core/project-io";
3
+
4
+ //#region src/storage.d.ts
5
+ type StorageStatKind = 'file' | 'directory';
6
+ interface BackendStorageStatLike {
7
+ kind?: StorageStatKind;
8
+ type?: StorageStatKind;
9
+ isFile?(): boolean;
10
+ isDirectory?(): boolean;
11
+ }
12
+ interface BackendAsyncStorageAdapter {
13
+ readFile(filePath: string): Promise<string>;
14
+ readFileRaw(filePath: string): Promise<Uint8Array>;
15
+ writeFile(filePath: string, content: string): Promise<void>;
16
+ writeFileRaw(filePath: string, data: Uint8Array): Promise<void>;
17
+ mkdir(dirPath: string, options?: {
18
+ recursive?: boolean;
19
+ }): Promise<void>;
20
+ readdir(dirPath: string): Promise<string[]>;
21
+ exists?(filePath: string): Promise<boolean>;
22
+ stat?(filePath: string): Promise<BackendStorageStatLike>;
23
+ resolvePath?(filePath: string): Promise<string>;
24
+ openExclusive?(filePath: string): Promise<BackendFileHandle>;
25
+ unlink(filePath: string): Promise<void>;
26
+ join?(...paths: string[]): string;
27
+ dirname?(filePath: string): string;
28
+ resolve?(...paths: string[]): string;
29
+ }
30
+ type BackendStorageFileSystem = BackendFileSystem & FileSystem;
31
+ declare function createBackendStorageFileSystem(storage: BackendAsyncStorageAdapter): BackendStorageFileSystem;
32
+ //#endregion
33
+ export { type AdvisoryLockConflictError, type ApplySessionTransactionInput, BACKEND_CAPABILITY_SCHEMA_VERSION, BACKEND_COMPATIBILITY_POLICY, BACKEND_CONTRACT_VERSION, type BackendArtifactBridgeCapability, type BackendAsyncStorageAdapter, type BackendCacheEntry, type BackendCacheSnapshot, type BackendCapabilities, type BackendCapabilityManifest, type BackendCapabilityUnavailableError, type BackendDiagnostic, type BackendError, type BackendEvent, type BackendEventKind, type BackendFailure, type BackendFileHandle, type BackendFileStat, type BackendFileSystem, type BackendHostAdapter, type BackendJobErrors, type BackendJobKind, type BackendJobListSnapshot, type BackendJobListStatusFilter, type BackendJobNotCancellableError, type BackendJobNotFoundError, type BackendJobProgress, type BackendJobSnapshot, type BackendJobStatus, type BackendMessage, type BackendProjectSessionStorage, type BackendResponseMeta, type BackendResult, BackendRuntime, type BackendRuntimeOptions, type BackendSessionSnapshot, type BackendStage, type BackendStorageFileSystem, type BackendStorageStatLike, type BackendSuccess, type CacheRefreshFailedError, type CancelJobInput, type EventCursorInvalidError, type GetCacheSnapshotInput, type GetEventsInput, type GetEventsSnapshot, type GetJobInput, type InProcessLockConflictError, type ListJobsInput, type MaterializeSessionInput, type MaterializeSessionSnapshot, type MaterializeValidationFailedError, type MaterializeWriteFailedError, type OpenProjectSessionInput, type RefreshCacheInput, type SavePartialFailureError, type SaveSessionInput, type SessionNotFoundError, type SessionStaleWriteError, type UamFidelityUnsupportedError, createBackendStorageFileSystem };
package/dist/index.mjs CHANGED
@@ -1,2 +1,146 @@
1
- import { i as BACKEND_CONTRACT_VERSION, n as BACKEND_CAPABILITY_SCHEMA_VERSION, r as BACKEND_COMPATIBILITY_POLICY, t as BackendRuntime } from "./runtime-BPWzx-Tv.mjs";
2
- export { BACKEND_CAPABILITY_SCHEMA_VERSION, BACKEND_COMPATIBILITY_POLICY, BACKEND_CONTRACT_VERSION, BackendRuntime };
1
+ import { i as BACKEND_CONTRACT_VERSION, n as BACKEND_CAPABILITY_SCHEMA_VERSION, r as BACKEND_COMPATIBILITY_POLICY, t as BackendRuntime } from "./runtime-CqGK_D9A.mjs";
2
+ //#region src/storage.ts
3
+ var StorageFileStat = class {
4
+ constructor(kind) {
5
+ this.kind = kind;
6
+ }
7
+ isFile() {
8
+ return this.kind === "file";
9
+ }
10
+ isDirectory() {
11
+ return this.kind === "directory";
12
+ }
13
+ };
14
+ function createPathError(code, message) {
15
+ const error = new Error(message);
16
+ error.code = code;
17
+ return error;
18
+ }
19
+ function normalizeStoragePath(value) {
20
+ const normalized = value.replace(/\\/g, "/").replace(/\/+/g, "/");
21
+ const absolute = normalized.startsWith("/");
22
+ const rawSegments = normalized.split("/").filter((segment) => segment.length > 0);
23
+ const segments = [];
24
+ for (const segment of rawSegments) {
25
+ if (segment === ".") continue;
26
+ if (segment === "..") {
27
+ if (segments.length > 0) segments.pop();
28
+ continue;
29
+ }
30
+ segments.push(segment);
31
+ }
32
+ const joined = segments.join("/");
33
+ if (absolute) return joined ? `/${joined}` : "/";
34
+ return joined || ".";
35
+ }
36
+ function joinStoragePath(...paths) {
37
+ return normalizeStoragePath(paths.filter((part) => part.length > 0).join("/"));
38
+ }
39
+ function dirnameStoragePath(filePath) {
40
+ const normalized = normalizeStoragePath(filePath);
41
+ if (normalized === "/" || normalized === ".") return ".";
42
+ const absolute = normalized.startsWith("/");
43
+ const parts = normalized.split("/").filter((part) => part.length > 0);
44
+ parts.pop();
45
+ if (parts.length === 0) return absolute ? "/" : ".";
46
+ return `${absolute ? "/" : ""}${parts.join("/")}`;
47
+ }
48
+ function statFromLike(stat) {
49
+ if (typeof stat.isFile === "function" && typeof stat.isDirectory === "function") return stat;
50
+ const kind = stat.kind ?? stat.type;
51
+ if (kind === "file" || kind === "directory") return new StorageFileStat(kind);
52
+ throw createPathError("EINVAL", "Storage stat must provide kind/type or isFile()/isDirectory().");
53
+ }
54
+ async function inferStat(storage, filePath) {
55
+ if (storage.stat) return statFromLike(await storage.stat(filePath));
56
+ try {
57
+ await storage.readdir(filePath);
58
+ return new StorageFileStat("directory");
59
+ } catch {}
60
+ try {
61
+ await storage.readFileRaw(filePath);
62
+ return new StorageFileStat("file");
63
+ } catch {
64
+ try {
65
+ await storage.readFile(filePath);
66
+ return new StorageFileStat("file");
67
+ } catch {
68
+ throw createPathError("ENOENT", `Storage path not found: ${filePath}`);
69
+ }
70
+ }
71
+ }
72
+ function createBackendStorageFileSystem(storage) {
73
+ if (typeof storage.unlink !== "function") throw createPathError("ENOTSUP", "Storage adapter must provide unlink() for project resource lifecycle writes.");
74
+ const lockedPaths = /* @__PURE__ */ new Set();
75
+ const fileSystem = {
76
+ stat(filePath) {
77
+ return inferStat(storage, fileSystem.resolve(filePath));
78
+ },
79
+ readdir(dirPath) {
80
+ return storage.readdir(fileSystem.resolve(dirPath));
81
+ },
82
+ readFile(filePath) {
83
+ return storage.readFile(fileSystem.resolve(filePath));
84
+ },
85
+ readFileRaw(filePath) {
86
+ return storage.readFileRaw(fileSystem.resolve(filePath));
87
+ },
88
+ writeFile(filePath, content) {
89
+ return storage.writeFile(fileSystem.resolve(filePath), content);
90
+ },
91
+ writeFileRaw(filePath, data) {
92
+ return storage.writeFileRaw(fileSystem.resolve(filePath), data);
93
+ },
94
+ mkdir(dirPath, options) {
95
+ return storage.mkdir(fileSystem.resolve(dirPath), options);
96
+ },
97
+ async exists(filePath) {
98
+ if (storage.exists) return storage.exists(fileSystem.resolve(filePath));
99
+ try {
100
+ await fileSystem.stat(filePath);
101
+ return true;
102
+ } catch {
103
+ return false;
104
+ }
105
+ },
106
+ resolvePath(filePath) {
107
+ const resolved = fileSystem.resolve(filePath);
108
+ return storage.resolvePath ? storage.resolvePath(resolved) : Promise.resolve(resolved);
109
+ },
110
+ async openExclusive(filePath) {
111
+ const resolved = fileSystem.resolve(filePath);
112
+ if (storage.openExclusive) return storage.openExclusive(resolved);
113
+ if (lockedPaths.has(resolved) || await fileSystem.exists(resolved)) throw createPathError("EEXIST", `Storage path already exists: ${resolved}`);
114
+ lockedPaths.add(resolved);
115
+ let closed = false;
116
+ return {
117
+ async writeFile(content) {
118
+ if (closed) throw createPathError("EBADF", `Storage lock handle is closed: ${resolved}`);
119
+ await fileSystem.mkdir(fileSystem.dirname(resolved), { recursive: true });
120
+ await storage.writeFile(resolved, content);
121
+ },
122
+ async close() {
123
+ closed = true;
124
+ lockedPaths.delete(resolved);
125
+ }
126
+ };
127
+ },
128
+ unlink(filePath) {
129
+ const resolved = fileSystem.resolve(filePath);
130
+ lockedPaths.delete(resolved);
131
+ return storage.unlink(resolved);
132
+ },
133
+ join(...paths) {
134
+ return storage.join ? storage.join(...paths) : joinStoragePath(...paths);
135
+ },
136
+ dirname(filePath) {
137
+ return storage.dirname ? storage.dirname(filePath) : dirnameStoragePath(filePath);
138
+ },
139
+ resolve(...paths) {
140
+ return storage.resolve ? storage.resolve(...paths) : joinStoragePath(...paths);
141
+ }
142
+ };
143
+ return fileSystem;
144
+ }
145
+ //#endregion
146
+ export { BACKEND_CAPABILITY_SCHEMA_VERSION, BACKEND_COMPATIBILITY_POLICY, BACKEND_CONTRACT_VERSION, BackendRuntime, createBackendStorageFileSystem };
package/dist/node.cjs CHANGED
@@ -21,7 +21,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
21
21
  enumerable: true
22
22
  }) : target, mod));
23
23
  //#endregion
24
- const require_runtime = require("./runtime-BL9Pkfc7.cjs");
24
+ const require_runtime = require("./runtime-D3bShb1X.cjs");
25
25
  let node_fs_promises = require("node:fs/promises");
26
26
  node_fs_promises = __toESM(node_fs_promises);
27
27
  let node_path = require("node:path");
package/dist/node.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { D as BackendRuntime, O as BackendRuntimeOptions, g as BackendHostAdapter, h as BackendFileSystem, m as BackendFileStat, p as BackendFileHandle } from "./runtime-qcSlD_9-.cjs";
1
+ import { _ as BackendHostAdapter, g as BackendFileSystem, h as BackendFileStat, k as BackendRuntimeOptions, m as BackendFileHandle, t as BackendRuntime } from "./runtime-BB_3Ws5y.cjs";
2
2
 
3
3
  //#region src/node.d.ts
4
4
  declare function createNodeBackendFileSystem(): BackendFileSystem;
package/dist/node.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { D as BackendRuntime, O as BackendRuntimeOptions, g as BackendHostAdapter, h as BackendFileSystem, m as BackendFileStat, p as BackendFileHandle } from "./runtime-OO1sHuCl.mjs";
1
+ import { _ as BackendHostAdapter, g as BackendFileSystem, h as BackendFileStat, k as BackendRuntimeOptions, m as BackendFileHandle, t as BackendRuntime } from "./runtime-B4SwHTRC.mjs";
2
2
 
3
3
  //#region src/node.d.ts
4
4
  declare function createNodeBackendFileSystem(): BackendFileSystem;
package/dist/node.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { t as BackendRuntime } from "./runtime-BPWzx-Tv.mjs";
1
+ import { t as BackendRuntime } from "./runtime-CqGK_D9A.mjs";
2
2
  import fs from "node:fs/promises";
3
3
  import path from "node:path";
4
4
  //#region src/node.ts