@openfairygui/backend 0.2.0-alpha.14 → 0.2.0-alpha.16

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
@@ -39,6 +39,9 @@ can clean up stale source files. The default Node filesystem/runtime lives under
39
39
  File-backed `openSession` hydrates primary resource bytes so browser-safe transactions can rename/move
40
40
  assets or add/replace/remove binary resources. `saveSession` writes replacement bytes before it removes
41
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.
42
45
 
43
46
  ## Relationship to other packages
44
47
 
package/dist/index.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_runtime = require("./runtime-DqApG30C.cjs");
2
+ const require_runtime = require("./runtime-P0Nhxk2f.cjs");
3
3
  //#region src/storage.ts
4
4
  var StorageFileStat = class {
5
5
  constructor(kind) {
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
@@ -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
@@ -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-B9z0n2yF.mjs";
1
+ import { i as BACKEND_CONTRACT_VERSION, n as BACKEND_CAPABILITY_SCHEMA_VERSION, r as BACKEND_COMPATIBILITY_POLICY, t as BackendRuntime } from "./runtime-B017UgBl.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-DqApG30C.cjs");
24
+ const require_runtime = require("./runtime-P0Nhxk2f.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-B9z0n2yF.mjs";
1
+ import { t as BackendRuntime } from "./runtime-B017UgBl.mjs";
2
2
  import fs from "node:fs/promises";
3
3
  import path from "node:path";
4
4
  //#region src/node.ts
@@ -179,6 +179,7 @@ function toSessionSnapshot(session, capabilities) {
179
179
  revision: session.revision,
180
180
  lastSavedRevision: session.lastSavedRevision,
181
181
  dirty: session.dirty,
182
+ uamFidelity: session.uamFidelity,
182
183
  lockHeld: session.lockHeld,
183
184
  capabilities: cloneCapabilitiesSnapshot(capabilities)
184
185
  };
@@ -257,17 +258,32 @@ function createWriterFileSystem(fileSystem, committedPaths, failedPaths) {
257
258
  }
258
259
  };
259
260
  }
260
- function projectAssetSourceFiles(project) {
261
+ function projectSourceFiles(project) {
261
262
  const result = /* @__PURE__ */ new Map();
262
- for (const pkg of project.packages) for (const resource of pkg.resources) {
263
- if (resource.kind === "component") continue;
264
- const fileName = resource.fileName ?? resource.file ?? "";
265
- if (!fileName) continue;
266
- result.set(`${pkg.id}/${resource.id}`, {
263
+ for (const pkg of project.packages) {
264
+ result.set(`${pkg.id}/package.xml`, {
267
265
  packageName: pkg.name,
268
- branch: resource.branch,
269
- path: resource.path,
270
- fileName
266
+ branch: "",
267
+ path: "",
268
+ fileName: "package.xml"
269
+ });
270
+ const branches = /* @__PURE__ */ new Set();
271
+ for (const resource of pkg.resources) {
272
+ if (resource.branch) branches.add(resource.branch);
273
+ const fileName = resource.kind === "component" ? `${resource.name}.xml` : resource.fileName ?? resource.file ?? "";
274
+ if (!fileName) continue;
275
+ result.set(`${pkg.id}/${resource.id}`, {
276
+ packageName: pkg.name,
277
+ branch: resource.branch,
278
+ path: resource.path,
279
+ fileName
280
+ });
281
+ }
282
+ for (const branch of branches) result.set(`${pkg.id}/branch/${branch}`, {
283
+ packageName: pkg.name,
284
+ branch,
285
+ path: "",
286
+ fileName: "package_branch.xml"
271
287
  });
272
288
  }
273
289
  return result;
@@ -280,10 +296,10 @@ function sourceFileKey(source) {
280
296
  source.fileName
281
297
  ].join("\0");
282
298
  }
283
- function recordStaleResourceSources(session, previousProject, nextProject) {
299
+ function recordStaleProjectFiles(session, previousProject, nextProject) {
284
300
  if (!session.fileSystem) return;
285
- const previousSources = projectAssetSourceFiles(previousProject);
286
- const nextSourceKeys = new Set([...projectAssetSourceFiles(nextProject).values()].map(sourceFileKey));
301
+ const previousSources = projectSourceFiles(previousProject);
302
+ const nextSourceKeys = new Set([...projectSourceFiles(nextProject).values()].map(sourceFileKey));
287
303
  for (const source of previousSources.values()) {
288
304
  const key = sourceFileKey(source);
289
305
  if (!nextSourceKeys.has(key)) session.pendingStaleSourceFiles.set(key, source);
@@ -308,6 +324,14 @@ function createCapabilityUnavailableError$1(message) {
308
324
  requiredAdapter: "BackendFileSystem"
309
325
  };
310
326
  }
327
+ function createUamFidelityUnsupportedError(session) {
328
+ return {
329
+ code: "uam_fidelity_unsupported",
330
+ message: "The source project contains formal properties that the current UAM cannot preserve.",
331
+ sessionId: session.sessionId,
332
+ canonicalPathKey: session.canonicalPathKey
333
+ };
334
+ }
311
335
  function validationDiagnostics(sessionProject) {
312
336
  return validateUamProject(sessionProject).map((issue) => ({
313
337
  code: "materialize_validation_failed",
@@ -339,12 +363,32 @@ function storageCanonicalTarget(input) {
339
363
  };
340
364
  }
341
365
  var AuthoringService = class {
366
+ sessionOperations = /* @__PURE__ */ new Map();
342
367
  constructor(context, cacheService, eventService) {
343
368
  this.context = context;
344
369
  this.cacheService = cacheService;
345
370
  this.eventService = eventService;
346
371
  }
372
+ async runSessionExclusive(sessionId, operation) {
373
+ const previous = this.sessionOperations.get(sessionId) ?? Promise.resolve();
374
+ let release = () => void 0;
375
+ const current = new Promise((resolve) => {
376
+ release = resolve;
377
+ });
378
+ const tail = previous.then(() => current);
379
+ this.sessionOperations.set(sessionId, tail);
380
+ await previous;
381
+ try {
382
+ return await operation();
383
+ } finally {
384
+ release();
385
+ if (this.sessionOperations.get(sessionId) === tail) this.sessionOperations.delete(sessionId);
386
+ }
387
+ }
347
388
  async applyTransaction(input) {
389
+ return this.runSessionExclusive(input.sessionId, () => this.applyTransactionExclusive(input));
390
+ }
391
+ async applyTransactionExclusive(input) {
348
392
  const startedAt = Date.now();
349
393
  const session = this.context.sessions.get(input.sessionId);
350
394
  if (!session || session.closed) return failure("authoring", startedAt, createSessionNotFoundError(input.sessionId));
@@ -379,7 +423,7 @@ var AuthoringService = class {
379
423
  diagnostics
380
424
  });
381
425
  }
382
- recordStaleResourceSources(session, session.project, result.project);
426
+ recordStaleProjectFiles(session, session.project, result.project);
383
427
  session.project = result.project;
384
428
  session.revision += 1;
385
429
  session.dirty = true;
@@ -411,6 +455,9 @@ var AuthoringService = class {
411
455
  mode: "fullProject",
412
456
  reason: "force_save"
413
457
  });
458
+ return this.runSessionExclusive(input.sessionId, () => this.saveSessionExclusive(input));
459
+ }
460
+ async saveSessionExclusive(input) {
414
461
  const startedAt = Date.now();
415
462
  const session = this.context.sessions.get(input.sessionId);
416
463
  if (!session || session.closed) return failure("authoring", startedAt, createSessionNotFoundError(input.sessionId));
@@ -432,6 +479,10 @@ var AuthoringService = class {
432
479
  sessionId: session.sessionId,
433
480
  revision: session.revision
434
481
  });
482
+ if (session.uamFidelity === "unsupported") return failure("authoring", startedAt, createUamFidelityUnsupportedError(session), toSessionSnapshot(session, this.context.capabilities), {
483
+ sessionId: session.sessionId,
484
+ revision: session.revision
485
+ });
435
486
  const committedPaths = [];
436
487
  const failedPaths = [];
437
488
  this.eventService.emit({
@@ -490,6 +541,9 @@ var AuthoringService = class {
490
541
  }
491
542
  }
492
543
  async materializeSession(input) {
544
+ return this.runSessionExclusive(input.sessionId, () => this.materializeSessionExclusive(input));
545
+ }
546
+ async materializeSessionExclusive(input) {
493
547
  const startedAt = Date.now();
494
548
  const session = this.context.sessions.get(input.sessionId);
495
549
  if (!session || session.closed) return failure("authoring", startedAt, createSessionNotFoundError(input.sessionId));
@@ -522,6 +576,10 @@ var AuthoringService = class {
522
576
  revision: session.revision
523
577
  });
524
578
  }
579
+ if (session.uamFidelity === "unsupported") return failure("authoring", startedAt, createUamFidelityUnsupportedError(session), toSessionSnapshot(session, this.context.capabilities), {
580
+ sessionId: session.sessionId,
581
+ revision: session.revision
582
+ });
525
583
  const diagnostics = validationDiagnostics(session.project);
526
584
  if (diagnostics.length > 0) return failure("authoring", startedAt, {
527
585
  code: "materialize_validation_failed",
@@ -1100,6 +1158,68 @@ function createProjectReaderFileSystem(fileSystem) {
1100
1158
  }
1101
1159
  };
1102
1160
  }
1161
+ function createCaptureFileSystem(files) {
1162
+ const normalize = (filePath) => filePath.replace(/\\/g, "/").replace(/\/+/g, "/");
1163
+ return {
1164
+ async readFile(filePath) {
1165
+ const value = files.get(normalize(filePath));
1166
+ if (typeof value !== "string") throw new Error(`Captured text file was not found: ${filePath}`);
1167
+ return value;
1168
+ },
1169
+ async readFileRaw(filePath) {
1170
+ const value = files.get(normalize(filePath));
1171
+ if (!(value instanceof Uint8Array)) throw new Error(`Captured binary file was not found: ${filePath}`);
1172
+ return value.slice();
1173
+ },
1174
+ async writeFile(filePath, content) {
1175
+ files.set(normalize(filePath), content);
1176
+ },
1177
+ async writeFileRaw(filePath, data) {
1178
+ files.set(normalize(filePath), data.slice());
1179
+ },
1180
+ async mkdir() {},
1181
+ async readdir() {
1182
+ return [];
1183
+ },
1184
+ async exists(filePath) {
1185
+ return files.has(normalize(filePath));
1186
+ },
1187
+ join(...paths) {
1188
+ return normalize(paths.filter(Boolean).join("/"));
1189
+ },
1190
+ dirname(filePath) {
1191
+ const normalized = normalize(filePath);
1192
+ const separator = normalized.lastIndexOf("/");
1193
+ return separator < 0 ? "" : normalized.slice(0, separator);
1194
+ },
1195
+ async unlink(filePath) {
1196
+ files.delete(normalize(filePath));
1197
+ }
1198
+ };
1199
+ }
1200
+ function capturedFilesEqual(left, right) {
1201
+ if (left.size !== right.size) return false;
1202
+ for (const [filePath, leftValue] of left) {
1203
+ const rightValue = right.get(filePath);
1204
+ if (typeof leftValue === "string") {
1205
+ if (leftValue !== rightValue) return false;
1206
+ continue;
1207
+ }
1208
+ if (!(rightValue instanceof Uint8Array) || leftValue.length !== rightValue.length) return false;
1209
+ for (let index = 0; index < leftValue.length; index += 1) if (leftValue[index] !== rightValue[index]) return false;
1210
+ }
1211
+ return true;
1212
+ }
1213
+ async function hasFullUamFidelity(document, project) {
1214
+ const sourceFiles = /* @__PURE__ */ new Map();
1215
+ const materializedFiles = /* @__PURE__ */ new Map();
1216
+ try {
1217
+ await Promise.all([new ProjectWriter(createCaptureFileSystem(sourceFiles)).write(document, "Project.fairy"), new ProjectWriter(createCaptureFileSystem(materializedFiles)).write(materializeUamProject(project), "Project.fairy")]);
1218
+ } catch {
1219
+ return false;
1220
+ }
1221
+ return capturedFilesEqual(sourceFiles, materializedFiles);
1222
+ }
1103
1223
  var RuntimeService = class {
1104
1224
  constructor(context, cacheService, eventService, jobService) {
1105
1225
  this.context = context;
@@ -1134,7 +1254,8 @@ var RuntimeService = class {
1134
1254
  canonicalPathKey
1135
1255
  }));
1136
1256
  await advisoryLock.close();
1137
- const project = liftDocumentToUamProject(await new ProjectReader(createProjectReaderFileSystem(fileSystem)).read(fairyPath, { hydrateResourceBytes: true }));
1257
+ const document = await new ProjectReader(createProjectReaderFileSystem(fileSystem)).read(fairyPath, { hydrateResourceBytes: true });
1258
+ const project = liftDocumentToUamProject(document);
1138
1259
  const sessionId = randomId();
1139
1260
  const session = {
1140
1261
  sessionId,
@@ -1144,6 +1265,7 @@ var RuntimeService = class {
1144
1265
  lockFilePath,
1145
1266
  fileSystem,
1146
1267
  project,
1268
+ uamFidelity: await hasFullUamFidelity(document, project) ? "full" : "unsupported",
1147
1269
  revision: 0,
1148
1270
  lastSavedRevision: 0,
1149
1271
  pendingStaleSourceFiles: /* @__PURE__ */ new Map(),
@@ -1202,6 +1324,7 @@ var RuntimeService = class {
1202
1324
  lockFilePath: "",
1203
1325
  fileSystem: storage?.fileSystem,
1204
1326
  project: normalizeUamProject(input.project),
1327
+ uamFidelity: "full",
1205
1328
  revision: 0,
1206
1329
  lastSavedRevision: 0,
1207
1330
  pendingStaleSourceFiles: /* @__PURE__ */ new Map(),
@@ -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 };