@openfairygui/backend 0.2.0-alpha.33 → 0.2.0-alpha.35
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 +1 -1
- package/dist/index.mjs +1 -1
- package/dist/node.cjs +1 -1
- package/dist/node.mjs +1 -1
- package/dist/{runtime-DtcaaVfy.cjs → runtime-CQQmzsxV.cjs} +30 -3
- package/dist/{runtime-BDkZmTFN.mjs → runtime-DtCn0Y44.mjs} +32 -5
- package/package.json +3 -3
- package/src/services/authoring-service.ts +39 -4
- package/src/services/context.ts +2 -0
- package/src/services/runtime-service.ts +2 -0
- package/src/services/session-project-writer.ts +3 -1
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-CQQmzsxV.cjs");
|
|
3
3
|
//#region src/storage.ts
|
|
4
4
|
var StorageFileStat = class {
|
|
5
5
|
constructor(kind) {
|
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-DtCn0Y44.mjs";
|
|
2
2
|
//#region src/storage.ts
|
|
3
3
|
var StorageFileStat = class {
|
|
4
4
|
constructor(kind) {
|
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-CQQmzsxV.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.mjs
CHANGED
|
@@ -162,7 +162,8 @@ function createWriterFileSystem(fileSystem, writtenPaths, failedPaths) {
|
|
|
162
162
|
async function writeSessionProject(input) {
|
|
163
163
|
await new _openfairygui_core_project_io.ProjectWriter(createWriterFileSystem(input.fileSystem, input.writtenPaths, input.failedPaths)).write(input.document, input.fairyPath, {
|
|
164
164
|
staleSourceFiles: input.staleSourceFiles,
|
|
165
|
-
staleResourceFolders: input.staleResourceFolders
|
|
165
|
+
staleResourceFolders: input.staleResourceFolders,
|
|
166
|
+
staleBranchDirectories: input.staleBranchDirectories
|
|
166
167
|
});
|
|
167
168
|
}
|
|
168
169
|
//#endregion
|
|
@@ -213,12 +214,17 @@ function resourceFolderKey(folder) {
|
|
|
213
214
|
folder.path
|
|
214
215
|
].join("\0");
|
|
215
216
|
}
|
|
217
|
+
function branchDirectoryKey(directory) {
|
|
218
|
+
return [directory.branch, directory.packageName ?? ""].join("\0");
|
|
219
|
+
}
|
|
216
220
|
function recordStaleProjectFiles(session, previousProject, nextProject) {
|
|
217
221
|
if (!session.fileSystem) return;
|
|
218
222
|
for (const source of (0, _openfairygui_core_uam.staleSourceFiles)(previousProject, nextProject)) session.pendingStaleSourceFiles.set(sourceFileKey(source), source);
|
|
219
223
|
for (const source of (0, _openfairygui_core_uam.staleSourceFiles)(nextProject, previousProject)) session.pendingStaleSourceFiles.delete(sourceFileKey(source));
|
|
220
224
|
for (const folder of (0, _openfairygui_core_uam.staleResourceFolders)(previousProject, nextProject)) session.pendingStaleResourceFolders.set(resourceFolderKey(folder), folder);
|
|
221
225
|
for (const folder of (0, _openfairygui_core_uam.staleResourceFolders)(nextProject, previousProject)) session.pendingStaleResourceFolders.delete(resourceFolderKey(folder));
|
|
226
|
+
for (const directory of (0, _openfairygui_core_uam.staleBranchDirectories)(previousProject, nextProject)) session.pendingStaleBranchDirectories.set(branchDirectoryKey(directory), directory);
|
|
227
|
+
for (const directory of (0, _openfairygui_core_uam.staleBranchDirectories)(nextProject, previousProject)) session.pendingStaleBranchDirectories.delete(branchDirectoryKey(directory));
|
|
222
228
|
}
|
|
223
229
|
function toBackendDiagnostics(error) {
|
|
224
230
|
return error.diagnostics.length > 0 ? error.diagnostics.map((diagnostic) => ({ ...diagnostic })) : [{
|
|
@@ -276,6 +282,17 @@ function storageCanonicalTarget(input) {
|
|
|
276
282
|
canonicalPathKey: input.canonicalPathKey ?? normalizeComparablePath(canonicalProjectPath)
|
|
277
283
|
};
|
|
278
284
|
}
|
|
285
|
+
function detachSharedByteViews(value, seen = /* @__PURE__ */ new WeakSet()) {
|
|
286
|
+
if (!value || typeof value !== "object" || seen.has(value)) return;
|
|
287
|
+
seen.add(value);
|
|
288
|
+
for (const [key, child] of Object.entries(value)) {
|
|
289
|
+
if (child instanceof Uint8Array) {
|
|
290
|
+
if (typeof SharedArrayBuffer !== "undefined" && child.buffer instanceof SharedArrayBuffer) value[key] = new Uint8Array(child);
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
detachSharedByteViews(child, seen);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
279
296
|
var AuthoringService = class {
|
|
280
297
|
sessionOperations = /* @__PURE__ */ new Map();
|
|
281
298
|
constructor(context, cacheService, eventService) {
|
|
@@ -300,7 +317,9 @@ var AuthoringService = class {
|
|
|
300
317
|
}
|
|
301
318
|
}
|
|
302
319
|
async applyTransaction(input) {
|
|
303
|
-
|
|
320
|
+
const queuedInput = structuredClone(input);
|
|
321
|
+
detachSharedByteViews(queuedInput);
|
|
322
|
+
return this.runSessionExclusive(queuedInput.sessionId, () => this.applyTransactionExclusive(queuedInput));
|
|
304
323
|
}
|
|
305
324
|
async applyTransactionExclusive(input) {
|
|
306
325
|
const startedAt = Date.now();
|
|
@@ -318,10 +337,11 @@ var AuthoringService = class {
|
|
|
318
337
|
revision: session.revision
|
|
319
338
|
});
|
|
320
339
|
}
|
|
321
|
-
const result = (0, _openfairygui_functions_uam.
|
|
340
|
+
const result = await (0, _openfairygui_functions_uam.applyUamTransactionAppAsync)({
|
|
322
341
|
project: session.project,
|
|
323
342
|
operations: input.operations
|
|
324
343
|
});
|
|
344
|
+
if (this.context.sessions.get(input.sessionId) !== session || session.closed) return failure("authoring", startedAt, createSessionNotFoundError(input.sessionId));
|
|
325
345
|
if (result.ok === false) {
|
|
326
346
|
const diagnostics = toBackendDiagnostics(result.error);
|
|
327
347
|
this.eventService.emit({
|
|
@@ -412,12 +432,14 @@ var AuthoringService = class {
|
|
|
412
432
|
fairyPath: session.fairyPath,
|
|
413
433
|
staleSourceFiles: [...session.pendingStaleSourceFiles.values()],
|
|
414
434
|
staleResourceFolders: [...session.pendingStaleResourceFolders.values()],
|
|
435
|
+
staleBranchDirectories: [...session.pendingStaleBranchDirectories.values()],
|
|
415
436
|
writtenPaths: committedPaths,
|
|
416
437
|
failedPaths
|
|
417
438
|
});
|
|
418
439
|
session.fileSystem ??= fileSystem;
|
|
419
440
|
session.pendingStaleSourceFiles.clear();
|
|
420
441
|
session.pendingStaleResourceFolders.clear();
|
|
442
|
+
session.pendingStaleBranchDirectories.clear();
|
|
421
443
|
(0, _openfairygui_core_uam.commitUamProjectSourcePaths)(session.project);
|
|
422
444
|
session.lastSavedRevision = session.revision;
|
|
423
445
|
session.dirty = false;
|
|
@@ -556,16 +578,19 @@ var AuthoringService = class {
|
|
|
556
578
|
fairyPath,
|
|
557
579
|
staleSourceFiles: isSessionStorageTarget ? [...session.pendingStaleSourceFiles.values()] : [],
|
|
558
580
|
staleResourceFolders: isSessionStorageTarget ? [...session.pendingStaleResourceFolders.values()] : [],
|
|
581
|
+
staleBranchDirectories: isSessionStorageTarget ? [...session.pendingStaleBranchDirectories.values()] : [],
|
|
559
582
|
writtenPaths,
|
|
560
583
|
failedPaths
|
|
561
584
|
});
|
|
562
585
|
if (isSessionStorageTarget) {
|
|
563
586
|
session.pendingStaleSourceFiles.clear();
|
|
564
587
|
session.pendingStaleResourceFolders.clear();
|
|
588
|
+
session.pendingStaleBranchDirectories.clear();
|
|
565
589
|
}
|
|
566
590
|
if (storageTarget && !isSessionStorageTarget) {
|
|
567
591
|
session.pendingStaleSourceFiles.clear();
|
|
568
592
|
session.pendingStaleResourceFolders.clear();
|
|
593
|
+
session.pendingStaleBranchDirectories.clear();
|
|
569
594
|
}
|
|
570
595
|
if (isSessionStorageTarget || storageTarget) (0, _openfairygui_core_uam.commitUamProjectSourcePaths)(session.project);
|
|
571
596
|
if (storageTarget) {
|
|
@@ -1214,6 +1239,7 @@ var RuntimeService = class {
|
|
|
1214
1239
|
lastSavedRevision: 0,
|
|
1215
1240
|
pendingStaleSourceFiles: /* @__PURE__ */ new Map(),
|
|
1216
1241
|
pendingStaleResourceFolders: /* @__PURE__ */ new Map(),
|
|
1242
|
+
pendingStaleBranchDirectories: /* @__PURE__ */ new Map(),
|
|
1217
1243
|
dirty: false,
|
|
1218
1244
|
lockHeld: true,
|
|
1219
1245
|
closed: false
|
|
@@ -1274,6 +1300,7 @@ var RuntimeService = class {
|
|
|
1274
1300
|
lastSavedRevision: 0,
|
|
1275
1301
|
pendingStaleSourceFiles: /* @__PURE__ */ new Map(),
|
|
1276
1302
|
pendingStaleResourceFolders: /* @__PURE__ */ new Map(),
|
|
1303
|
+
pendingStaleBranchDirectories: /* @__PURE__ */ new Map(),
|
|
1277
1304
|
dirty: false,
|
|
1278
1305
|
lockHeld: false,
|
|
1279
1306
|
closed: false
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { UAM_SUPPORTED_MATERIALIZATION_SCOPE, UAM_SUPPORTED_TRANSACTION_SCOPE, commitUamProjectSourcePaths, liftDocumentToUamProject, materializeUamProject, normalizeUamProject, staleResourceFolders, staleSourceFiles, validateUamProject } from "@openfairygui/core/uam";
|
|
2
|
-
import {
|
|
1
|
+
import { UAM_SUPPORTED_MATERIALIZATION_SCOPE, UAM_SUPPORTED_TRANSACTION_SCOPE, commitUamProjectSourcePaths, liftDocumentToUamProject, materializeUamProject, normalizeUamProject, staleBranchDirectories, staleResourceFolders, staleSourceFiles, validateUamProject } from "@openfairygui/core/uam";
|
|
2
|
+
import { applyUamTransactionAppAsync } from "@openfairygui/functions/uam";
|
|
3
3
|
import { ProjectReader, ProjectWriter } from "@openfairygui/core/project-io";
|
|
4
4
|
//#region src/contracts.ts
|
|
5
5
|
const BACKEND_CONTRACT_VERSION = "1.1.0-p2";
|
|
@@ -161,7 +161,8 @@ function createWriterFileSystem(fileSystem, writtenPaths, failedPaths) {
|
|
|
161
161
|
async function writeSessionProject(input) {
|
|
162
162
|
await new ProjectWriter(createWriterFileSystem(input.fileSystem, input.writtenPaths, input.failedPaths)).write(input.document, input.fairyPath, {
|
|
163
163
|
staleSourceFiles: input.staleSourceFiles,
|
|
164
|
-
staleResourceFolders: input.staleResourceFolders
|
|
164
|
+
staleResourceFolders: input.staleResourceFolders,
|
|
165
|
+
staleBranchDirectories: input.staleBranchDirectories
|
|
165
166
|
});
|
|
166
167
|
}
|
|
167
168
|
//#endregion
|
|
@@ -212,12 +213,17 @@ function resourceFolderKey(folder) {
|
|
|
212
213
|
folder.path
|
|
213
214
|
].join("\0");
|
|
214
215
|
}
|
|
216
|
+
function branchDirectoryKey(directory) {
|
|
217
|
+
return [directory.branch, directory.packageName ?? ""].join("\0");
|
|
218
|
+
}
|
|
215
219
|
function recordStaleProjectFiles(session, previousProject, nextProject) {
|
|
216
220
|
if (!session.fileSystem) return;
|
|
217
221
|
for (const source of staleSourceFiles(previousProject, nextProject)) session.pendingStaleSourceFiles.set(sourceFileKey(source), source);
|
|
218
222
|
for (const source of staleSourceFiles(nextProject, previousProject)) session.pendingStaleSourceFiles.delete(sourceFileKey(source));
|
|
219
223
|
for (const folder of staleResourceFolders(previousProject, nextProject)) session.pendingStaleResourceFolders.set(resourceFolderKey(folder), folder);
|
|
220
224
|
for (const folder of staleResourceFolders(nextProject, previousProject)) session.pendingStaleResourceFolders.delete(resourceFolderKey(folder));
|
|
225
|
+
for (const directory of staleBranchDirectories(previousProject, nextProject)) session.pendingStaleBranchDirectories.set(branchDirectoryKey(directory), directory);
|
|
226
|
+
for (const directory of staleBranchDirectories(nextProject, previousProject)) session.pendingStaleBranchDirectories.delete(branchDirectoryKey(directory));
|
|
221
227
|
}
|
|
222
228
|
function toBackendDiagnostics(error) {
|
|
223
229
|
return error.diagnostics.length > 0 ? error.diagnostics.map((diagnostic) => ({ ...diagnostic })) : [{
|
|
@@ -275,6 +281,17 @@ function storageCanonicalTarget(input) {
|
|
|
275
281
|
canonicalPathKey: input.canonicalPathKey ?? normalizeComparablePath(canonicalProjectPath)
|
|
276
282
|
};
|
|
277
283
|
}
|
|
284
|
+
function detachSharedByteViews(value, seen = /* @__PURE__ */ new WeakSet()) {
|
|
285
|
+
if (!value || typeof value !== "object" || seen.has(value)) return;
|
|
286
|
+
seen.add(value);
|
|
287
|
+
for (const [key, child] of Object.entries(value)) {
|
|
288
|
+
if (child instanceof Uint8Array) {
|
|
289
|
+
if (typeof SharedArrayBuffer !== "undefined" && child.buffer instanceof SharedArrayBuffer) value[key] = new Uint8Array(child);
|
|
290
|
+
continue;
|
|
291
|
+
}
|
|
292
|
+
detachSharedByteViews(child, seen);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
278
295
|
var AuthoringService = class {
|
|
279
296
|
sessionOperations = /* @__PURE__ */ new Map();
|
|
280
297
|
constructor(context, cacheService, eventService) {
|
|
@@ -299,7 +316,9 @@ var AuthoringService = class {
|
|
|
299
316
|
}
|
|
300
317
|
}
|
|
301
318
|
async applyTransaction(input) {
|
|
302
|
-
|
|
319
|
+
const queuedInput = structuredClone(input);
|
|
320
|
+
detachSharedByteViews(queuedInput);
|
|
321
|
+
return this.runSessionExclusive(queuedInput.sessionId, () => this.applyTransactionExclusive(queuedInput));
|
|
303
322
|
}
|
|
304
323
|
async applyTransactionExclusive(input) {
|
|
305
324
|
const startedAt = Date.now();
|
|
@@ -317,10 +336,11 @@ var AuthoringService = class {
|
|
|
317
336
|
revision: session.revision
|
|
318
337
|
});
|
|
319
338
|
}
|
|
320
|
-
const result =
|
|
339
|
+
const result = await applyUamTransactionAppAsync({
|
|
321
340
|
project: session.project,
|
|
322
341
|
operations: input.operations
|
|
323
342
|
});
|
|
343
|
+
if (this.context.sessions.get(input.sessionId) !== session || session.closed) return failure("authoring", startedAt, createSessionNotFoundError(input.sessionId));
|
|
324
344
|
if (result.ok === false) {
|
|
325
345
|
const diagnostics = toBackendDiagnostics(result.error);
|
|
326
346
|
this.eventService.emit({
|
|
@@ -411,12 +431,14 @@ var AuthoringService = class {
|
|
|
411
431
|
fairyPath: session.fairyPath,
|
|
412
432
|
staleSourceFiles: [...session.pendingStaleSourceFiles.values()],
|
|
413
433
|
staleResourceFolders: [...session.pendingStaleResourceFolders.values()],
|
|
434
|
+
staleBranchDirectories: [...session.pendingStaleBranchDirectories.values()],
|
|
414
435
|
writtenPaths: committedPaths,
|
|
415
436
|
failedPaths
|
|
416
437
|
});
|
|
417
438
|
session.fileSystem ??= fileSystem;
|
|
418
439
|
session.pendingStaleSourceFiles.clear();
|
|
419
440
|
session.pendingStaleResourceFolders.clear();
|
|
441
|
+
session.pendingStaleBranchDirectories.clear();
|
|
420
442
|
commitUamProjectSourcePaths(session.project);
|
|
421
443
|
session.lastSavedRevision = session.revision;
|
|
422
444
|
session.dirty = false;
|
|
@@ -555,16 +577,19 @@ var AuthoringService = class {
|
|
|
555
577
|
fairyPath,
|
|
556
578
|
staleSourceFiles: isSessionStorageTarget ? [...session.pendingStaleSourceFiles.values()] : [],
|
|
557
579
|
staleResourceFolders: isSessionStorageTarget ? [...session.pendingStaleResourceFolders.values()] : [],
|
|
580
|
+
staleBranchDirectories: isSessionStorageTarget ? [...session.pendingStaleBranchDirectories.values()] : [],
|
|
558
581
|
writtenPaths,
|
|
559
582
|
failedPaths
|
|
560
583
|
});
|
|
561
584
|
if (isSessionStorageTarget) {
|
|
562
585
|
session.pendingStaleSourceFiles.clear();
|
|
563
586
|
session.pendingStaleResourceFolders.clear();
|
|
587
|
+
session.pendingStaleBranchDirectories.clear();
|
|
564
588
|
}
|
|
565
589
|
if (storageTarget && !isSessionStorageTarget) {
|
|
566
590
|
session.pendingStaleSourceFiles.clear();
|
|
567
591
|
session.pendingStaleResourceFolders.clear();
|
|
592
|
+
session.pendingStaleBranchDirectories.clear();
|
|
568
593
|
}
|
|
569
594
|
if (isSessionStorageTarget || storageTarget) commitUamProjectSourcePaths(session.project);
|
|
570
595
|
if (storageTarget) {
|
|
@@ -1213,6 +1238,7 @@ var RuntimeService = class {
|
|
|
1213
1238
|
lastSavedRevision: 0,
|
|
1214
1239
|
pendingStaleSourceFiles: /* @__PURE__ */ new Map(),
|
|
1215
1240
|
pendingStaleResourceFolders: /* @__PURE__ */ new Map(),
|
|
1241
|
+
pendingStaleBranchDirectories: /* @__PURE__ */ new Map(),
|
|
1216
1242
|
dirty: false,
|
|
1217
1243
|
lockHeld: true,
|
|
1218
1244
|
closed: false
|
|
@@ -1273,6 +1299,7 @@ var RuntimeService = class {
|
|
|
1273
1299
|
lastSavedRevision: 0,
|
|
1274
1300
|
pendingStaleSourceFiles: /* @__PURE__ */ new Map(),
|
|
1275
1301
|
pendingStaleResourceFolders: /* @__PURE__ */ new Map(),
|
|
1302
|
+
pendingStaleBranchDirectories: /* @__PURE__ */ new Map(),
|
|
1276
1303
|
dirty: false,
|
|
1277
1304
|
lockHeld: false,
|
|
1278
1305
|
closed: false
|
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.35",
|
|
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.35",
|
|
56
|
+
"@openfairygui/functions": "0.2.0-alpha.35"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
59
|
"ava": "^7.0.0",
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import type { ProjectResourceFolder, ProjectSourceFile } from '@openfairygui/core/project-io';
|
|
1
|
+
import type { ProjectBranchDirectory, ProjectResourceFolder, ProjectSourceFile } from '@openfairygui/core/project-io';
|
|
2
2
|
import {
|
|
3
3
|
commitUamProjectSourcePaths,
|
|
4
4
|
materializeUamProject,
|
|
5
|
+
staleBranchDirectories,
|
|
5
6
|
staleResourceFolders,
|
|
6
7
|
staleSourceFiles,
|
|
7
8
|
type UamProject,
|
|
8
9
|
validateUamProject,
|
|
9
10
|
} from '@openfairygui/core/uam';
|
|
10
|
-
import { type ApplyUamTransactionAppError,
|
|
11
|
+
import { type ApplyUamTransactionAppError, applyUamTransactionAppAsync } from '@openfairygui/functions/uam';
|
|
11
12
|
import type { BackendDiagnostic } from '../contracts.js';
|
|
12
13
|
import { normalizeComparablePath, type PathPolicyViolationError, validateSaveTarget } from '../path-policy.js';
|
|
13
14
|
import type {
|
|
@@ -41,6 +42,10 @@ function resourceFolderKey(folder: ProjectResourceFolder): string {
|
|
|
41
42
|
return [folder.branch, folder.packageName, folder.path].join('\0');
|
|
42
43
|
}
|
|
43
44
|
|
|
45
|
+
function branchDirectoryKey(directory: ProjectBranchDirectory): string {
|
|
46
|
+
return [directory.branch, directory.packageName ?? ''].join('\0');
|
|
47
|
+
}
|
|
48
|
+
|
|
44
49
|
function recordStaleProjectFiles(
|
|
45
50
|
session: Parameters<typeof toSessionSnapshot>[0],
|
|
46
51
|
previousProject: UamProject,
|
|
@@ -59,6 +64,12 @@ function recordStaleProjectFiles(
|
|
|
59
64
|
for (const folder of staleResourceFolders(nextProject, previousProject)) {
|
|
60
65
|
session.pendingStaleResourceFolders.delete(resourceFolderKey(folder));
|
|
61
66
|
}
|
|
67
|
+
for (const directory of staleBranchDirectories(previousProject, nextProject)) {
|
|
68
|
+
session.pendingStaleBranchDirectories.set(branchDirectoryKey(directory), directory);
|
|
69
|
+
}
|
|
70
|
+
for (const directory of staleBranchDirectories(nextProject, previousProject)) {
|
|
71
|
+
session.pendingStaleBranchDirectories.delete(branchDirectoryKey(directory));
|
|
72
|
+
}
|
|
62
73
|
}
|
|
63
74
|
|
|
64
75
|
function toBackendDiagnostics(error: ApplyUamTransactionAppError): BackendDiagnostic[] {
|
|
@@ -144,6 +155,20 @@ function storageCanonicalTarget(input: NonNullable<MaterializeSessionInput['stor
|
|
|
144
155
|
};
|
|
145
156
|
}
|
|
146
157
|
|
|
158
|
+
function detachSharedByteViews(value: unknown, seen = new WeakSet<object>()): void {
|
|
159
|
+
if (!value || typeof value !== 'object' || seen.has(value)) return;
|
|
160
|
+
seen.add(value);
|
|
161
|
+
for (const [key, child] of Object.entries(value)) {
|
|
162
|
+
if (child instanceof Uint8Array) {
|
|
163
|
+
if (typeof SharedArrayBuffer !== 'undefined' && child.buffer instanceof SharedArrayBuffer) {
|
|
164
|
+
(value as Record<string, unknown>)[key] = new Uint8Array(child);
|
|
165
|
+
}
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
detachSharedByteViews(child, seen);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
147
172
|
export class AuthoringService {
|
|
148
173
|
private readonly sessionOperations = new Map<string, Promise<void>>();
|
|
149
174
|
|
|
@@ -178,7 +203,9 @@ export class AuthoringService {
|
|
|
178
203
|
SessionNotFoundError | SessionStaleWriteError | ApplyUamTransactionAppError
|
|
179
204
|
>
|
|
180
205
|
> {
|
|
181
|
-
|
|
206
|
+
const queuedInput = structuredClone(input);
|
|
207
|
+
detachSharedByteViews(queuedInput);
|
|
208
|
+
return this.runSessionExclusive(queuedInput.sessionId, () => this.applyTransactionExclusive(queuedInput));
|
|
182
209
|
}
|
|
183
210
|
|
|
184
211
|
private async applyTransactionExclusive(
|
|
@@ -213,10 +240,13 @@ export class AuthoringService {
|
|
|
213
240
|
);
|
|
214
241
|
}
|
|
215
242
|
|
|
216
|
-
const result =
|
|
243
|
+
const result = await applyUamTransactionAppAsync({
|
|
217
244
|
project: session.project,
|
|
218
245
|
operations: input.operations,
|
|
219
246
|
});
|
|
247
|
+
if (this.context.sessions.get(input.sessionId) !== session || session.closed) {
|
|
248
|
+
return failure('authoring', startedAt, createSessionNotFoundError(input.sessionId));
|
|
249
|
+
}
|
|
220
250
|
if (result.ok === false) {
|
|
221
251
|
const diagnostics = toBackendDiagnostics(result.error);
|
|
222
252
|
this.eventService.emit({
|
|
@@ -386,12 +416,14 @@ export class AuthoringService {
|
|
|
386
416
|
fairyPath: session.fairyPath,
|
|
387
417
|
staleSourceFiles: [...session.pendingStaleSourceFiles.values()],
|
|
388
418
|
staleResourceFolders: [...session.pendingStaleResourceFolders.values()],
|
|
419
|
+
staleBranchDirectories: [...session.pendingStaleBranchDirectories.values()],
|
|
389
420
|
writtenPaths: committedPaths,
|
|
390
421
|
failedPaths,
|
|
391
422
|
});
|
|
392
423
|
session.fileSystem ??= fileSystem;
|
|
393
424
|
session.pendingStaleSourceFiles.clear();
|
|
394
425
|
session.pendingStaleResourceFolders.clear();
|
|
426
|
+
session.pendingStaleBranchDirectories.clear();
|
|
395
427
|
commitUamProjectSourcePaths(session.project);
|
|
396
428
|
session.lastSavedRevision = session.revision;
|
|
397
429
|
session.dirty = false;
|
|
@@ -627,16 +659,19 @@ export class AuthoringService {
|
|
|
627
659
|
fairyPath,
|
|
628
660
|
staleSourceFiles: isSessionStorageTarget ? [...session.pendingStaleSourceFiles.values()] : [],
|
|
629
661
|
staleResourceFolders: isSessionStorageTarget ? [...session.pendingStaleResourceFolders.values()] : [],
|
|
662
|
+
staleBranchDirectories: isSessionStorageTarget ? [...session.pendingStaleBranchDirectories.values()] : [],
|
|
630
663
|
writtenPaths,
|
|
631
664
|
failedPaths,
|
|
632
665
|
});
|
|
633
666
|
if (isSessionStorageTarget) {
|
|
634
667
|
session.pendingStaleSourceFiles.clear();
|
|
635
668
|
session.pendingStaleResourceFolders.clear();
|
|
669
|
+
session.pendingStaleBranchDirectories.clear();
|
|
636
670
|
}
|
|
637
671
|
if (storageTarget && !isSessionStorageTarget) {
|
|
638
672
|
session.pendingStaleSourceFiles.clear();
|
|
639
673
|
session.pendingStaleResourceFolders.clear();
|
|
674
|
+
session.pendingStaleBranchDirectories.clear();
|
|
640
675
|
}
|
|
641
676
|
if (isSessionStorageTarget || storageTarget) commitUamProjectSourcePaths(session.project);
|
|
642
677
|
if (storageTarget) {
|
package/src/services/context.ts
CHANGED
|
@@ -34,6 +34,8 @@ export interface BackendSessionState {
|
|
|
34
34
|
pendingStaleSourceFiles: Map<string, import('@openfairygui/core/project-io').ProjectSourceFile>;
|
|
35
35
|
/** Empty resource directories deferred until a successful replacement project write. */
|
|
36
36
|
pendingStaleResourceFolders: Map<string, import('@openfairygui/core/project-io').ProjectResourceFolder>;
|
|
37
|
+
/** Removed package-branch and root-branch directories deferred until controlled files are replaced. */
|
|
38
|
+
pendingStaleBranchDirectories: Map<string, import('@openfairygui/core/project-io').ProjectBranchDirectory>;
|
|
37
39
|
dirty: boolean;
|
|
38
40
|
lockHeld: boolean;
|
|
39
41
|
closed: boolean;
|
|
@@ -238,6 +238,7 @@ export class RuntimeService {
|
|
|
238
238
|
lastSavedRevision: 0,
|
|
239
239
|
pendingStaleSourceFiles: new Map(),
|
|
240
240
|
pendingStaleResourceFolders: new Map(),
|
|
241
|
+
pendingStaleBranchDirectories: new Map(),
|
|
241
242
|
dirty: false,
|
|
242
243
|
lockHeld: true,
|
|
243
244
|
closed: false,
|
|
@@ -306,6 +307,7 @@ export class RuntimeService {
|
|
|
306
307
|
lastSavedRevision: 0,
|
|
307
308
|
pendingStaleSourceFiles: new Map(),
|
|
308
309
|
pendingStaleResourceFolders: new Map(),
|
|
310
|
+
pendingStaleBranchDirectories: new Map(),
|
|
309
311
|
dirty: false,
|
|
310
312
|
lockHeld: false,
|
|
311
313
|
closed: false,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Document } from '@openfairygui/core';
|
|
2
|
-
import { type FileSystem, type ProjectSourceFile, ProjectWriter } from '@openfairygui/core/project-io';
|
|
2
|
+
import { type FileSystem, type ProjectBranchDirectory, type ProjectSourceFile, ProjectWriter } from '@openfairygui/core/project-io';
|
|
3
3
|
import type { BackendFileSystem } from '../runtime.js';
|
|
4
4
|
|
|
5
5
|
function createWriterFileSystem(
|
|
@@ -54,6 +54,7 @@ export async function writeSessionProject(input: {
|
|
|
54
54
|
fairyPath: string;
|
|
55
55
|
staleSourceFiles: ProjectSourceFile[];
|
|
56
56
|
staleResourceFolders: import('@openfairygui/core/project-io').ProjectResourceFolder[];
|
|
57
|
+
staleBranchDirectories: ProjectBranchDirectory[];
|
|
57
58
|
writtenPaths: string[];
|
|
58
59
|
failedPaths: string[];
|
|
59
60
|
}): Promise<void> {
|
|
@@ -63,5 +64,6 @@ export async function writeSessionProject(input: {
|
|
|
63
64
|
await writer.write(input.document, input.fairyPath, {
|
|
64
65
|
staleSourceFiles: input.staleSourceFiles,
|
|
65
66
|
staleResourceFolders: input.staleResourceFolders,
|
|
67
|
+
staleBranchDirectories: input.staleBranchDirectories,
|
|
66
68
|
});
|
|
67
69
|
}
|