@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 CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_runtime = require("./runtime-DtcaaVfy.cjs");
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-BDkZmTFN.mjs";
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-DtcaaVfy.cjs");
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
@@ -1,4 +1,4 @@
1
- import { t as BackendRuntime } from "./runtime-BDkZmTFN.mjs";
1
+ import { t as BackendRuntime } from "./runtime-DtCn0Y44.mjs";
2
2
  import fs from "node:fs/promises";
3
3
  import path from "node:path";
4
4
  //#region src/node.ts
@@ -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
- return this.runSessionExclusive(input.sessionId, () => this.applyTransactionExclusive(input));
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.applyUamTransactionApp)({
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 { applyUamTransactionApp } from "@openfairygui/functions/uam";
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
- return this.runSessionExclusive(input.sessionId, () => this.applyTransactionExclusive(input));
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 = applyUamTransactionApp({
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.33",
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.33",
56
- "@openfairygui/functions": "0.2.0-alpha.33"
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, applyUamTransactionApp } from '@openfairygui/functions/uam';
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
- return this.runSessionExclusive(input.sessionId, () => this.applyTransactionExclusive(input));
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 = applyUamTransactionApp({
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) {
@@ -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
  }