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

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.
@@ -1,6 +1,7 @@
1
1
  require("./node.cjs");
2
- let _openfairygui_core = require("@openfairygui/core");
3
- let _openfairygui_functions = require("@openfairygui/functions");
2
+ let _openfairygui_core_uam = require("@openfairygui/core/uam");
3
+ let _openfairygui_core_project_io = require("@openfairygui/core/project-io");
4
+ let _openfairygui_functions_uam = require("@openfairygui/functions/uam");
4
5
  //#region src/contracts.ts
5
6
  const BACKEND_CONTRACT_VERSION = "1.1.0-p2";
6
7
  const BACKEND_CAPABILITY_SCHEMA_VERSION = 2;
@@ -254,6 +255,54 @@ function createWriterFileSystem(fileSystem, committedPaths, failedPaths) {
254
255
  }
255
256
  };
256
257
  }
258
+ function toBackendDiagnostics(error) {
259
+ return error.diagnostics.length > 0 ? error.diagnostics.map((diagnostic) => ({ ...diagnostic })) : [{
260
+ code: error.code,
261
+ message: error.message,
262
+ severity: "error",
263
+ operationKind: error.operationKind,
264
+ opIndex: error.opIndex,
265
+ opId: error.opId
266
+ }];
267
+ }
268
+ function createCapabilityUnavailableError$1(message) {
269
+ return {
270
+ code: "capability_unavailable",
271
+ message,
272
+ capability: "fileSystem",
273
+ requiredAdapter: "BackendFileSystem"
274
+ };
275
+ }
276
+ function validationDiagnostics(sessionProject) {
277
+ return (0, _openfairygui_core_uam.validateUamProject)(sessionProject).map((issue) => ({
278
+ code: "materialize_validation_failed",
279
+ message: issue.message,
280
+ severity: "error",
281
+ path: issue.path,
282
+ operationKind: "materializeSession"
283
+ }));
284
+ }
285
+ function toMaterializeSnapshot(session, capabilities, input) {
286
+ return {
287
+ ...toSessionSnapshot(session, capabilities),
288
+ mode: "fullProject",
289
+ reason: input.reason,
290
+ materializeRevision: session.revision,
291
+ saveRevision: session.lastSavedRevision,
292
+ writtenPaths: [...input.writtenPaths],
293
+ skippedPaths: [...input.skippedPaths],
294
+ diagnostics: input.diagnostics.map((diagnostic) => ({ ...diagnostic }))
295
+ };
296
+ }
297
+ function storageCanonicalTarget(input) {
298
+ const canonicalProjectPath = input.canonicalProjectPath ?? (input.fileSystem.dirname(input.fairyPath) || ".");
299
+ return {
300
+ fileSystem: input.fileSystem,
301
+ fairyPath: input.fairyPath,
302
+ canonicalProjectPath,
303
+ canonicalPathKey: input.canonicalPathKey ?? normalizeComparablePath(canonicalProjectPath)
304
+ };
305
+ }
257
306
  var AuthoringService = class {
258
307
  constructor(context, cacheService, eventService) {
259
308
  this.context = context;
@@ -276,25 +325,23 @@ var AuthoringService = class {
276
325
  revision: session.revision
277
326
  });
278
327
  }
279
- const result = (0, _openfairygui_functions.applyUamTransactionApp)({
328
+ const result = (0, _openfairygui_functions_uam.applyUamTransactionApp)({
280
329
  project: session.project,
281
330
  operations: input.operations
282
331
  });
283
332
  if (result.ok === false) {
333
+ const diagnostics = toBackendDiagnostics(result.error);
284
334
  this.eventService.emit({
285
335
  kind: "transaction.rejected",
286
336
  sessionId: session.sessionId,
287
337
  canonicalPathKey: session.canonicalPathKey,
288
338
  revision: session.revision,
289
- diagnostics: result.error.issues?.map((issue) => ({
290
- code: result.error.code,
291
- message: issue.message,
292
- severity: "error"
293
- })) ?? []
339
+ diagnostics
294
340
  });
295
341
  return failure("authoring", startedAt, result.error, toSessionSnapshot(session, this.context.capabilities), {
296
342
  sessionId: session.sessionId,
297
- revision: session.revision
343
+ revision: session.revision,
344
+ diagnostics
298
345
  });
299
346
  }
300
347
  session.project = result.project;
@@ -320,15 +367,19 @@ var AuthoringService = class {
320
367
  });
321
368
  }
322
369
  async saveSession(input) {
370
+ if (input.force === true || input.mode === "materializeCleanSession") return this.materializeSession({
371
+ sessionId: input.sessionId,
372
+ expectedRevision: input.expectedRevision,
373
+ targetPath: input.targetPath,
374
+ fileSystem: input.fileSystem,
375
+ mode: "fullProject",
376
+ reason: "force_save"
377
+ });
323
378
  const startedAt = Date.now();
324
379
  const session = this.context.sessions.get(input.sessionId);
325
380
  if (!session || session.closed) return failure("authoring", startedAt, createSessionNotFoundError(input.sessionId));
326
- if (!this.context.fileSystem) return failure("authoring", startedAt, {
327
- code: "capability_unavailable",
328
- message: "saveSession requires an injected BackendFileSystem adapter.",
329
- capability: "fileSystem",
330
- requiredAdapter: "BackendFileSystem"
331
- }, toSessionSnapshot(session, this.context.capabilities), {
381
+ const fileSystem = input.fileSystem ?? session.fileSystem ?? this.context.fileSystem;
382
+ if (!fileSystem) return failure("authoring", startedAt, createCapabilityUnavailableError$1("saveSession requires an injected BackendFileSystem adapter."), toSessionSnapshot(session, this.context.capabilities), {
332
383
  sessionId: session.sessionId,
333
384
  revision: session.revision
334
385
  });
@@ -336,7 +387,6 @@ var AuthoringService = class {
336
387
  sessionId: session.sessionId,
337
388
  revision: session.revision
338
389
  });
339
- const fileSystem = this.context.fileSystem;
340
390
  const targetViolation = await validateSaveTarget(fileSystem, session.fairyPath, input.targetPath);
341
391
  if (targetViolation) return failure("authoring", startedAt, targetViolation, toSessionSnapshot(session, this.context.capabilities), {
342
392
  sessionId: session.sessionId,
@@ -355,7 +405,7 @@ var AuthoringService = class {
355
405
  revision: session.revision
356
406
  });
357
407
  try {
358
- await new _openfairygui_core.ProjectWriter(createWriterFileSystem(fileSystem, committedPaths, failedPaths)).write((0, _openfairygui_core.materializeUamProject)(session.project), session.fairyPath);
408
+ await new _openfairygui_core_project_io.ProjectWriter(createWriterFileSystem(fileSystem, committedPaths, failedPaths)).write((0, _openfairygui_core_uam.materializeUamProject)(session.project), session.fairyPath);
359
409
  session.lastSavedRevision = session.revision;
360
410
  session.dirty = false;
361
411
  const cacheEntry = this.cacheService.refreshSession(session);
@@ -400,6 +450,154 @@ var AuthoringService = class {
400
450
  });
401
451
  }
402
452
  }
453
+ async materializeSession(input) {
454
+ const startedAt = Date.now();
455
+ const session = this.context.sessions.get(input.sessionId);
456
+ if (!session || session.closed) return failure("authoring", startedAt, createSessionNotFoundError(input.sessionId));
457
+ if (input.expectedRevision !== void 0 && input.expectedRevision !== session.revision) return failure("authoring", startedAt, createStaleWriteError(session, input.expectedRevision), toSessionSnapshot(session, this.context.capabilities), {
458
+ sessionId: session.sessionId,
459
+ revision: session.revision
460
+ });
461
+ const storageTarget = input.storage ? storageCanonicalTarget(input.storage) : null;
462
+ const fileSystem = storageTarget?.fileSystem ?? input.fileSystem ?? session.fileSystem ?? this.context.fileSystem;
463
+ if (!fileSystem) return failure("authoring", startedAt, createCapabilityUnavailableError$1("materializeSession requires an injected BackendFileSystem adapter."), toSessionSnapshot(session, this.context.capabilities), {
464
+ sessionId: session.sessionId,
465
+ revision: session.revision
466
+ });
467
+ const fairyPath = storageTarget?.fairyPath ?? session.fairyPath;
468
+ const targetViolation = await validateSaveTarget(fileSystem, fairyPath, input.targetPath);
469
+ if (targetViolation) return failure("authoring", startedAt, targetViolation, toSessionSnapshot(session, this.context.capabilities), {
470
+ sessionId: session.sessionId,
471
+ revision: session.revision
472
+ });
473
+ if (storageTarget) {
474
+ const holderSessionId = this.context.sessionsByPath.get(storageTarget.canonicalPathKey);
475
+ if (holderSessionId && holderSessionId !== session.sessionId) return failure("authoring", startedAt, {
476
+ code: "lock_conflict",
477
+ kind: "in_process_session_exists",
478
+ message: `Project is already open in this backend runtime: ${storageTarget.canonicalProjectPath}`,
479
+ canonicalPathKey: storageTarget.canonicalPathKey,
480
+ holderSessionId
481
+ }, toSessionSnapshot(session, this.context.capabilities), {
482
+ sessionId: session.sessionId,
483
+ revision: session.revision
484
+ });
485
+ }
486
+ const diagnostics = validationDiagnostics(session.project);
487
+ if (diagnostics.length > 0) return failure("authoring", startedAt, {
488
+ code: "materialize_validation_failed",
489
+ message: `UAM materialize validation failed with ${diagnostics.length} issue(s).`,
490
+ sessionId: session.sessionId,
491
+ canonicalPathKey: session.canonicalPathKey,
492
+ issueCount: diagnostics.length,
493
+ diagnostics
494
+ }, toSessionSnapshot(session, this.context.capabilities), {
495
+ sessionId: session.sessionId,
496
+ revision: session.revision,
497
+ diagnostics
498
+ });
499
+ let document;
500
+ try {
501
+ document = (0, _openfairygui_core_uam.materializeUamProject)(session.project);
502
+ } catch (error) {
503
+ const diagnosticsFromError = [{
504
+ code: "materialize_validation_failed",
505
+ message: error instanceof Error ? error.message : String(error),
506
+ severity: "error",
507
+ operationKind: "materializeSession"
508
+ }];
509
+ return failure("authoring", startedAt, {
510
+ code: "materialize_validation_failed",
511
+ message: error instanceof Error ? error.message : String(error),
512
+ sessionId: session.sessionId,
513
+ canonicalPathKey: session.canonicalPathKey,
514
+ issueCount: diagnosticsFromError.length,
515
+ diagnostics: diagnosticsFromError
516
+ }, toSessionSnapshot(session, this.context.capabilities), {
517
+ sessionId: session.sessionId,
518
+ revision: session.revision,
519
+ diagnostics: diagnosticsFromError
520
+ });
521
+ }
522
+ const writtenPaths = [];
523
+ const failedPaths = [];
524
+ const skippedPaths = [];
525
+ this.eventService.emit({
526
+ kind: "save.started",
527
+ sessionId: session.sessionId,
528
+ canonicalPathKey: storageTarget?.canonicalPathKey ?? session.canonicalPathKey,
529
+ revision: session.revision
530
+ });
531
+ try {
532
+ await new _openfairygui_core_project_io.ProjectWriter(createWriterFileSystem(fileSystem, writtenPaths, failedPaths)).write(document, fairyPath);
533
+ if (storageTarget) {
534
+ this.context.sessionsByPath.delete(session.canonicalPathKey);
535
+ session.fileSystem = storageTarget.fileSystem;
536
+ session.fairyPath = storageTarget.fairyPath;
537
+ session.canonicalProjectPath = storageTarget.canonicalProjectPath;
538
+ session.canonicalPathKey = storageTarget.canonicalPathKey;
539
+ this.context.sessionsByPath.set(session.canonicalPathKey, session.sessionId);
540
+ }
541
+ session.lastSavedRevision = session.revision;
542
+ session.dirty = false;
543
+ const cacheEntry = this.cacheService.refreshSession(session);
544
+ this.eventService.emit({
545
+ kind: "save.completed",
546
+ sessionId: session.sessionId,
547
+ canonicalPathKey: session.canonicalPathKey,
548
+ revision: session.revision
549
+ });
550
+ this.eventService.emit({
551
+ kind: "cache.updated",
552
+ sessionId: session.sessionId,
553
+ canonicalPathKey: session.canonicalPathKey,
554
+ revision: session.revision,
555
+ cacheRevision: cacheEntry.revision
556
+ });
557
+ return success("authoring", startedAt, toMaterializeSnapshot(session, this.context.capabilities, {
558
+ reason: input.reason,
559
+ writtenPaths,
560
+ skippedPaths,
561
+ diagnostics: []
562
+ }), {
563
+ sessionId: session.sessionId,
564
+ revision: session.revision
565
+ });
566
+ } catch (error) {
567
+ const diagnosticsFromError = [{
568
+ code: "write_failed",
569
+ message: error instanceof Error ? error.message : String(error),
570
+ severity: "error",
571
+ path: failedPaths[0],
572
+ operationKind: "materializeSession"
573
+ }];
574
+ this.cacheService.invalidateSession(session);
575
+ this.eventService.emit({
576
+ kind: "save.failed",
577
+ sessionId: session.sessionId,
578
+ canonicalPathKey: session.canonicalPathKey,
579
+ revision: session.revision,
580
+ diagnostics: diagnosticsFromError
581
+ });
582
+ return failure("authoring", startedAt, {
583
+ code: "write_failed",
584
+ message: error instanceof Error ? error.message : String(error),
585
+ sessionId: session.sessionId,
586
+ canonicalPathKey: session.canonicalPathKey,
587
+ attemptedRevision: session.revision,
588
+ lastSavedRevision: session.lastSavedRevision,
589
+ writtenPaths,
590
+ failedPaths,
591
+ skippedPaths,
592
+ diagnostics: diagnosticsFromError,
593
+ diskMayBePartiallyUpdated: true
594
+ }, toSessionSnapshot(session, this.context.capabilities), {
595
+ sessionId: session.sessionId,
596
+ revision: session.revision,
597
+ diagnostics: diagnosticsFromError
598
+ });
599
+ }
600
+ }
403
601
  };
404
602
  //#endregion
405
603
  //#region src/services/cache-service.ts
@@ -892,7 +1090,7 @@ var RuntimeService = class {
892
1090
  canonicalPathKey
893
1091
  }));
894
1092
  await advisoryLock.close();
895
- const project = (0, _openfairygui_core.liftDocumentToUamProject)(await new _openfairygui_core.ProjectReader(createProjectReaderFileSystem(fileSystem)).read(fairyPath));
1093
+ const project = (0, _openfairygui_core_uam.liftDocumentToUamProject)(await new _openfairygui_core_project_io.ProjectReader(createProjectReaderFileSystem(fileSystem)).read(fairyPath));
896
1094
  const sessionId = randomId();
897
1095
  const session = {
898
1096
  sessionId,
@@ -900,6 +1098,7 @@ var RuntimeService = class {
900
1098
  canonicalProjectPath,
901
1099
  canonicalPathKey,
902
1100
  lockFilePath,
1101
+ fileSystem,
903
1102
  project,
904
1103
  revision: 0,
905
1104
  lastSavedRevision: 0,
@@ -938,8 +1137,10 @@ var RuntimeService = class {
938
1137
  openProjectSession(input) {
939
1138
  const startedAt = Date.now();
940
1139
  const sessionId = input.sessionId ?? randomId();
941
- const canonicalProjectPath = input.canonicalProjectPath ?? `memory://${sessionId}`;
942
- const canonicalPathKey = input.canonicalPathKey ?? canonicalProjectPath.toLowerCase();
1140
+ const storage = input.storage;
1141
+ const memoryProjectPath = `memory://${sessionId}`;
1142
+ const canonicalProjectPath = storage?.canonicalProjectPath ?? input.canonicalProjectPath ?? (storage ? storage.fileSystem.dirname(storage.fairyPath) || "." : memoryProjectPath);
1143
+ const canonicalPathKey = storage?.canonicalPathKey ?? input.canonicalPathKey ?? (storage ? normalizeComparablePath(canonicalProjectPath) : canonicalProjectPath.toLowerCase());
943
1144
  const existingSessionId = this.context.sessionsByPath.get(canonicalPathKey);
944
1145
  if (existingSessionId) return failure("runtime", startedAt, {
945
1146
  code: "lock_conflict",
@@ -950,11 +1151,12 @@ var RuntimeService = class {
950
1151
  });
951
1152
  const session = {
952
1153
  sessionId,
953
- fairyPath: canonicalProjectPath,
1154
+ fairyPath: storage?.fairyPath ?? canonicalProjectPath,
954
1155
  canonicalProjectPath,
955
1156
  canonicalPathKey,
956
1157
  lockFilePath: "",
957
- project: (0, _openfairygui_core.normalizeUamProject)(input.project),
1158
+ fileSystem: storage?.fileSystem,
1159
+ project: (0, _openfairygui_core_uam.normalizeUamProject)(input.project),
958
1160
  revision: 0,
959
1161
  lastSavedRevision: 0,
960
1162
  dirty: false,
@@ -1017,6 +1219,7 @@ const BACKEND_METHODS = [
1017
1219
  "getSession",
1018
1220
  "applyTransaction",
1019
1221
  "saveSession",
1222
+ "materializeSession",
1020
1223
  "closeSession",
1021
1224
  "getEvents",
1022
1225
  "getJob",
@@ -1047,9 +1250,14 @@ function createCapabilities() {
1047
1250
  authoring: {
1048
1251
  applyTransaction: true,
1049
1252
  saveSession: true,
1050
- resourceKinds: [..._openfairygui_core.UAM_SUPPORTED_MATERIALIZATION_SCOPE.resourceKinds],
1051
- nodeKinds: [..._openfairygui_core.UAM_SUPPORTED_MATERIALIZATION_SCOPE.nodeKinds],
1052
- gearKinds: [..._openfairygui_core.UAM_SUPPORTED_MATERIALIZATION_SCOPE.gearKinds],
1253
+ resourceKinds: [..._openfairygui_core_uam.UAM_SUPPORTED_MATERIALIZATION_SCOPE.resourceKinds],
1254
+ nodeKinds: [..._openfairygui_core_uam.UAM_SUPPORTED_MATERIALIZATION_SCOPE.nodeKinds],
1255
+ gearKinds: [..._openfairygui_core_uam.UAM_SUPPORTED_MATERIALIZATION_SCOPE.gearKinds],
1256
+ transactionScope: {
1257
+ resourceKinds: [..._openfairygui_core_uam.UAM_SUPPORTED_TRANSACTION_SCOPE.resourceKinds],
1258
+ nodeKinds: [..._openfairygui_core_uam.UAM_SUPPORTED_TRANSACTION_SCOPE.nodeKinds],
1259
+ gearKinds: [..._openfairygui_core_uam.UAM_SUPPORTED_TRANSACTION_SCOPE.gearKinds]
1260
+ },
1053
1261
  unsupported: ["artifact.publish", "artifact.restore"]
1054
1262
  },
1055
1263
  artifact: createArtifactCapabilities(),
@@ -1060,7 +1268,21 @@ function createCapabilities() {
1060
1268
  adapters: {
1061
1269
  fileSystem: {
1062
1270
  injected: true,
1063
- requiredFor: ["openSession", "saveSession"]
1271
+ requiredFor: [
1272
+ "openSession",
1273
+ "saveSession",
1274
+ "materializeSession"
1275
+ ]
1276
+ },
1277
+ projectStorage: {
1278
+ injected: true,
1279
+ browserSafe: true,
1280
+ requiredFor: [
1281
+ "openProjectSession.writeback",
1282
+ "saveSession",
1283
+ "materializeSession"
1284
+ ],
1285
+ adapterFactory: "createBackendStorageFileSystem"
1064
1286
  },
1065
1287
  host: {
1066
1288
  injected: true,
@@ -1167,6 +1389,9 @@ var BackendRuntime = class {
1167
1389
  async saveSession(input) {
1168
1390
  return this.authoringService.saveSession(input);
1169
1391
  }
1392
+ async materializeSession(input) {
1393
+ return this.authoringService.materializeSession(input);
1394
+ }
1170
1395
  async closeSession(input) {
1171
1396
  return this.runtimeService.closeSession(input);
1172
1397
  }
@@ -1,5 +1,5 @@
1
- import { UamProject, UamTransactionOperation } from "@openfairygui/core";
2
- import { ApplyUamTransactionAppError } from "@openfairygui/functions";
1
+ import { UamProject, UamTransactionOperation } from "@openfairygui/core/uam";
2
+ import { ApplyUamTransactionAppError } from "@openfairygui/functions/uam";
3
3
 
4
4
  //#region src/contracts.d.ts
5
5
  declare const BACKEND_CONTRACT_VERSION: "1.1.0-p2";
@@ -18,6 +18,14 @@ interface BackendDiagnostic {
18
18
  code: string;
19
19
  message: string;
20
20
  severity: 'info' | 'warning' | 'error';
21
+ path?: string;
22
+ nodeKind?: string;
23
+ resourceKind?: string;
24
+ gearKind?: string;
25
+ field?: string;
26
+ operationKind?: string;
27
+ opIndex?: number;
28
+ opId?: string;
21
29
  }
22
30
  interface BackendResponseMeta {
23
31
  requestId: string;
@@ -87,7 +95,13 @@ interface BackendCapabilityManifest {
87
95
  adapters: {
88
96
  fileSystem: {
89
97
  injected: true;
90
- requiredFor: readonly ['openSession', 'saveSession'];
98
+ requiredFor: readonly ['openSession', 'saveSession', 'materializeSession'];
99
+ };
100
+ projectStorage: {
101
+ injected: true;
102
+ browserSafe: true;
103
+ requiredFor: readonly ['openProjectSession.writeback', 'saveSession', 'materializeSession'];
104
+ adapterFactory: 'createBackendStorageFileSystem';
91
105
  };
92
106
  host: {
93
107
  injected: true;
@@ -111,7 +125,7 @@ interface BackendCapabilities {
111
125
  transactionKernelOwner: '@openfairygui/core';
112
126
  appSeamOwner: '@openfairygui/functions';
113
127
  runtimeOwner: '@openfairygui/backend';
114
- methods: readonly ['getCapabilities', 'openSession', 'openProjectSession', 'getSession', 'applyTransaction', 'saveSession', 'closeSession', 'getEvents', 'getJob', 'listJobs', 'cancelJob', 'getCacheSnapshot', 'refreshCache'];
128
+ methods: readonly ['getCapabilities', 'openSession', 'openProjectSession', 'getSession', 'applyTransaction', 'saveSession', 'materializeSession', 'closeSession', 'getEvents', 'getJob', 'listJobs', 'cancelJob', 'getCacheSnapshot', 'refreshCache'];
115
129
  read: {
116
130
  capabilitySnapshot: true;
117
131
  sessionSnapshot: true;
@@ -122,6 +136,11 @@ interface BackendCapabilities {
122
136
  resourceKinds: readonly string[];
123
137
  nodeKinds: readonly string[];
124
138
  gearKinds: readonly string[];
139
+ transactionScope: {
140
+ resourceKinds: readonly string[];
141
+ nodeKinds: readonly string[];
142
+ gearKinds: readonly string[];
143
+ };
125
144
  unsupported: readonly ['artifact.publish', 'artifact.restore'];
126
145
  };
127
146
  artifact: {
@@ -177,6 +196,15 @@ interface BackendSessionSnapshot {
177
196
  lockHeld: boolean;
178
197
  capabilities: BackendCapabilities;
179
198
  }
199
+ interface MaterializeSessionSnapshot extends BackendSessionSnapshot {
200
+ mode: 'fullProject';
201
+ reason?: string;
202
+ materializeRevision: number;
203
+ saveRevision: number;
204
+ writtenPaths: string[];
205
+ skippedPaths: string[];
206
+ diagnostics: BackendDiagnostic[];
207
+ }
180
208
  interface BackendSuccess<T> {
181
209
  ok: true;
182
210
  meta: BackendResponseMeta;
@@ -229,6 +257,27 @@ interface SavePartialFailureError {
229
257
  failedPaths: string[];
230
258
  diskMayBePartiallyUpdated: true;
231
259
  }
260
+ interface MaterializeValidationFailedError {
261
+ code: 'materialize_validation_failed';
262
+ message: string;
263
+ sessionId: string;
264
+ canonicalPathKey: string;
265
+ issueCount: number;
266
+ diagnostics: BackendDiagnostic[];
267
+ }
268
+ interface MaterializeWriteFailedError {
269
+ code: 'write_failed';
270
+ message: string;
271
+ sessionId: string;
272
+ canonicalPathKey: string;
273
+ attemptedRevision: number;
274
+ lastSavedRevision: number;
275
+ writtenPaths: string[];
276
+ failedPaths: string[];
277
+ skippedPaths: string[];
278
+ diagnostics: BackendDiagnostic[];
279
+ diskMayBePartiallyUpdated: true;
280
+ }
232
281
  type BackendEventKind = 'session.opened' | 'transaction.applied' | 'transaction.rejected' | 'save.started' | 'save.completed' | 'save.failed' | 'session.closeRequested' | 'session.closed' | 'cache.invalidated' | 'cache.updated' | 'job.created' | 'job.started' | 'job.progress' | 'job.cancelRequested' | 'job.cancelled' | 'job.completed' | 'job.failed';
233
282
  interface BackendEvent {
234
283
  sequence: number;
@@ -360,7 +409,7 @@ interface RefreshCacheInput {
360
409
  sessionId: string;
361
410
  reason?: 'manual' | 'session_open' | 'after_save';
362
411
  }
363
- type BackendError = SessionNotFoundError | SessionStaleWriteError | InProcessLockConflictError | AdvisoryLockConflictError | SavePartialFailureError | PathPolicyViolationError | EventCursorInvalidError | BackendJobNotFoundError | BackendJobNotCancellableError | BackendJobCancelledError | CacheRefreshFailedError | BackendCapabilityUnavailableError | ApplyUamTransactionAppError;
412
+ type BackendError = SessionNotFoundError | SessionStaleWriteError | InProcessLockConflictError | AdvisoryLockConflictError | SavePartialFailureError | MaterializeValidationFailedError | MaterializeWriteFailedError | PathPolicyViolationError | EventCursorInvalidError | BackendJobNotFoundError | BackendJobNotCancellableError | BackendJobCancelledError | CacheRefreshFailedError | BackendCapabilityUnavailableError | ApplyUamTransactionAppError;
364
413
  interface ApplySessionTransactionInput {
365
414
  sessionId: string;
366
415
  expectedRevision: number;
@@ -371,6 +420,30 @@ interface OpenProjectSessionInput {
371
420
  sessionId?: string;
372
421
  canonicalProjectPath?: string;
373
422
  canonicalPathKey?: string;
423
+ storage?: BackendProjectSessionStorage;
424
+ }
425
+ interface BackendProjectSessionStorage {
426
+ fileSystem: BackendFileSystem;
427
+ fairyPath: string;
428
+ canonicalProjectPath?: string;
429
+ canonicalPathKey?: string;
430
+ }
431
+ interface SaveSessionInput {
432
+ sessionId: string;
433
+ expectedRevision?: number;
434
+ targetPath?: string;
435
+ fileSystem?: BackendFileSystem;
436
+ force?: boolean;
437
+ mode?: 'materializeCleanSession';
438
+ }
439
+ interface MaterializeSessionInput {
440
+ sessionId: string;
441
+ expectedRevision?: number;
442
+ storage?: BackendProjectSessionStorage;
443
+ targetPath?: string;
444
+ fileSystem?: BackendFileSystem;
445
+ mode?: 'fullProject';
446
+ reason?: string;
374
447
  }
375
448
  interface BackendRuntimeOptions {
376
449
  fileSystem?: BackendFileSystem;
@@ -402,11 +475,8 @@ declare class BackendRuntime {
402
475
  sessionId: string;
403
476
  }): BackendResult<BackendSessionSnapshot, SessionNotFoundError>;
404
477
  applyTransaction(input: ApplySessionTransactionInput): Promise<BackendResult<BackendSessionSnapshot, SessionNotFoundError | SessionStaleWriteError | ApplyUamTransactionAppError>>;
405
- saveSession(input: {
406
- sessionId: string;
407
- expectedRevision?: number;
408
- targetPath?: string;
409
- }): Promise<BackendResult<BackendSessionSnapshot, SessionNotFoundError | SessionStaleWriteError | SavePartialFailureError | PathPolicyViolationError | BackendCapabilityUnavailableError>>;
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>>;
410
480
  closeSession(input: {
411
481
  sessionId: string;
412
482
  }): Promise<BackendResult<{
@@ -421,4 +491,4 @@ declare class BackendRuntime {
421
491
  refreshCache(input: RefreshCacheInput): BackendResult<BackendJobSnapshot, SessionNotFoundError>;
422
492
  }
423
493
  //#endregion
424
- export { BackendSuccess as A, OpenProjectSessionInput as B, BackendJobProgress as C, BackendRuntime as D, BackendResult as E, GetEventsInput as F, BACKEND_CAPABILITY_SCHEMA_VERSION as G, SavePartialFailureError as H, GetEventsSnapshot as I, BackendDiagnostic as J, BACKEND_COMPATIBILITY_POLICY as K, GetJobInput as L, CancelJobInput as M, EventCursorInvalidError as N, BackendRuntimeOptions as O, GetCacheSnapshotInput as P, InProcessLockConflictError as R, BackendJobNotFoundError as S, BackendJobStatus as T, SessionNotFoundError as U, RefreshCacheInput as V, SessionStaleWriteError as W, BackendResponseMeta as X, BackendMessage as Y, BackendStage as Z, BackendJobErrors as _, BackendCacheSnapshot as a, BackendJobListStatusFilter as b, BackendCapabilityUnavailableError as c, BackendEventKind as d, BackendFailure as f, BackendHostAdapter as g, BackendFileSystem as h, BackendCacheEntry as i, CacheRefreshFailedError as j, BackendSessionSnapshot as k, BackendError as l, BackendFileStat as m, ApplySessionTransactionInput as n, BackendCapabilities as o, BackendFileHandle as p, BACKEND_CONTRACT_VERSION as q, BackendArtifactBridgeCapability as r, BackendCapabilityManifest as s, AdvisoryLockConflictError as t, BackendEvent as u, BackendJobKind as v, BackendJobSnapshot as w, BackendJobNotCancellableError as x, BackendJobListSnapshot as y, ListJobsInput as z };
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 };