@openfairygui/backend 0.3.0-alpha.1 → 0.3.0-alpha.2

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
@@ -29,6 +29,7 @@ It also provides:
29
29
  - `cache.refresh` in-memory jobs with cooperative cancel and terminal retention
30
30
  - revision-bound derived read-only cache snapshots
31
31
  - revision-bound project identity outlines for transaction planning
32
+ - revision-bound read-only project validation reports
32
33
  - explicit Node bridge boundaries for publish/restore
33
34
 
34
35
  It does **not** redefine transaction grammar or expose `Document`.
@@ -71,6 +72,9 @@ if (!opened.ok) throw new Error(opened.error.message);
71
72
  const outline = runtime.getProjectOutline({ sessionId: opened.data.sessionId });
72
73
  if (!outline.ok) throw new Error(outline.error.message);
73
74
 
75
+ const validation = runtime.validateSession({ sessionId: opened.data.sessionId });
76
+ if (!validation.ok) throw new Error(validation.error.message);
77
+
74
78
  const applied = await runtime.applyTransaction({
75
79
  sessionId: opened.data.sessionId,
76
80
  expectedRevision: opened.data.revision,
package/dist/index.cjs CHANGED
@@ -2,9 +2,10 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  let _openfairygui_core_uam = require("@openfairygui/core/uam");
3
3
  let _openfairygui_functions_uam = require("@openfairygui/functions/uam");
4
4
  let _openfairygui_core_project_io = require("@openfairygui/core/project-io");
5
+ let _openfairygui_functions = require("@openfairygui/functions");
5
6
  //#region src/contracts.ts
6
7
  const BACKEND_CONTRACT_VERSION = "1.1.0-p2";
7
- const BACKEND_CAPABILITY_SCHEMA_VERSION = 2;
8
+ const BACKEND_CAPABILITY_SCHEMA_VERSION = 3;
8
9
  const BACKEND_COMPATIBILITY_POLICY = {
9
10
  incompatibleChange: "requires contractVersion bump",
10
11
  capabilitySchemaChange: "requires capabilitySchemaVersion bump",
@@ -97,7 +98,7 @@ function createMeta(stage, startedAt, options) {
97
98
  diagnostics: options?.diagnostics ?? [],
98
99
  stage,
99
100
  contractVersion: BACKEND_CONTRACT_VERSION,
100
- capabilitySchemaVersion: 2
101
+ capabilitySchemaVersion: 3
101
102
  };
102
103
  }
103
104
  function success(stage, startedAt, data, options) {
@@ -1118,6 +1119,26 @@ var ReadService = class {
1118
1119
  revision: session.revision
1119
1120
  });
1120
1121
  }
1122
+ validateSession(input) {
1123
+ const startedAt = Date.now();
1124
+ const session = this.context.sessions.get(input.sessionId);
1125
+ if (!session || session.closed) return failure("read", startedAt, createSessionNotFoundError(input.sessionId));
1126
+ const report = (0, _openfairygui_functions.validateProject)(session.project, {
1127
+ readDiagnostics: session.readDiagnostics,
1128
+ complete: session.readComplete,
1129
+ validateSources: true
1130
+ });
1131
+ return success("read", startedAt, report, {
1132
+ sessionId: session.sessionId,
1133
+ revision: session.revision,
1134
+ diagnostics: report.diagnostics.map(({ code, message, severity, path }) => ({
1135
+ code,
1136
+ message,
1137
+ severity,
1138
+ path
1139
+ }))
1140
+ });
1141
+ }
1121
1142
  };
1122
1143
  //#endregion
1123
1144
  //#region src/services/runtime-service.ts
@@ -1273,7 +1294,9 @@ var RuntimeService = class {
1273
1294
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1274
1295
  canonicalPathKey
1275
1296
  }));
1276
- const document = await new _openfairygui_core_project_io.ProjectReader(createProjectReaderFileSystem(fileSystem)).read(fairyPath, { hydrateResourceBytes: true });
1297
+ const read = await new _openfairygui_core_project_io.ProjectReader(createProjectReaderFileSystem(fileSystem)).readDetailed(fairyPath, { hydrateResourceBytes: true });
1298
+ if (!read.document) throw new Error(read.diagnostics[0]?.message ?? `Unable to read project: ${fairyPath}`);
1299
+ const document = read.document;
1277
1300
  const project = (0, _openfairygui_core_uam.liftDocumentToUamProject)(document);
1278
1301
  const sessionId = randomId();
1279
1302
  const session = {
@@ -1285,6 +1308,8 @@ var RuntimeService = class {
1285
1308
  sessionLock,
1286
1309
  fileSystem,
1287
1310
  project,
1311
+ readDiagnostics: read.diagnostics,
1312
+ readComplete: read.complete,
1288
1313
  uamFidelity: await hasFullUamFidelity(document, project) ? "full" : "unsupported",
1289
1314
  revision: 0,
1290
1315
  lastSavedRevision: 0,
@@ -1348,6 +1373,8 @@ var RuntimeService = class {
1348
1373
  sessionLock: null,
1349
1374
  fileSystem: storage?.fileSystem,
1350
1375
  project: (0, _openfairygui_core_uam.normalizeUamProject)(input.project),
1376
+ readDiagnostics: [],
1377
+ readComplete: true,
1351
1378
  uamFidelity: "full",
1352
1379
  revision: 0,
1353
1380
  lastSavedRevision: 0,
@@ -1432,6 +1459,7 @@ const BACKEND_METHODS = [
1432
1459
  "openProjectSession",
1433
1460
  "getSession",
1434
1461
  "getProjectOutline",
1462
+ "validateSession",
1435
1463
  "applyTransaction",
1436
1464
  "saveSession",
1437
1465
  "materializeSession",
@@ -1453,7 +1481,7 @@ const ARTIFACT_BRIDGE_CAPABILITY = {
1453
1481
  function createCapabilities() {
1454
1482
  return {
1455
1483
  contractVersion: BACKEND_CONTRACT_VERSION,
1456
- capabilitySchemaVersion: 2,
1484
+ capabilitySchemaVersion: 3,
1457
1485
  transactionKernelOwner: "@openfairygui/core",
1458
1486
  appSeamOwner: "@openfairygui/functions",
1459
1487
  runtimeOwner: "@openfairygui/backend",
@@ -1461,7 +1489,8 @@ function createCapabilities() {
1461
1489
  read: {
1462
1490
  capabilitySnapshot: true,
1463
1491
  sessionSnapshot: true,
1464
- projectOutline: true
1492
+ projectOutline: true,
1493
+ projectValidation: true
1465
1494
  },
1466
1495
  authoring: {
1467
1496
  applyTransaction: true,
@@ -1604,6 +1633,9 @@ var BackendRuntime = class {
1604
1633
  getProjectOutline(input) {
1605
1634
  return this.readService.getProjectOutline(input);
1606
1635
  }
1636
+ validateSession(input) {
1637
+ return this.readService.validateSession(input);
1638
+ }
1607
1639
  async applyTransaction(input) {
1608
1640
  return this.authoringService.applyTransaction(input);
1609
1641
  }
package/dist/index.d.cts CHANGED
@@ -1,10 +1,11 @@
1
1
  import { ApplyUamTransactionAppError } from "@openfairygui/functions/uam";
2
+ import { ProjectValidationReport } from "@openfairygui/core";
2
3
  import { UamDisplayNodeKind, UamProject, UamResource, UamTransactionOperation } from "@openfairygui/core/uam";
3
4
  import { FileSystem } from "@openfairygui/core/project-io";
4
5
 
5
6
  //#region src/contracts.d.ts
6
7
  declare const BACKEND_CONTRACT_VERSION: "1.1.0-p2";
7
- declare const BACKEND_CAPABILITY_SCHEMA_VERSION: 2;
8
+ declare const BACKEND_CAPABILITY_SCHEMA_VERSION: 3;
8
9
  declare const BACKEND_COMPATIBILITY_POLICY: {
9
10
  readonly incompatibleChange: "requires contractVersion bump";
10
11
  readonly capabilitySchemaChange: "requires capabilitySchemaVersion bump";
@@ -130,11 +131,12 @@ interface BackendCapabilities {
130
131
  transactionKernelOwner: '@openfairygui/core';
131
132
  appSeamOwner: '@openfairygui/functions';
132
133
  runtimeOwner: '@openfairygui/backend';
133
- methods: readonly ['getCapabilities', 'openSession', 'openProjectSession', 'getSession', 'getProjectOutline', 'applyTransaction', 'saveSession', 'materializeSession', 'closeSession', 'getEvents', 'getJob', 'listJobs', 'cancelJob', 'getCacheSnapshot', 'refreshCache'];
134
+ methods: readonly ['getCapabilities', 'openSession', 'openProjectSession', 'getSession', 'getProjectOutline', 'validateSession', 'applyTransaction', 'saveSession', 'materializeSession', 'closeSession', 'getEvents', 'getJob', 'listJobs', 'cancelJob', 'getCacheSnapshot', 'refreshCache'];
134
135
  read: {
135
136
  capabilitySnapshot: true;
136
137
  sessionSnapshot: true;
137
138
  projectOutline: true;
139
+ projectValidation: true;
138
140
  };
139
141
  authoring: {
140
142
  applyTransaction: true;
@@ -474,6 +476,9 @@ interface ApplySessionTransactionInput {
474
476
  interface GetProjectOutlineInput {
475
477
  sessionId: string;
476
478
  }
479
+ interface ValidateSessionInput {
480
+ sessionId: string;
481
+ }
477
482
  interface OpenProjectSessionInput {
478
483
  /** Authoritative UAM project. Use BackendRuntime.openSession() when importing an existing project from storage. */
479
484
  project: UamProject;
@@ -538,6 +543,7 @@ declare class BackendRuntime {
538
543
  sessionId: string;
539
544
  }): BackendResult<BackendSessionSnapshot, SessionNotFoundError>;
540
545
  getProjectOutline(input: GetProjectOutlineInput): BackendResult<BackendProjectOutline, SessionNotFoundError>;
546
+ validateSession(input: ValidateSessionInput): BackendResult<ProjectValidationReport, SessionNotFoundError>;
541
547
  applyTransaction(input: ApplySessionTransactionInput): Promise<BackendResult<BackendSessionSnapshot, SessionNotFoundError | SessionStaleWriteError | ApplyUamTransactionAppError>>;
542
548
  saveSession(input: SaveSessionInput): Promise<BackendResult<BackendSessionSnapshot | MaterializeSessionSnapshot, SessionNotFoundError | SessionStaleWriteError | SavePartialFailureError | UamFidelityUnsupportedError | MaterializeValidationFailedError | MaterializeWriteFailedError | PathPolicyViolationError | InProcessLockConflictError | BackendCapabilityUnavailableError>>;
543
549
  materializeSession(input: MaterializeSessionInput): Promise<BackendResult<MaterializeSessionSnapshot, SessionNotFoundError | SessionStaleWriteError | UamFidelityUnsupportedError | MaterializeValidationFailedError | MaterializeWriteFailedError | PathPolicyViolationError | InProcessLockConflictError | BackendCapabilityUnavailableError>>;
@@ -590,4 +596,4 @@ interface BackendAsyncStorageAdapter {
590
596
  type BackendStorageFileSystem = BackendFileSystem & FileSystem;
591
597
  declare function createBackendStorageFileSystem(storage: BackendAsyncStorageAdapter): BackendStorageFileSystem;
592
598
  //#endregion
593
- 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 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 BackendProjectOutline, type BackendProjectOutlinePackage, type BackendProjectOutlineResource, type BackendProjectSessionStorage, type BackendResponseMeta, type BackendResult, BackendRuntime, type BackendRuntimeOptions, type BackendSessionLock, 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 GetProjectOutlineInput, 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 };
599
+ 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 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 BackendProjectOutline, type BackendProjectOutlinePackage, type BackendProjectOutlineResource, type BackendProjectSessionStorage, type BackendResponseMeta, type BackendResult, BackendRuntime, type BackendRuntimeOptions, type BackendSessionLock, 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 GetProjectOutlineInput, 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, type ValidateSessionInput, createBackendStorageFileSystem };
package/dist/index.d.mts CHANGED
@@ -1,10 +1,11 @@
1
1
  import { UamDisplayNodeKind, UamProject, UamResource, UamTransactionOperation } from "@openfairygui/core/uam";
2
2
  import { ApplyUamTransactionAppError } from "@openfairygui/functions/uam";
3
3
  import { FileSystem } from "@openfairygui/core/project-io";
4
+ import { ProjectValidationReport } from "@openfairygui/core";
4
5
 
5
6
  //#region src/contracts.d.ts
6
7
  declare const BACKEND_CONTRACT_VERSION: "1.1.0-p2";
7
- declare const BACKEND_CAPABILITY_SCHEMA_VERSION: 2;
8
+ declare const BACKEND_CAPABILITY_SCHEMA_VERSION: 3;
8
9
  declare const BACKEND_COMPATIBILITY_POLICY: {
9
10
  readonly incompatibleChange: "requires contractVersion bump";
10
11
  readonly capabilitySchemaChange: "requires capabilitySchemaVersion bump";
@@ -130,11 +131,12 @@ interface BackendCapabilities {
130
131
  transactionKernelOwner: '@openfairygui/core';
131
132
  appSeamOwner: '@openfairygui/functions';
132
133
  runtimeOwner: '@openfairygui/backend';
133
- methods: readonly ['getCapabilities', 'openSession', 'openProjectSession', 'getSession', 'getProjectOutline', 'applyTransaction', 'saveSession', 'materializeSession', 'closeSession', 'getEvents', 'getJob', 'listJobs', 'cancelJob', 'getCacheSnapshot', 'refreshCache'];
134
+ methods: readonly ['getCapabilities', 'openSession', 'openProjectSession', 'getSession', 'getProjectOutline', 'validateSession', 'applyTransaction', 'saveSession', 'materializeSession', 'closeSession', 'getEvents', 'getJob', 'listJobs', 'cancelJob', 'getCacheSnapshot', 'refreshCache'];
134
135
  read: {
135
136
  capabilitySnapshot: true;
136
137
  sessionSnapshot: true;
137
138
  projectOutline: true;
139
+ projectValidation: true;
138
140
  };
139
141
  authoring: {
140
142
  applyTransaction: true;
@@ -474,6 +476,9 @@ interface ApplySessionTransactionInput {
474
476
  interface GetProjectOutlineInput {
475
477
  sessionId: string;
476
478
  }
479
+ interface ValidateSessionInput {
480
+ sessionId: string;
481
+ }
477
482
  interface OpenProjectSessionInput {
478
483
  /** Authoritative UAM project. Use BackendRuntime.openSession() when importing an existing project from storage. */
479
484
  project: UamProject;
@@ -538,6 +543,7 @@ declare class BackendRuntime {
538
543
  sessionId: string;
539
544
  }): BackendResult<BackendSessionSnapshot, SessionNotFoundError>;
540
545
  getProjectOutline(input: GetProjectOutlineInput): BackendResult<BackendProjectOutline, SessionNotFoundError>;
546
+ validateSession(input: ValidateSessionInput): BackendResult<ProjectValidationReport, SessionNotFoundError>;
541
547
  applyTransaction(input: ApplySessionTransactionInput): Promise<BackendResult<BackendSessionSnapshot, SessionNotFoundError | SessionStaleWriteError | ApplyUamTransactionAppError>>;
542
548
  saveSession(input: SaveSessionInput): Promise<BackendResult<BackendSessionSnapshot | MaterializeSessionSnapshot, SessionNotFoundError | SessionStaleWriteError | SavePartialFailureError | UamFidelityUnsupportedError | MaterializeValidationFailedError | MaterializeWriteFailedError | PathPolicyViolationError | InProcessLockConflictError | BackendCapabilityUnavailableError>>;
543
549
  materializeSession(input: MaterializeSessionInput): Promise<BackendResult<MaterializeSessionSnapshot, SessionNotFoundError | SessionStaleWriteError | UamFidelityUnsupportedError | MaterializeValidationFailedError | MaterializeWriteFailedError | PathPolicyViolationError | InProcessLockConflictError | BackendCapabilityUnavailableError>>;
@@ -590,4 +596,4 @@ interface BackendAsyncStorageAdapter {
590
596
  type BackendStorageFileSystem = BackendFileSystem & FileSystem;
591
597
  declare function createBackendStorageFileSystem(storage: BackendAsyncStorageAdapter): BackendStorageFileSystem;
592
598
  //#endregion
593
- 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 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 BackendProjectOutline, type BackendProjectOutlinePackage, type BackendProjectOutlineResource, type BackendProjectSessionStorage, type BackendResponseMeta, type BackendResult, BackendRuntime, type BackendRuntimeOptions, type BackendSessionLock, 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 GetProjectOutlineInput, 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 };
599
+ 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 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 BackendProjectOutline, type BackendProjectOutlinePackage, type BackendProjectOutlineResource, type BackendProjectSessionStorage, type BackendResponseMeta, type BackendResult, BackendRuntime, type BackendRuntimeOptions, type BackendSessionLock, 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 GetProjectOutlineInput, 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, type ValidateSessionInput, createBackendStorageFileSystem };
package/dist/index.mjs CHANGED
@@ -1,9 +1,10 @@
1
1
  import { UAM_SUPPORTED_MATERIALIZATION_SCOPE, UAM_SUPPORTED_TRANSACTION_SCOPE, commitUamProjectSourcePaths, liftDocumentToUamProject, materializeUamProject, normalizeUamProject, staleBranchDirectories, staleResourceFolders, staleSourceFiles, validateUamProject } from "@openfairygui/core/uam";
2
2
  import { applyUamTransactionAppAsync } from "@openfairygui/functions/uam";
3
3
  import { ProjectReader, ProjectWriter } from "@openfairygui/core/project-io";
4
+ import { validateProject } from "@openfairygui/functions";
4
5
  //#region src/contracts.ts
5
6
  const BACKEND_CONTRACT_VERSION = "1.1.0-p2";
6
- const BACKEND_CAPABILITY_SCHEMA_VERSION = 2;
7
+ const BACKEND_CAPABILITY_SCHEMA_VERSION = 3;
7
8
  const BACKEND_COMPATIBILITY_POLICY = {
8
9
  incompatibleChange: "requires contractVersion bump",
9
10
  capabilitySchemaChange: "requires capabilitySchemaVersion bump",
@@ -96,7 +97,7 @@ function createMeta(stage, startedAt, options) {
96
97
  diagnostics: options?.diagnostics ?? [],
97
98
  stage,
98
99
  contractVersion: BACKEND_CONTRACT_VERSION,
99
- capabilitySchemaVersion: 2
100
+ capabilitySchemaVersion: 3
100
101
  };
101
102
  }
102
103
  function success(stage, startedAt, data, options) {
@@ -1117,6 +1118,26 @@ var ReadService = class {
1117
1118
  revision: session.revision
1118
1119
  });
1119
1120
  }
1121
+ validateSession(input) {
1122
+ const startedAt = Date.now();
1123
+ const session = this.context.sessions.get(input.sessionId);
1124
+ if (!session || session.closed) return failure("read", startedAt, createSessionNotFoundError(input.sessionId));
1125
+ const report = validateProject(session.project, {
1126
+ readDiagnostics: session.readDiagnostics,
1127
+ complete: session.readComplete,
1128
+ validateSources: true
1129
+ });
1130
+ return success("read", startedAt, report, {
1131
+ sessionId: session.sessionId,
1132
+ revision: session.revision,
1133
+ diagnostics: report.diagnostics.map(({ code, message, severity, path }) => ({
1134
+ code,
1135
+ message,
1136
+ severity,
1137
+ path
1138
+ }))
1139
+ });
1140
+ }
1120
1141
  };
1121
1142
  //#endregion
1122
1143
  //#region src/services/runtime-service.ts
@@ -1272,7 +1293,9 @@ var RuntimeService = class {
1272
1293
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1273
1294
  canonicalPathKey
1274
1295
  }));
1275
- const document = await new ProjectReader(createProjectReaderFileSystem(fileSystem)).read(fairyPath, { hydrateResourceBytes: true });
1296
+ const read = await new ProjectReader(createProjectReaderFileSystem(fileSystem)).readDetailed(fairyPath, { hydrateResourceBytes: true });
1297
+ if (!read.document) throw new Error(read.diagnostics[0]?.message ?? `Unable to read project: ${fairyPath}`);
1298
+ const document = read.document;
1276
1299
  const project = liftDocumentToUamProject(document);
1277
1300
  const sessionId = randomId();
1278
1301
  const session = {
@@ -1284,6 +1307,8 @@ var RuntimeService = class {
1284
1307
  sessionLock,
1285
1308
  fileSystem,
1286
1309
  project,
1310
+ readDiagnostics: read.diagnostics,
1311
+ readComplete: read.complete,
1287
1312
  uamFidelity: await hasFullUamFidelity(document, project) ? "full" : "unsupported",
1288
1313
  revision: 0,
1289
1314
  lastSavedRevision: 0,
@@ -1347,6 +1372,8 @@ var RuntimeService = class {
1347
1372
  sessionLock: null,
1348
1373
  fileSystem: storage?.fileSystem,
1349
1374
  project: normalizeUamProject(input.project),
1375
+ readDiagnostics: [],
1376
+ readComplete: true,
1350
1377
  uamFidelity: "full",
1351
1378
  revision: 0,
1352
1379
  lastSavedRevision: 0,
@@ -1431,6 +1458,7 @@ const BACKEND_METHODS = [
1431
1458
  "openProjectSession",
1432
1459
  "getSession",
1433
1460
  "getProjectOutline",
1461
+ "validateSession",
1434
1462
  "applyTransaction",
1435
1463
  "saveSession",
1436
1464
  "materializeSession",
@@ -1452,7 +1480,7 @@ const ARTIFACT_BRIDGE_CAPABILITY = {
1452
1480
  function createCapabilities() {
1453
1481
  return {
1454
1482
  contractVersion: BACKEND_CONTRACT_VERSION,
1455
- capabilitySchemaVersion: 2,
1483
+ capabilitySchemaVersion: 3,
1456
1484
  transactionKernelOwner: "@openfairygui/core",
1457
1485
  appSeamOwner: "@openfairygui/functions",
1458
1486
  runtimeOwner: "@openfairygui/backend",
@@ -1460,7 +1488,8 @@ function createCapabilities() {
1460
1488
  read: {
1461
1489
  capabilitySnapshot: true,
1462
1490
  sessionSnapshot: true,
1463
- projectOutline: true
1491
+ projectOutline: true,
1492
+ projectValidation: true
1464
1493
  },
1465
1494
  authoring: {
1466
1495
  applyTransaction: true,
@@ -1603,6 +1632,9 @@ var BackendRuntime = class {
1603
1632
  getProjectOutline(input) {
1604
1633
  return this.readService.getProjectOutline(input);
1605
1634
  }
1635
+ validateSession(input) {
1636
+ return this.readService.validateSession(input);
1637
+ }
1606
1638
  async applyTransaction(input) {
1607
1639
  return this.authoringService.applyTransaction(input);
1608
1640
  }
package/dist/node.cjs CHANGED
@@ -28,6 +28,7 @@ node_path = __toESM(node_path);
28
28
  let _openfairygui_core_uam = require("@openfairygui/core/uam");
29
29
  let _openfairygui_functions_uam = require("@openfairygui/functions/uam");
30
30
  let _openfairygui_core_project_io = require("@openfairygui/core/project-io");
31
+ let _openfairygui_functions = require("@openfairygui/functions");
31
32
  //#region src/path-policy.ts
32
33
  function normalizeComparablePath(value) {
33
34
  const normalized = value.replace(/[/\\]+$/, "").replace(/\\/g, "/");
@@ -122,7 +123,7 @@ function createMeta(stage, startedAt, options) {
122
123
  diagnostics: options?.diagnostics ?? [],
123
124
  stage,
124
125
  contractVersion: BACKEND_CONTRACT_VERSION,
125
- capabilitySchemaVersion: 2
126
+ capabilitySchemaVersion: 3
126
127
  };
127
128
  }
128
129
  function success(stage, startedAt, data, options) {
@@ -1143,6 +1144,26 @@ var ReadService = class {
1143
1144
  revision: session.revision
1144
1145
  });
1145
1146
  }
1147
+ validateSession(input) {
1148
+ const startedAt = Date.now();
1149
+ const session = this.context.sessions.get(input.sessionId);
1150
+ if (!session || session.closed) return failure("read", startedAt, createSessionNotFoundError(input.sessionId));
1151
+ const report = (0, _openfairygui_functions.validateProject)(session.project, {
1152
+ readDiagnostics: session.readDiagnostics,
1153
+ complete: session.readComplete,
1154
+ validateSources: true
1155
+ });
1156
+ return success("read", startedAt, report, {
1157
+ sessionId: session.sessionId,
1158
+ revision: session.revision,
1159
+ diagnostics: report.diagnostics.map(({ code, message, severity, path }) => ({
1160
+ code,
1161
+ message,
1162
+ severity,
1163
+ path
1164
+ }))
1165
+ });
1166
+ }
1146
1167
  };
1147
1168
  //#endregion
1148
1169
  //#region src/services/runtime-service.ts
@@ -1298,7 +1319,9 @@ var RuntimeService = class {
1298
1319
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1299
1320
  canonicalPathKey
1300
1321
  }));
1301
- const document = await new _openfairygui_core_project_io.ProjectReader(createProjectReaderFileSystem(fileSystem)).read(fairyPath, { hydrateResourceBytes: true });
1322
+ const read = await new _openfairygui_core_project_io.ProjectReader(createProjectReaderFileSystem(fileSystem)).readDetailed(fairyPath, { hydrateResourceBytes: true });
1323
+ if (!read.document) throw new Error(read.diagnostics[0]?.message ?? `Unable to read project: ${fairyPath}`);
1324
+ const document = read.document;
1302
1325
  const project = (0, _openfairygui_core_uam.liftDocumentToUamProject)(document);
1303
1326
  const sessionId = randomId();
1304
1327
  const session = {
@@ -1310,6 +1333,8 @@ var RuntimeService = class {
1310
1333
  sessionLock,
1311
1334
  fileSystem,
1312
1335
  project,
1336
+ readDiagnostics: read.diagnostics,
1337
+ readComplete: read.complete,
1313
1338
  uamFidelity: await hasFullUamFidelity(document, project) ? "full" : "unsupported",
1314
1339
  revision: 0,
1315
1340
  lastSavedRevision: 0,
@@ -1373,6 +1398,8 @@ var RuntimeService = class {
1373
1398
  sessionLock: null,
1374
1399
  fileSystem: storage?.fileSystem,
1375
1400
  project: (0, _openfairygui_core_uam.normalizeUamProject)(input.project),
1401
+ readDiagnostics: [],
1402
+ readComplete: true,
1376
1403
  uamFidelity: "full",
1377
1404
  revision: 0,
1378
1405
  lastSavedRevision: 0,
@@ -1457,6 +1484,7 @@ const BACKEND_METHODS = [
1457
1484
  "openProjectSession",
1458
1485
  "getSession",
1459
1486
  "getProjectOutline",
1487
+ "validateSession",
1460
1488
  "applyTransaction",
1461
1489
  "saveSession",
1462
1490
  "materializeSession",
@@ -1478,7 +1506,7 @@ const ARTIFACT_BRIDGE_CAPABILITY = {
1478
1506
  function createCapabilities() {
1479
1507
  return {
1480
1508
  contractVersion: BACKEND_CONTRACT_VERSION,
1481
- capabilitySchemaVersion: 2,
1509
+ capabilitySchemaVersion: 3,
1482
1510
  transactionKernelOwner: "@openfairygui/core",
1483
1511
  appSeamOwner: "@openfairygui/functions",
1484
1512
  runtimeOwner: "@openfairygui/backend",
@@ -1486,7 +1514,8 @@ function createCapabilities() {
1486
1514
  read: {
1487
1515
  capabilitySnapshot: true,
1488
1516
  sessionSnapshot: true,
1489
- projectOutline: true
1517
+ projectOutline: true,
1518
+ projectValidation: true
1490
1519
  },
1491
1520
  authoring: {
1492
1521
  applyTransaction: true,
@@ -1629,6 +1658,9 @@ var BackendRuntime = class {
1629
1658
  getProjectOutline(input) {
1630
1659
  return this.readService.getProjectOutline(input);
1631
1660
  }
1661
+ validateSession(input) {
1662
+ return this.readService.validateSession(input);
1663
+ }
1632
1664
  async applyTransaction(input) {
1633
1665
  return this.authoringService.applyTransaction(input);
1634
1666
  }
package/dist/node.d.cts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { ApplyUamTransactionAppError } from "@openfairygui/functions/uam";
2
+ import { ProjectValidationReport } from "@openfairygui/core";
2
3
  import { UamDisplayNodeKind, UamProject, UamResource, UamTransactionOperation } from "@openfairygui/core/uam";
3
4
 
4
5
  //#region src/path-policy.d.ts
@@ -12,7 +13,7 @@ interface PathPolicyViolationError {
12
13
  //#endregion
13
14
  //#region src/contracts.d.ts
14
15
  declare const BACKEND_CONTRACT_VERSION: "1.1.0-p2";
15
- declare const BACKEND_CAPABILITY_SCHEMA_VERSION: 2;
16
+ declare const BACKEND_CAPABILITY_SCHEMA_VERSION: 3;
16
17
  declare const BACKEND_COMPATIBILITY_POLICY: {
17
18
  readonly incompatibleChange: "requires contractVersion bump";
18
19
  readonly capabilitySchemaChange: "requires capabilitySchemaVersion bump";
@@ -129,11 +130,12 @@ interface BackendCapabilities {
129
130
  transactionKernelOwner: '@openfairygui/core';
130
131
  appSeamOwner: '@openfairygui/functions';
131
132
  runtimeOwner: '@openfairygui/backend';
132
- methods: readonly ['getCapabilities', 'openSession', 'openProjectSession', 'getSession', 'getProjectOutline', 'applyTransaction', 'saveSession', 'materializeSession', 'closeSession', 'getEvents', 'getJob', 'listJobs', 'cancelJob', 'getCacheSnapshot', 'refreshCache'];
133
+ methods: readonly ['getCapabilities', 'openSession', 'openProjectSession', 'getSession', 'getProjectOutline', 'validateSession', 'applyTransaction', 'saveSession', 'materializeSession', 'closeSession', 'getEvents', 'getJob', 'listJobs', 'cancelJob', 'getCacheSnapshot', 'refreshCache'];
133
134
  read: {
134
135
  capabilitySnapshot: true;
135
136
  sessionSnapshot: true;
136
137
  projectOutline: true;
138
+ projectValidation: true;
137
139
  };
138
140
  authoring: {
139
141
  applyTransaction: true;
@@ -472,6 +474,9 @@ interface ApplySessionTransactionInput {
472
474
  interface GetProjectOutlineInput {
473
475
  sessionId: string;
474
476
  }
477
+ interface ValidateSessionInput {
478
+ sessionId: string;
479
+ }
475
480
  interface OpenProjectSessionInput {
476
481
  /** Authoritative UAM project. Use BackendRuntime.openSession() when importing an existing project from storage. */
477
482
  project: UamProject;
@@ -536,6 +541,7 @@ declare class BackendRuntime {
536
541
  sessionId: string;
537
542
  }): BackendResult<BackendSessionSnapshot, SessionNotFoundError>;
538
543
  getProjectOutline(input: GetProjectOutlineInput): BackendResult<BackendProjectOutline, SessionNotFoundError>;
544
+ validateSession(input: ValidateSessionInput): BackendResult<ProjectValidationReport, SessionNotFoundError>;
539
545
  applyTransaction(input: ApplySessionTransactionInput): Promise<BackendResult<BackendSessionSnapshot, SessionNotFoundError | SessionStaleWriteError | ApplyUamTransactionAppError>>;
540
546
  saveSession(input: SaveSessionInput): Promise<BackendResult<BackendSessionSnapshot | MaterializeSessionSnapshot, SessionNotFoundError | SessionStaleWriteError | SavePartialFailureError | UamFidelityUnsupportedError | MaterializeValidationFailedError | MaterializeWriteFailedError | PathPolicyViolationError | InProcessLockConflictError | BackendCapabilityUnavailableError>>;
541
547
  materializeSession(input: MaterializeSessionInput): Promise<BackendResult<MaterializeSessionSnapshot, SessionNotFoundError | SessionStaleWriteError | UamFidelityUnsupportedError | MaterializeValidationFailedError | MaterializeWriteFailedError | PathPolicyViolationError | InProcessLockConflictError | BackendCapabilityUnavailableError>>;
package/dist/node.d.mts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { UamDisplayNodeKind, UamProject, UamResource, UamTransactionOperation } from "@openfairygui/core/uam";
2
2
  import { ApplyUamTransactionAppError } from "@openfairygui/functions/uam";
3
+ import { ProjectValidationReport } from "@openfairygui/core";
3
4
 
4
5
  //#region src/path-policy.d.ts
5
6
  interface PathPolicyViolationError {
@@ -12,7 +13,7 @@ interface PathPolicyViolationError {
12
13
  //#endregion
13
14
  //#region src/contracts.d.ts
14
15
  declare const BACKEND_CONTRACT_VERSION: "1.1.0-p2";
15
- declare const BACKEND_CAPABILITY_SCHEMA_VERSION: 2;
16
+ declare const BACKEND_CAPABILITY_SCHEMA_VERSION: 3;
16
17
  declare const BACKEND_COMPATIBILITY_POLICY: {
17
18
  readonly incompatibleChange: "requires contractVersion bump";
18
19
  readonly capabilitySchemaChange: "requires capabilitySchemaVersion bump";
@@ -129,11 +130,12 @@ interface BackendCapabilities {
129
130
  transactionKernelOwner: '@openfairygui/core';
130
131
  appSeamOwner: '@openfairygui/functions';
131
132
  runtimeOwner: '@openfairygui/backend';
132
- methods: readonly ['getCapabilities', 'openSession', 'openProjectSession', 'getSession', 'getProjectOutline', 'applyTransaction', 'saveSession', 'materializeSession', 'closeSession', 'getEvents', 'getJob', 'listJobs', 'cancelJob', 'getCacheSnapshot', 'refreshCache'];
133
+ methods: readonly ['getCapabilities', 'openSession', 'openProjectSession', 'getSession', 'getProjectOutline', 'validateSession', 'applyTransaction', 'saveSession', 'materializeSession', 'closeSession', 'getEvents', 'getJob', 'listJobs', 'cancelJob', 'getCacheSnapshot', 'refreshCache'];
133
134
  read: {
134
135
  capabilitySnapshot: true;
135
136
  sessionSnapshot: true;
136
137
  projectOutline: true;
138
+ projectValidation: true;
137
139
  };
138
140
  authoring: {
139
141
  applyTransaction: true;
@@ -472,6 +474,9 @@ interface ApplySessionTransactionInput {
472
474
  interface GetProjectOutlineInput {
473
475
  sessionId: string;
474
476
  }
477
+ interface ValidateSessionInput {
478
+ sessionId: string;
479
+ }
475
480
  interface OpenProjectSessionInput {
476
481
  /** Authoritative UAM project. Use BackendRuntime.openSession() when importing an existing project from storage. */
477
482
  project: UamProject;
@@ -536,6 +541,7 @@ declare class BackendRuntime {
536
541
  sessionId: string;
537
542
  }): BackendResult<BackendSessionSnapshot, SessionNotFoundError>;
538
543
  getProjectOutline(input: GetProjectOutlineInput): BackendResult<BackendProjectOutline, SessionNotFoundError>;
544
+ validateSession(input: ValidateSessionInput): BackendResult<ProjectValidationReport, SessionNotFoundError>;
539
545
  applyTransaction(input: ApplySessionTransactionInput): Promise<BackendResult<BackendSessionSnapshot, SessionNotFoundError | SessionStaleWriteError | ApplyUamTransactionAppError>>;
540
546
  saveSession(input: SaveSessionInput): Promise<BackendResult<BackendSessionSnapshot | MaterializeSessionSnapshot, SessionNotFoundError | SessionStaleWriteError | SavePartialFailureError | UamFidelityUnsupportedError | MaterializeValidationFailedError | MaterializeWriteFailedError | PathPolicyViolationError | InProcessLockConflictError | BackendCapabilityUnavailableError>>;
541
547
  materializeSession(input: MaterializeSessionInput): Promise<BackendResult<MaterializeSessionSnapshot, SessionNotFoundError | SessionStaleWriteError | UamFidelityUnsupportedError | MaterializeValidationFailedError | MaterializeWriteFailedError | PathPolicyViolationError | InProcessLockConflictError | BackendCapabilityUnavailableError>>;
package/dist/node.mjs CHANGED
@@ -3,6 +3,7 @@ import path from "node:path";
3
3
  import { UAM_SUPPORTED_MATERIALIZATION_SCOPE, UAM_SUPPORTED_TRANSACTION_SCOPE, commitUamProjectSourcePaths, liftDocumentToUamProject, materializeUamProject, normalizeUamProject, staleBranchDirectories, staleResourceFolders, staleSourceFiles, validateUamProject } from "@openfairygui/core/uam";
4
4
  import { applyUamTransactionAppAsync } from "@openfairygui/functions/uam";
5
5
  import { ProjectReader, ProjectWriter } from "@openfairygui/core/project-io";
6
+ import { validateProject } from "@openfairygui/functions";
6
7
  //#region src/path-policy.ts
7
8
  function normalizeComparablePath(value) {
8
9
  const normalized = value.replace(/[/\\]+$/, "").replace(/\\/g, "/");
@@ -97,7 +98,7 @@ function createMeta(stage, startedAt, options) {
97
98
  diagnostics: options?.diagnostics ?? [],
98
99
  stage,
99
100
  contractVersion: BACKEND_CONTRACT_VERSION,
100
- capabilitySchemaVersion: 2
101
+ capabilitySchemaVersion: 3
101
102
  };
102
103
  }
103
104
  function success(stage, startedAt, data, options) {
@@ -1118,6 +1119,26 @@ var ReadService = class {
1118
1119
  revision: session.revision
1119
1120
  });
1120
1121
  }
1122
+ validateSession(input) {
1123
+ const startedAt = Date.now();
1124
+ const session = this.context.sessions.get(input.sessionId);
1125
+ if (!session || session.closed) return failure("read", startedAt, createSessionNotFoundError(input.sessionId));
1126
+ const report = validateProject(session.project, {
1127
+ readDiagnostics: session.readDiagnostics,
1128
+ complete: session.readComplete,
1129
+ validateSources: true
1130
+ });
1131
+ return success("read", startedAt, report, {
1132
+ sessionId: session.sessionId,
1133
+ revision: session.revision,
1134
+ diagnostics: report.diagnostics.map(({ code, message, severity, path }) => ({
1135
+ code,
1136
+ message,
1137
+ severity,
1138
+ path
1139
+ }))
1140
+ });
1141
+ }
1121
1142
  };
1122
1143
  //#endregion
1123
1144
  //#region src/services/runtime-service.ts
@@ -1273,7 +1294,9 @@ var RuntimeService = class {
1273
1294
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1274
1295
  canonicalPathKey
1275
1296
  }));
1276
- const document = await new ProjectReader(createProjectReaderFileSystem(fileSystem)).read(fairyPath, { hydrateResourceBytes: true });
1297
+ const read = await new ProjectReader(createProjectReaderFileSystem(fileSystem)).readDetailed(fairyPath, { hydrateResourceBytes: true });
1298
+ if (!read.document) throw new Error(read.diagnostics[0]?.message ?? `Unable to read project: ${fairyPath}`);
1299
+ const document = read.document;
1277
1300
  const project = liftDocumentToUamProject(document);
1278
1301
  const sessionId = randomId();
1279
1302
  const session = {
@@ -1285,6 +1308,8 @@ var RuntimeService = class {
1285
1308
  sessionLock,
1286
1309
  fileSystem,
1287
1310
  project,
1311
+ readDiagnostics: read.diagnostics,
1312
+ readComplete: read.complete,
1288
1313
  uamFidelity: await hasFullUamFidelity(document, project) ? "full" : "unsupported",
1289
1314
  revision: 0,
1290
1315
  lastSavedRevision: 0,
@@ -1348,6 +1373,8 @@ var RuntimeService = class {
1348
1373
  sessionLock: null,
1349
1374
  fileSystem: storage?.fileSystem,
1350
1375
  project: normalizeUamProject(input.project),
1376
+ readDiagnostics: [],
1377
+ readComplete: true,
1351
1378
  uamFidelity: "full",
1352
1379
  revision: 0,
1353
1380
  lastSavedRevision: 0,
@@ -1432,6 +1459,7 @@ const BACKEND_METHODS = [
1432
1459
  "openProjectSession",
1433
1460
  "getSession",
1434
1461
  "getProjectOutline",
1462
+ "validateSession",
1435
1463
  "applyTransaction",
1436
1464
  "saveSession",
1437
1465
  "materializeSession",
@@ -1453,7 +1481,7 @@ const ARTIFACT_BRIDGE_CAPABILITY = {
1453
1481
  function createCapabilities() {
1454
1482
  return {
1455
1483
  contractVersion: BACKEND_CONTRACT_VERSION,
1456
- capabilitySchemaVersion: 2,
1484
+ capabilitySchemaVersion: 3,
1457
1485
  transactionKernelOwner: "@openfairygui/core",
1458
1486
  appSeamOwner: "@openfairygui/functions",
1459
1487
  runtimeOwner: "@openfairygui/backend",
@@ -1461,7 +1489,8 @@ function createCapabilities() {
1461
1489
  read: {
1462
1490
  capabilitySnapshot: true,
1463
1491
  sessionSnapshot: true,
1464
- projectOutline: true
1492
+ projectOutline: true,
1493
+ projectValidation: true
1465
1494
  },
1466
1495
  authoring: {
1467
1496
  applyTransaction: true,
@@ -1604,6 +1633,9 @@ var BackendRuntime = class {
1604
1633
  getProjectOutline(input) {
1605
1634
  return this.readService.getProjectOutline(input);
1606
1635
  }
1636
+ validateSession(input) {
1637
+ return this.readService.validateSession(input);
1638
+ }
1607
1639
  async applyTransaction(input) {
1608
1640
  return this.authoringService.applyTransaction(input);
1609
1641
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openfairygui/backend",
3
- "version": "0.3.0-alpha.1",
3
+ "version": "0.3.0-alpha.2",
4
4
  "description": "FairyGUI Headless Authoring SDK — stateful backend runtime and session services.",
5
5
  "author": "OpenFairyGUI Contributors",
6
6
  "license": "MIT",
@@ -52,8 +52,8 @@
52
52
  "runtime"
53
53
  ],
54
54
  "dependencies": {
55
- "@openfairygui/functions": "0.3.0-alpha.1",
56
- "@openfairygui/core": "0.3.0-alpha.1"
55
+ "@openfairygui/core": "0.3.0-alpha.2",
56
+ "@openfairygui/functions": "0.3.0-alpha.2"
57
57
  },
58
58
  "devDependencies": {
59
59
  "ava": "^7.0.0",
package/src/contracts.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export const BACKEND_CONTRACT_VERSION = '1.1.0-p2' as const;
2
- export const BACKEND_CAPABILITY_SCHEMA_VERSION = 2 as const;
2
+ export const BACKEND_CAPABILITY_SCHEMA_VERSION = 3 as const;
3
3
  export const BACKEND_COMPATIBILITY_POLICY = {
4
4
  incompatibleChange: 'requires contractVersion bump',
5
5
  capabilitySchemaChange: 'requires capabilitySchemaVersion bump',
package/src/index.ts CHANGED
@@ -63,6 +63,7 @@ export {
63
63
  type SessionNotFoundError,
64
64
  type SessionStaleWriteError,
65
65
  type UamFidelityUnsupportedError,
66
+ type ValidateSessionInput,
66
67
  } from './runtime.js';
67
68
  export {
68
69
  type BackendAsyncStorageAdapter,
@@ -17,6 +17,7 @@ const BACKEND_METHODS = [
17
17
  'openProjectSession',
18
18
  'getSession',
19
19
  'getProjectOutline',
20
+ 'validateSession',
20
21
  'applyTransaction',
21
22
  'saveSession',
22
23
  'materializeSession',
@@ -49,6 +50,7 @@ export function createCapabilities(): BackendCapabilities {
49
50
  capabilitySnapshot: true,
50
51
  sessionSnapshot: true,
51
52
  projectOutline: true,
53
+ projectValidation: true,
52
54
  },
53
55
  authoring: {
54
56
  applyTransaction: true,
@@ -100,6 +100,7 @@ export interface BackendCapabilities {
100
100
  'openProjectSession',
101
101
  'getSession',
102
102
  'getProjectOutline',
103
+ 'validateSession',
103
104
  'applyTransaction',
104
105
  'saveSession',
105
106
  'materializeSession',
@@ -115,6 +116,7 @@ export interface BackendCapabilities {
115
116
  capabilitySnapshot: true;
116
117
  sessionSnapshot: true;
117
118
  projectOutline: true;
119
+ projectValidation: true;
118
120
  };
119
121
  authoring: {
120
122
  applyTransaction: true;
@@ -518,6 +520,10 @@ export interface GetProjectOutlineInput {
518
520
  sessionId: string;
519
521
  }
520
522
 
523
+ export interface ValidateSessionInput {
524
+ sessionId: string;
525
+ }
526
+
521
527
  export interface OpenProjectSessionInput {
522
528
  /** Authoritative UAM project. Use BackendRuntime.openSession() when importing an existing project from storage. */
523
529
  project: UamProject;
package/src/runtime.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { ApplyUamTransactionAppError } from '@openfairygui/functions/uam';
2
+ import type { ProjectValidationReport } from '@openfairygui/core';
2
3
  import type { PathPolicyViolationError } from './path-policy.js';
3
4
  import { AuthoringService } from './services/authoring-service.js';
4
5
  import { CacheService } from './services/cache-service.js';
@@ -33,6 +34,7 @@ import type {
33
34
  GetEventsSnapshot,
34
35
  GetJobInput,
35
36
  GetProjectOutlineInput,
37
+ ValidateSessionInput,
36
38
  InProcessLockConflictError,
37
39
  ListJobsInput,
38
40
  MaterializeSessionInput,
@@ -121,6 +123,12 @@ export class BackendRuntime {
121
123
  return this.readService.getProjectOutline(input);
122
124
  }
123
125
 
126
+ public validateSession(
127
+ input: ValidateSessionInput,
128
+ ): BackendResult<ProjectValidationReport, SessionNotFoundError> {
129
+ return this.readService.validateSession(input);
130
+ }
131
+
124
132
  public async applyTransaction(
125
133
  input: ApplySessionTransactionInput,
126
134
  ): Promise<
@@ -29,6 +29,8 @@ export interface BackendSessionState {
29
29
  sessionLock: BackendSessionLock | null;
30
30
  fileSystem?: BackendFileSystem;
31
31
  project: import('@openfairygui/core/uam').UamProject;
32
+ readDiagnostics: import('@openfairygui/core').ProjectDiagnostic[];
33
+ readComplete: boolean;
32
34
  uamFidelity: 'full' | 'unsupported';
33
35
  revision: number;
34
36
  lastSavedRevision: number;
@@ -8,6 +8,8 @@ import type {
8
8
  SessionNotFoundError,
9
9
  } from '../runtime.js';
10
10
  import { createSessionNotFoundError, toSessionSnapshot } from './session-utils.js';
11
+ import { validateProject } from '@openfairygui/functions';
12
+ import type { ProjectValidationReport } from '@openfairygui/core';
11
13
 
12
14
  function toProjectOutline(session: BackendSessionState): BackendProjectOutline {
13
15
  const project = session.project;
@@ -81,4 +83,24 @@ export class ReadService {
81
83
  revision: session.revision,
82
84
  });
83
85
  }
86
+
87
+ public validateSession(
88
+ input: { sessionId: string },
89
+ ): BackendResult<ProjectValidationReport, SessionNotFoundError> {
90
+ const startedAt = Date.now();
91
+ const session = this.context.sessions.get(input.sessionId);
92
+ if (!session || session.closed) {
93
+ return failure('read', startedAt, createSessionNotFoundError(input.sessionId));
94
+ }
95
+ const report = validateProject(session.project, {
96
+ readDiagnostics: session.readDiagnostics,
97
+ complete: session.readComplete,
98
+ validateSources: true,
99
+ });
100
+ return success('read', startedAt, report, {
101
+ sessionId: session.sessionId,
102
+ revision: session.revision,
103
+ diagnostics: report.diagnostics.map(({ code, message, severity, path }) => ({ code, message, severity, path })),
104
+ });
105
+ }
84
106
  }
@@ -220,7 +220,9 @@ export class RuntimeService {
220
220
  ),
221
221
  );
222
222
  const reader = new ProjectReader(createProjectReaderFileSystem(fileSystem));
223
- const document = await reader.read(fairyPath, { hydrateResourceBytes: true });
223
+ const read = await reader.readDetailed(fairyPath, { hydrateResourceBytes: true });
224
+ if (!read.document) throw new Error(read.diagnostics[0]?.message ?? `Unable to read project: ${fairyPath}`);
225
+ const document = read.document;
224
226
  const project = liftDocumentToUamProject(document);
225
227
  const sessionId = randomId();
226
228
  const session: BackendSessionState = {
@@ -232,6 +234,8 @@ export class RuntimeService {
232
234
  sessionLock,
233
235
  fileSystem,
234
236
  project,
237
+ readDiagnostics: read.diagnostics,
238
+ readComplete: read.complete,
235
239
  uamFidelity: (await hasFullUamFidelity(document, project)) ? 'full' : 'unsupported',
236
240
  revision: 0,
237
241
  lastSavedRevision: 0,
@@ -305,6 +309,8 @@ export class RuntimeService {
305
309
  sessionLock: null,
306
310
  fileSystem: storage?.fileSystem,
307
311
  project: normalizeUamProject(input.project),
312
+ readDiagnostics: [],
313
+ readComplete: true,
308
314
  uamFidelity: 'full',
309
315
  revision: 0,
310
316
  lastSavedRevision: 0,