@openfairygui/backend 0.2.0-alpha.1 → 0.2.0-alpha.10
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/LICENSE +21 -0
- package/README.md +40 -2
- package/dist/index.cjs +146 -1
- package/dist/index.d.cts +33 -2
- package/dist/index.d.mts +33 -2
- package/dist/index.mjs +146 -2
- package/dist/node.cjs +1 -1
- package/dist/node.d.cts +1 -1
- package/dist/node.d.mts +1 -1
- package/dist/node.mjs +1 -1
- package/dist/{runtime-DLWLghmH.mjs → runtime-DFatY9W0.mjs} +244 -19
- package/dist/{runtime-BXbt135W.cjs → runtime-GKzsXJdO.cjs} +251 -26
- package/dist/{runtime-C5JY_kRI.d.cts → runtime-GyNVxAQ0.d.mts} +81 -11
- package/dist/{runtime-C5ZsWMnH.d.mts → runtime-Jec6FcF5.d.cts} +81 -11
- package/package.json +65 -65
- package/src/contracts.ts +8 -0
- package/src/index.ts +12 -0
- package/src/runtime.ts +156 -29
- package/src/services/authoring-service.ts +462 -46
- package/src/services/context.ts +2 -1
- package/src/services/runtime-service.ts +62 -25
- package/src/storage.ts +192 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 OpenFairyGUI Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
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,9 @@ 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:
|
|
35
|
-
|
|
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. The default Node filesystem/runtime lives under `@openfairygui/backend/node`.
|
|
36
38
|
|
|
37
39
|
## Relationship to other packages
|
|
38
40
|
|
|
@@ -58,6 +60,42 @@ const applied = await runtime.applyTransaction({
|
|
|
58
60
|
});
|
|
59
61
|
```
|
|
60
62
|
|
|
63
|
+
Browser-safe project session with injected storage:
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
import { BackendRuntime, createBackendStorageFileSystem } from '@openfairygui/backend';
|
|
67
|
+
|
|
68
|
+
const fileSystem = createBackendStorageFileSystem({
|
|
69
|
+
async readFile(filePath) { return storage.readText(filePath); },
|
|
70
|
+
async readFileRaw(filePath) { return storage.readBytes(filePath); },
|
|
71
|
+
async writeFile(filePath, content) { await storage.writeText(filePath, content); },
|
|
72
|
+
async writeFileRaw(filePath, data) { await storage.writeBytes(filePath, data); },
|
|
73
|
+
async mkdir(dirPath) { await storage.mkdir(dirPath); },
|
|
74
|
+
async readdir(dirPath) { return storage.readdir(dirPath); },
|
|
75
|
+
async exists(filePath) { return storage.exists(filePath); },
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const runtime = new BackendRuntime();
|
|
79
|
+
const opened = runtime.openProjectSession({
|
|
80
|
+
project: uamProject,
|
|
81
|
+
storage: {
|
|
82
|
+
fileSystem,
|
|
83
|
+
fairyPath: 'Project.fairy',
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
if (!opened.ok) throw new Error(opened.error.message);
|
|
87
|
+
|
|
88
|
+
const bootstrapped = await runtime.materializeSession({
|
|
89
|
+
sessionId: opened.data.sessionId,
|
|
90
|
+
expectedRevision: opened.data.revision,
|
|
91
|
+
mode: 'fullProject',
|
|
92
|
+
reason: 'workspace_bootstrap',
|
|
93
|
+
});
|
|
94
|
+
if (!bootstrapped.ok) throw new Error(bootstrapped.error.message);
|
|
95
|
+
|
|
96
|
+
console.log(bootstrapped.data.writtenPaths);
|
|
97
|
+
```
|
|
98
|
+
|
|
61
99
|
Node file-backed session:
|
|
62
100
|
|
|
63
101
|
```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-
|
|
2
|
+
const require_runtime = require("./runtime-GKzsXJdO.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
|
+
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
|
+
if (storage.unlink) return storage.unlink(resolved);
|
|
132
|
+
throw createPathError("ENOTSUP", "Storage adapter does not provide unlink().");
|
|
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
|
|
2
|
-
|
|
1
|
+
import { $ as BACKEND_CONTRACT_VERSION, A as BackendSessionSnapshot, B as ListJobsInput, C as BackendJobProgress, D as BackendResult, E as BackendProjectSessionStorage, 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 BackendRuntime, P as EventCursorInvalidError, Q as BACKEND_COMPATIBILITY_POLICY, R as GetJobInput, S as BackendJobNotFoundError, T as BackendJobStatus, U as MaterializeValidationFailedError, V as MaterializeSessionInput, W as MaterializeWriteFailedError, X as SessionStaleWriteError, Y as SessionNotFoundError, Z as BACKEND_CAPABILITY_SCHEMA_VERSION, _ as BackendJobErrors, a as BackendCacheSnapshot, b as BackendJobListStatusFilter, c as BackendCapabilityUnavailableError, d as BackendEventKind, et as BackendDiagnostic, f as BackendFailure, g as BackendHostAdapter, h as BackendFileSystem, i as BackendCacheEntry, j as BackendSuccess, k as BackendRuntimeOptions, l as BackendError, m as BackendFileStat, n as ApplySessionTransactionInput, nt as BackendResponseMeta, o as BackendCapabilities, p as BackendFileHandle, q as SavePartialFailureError, r as BackendArtifactBridgeCapability, rt as BackendStage, s as BackendCapabilityManifest, t as AdvisoryLockConflictError, tt as BackendMessage, u as BackendEvent, v as BackendJobKind, w as BackendJobSnapshot, x as BackendJobNotCancellableError, y as BackendJobListSnapshot, z as InProcessLockConflictError } from "./runtime-Jec6FcF5.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, createBackendStorageFileSystem };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,2 +1,33 @@
|
|
|
1
|
-
import { A as
|
|
2
|
-
|
|
1
|
+
import { $ as BACKEND_CONTRACT_VERSION, A as BackendSessionSnapshot, B as ListJobsInput, C as BackendJobProgress, D as BackendResult, E as BackendProjectSessionStorage, 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 BackendRuntime, P as EventCursorInvalidError, Q as BACKEND_COMPATIBILITY_POLICY, R as GetJobInput, S as BackendJobNotFoundError, T as BackendJobStatus, U as MaterializeValidationFailedError, V as MaterializeSessionInput, W as MaterializeWriteFailedError, X as SessionStaleWriteError, Y as SessionNotFoundError, Z as BACKEND_CAPABILITY_SCHEMA_VERSION, _ as BackendJobErrors, a as BackendCacheSnapshot, b as BackendJobListStatusFilter, c as BackendCapabilityUnavailableError, d as BackendEventKind, et as BackendDiagnostic, f as BackendFailure, g as BackendHostAdapter, h as BackendFileSystem, i as BackendCacheEntry, j as BackendSuccess, k as BackendRuntimeOptions, l as BackendError, m as BackendFileStat, n as ApplySessionTransactionInput, nt as BackendResponseMeta, o as BackendCapabilities, p as BackendFileHandle, q as SavePartialFailureError, r as BackendArtifactBridgeCapability, rt as BackendStage, s as BackendCapabilityManifest, t as AdvisoryLockConflictError, tt as BackendMessage, u as BackendEvent, v as BackendJobKind, w as BackendJobSnapshot, x as BackendJobNotCancellableError, y as BackendJobListSnapshot, z as InProcessLockConflictError } from "./runtime-GyNVxAQ0.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, 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-
|
|
2
|
-
|
|
1
|
+
import { i as BACKEND_CONTRACT_VERSION, n as BACKEND_CAPABILITY_SCHEMA_VERSION, r as BACKEND_COMPATIBILITY_POLICY, t as BackendRuntime } from "./runtime-DFatY9W0.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
|
+
const lockedPaths = /* @__PURE__ */ new Set();
|
|
74
|
+
const fileSystem = {
|
|
75
|
+
stat(filePath) {
|
|
76
|
+
return inferStat(storage, fileSystem.resolve(filePath));
|
|
77
|
+
},
|
|
78
|
+
readdir(dirPath) {
|
|
79
|
+
return storage.readdir(fileSystem.resolve(dirPath));
|
|
80
|
+
},
|
|
81
|
+
readFile(filePath) {
|
|
82
|
+
return storage.readFile(fileSystem.resolve(filePath));
|
|
83
|
+
},
|
|
84
|
+
readFileRaw(filePath) {
|
|
85
|
+
return storage.readFileRaw(fileSystem.resolve(filePath));
|
|
86
|
+
},
|
|
87
|
+
writeFile(filePath, content) {
|
|
88
|
+
return storage.writeFile(fileSystem.resolve(filePath), content);
|
|
89
|
+
},
|
|
90
|
+
writeFileRaw(filePath, data) {
|
|
91
|
+
return storage.writeFileRaw(fileSystem.resolve(filePath), data);
|
|
92
|
+
},
|
|
93
|
+
mkdir(dirPath, options) {
|
|
94
|
+
return storage.mkdir(fileSystem.resolve(dirPath), options);
|
|
95
|
+
},
|
|
96
|
+
async exists(filePath) {
|
|
97
|
+
if (storage.exists) return storage.exists(fileSystem.resolve(filePath));
|
|
98
|
+
try {
|
|
99
|
+
await fileSystem.stat(filePath);
|
|
100
|
+
return true;
|
|
101
|
+
} catch {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
resolvePath(filePath) {
|
|
106
|
+
const resolved = fileSystem.resolve(filePath);
|
|
107
|
+
return storage.resolvePath ? storage.resolvePath(resolved) : Promise.resolve(resolved);
|
|
108
|
+
},
|
|
109
|
+
async openExclusive(filePath) {
|
|
110
|
+
const resolved = fileSystem.resolve(filePath);
|
|
111
|
+
if (storage.openExclusive) return storage.openExclusive(resolved);
|
|
112
|
+
if (lockedPaths.has(resolved) || await fileSystem.exists(resolved)) throw createPathError("EEXIST", `Storage path already exists: ${resolved}`);
|
|
113
|
+
lockedPaths.add(resolved);
|
|
114
|
+
let closed = false;
|
|
115
|
+
return {
|
|
116
|
+
async writeFile(content) {
|
|
117
|
+
if (closed) throw createPathError("EBADF", `Storage lock handle is closed: ${resolved}`);
|
|
118
|
+
await fileSystem.mkdir(fileSystem.dirname(resolved), { recursive: true });
|
|
119
|
+
await storage.writeFile(resolved, content);
|
|
120
|
+
},
|
|
121
|
+
async close() {
|
|
122
|
+
closed = true;
|
|
123
|
+
lockedPaths.delete(resolved);
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
},
|
|
127
|
+
unlink(filePath) {
|
|
128
|
+
const resolved = fileSystem.resolve(filePath);
|
|
129
|
+
lockedPaths.delete(resolved);
|
|
130
|
+
if (storage.unlink) return storage.unlink(resolved);
|
|
131
|
+
throw createPathError("ENOTSUP", "Storage adapter does not provide unlink().");
|
|
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-
|
|
24
|
+
const require_runtime = require("./runtime-GKzsXJdO.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 {
|
|
1
|
+
import { O as BackendRuntime, g as BackendHostAdapter, h as BackendFileSystem, k as BackendRuntimeOptions, m as BackendFileStat, p as BackendFileHandle } from "./runtime-Jec6FcF5.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 {
|
|
1
|
+
import { O as BackendRuntime, g as BackendHostAdapter, h as BackendFileSystem, k as BackendRuntimeOptions, m as BackendFileStat, p as BackendFileHandle } from "./runtime-GyNVxAQ0.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/node.d.ts
|
|
4
4
|
declare function createNodeBackendFileSystem(): BackendFileSystem;
|
package/dist/node.mjs
CHANGED