@openfairygui/backend 0.2.0-alpha.29 → 0.2.0-alpha.30
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.cjs +5 -1
- package/dist/index.d.cts +2 -1
- package/dist/index.d.mts +2 -1
- package/dist/index.mjs +5 -1
- package/dist/node.cjs +4 -1
- package/dist/node.d.cts +1 -1
- package/dist/node.d.mts +1 -1
- package/dist/node.mjs +4 -1
- package/dist/{runtime-B4SwHTRC.d.mts → runtime-BQOEw22M.d.mts} +1 -0
- package/dist/{runtime-BxbnisWJ.cjs → runtime-CcBLI-J7.cjs} +54 -8
- package/dist/{runtime-griAoBRA.mjs → runtime-NiBgr7PF.mjs} +54 -8
- package/dist/{runtime-BB_3Ws5y.d.cts → runtime-Wu6vGZ-j.d.cts} +1 -0
- package/package.json +3 -3
- package/src/node.ts +3 -0
- package/src/runtime/contracts.ts +1 -0
- package/src/services/authoring-service.ts +42 -3
- package/src/services/context.ts +2 -0
- package/src/services/runtime-service.ts +19 -5
- package/src/services/session-project-writer.ts +3 -0
- package/src/storage.ts +7 -0
package/dist/index.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_runtime = require("./runtime-
|
|
2
|
+
const require_runtime = require("./runtime-CcBLI-J7.cjs");
|
|
3
3
|
//#region src/storage.ts
|
|
4
4
|
var StorageFileStat = class {
|
|
5
5
|
constructor(kind) {
|
|
@@ -72,6 +72,7 @@ async function inferStat(storage, filePath) {
|
|
|
72
72
|
}
|
|
73
73
|
function createBackendStorageFileSystem(storage) {
|
|
74
74
|
if (typeof storage.unlink !== "function") throw createPathError("ENOTSUP", "Storage adapter must provide unlink() for project resource lifecycle writes.");
|
|
75
|
+
if (typeof storage.rmdir !== "function") throw createPathError("ENOTSUP", "Storage adapter must provide rmdir() for project resource folder lifecycle writes.");
|
|
75
76
|
const lockedPaths = /* @__PURE__ */ new Set();
|
|
76
77
|
const fileSystem = {
|
|
77
78
|
stat(filePath) {
|
|
@@ -131,6 +132,9 @@ function createBackendStorageFileSystem(storage) {
|
|
|
131
132
|
lockedPaths.delete(resolved);
|
|
132
133
|
return storage.unlink(resolved);
|
|
133
134
|
},
|
|
135
|
+
rmdir(dirPath) {
|
|
136
|
+
return storage.rmdir(fileSystem.resolve(dirPath));
|
|
137
|
+
},
|
|
134
138
|
join(...paths) {
|
|
135
139
|
return storage.join ? storage.join(...paths) : joinStoragePath(...paths);
|
|
136
140
|
},
|
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
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-
|
|
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-Wu6vGZ-j.cjs";
|
|
2
2
|
import { FileSystem } from "@openfairygui/core/project-io";
|
|
3
3
|
|
|
4
4
|
//#region src/storage.d.ts
|
|
@@ -23,6 +23,7 @@ interface BackendAsyncStorageAdapter {
|
|
|
23
23
|
resolvePath?(filePath: string): Promise<string>;
|
|
24
24
|
openExclusive?(filePath: string): Promise<BackendFileHandle>;
|
|
25
25
|
unlink(filePath: string): Promise<void>;
|
|
26
|
+
rmdir(dirPath: string): Promise<void>;
|
|
26
27
|
join?(...paths: string[]): string;
|
|
27
28
|
dirname?(filePath: string): string;
|
|
28
29
|
resolve?(...paths: string[]): string;
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
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-
|
|
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-BQOEw22M.mjs";
|
|
2
2
|
import { FileSystem } from "@openfairygui/core/project-io";
|
|
3
3
|
|
|
4
4
|
//#region src/storage.d.ts
|
|
@@ -23,6 +23,7 @@ interface BackendAsyncStorageAdapter {
|
|
|
23
23
|
resolvePath?(filePath: string): Promise<string>;
|
|
24
24
|
openExclusive?(filePath: string): Promise<BackendFileHandle>;
|
|
25
25
|
unlink(filePath: string): Promise<void>;
|
|
26
|
+
rmdir(dirPath: string): Promise<void>;
|
|
26
27
|
join?(...paths: string[]): string;
|
|
27
28
|
dirname?(filePath: string): string;
|
|
28
29
|
resolve?(...paths: string[]): string;
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as BACKEND_CONTRACT_VERSION, n as BACKEND_CAPABILITY_SCHEMA_VERSION, r as BACKEND_COMPATIBILITY_POLICY, t as BackendRuntime } from "./runtime-
|
|
1
|
+
import { i as BACKEND_CONTRACT_VERSION, n as BACKEND_CAPABILITY_SCHEMA_VERSION, r as BACKEND_COMPATIBILITY_POLICY, t as BackendRuntime } from "./runtime-NiBgr7PF.mjs";
|
|
2
2
|
//#region src/storage.ts
|
|
3
3
|
var StorageFileStat = class {
|
|
4
4
|
constructor(kind) {
|
|
@@ -71,6 +71,7 @@ async function inferStat(storage, filePath) {
|
|
|
71
71
|
}
|
|
72
72
|
function createBackendStorageFileSystem(storage) {
|
|
73
73
|
if (typeof storage.unlink !== "function") throw createPathError("ENOTSUP", "Storage adapter must provide unlink() for project resource lifecycle writes.");
|
|
74
|
+
if (typeof storage.rmdir !== "function") throw createPathError("ENOTSUP", "Storage adapter must provide rmdir() for project resource folder lifecycle writes.");
|
|
74
75
|
const lockedPaths = /* @__PURE__ */ new Set();
|
|
75
76
|
const fileSystem = {
|
|
76
77
|
stat(filePath) {
|
|
@@ -130,6 +131,9 @@ function createBackendStorageFileSystem(storage) {
|
|
|
130
131
|
lockedPaths.delete(resolved);
|
|
131
132
|
return storage.unlink(resolved);
|
|
132
133
|
},
|
|
134
|
+
rmdir(dirPath) {
|
|
135
|
+
return storage.rmdir(fileSystem.resolve(dirPath));
|
|
136
|
+
},
|
|
133
137
|
join(...paths) {
|
|
134
138
|
return storage.join ? storage.join(...paths) : joinStoragePath(...paths);
|
|
135
139
|
},
|
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-CcBLI-J7.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");
|
|
@@ -72,6 +72,9 @@ function createNodeBackendFileSystem() {
|
|
|
72
72
|
unlink(filePath) {
|
|
73
73
|
return node_fs_promises.default.unlink(filePath);
|
|
74
74
|
},
|
|
75
|
+
rmdir(dirPath) {
|
|
76
|
+
return node_fs_promises.default.rmdir(dirPath);
|
|
77
|
+
},
|
|
75
78
|
join(...paths) {
|
|
76
79
|
return node_path.default.join(...paths);
|
|
77
80
|
},
|
package/dist/node.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { _ as BackendHostAdapter, g as BackendFileSystem, h as BackendFileStat, k as BackendRuntimeOptions, m as BackendFileHandle, t as BackendRuntime } from "./runtime-
|
|
1
|
+
import { _ as BackendHostAdapter, g as BackendFileSystem, h as BackendFileStat, k as BackendRuntimeOptions, m as BackendFileHandle, t as BackendRuntime } from "./runtime-Wu6vGZ-j.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 { _ as BackendHostAdapter, g as BackendFileSystem, h as BackendFileStat, k as BackendRuntimeOptions, m as BackendFileHandle, t as BackendRuntime } from "./runtime-
|
|
1
|
+
import { _ as BackendHostAdapter, g as BackendFileSystem, h as BackendFileStat, k as BackendRuntimeOptions, m as BackendFileHandle, t as BackendRuntime } from "./runtime-BQOEw22M.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-
|
|
1
|
+
import { t as BackendRuntime } from "./runtime-NiBgr7PF.mjs";
|
|
2
2
|
import fs from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
//#region src/node.ts
|
|
@@ -47,6 +47,9 @@ function createNodeBackendFileSystem() {
|
|
|
47
47
|
unlink(filePath) {
|
|
48
48
|
return fs.unlink(filePath);
|
|
49
49
|
},
|
|
50
|
+
rmdir(dirPath) {
|
|
51
|
+
return fs.rmdir(dirPath);
|
|
52
|
+
},
|
|
50
53
|
join(...paths) {
|
|
51
54
|
return path.join(...paths);
|
|
52
55
|
},
|
|
@@ -70,6 +70,7 @@ interface BackendFileSystem {
|
|
|
70
70
|
resolvePath(filePath: string): Promise<string>;
|
|
71
71
|
openExclusive(filePath: string): Promise<BackendFileHandle>;
|
|
72
72
|
unlink(filePath: string): Promise<void>;
|
|
73
|
+
rmdir(dirPath: string): Promise<void>;
|
|
73
74
|
join(...paths: string[]): string;
|
|
74
75
|
dirname(filePath: string): string;
|
|
75
76
|
resolve(...paths: string[]): string;
|
|
@@ -155,11 +155,15 @@ function createWriterFileSystem(fileSystem, writtenPaths, failedPaths) {
|
|
|
155
155
|
},
|
|
156
156
|
join: (...paths) => fileSystem.join(...paths),
|
|
157
157
|
dirname: (path) => fileSystem.dirname(path),
|
|
158
|
-
unlink: (path) => trackWrite(path, () => fileSystem.unlink(path))
|
|
158
|
+
unlink: (path) => trackWrite(path, () => fileSystem.unlink(path)),
|
|
159
|
+
rmdir: (path) => trackWrite(path, () => fileSystem.rmdir(path))
|
|
159
160
|
};
|
|
160
161
|
}
|
|
161
162
|
async function writeSessionProject(input) {
|
|
162
|
-
await new _openfairygui_core_project_io.ProjectWriter(createWriterFileSystem(input.fileSystem, input.writtenPaths, input.failedPaths)).write(input.document, input.fairyPath, {
|
|
163
|
+
await new _openfairygui_core_project_io.ProjectWriter(createWriterFileSystem(input.fileSystem, input.writtenPaths, input.failedPaths)).write(input.document, input.fairyPath, {
|
|
164
|
+
staleSourceFiles: input.staleSourceFiles,
|
|
165
|
+
staleResourceFolders: input.staleResourceFolders
|
|
166
|
+
});
|
|
163
167
|
}
|
|
164
168
|
//#endregion
|
|
165
169
|
//#region src/services/snapshot-utils.ts
|
|
@@ -238,6 +242,7 @@ function projectSourceFiles(project) {
|
|
|
238
242
|
fileName: "package.xml"
|
|
239
243
|
});
|
|
240
244
|
const branches = /* @__PURE__ */ new Set();
|
|
245
|
+
for (const folder of pkg.folders) if (folder.branch) branches.add(folder.branch);
|
|
241
246
|
for (const resource of pkg.resources) {
|
|
242
247
|
if (resource.branch) branches.add(resource.branch);
|
|
243
248
|
const fileName = resource.kind === "component" ? `${resource.name}.xml` : resource.fileName ?? (resource.kind === "image" ? "" : resource.file) ?? "";
|
|
@@ -266,6 +271,22 @@ function sourceFileKey(source) {
|
|
|
266
271
|
source.fileName
|
|
267
272
|
].join("\0");
|
|
268
273
|
}
|
|
274
|
+
function projectResourceFolders(project) {
|
|
275
|
+
const result = /* @__PURE__ */ new Map();
|
|
276
|
+
for (const pkg of project.packages) for (const folder of pkg.folders) result.set(`${pkg.id}/${folder.branch}/${folder.path}`, {
|
|
277
|
+
packageName: pkg.name,
|
|
278
|
+
branch: folder.branch,
|
|
279
|
+
path: folder.path
|
|
280
|
+
});
|
|
281
|
+
return result;
|
|
282
|
+
}
|
|
283
|
+
function resourceFolderKey(folder) {
|
|
284
|
+
return [
|
|
285
|
+
folder.branch,
|
|
286
|
+
folder.packageName,
|
|
287
|
+
folder.path
|
|
288
|
+
].join("\0");
|
|
289
|
+
}
|
|
269
290
|
function recordStaleProjectFiles(session, previousProject, nextProject) {
|
|
270
291
|
if (!session.fileSystem) return;
|
|
271
292
|
const previousSources = projectSourceFiles(previousProject);
|
|
@@ -275,6 +296,13 @@ function recordStaleProjectFiles(session, previousProject, nextProject) {
|
|
|
275
296
|
if (!nextSourceKeys.has(key)) session.pendingStaleSourceFiles.set(key, source);
|
|
276
297
|
}
|
|
277
298
|
for (const key of nextSourceKeys) session.pendingStaleSourceFiles.delete(key);
|
|
299
|
+
const previousFolders = projectResourceFolders(previousProject);
|
|
300
|
+
const nextFolderKeys = new Set([...projectResourceFolders(nextProject).values()].map(resourceFolderKey));
|
|
301
|
+
for (const folder of previousFolders.values()) {
|
|
302
|
+
const key = resourceFolderKey(folder);
|
|
303
|
+
if (!nextFolderKeys.has(key)) session.pendingStaleResourceFolders.set(key, folder);
|
|
304
|
+
}
|
|
305
|
+
for (const key of nextFolderKeys) session.pendingStaleResourceFolders.delete(key);
|
|
278
306
|
}
|
|
279
307
|
function toBackendDiagnostics(error) {
|
|
280
308
|
return error.diagnostics.length > 0 ? error.diagnostics.map((diagnostic) => ({ ...diagnostic })) : [{
|
|
@@ -467,11 +495,13 @@ var AuthoringService = class {
|
|
|
467
495
|
document: (0, _openfairygui_core_uam.materializeUamProject)(session.project),
|
|
468
496
|
fairyPath: session.fairyPath,
|
|
469
497
|
staleSourceFiles: [...session.pendingStaleSourceFiles.values()],
|
|
498
|
+
staleResourceFolders: [...session.pendingStaleResourceFolders.values()],
|
|
470
499
|
writtenPaths: committedPaths,
|
|
471
500
|
failedPaths
|
|
472
501
|
});
|
|
473
502
|
session.fileSystem ??= fileSystem;
|
|
474
503
|
session.pendingStaleSourceFiles.clear();
|
|
504
|
+
session.pendingStaleResourceFolders.clear();
|
|
475
505
|
(0, _openfairygui_core_uam.commitUamProjectSourcePaths)(session.project);
|
|
476
506
|
session.lastSavedRevision = session.revision;
|
|
477
507
|
session.dirty = false;
|
|
@@ -609,11 +639,18 @@ var AuthoringService = class {
|
|
|
609
639
|
document,
|
|
610
640
|
fairyPath,
|
|
611
641
|
staleSourceFiles: isSessionStorageTarget ? [...session.pendingStaleSourceFiles.values()] : [],
|
|
642
|
+
staleResourceFolders: isSessionStorageTarget ? [...session.pendingStaleResourceFolders.values()] : [],
|
|
612
643
|
writtenPaths,
|
|
613
644
|
failedPaths
|
|
614
645
|
});
|
|
615
|
-
if (isSessionStorageTarget)
|
|
616
|
-
|
|
646
|
+
if (isSessionStorageTarget) {
|
|
647
|
+
session.pendingStaleSourceFiles.clear();
|
|
648
|
+
session.pendingStaleResourceFolders.clear();
|
|
649
|
+
}
|
|
650
|
+
if (storageTarget && !isSessionStorageTarget) {
|
|
651
|
+
session.pendingStaleSourceFiles.clear();
|
|
652
|
+
session.pendingStaleResourceFolders.clear();
|
|
653
|
+
}
|
|
617
654
|
if (isSessionStorageTarget || storageTarget) (0, _openfairygui_core_uam.commitUamProjectSourcePaths)(session.project);
|
|
618
655
|
if (storageTarget) {
|
|
619
656
|
this.context.sessionsByPath.delete(session.canonicalPathKey);
|
|
@@ -1141,7 +1178,7 @@ function createProjectReaderFileSystem(fileSystem) {
|
|
|
1141
1178
|
}
|
|
1142
1179
|
};
|
|
1143
1180
|
}
|
|
1144
|
-
function createCaptureFileSystem(files) {
|
|
1181
|
+
function createCaptureFileSystem(files, directories) {
|
|
1145
1182
|
const normalize = (filePath) => filePath.replace(/\\/g, "/").replace(/\/+/g, "/");
|
|
1146
1183
|
return {
|
|
1147
1184
|
async readFile(filePath) {
|
|
@@ -1160,7 +1197,9 @@ function createCaptureFileSystem(files) {
|
|
|
1160
1197
|
async writeFileRaw(filePath, data) {
|
|
1161
1198
|
files.set(normalize(filePath), data.slice());
|
|
1162
1199
|
},
|
|
1163
|
-
async mkdir() {
|
|
1200
|
+
async mkdir(dirPath) {
|
|
1201
|
+
directories.add(normalize(dirPath));
|
|
1202
|
+
},
|
|
1164
1203
|
async readdir() {
|
|
1165
1204
|
return [];
|
|
1166
1205
|
},
|
|
@@ -1193,15 +1232,20 @@ function capturedFilesEqual(left, right) {
|
|
|
1193
1232
|
}
|
|
1194
1233
|
return true;
|
|
1195
1234
|
}
|
|
1235
|
+
function capturedDirectoriesEqual(left, right) {
|
|
1236
|
+
return left.size === right.size && [...left].every((directory) => right.has(directory));
|
|
1237
|
+
}
|
|
1196
1238
|
async function hasFullUamFidelity(document, project) {
|
|
1197
1239
|
const sourceFiles = /* @__PURE__ */ new Map();
|
|
1198
1240
|
const materializedFiles = /* @__PURE__ */ new Map();
|
|
1241
|
+
const sourceDirectories = /* @__PURE__ */ new Set();
|
|
1242
|
+
const materializedDirectories = /* @__PURE__ */ new Set();
|
|
1199
1243
|
try {
|
|
1200
|
-
await Promise.all([new _openfairygui_core_project_io.ProjectWriter(createCaptureFileSystem(sourceFiles)).write(document, "Project.fairy"), new _openfairygui_core_project_io.ProjectWriter(createCaptureFileSystem(materializedFiles)).write((0, _openfairygui_core_uam.materializeUamProject)(project), "Project.fairy")]);
|
|
1244
|
+
await Promise.all([new _openfairygui_core_project_io.ProjectWriter(createCaptureFileSystem(sourceFiles, sourceDirectories)).write(document, "Project.fairy"), new _openfairygui_core_project_io.ProjectWriter(createCaptureFileSystem(materializedFiles, materializedDirectories)).write((0, _openfairygui_core_uam.materializeUamProject)(project), "Project.fairy")]);
|
|
1201
1245
|
} catch {
|
|
1202
1246
|
return false;
|
|
1203
1247
|
}
|
|
1204
|
-
return capturedFilesEqual(sourceFiles, materializedFiles);
|
|
1248
|
+
return capturedFilesEqual(sourceFiles, materializedFiles) && capturedDirectoriesEqual(sourceDirectories, materializedDirectories);
|
|
1205
1249
|
}
|
|
1206
1250
|
var RuntimeService = class {
|
|
1207
1251
|
constructor(context, cacheService, eventService, jobService) {
|
|
@@ -1252,6 +1296,7 @@ var RuntimeService = class {
|
|
|
1252
1296
|
revision: 0,
|
|
1253
1297
|
lastSavedRevision: 0,
|
|
1254
1298
|
pendingStaleSourceFiles: /* @__PURE__ */ new Map(),
|
|
1299
|
+
pendingStaleResourceFolders: /* @__PURE__ */ new Map(),
|
|
1255
1300
|
dirty: false,
|
|
1256
1301
|
lockHeld: true,
|
|
1257
1302
|
closed: false
|
|
@@ -1311,6 +1356,7 @@ var RuntimeService = class {
|
|
|
1311
1356
|
revision: 0,
|
|
1312
1357
|
lastSavedRevision: 0,
|
|
1313
1358
|
pendingStaleSourceFiles: /* @__PURE__ */ new Map(),
|
|
1359
|
+
pendingStaleResourceFolders: /* @__PURE__ */ new Map(),
|
|
1314
1360
|
dirty: false,
|
|
1315
1361
|
lockHeld: false,
|
|
1316
1362
|
closed: false
|
|
@@ -154,11 +154,15 @@ function createWriterFileSystem(fileSystem, writtenPaths, failedPaths) {
|
|
|
154
154
|
},
|
|
155
155
|
join: (...paths) => fileSystem.join(...paths),
|
|
156
156
|
dirname: (path) => fileSystem.dirname(path),
|
|
157
|
-
unlink: (path) => trackWrite(path, () => fileSystem.unlink(path))
|
|
157
|
+
unlink: (path) => trackWrite(path, () => fileSystem.unlink(path)),
|
|
158
|
+
rmdir: (path) => trackWrite(path, () => fileSystem.rmdir(path))
|
|
158
159
|
};
|
|
159
160
|
}
|
|
160
161
|
async function writeSessionProject(input) {
|
|
161
|
-
await new ProjectWriter(createWriterFileSystem(input.fileSystem, input.writtenPaths, input.failedPaths)).write(input.document, input.fairyPath, {
|
|
162
|
+
await new ProjectWriter(createWriterFileSystem(input.fileSystem, input.writtenPaths, input.failedPaths)).write(input.document, input.fairyPath, {
|
|
163
|
+
staleSourceFiles: input.staleSourceFiles,
|
|
164
|
+
staleResourceFolders: input.staleResourceFolders
|
|
165
|
+
});
|
|
162
166
|
}
|
|
163
167
|
//#endregion
|
|
164
168
|
//#region src/services/snapshot-utils.ts
|
|
@@ -237,6 +241,7 @@ function projectSourceFiles(project) {
|
|
|
237
241
|
fileName: "package.xml"
|
|
238
242
|
});
|
|
239
243
|
const branches = /* @__PURE__ */ new Set();
|
|
244
|
+
for (const folder of pkg.folders) if (folder.branch) branches.add(folder.branch);
|
|
240
245
|
for (const resource of pkg.resources) {
|
|
241
246
|
if (resource.branch) branches.add(resource.branch);
|
|
242
247
|
const fileName = resource.kind === "component" ? `${resource.name}.xml` : resource.fileName ?? (resource.kind === "image" ? "" : resource.file) ?? "";
|
|
@@ -265,6 +270,22 @@ function sourceFileKey(source) {
|
|
|
265
270
|
source.fileName
|
|
266
271
|
].join("\0");
|
|
267
272
|
}
|
|
273
|
+
function projectResourceFolders(project) {
|
|
274
|
+
const result = /* @__PURE__ */ new Map();
|
|
275
|
+
for (const pkg of project.packages) for (const folder of pkg.folders) result.set(`${pkg.id}/${folder.branch}/${folder.path}`, {
|
|
276
|
+
packageName: pkg.name,
|
|
277
|
+
branch: folder.branch,
|
|
278
|
+
path: folder.path
|
|
279
|
+
});
|
|
280
|
+
return result;
|
|
281
|
+
}
|
|
282
|
+
function resourceFolderKey(folder) {
|
|
283
|
+
return [
|
|
284
|
+
folder.branch,
|
|
285
|
+
folder.packageName,
|
|
286
|
+
folder.path
|
|
287
|
+
].join("\0");
|
|
288
|
+
}
|
|
268
289
|
function recordStaleProjectFiles(session, previousProject, nextProject) {
|
|
269
290
|
if (!session.fileSystem) return;
|
|
270
291
|
const previousSources = projectSourceFiles(previousProject);
|
|
@@ -274,6 +295,13 @@ function recordStaleProjectFiles(session, previousProject, nextProject) {
|
|
|
274
295
|
if (!nextSourceKeys.has(key)) session.pendingStaleSourceFiles.set(key, source);
|
|
275
296
|
}
|
|
276
297
|
for (const key of nextSourceKeys) session.pendingStaleSourceFiles.delete(key);
|
|
298
|
+
const previousFolders = projectResourceFolders(previousProject);
|
|
299
|
+
const nextFolderKeys = new Set([...projectResourceFolders(nextProject).values()].map(resourceFolderKey));
|
|
300
|
+
for (const folder of previousFolders.values()) {
|
|
301
|
+
const key = resourceFolderKey(folder);
|
|
302
|
+
if (!nextFolderKeys.has(key)) session.pendingStaleResourceFolders.set(key, folder);
|
|
303
|
+
}
|
|
304
|
+
for (const key of nextFolderKeys) session.pendingStaleResourceFolders.delete(key);
|
|
277
305
|
}
|
|
278
306
|
function toBackendDiagnostics(error) {
|
|
279
307
|
return error.diagnostics.length > 0 ? error.diagnostics.map((diagnostic) => ({ ...diagnostic })) : [{
|
|
@@ -466,11 +494,13 @@ var AuthoringService = class {
|
|
|
466
494
|
document: materializeUamProject(session.project),
|
|
467
495
|
fairyPath: session.fairyPath,
|
|
468
496
|
staleSourceFiles: [...session.pendingStaleSourceFiles.values()],
|
|
497
|
+
staleResourceFolders: [...session.pendingStaleResourceFolders.values()],
|
|
469
498
|
writtenPaths: committedPaths,
|
|
470
499
|
failedPaths
|
|
471
500
|
});
|
|
472
501
|
session.fileSystem ??= fileSystem;
|
|
473
502
|
session.pendingStaleSourceFiles.clear();
|
|
503
|
+
session.pendingStaleResourceFolders.clear();
|
|
474
504
|
commitUamProjectSourcePaths(session.project);
|
|
475
505
|
session.lastSavedRevision = session.revision;
|
|
476
506
|
session.dirty = false;
|
|
@@ -608,11 +638,18 @@ var AuthoringService = class {
|
|
|
608
638
|
document,
|
|
609
639
|
fairyPath,
|
|
610
640
|
staleSourceFiles: isSessionStorageTarget ? [...session.pendingStaleSourceFiles.values()] : [],
|
|
641
|
+
staleResourceFolders: isSessionStorageTarget ? [...session.pendingStaleResourceFolders.values()] : [],
|
|
611
642
|
writtenPaths,
|
|
612
643
|
failedPaths
|
|
613
644
|
});
|
|
614
|
-
if (isSessionStorageTarget)
|
|
615
|
-
|
|
645
|
+
if (isSessionStorageTarget) {
|
|
646
|
+
session.pendingStaleSourceFiles.clear();
|
|
647
|
+
session.pendingStaleResourceFolders.clear();
|
|
648
|
+
}
|
|
649
|
+
if (storageTarget && !isSessionStorageTarget) {
|
|
650
|
+
session.pendingStaleSourceFiles.clear();
|
|
651
|
+
session.pendingStaleResourceFolders.clear();
|
|
652
|
+
}
|
|
616
653
|
if (isSessionStorageTarget || storageTarget) commitUamProjectSourcePaths(session.project);
|
|
617
654
|
if (storageTarget) {
|
|
618
655
|
this.context.sessionsByPath.delete(session.canonicalPathKey);
|
|
@@ -1140,7 +1177,7 @@ function createProjectReaderFileSystem(fileSystem) {
|
|
|
1140
1177
|
}
|
|
1141
1178
|
};
|
|
1142
1179
|
}
|
|
1143
|
-
function createCaptureFileSystem(files) {
|
|
1180
|
+
function createCaptureFileSystem(files, directories) {
|
|
1144
1181
|
const normalize = (filePath) => filePath.replace(/\\/g, "/").replace(/\/+/g, "/");
|
|
1145
1182
|
return {
|
|
1146
1183
|
async readFile(filePath) {
|
|
@@ -1159,7 +1196,9 @@ function createCaptureFileSystem(files) {
|
|
|
1159
1196
|
async writeFileRaw(filePath, data) {
|
|
1160
1197
|
files.set(normalize(filePath), data.slice());
|
|
1161
1198
|
},
|
|
1162
|
-
async mkdir() {
|
|
1199
|
+
async mkdir(dirPath) {
|
|
1200
|
+
directories.add(normalize(dirPath));
|
|
1201
|
+
},
|
|
1163
1202
|
async readdir() {
|
|
1164
1203
|
return [];
|
|
1165
1204
|
},
|
|
@@ -1192,15 +1231,20 @@ function capturedFilesEqual(left, right) {
|
|
|
1192
1231
|
}
|
|
1193
1232
|
return true;
|
|
1194
1233
|
}
|
|
1234
|
+
function capturedDirectoriesEqual(left, right) {
|
|
1235
|
+
return left.size === right.size && [...left].every((directory) => right.has(directory));
|
|
1236
|
+
}
|
|
1195
1237
|
async function hasFullUamFidelity(document, project) {
|
|
1196
1238
|
const sourceFiles = /* @__PURE__ */ new Map();
|
|
1197
1239
|
const materializedFiles = /* @__PURE__ */ new Map();
|
|
1240
|
+
const sourceDirectories = /* @__PURE__ */ new Set();
|
|
1241
|
+
const materializedDirectories = /* @__PURE__ */ new Set();
|
|
1198
1242
|
try {
|
|
1199
|
-
await Promise.all([new ProjectWriter(createCaptureFileSystem(sourceFiles)).write(document, "Project.fairy"), new ProjectWriter(createCaptureFileSystem(materializedFiles)).write(materializeUamProject(project), "Project.fairy")]);
|
|
1243
|
+
await Promise.all([new ProjectWriter(createCaptureFileSystem(sourceFiles, sourceDirectories)).write(document, "Project.fairy"), new ProjectWriter(createCaptureFileSystem(materializedFiles, materializedDirectories)).write(materializeUamProject(project), "Project.fairy")]);
|
|
1200
1244
|
} catch {
|
|
1201
1245
|
return false;
|
|
1202
1246
|
}
|
|
1203
|
-
return capturedFilesEqual(sourceFiles, materializedFiles);
|
|
1247
|
+
return capturedFilesEqual(sourceFiles, materializedFiles) && capturedDirectoriesEqual(sourceDirectories, materializedDirectories);
|
|
1204
1248
|
}
|
|
1205
1249
|
var RuntimeService = class {
|
|
1206
1250
|
constructor(context, cacheService, eventService, jobService) {
|
|
@@ -1251,6 +1295,7 @@ var RuntimeService = class {
|
|
|
1251
1295
|
revision: 0,
|
|
1252
1296
|
lastSavedRevision: 0,
|
|
1253
1297
|
pendingStaleSourceFiles: /* @__PURE__ */ new Map(),
|
|
1298
|
+
pendingStaleResourceFolders: /* @__PURE__ */ new Map(),
|
|
1254
1299
|
dirty: false,
|
|
1255
1300
|
lockHeld: true,
|
|
1256
1301
|
closed: false
|
|
@@ -1310,6 +1355,7 @@ var RuntimeService = class {
|
|
|
1310
1355
|
revision: 0,
|
|
1311
1356
|
lastSavedRevision: 0,
|
|
1312
1357
|
pendingStaleSourceFiles: /* @__PURE__ */ new Map(),
|
|
1358
|
+
pendingStaleResourceFolders: /* @__PURE__ */ new Map(),
|
|
1313
1359
|
dirty: false,
|
|
1314
1360
|
lockHeld: false,
|
|
1315
1361
|
closed: false
|
|
@@ -70,6 +70,7 @@ interface BackendFileSystem {
|
|
|
70
70
|
resolvePath(filePath: string): Promise<string>;
|
|
71
71
|
openExclusive(filePath: string): Promise<BackendFileHandle>;
|
|
72
72
|
unlink(filePath: string): Promise<void>;
|
|
73
|
+
rmdir(dirPath: string): Promise<void>;
|
|
73
74
|
join(...paths: string[]): string;
|
|
74
75
|
dirname(filePath: string): string;
|
|
75
76
|
resolve(...paths: string[]): string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openfairygui/backend",
|
|
3
|
-
"version": "0.2.0-alpha.
|
|
3
|
+
"version": "0.2.0-alpha.30",
|
|
4
4
|
"description": "FairyGUI Headless Authoring SDK — stateful backend runtime and session services.",
|
|
5
5
|
"author": "OpenFairyGUI Contributors",
|
|
6
6
|
"license": "MIT",
|
|
@@ -52,8 +52,8 @@
|
|
|
52
52
|
"runtime"
|
|
53
53
|
],
|
|
54
54
|
"dependencies": {
|
|
55
|
-
"@openfairygui/core": "0.2.0-alpha.
|
|
56
|
-
"@openfairygui/functions": "0.2.0-alpha.
|
|
55
|
+
"@openfairygui/core": "0.2.0-alpha.30",
|
|
56
|
+
"@openfairygui/functions": "0.2.0-alpha.30"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
59
|
"ava": "^7.0.0",
|
package/src/node.ts
CHANGED
|
@@ -54,6 +54,9 @@ export function createNodeBackendFileSystem(): BackendFileSystem {
|
|
|
54
54
|
unlink(filePath: string): Promise<void> {
|
|
55
55
|
return fs.unlink(filePath);
|
|
56
56
|
},
|
|
57
|
+
rmdir(dirPath: string): Promise<void> {
|
|
58
|
+
return fs.rmdir(dirPath);
|
|
59
|
+
},
|
|
57
60
|
join(...paths: string[]): string {
|
|
58
61
|
return path.join(...paths);
|
|
59
62
|
},
|
package/src/runtime/contracts.ts
CHANGED
|
@@ -30,6 +30,7 @@ export interface BackendFileSystem {
|
|
|
30
30
|
resolvePath(filePath: string): Promise<string>;
|
|
31
31
|
openExclusive(filePath: string): Promise<BackendFileHandle>;
|
|
32
32
|
unlink(filePath: string): Promise<void>;
|
|
33
|
+
rmdir(dirPath: string): Promise<void>;
|
|
33
34
|
join(...paths: string[]): string;
|
|
34
35
|
dirname(filePath: string): string;
|
|
35
36
|
resolve(...paths: string[]): string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ProjectSourceFile } from '@openfairygui/core/project-io';
|
|
1
|
+
import type { ProjectResourceFolder, ProjectSourceFile } from '@openfairygui/core/project-io';
|
|
2
2
|
import {
|
|
3
3
|
commitUamProjectSourcePaths,
|
|
4
4
|
materializeUamProject,
|
|
@@ -41,6 +41,9 @@ function projectSourceFiles(project: UamProject): Map<string, ProjectSourceFile>
|
|
|
41
41
|
fileName: 'package.xml',
|
|
42
42
|
});
|
|
43
43
|
const branches = new Set<string>();
|
|
44
|
+
for (const folder of pkg.folders) {
|
|
45
|
+
if (folder.branch) branches.add(folder.branch);
|
|
46
|
+
}
|
|
44
47
|
for (const resource of pkg.resources) {
|
|
45
48
|
if (resource.branch) branches.add(resource.branch);
|
|
46
49
|
const fileName = resource.kind === 'component'
|
|
@@ -70,6 +73,24 @@ function sourceFileKey(source: ProjectSourceFile): string {
|
|
|
70
73
|
return [source.branch, source.packageName, source.path, source.fileName].join('\0');
|
|
71
74
|
}
|
|
72
75
|
|
|
76
|
+
function projectResourceFolders(project: UamProject): Map<string, ProjectResourceFolder> {
|
|
77
|
+
const result = new Map<string, ProjectResourceFolder>();
|
|
78
|
+
for (const pkg of project.packages) {
|
|
79
|
+
for (const folder of pkg.folders) {
|
|
80
|
+
result.set(`${pkg.id}/${folder.branch}/${folder.path}`, {
|
|
81
|
+
packageName: pkg.name,
|
|
82
|
+
branch: folder.branch,
|
|
83
|
+
path: folder.path,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return result;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function resourceFolderKey(folder: ProjectResourceFolder): string {
|
|
91
|
+
return [folder.branch, folder.packageName, folder.path].join('\0');
|
|
92
|
+
}
|
|
93
|
+
|
|
73
94
|
function recordStaleProjectFiles(
|
|
74
95
|
session: Parameters<typeof toSessionSnapshot>[0],
|
|
75
96
|
previousProject: UamProject,
|
|
@@ -85,6 +106,15 @@ function recordStaleProjectFiles(
|
|
|
85
106
|
for (const key of nextSourceKeys) {
|
|
86
107
|
session.pendingStaleSourceFiles.delete(key);
|
|
87
108
|
}
|
|
109
|
+
const previousFolders = projectResourceFolders(previousProject);
|
|
110
|
+
const nextFolderKeys = new Set([...projectResourceFolders(nextProject).values()].map(resourceFolderKey));
|
|
111
|
+
for (const folder of previousFolders.values()) {
|
|
112
|
+
const key = resourceFolderKey(folder);
|
|
113
|
+
if (!nextFolderKeys.has(key)) session.pendingStaleResourceFolders.set(key, folder);
|
|
114
|
+
}
|
|
115
|
+
for (const key of nextFolderKeys) {
|
|
116
|
+
session.pendingStaleResourceFolders.delete(key);
|
|
117
|
+
}
|
|
88
118
|
}
|
|
89
119
|
|
|
90
120
|
function toBackendDiagnostics(error: ApplyUamTransactionAppError): BackendDiagnostic[] {
|
|
@@ -411,11 +441,13 @@ export class AuthoringService {
|
|
|
411
441
|
document: materializeUamProject(session.project),
|
|
412
442
|
fairyPath: session.fairyPath,
|
|
413
443
|
staleSourceFiles: [...session.pendingStaleSourceFiles.values()],
|
|
444
|
+
staleResourceFolders: [...session.pendingStaleResourceFolders.values()],
|
|
414
445
|
writtenPaths: committedPaths,
|
|
415
446
|
failedPaths,
|
|
416
447
|
});
|
|
417
448
|
session.fileSystem ??= fileSystem;
|
|
418
449
|
session.pendingStaleSourceFiles.clear();
|
|
450
|
+
session.pendingStaleResourceFolders.clear();
|
|
419
451
|
commitUamProjectSourcePaths(session.project);
|
|
420
452
|
session.lastSavedRevision = session.revision;
|
|
421
453
|
session.dirty = false;
|
|
@@ -650,11 +682,18 @@ export class AuthoringService {
|
|
|
650
682
|
document,
|
|
651
683
|
fairyPath,
|
|
652
684
|
staleSourceFiles: isSessionStorageTarget ? [...session.pendingStaleSourceFiles.values()] : [],
|
|
685
|
+
staleResourceFolders: isSessionStorageTarget ? [...session.pendingStaleResourceFolders.values()] : [],
|
|
653
686
|
writtenPaths,
|
|
654
687
|
failedPaths,
|
|
655
688
|
});
|
|
656
|
-
if (isSessionStorageTarget)
|
|
657
|
-
|
|
689
|
+
if (isSessionStorageTarget) {
|
|
690
|
+
session.pendingStaleSourceFiles.clear();
|
|
691
|
+
session.pendingStaleResourceFolders.clear();
|
|
692
|
+
}
|
|
693
|
+
if (storageTarget && !isSessionStorageTarget) {
|
|
694
|
+
session.pendingStaleSourceFiles.clear();
|
|
695
|
+
session.pendingStaleResourceFolders.clear();
|
|
696
|
+
}
|
|
658
697
|
if (isSessionStorageTarget || storageTarget) commitUamProjectSourcePaths(session.project);
|
|
659
698
|
if (storageTarget) {
|
|
660
699
|
this.context.sessionsByPath.delete(session.canonicalPathKey);
|
package/src/services/context.ts
CHANGED
|
@@ -32,6 +32,8 @@ export interface BackendSessionState {
|
|
|
32
32
|
lastSavedRevision: number;
|
|
33
33
|
/** Package-controlled files deferred until a successful replacement project write. */
|
|
34
34
|
pendingStaleSourceFiles: Map<string, import('@openfairygui/core/project-io').ProjectSourceFile>;
|
|
35
|
+
/** Empty resource directories deferred until a successful replacement project write. */
|
|
36
|
+
pendingStaleResourceFolders: Map<string, import('@openfairygui/core/project-io').ProjectResourceFolder>;
|
|
35
37
|
dirty: boolean;
|
|
36
38
|
lockHeld: boolean;
|
|
37
39
|
closed: boolean;
|
|
@@ -79,7 +79,10 @@ function createProjectReaderFileSystem(fileSystem: NonNullable<BackendContext['f
|
|
|
79
79
|
};
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
function createCaptureFileSystem(
|
|
82
|
+
function createCaptureFileSystem(
|
|
83
|
+
files: Map<string, string | Uint8Array>,
|
|
84
|
+
directories: Set<string>,
|
|
85
|
+
): FileSystem {
|
|
83
86
|
const normalize = (filePath: string): string => filePath.replace(/\\/g, '/').replace(/\/+/g, '/');
|
|
84
87
|
return {
|
|
85
88
|
async readFile(filePath: string): Promise<string> {
|
|
@@ -98,7 +101,9 @@ function createCaptureFileSystem(files: Map<string, string | Uint8Array>): FileS
|
|
|
98
101
|
async writeFileRaw(filePath: string, data: Uint8Array): Promise<void> {
|
|
99
102
|
files.set(normalize(filePath), data.slice());
|
|
100
103
|
},
|
|
101
|
-
async mkdir(): Promise<void> {
|
|
104
|
+
async mkdir(dirPath: string): Promise<void> {
|
|
105
|
+
directories.add(normalize(dirPath));
|
|
106
|
+
},
|
|
102
107
|
async readdir(): Promise<string[]> {
|
|
103
108
|
return [];
|
|
104
109
|
},
|
|
@@ -135,16 +140,22 @@ function capturedFilesEqual(left: Map<string, string | Uint8Array>, right: Map<s
|
|
|
135
140
|
return true;
|
|
136
141
|
}
|
|
137
142
|
|
|
143
|
+
function capturedDirectoriesEqual(left: Set<string>, right: Set<string>): boolean {
|
|
144
|
+
return left.size === right.size && [...left].every((directory) => right.has(directory));
|
|
145
|
+
}
|
|
146
|
+
|
|
138
147
|
async function hasFullUamFidelity(
|
|
139
148
|
document: Awaited<ReturnType<ProjectReader['read']>>,
|
|
140
149
|
project: UamProject,
|
|
141
150
|
): Promise<boolean> {
|
|
142
151
|
const sourceFiles = new Map<string, string | Uint8Array>();
|
|
143
152
|
const materializedFiles = new Map<string, string | Uint8Array>();
|
|
153
|
+
const sourceDirectories = new Set<string>();
|
|
154
|
+
const materializedDirectories = new Set<string>();
|
|
144
155
|
try {
|
|
145
156
|
await Promise.all([
|
|
146
|
-
new ProjectWriter(createCaptureFileSystem(sourceFiles)).write(document, 'Project.fairy'),
|
|
147
|
-
new ProjectWriter(createCaptureFileSystem(materializedFiles)).write(
|
|
157
|
+
new ProjectWriter(createCaptureFileSystem(sourceFiles, sourceDirectories)).write(document, 'Project.fairy'),
|
|
158
|
+
new ProjectWriter(createCaptureFileSystem(materializedFiles, materializedDirectories)).write(
|
|
148
159
|
materializeUamProject(project),
|
|
149
160
|
'Project.fairy',
|
|
150
161
|
),
|
|
@@ -152,7 +163,8 @@ async function hasFullUamFidelity(
|
|
|
152
163
|
} catch {
|
|
153
164
|
return false;
|
|
154
165
|
}
|
|
155
|
-
return capturedFilesEqual(sourceFiles, materializedFiles)
|
|
166
|
+
return capturedFilesEqual(sourceFiles, materializedFiles)
|
|
167
|
+
&& capturedDirectoriesEqual(sourceDirectories, materializedDirectories);
|
|
156
168
|
}
|
|
157
169
|
|
|
158
170
|
export class RuntimeService {
|
|
@@ -225,6 +237,7 @@ export class RuntimeService {
|
|
|
225
237
|
revision: 0,
|
|
226
238
|
lastSavedRevision: 0,
|
|
227
239
|
pendingStaleSourceFiles: new Map(),
|
|
240
|
+
pendingStaleResourceFolders: new Map(),
|
|
228
241
|
dirty: false,
|
|
229
242
|
lockHeld: true,
|
|
230
243
|
closed: false,
|
|
@@ -292,6 +305,7 @@ export class RuntimeService {
|
|
|
292
305
|
revision: 0,
|
|
293
306
|
lastSavedRevision: 0,
|
|
294
307
|
pendingStaleSourceFiles: new Map(),
|
|
308
|
+
pendingStaleResourceFolders: new Map(),
|
|
295
309
|
dirty: false,
|
|
296
310
|
lockHeld: false,
|
|
297
311
|
closed: false,
|
|
@@ -44,6 +44,7 @@ function createWriterFileSystem(
|
|
|
44
44
|
join: (...paths) => fileSystem.join(...paths),
|
|
45
45
|
dirname: (path) => fileSystem.dirname(path),
|
|
46
46
|
unlink: (path) => trackWrite(path, () => fileSystem.unlink(path)),
|
|
47
|
+
rmdir: (path) => trackWrite(path, () => fileSystem.rmdir(path)),
|
|
47
48
|
};
|
|
48
49
|
}
|
|
49
50
|
|
|
@@ -52,6 +53,7 @@ export async function writeSessionProject(input: {
|
|
|
52
53
|
document: Document;
|
|
53
54
|
fairyPath: string;
|
|
54
55
|
staleSourceFiles: ProjectSourceFile[];
|
|
56
|
+
staleResourceFolders: import('@openfairygui/core/project-io').ProjectResourceFolder[];
|
|
55
57
|
writtenPaths: string[];
|
|
56
58
|
failedPaths: string[];
|
|
57
59
|
}): Promise<void> {
|
|
@@ -60,5 +62,6 @@ export async function writeSessionProject(input: {
|
|
|
60
62
|
);
|
|
61
63
|
await writer.write(input.document, input.fairyPath, {
|
|
62
64
|
staleSourceFiles: input.staleSourceFiles,
|
|
65
|
+
staleResourceFolders: input.staleResourceFolders,
|
|
63
66
|
});
|
|
64
67
|
}
|
package/src/storage.ts
CHANGED
|
@@ -22,6 +22,7 @@ export interface BackendAsyncStorageAdapter {
|
|
|
22
22
|
resolvePath?(filePath: string): Promise<string>;
|
|
23
23
|
openExclusive?(filePath: string): Promise<BackendFileHandle>;
|
|
24
24
|
unlink(filePath: string): Promise<void>;
|
|
25
|
+
rmdir(dirPath: string): Promise<void>;
|
|
25
26
|
join?(...paths: string[]): string;
|
|
26
27
|
dirname?(filePath: string): string;
|
|
27
28
|
resolve?(...paths: string[]): string;
|
|
@@ -117,6 +118,9 @@ export function createBackendStorageFileSystem(storage: BackendAsyncStorageAdapt
|
|
|
117
118
|
if (typeof storage.unlink !== 'function') {
|
|
118
119
|
throw createPathError('ENOTSUP', 'Storage adapter must provide unlink() for project resource lifecycle writes.');
|
|
119
120
|
}
|
|
121
|
+
if (typeof storage.rmdir !== 'function') {
|
|
122
|
+
throw createPathError('ENOTSUP', 'Storage adapter must provide rmdir() for project resource folder lifecycle writes.');
|
|
123
|
+
}
|
|
120
124
|
const lockedPaths = new Set<string>();
|
|
121
125
|
|
|
122
126
|
const fileSystem: BackendStorageFileSystem = {
|
|
@@ -179,6 +183,9 @@ export function createBackendStorageFileSystem(storage: BackendAsyncStorageAdapt
|
|
|
179
183
|
lockedPaths.delete(resolved);
|
|
180
184
|
return storage.unlink(resolved);
|
|
181
185
|
},
|
|
186
|
+
rmdir(dirPath: string): Promise<void> {
|
|
187
|
+
return storage.rmdir(fileSystem.resolve(dirPath));
|
|
188
|
+
},
|
|
182
189
|
join(...paths: string[]): string {
|
|
183
190
|
return storage.join ? storage.join(...paths) : joinStoragePath(...paths);
|
|
184
191
|
},
|