@openfairygui/backend 0.2.0-alpha.3 → 0.2.0-alpha.31
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 +48 -2
- package/dist/index.cjs +150 -1
- package/dist/index.d.cts +34 -2
- package/dist/index.d.mts +34 -2
- package/dist/index.mjs +150 -2
- package/dist/node.cjs +4 -1
- package/dist/node.d.cts +1 -1
- package/dist/node.d.mts +1 -1
- package/dist/node.mjs +4 -1
- package/dist/{runtime-BG5qOVkJ.mjs → runtime-BDkZmTFN.mjs} +480 -126
- package/dist/{runtime-IrXqqmko.d.cts → runtime-BQOEw22M.d.mts} +85 -10
- package/dist/{runtime-jKuQmcqM.cjs → runtime-DtcaaVfy.cjs} +479 -125
- package/dist/{runtime-u1Bq_ysz.d.mts → runtime-Wu6vGZ-j.d.cts} +86 -11
- package/package.json +5 -4
- package/src/contracts.ts +8 -0
- package/src/index.ts +25 -12
- package/src/node.ts +3 -0
- package/src/runtime/capabilities.ts +126 -0
- package/src/runtime/contracts.ts +513 -0
- package/src/runtime.ts +67 -551
- package/src/services/authoring-service.ts +504 -81
- package/src/services/cache-service.ts +1 -2
- package/src/services/context.ts +14 -8
- package/src/services/event-service.ts +1 -2
- package/src/services/job-service.ts +4 -5
- package/src/services/read-service.ts +1 -2
- package/src/services/runtime-service.ts +122 -12
- package/src/services/session-project-writer.ts +67 -0
- package/src/services/session-utils.ts +6 -3
- package/src/storage.ts +201 -0
- package/src/services/snapshot-utils.ts +0 -43
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { UAM_SUPPORTED_MATERIALIZATION_SCOPE, UAM_SUPPORTED_TRANSACTION_SCOPE, liftDocumentToUamProject, materializeUamProject, normalizeUamProject } from "@openfairygui/core/uam";
|
|
2
|
-
import { ProjectReader, ProjectWriter } from "@openfairygui/core/project-io";
|
|
1
|
+
import { UAM_SUPPORTED_MATERIALIZATION_SCOPE, UAM_SUPPORTED_TRANSACTION_SCOPE, commitUamProjectSourcePaths, liftDocumentToUamProject, materializeUamProject, normalizeUamProject, staleResourceFolders, staleSourceFiles, validateUamProject } from "@openfairygui/core/uam";
|
|
3
2
|
import { applyUamTransactionApp } from "@openfairygui/functions/uam";
|
|
3
|
+
import { ProjectReader, ProjectWriter } from "@openfairygui/core/project-io";
|
|
4
4
|
//#region src/contracts.ts
|
|
5
5
|
const BACKEND_CONTRACT_VERSION = "1.1.0-p2";
|
|
6
6
|
const BACKEND_CAPABILITY_SCHEMA_VERSION = 2;
|
|
@@ -75,24 +75,6 @@ async function validateSaveTarget(fileSystem, openedFairyPath, targetPath) {
|
|
|
75
75
|
};
|
|
76
76
|
}
|
|
77
77
|
//#endregion
|
|
78
|
-
//#region src/services/artifact-service.ts
|
|
79
|
-
function createArtifactCapabilities() {
|
|
80
|
-
const bridge = {
|
|
81
|
-
available: false,
|
|
82
|
-
requiredHost: "node",
|
|
83
|
-
executionBoundary: "external-bridge",
|
|
84
|
-
bridgeEntrypoint: "@openfairygui/backend/node",
|
|
85
|
-
reason: "publish/restore require explicit Node-hosted filesystem and artifact execution."
|
|
86
|
-
};
|
|
87
|
-
return {
|
|
88
|
-
publish: false,
|
|
89
|
-
restore: false,
|
|
90
|
-
status: "bridge-required",
|
|
91
|
-
publishBridge: bridge,
|
|
92
|
-
restoreBridge: bridge
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
//#endregion
|
|
96
78
|
//#region src/services/context.ts
|
|
97
79
|
function diagnosticFromError(error) {
|
|
98
80
|
return {
|
|
@@ -137,38 +119,50 @@ function failure(stage, startedAt, error, session, options) {
|
|
|
137
119
|
};
|
|
138
120
|
}
|
|
139
121
|
//#endregion
|
|
140
|
-
//#region src/services/
|
|
141
|
-
function
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
};
|
|
151
|
-
}
|
|
152
|
-
function cloneJobSnapshot(job) {
|
|
153
|
-
return {
|
|
154
|
-
...job,
|
|
155
|
-
diagnostics: job.diagnostics.map((diagnostic) => ({ ...diagnostic })),
|
|
156
|
-
progress: job.progress ? { ...job.progress } : void 0,
|
|
157
|
-
result: cloneJsonValue(job.result),
|
|
158
|
-
error: cloneJsonValue(job.error)
|
|
159
|
-
};
|
|
160
|
-
}
|
|
161
|
-
function cloneCacheEntrySnapshot(entry) {
|
|
162
|
-
return {
|
|
163
|
-
...entry,
|
|
164
|
-
summary: {
|
|
165
|
-
...entry.summary,
|
|
166
|
-
diagnostics: entry.summary.diagnostics.map((diagnostic) => ({ ...diagnostic }))
|
|
122
|
+
//#region src/services/session-project-writer.ts
|
|
123
|
+
function createWriterFileSystem(fileSystem, writtenPaths, failedPaths) {
|
|
124
|
+
async function trackWrite(targetPath, write) {
|
|
125
|
+
try {
|
|
126
|
+
const result = await write();
|
|
127
|
+
writtenPaths.push(targetPath);
|
|
128
|
+
return result;
|
|
129
|
+
} catch (error) {
|
|
130
|
+
failedPaths.push(targetPath);
|
|
131
|
+
throw error;
|
|
167
132
|
}
|
|
133
|
+
}
|
|
134
|
+
return {
|
|
135
|
+
readFile: (path) => fileSystem.readFile(path),
|
|
136
|
+
readFileRaw: (path) => fileSystem.readFileRaw(path),
|
|
137
|
+
writeFile: (path, content) => trackWrite(path, async () => {
|
|
138
|
+
await fileSystem.mkdir(fileSystem.dirname(path), { recursive: true });
|
|
139
|
+
await fileSystem.writeFile(path, content);
|
|
140
|
+
}),
|
|
141
|
+
writeFileRaw: (path, data) => trackWrite(path, async () => {
|
|
142
|
+
await fileSystem.mkdir(fileSystem.dirname(path), { recursive: true });
|
|
143
|
+
await fileSystem.writeFileRaw(path, data);
|
|
144
|
+
}),
|
|
145
|
+
mkdir: (path) => fileSystem.mkdir(path, { recursive: true }),
|
|
146
|
+
readdir: (path) => fileSystem.readdir(path),
|
|
147
|
+
async exists(path) {
|
|
148
|
+
try {
|
|
149
|
+
await fileSystem.stat(path);
|
|
150
|
+
return true;
|
|
151
|
+
} catch {
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
join: (...paths) => fileSystem.join(...paths),
|
|
156
|
+
dirname: (path) => fileSystem.dirname(path),
|
|
157
|
+
unlink: (path) => trackWrite(path, () => fileSystem.unlink(path)),
|
|
158
|
+
rmdir: (path) => trackWrite(path, () => fileSystem.rmdir(path))
|
|
168
159
|
};
|
|
169
160
|
}
|
|
170
|
-
function
|
|
171
|
-
|
|
161
|
+
async function writeSessionProject(input) {
|
|
162
|
+
await new ProjectWriter(createWriterFileSystem(input.fileSystem, input.writtenPaths, input.failedPaths)).write(input.document, input.fairyPath, {
|
|
163
|
+
staleSourceFiles: input.staleSourceFiles,
|
|
164
|
+
staleResourceFolders: input.staleResourceFolders
|
|
165
|
+
});
|
|
172
166
|
}
|
|
173
167
|
//#endregion
|
|
174
168
|
//#region src/services/session-utils.ts
|
|
@@ -179,8 +173,9 @@ function toSessionSnapshot(session, capabilities) {
|
|
|
179
173
|
revision: session.revision,
|
|
180
174
|
lastSavedRevision: session.lastSavedRevision,
|
|
181
175
|
dirty: session.dirty,
|
|
176
|
+
uamFidelity: session.uamFidelity,
|
|
182
177
|
lockHeld: session.lockHeld,
|
|
183
|
-
capabilities:
|
|
178
|
+
capabilities: structuredClone(capabilities)
|
|
184
179
|
};
|
|
185
180
|
}
|
|
186
181
|
function createSessionNotFoundError(sessionId) {
|
|
@@ -202,65 +197,111 @@ function createStaleWriteError(session, expectedRevision) {
|
|
|
202
197
|
}
|
|
203
198
|
//#endregion
|
|
204
199
|
//#region src/services/authoring-service.ts
|
|
205
|
-
function
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
200
|
+
function sourceFileKey(source) {
|
|
201
|
+
return [
|
|
202
|
+
source.branch,
|
|
203
|
+
source.packageName,
|
|
204
|
+
source.path,
|
|
205
|
+
source.fileName
|
|
206
|
+
].join("\0");
|
|
207
|
+
}
|
|
208
|
+
function resourceFolderKey(folder) {
|
|
209
|
+
return [
|
|
210
|
+
folder.branch,
|
|
211
|
+
folder.packageName,
|
|
212
|
+
folder.path
|
|
213
|
+
].join("\0");
|
|
214
|
+
}
|
|
215
|
+
function recordStaleProjectFiles(session, previousProject, nextProject) {
|
|
216
|
+
if (!session.fileSystem) return;
|
|
217
|
+
for (const source of staleSourceFiles(previousProject, nextProject)) session.pendingStaleSourceFiles.set(sourceFileKey(source), source);
|
|
218
|
+
for (const source of staleSourceFiles(nextProject, previousProject)) session.pendingStaleSourceFiles.delete(sourceFileKey(source));
|
|
219
|
+
for (const folder of staleResourceFolders(previousProject, nextProject)) session.pendingStaleResourceFolders.set(resourceFolderKey(folder), folder);
|
|
220
|
+
for (const folder of staleResourceFolders(nextProject, previousProject)) session.pendingStaleResourceFolders.delete(resourceFolderKey(folder));
|
|
221
|
+
}
|
|
222
|
+
function toBackendDiagnostics(error) {
|
|
223
|
+
return error.diagnostics.length > 0 ? error.diagnostics.map((diagnostic) => ({ ...diagnostic })) : [{
|
|
224
|
+
code: error.code,
|
|
225
|
+
message: error.message,
|
|
226
|
+
severity: "error",
|
|
227
|
+
operationKind: error.operationKind,
|
|
228
|
+
opIndex: error.opIndex,
|
|
229
|
+
opId: error.opId
|
|
230
|
+
}];
|
|
231
|
+
}
|
|
232
|
+
function createCapabilityUnavailableError$1(message) {
|
|
216
233
|
return {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
234
|
+
code: "capability_unavailable",
|
|
235
|
+
message,
|
|
236
|
+
capability: "fileSystem",
|
|
237
|
+
requiredAdapter: "BackendFileSystem"
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
function createUamFidelityUnsupportedError(session) {
|
|
241
|
+
return {
|
|
242
|
+
code: "uam_fidelity_unsupported",
|
|
243
|
+
message: "The source project contains formal properties that the current UAM cannot preserve.",
|
|
244
|
+
sessionId: session.sessionId,
|
|
245
|
+
canonicalPathKey: session.canonicalPathKey
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
function validationDiagnostics(sessionProject) {
|
|
249
|
+
return validateUamProject(sessionProject).map((issue) => ({
|
|
250
|
+
code: "materialize_validation_failed",
|
|
251
|
+
message: issue.message,
|
|
252
|
+
severity: "error",
|
|
253
|
+
path: issue.path,
|
|
254
|
+
operationKind: "materializeSession"
|
|
255
|
+
}));
|
|
256
|
+
}
|
|
257
|
+
function toMaterializeSnapshot(session, capabilities, input) {
|
|
258
|
+
return {
|
|
259
|
+
...toSessionSnapshot(session, capabilities),
|
|
260
|
+
mode: "fullProject",
|
|
261
|
+
reason: input.reason,
|
|
262
|
+
materializeRevision: session.revision,
|
|
263
|
+
saveRevision: session.lastSavedRevision,
|
|
264
|
+
writtenPaths: [...input.writtenPaths],
|
|
265
|
+
skippedPaths: [...input.skippedPaths],
|
|
266
|
+
diagnostics: input.diagnostics.map((diagnostic) => ({ ...diagnostic }))
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
function storageCanonicalTarget(input) {
|
|
270
|
+
const canonicalProjectPath = input.canonicalProjectPath ?? (input.fileSystem.dirname(input.fairyPath) || ".");
|
|
271
|
+
return {
|
|
272
|
+
fileSystem: input.fileSystem,
|
|
273
|
+
fairyPath: input.fairyPath,
|
|
274
|
+
canonicalProjectPath,
|
|
275
|
+
canonicalPathKey: input.canonicalPathKey ?? normalizeComparablePath(canonicalProjectPath)
|
|
255
276
|
};
|
|
256
277
|
}
|
|
257
278
|
var AuthoringService = class {
|
|
279
|
+
sessionOperations = /* @__PURE__ */ new Map();
|
|
258
280
|
constructor(context, cacheService, eventService) {
|
|
259
281
|
this.context = context;
|
|
260
282
|
this.cacheService = cacheService;
|
|
261
283
|
this.eventService = eventService;
|
|
262
284
|
}
|
|
285
|
+
async runSessionExclusive(sessionId, operation) {
|
|
286
|
+
const previous = this.sessionOperations.get(sessionId) ?? Promise.resolve();
|
|
287
|
+
let release = () => void 0;
|
|
288
|
+
const current = new Promise((resolve) => {
|
|
289
|
+
release = resolve;
|
|
290
|
+
});
|
|
291
|
+
const tail = previous.then(() => current);
|
|
292
|
+
this.sessionOperations.set(sessionId, tail);
|
|
293
|
+
await previous;
|
|
294
|
+
try {
|
|
295
|
+
return await operation();
|
|
296
|
+
} finally {
|
|
297
|
+
release();
|
|
298
|
+
if (this.sessionOperations.get(sessionId) === tail) this.sessionOperations.delete(sessionId);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
263
301
|
async applyTransaction(input) {
|
|
302
|
+
return this.runSessionExclusive(input.sessionId, () => this.applyTransactionExclusive(input));
|
|
303
|
+
}
|
|
304
|
+
async applyTransactionExclusive(input) {
|
|
264
305
|
const startedAt = Date.now();
|
|
265
306
|
const session = this.context.sessions.get(input.sessionId);
|
|
266
307
|
if (!session || session.closed) return failure("authoring", startedAt, createSessionNotFoundError(input.sessionId));
|
|
@@ -281,22 +322,21 @@ var AuthoringService = class {
|
|
|
281
322
|
operations: input.operations
|
|
282
323
|
});
|
|
283
324
|
if (result.ok === false) {
|
|
325
|
+
const diagnostics = toBackendDiagnostics(result.error);
|
|
284
326
|
this.eventService.emit({
|
|
285
327
|
kind: "transaction.rejected",
|
|
286
328
|
sessionId: session.sessionId,
|
|
287
329
|
canonicalPathKey: session.canonicalPathKey,
|
|
288
330
|
revision: session.revision,
|
|
289
|
-
diagnostics
|
|
290
|
-
code: result.error.code,
|
|
291
|
-
message: issue.message,
|
|
292
|
-
severity: "error"
|
|
293
|
-
})) ?? []
|
|
331
|
+
diagnostics
|
|
294
332
|
});
|
|
295
333
|
return failure("authoring", startedAt, result.error, toSessionSnapshot(session, this.context.capabilities), {
|
|
296
334
|
sessionId: session.sessionId,
|
|
297
|
-
revision: session.revision
|
|
335
|
+
revision: session.revision,
|
|
336
|
+
diagnostics
|
|
298
337
|
});
|
|
299
338
|
}
|
|
339
|
+
recordStaleProjectFiles(session, session.project, result.project);
|
|
300
340
|
session.project = result.project;
|
|
301
341
|
session.revision += 1;
|
|
302
342
|
session.dirty = true;
|
|
@@ -320,15 +360,22 @@ var AuthoringService = class {
|
|
|
320
360
|
});
|
|
321
361
|
}
|
|
322
362
|
async saveSession(input) {
|
|
363
|
+
if (input.force === true || input.mode === "materializeCleanSession") return this.materializeSession({
|
|
364
|
+
sessionId: input.sessionId,
|
|
365
|
+
expectedRevision: input.expectedRevision,
|
|
366
|
+
targetPath: input.targetPath,
|
|
367
|
+
fileSystem: input.fileSystem,
|
|
368
|
+
mode: "fullProject",
|
|
369
|
+
reason: "force_save"
|
|
370
|
+
});
|
|
371
|
+
return this.runSessionExclusive(input.sessionId, () => this.saveSessionExclusive(input));
|
|
372
|
+
}
|
|
373
|
+
async saveSessionExclusive(input) {
|
|
323
374
|
const startedAt = Date.now();
|
|
324
375
|
const session = this.context.sessions.get(input.sessionId);
|
|
325
376
|
if (!session || session.closed) return failure("authoring", startedAt, createSessionNotFoundError(input.sessionId));
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
message: "saveSession requires an injected BackendFileSystem adapter.",
|
|
329
|
-
capability: "fileSystem",
|
|
330
|
-
requiredAdapter: "BackendFileSystem"
|
|
331
|
-
}, toSessionSnapshot(session, this.context.capabilities), {
|
|
377
|
+
const fileSystem = session.fileSystem ?? input.fileSystem ?? this.context.fileSystem;
|
|
378
|
+
if (!fileSystem) return failure("authoring", startedAt, createCapabilityUnavailableError$1("saveSession requires an injected BackendFileSystem adapter."), toSessionSnapshot(session, this.context.capabilities), {
|
|
332
379
|
sessionId: session.sessionId,
|
|
333
380
|
revision: session.revision
|
|
334
381
|
});
|
|
@@ -336,7 +383,6 @@ var AuthoringService = class {
|
|
|
336
383
|
sessionId: session.sessionId,
|
|
337
384
|
revision: session.revision
|
|
338
385
|
});
|
|
339
|
-
const fileSystem = this.context.fileSystem;
|
|
340
386
|
const targetViolation = await validateSaveTarget(fileSystem, session.fairyPath, input.targetPath);
|
|
341
387
|
if (targetViolation) return failure("authoring", startedAt, targetViolation, toSessionSnapshot(session, this.context.capabilities), {
|
|
342
388
|
sessionId: session.sessionId,
|
|
@@ -346,6 +392,10 @@ var AuthoringService = class {
|
|
|
346
392
|
sessionId: session.sessionId,
|
|
347
393
|
revision: session.revision
|
|
348
394
|
});
|
|
395
|
+
if (session.uamFidelity === "unsupported") return failure("authoring", startedAt, createUamFidelityUnsupportedError(session), toSessionSnapshot(session, this.context.capabilities), {
|
|
396
|
+
sessionId: session.sessionId,
|
|
397
|
+
revision: session.revision
|
|
398
|
+
});
|
|
349
399
|
const committedPaths = [];
|
|
350
400
|
const failedPaths = [];
|
|
351
401
|
this.eventService.emit({
|
|
@@ -355,7 +405,19 @@ var AuthoringService = class {
|
|
|
355
405
|
revision: session.revision
|
|
356
406
|
});
|
|
357
407
|
try {
|
|
358
|
-
await
|
|
408
|
+
await writeSessionProject({
|
|
409
|
+
fileSystem,
|
|
410
|
+
document: materializeUamProject(session.project),
|
|
411
|
+
fairyPath: session.fairyPath,
|
|
412
|
+
staleSourceFiles: [...session.pendingStaleSourceFiles.values()],
|
|
413
|
+
staleResourceFolders: [...session.pendingStaleResourceFolders.values()],
|
|
414
|
+
writtenPaths: committedPaths,
|
|
415
|
+
failedPaths
|
|
416
|
+
});
|
|
417
|
+
session.fileSystem ??= fileSystem;
|
|
418
|
+
session.pendingStaleSourceFiles.clear();
|
|
419
|
+
session.pendingStaleResourceFolders.clear();
|
|
420
|
+
commitUamProjectSourcePaths(session.project);
|
|
359
421
|
session.lastSavedRevision = session.revision;
|
|
360
422
|
session.dirty = false;
|
|
361
423
|
const cacheEntry = this.cacheService.refreshSession(session);
|
|
@@ -400,6 +462,179 @@ var AuthoringService = class {
|
|
|
400
462
|
});
|
|
401
463
|
}
|
|
402
464
|
}
|
|
465
|
+
async materializeSession(input) {
|
|
466
|
+
return this.runSessionExclusive(input.sessionId, () => this.materializeSessionExclusive(input));
|
|
467
|
+
}
|
|
468
|
+
async materializeSessionExclusive(input) {
|
|
469
|
+
const startedAt = Date.now();
|
|
470
|
+
const session = this.context.sessions.get(input.sessionId);
|
|
471
|
+
if (!session || session.closed) return failure("authoring", startedAt, createSessionNotFoundError(input.sessionId));
|
|
472
|
+
if (input.expectedRevision !== void 0 && input.expectedRevision !== session.revision) return failure("authoring", startedAt, createStaleWriteError(session, input.expectedRevision), toSessionSnapshot(session, this.context.capabilities), {
|
|
473
|
+
sessionId: session.sessionId,
|
|
474
|
+
revision: session.revision
|
|
475
|
+
});
|
|
476
|
+
const storageTarget = input.storage ? storageCanonicalTarget(input.storage) : null;
|
|
477
|
+
const fileSystem = storageTarget?.fileSystem ?? input.fileSystem ?? session.fileSystem ?? this.context.fileSystem;
|
|
478
|
+
if (!fileSystem) return failure("authoring", startedAt, createCapabilityUnavailableError$1("materializeSession requires an injected BackendFileSystem adapter."), toSessionSnapshot(session, this.context.capabilities), {
|
|
479
|
+
sessionId: session.sessionId,
|
|
480
|
+
revision: session.revision
|
|
481
|
+
});
|
|
482
|
+
const fairyPath = storageTarget?.fairyPath ?? session.fairyPath;
|
|
483
|
+
const targetViolation = await validateSaveTarget(fileSystem, fairyPath, input.targetPath);
|
|
484
|
+
if (targetViolation) return failure("authoring", startedAt, targetViolation, toSessionSnapshot(session, this.context.capabilities), {
|
|
485
|
+
sessionId: session.sessionId,
|
|
486
|
+
revision: session.revision
|
|
487
|
+
});
|
|
488
|
+
if (storageTarget) {
|
|
489
|
+
const holderSessionId = this.context.sessionsByPath.get(storageTarget.canonicalPathKey);
|
|
490
|
+
if (holderSessionId && holderSessionId !== session.sessionId) return failure("authoring", startedAt, {
|
|
491
|
+
code: "lock_conflict",
|
|
492
|
+
kind: "in_process_session_exists",
|
|
493
|
+
message: `Project is already open in this backend runtime: ${storageTarget.canonicalProjectPath}`,
|
|
494
|
+
canonicalPathKey: storageTarget.canonicalPathKey,
|
|
495
|
+
holderSessionId
|
|
496
|
+
}, toSessionSnapshot(session, this.context.capabilities), {
|
|
497
|
+
sessionId: session.sessionId,
|
|
498
|
+
revision: session.revision
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
if (session.uamFidelity === "unsupported") return failure("authoring", startedAt, createUamFidelityUnsupportedError(session), toSessionSnapshot(session, this.context.capabilities), {
|
|
502
|
+
sessionId: session.sessionId,
|
|
503
|
+
revision: session.revision
|
|
504
|
+
});
|
|
505
|
+
const diagnostics = validationDiagnostics(session.project);
|
|
506
|
+
if (diagnostics.length > 0) return failure("authoring", startedAt, {
|
|
507
|
+
code: "materialize_validation_failed",
|
|
508
|
+
message: `UAM materialize validation failed with ${diagnostics.length} issue(s).`,
|
|
509
|
+
sessionId: session.sessionId,
|
|
510
|
+
canonicalPathKey: session.canonicalPathKey,
|
|
511
|
+
issueCount: diagnostics.length,
|
|
512
|
+
diagnostics
|
|
513
|
+
}, toSessionSnapshot(session, this.context.capabilities), {
|
|
514
|
+
sessionId: session.sessionId,
|
|
515
|
+
revision: session.revision,
|
|
516
|
+
diagnostics
|
|
517
|
+
});
|
|
518
|
+
let document;
|
|
519
|
+
try {
|
|
520
|
+
document = materializeUamProject(session.project);
|
|
521
|
+
} catch (error) {
|
|
522
|
+
const diagnosticsFromError = [{
|
|
523
|
+
code: "materialize_validation_failed",
|
|
524
|
+
message: error instanceof Error ? error.message : String(error),
|
|
525
|
+
severity: "error",
|
|
526
|
+
operationKind: "materializeSession"
|
|
527
|
+
}];
|
|
528
|
+
return failure("authoring", startedAt, {
|
|
529
|
+
code: "materialize_validation_failed",
|
|
530
|
+
message: error instanceof Error ? error.message : String(error),
|
|
531
|
+
sessionId: session.sessionId,
|
|
532
|
+
canonicalPathKey: session.canonicalPathKey,
|
|
533
|
+
issueCount: diagnosticsFromError.length,
|
|
534
|
+
diagnostics: diagnosticsFromError
|
|
535
|
+
}, toSessionSnapshot(session, this.context.capabilities), {
|
|
536
|
+
sessionId: session.sessionId,
|
|
537
|
+
revision: session.revision,
|
|
538
|
+
diagnostics: diagnosticsFromError
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
const writtenPaths = [];
|
|
542
|
+
const failedPaths = [];
|
|
543
|
+
const skippedPaths = [];
|
|
544
|
+
this.eventService.emit({
|
|
545
|
+
kind: "save.started",
|
|
546
|
+
sessionId: session.sessionId,
|
|
547
|
+
canonicalPathKey: storageTarget?.canonicalPathKey ?? session.canonicalPathKey,
|
|
548
|
+
revision: session.revision
|
|
549
|
+
});
|
|
550
|
+
try {
|
|
551
|
+
const isSessionStorageTarget = fileSystem === session.fileSystem && fairyPath === session.fairyPath;
|
|
552
|
+
await writeSessionProject({
|
|
553
|
+
fileSystem,
|
|
554
|
+
document,
|
|
555
|
+
fairyPath,
|
|
556
|
+
staleSourceFiles: isSessionStorageTarget ? [...session.pendingStaleSourceFiles.values()] : [],
|
|
557
|
+
staleResourceFolders: isSessionStorageTarget ? [...session.pendingStaleResourceFolders.values()] : [],
|
|
558
|
+
writtenPaths,
|
|
559
|
+
failedPaths
|
|
560
|
+
});
|
|
561
|
+
if (isSessionStorageTarget) {
|
|
562
|
+
session.pendingStaleSourceFiles.clear();
|
|
563
|
+
session.pendingStaleResourceFolders.clear();
|
|
564
|
+
}
|
|
565
|
+
if (storageTarget && !isSessionStorageTarget) {
|
|
566
|
+
session.pendingStaleSourceFiles.clear();
|
|
567
|
+
session.pendingStaleResourceFolders.clear();
|
|
568
|
+
}
|
|
569
|
+
if (isSessionStorageTarget || storageTarget) commitUamProjectSourcePaths(session.project);
|
|
570
|
+
if (storageTarget) {
|
|
571
|
+
this.context.sessionsByPath.delete(session.canonicalPathKey);
|
|
572
|
+
session.fileSystem = storageTarget.fileSystem;
|
|
573
|
+
session.fairyPath = storageTarget.fairyPath;
|
|
574
|
+
session.canonicalProjectPath = storageTarget.canonicalProjectPath;
|
|
575
|
+
session.canonicalPathKey = storageTarget.canonicalPathKey;
|
|
576
|
+
this.context.sessionsByPath.set(session.canonicalPathKey, session.sessionId);
|
|
577
|
+
}
|
|
578
|
+
session.lastSavedRevision = session.revision;
|
|
579
|
+
session.dirty = false;
|
|
580
|
+
const cacheEntry = this.cacheService.refreshSession(session);
|
|
581
|
+
this.eventService.emit({
|
|
582
|
+
kind: "save.completed",
|
|
583
|
+
sessionId: session.sessionId,
|
|
584
|
+
canonicalPathKey: session.canonicalPathKey,
|
|
585
|
+
revision: session.revision
|
|
586
|
+
});
|
|
587
|
+
this.eventService.emit({
|
|
588
|
+
kind: "cache.updated",
|
|
589
|
+
sessionId: session.sessionId,
|
|
590
|
+
canonicalPathKey: session.canonicalPathKey,
|
|
591
|
+
revision: session.revision,
|
|
592
|
+
cacheRevision: cacheEntry.revision
|
|
593
|
+
});
|
|
594
|
+
return success("authoring", startedAt, toMaterializeSnapshot(session, this.context.capabilities, {
|
|
595
|
+
reason: input.reason,
|
|
596
|
+
writtenPaths,
|
|
597
|
+
skippedPaths,
|
|
598
|
+
diagnostics: []
|
|
599
|
+
}), {
|
|
600
|
+
sessionId: session.sessionId,
|
|
601
|
+
revision: session.revision
|
|
602
|
+
});
|
|
603
|
+
} catch (error) {
|
|
604
|
+
const diagnosticsFromError = [{
|
|
605
|
+
code: "write_failed",
|
|
606
|
+
message: error instanceof Error ? error.message : String(error),
|
|
607
|
+
severity: "error",
|
|
608
|
+
path: failedPaths[0],
|
|
609
|
+
operationKind: "materializeSession"
|
|
610
|
+
}];
|
|
611
|
+
this.cacheService.invalidateSession(session);
|
|
612
|
+
this.eventService.emit({
|
|
613
|
+
kind: "save.failed",
|
|
614
|
+
sessionId: session.sessionId,
|
|
615
|
+
canonicalPathKey: session.canonicalPathKey,
|
|
616
|
+
revision: session.revision,
|
|
617
|
+
diagnostics: diagnosticsFromError
|
|
618
|
+
});
|
|
619
|
+
return failure("authoring", startedAt, {
|
|
620
|
+
code: "write_failed",
|
|
621
|
+
message: error instanceof Error ? error.message : String(error),
|
|
622
|
+
sessionId: session.sessionId,
|
|
623
|
+
canonicalPathKey: session.canonicalPathKey,
|
|
624
|
+
attemptedRevision: session.revision,
|
|
625
|
+
lastSavedRevision: session.lastSavedRevision,
|
|
626
|
+
writtenPaths,
|
|
627
|
+
failedPaths,
|
|
628
|
+
skippedPaths,
|
|
629
|
+
diagnostics: diagnosticsFromError,
|
|
630
|
+
diskMayBePartiallyUpdated: true
|
|
631
|
+
}, toSessionSnapshot(session, this.context.capabilities), {
|
|
632
|
+
sessionId: session.sessionId,
|
|
633
|
+
revision: session.revision,
|
|
634
|
+
diagnostics: diagnosticsFromError
|
|
635
|
+
});
|
|
636
|
+
}
|
|
637
|
+
}
|
|
403
638
|
};
|
|
404
639
|
//#endregion
|
|
405
640
|
//#region src/services/cache-service.ts
|
|
@@ -430,7 +665,7 @@ var CacheService = class {
|
|
|
430
665
|
const entry = this.context.cacheBySession.get(input.sessionId);
|
|
431
666
|
return success("read", startedAt, {
|
|
432
667
|
cacheRevision: entry?.revision ?? session.revision,
|
|
433
|
-
entries: entry ? [
|
|
668
|
+
entries: entry ? [structuredClone(entry)] : []
|
|
434
669
|
}, {
|
|
435
670
|
sessionId: session.sessionId,
|
|
436
671
|
revision: session.revision
|
|
@@ -501,7 +736,7 @@ var EventService = class {
|
|
|
501
736
|
const filtered = events.filter((event) => event.sequence > after);
|
|
502
737
|
const limit = input.limit === void 0 ? filtered.length : Math.max(0, input.limit);
|
|
503
738
|
return success("runtime", startedAt, {
|
|
504
|
-
events: filtered.slice(0, limit).map(
|
|
739
|
+
events: filtered.slice(0, limit).map((event) => structuredClone(event)),
|
|
505
740
|
oldestSequence,
|
|
506
741
|
currentSequence,
|
|
507
742
|
cursorExpired: false
|
|
@@ -576,7 +811,7 @@ var JobService = class {
|
|
|
576
811
|
jobId
|
|
577
812
|
});
|
|
578
813
|
this.scheduleRefreshJob(session.sessionId, jobId);
|
|
579
|
-
return success("runtime", startedAt,
|
|
814
|
+
return success("runtime", startedAt, structuredClone(job), {
|
|
580
815
|
sessionId: session.sessionId,
|
|
581
816
|
revision: session.revision
|
|
582
817
|
});
|
|
@@ -595,7 +830,7 @@ var JobService = class {
|
|
|
595
830
|
sessionId: session.sessionId,
|
|
596
831
|
revision: session.revision
|
|
597
832
|
});
|
|
598
|
-
return success("runtime", startedAt,
|
|
833
|
+
return success("runtime", startedAt, structuredClone(job), {
|
|
599
834
|
sessionId: session.sessionId,
|
|
600
835
|
revision: session.revision
|
|
601
836
|
});
|
|
@@ -610,7 +845,7 @@ var JobService = class {
|
|
|
610
845
|
else if (input.status === "terminal") jobs = jobs.filter((job) => isTerminal(job.status));
|
|
611
846
|
else jobs = jobs.filter((job) => job.status === input.status);
|
|
612
847
|
if (input.limit !== void 0) jobs = jobs.slice(0, Math.max(0, input.limit));
|
|
613
|
-
return success("runtime", startedAt, { jobs: jobs.map(
|
|
848
|
+
return success("runtime", startedAt, { jobs: jobs.map((job) => structuredClone(job)) }, {
|
|
614
849
|
sessionId: session.sessionId,
|
|
615
850
|
revision: session.revision
|
|
616
851
|
});
|
|
@@ -647,7 +882,8 @@ var JobService = class {
|
|
|
647
882
|
revision: session.revision,
|
|
648
883
|
jobId: job.jobId
|
|
649
884
|
});
|
|
650
|
-
|
|
885
|
+
const cancelled = this.cancelRefreshJob(session.sessionId, job);
|
|
886
|
+
return success("runtime", startedAt, structuredClone(cancelled), {
|
|
651
887
|
sessionId: session.sessionId,
|
|
652
888
|
revision: session.revision
|
|
653
889
|
});
|
|
@@ -794,7 +1030,7 @@ var ReadService = class {
|
|
|
794
1030
|
this.context = context;
|
|
795
1031
|
}
|
|
796
1032
|
getCapabilities() {
|
|
797
|
-
return success("read", Date.now(),
|
|
1033
|
+
return success("read", Date.now(), structuredClone(this.context.capabilities));
|
|
798
1034
|
}
|
|
799
1035
|
getSession(input) {
|
|
800
1036
|
const startedAt = Date.now();
|
|
@@ -858,6 +1094,75 @@ function createProjectReaderFileSystem(fileSystem) {
|
|
|
858
1094
|
}
|
|
859
1095
|
};
|
|
860
1096
|
}
|
|
1097
|
+
function createCaptureFileSystem(files, directories) {
|
|
1098
|
+
const normalize = (filePath) => filePath.replace(/\\/g, "/").replace(/\/+/g, "/");
|
|
1099
|
+
return {
|
|
1100
|
+
async readFile(filePath) {
|
|
1101
|
+
const value = files.get(normalize(filePath));
|
|
1102
|
+
if (typeof value !== "string") throw new Error(`Captured text file was not found: ${filePath}`);
|
|
1103
|
+
return value;
|
|
1104
|
+
},
|
|
1105
|
+
async readFileRaw(filePath) {
|
|
1106
|
+
const value = files.get(normalize(filePath));
|
|
1107
|
+
if (!(value instanceof Uint8Array)) throw new Error(`Captured binary file was not found: ${filePath}`);
|
|
1108
|
+
return value.slice();
|
|
1109
|
+
},
|
|
1110
|
+
async writeFile(filePath, content) {
|
|
1111
|
+
files.set(normalize(filePath), content);
|
|
1112
|
+
},
|
|
1113
|
+
async writeFileRaw(filePath, data) {
|
|
1114
|
+
files.set(normalize(filePath), data.slice());
|
|
1115
|
+
},
|
|
1116
|
+
async mkdir(dirPath) {
|
|
1117
|
+
directories.add(normalize(dirPath));
|
|
1118
|
+
},
|
|
1119
|
+
async readdir() {
|
|
1120
|
+
return [];
|
|
1121
|
+
},
|
|
1122
|
+
async exists(filePath) {
|
|
1123
|
+
return files.has(normalize(filePath));
|
|
1124
|
+
},
|
|
1125
|
+
join(...paths) {
|
|
1126
|
+
return normalize(paths.filter(Boolean).join("/"));
|
|
1127
|
+
},
|
|
1128
|
+
dirname(filePath) {
|
|
1129
|
+
const normalized = normalize(filePath);
|
|
1130
|
+
const separator = normalized.lastIndexOf("/");
|
|
1131
|
+
return separator < 0 ? "" : normalized.slice(0, separator);
|
|
1132
|
+
},
|
|
1133
|
+
async unlink(filePath) {
|
|
1134
|
+
files.delete(normalize(filePath));
|
|
1135
|
+
}
|
|
1136
|
+
};
|
|
1137
|
+
}
|
|
1138
|
+
function capturedFilesEqual(left, right) {
|
|
1139
|
+
if (left.size !== right.size) return false;
|
|
1140
|
+
for (const [filePath, leftValue] of left) {
|
|
1141
|
+
const rightValue = right.get(filePath);
|
|
1142
|
+
if (typeof leftValue === "string") {
|
|
1143
|
+
if (leftValue !== rightValue) return false;
|
|
1144
|
+
continue;
|
|
1145
|
+
}
|
|
1146
|
+
if (!(rightValue instanceof Uint8Array) || leftValue.length !== rightValue.length) return false;
|
|
1147
|
+
for (let index = 0; index < leftValue.length; index += 1) if (leftValue[index] !== rightValue[index]) return false;
|
|
1148
|
+
}
|
|
1149
|
+
return true;
|
|
1150
|
+
}
|
|
1151
|
+
function capturedDirectoriesEqual(left, right) {
|
|
1152
|
+
return left.size === right.size && [...left].every((directory) => right.has(directory));
|
|
1153
|
+
}
|
|
1154
|
+
async function hasFullUamFidelity(document, project) {
|
|
1155
|
+
const sourceFiles = /* @__PURE__ */ new Map();
|
|
1156
|
+
const materializedFiles = /* @__PURE__ */ new Map();
|
|
1157
|
+
const sourceDirectories = /* @__PURE__ */ new Set();
|
|
1158
|
+
const materializedDirectories = /* @__PURE__ */ new Set();
|
|
1159
|
+
try {
|
|
1160
|
+
await Promise.all([new ProjectWriter(createCaptureFileSystem(sourceFiles, sourceDirectories)).write(document, "Project.fairy"), new ProjectWriter(createCaptureFileSystem(materializedFiles, materializedDirectories)).write(materializeUamProject(project), "Project.fairy")]);
|
|
1161
|
+
} catch {
|
|
1162
|
+
return false;
|
|
1163
|
+
}
|
|
1164
|
+
return capturedFilesEqual(sourceFiles, materializedFiles) && capturedDirectoriesEqual(sourceDirectories, materializedDirectories);
|
|
1165
|
+
}
|
|
861
1166
|
var RuntimeService = class {
|
|
862
1167
|
constructor(context, cacheService, eventService, jobService) {
|
|
863
1168
|
this.context = context;
|
|
@@ -892,7 +1197,8 @@ var RuntimeService = class {
|
|
|
892
1197
|
canonicalPathKey
|
|
893
1198
|
}));
|
|
894
1199
|
await advisoryLock.close();
|
|
895
|
-
const
|
|
1200
|
+
const document = await new ProjectReader(createProjectReaderFileSystem(fileSystem)).read(fairyPath, { hydrateResourceBytes: true });
|
|
1201
|
+
const project = liftDocumentToUamProject(document);
|
|
896
1202
|
const sessionId = randomId();
|
|
897
1203
|
const session = {
|
|
898
1204
|
sessionId,
|
|
@@ -900,9 +1206,13 @@ var RuntimeService = class {
|
|
|
900
1206
|
canonicalProjectPath,
|
|
901
1207
|
canonicalPathKey,
|
|
902
1208
|
lockFilePath,
|
|
1209
|
+
fileSystem,
|
|
903
1210
|
project,
|
|
1211
|
+
uamFidelity: await hasFullUamFidelity(document, project) ? "full" : "unsupported",
|
|
904
1212
|
revision: 0,
|
|
905
1213
|
lastSavedRevision: 0,
|
|
1214
|
+
pendingStaleSourceFiles: /* @__PURE__ */ new Map(),
|
|
1215
|
+
pendingStaleResourceFolders: /* @__PURE__ */ new Map(),
|
|
906
1216
|
dirty: false,
|
|
907
1217
|
lockHeld: true,
|
|
908
1218
|
closed: false
|
|
@@ -938,8 +1248,10 @@ var RuntimeService = class {
|
|
|
938
1248
|
openProjectSession(input) {
|
|
939
1249
|
const startedAt = Date.now();
|
|
940
1250
|
const sessionId = input.sessionId ?? randomId();
|
|
941
|
-
const
|
|
942
|
-
const
|
|
1251
|
+
const storage = input.storage;
|
|
1252
|
+
const memoryProjectPath = `memory://${sessionId}`;
|
|
1253
|
+
const canonicalProjectPath = storage?.canonicalProjectPath ?? input.canonicalProjectPath ?? (storage ? storage.fileSystem.dirname(storage.fairyPath) || "." : memoryProjectPath);
|
|
1254
|
+
const canonicalPathKey = storage?.canonicalPathKey ?? input.canonicalPathKey ?? (storage ? normalizeComparablePath(canonicalProjectPath) : canonicalProjectPath.toLowerCase());
|
|
943
1255
|
const existingSessionId = this.context.sessionsByPath.get(canonicalPathKey);
|
|
944
1256
|
if (existingSessionId) return failure("runtime", startedAt, {
|
|
945
1257
|
code: "lock_conflict",
|
|
@@ -950,13 +1262,17 @@ var RuntimeService = class {
|
|
|
950
1262
|
});
|
|
951
1263
|
const session = {
|
|
952
1264
|
sessionId,
|
|
953
|
-
fairyPath: canonicalProjectPath,
|
|
1265
|
+
fairyPath: storage?.fairyPath ?? canonicalProjectPath,
|
|
954
1266
|
canonicalProjectPath,
|
|
955
1267
|
canonicalPathKey,
|
|
956
1268
|
lockFilePath: "",
|
|
1269
|
+
fileSystem: storage?.fileSystem,
|
|
957
1270
|
project: normalizeUamProject(input.project),
|
|
1271
|
+
uamFidelity: "full",
|
|
958
1272
|
revision: 0,
|
|
959
1273
|
lastSavedRevision: 0,
|
|
1274
|
+
pendingStaleSourceFiles: /* @__PURE__ */ new Map(),
|
|
1275
|
+
pendingStaleResourceFolders: /* @__PURE__ */ new Map(),
|
|
960
1276
|
dirty: false,
|
|
961
1277
|
lockHeld: false,
|
|
962
1278
|
closed: false
|
|
@@ -1009,7 +1325,25 @@ var RuntimeService = class {
|
|
|
1009
1325
|
}
|
|
1010
1326
|
};
|
|
1011
1327
|
//#endregion
|
|
1012
|
-
//#region src/
|
|
1328
|
+
//#region src/services/artifact-service.ts
|
|
1329
|
+
function createArtifactCapabilities() {
|
|
1330
|
+
const bridge = {
|
|
1331
|
+
available: false,
|
|
1332
|
+
requiredHost: "node",
|
|
1333
|
+
executionBoundary: "external-bridge",
|
|
1334
|
+
bridgeEntrypoint: "@openfairygui/backend/node",
|
|
1335
|
+
reason: "publish/restore require explicit Node-hosted filesystem and artifact execution."
|
|
1336
|
+
};
|
|
1337
|
+
return {
|
|
1338
|
+
publish: false,
|
|
1339
|
+
restore: false,
|
|
1340
|
+
status: "bridge-required",
|
|
1341
|
+
publishBridge: bridge,
|
|
1342
|
+
restoreBridge: bridge
|
|
1343
|
+
};
|
|
1344
|
+
}
|
|
1345
|
+
//#endregion
|
|
1346
|
+
//#region src/runtime/capabilities.ts
|
|
1013
1347
|
const BACKEND_METHODS = [
|
|
1014
1348
|
"getCapabilities",
|
|
1015
1349
|
"openSession",
|
|
@@ -1017,6 +1351,7 @@ const BACKEND_METHODS = [
|
|
|
1017
1351
|
"getSession",
|
|
1018
1352
|
"applyTransaction",
|
|
1019
1353
|
"saveSession",
|
|
1354
|
+
"materializeSession",
|
|
1020
1355
|
"closeSession",
|
|
1021
1356
|
"getEvents",
|
|
1022
1357
|
"getJob",
|
|
@@ -1065,7 +1400,21 @@ function createCapabilities() {
|
|
|
1065
1400
|
adapters: {
|
|
1066
1401
|
fileSystem: {
|
|
1067
1402
|
injected: true,
|
|
1068
|
-
requiredFor: [
|
|
1403
|
+
requiredFor: [
|
|
1404
|
+
"openSession",
|
|
1405
|
+
"saveSession",
|
|
1406
|
+
"materializeSession"
|
|
1407
|
+
]
|
|
1408
|
+
},
|
|
1409
|
+
projectStorage: {
|
|
1410
|
+
injected: true,
|
|
1411
|
+
browserSafe: true,
|
|
1412
|
+
requiredFor: [
|
|
1413
|
+
"openProjectSession.writeback",
|
|
1414
|
+
"saveSession",
|
|
1415
|
+
"materializeSession"
|
|
1416
|
+
],
|
|
1417
|
+
adapterFactory: "createBackendStorageFileSystem"
|
|
1069
1418
|
},
|
|
1070
1419
|
host: {
|
|
1071
1420
|
injected: true,
|
|
@@ -1114,6 +1463,8 @@ function createCapabilities() {
|
|
|
1114
1463
|
}
|
|
1115
1464
|
};
|
|
1116
1465
|
}
|
|
1466
|
+
//#endregion
|
|
1467
|
+
//#region src/runtime.ts
|
|
1117
1468
|
var BackendRuntime = class {
|
|
1118
1469
|
fileSystem;
|
|
1119
1470
|
capabilities;
|
|
@@ -1172,6 +1523,9 @@ var BackendRuntime = class {
|
|
|
1172
1523
|
async saveSession(input) {
|
|
1173
1524
|
return this.authoringService.saveSession(input);
|
|
1174
1525
|
}
|
|
1526
|
+
async materializeSession(input) {
|
|
1527
|
+
return this.authoringService.materializeSession(input);
|
|
1528
|
+
}
|
|
1175
1529
|
async closeSession(input) {
|
|
1176
1530
|
return this.runtimeService.closeSession(input);
|
|
1177
1531
|
}
|