@openfairygui/backend 0.2.0-alpha.13 → 0.2.0-alpha.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -34,7 +34,14 @@ It does **not** redefine transaction grammar or expose `Document`.
34
34
  It also does **not** implement MCP or any transport-specific wire protocol.
35
35
  The root `@openfairygui/backend` entrypoint is browser-safe: pure authoring sessions can run in memory,
36
36
  and browser editors can inject an async storage adapter for OPFS, IndexedDB, ZIP-backed virtual filesystems,
37
- or File System Access API bridges. The default Node filesystem/runtime lives under `@openfairygui/backend/node`.
37
+ or File System Access API bridges. Storage adapters must implement `unlink()` so resource rename/move/remove
38
+ can clean up stale source files. The default Node filesystem/runtime lives under `@openfairygui/backend/node`.
39
+ File-backed `openSession` hydrates primary resource bytes so browser-safe transactions can rename/move
40
+ assets or add/replace/remove binary resources. `saveSession` writes replacement bytes before it removes
41
+ stale source files, preserving the prior file when a write fails.
42
+ It also compares the source project with a UAM round trip through `ProjectWriter`; sessions with
43
+ unrepresented persisted properties expose `uamFidelity: 'unsupported'`, and write attempts fail with
44
+ `uam_fidelity_unsupported`. Transactions, saves, and materialization are serialized per session.
38
45
 
39
46
  ## Relationship to other packages
40
47
 
@@ -73,6 +80,7 @@ const fileSystem = createBackendStorageFileSystem({
73
80
  async mkdir(dirPath) { await storage.mkdir(dirPath); },
74
81
  async readdir(dirPath) { return storage.readdir(dirPath); },
75
82
  async exists(filePath) { return storage.exists(filePath); },
83
+ async unlink(filePath) { await storage.remove(filePath); },
76
84
  });
77
85
 
78
86
  const runtime = new BackendRuntime();
package/dist/index.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_runtime = require("./runtime-GKzsXJdO.cjs");
2
+ const require_runtime = require("./runtime-C5N7GLsG.cjs");
3
3
  //#region src/storage.ts
4
4
  var StorageFileStat = class {
5
5
  constructor(kind) {
@@ -71,6 +71,7 @@ async function inferStat(storage, filePath) {
71
71
  }
72
72
  }
73
73
  function createBackendStorageFileSystem(storage) {
74
+ if (typeof storage.unlink !== "function") throw createPathError("ENOTSUP", "Storage adapter must provide unlink() for project resource lifecycle writes.");
74
75
  const lockedPaths = /* @__PURE__ */ new Set();
75
76
  const fileSystem = {
76
77
  stat(filePath) {
@@ -128,8 +129,7 @@ function createBackendStorageFileSystem(storage) {
128
129
  unlink(filePath) {
129
130
  const resolved = fileSystem.resolve(filePath);
130
131
  lockedPaths.delete(resolved);
131
- if (storage.unlink) return storage.unlink(resolved);
132
- throw createPathError("ENOTSUP", "Storage adapter does not provide unlink().");
132
+ return storage.unlink(resolved);
133
133
  },
134
134
  join(...paths) {
135
135
  return storage.join ? storage.join(...paths) : joinStoragePath(...paths);
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { $ as BACKEND_CONTRACT_VERSION, A as BackendSessionSnapshot, B as ListJobsInput, C as BackendJobProgress, D as BackendResult, E as BackendProjectSessionStorage, F as GetCacheSnapshotInput, G as OpenProjectSessionInput, H as MaterializeSessionSnapshot, I as GetEventsInput, J as SaveSessionInput, K as RefreshCacheInput, L as GetEventsSnapshot, M as CacheRefreshFailedError, N as CancelJobInput, O as BackendRuntime, P as EventCursorInvalidError, Q as BACKEND_COMPATIBILITY_POLICY, R as GetJobInput, S as BackendJobNotFoundError, T as BackendJobStatus, U as MaterializeValidationFailedError, V as MaterializeSessionInput, W as MaterializeWriteFailedError, X as SessionStaleWriteError, Y as SessionNotFoundError, Z as BACKEND_CAPABILITY_SCHEMA_VERSION, _ as BackendJobErrors, a as BackendCacheSnapshot, b as BackendJobListStatusFilter, c as BackendCapabilityUnavailableError, d as BackendEventKind, et as BackendDiagnostic, f as BackendFailure, g as BackendHostAdapter, h as BackendFileSystem, i as BackendCacheEntry, j as BackendSuccess, k as BackendRuntimeOptions, l as BackendError, m as BackendFileStat, n as ApplySessionTransactionInput, nt as BackendResponseMeta, o as BackendCapabilities, p as BackendFileHandle, q as SavePartialFailureError, r as BackendArtifactBridgeCapability, rt as BackendStage, s as BackendCapabilityManifest, t as AdvisoryLockConflictError, tt as BackendMessage, u as BackendEvent, v as BackendJobKind, w as BackendJobSnapshot, x as BackendJobNotCancellableError, y as BackendJobListSnapshot, z as InProcessLockConflictError } from "./runtime-Jec6FcF5.cjs";
1
+ import { $ as BACKEND_COMPATIBILITY_POLICY, A as BackendSessionSnapshot, B as ListJobsInput, C as BackendJobProgress, D as BackendResult, E as BackendProjectSessionStorage, F as GetCacheSnapshotInput, G as OpenProjectSessionInput, H as MaterializeSessionSnapshot, I as GetEventsInput, J as SaveSessionInput, K as RefreshCacheInput, L as GetEventsSnapshot, M as CacheRefreshFailedError, N as CancelJobInput, O as BackendRuntime, P as EventCursorInvalidError, Q as BACKEND_CAPABILITY_SCHEMA_VERSION, R as GetJobInput, S as BackendJobNotFoundError, T as BackendJobStatus, U as MaterializeValidationFailedError, V as MaterializeSessionInput, W as MaterializeWriteFailedError, X as SessionStaleWriteError, Y as SessionNotFoundError, Z as UamFidelityUnsupportedError, _ as BackendJobErrors, a as BackendCacheSnapshot, b as BackendJobListStatusFilter, c as BackendCapabilityUnavailableError, d as BackendEventKind, et as BACKEND_CONTRACT_VERSION, f as BackendFailure, g as BackendHostAdapter, h as BackendFileSystem, i as BackendCacheEntry, it as BackendStage, j as BackendSuccess, k as BackendRuntimeOptions, l as BackendError, m as BackendFileStat, n as ApplySessionTransactionInput, nt as BackendMessage, o as BackendCapabilities, p as BackendFileHandle, q as SavePartialFailureError, r as BackendArtifactBridgeCapability, rt as BackendResponseMeta, s as BackendCapabilityManifest, t as AdvisoryLockConflictError, tt as BackendDiagnostic, u as BackendEvent, v as BackendJobKind, w as BackendJobSnapshot, x as BackendJobNotCancellableError, y as BackendJobListSnapshot, z as InProcessLockConflictError } from "./runtime-B6UjcyIt.cjs";
2
2
  import { FileSystem } from "@openfairygui/core/project-io";
3
3
 
4
4
  //#region src/storage.d.ts
@@ -22,7 +22,7 @@ interface BackendAsyncStorageAdapter {
22
22
  stat?(filePath: string): Promise<BackendStorageStatLike>;
23
23
  resolvePath?(filePath: string): Promise<string>;
24
24
  openExclusive?(filePath: string): Promise<BackendFileHandle>;
25
- unlink?(filePath: string): Promise<void>;
25
+ unlink(filePath: string): Promise<void>;
26
26
  join?(...paths: string[]): string;
27
27
  dirname?(filePath: string): string;
28
28
  resolve?(...paths: string[]): string;
@@ -30,4 +30,4 @@ interface BackendAsyncStorageAdapter {
30
30
  type BackendStorageFileSystem = BackendFileSystem & FileSystem;
31
31
  declare function createBackendStorageFileSystem(storage: BackendAsyncStorageAdapter): BackendStorageFileSystem;
32
32
  //#endregion
33
- export { type AdvisoryLockConflictError, type ApplySessionTransactionInput, BACKEND_CAPABILITY_SCHEMA_VERSION, BACKEND_COMPATIBILITY_POLICY, BACKEND_CONTRACT_VERSION, type BackendArtifactBridgeCapability, type BackendAsyncStorageAdapter, type BackendCacheEntry, type BackendCacheSnapshot, type BackendCapabilities, type BackendCapabilityManifest, type BackendCapabilityUnavailableError, type BackendDiagnostic, type BackendError, type BackendEvent, type BackendEventKind, type BackendFailure, type BackendFileHandle, type BackendFileStat, type BackendFileSystem, type BackendHostAdapter, type BackendJobErrors, type BackendJobKind, type BackendJobListSnapshot, type BackendJobListStatusFilter, type BackendJobNotCancellableError, type BackendJobNotFoundError, type BackendJobProgress, type BackendJobSnapshot, type BackendJobStatus, type BackendMessage, type BackendProjectSessionStorage, type BackendResponseMeta, type BackendResult, BackendRuntime, type BackendRuntimeOptions, type BackendSessionSnapshot, type BackendStage, type BackendStorageFileSystem, type BackendStorageStatLike, type BackendSuccess, type CacheRefreshFailedError, type CancelJobInput, type EventCursorInvalidError, type GetCacheSnapshotInput, type GetEventsInput, type GetEventsSnapshot, type GetJobInput, type InProcessLockConflictError, type ListJobsInput, type MaterializeSessionInput, type MaterializeSessionSnapshot, type MaterializeValidationFailedError, type MaterializeWriteFailedError, type OpenProjectSessionInput, type RefreshCacheInput, type SavePartialFailureError, type SaveSessionInput, type SessionNotFoundError, type SessionStaleWriteError, createBackendStorageFileSystem };
33
+ export { type AdvisoryLockConflictError, type ApplySessionTransactionInput, BACKEND_CAPABILITY_SCHEMA_VERSION, BACKEND_COMPATIBILITY_POLICY, BACKEND_CONTRACT_VERSION, type BackendArtifactBridgeCapability, type BackendAsyncStorageAdapter, type BackendCacheEntry, type BackendCacheSnapshot, type BackendCapabilities, type BackendCapabilityManifest, type BackendCapabilityUnavailableError, type BackendDiagnostic, type BackendError, type BackendEvent, type BackendEventKind, type BackendFailure, type BackendFileHandle, type BackendFileStat, type BackendFileSystem, type BackendHostAdapter, type BackendJobErrors, type BackendJobKind, type BackendJobListSnapshot, type BackendJobListStatusFilter, type BackendJobNotCancellableError, type BackendJobNotFoundError, type BackendJobProgress, type BackendJobSnapshot, type BackendJobStatus, type BackendMessage, type BackendProjectSessionStorage, type BackendResponseMeta, type BackendResult, BackendRuntime, type BackendRuntimeOptions, type BackendSessionSnapshot, type BackendStage, type BackendStorageFileSystem, type BackendStorageStatLike, type BackendSuccess, type CacheRefreshFailedError, type CancelJobInput, type EventCursorInvalidError, type GetCacheSnapshotInput, type GetEventsInput, type GetEventsSnapshot, type GetJobInput, type InProcessLockConflictError, type ListJobsInput, type MaterializeSessionInput, type MaterializeSessionSnapshot, type MaterializeValidationFailedError, type MaterializeWriteFailedError, type OpenProjectSessionInput, type RefreshCacheInput, type SavePartialFailureError, type SaveSessionInput, type SessionNotFoundError, type SessionStaleWriteError, type UamFidelityUnsupportedError, createBackendStorageFileSystem };
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { $ as BACKEND_CONTRACT_VERSION, A as BackendSessionSnapshot, B as ListJobsInput, C as BackendJobProgress, D as BackendResult, E as BackendProjectSessionStorage, F as GetCacheSnapshotInput, G as OpenProjectSessionInput, H as MaterializeSessionSnapshot, I as GetEventsInput, J as SaveSessionInput, K as RefreshCacheInput, L as GetEventsSnapshot, M as CacheRefreshFailedError, N as CancelJobInput, O as BackendRuntime, P as EventCursorInvalidError, Q as BACKEND_COMPATIBILITY_POLICY, R as GetJobInput, S as BackendJobNotFoundError, T as BackendJobStatus, U as MaterializeValidationFailedError, V as MaterializeSessionInput, W as MaterializeWriteFailedError, X as SessionStaleWriteError, Y as SessionNotFoundError, Z as BACKEND_CAPABILITY_SCHEMA_VERSION, _ as BackendJobErrors, a as BackendCacheSnapshot, b as BackendJobListStatusFilter, c as BackendCapabilityUnavailableError, d as BackendEventKind, et as BackendDiagnostic, f as BackendFailure, g as BackendHostAdapter, h as BackendFileSystem, i as BackendCacheEntry, j as BackendSuccess, k as BackendRuntimeOptions, l as BackendError, m as BackendFileStat, n as ApplySessionTransactionInput, nt as BackendResponseMeta, o as BackendCapabilities, p as BackendFileHandle, q as SavePartialFailureError, r as BackendArtifactBridgeCapability, rt as BackendStage, s as BackendCapabilityManifest, t as AdvisoryLockConflictError, tt as BackendMessage, u as BackendEvent, v as BackendJobKind, w as BackendJobSnapshot, x as BackendJobNotCancellableError, y as BackendJobListSnapshot, z as InProcessLockConflictError } from "./runtime-GyNVxAQ0.mjs";
1
+ import { $ as BACKEND_COMPATIBILITY_POLICY, A as BackendSessionSnapshot, B as ListJobsInput, C as BackendJobProgress, D as BackendResult, E as BackendProjectSessionStorage, F as GetCacheSnapshotInput, G as OpenProjectSessionInput, H as MaterializeSessionSnapshot, I as GetEventsInput, J as SaveSessionInput, K as RefreshCacheInput, L as GetEventsSnapshot, M as CacheRefreshFailedError, N as CancelJobInput, O as BackendRuntime, P as EventCursorInvalidError, Q as BACKEND_CAPABILITY_SCHEMA_VERSION, R as GetJobInput, S as BackendJobNotFoundError, T as BackendJobStatus, U as MaterializeValidationFailedError, V as MaterializeSessionInput, W as MaterializeWriteFailedError, X as SessionStaleWriteError, Y as SessionNotFoundError, Z as UamFidelityUnsupportedError, _ as BackendJobErrors, a as BackendCacheSnapshot, b as BackendJobListStatusFilter, c as BackendCapabilityUnavailableError, d as BackendEventKind, et as BACKEND_CONTRACT_VERSION, f as BackendFailure, g as BackendHostAdapter, h as BackendFileSystem, i as BackendCacheEntry, it as BackendStage, j as BackendSuccess, k as BackendRuntimeOptions, l as BackendError, m as BackendFileStat, n as ApplySessionTransactionInput, nt as BackendMessage, o as BackendCapabilities, p as BackendFileHandle, q as SavePartialFailureError, r as BackendArtifactBridgeCapability, rt as BackendResponseMeta, s as BackendCapabilityManifest, t as AdvisoryLockConflictError, tt as BackendDiagnostic, u as BackendEvent, v as BackendJobKind, w as BackendJobSnapshot, x as BackendJobNotCancellableError, y as BackendJobListSnapshot, z as InProcessLockConflictError } from "./runtime-BihiQW-H.mjs";
2
2
  import { FileSystem } from "@openfairygui/core/project-io";
3
3
 
4
4
  //#region src/storage.d.ts
@@ -22,7 +22,7 @@ interface BackendAsyncStorageAdapter {
22
22
  stat?(filePath: string): Promise<BackendStorageStatLike>;
23
23
  resolvePath?(filePath: string): Promise<string>;
24
24
  openExclusive?(filePath: string): Promise<BackendFileHandle>;
25
- unlink?(filePath: string): Promise<void>;
25
+ unlink(filePath: string): Promise<void>;
26
26
  join?(...paths: string[]): string;
27
27
  dirname?(filePath: string): string;
28
28
  resolve?(...paths: string[]): string;
@@ -30,4 +30,4 @@ interface BackendAsyncStorageAdapter {
30
30
  type BackendStorageFileSystem = BackendFileSystem & FileSystem;
31
31
  declare function createBackendStorageFileSystem(storage: BackendAsyncStorageAdapter): BackendStorageFileSystem;
32
32
  //#endregion
33
- export { type AdvisoryLockConflictError, type ApplySessionTransactionInput, BACKEND_CAPABILITY_SCHEMA_VERSION, BACKEND_COMPATIBILITY_POLICY, BACKEND_CONTRACT_VERSION, type BackendArtifactBridgeCapability, type BackendAsyncStorageAdapter, type BackendCacheEntry, type BackendCacheSnapshot, type BackendCapabilities, type BackendCapabilityManifest, type BackendCapabilityUnavailableError, type BackendDiagnostic, type BackendError, type BackendEvent, type BackendEventKind, type BackendFailure, type BackendFileHandle, type BackendFileStat, type BackendFileSystem, type BackendHostAdapter, type BackendJobErrors, type BackendJobKind, type BackendJobListSnapshot, type BackendJobListStatusFilter, type BackendJobNotCancellableError, type BackendJobNotFoundError, type BackendJobProgress, type BackendJobSnapshot, type BackendJobStatus, type BackendMessage, type BackendProjectSessionStorage, type BackendResponseMeta, type BackendResult, BackendRuntime, type BackendRuntimeOptions, type BackendSessionSnapshot, type BackendStage, type BackendStorageFileSystem, type BackendStorageStatLike, type BackendSuccess, type CacheRefreshFailedError, type CancelJobInput, type EventCursorInvalidError, type GetCacheSnapshotInput, type GetEventsInput, type GetEventsSnapshot, type GetJobInput, type InProcessLockConflictError, type ListJobsInput, type MaterializeSessionInput, type MaterializeSessionSnapshot, type MaterializeValidationFailedError, type MaterializeWriteFailedError, type OpenProjectSessionInput, type RefreshCacheInput, type SavePartialFailureError, type SaveSessionInput, type SessionNotFoundError, type SessionStaleWriteError, createBackendStorageFileSystem };
33
+ export { type AdvisoryLockConflictError, type ApplySessionTransactionInput, BACKEND_CAPABILITY_SCHEMA_VERSION, BACKEND_COMPATIBILITY_POLICY, BACKEND_CONTRACT_VERSION, type BackendArtifactBridgeCapability, type BackendAsyncStorageAdapter, type BackendCacheEntry, type BackendCacheSnapshot, type BackendCapabilities, type BackendCapabilityManifest, type BackendCapabilityUnavailableError, type BackendDiagnostic, type BackendError, type BackendEvent, type BackendEventKind, type BackendFailure, type BackendFileHandle, type BackendFileStat, type BackendFileSystem, type BackendHostAdapter, type BackendJobErrors, type BackendJobKind, type BackendJobListSnapshot, type BackendJobListStatusFilter, type BackendJobNotCancellableError, type BackendJobNotFoundError, type BackendJobProgress, type BackendJobSnapshot, type BackendJobStatus, type BackendMessage, type BackendProjectSessionStorage, type BackendResponseMeta, type BackendResult, BackendRuntime, type BackendRuntimeOptions, type BackendSessionSnapshot, type BackendStage, type BackendStorageFileSystem, type BackendStorageStatLike, type BackendSuccess, type CacheRefreshFailedError, type CancelJobInput, type EventCursorInvalidError, type GetCacheSnapshotInput, type GetEventsInput, type GetEventsSnapshot, type GetJobInput, type InProcessLockConflictError, type ListJobsInput, type MaterializeSessionInput, type MaterializeSessionSnapshot, type MaterializeValidationFailedError, type MaterializeWriteFailedError, type OpenProjectSessionInput, type RefreshCacheInput, type SavePartialFailureError, type SaveSessionInput, type SessionNotFoundError, type SessionStaleWriteError, type UamFidelityUnsupportedError, createBackendStorageFileSystem };
package/dist/index.mjs CHANGED
@@ -1,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-DFatY9W0.mjs";
1
+ import { i as BACKEND_CONTRACT_VERSION, n as BACKEND_CAPABILITY_SCHEMA_VERSION, r as BACKEND_COMPATIBILITY_POLICY, t as BackendRuntime } from "./runtime-CxH7aT_v.mjs";
2
2
  //#region src/storage.ts
3
3
  var StorageFileStat = class {
4
4
  constructor(kind) {
@@ -70,6 +70,7 @@ async function inferStat(storage, filePath) {
70
70
  }
71
71
  }
72
72
  function createBackendStorageFileSystem(storage) {
73
+ if (typeof storage.unlink !== "function") throw createPathError("ENOTSUP", "Storage adapter must provide unlink() for project resource lifecycle writes.");
73
74
  const lockedPaths = /* @__PURE__ */ new Set();
74
75
  const fileSystem = {
75
76
  stat(filePath) {
@@ -127,8 +128,7 @@ function createBackendStorageFileSystem(storage) {
127
128
  unlink(filePath) {
128
129
  const resolved = fileSystem.resolve(filePath);
129
130
  lockedPaths.delete(resolved);
130
- if (storage.unlink) return storage.unlink(resolved);
131
- throw createPathError("ENOTSUP", "Storage adapter does not provide unlink().");
131
+ return storage.unlink(resolved);
132
132
  },
133
133
  join(...paths) {
134
134
  return storage.join ? storage.join(...paths) : joinStoragePath(...paths);
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-GKzsXJdO.cjs");
24
+ const require_runtime = require("./runtime-C5N7GLsG.cjs");
25
25
  let node_fs_promises = require("node:fs/promises");
26
26
  node_fs_promises = __toESM(node_fs_promises);
27
27
  let node_path = require("node:path");
package/dist/node.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { O as BackendRuntime, g as BackendHostAdapter, h as BackendFileSystem, k as BackendRuntimeOptions, m as BackendFileStat, p as BackendFileHandle } from "./runtime-Jec6FcF5.cjs";
1
+ import { O as BackendRuntime, g as BackendHostAdapter, h as BackendFileSystem, k as BackendRuntimeOptions, m as BackendFileStat, p as BackendFileHandle } from "./runtime-B6UjcyIt.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 { O as BackendRuntime, g as BackendHostAdapter, h as BackendFileSystem, k as BackendRuntimeOptions, m as BackendFileStat, p as BackendFileHandle } from "./runtime-GyNVxAQ0.mjs";
1
+ import { O as BackendRuntime, g as BackendHostAdapter, h as BackendFileSystem, k as BackendRuntimeOptions, m as BackendFileStat, p as BackendFileHandle } from "./runtime-BihiQW-H.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-DFatY9W0.mjs";
1
+ import { t as BackendRuntime } from "./runtime-CxH7aT_v.mjs";
2
2
  import fs from "node:fs/promises";
3
3
  import path from "node:path";
4
4
  //#region src/node.ts
@@ -193,6 +193,7 @@ interface BackendSessionSnapshot {
193
193
  revision: number;
194
194
  lastSavedRevision: number;
195
195
  dirty: boolean;
196
+ uamFidelity: 'full' | 'unsupported';
196
197
  lockHeld: boolean;
197
198
  capabilities: BackendCapabilities;
198
199
  }
@@ -257,6 +258,12 @@ interface SavePartialFailureError {
257
258
  failedPaths: string[];
258
259
  diskMayBePartiallyUpdated: true;
259
260
  }
261
+ interface UamFidelityUnsupportedError {
262
+ code: 'uam_fidelity_unsupported';
263
+ message: string;
264
+ sessionId: string;
265
+ canonicalPathKey: string;
266
+ }
260
267
  interface MaterializeValidationFailedError {
261
268
  code: 'materialize_validation_failed';
262
269
  message: string;
@@ -409,7 +416,7 @@ interface RefreshCacheInput {
409
416
  sessionId: string;
410
417
  reason?: 'manual' | 'session_open' | 'after_save';
411
418
  }
412
- type BackendError = SessionNotFoundError | SessionStaleWriteError | InProcessLockConflictError | AdvisoryLockConflictError | SavePartialFailureError | MaterializeValidationFailedError | MaterializeWriteFailedError | PathPolicyViolationError | EventCursorInvalidError | BackendJobNotFoundError | BackendJobNotCancellableError | BackendJobCancelledError | CacheRefreshFailedError | BackendCapabilityUnavailableError | ApplyUamTransactionAppError;
419
+ type BackendError = SessionNotFoundError | SessionStaleWriteError | InProcessLockConflictError | AdvisoryLockConflictError | SavePartialFailureError | UamFidelityUnsupportedError | MaterializeValidationFailedError | MaterializeWriteFailedError | PathPolicyViolationError | EventCursorInvalidError | BackendJobNotFoundError | BackendJobNotCancellableError | BackendJobCancelledError | CacheRefreshFailedError | BackendCapabilityUnavailableError | ApplyUamTransactionAppError;
413
420
  interface ApplySessionTransactionInput {
414
421
  sessionId: string;
415
422
  expectedRevision: number;
@@ -475,8 +482,8 @@ declare class BackendRuntime {
475
482
  sessionId: string;
476
483
  }): BackendResult<BackendSessionSnapshot, SessionNotFoundError>;
477
484
  applyTransaction(input: ApplySessionTransactionInput): Promise<BackendResult<BackendSessionSnapshot, SessionNotFoundError | SessionStaleWriteError | ApplyUamTransactionAppError>>;
478
- saveSession(input: SaveSessionInput): Promise<BackendResult<BackendSessionSnapshot | MaterializeSessionSnapshot, SessionNotFoundError | SessionStaleWriteError | SavePartialFailureError | MaterializeValidationFailedError | MaterializeWriteFailedError | PathPolicyViolationError | InProcessLockConflictError | BackendCapabilityUnavailableError>>;
479
- materializeSession(input: MaterializeSessionInput): Promise<BackendResult<MaterializeSessionSnapshot, SessionNotFoundError | SessionStaleWriteError | MaterializeValidationFailedError | MaterializeWriteFailedError | PathPolicyViolationError | InProcessLockConflictError | BackendCapabilityUnavailableError>>;
485
+ saveSession(input: SaveSessionInput): Promise<BackendResult<BackendSessionSnapshot | MaterializeSessionSnapshot, SessionNotFoundError | SessionStaleWriteError | SavePartialFailureError | UamFidelityUnsupportedError | MaterializeValidationFailedError | MaterializeWriteFailedError | PathPolicyViolationError | InProcessLockConflictError | BackendCapabilityUnavailableError>>;
486
+ materializeSession(input: MaterializeSessionInput): Promise<BackendResult<MaterializeSessionSnapshot, SessionNotFoundError | SessionStaleWriteError | UamFidelityUnsupportedError | MaterializeValidationFailedError | MaterializeWriteFailedError | PathPolicyViolationError | InProcessLockConflictError | BackendCapabilityUnavailableError>>;
480
487
  closeSession(input: {
481
488
  sessionId: string;
482
489
  }): Promise<BackendResult<{
@@ -491,4 +498,4 @@ declare class BackendRuntime {
491
498
  refreshCache(input: RefreshCacheInput): BackendResult<BackendJobSnapshot, SessionNotFoundError>;
492
499
  }
493
500
  //#endregion
494
- export { BACKEND_CONTRACT_VERSION as $, BackendSessionSnapshot as A, ListJobsInput as B, BackendJobProgress as C, BackendResult as D, BackendProjectSessionStorage as E, GetCacheSnapshotInput as F, OpenProjectSessionInput as G, MaterializeSessionSnapshot as H, GetEventsInput as I, SaveSessionInput as J, RefreshCacheInput as K, GetEventsSnapshot as L, CacheRefreshFailedError as M, CancelJobInput as N, BackendRuntime as O, EventCursorInvalidError as P, BACKEND_COMPATIBILITY_POLICY as Q, GetJobInput as R, BackendJobNotFoundError as S, BackendJobStatus as T, MaterializeValidationFailedError as U, MaterializeSessionInput as V, MaterializeWriteFailedError as W, SessionStaleWriteError as X, SessionNotFoundError as Y, BACKEND_CAPABILITY_SCHEMA_VERSION as Z, BackendJobErrors as _, BackendCacheSnapshot as a, BackendJobListStatusFilter as b, BackendCapabilityUnavailableError as c, BackendEventKind as d, BackendDiagnostic as et, BackendFailure as f, BackendHostAdapter as g, BackendFileSystem as h, BackendCacheEntry as i, BackendSuccess as j, BackendRuntimeOptions as k, BackendError as l, BackendFileStat as m, ApplySessionTransactionInput as n, BackendResponseMeta as nt, BackendCapabilities as o, BackendFileHandle as p, SavePartialFailureError as q, BackendArtifactBridgeCapability as r, BackendStage as rt, BackendCapabilityManifest as s, AdvisoryLockConflictError as t, BackendMessage as tt, BackendEvent as u, BackendJobKind as v, BackendJobSnapshot as w, BackendJobNotCancellableError as x, BackendJobListSnapshot as y, InProcessLockConflictError as z };
501
+ export { BACKEND_COMPATIBILITY_POLICY as $, BackendSessionSnapshot as A, ListJobsInput as B, BackendJobProgress as C, BackendResult as D, BackendProjectSessionStorage as E, GetCacheSnapshotInput as F, OpenProjectSessionInput as G, MaterializeSessionSnapshot as H, GetEventsInput as I, SaveSessionInput as J, RefreshCacheInput as K, GetEventsSnapshot as L, CacheRefreshFailedError as M, CancelJobInput as N, BackendRuntime as O, EventCursorInvalidError as P, BACKEND_CAPABILITY_SCHEMA_VERSION as Q, GetJobInput as R, BackendJobNotFoundError as S, BackendJobStatus as T, MaterializeValidationFailedError as U, MaterializeSessionInput as V, MaterializeWriteFailedError as W, SessionStaleWriteError as X, SessionNotFoundError as Y, UamFidelityUnsupportedError as Z, BackendJobErrors as _, BackendCacheSnapshot as a, BackendJobListStatusFilter as b, BackendCapabilityUnavailableError as c, BackendEventKind as d, BACKEND_CONTRACT_VERSION as et, BackendFailure as f, BackendHostAdapter as g, BackendFileSystem as h, BackendCacheEntry as i, BackendStage as it, BackendSuccess as j, BackendRuntimeOptions as k, BackendError as l, BackendFileStat as m, ApplySessionTransactionInput as n, BackendMessage as nt, BackendCapabilities as o, BackendFileHandle as p, SavePartialFailureError as q, BackendArtifactBridgeCapability as r, BackendResponseMeta as rt, BackendCapabilityManifest as s, AdvisoryLockConflictError as t, BackendDiagnostic as tt, BackendEvent as u, BackendJobKind as v, BackendJobSnapshot as w, BackendJobNotCancellableError as x, BackendJobListSnapshot as y, InProcessLockConflictError as z };
@@ -193,6 +193,7 @@ interface BackendSessionSnapshot {
193
193
  revision: number;
194
194
  lastSavedRevision: number;
195
195
  dirty: boolean;
196
+ uamFidelity: 'full' | 'unsupported';
196
197
  lockHeld: boolean;
197
198
  capabilities: BackendCapabilities;
198
199
  }
@@ -257,6 +258,12 @@ interface SavePartialFailureError {
257
258
  failedPaths: string[];
258
259
  diskMayBePartiallyUpdated: true;
259
260
  }
261
+ interface UamFidelityUnsupportedError {
262
+ code: 'uam_fidelity_unsupported';
263
+ message: string;
264
+ sessionId: string;
265
+ canonicalPathKey: string;
266
+ }
260
267
  interface MaterializeValidationFailedError {
261
268
  code: 'materialize_validation_failed';
262
269
  message: string;
@@ -409,7 +416,7 @@ interface RefreshCacheInput {
409
416
  sessionId: string;
410
417
  reason?: 'manual' | 'session_open' | 'after_save';
411
418
  }
412
- type BackendError = SessionNotFoundError | SessionStaleWriteError | InProcessLockConflictError | AdvisoryLockConflictError | SavePartialFailureError | MaterializeValidationFailedError | MaterializeWriteFailedError | PathPolicyViolationError | EventCursorInvalidError | BackendJobNotFoundError | BackendJobNotCancellableError | BackendJobCancelledError | CacheRefreshFailedError | BackendCapabilityUnavailableError | ApplyUamTransactionAppError;
419
+ type BackendError = SessionNotFoundError | SessionStaleWriteError | InProcessLockConflictError | AdvisoryLockConflictError | SavePartialFailureError | UamFidelityUnsupportedError | MaterializeValidationFailedError | MaterializeWriteFailedError | PathPolicyViolationError | EventCursorInvalidError | BackendJobNotFoundError | BackendJobNotCancellableError | BackendJobCancelledError | CacheRefreshFailedError | BackendCapabilityUnavailableError | ApplyUamTransactionAppError;
413
420
  interface ApplySessionTransactionInput {
414
421
  sessionId: string;
415
422
  expectedRevision: number;
@@ -475,8 +482,8 @@ declare class BackendRuntime {
475
482
  sessionId: string;
476
483
  }): BackendResult<BackendSessionSnapshot, SessionNotFoundError>;
477
484
  applyTransaction(input: ApplySessionTransactionInput): Promise<BackendResult<BackendSessionSnapshot, SessionNotFoundError | SessionStaleWriteError | ApplyUamTransactionAppError>>;
478
- saveSession(input: SaveSessionInput): Promise<BackendResult<BackendSessionSnapshot | MaterializeSessionSnapshot, SessionNotFoundError | SessionStaleWriteError | SavePartialFailureError | MaterializeValidationFailedError | MaterializeWriteFailedError | PathPolicyViolationError | InProcessLockConflictError | BackendCapabilityUnavailableError>>;
479
- materializeSession(input: MaterializeSessionInput): Promise<BackendResult<MaterializeSessionSnapshot, SessionNotFoundError | SessionStaleWriteError | MaterializeValidationFailedError | MaterializeWriteFailedError | PathPolicyViolationError | InProcessLockConflictError | BackendCapabilityUnavailableError>>;
485
+ saveSession(input: SaveSessionInput): Promise<BackendResult<BackendSessionSnapshot | MaterializeSessionSnapshot, SessionNotFoundError | SessionStaleWriteError | SavePartialFailureError | UamFidelityUnsupportedError | MaterializeValidationFailedError | MaterializeWriteFailedError | PathPolicyViolationError | InProcessLockConflictError | BackendCapabilityUnavailableError>>;
486
+ materializeSession(input: MaterializeSessionInput): Promise<BackendResult<MaterializeSessionSnapshot, SessionNotFoundError | SessionStaleWriteError | UamFidelityUnsupportedError | MaterializeValidationFailedError | MaterializeWriteFailedError | PathPolicyViolationError | InProcessLockConflictError | BackendCapabilityUnavailableError>>;
480
487
  closeSession(input: {
481
488
  sessionId: string;
482
489
  }): Promise<BackendResult<{
@@ -491,4 +498,4 @@ declare class BackendRuntime {
491
498
  refreshCache(input: RefreshCacheInput): BackendResult<BackendJobSnapshot, SessionNotFoundError>;
492
499
  }
493
500
  //#endregion
494
- export { BACKEND_CONTRACT_VERSION as $, BackendSessionSnapshot as A, ListJobsInput as B, BackendJobProgress as C, BackendResult as D, BackendProjectSessionStorage as E, GetCacheSnapshotInput as F, OpenProjectSessionInput as G, MaterializeSessionSnapshot as H, GetEventsInput as I, SaveSessionInput as J, RefreshCacheInput as K, GetEventsSnapshot as L, CacheRefreshFailedError as M, CancelJobInput as N, BackendRuntime as O, EventCursorInvalidError as P, BACKEND_COMPATIBILITY_POLICY as Q, GetJobInput as R, BackendJobNotFoundError as S, BackendJobStatus as T, MaterializeValidationFailedError as U, MaterializeSessionInput as V, MaterializeWriteFailedError as W, SessionStaleWriteError as X, SessionNotFoundError as Y, BACKEND_CAPABILITY_SCHEMA_VERSION as Z, BackendJobErrors as _, BackendCacheSnapshot as a, BackendJobListStatusFilter as b, BackendCapabilityUnavailableError as c, BackendEventKind as d, BackendDiagnostic as et, BackendFailure as f, BackendHostAdapter as g, BackendFileSystem as h, BackendCacheEntry as i, BackendSuccess as j, BackendRuntimeOptions as k, BackendError as l, BackendFileStat as m, ApplySessionTransactionInput as n, BackendResponseMeta as nt, BackendCapabilities as o, BackendFileHandle as p, SavePartialFailureError as q, BackendArtifactBridgeCapability as r, BackendStage as rt, BackendCapabilityManifest as s, AdvisoryLockConflictError as t, BackendMessage as tt, BackendEvent as u, BackendJobKind as v, BackendJobSnapshot as w, BackendJobNotCancellableError as x, BackendJobListSnapshot as y, InProcessLockConflictError as z };
501
+ export { BACKEND_COMPATIBILITY_POLICY as $, BackendSessionSnapshot as A, ListJobsInput as B, BackendJobProgress as C, BackendResult as D, BackendProjectSessionStorage as E, GetCacheSnapshotInput as F, OpenProjectSessionInput as G, MaterializeSessionSnapshot as H, GetEventsInput as I, SaveSessionInput as J, RefreshCacheInput as K, GetEventsSnapshot as L, CacheRefreshFailedError as M, CancelJobInput as N, BackendRuntime as O, EventCursorInvalidError as P, BACKEND_CAPABILITY_SCHEMA_VERSION as Q, GetJobInput as R, BackendJobNotFoundError as S, BackendJobStatus as T, MaterializeValidationFailedError as U, MaterializeSessionInput as V, MaterializeWriteFailedError as W, SessionStaleWriteError as X, SessionNotFoundError as Y, UamFidelityUnsupportedError as Z, BackendJobErrors as _, BackendCacheSnapshot as a, BackendJobListStatusFilter as b, BackendCapabilityUnavailableError as c, BackendEventKind as d, BACKEND_CONTRACT_VERSION as et, BackendFailure as f, BackendHostAdapter as g, BackendFileSystem as h, BackendCacheEntry as i, BackendStage as it, BackendSuccess as j, BackendRuntimeOptions as k, BackendError as l, BackendFileStat as m, ApplySessionTransactionInput as n, BackendMessage as nt, BackendCapabilities as o, BackendFileHandle as p, SavePartialFailureError as q, BackendArtifactBridgeCapability as r, BackendResponseMeta as rt, BackendCapabilityManifest as s, AdvisoryLockConflictError as t, BackendDiagnostic as tt, BackendEvent as u, BackendJobKind as v, BackendJobSnapshot as w, BackendJobNotCancellableError as x, BackendJobListSnapshot as y, InProcessLockConflictError as z };
@@ -180,6 +180,7 @@ function toSessionSnapshot(session, capabilities) {
180
180
  revision: session.revision,
181
181
  lastSavedRevision: session.lastSavedRevision,
182
182
  dirty: session.dirty,
183
+ uamFidelity: session.uamFidelity,
183
184
  lockHeld: session.lockHeld,
184
185
  capabilities: cloneCapabilitiesSnapshot(capabilities)
185
186
  };
@@ -252,9 +253,45 @@ function createWriterFileSystem(fileSystem, committedPaths, failedPaths) {
252
253
  },
253
254
  dirname(filePath) {
254
255
  return fileSystem.dirname(filePath);
256
+ },
257
+ async unlink(filePath) {
258
+ await trackWrite(filePath, () => fileSystem.unlink(filePath));
255
259
  }
256
260
  };
257
261
  }
262
+ function projectAssetSourceFiles(project) {
263
+ const result = /* @__PURE__ */ new Map();
264
+ for (const pkg of project.packages) for (const resource of pkg.resources) {
265
+ if (resource.kind === "component") continue;
266
+ const fileName = resource.fileName ?? resource.file ?? "";
267
+ if (!fileName) continue;
268
+ result.set(`${pkg.id}/${resource.id}`, {
269
+ packageName: pkg.name,
270
+ branch: resource.branch,
271
+ path: resource.path,
272
+ fileName
273
+ });
274
+ }
275
+ return result;
276
+ }
277
+ function sourceFileKey(source) {
278
+ return [
279
+ source.branch,
280
+ source.packageName,
281
+ source.path,
282
+ source.fileName
283
+ ].join("\0");
284
+ }
285
+ function recordStaleResourceSources(session, previousProject, nextProject) {
286
+ if (!session.fileSystem) return;
287
+ const previousSources = projectAssetSourceFiles(previousProject);
288
+ const nextSourceKeys = new Set([...projectAssetSourceFiles(nextProject).values()].map(sourceFileKey));
289
+ for (const source of previousSources.values()) {
290
+ const key = sourceFileKey(source);
291
+ if (!nextSourceKeys.has(key)) session.pendingStaleSourceFiles.set(key, source);
292
+ }
293
+ for (const key of nextSourceKeys) session.pendingStaleSourceFiles.delete(key);
294
+ }
258
295
  function toBackendDiagnostics(error) {
259
296
  return error.diagnostics.length > 0 ? error.diagnostics.map((diagnostic) => ({ ...diagnostic })) : [{
260
297
  code: error.code,
@@ -273,6 +310,14 @@ function createCapabilityUnavailableError$1(message) {
273
310
  requiredAdapter: "BackendFileSystem"
274
311
  };
275
312
  }
313
+ function createUamFidelityUnsupportedError(session) {
314
+ return {
315
+ code: "uam_fidelity_unsupported",
316
+ message: "The source project contains formal properties that the current UAM cannot preserve.",
317
+ sessionId: session.sessionId,
318
+ canonicalPathKey: session.canonicalPathKey
319
+ };
320
+ }
276
321
  function validationDiagnostics(sessionProject) {
277
322
  return (0, _openfairygui_core_uam.validateUamProject)(sessionProject).map((issue) => ({
278
323
  code: "materialize_validation_failed",
@@ -304,12 +349,32 @@ function storageCanonicalTarget(input) {
304
349
  };
305
350
  }
306
351
  var AuthoringService = class {
352
+ sessionOperations = /* @__PURE__ */ new Map();
307
353
  constructor(context, cacheService, eventService) {
308
354
  this.context = context;
309
355
  this.cacheService = cacheService;
310
356
  this.eventService = eventService;
311
357
  }
358
+ async runSessionExclusive(sessionId, operation) {
359
+ const previous = this.sessionOperations.get(sessionId) ?? Promise.resolve();
360
+ let release = () => void 0;
361
+ const current = new Promise((resolve) => {
362
+ release = resolve;
363
+ });
364
+ const tail = previous.then(() => current);
365
+ this.sessionOperations.set(sessionId, tail);
366
+ await previous;
367
+ try {
368
+ return await operation();
369
+ } finally {
370
+ release();
371
+ if (this.sessionOperations.get(sessionId) === tail) this.sessionOperations.delete(sessionId);
372
+ }
373
+ }
312
374
  async applyTransaction(input) {
375
+ return this.runSessionExclusive(input.sessionId, () => this.applyTransactionExclusive(input));
376
+ }
377
+ async applyTransactionExclusive(input) {
313
378
  const startedAt = Date.now();
314
379
  const session = this.context.sessions.get(input.sessionId);
315
380
  if (!session || session.closed) return failure("authoring", startedAt, createSessionNotFoundError(input.sessionId));
@@ -344,6 +409,7 @@ var AuthoringService = class {
344
409
  diagnostics
345
410
  });
346
411
  }
412
+ recordStaleResourceSources(session, session.project, result.project);
347
413
  session.project = result.project;
348
414
  session.revision += 1;
349
415
  session.dirty = true;
@@ -375,10 +441,13 @@ var AuthoringService = class {
375
441
  mode: "fullProject",
376
442
  reason: "force_save"
377
443
  });
444
+ return this.runSessionExclusive(input.sessionId, () => this.saveSessionExclusive(input));
445
+ }
446
+ async saveSessionExclusive(input) {
378
447
  const startedAt = Date.now();
379
448
  const session = this.context.sessions.get(input.sessionId);
380
449
  if (!session || session.closed) return failure("authoring", startedAt, createSessionNotFoundError(input.sessionId));
381
- const fileSystem = input.fileSystem ?? session.fileSystem ?? this.context.fileSystem;
450
+ const fileSystem = session.fileSystem ?? input.fileSystem ?? this.context.fileSystem;
382
451
  if (!fileSystem) return failure("authoring", startedAt, createCapabilityUnavailableError$1("saveSession requires an injected BackendFileSystem adapter."), toSessionSnapshot(session, this.context.capabilities), {
383
452
  sessionId: session.sessionId,
384
453
  revision: session.revision
@@ -396,6 +465,10 @@ var AuthoringService = class {
396
465
  sessionId: session.sessionId,
397
466
  revision: session.revision
398
467
  });
468
+ if (session.uamFidelity === "unsupported") return failure("authoring", startedAt, createUamFidelityUnsupportedError(session), toSessionSnapshot(session, this.context.capabilities), {
469
+ sessionId: session.sessionId,
470
+ revision: session.revision
471
+ });
399
472
  const committedPaths = [];
400
473
  const failedPaths = [];
401
474
  this.eventService.emit({
@@ -405,7 +478,10 @@ var AuthoringService = class {
405
478
  revision: session.revision
406
479
  });
407
480
  try {
408
- await new _openfairygui_core_project_io.ProjectWriter(createWriterFileSystem(fileSystem, committedPaths, failedPaths)).write((0, _openfairygui_core_uam.materializeUamProject)(session.project), session.fairyPath);
481
+ await new _openfairygui_core_project_io.ProjectWriter(createWriterFileSystem(fileSystem, committedPaths, failedPaths)).write((0, _openfairygui_core_uam.materializeUamProject)(session.project), session.fairyPath, { staleSourceFiles: [...session.pendingStaleSourceFiles.values()] });
482
+ session.fileSystem ??= fileSystem;
483
+ session.pendingStaleSourceFiles.clear();
484
+ (0, _openfairygui_core_uam.commitUamProjectSourcePaths)(session.project);
409
485
  session.lastSavedRevision = session.revision;
410
486
  session.dirty = false;
411
487
  const cacheEntry = this.cacheService.refreshSession(session);
@@ -451,6 +527,9 @@ var AuthoringService = class {
451
527
  }
452
528
  }
453
529
  async materializeSession(input) {
530
+ return this.runSessionExclusive(input.sessionId, () => this.materializeSessionExclusive(input));
531
+ }
532
+ async materializeSessionExclusive(input) {
454
533
  const startedAt = Date.now();
455
534
  const session = this.context.sessions.get(input.sessionId);
456
535
  if (!session || session.closed) return failure("authoring", startedAt, createSessionNotFoundError(input.sessionId));
@@ -483,6 +562,10 @@ var AuthoringService = class {
483
562
  revision: session.revision
484
563
  });
485
564
  }
565
+ if (session.uamFidelity === "unsupported") return failure("authoring", startedAt, createUamFidelityUnsupportedError(session), toSessionSnapshot(session, this.context.capabilities), {
566
+ sessionId: session.sessionId,
567
+ revision: session.revision
568
+ });
486
569
  const diagnostics = validationDiagnostics(session.project);
487
570
  if (diagnostics.length > 0) return failure("authoring", startedAt, {
488
571
  code: "materialize_validation_failed",
@@ -529,7 +612,12 @@ var AuthoringService = class {
529
612
  revision: session.revision
530
613
  });
531
614
  try {
532
- await new _openfairygui_core_project_io.ProjectWriter(createWriterFileSystem(fileSystem, writtenPaths, failedPaths)).write(document, fairyPath);
615
+ const writer = new _openfairygui_core_project_io.ProjectWriter(createWriterFileSystem(fileSystem, writtenPaths, failedPaths));
616
+ const isSessionStorageTarget = fileSystem === session.fileSystem && fairyPath === session.fairyPath;
617
+ await writer.write(document, fairyPath, { staleSourceFiles: isSessionStorageTarget ? [...session.pendingStaleSourceFiles.values()] : [] });
618
+ if (isSessionStorageTarget) session.pendingStaleSourceFiles.clear();
619
+ if (storageTarget && !isSessionStorageTarget) session.pendingStaleSourceFiles.clear();
620
+ if (isSessionStorageTarget || storageTarget) (0, _openfairygui_core_uam.commitUamProjectSourcePaths)(session.project);
533
621
  if (storageTarget) {
534
622
  this.context.sessionsByPath.delete(session.canonicalPathKey);
535
623
  session.fileSystem = storageTarget.fileSystem;
@@ -1056,6 +1144,68 @@ function createProjectReaderFileSystem(fileSystem) {
1056
1144
  }
1057
1145
  };
1058
1146
  }
1147
+ function createCaptureFileSystem(files) {
1148
+ const normalize = (filePath) => filePath.replace(/\\/g, "/").replace(/\/+/g, "/");
1149
+ return {
1150
+ async readFile(filePath) {
1151
+ const value = files.get(normalize(filePath));
1152
+ if (typeof value !== "string") throw new Error(`Captured text file was not found: ${filePath}`);
1153
+ return value;
1154
+ },
1155
+ async readFileRaw(filePath) {
1156
+ const value = files.get(normalize(filePath));
1157
+ if (!(value instanceof Uint8Array)) throw new Error(`Captured binary file was not found: ${filePath}`);
1158
+ return value.slice();
1159
+ },
1160
+ async writeFile(filePath, content) {
1161
+ files.set(normalize(filePath), content);
1162
+ },
1163
+ async writeFileRaw(filePath, data) {
1164
+ files.set(normalize(filePath), data.slice());
1165
+ },
1166
+ async mkdir() {},
1167
+ async readdir() {
1168
+ return [];
1169
+ },
1170
+ async exists(filePath) {
1171
+ return files.has(normalize(filePath));
1172
+ },
1173
+ join(...paths) {
1174
+ return normalize(paths.filter(Boolean).join("/"));
1175
+ },
1176
+ dirname(filePath) {
1177
+ const normalized = normalize(filePath);
1178
+ const separator = normalized.lastIndexOf("/");
1179
+ return separator < 0 ? "" : normalized.slice(0, separator);
1180
+ },
1181
+ async unlink(filePath) {
1182
+ files.delete(normalize(filePath));
1183
+ }
1184
+ };
1185
+ }
1186
+ function capturedFilesEqual(left, right) {
1187
+ if (left.size !== right.size) return false;
1188
+ for (const [filePath, leftValue] of left) {
1189
+ const rightValue = right.get(filePath);
1190
+ if (typeof leftValue === "string") {
1191
+ if (leftValue !== rightValue) return false;
1192
+ continue;
1193
+ }
1194
+ if (!(rightValue instanceof Uint8Array) || leftValue.length !== rightValue.length) return false;
1195
+ for (let index = 0; index < leftValue.length; index += 1) if (leftValue[index] !== rightValue[index]) return false;
1196
+ }
1197
+ return true;
1198
+ }
1199
+ async function hasFullUamFidelity(document, project) {
1200
+ const sourceFiles = /* @__PURE__ */ new Map();
1201
+ const materializedFiles = /* @__PURE__ */ new Map();
1202
+ try {
1203
+ 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")]);
1204
+ } catch {
1205
+ return false;
1206
+ }
1207
+ return capturedFilesEqual(sourceFiles, materializedFiles);
1208
+ }
1059
1209
  var RuntimeService = class {
1060
1210
  constructor(context, cacheService, eventService, jobService) {
1061
1211
  this.context = context;
@@ -1090,7 +1240,8 @@ var RuntimeService = class {
1090
1240
  canonicalPathKey
1091
1241
  }));
1092
1242
  await advisoryLock.close();
1093
- const project = (0, _openfairygui_core_uam.liftDocumentToUamProject)(await new _openfairygui_core_project_io.ProjectReader(createProjectReaderFileSystem(fileSystem)).read(fairyPath));
1243
+ const document = await new _openfairygui_core_project_io.ProjectReader(createProjectReaderFileSystem(fileSystem)).read(fairyPath, { hydrateResourceBytes: true });
1244
+ const project = (0, _openfairygui_core_uam.liftDocumentToUamProject)(document);
1094
1245
  const sessionId = randomId();
1095
1246
  const session = {
1096
1247
  sessionId,
@@ -1100,8 +1251,10 @@ var RuntimeService = class {
1100
1251
  lockFilePath,
1101
1252
  fileSystem,
1102
1253
  project,
1254
+ uamFidelity: await hasFullUamFidelity(document, project) ? "full" : "unsupported",
1103
1255
  revision: 0,
1104
1256
  lastSavedRevision: 0,
1257
+ pendingStaleSourceFiles: /* @__PURE__ */ new Map(),
1105
1258
  dirty: false,
1106
1259
  lockHeld: true,
1107
1260
  closed: false
@@ -1157,8 +1310,10 @@ var RuntimeService = class {
1157
1310
  lockFilePath: "",
1158
1311
  fileSystem: storage?.fileSystem,
1159
1312
  project: (0, _openfairygui_core_uam.normalizeUamProject)(input.project),
1313
+ uamFidelity: "full",
1160
1314
  revision: 0,
1161
1315
  lastSavedRevision: 0,
1316
+ pendingStaleSourceFiles: /* @__PURE__ */ new Map(),
1162
1317
  dirty: false,
1163
1318
  lockHeld: false,
1164
1319
  closed: false