@openfairygui/backend 0.2.0-alpha.1 → 0.2.0-alpha.3
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/LICENSE +21 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +1 -1
- package/dist/node.cjs +1 -1
- package/dist/node.d.cts +1 -1
- package/dist/node.d.mts +1 -1
- package/dist/node.mjs +1 -1
- package/dist/{runtime-DLWLghmH.mjs → runtime-BG5qOVkJ.mjs} +8 -2
- package/dist/{runtime-C5JY_kRI.d.cts → runtime-IrXqqmko.d.cts} +7 -2
- package/dist/{runtime-BXbt135W.cjs → runtime-jKuQmcqM.cjs} +15 -9
- package/dist/{runtime-C5ZsWMnH.d.mts → runtime-u1Bq_ysz.d.mts} +7 -2
- package/package.json +65 -65
- package/src/runtime.ts +64 -27
- package/src/services/authoring-service.ts +145 -44
- package/src/services/context.ts +1 -1
- package/src/services/runtime-service.ts +50 -21
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 OpenFairyGUI Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/index.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_runtime = require("./runtime-
|
|
2
|
+
const require_runtime = require("./runtime-jKuQmcqM.cjs");
|
|
3
3
|
exports.BACKEND_CAPABILITY_SCHEMA_VERSION = require_runtime.BACKEND_CAPABILITY_SCHEMA_VERSION;
|
|
4
4
|
exports.BACKEND_COMPATIBILITY_POLICY = require_runtime.BACKEND_COMPATIBILITY_POLICY;
|
|
5
5
|
exports.BACKEND_CONTRACT_VERSION = require_runtime.BACKEND_CONTRACT_VERSION;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { A as BackendSuccess, B as OpenProjectSessionInput, C as BackendJobProgress, D as BackendRuntime, E as BackendResult, F as GetEventsInput, G as BACKEND_CAPABILITY_SCHEMA_VERSION, H as SavePartialFailureError, I as GetEventsSnapshot, J as BackendDiagnostic, K as BACKEND_COMPATIBILITY_POLICY, L as GetJobInput, M as CancelJobInput, N as EventCursorInvalidError, O as BackendRuntimeOptions, P as GetCacheSnapshotInput, R as InProcessLockConflictError, S as BackendJobNotFoundError, T as BackendJobStatus, U as SessionNotFoundError, V as RefreshCacheInput, W as SessionStaleWriteError, X as BackendResponseMeta, Y as BackendMessage, Z as BackendStage, _ as BackendJobErrors, a as BackendCacheSnapshot, b as BackendJobListStatusFilter, c as BackendCapabilityUnavailableError, d as BackendEventKind, f as BackendFailure, g as BackendHostAdapter, h as BackendFileSystem, i as BackendCacheEntry, j as CacheRefreshFailedError, k as BackendSessionSnapshot, l as BackendError, m as BackendFileStat, n as ApplySessionTransactionInput, o as BackendCapabilities, p as BackendFileHandle, q as BACKEND_CONTRACT_VERSION, r as BackendArtifactBridgeCapability, s as BackendCapabilityManifest, t as AdvisoryLockConflictError, u as BackendEvent, v as BackendJobKind, w as BackendJobSnapshot, x as BackendJobNotCancellableError, y as BackendJobListSnapshot, z as ListJobsInput } from "./runtime-
|
|
1
|
+
import { A as BackendSuccess, B as OpenProjectSessionInput, C as BackendJobProgress, D as BackendRuntime, E as BackendResult, F as GetEventsInput, G as BACKEND_CAPABILITY_SCHEMA_VERSION, H as SavePartialFailureError, I as GetEventsSnapshot, J as BackendDiagnostic, K as BACKEND_COMPATIBILITY_POLICY, L as GetJobInput, M as CancelJobInput, N as EventCursorInvalidError, O as BackendRuntimeOptions, P as GetCacheSnapshotInput, R as InProcessLockConflictError, S as BackendJobNotFoundError, T as BackendJobStatus, U as SessionNotFoundError, V as RefreshCacheInput, W as SessionStaleWriteError, X as BackendResponseMeta, Y as BackendMessage, Z as BackendStage, _ as BackendJobErrors, a as BackendCacheSnapshot, b as BackendJobListStatusFilter, c as BackendCapabilityUnavailableError, d as BackendEventKind, f as BackendFailure, g as BackendHostAdapter, h as BackendFileSystem, i as BackendCacheEntry, j as CacheRefreshFailedError, k as BackendSessionSnapshot, l as BackendError, m as BackendFileStat, n as ApplySessionTransactionInput, o as BackendCapabilities, p as BackendFileHandle, q as BACKEND_CONTRACT_VERSION, r as BackendArtifactBridgeCapability, s as BackendCapabilityManifest, t as AdvisoryLockConflictError, u as BackendEvent, v as BackendJobKind, w as BackendJobSnapshot, x as BackendJobNotCancellableError, y as BackendJobListSnapshot, z as ListJobsInput } from "./runtime-IrXqqmko.cjs";
|
|
2
2
|
export { type AdvisoryLockConflictError, type ApplySessionTransactionInput, BACKEND_CAPABILITY_SCHEMA_VERSION, BACKEND_COMPATIBILITY_POLICY, BACKEND_CONTRACT_VERSION, type BackendArtifactBridgeCapability, type BackendCacheEntry, type BackendCacheSnapshot, type BackendCapabilities, type BackendCapabilityManifest, type BackendCapabilityUnavailableError, type BackendDiagnostic, type BackendError, type BackendEvent, type BackendEventKind, type BackendFailure, type BackendFileHandle, type BackendFileStat, type BackendFileSystem, type BackendHostAdapter, type BackendJobErrors, type BackendJobKind, type BackendJobListSnapshot, type BackendJobListStatusFilter, type BackendJobNotCancellableError, type BackendJobNotFoundError, type BackendJobProgress, type BackendJobSnapshot, type BackendJobStatus, type BackendMessage, type BackendResponseMeta, type BackendResult, BackendRuntime, type BackendRuntimeOptions, type BackendSessionSnapshot, type BackendStage, type BackendSuccess, type CacheRefreshFailedError, type CancelJobInput, type EventCursorInvalidError, type GetCacheSnapshotInput, type GetEventsInput, type GetEventsSnapshot, type GetJobInput, type InProcessLockConflictError, type ListJobsInput, type OpenProjectSessionInput, type RefreshCacheInput, type SavePartialFailureError, type SessionNotFoundError, type SessionStaleWriteError };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { A as BackendSuccess, B as OpenProjectSessionInput, C as BackendJobProgress, D as BackendRuntime, E as BackendResult, F as GetEventsInput, G as BACKEND_CAPABILITY_SCHEMA_VERSION, H as SavePartialFailureError, I as GetEventsSnapshot, J as BackendDiagnostic, K as BACKEND_COMPATIBILITY_POLICY, L as GetJobInput, M as CancelJobInput, N as EventCursorInvalidError, O as BackendRuntimeOptions, P as GetCacheSnapshotInput, R as InProcessLockConflictError, S as BackendJobNotFoundError, T as BackendJobStatus, U as SessionNotFoundError, V as RefreshCacheInput, W as SessionStaleWriteError, X as BackendResponseMeta, Y as BackendMessage, Z as BackendStage, _ as BackendJobErrors, a as BackendCacheSnapshot, b as BackendJobListStatusFilter, c as BackendCapabilityUnavailableError, d as BackendEventKind, f as BackendFailure, g as BackendHostAdapter, h as BackendFileSystem, i as BackendCacheEntry, j as CacheRefreshFailedError, k as BackendSessionSnapshot, l as BackendError, m as BackendFileStat, n as ApplySessionTransactionInput, o as BackendCapabilities, p as BackendFileHandle, q as BACKEND_CONTRACT_VERSION, r as BackendArtifactBridgeCapability, s as BackendCapabilityManifest, t as AdvisoryLockConflictError, u as BackendEvent, v as BackendJobKind, w as BackendJobSnapshot, x as BackendJobNotCancellableError, y as BackendJobListSnapshot, z as ListJobsInput } from "./runtime-
|
|
1
|
+
import { A as BackendSuccess, B as OpenProjectSessionInput, C as BackendJobProgress, D as BackendRuntime, E as BackendResult, F as GetEventsInput, G as BACKEND_CAPABILITY_SCHEMA_VERSION, H as SavePartialFailureError, I as GetEventsSnapshot, J as BackendDiagnostic, K as BACKEND_COMPATIBILITY_POLICY, L as GetJobInput, M as CancelJobInput, N as EventCursorInvalidError, O as BackendRuntimeOptions, P as GetCacheSnapshotInput, R as InProcessLockConflictError, S as BackendJobNotFoundError, T as BackendJobStatus, U as SessionNotFoundError, V as RefreshCacheInput, W as SessionStaleWriteError, X as BackendResponseMeta, Y as BackendMessage, Z as BackendStage, _ as BackendJobErrors, a as BackendCacheSnapshot, b as BackendJobListStatusFilter, c as BackendCapabilityUnavailableError, d as BackendEventKind, f as BackendFailure, g as BackendHostAdapter, h as BackendFileSystem, i as BackendCacheEntry, j as CacheRefreshFailedError, k as BackendSessionSnapshot, l as BackendError, m as BackendFileStat, n as ApplySessionTransactionInput, o as BackendCapabilities, p as BackendFileHandle, q as BACKEND_CONTRACT_VERSION, r as BackendArtifactBridgeCapability, s as BackendCapabilityManifest, t as AdvisoryLockConflictError, u as BackendEvent, v as BackendJobKind, w as BackendJobSnapshot, x as BackendJobNotCancellableError, y as BackendJobListSnapshot, z as ListJobsInput } from "./runtime-u1Bq_ysz.mjs";
|
|
2
2
|
export { type AdvisoryLockConflictError, type ApplySessionTransactionInput, BACKEND_CAPABILITY_SCHEMA_VERSION, BACKEND_COMPATIBILITY_POLICY, BACKEND_CONTRACT_VERSION, type BackendArtifactBridgeCapability, type BackendCacheEntry, type BackendCacheSnapshot, type BackendCapabilities, type BackendCapabilityManifest, type BackendCapabilityUnavailableError, type BackendDiagnostic, type BackendError, type BackendEvent, type BackendEventKind, type BackendFailure, type BackendFileHandle, type BackendFileStat, type BackendFileSystem, type BackendHostAdapter, type BackendJobErrors, type BackendJobKind, type BackendJobListSnapshot, type BackendJobListStatusFilter, type BackendJobNotCancellableError, type BackendJobNotFoundError, type BackendJobProgress, type BackendJobSnapshot, type BackendJobStatus, type BackendMessage, type BackendResponseMeta, type BackendResult, BackendRuntime, type BackendRuntimeOptions, type BackendSessionSnapshot, type BackendStage, type BackendSuccess, type CacheRefreshFailedError, type CancelJobInput, type EventCursorInvalidError, type GetCacheSnapshotInput, type GetEventsInput, type GetEventsSnapshot, type GetJobInput, type InProcessLockConflictError, type ListJobsInput, type OpenProjectSessionInput, type RefreshCacheInput, type SavePartialFailureError, type SessionNotFoundError, type SessionStaleWriteError };
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { i as BACKEND_CONTRACT_VERSION, n as BACKEND_CAPABILITY_SCHEMA_VERSION, r as BACKEND_COMPATIBILITY_POLICY, t as BackendRuntime } from "./runtime-
|
|
1
|
+
import { i as BACKEND_CONTRACT_VERSION, n as BACKEND_CAPABILITY_SCHEMA_VERSION, r as BACKEND_COMPATIBILITY_POLICY, t as BackendRuntime } from "./runtime-BG5qOVkJ.mjs";
|
|
2
2
|
export { BACKEND_CAPABILITY_SCHEMA_VERSION, BACKEND_COMPATIBILITY_POLICY, BACKEND_CONTRACT_VERSION, BackendRuntime };
|
package/dist/node.cjs
CHANGED
|
@@ -21,7 +21,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
21
21
|
enumerable: true
|
|
22
22
|
}) : target, mod));
|
|
23
23
|
//#endregion
|
|
24
|
-
const require_runtime = require("./runtime-
|
|
24
|
+
const require_runtime = require("./runtime-jKuQmcqM.cjs");
|
|
25
25
|
let node_fs_promises = require("node:fs/promises");
|
|
26
26
|
node_fs_promises = __toESM(node_fs_promises);
|
|
27
27
|
let node_path = require("node:path");
|
package/dist/node.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { D as BackendRuntime, O as BackendRuntimeOptions, g as BackendHostAdapter, h as BackendFileSystem, m as BackendFileStat, p as BackendFileHandle } from "./runtime-
|
|
1
|
+
import { D as BackendRuntime, O as BackendRuntimeOptions, g as BackendHostAdapter, h as BackendFileSystem, m as BackendFileStat, p as BackendFileHandle } from "./runtime-IrXqqmko.cjs";
|
|
2
2
|
|
|
3
3
|
//#region src/node.d.ts
|
|
4
4
|
declare function createNodeBackendFileSystem(): BackendFileSystem;
|
package/dist/node.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { D as BackendRuntime, O as BackendRuntimeOptions, g as BackendHostAdapter, h as BackendFileSystem, m as BackendFileStat, p as BackendFileHandle } from "./runtime-
|
|
1
|
+
import { D as BackendRuntime, O as BackendRuntimeOptions, g as BackendHostAdapter, h as BackendFileSystem, m as BackendFileStat, p as BackendFileHandle } from "./runtime-u1Bq_ysz.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/node.d.ts
|
|
4
4
|
declare function createNodeBackendFileSystem(): BackendFileSystem;
|
package/dist/node.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
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";
|
|
3
|
+
import { applyUamTransactionApp } from "@openfairygui/functions/uam";
|
|
3
4
|
//#region src/contracts.ts
|
|
4
5
|
const BACKEND_CONTRACT_VERSION = "1.1.0-p2";
|
|
5
6
|
const BACKEND_CAPABILITY_SCHEMA_VERSION = 2;
|
|
@@ -1049,6 +1050,11 @@ function createCapabilities() {
|
|
|
1049
1050
|
resourceKinds: [...UAM_SUPPORTED_MATERIALIZATION_SCOPE.resourceKinds],
|
|
1050
1051
|
nodeKinds: [...UAM_SUPPORTED_MATERIALIZATION_SCOPE.nodeKinds],
|
|
1051
1052
|
gearKinds: [...UAM_SUPPORTED_MATERIALIZATION_SCOPE.gearKinds],
|
|
1053
|
+
transactionScope: {
|
|
1054
|
+
resourceKinds: [...UAM_SUPPORTED_TRANSACTION_SCOPE.resourceKinds],
|
|
1055
|
+
nodeKinds: [...UAM_SUPPORTED_TRANSACTION_SCOPE.nodeKinds],
|
|
1056
|
+
gearKinds: [...UAM_SUPPORTED_TRANSACTION_SCOPE.gearKinds]
|
|
1057
|
+
},
|
|
1052
1058
|
unsupported: ["artifact.publish", "artifact.restore"]
|
|
1053
1059
|
},
|
|
1054
1060
|
artifact: createArtifactCapabilities(),
|
|
@@ -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";
|
|
@@ -122,6 +122,11 @@ interface BackendCapabilities {
|
|
|
122
122
|
resourceKinds: readonly string[];
|
|
123
123
|
nodeKinds: readonly string[];
|
|
124
124
|
gearKinds: readonly string[];
|
|
125
|
+
transactionScope: {
|
|
126
|
+
resourceKinds: readonly string[];
|
|
127
|
+
nodeKinds: readonly string[];
|
|
128
|
+
gearKinds: readonly string[];
|
|
129
|
+
};
|
|
125
130
|
unsupported: readonly ['artifact.publish', 'artifact.restore'];
|
|
126
131
|
};
|
|
127
132
|
artifact: {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
require("./node.cjs");
|
|
2
|
-
let
|
|
3
|
-
let
|
|
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;
|
|
@@ -276,7 +277,7 @@ var AuthoringService = class {
|
|
|
276
277
|
revision: session.revision
|
|
277
278
|
});
|
|
278
279
|
}
|
|
279
|
-
const result = (0,
|
|
280
|
+
const result = (0, _openfairygui_functions_uam.applyUamTransactionApp)({
|
|
280
281
|
project: session.project,
|
|
281
282
|
operations: input.operations
|
|
282
283
|
});
|
|
@@ -355,7 +356,7 @@ var AuthoringService = class {
|
|
|
355
356
|
revision: session.revision
|
|
356
357
|
});
|
|
357
358
|
try {
|
|
358
|
-
await new
|
|
359
|
+
await new _openfairygui_core_project_io.ProjectWriter(createWriterFileSystem(fileSystem, committedPaths, failedPaths)).write((0, _openfairygui_core_uam.materializeUamProject)(session.project), session.fairyPath);
|
|
359
360
|
session.lastSavedRevision = session.revision;
|
|
360
361
|
session.dirty = false;
|
|
361
362
|
const cacheEntry = this.cacheService.refreshSession(session);
|
|
@@ -892,7 +893,7 @@ var RuntimeService = class {
|
|
|
892
893
|
canonicalPathKey
|
|
893
894
|
}));
|
|
894
895
|
await advisoryLock.close();
|
|
895
|
-
const project = (0,
|
|
896
|
+
const project = (0, _openfairygui_core_uam.liftDocumentToUamProject)(await new _openfairygui_core_project_io.ProjectReader(createProjectReaderFileSystem(fileSystem)).read(fairyPath));
|
|
896
897
|
const sessionId = randomId();
|
|
897
898
|
const session = {
|
|
898
899
|
sessionId,
|
|
@@ -954,7 +955,7 @@ var RuntimeService = class {
|
|
|
954
955
|
canonicalProjectPath,
|
|
955
956
|
canonicalPathKey,
|
|
956
957
|
lockFilePath: "",
|
|
957
|
-
project: (0,
|
|
958
|
+
project: (0, _openfairygui_core_uam.normalizeUamProject)(input.project),
|
|
958
959
|
revision: 0,
|
|
959
960
|
lastSavedRevision: 0,
|
|
960
961
|
dirty: false,
|
|
@@ -1047,9 +1048,14 @@ function createCapabilities() {
|
|
|
1047
1048
|
authoring: {
|
|
1048
1049
|
applyTransaction: true,
|
|
1049
1050
|
saveSession: true,
|
|
1050
|
-
resourceKinds: [...
|
|
1051
|
-
nodeKinds: [...
|
|
1052
|
-
gearKinds: [...
|
|
1051
|
+
resourceKinds: [..._openfairygui_core_uam.UAM_SUPPORTED_MATERIALIZATION_SCOPE.resourceKinds],
|
|
1052
|
+
nodeKinds: [..._openfairygui_core_uam.UAM_SUPPORTED_MATERIALIZATION_SCOPE.nodeKinds],
|
|
1053
|
+
gearKinds: [..._openfairygui_core_uam.UAM_SUPPORTED_MATERIALIZATION_SCOPE.gearKinds],
|
|
1054
|
+
transactionScope: {
|
|
1055
|
+
resourceKinds: [..._openfairygui_core_uam.UAM_SUPPORTED_TRANSACTION_SCOPE.resourceKinds],
|
|
1056
|
+
nodeKinds: [..._openfairygui_core_uam.UAM_SUPPORTED_TRANSACTION_SCOPE.nodeKinds],
|
|
1057
|
+
gearKinds: [..._openfairygui_core_uam.UAM_SUPPORTED_TRANSACTION_SCOPE.gearKinds]
|
|
1058
|
+
},
|
|
1053
1059
|
unsupported: ["artifact.publish", "artifact.restore"]
|
|
1054
1060
|
},
|
|
1055
1061
|
artifact: createArtifactCapabilities(),
|
|
@@ -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";
|
|
@@ -122,6 +122,11 @@ interface BackendCapabilities {
|
|
|
122
122
|
resourceKinds: readonly string[];
|
|
123
123
|
nodeKinds: readonly string[];
|
|
124
124
|
gearKinds: readonly string[];
|
|
125
|
+
transactionScope: {
|
|
126
|
+
resourceKinds: readonly string[];
|
|
127
|
+
nodeKinds: readonly string[];
|
|
128
|
+
gearKinds: readonly string[];
|
|
129
|
+
};
|
|
125
130
|
unsupported: readonly ['artifact.publish', 'artifact.restore'];
|
|
126
131
|
};
|
|
127
132
|
artifact: {
|
package/package.json
CHANGED
|
@@ -1,66 +1,66 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
}
|
|
2
|
+
"name": "@openfairygui/backend",
|
|
3
|
+
"version": "0.2.0-alpha.3",
|
|
4
|
+
"description": "FairyGUI Headless Authoring SDK — stateful backend runtime and session services.",
|
|
5
|
+
"author": "OpenFairyGUI Contributors",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/OpenFairyGUI/OpenFairyGUI.git",
|
|
10
|
+
"directory": "packages/backend"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://github.com/OpenFairyGUI/OpenFairyGUI#readme",
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/OpenFairyGUI/OpenFairYGUI/issues"
|
|
15
|
+
},
|
|
16
|
+
"type": "module",
|
|
17
|
+
"sideEffects": false,
|
|
18
|
+
"main": "./dist/index.cjs",
|
|
19
|
+
"module": "./dist/index.mjs",
|
|
20
|
+
"types": "./dist/index.d.mts",
|
|
21
|
+
"exports": {
|
|
22
|
+
".": {
|
|
23
|
+
"require": {
|
|
24
|
+
"types": "./dist/index.d.cts",
|
|
25
|
+
"default": "./dist/index.cjs"
|
|
26
|
+
},
|
|
27
|
+
"default": {
|
|
28
|
+
"types": "./dist/index.d.mts",
|
|
29
|
+
"default": "./dist/index.mjs"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"./node": {
|
|
33
|
+
"require": {
|
|
34
|
+
"types": "./dist/node.d.cts",
|
|
35
|
+
"default": "./dist/node.cjs"
|
|
36
|
+
},
|
|
37
|
+
"default": {
|
|
38
|
+
"types": "./dist/node.d.mts",
|
|
39
|
+
"default": "./dist/node.mjs"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"files": [
|
|
44
|
+
"dist/",
|
|
45
|
+
"src/"
|
|
46
|
+
],
|
|
47
|
+
"keywords": [
|
|
48
|
+
"fairygui",
|
|
49
|
+
"backend",
|
|
50
|
+
"session",
|
|
51
|
+
"authoring",
|
|
52
|
+
"runtime"
|
|
53
|
+
],
|
|
54
|
+
"dependencies": {
|
|
55
|
+
"@openfairygui/core": "0.2.0-alpha.3",
|
|
56
|
+
"@openfairygui/functions": "0.2.0-alpha.3"
|
|
57
|
+
},
|
|
58
|
+
"devDependencies": {
|
|
59
|
+
"ava": "^7.0.0",
|
|
60
|
+
"tsx": "^4.0.0"
|
|
61
|
+
},
|
|
62
|
+
"scripts": {
|
|
63
|
+
"build": "tsdown src/index.ts src/node.ts --format esm,cjs --platform node --external node:fs --external node:fs/promises --external node:path --env.PACKAGE_VERSION=$npm_package_version",
|
|
64
|
+
"build:watch": "tsdown src/index.ts src/node.ts --watch --format esm,cjs --platform node --external node:fs --external node:fs/promises --external node:path --env.PACKAGE_VERSION=$npm_package_version"
|
|
65
|
+
}
|
|
66
|
+
}
|
package/src/runtime.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
UAM_SUPPORTED_MATERIALIZATION_SCOPE,
|
|
3
|
+
UAM_SUPPORTED_TRANSACTION_SCOPE,
|
|
3
4
|
type UamProject,
|
|
4
5
|
type UamTransactionOperation,
|
|
5
|
-
} from '@openfairygui/core';
|
|
6
|
-
import type { ApplyUamTransactionAppError } from '@openfairygui/functions';
|
|
6
|
+
} from '@openfairygui/core/uam';
|
|
7
|
+
import type { ApplyUamTransactionAppError } from '@openfairygui/functions/uam';
|
|
7
8
|
import {
|
|
8
9
|
BACKEND_CAPABILITY_SCHEMA_VERSION,
|
|
9
10
|
BACKEND_COMPATIBILITY_POLICY,
|
|
@@ -47,11 +48,7 @@ export interface BackendFileSystem {
|
|
|
47
48
|
}
|
|
48
49
|
|
|
49
50
|
export interface BackendHostAdapter {
|
|
50
|
-
lockMetadata?(input: {
|
|
51
|
-
canonicalPathKey: string;
|
|
52
|
-
canonicalProjectPath: string;
|
|
53
|
-
lockFilePath: string;
|
|
54
|
-
}): unknown;
|
|
51
|
+
lockMetadata?(input: { canonicalPathKey: string; canonicalProjectPath: string; lockFilePath: string }): unknown;
|
|
55
52
|
}
|
|
56
53
|
|
|
57
54
|
export interface BackendArtifactBridgeCapability {
|
|
@@ -119,6 +116,11 @@ export interface BackendCapabilities {
|
|
|
119
116
|
resourceKinds: readonly string[];
|
|
120
117
|
nodeKinds: readonly string[];
|
|
121
118
|
gearKinds: readonly string[];
|
|
119
|
+
transactionScope: {
|
|
120
|
+
resourceKinds: readonly string[];
|
|
121
|
+
nodeKinds: readonly string[];
|
|
122
|
+
gearKinds: readonly string[];
|
|
123
|
+
};
|
|
122
124
|
unsupported: readonly ['artifact.publish', 'artifact.restore'];
|
|
123
125
|
};
|
|
124
126
|
artifact: {
|
|
@@ -189,9 +191,7 @@ export interface BackendFailure<E extends BackendError = BackendError> {
|
|
|
189
191
|
session?: BackendSessionSnapshot;
|
|
190
192
|
}
|
|
191
193
|
|
|
192
|
-
export type BackendResult<T, E extends BackendError = BackendError> =
|
|
193
|
-
| BackendSuccess<T>
|
|
194
|
-
| BackendFailure<E>;
|
|
194
|
+
export type BackendResult<T, E extends BackendError = BackendError> = BackendSuccess<T> | BackendFailure<E>;
|
|
195
195
|
|
|
196
196
|
export interface SessionNotFoundError {
|
|
197
197
|
code: 'session_not_found';
|
|
@@ -446,10 +446,10 @@ export interface BackendRuntimeOptions {
|
|
|
446
446
|
}
|
|
447
447
|
|
|
448
448
|
const BACKEND_METHODS = [
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
449
|
+
'getCapabilities',
|
|
450
|
+
'openSession',
|
|
451
|
+
'openProjectSession',
|
|
452
|
+
'getSession',
|
|
453
453
|
'applyTransaction',
|
|
454
454
|
'saveSession',
|
|
455
455
|
'closeSession',
|
|
@@ -457,8 +457,8 @@ const BACKEND_METHODS = [
|
|
|
457
457
|
'getJob',
|
|
458
458
|
'listJobs',
|
|
459
459
|
'cancelJob',
|
|
460
|
-
|
|
461
|
-
|
|
460
|
+
'getCacheSnapshot',
|
|
461
|
+
'refreshCache',
|
|
462
462
|
] as const;
|
|
463
463
|
|
|
464
464
|
const ARTIFACT_BRIDGE_CAPABILITY = {
|
|
@@ -487,6 +487,11 @@ function createCapabilities(): BackendCapabilities {
|
|
|
487
487
|
resourceKinds: [...UAM_SUPPORTED_MATERIALIZATION_SCOPE.resourceKinds],
|
|
488
488
|
nodeKinds: [...UAM_SUPPORTED_MATERIALIZATION_SCOPE.nodeKinds],
|
|
489
489
|
gearKinds: [...UAM_SUPPORTED_MATERIALIZATION_SCOPE.gearKinds],
|
|
490
|
+
transactionScope: {
|
|
491
|
+
resourceKinds: [...UAM_SUPPORTED_TRANSACTION_SCOPE.resourceKinds],
|
|
492
|
+
nodeKinds: [...UAM_SUPPORTED_TRANSACTION_SCOPE.nodeKinds],
|
|
493
|
+
gearKinds: [...UAM_SUPPORTED_TRANSACTION_SCOPE.gearKinds],
|
|
494
|
+
},
|
|
490
495
|
unsupported: ['artifact.publish', 'artifact.restore'],
|
|
491
496
|
},
|
|
492
497
|
artifact: createArtifactCapabilities(),
|
|
@@ -593,7 +598,14 @@ export class BackendRuntime {
|
|
|
593
598
|
return this.readService.getCapabilities() as BackendSuccess<BackendCapabilities>;
|
|
594
599
|
}
|
|
595
600
|
|
|
596
|
-
public async openSession(input: {
|
|
601
|
+
public async openSession(input: {
|
|
602
|
+
projectPath: string;
|
|
603
|
+
}): Promise<
|
|
604
|
+
BackendResult<
|
|
605
|
+
BackendSessionSnapshot,
|
|
606
|
+
InProcessLockConflictError | AdvisoryLockConflictError | BackendCapabilityUnavailableError
|
|
607
|
+
>
|
|
608
|
+
> {
|
|
597
609
|
return this.runtimeService.openSession(input);
|
|
598
610
|
}
|
|
599
611
|
|
|
@@ -607,27 +619,47 @@ export class BackendRuntime {
|
|
|
607
619
|
|
|
608
620
|
public async applyTransaction(
|
|
609
621
|
input: ApplySessionTransactionInput,
|
|
610
|
-
): Promise<
|
|
622
|
+
): Promise<
|
|
623
|
+
BackendResult<
|
|
624
|
+
BackendSessionSnapshot,
|
|
625
|
+
SessionNotFoundError | SessionStaleWriteError | ApplyUamTransactionAppError
|
|
626
|
+
>
|
|
627
|
+
> {
|
|
611
628
|
return this.authoringService.applyTransaction(input);
|
|
612
629
|
}
|
|
613
630
|
|
|
614
|
-
public async saveSession(
|
|
615
|
-
|
|
616
|
-
|
|
631
|
+
public async saveSession(input: {
|
|
632
|
+
sessionId: string;
|
|
633
|
+
expectedRevision?: number;
|
|
634
|
+
targetPath?: string;
|
|
635
|
+
}): Promise<
|
|
636
|
+
BackendResult<
|
|
637
|
+
BackendSessionSnapshot,
|
|
638
|
+
| SessionNotFoundError
|
|
639
|
+
| SessionStaleWriteError
|
|
640
|
+
| SavePartialFailureError
|
|
641
|
+
| PathPolicyViolationError
|
|
642
|
+
| BackendCapabilityUnavailableError
|
|
643
|
+
>
|
|
644
|
+
> {
|
|
617
645
|
return this.authoringService.saveSession(input);
|
|
618
646
|
}
|
|
619
647
|
|
|
620
|
-
public async closeSession(
|
|
621
|
-
|
|
622
|
-
): Promise<BackendResult<{ sessionId: string; closed: true }, SessionNotFoundError>> {
|
|
648
|
+
public async closeSession(input: {
|
|
649
|
+
sessionId: string;
|
|
650
|
+
}): Promise<BackendResult<{ sessionId: string; closed: true }, SessionNotFoundError>> {
|
|
623
651
|
return this.runtimeService.closeSession(input);
|
|
624
652
|
}
|
|
625
653
|
|
|
626
|
-
public getEvents(
|
|
654
|
+
public getEvents(
|
|
655
|
+
input: GetEventsInput,
|
|
656
|
+
): BackendResult<GetEventsSnapshot, SessionNotFoundError | EventCursorInvalidError> {
|
|
627
657
|
return this.eventService.getEvents(input);
|
|
628
658
|
}
|
|
629
659
|
|
|
630
|
-
public getJob(
|
|
660
|
+
public getJob(
|
|
661
|
+
input: GetJobInput,
|
|
662
|
+
): BackendResult<BackendJobSnapshot, SessionNotFoundError | BackendJobNotFoundError> {
|
|
631
663
|
return this.jobService.getJob(input);
|
|
632
664
|
}
|
|
633
665
|
|
|
@@ -635,7 +667,12 @@ export class BackendRuntime {
|
|
|
635
667
|
return this.jobService.listJobs(input);
|
|
636
668
|
}
|
|
637
669
|
|
|
638
|
-
public cancelJob(
|
|
670
|
+
public cancelJob(
|
|
671
|
+
input: CancelJobInput,
|
|
672
|
+
): BackendResult<
|
|
673
|
+
BackendJobSnapshot,
|
|
674
|
+
SessionNotFoundError | BackendJobNotFoundError | BackendJobNotCancellableError
|
|
675
|
+
> {
|
|
639
676
|
return this.jobService.cancelJob(input);
|
|
640
677
|
}
|
|
641
678
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { ProjectWriter, type FileSystem } from '@openfairygui/core/project-io';
|
|
2
|
+
import { materializeUamProject } from '@openfairygui/core/uam';
|
|
3
|
+
import { applyUamTransactionApp, type ApplyUamTransactionAppError } from '@openfairygui/functions/uam';
|
|
3
4
|
import type { CacheService } from './cache-service.js';
|
|
4
5
|
import { failure, success, type BackendContext } from './context.js';
|
|
5
6
|
import type { EventService } from './event-service.js';
|
|
@@ -20,7 +21,7 @@ function createWriterFileSystem(
|
|
|
20
21
|
fileSystem: BackendFileSystem,
|
|
21
22
|
committedPaths: string[],
|
|
22
23
|
failedPaths: string[],
|
|
23
|
-
):
|
|
24
|
+
): FileSystem {
|
|
24
25
|
async function trackWrite<T>(targetPath: string, fn: () => Promise<T>): Promise<T> {
|
|
25
26
|
try {
|
|
26
27
|
const result = await fn();
|
|
@@ -83,18 +84,34 @@ export class AuthoringService {
|
|
|
83
84
|
|
|
84
85
|
public async applyTransaction(
|
|
85
86
|
input: ApplySessionTransactionInput,
|
|
86
|
-
): Promise<
|
|
87
|
+
): Promise<
|
|
88
|
+
BackendResult<
|
|
89
|
+
BackendSessionSnapshot,
|
|
90
|
+
SessionNotFoundError | SessionStaleWriteError | ApplyUamTransactionAppError
|
|
91
|
+
>
|
|
92
|
+
> {
|
|
87
93
|
const startedAt = Date.now();
|
|
88
94
|
const session = this.context.sessions.get(input.sessionId);
|
|
89
95
|
if (!session || session.closed) {
|
|
90
96
|
return failure('authoring', startedAt, createSessionNotFoundError(input.sessionId));
|
|
91
97
|
}
|
|
92
98
|
if (input.expectedRevision !== session.revision) {
|
|
93
|
-
this.eventService.emit({
|
|
94
|
-
|
|
99
|
+
this.eventService.emit({
|
|
100
|
+
kind: 'transaction.rejected',
|
|
95
101
|
sessionId: session.sessionId,
|
|
102
|
+
canonicalPathKey: session.canonicalPathKey,
|
|
96
103
|
revision: session.revision,
|
|
97
104
|
});
|
|
105
|
+
return failure(
|
|
106
|
+
'authoring',
|
|
107
|
+
startedAt,
|
|
108
|
+
createStaleWriteError(session, input.expectedRevision),
|
|
109
|
+
toSessionSnapshot(session, this.context.capabilities),
|
|
110
|
+
{
|
|
111
|
+
sessionId: session.sessionId,
|
|
112
|
+
revision: session.revision,
|
|
113
|
+
},
|
|
114
|
+
);
|
|
98
115
|
}
|
|
99
116
|
|
|
100
117
|
const result = applyUamTransactionApp({
|
|
@@ -102,19 +119,47 @@ export class AuthoringService {
|
|
|
102
119
|
operations: input.operations,
|
|
103
120
|
});
|
|
104
121
|
if (result.ok === false) {
|
|
105
|
-
this.eventService.emit({
|
|
106
|
-
|
|
122
|
+
this.eventService.emit({
|
|
123
|
+
kind: 'transaction.rejected',
|
|
107
124
|
sessionId: session.sessionId,
|
|
125
|
+
canonicalPathKey: session.canonicalPathKey,
|
|
108
126
|
revision: session.revision,
|
|
127
|
+
diagnostics:
|
|
128
|
+
result.error.issues?.map((issue) => ({
|
|
129
|
+
code: result.error.code,
|
|
130
|
+
message: issue.message,
|
|
131
|
+
severity: 'error' as const,
|
|
132
|
+
})) ?? [],
|
|
109
133
|
});
|
|
134
|
+
return failure(
|
|
135
|
+
'authoring',
|
|
136
|
+
startedAt,
|
|
137
|
+
result.error,
|
|
138
|
+
toSessionSnapshot(session, this.context.capabilities),
|
|
139
|
+
{
|
|
140
|
+
sessionId: session.sessionId,
|
|
141
|
+
revision: session.revision,
|
|
142
|
+
},
|
|
143
|
+
);
|
|
110
144
|
}
|
|
111
145
|
|
|
112
146
|
session.project = result.project;
|
|
113
147
|
session.revision += 1;
|
|
114
148
|
session.dirty = true;
|
|
115
149
|
const cacheEntry = this.cacheService.invalidateSession(session);
|
|
116
|
-
this.eventService.emit({
|
|
117
|
-
|
|
150
|
+
this.eventService.emit({
|
|
151
|
+
kind: 'transaction.applied',
|
|
152
|
+
sessionId: session.sessionId,
|
|
153
|
+
canonicalPathKey: session.canonicalPathKey,
|
|
154
|
+
revision: session.revision,
|
|
155
|
+
});
|
|
156
|
+
this.eventService.emit({
|
|
157
|
+
kind: 'cache.invalidated',
|
|
158
|
+
sessionId: session.sessionId,
|
|
159
|
+
canonicalPathKey: session.canonicalPathKey,
|
|
160
|
+
revision: session.revision,
|
|
161
|
+
cacheRevision: cacheEntry.revision,
|
|
162
|
+
});
|
|
118
163
|
|
|
119
164
|
return success('authoring', startedAt, toSessionSnapshot(session, this.context.capabilities), {
|
|
120
165
|
sessionId: session.sessionId,
|
|
@@ -122,38 +167,67 @@ export class AuthoringService {
|
|
|
122
167
|
});
|
|
123
168
|
}
|
|
124
169
|
|
|
125
|
-
public async saveSession(
|
|
126
|
-
|
|
127
|
-
|
|
170
|
+
public async saveSession(input: {
|
|
171
|
+
sessionId: string;
|
|
172
|
+
expectedRevision?: number;
|
|
173
|
+
targetPath?: string;
|
|
174
|
+
}): Promise<
|
|
175
|
+
BackendResult<
|
|
176
|
+
BackendSessionSnapshot,
|
|
177
|
+
| SessionNotFoundError
|
|
178
|
+
| SessionStaleWriteError
|
|
179
|
+
| SavePartialFailureError
|
|
180
|
+
| PathPolicyViolationError
|
|
181
|
+
| BackendCapabilityUnavailableError
|
|
182
|
+
>
|
|
183
|
+
> {
|
|
128
184
|
const startedAt = Date.now();
|
|
129
185
|
const session = this.context.sessions.get(input.sessionId);
|
|
130
186
|
if (!session || session.closed) {
|
|
131
187
|
return failure('authoring', startedAt, createSessionNotFoundError(input.sessionId));
|
|
132
188
|
}
|
|
133
189
|
if (!this.context.fileSystem) {
|
|
134
|
-
return failure(
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
190
|
+
return failure(
|
|
191
|
+
'authoring',
|
|
192
|
+
startedAt,
|
|
193
|
+
{
|
|
194
|
+
code: 'capability_unavailable',
|
|
195
|
+
message: 'saveSession requires an injected BackendFileSystem adapter.',
|
|
196
|
+
capability: 'fileSystem',
|
|
197
|
+
requiredAdapter: 'BackendFileSystem',
|
|
198
|
+
},
|
|
199
|
+
toSessionSnapshot(session, this.context.capabilities),
|
|
200
|
+
{
|
|
201
|
+
sessionId: session.sessionId,
|
|
202
|
+
revision: session.revision,
|
|
203
|
+
},
|
|
204
|
+
);
|
|
143
205
|
}
|
|
144
206
|
if (input.expectedRevision !== undefined && input.expectedRevision !== session.revision) {
|
|
145
|
-
return failure(
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
207
|
+
return failure(
|
|
208
|
+
'authoring',
|
|
209
|
+
startedAt,
|
|
210
|
+
createStaleWriteError(session, input.expectedRevision),
|
|
211
|
+
toSessionSnapshot(session, this.context.capabilities),
|
|
212
|
+
{
|
|
213
|
+
sessionId: session.sessionId,
|
|
214
|
+
revision: session.revision,
|
|
215
|
+
},
|
|
216
|
+
);
|
|
149
217
|
}
|
|
150
218
|
const fileSystem = this.context.fileSystem;
|
|
151
219
|
const targetViolation = await validateSaveTarget(fileSystem, session.fairyPath, input.targetPath);
|
|
152
220
|
if (targetViolation) {
|
|
153
|
-
return failure(
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
221
|
+
return failure(
|
|
222
|
+
'authoring',
|
|
223
|
+
startedAt,
|
|
224
|
+
targetViolation,
|
|
225
|
+
toSessionSnapshot(session, this.context.capabilities),
|
|
226
|
+
{
|
|
227
|
+
sessionId: session.sessionId,
|
|
228
|
+
revision: session.revision,
|
|
229
|
+
},
|
|
230
|
+
);
|
|
157
231
|
}
|
|
158
232
|
if (!session.dirty) {
|
|
159
233
|
return success('authoring', startedAt, toSessionSnapshot(session, this.context.capabilities), {
|
|
@@ -164,36 +238,63 @@ export class AuthoringService {
|
|
|
164
238
|
|
|
165
239
|
const committedPaths: string[] = [];
|
|
166
240
|
const failedPaths: string[] = [];
|
|
167
|
-
this.eventService.emit({
|
|
241
|
+
this.eventService.emit({
|
|
242
|
+
kind: 'save.started',
|
|
243
|
+
sessionId: session.sessionId,
|
|
244
|
+
canonicalPathKey: session.canonicalPathKey,
|
|
245
|
+
revision: session.revision,
|
|
246
|
+
});
|
|
168
247
|
try {
|
|
169
248
|
const writer = new ProjectWriter(createWriterFileSystem(fileSystem, committedPaths, failedPaths));
|
|
170
249
|
await writer.write(materializeUamProject(session.project), session.fairyPath);
|
|
171
250
|
session.lastSavedRevision = session.revision;
|
|
172
251
|
session.dirty = false;
|
|
173
252
|
const cacheEntry = this.cacheService.refreshSession(session);
|
|
174
|
-
this.eventService.emit({
|
|
175
|
-
|
|
253
|
+
this.eventService.emit({
|
|
254
|
+
kind: 'save.completed',
|
|
255
|
+
sessionId: session.sessionId,
|
|
256
|
+
canonicalPathKey: session.canonicalPathKey,
|
|
257
|
+
revision: session.revision,
|
|
258
|
+
});
|
|
259
|
+
this.eventService.emit({
|
|
260
|
+
kind: 'cache.updated',
|
|
261
|
+
sessionId: session.sessionId,
|
|
262
|
+
canonicalPathKey: session.canonicalPathKey,
|
|
263
|
+
revision: session.revision,
|
|
264
|
+
cacheRevision: cacheEntry.revision,
|
|
265
|
+
});
|
|
176
266
|
return success('authoring', startedAt, toSessionSnapshot(session, this.context.capabilities), {
|
|
177
267
|
sessionId: session.sessionId,
|
|
178
268
|
revision: session.revision,
|
|
179
269
|
});
|
|
180
270
|
} catch (error) {
|
|
181
271
|
this.cacheService.invalidateSession(session);
|
|
182
|
-
this.eventService.emit({
|
|
183
|
-
|
|
184
|
-
code: 'save_partial_failure',
|
|
185
|
-
message: error instanceof Error ? error.message : String(error),
|
|
272
|
+
this.eventService.emit({
|
|
273
|
+
kind: 'save.failed',
|
|
186
274
|
sessionId: session.sessionId,
|
|
187
275
|
canonicalPathKey: session.canonicalPathKey,
|
|
188
|
-
attemptedRevision: session.revision,
|
|
189
|
-
lastSavedRevision: session.lastSavedRevision,
|
|
190
|
-
committedPaths,
|
|
191
|
-
failedPaths,
|
|
192
|
-
diskMayBePartiallyUpdated: true,
|
|
193
|
-
}, toSessionSnapshot(session, this.context.capabilities), {
|
|
194
|
-
sessionId: session.sessionId,
|
|
195
276
|
revision: session.revision,
|
|
196
277
|
});
|
|
278
|
+
return failure(
|
|
279
|
+
'authoring',
|
|
280
|
+
startedAt,
|
|
281
|
+
{
|
|
282
|
+
code: 'save_partial_failure',
|
|
283
|
+
message: error instanceof Error ? error.message : String(error),
|
|
284
|
+
sessionId: session.sessionId,
|
|
285
|
+
canonicalPathKey: session.canonicalPathKey,
|
|
286
|
+
attemptedRevision: session.revision,
|
|
287
|
+
lastSavedRevision: session.lastSavedRevision,
|
|
288
|
+
committedPaths,
|
|
289
|
+
failedPaths,
|
|
290
|
+
diskMayBePartiallyUpdated: true,
|
|
291
|
+
},
|
|
292
|
+
toSessionSnapshot(session, this.context.capabilities),
|
|
293
|
+
{
|
|
294
|
+
sessionId: session.sessionId,
|
|
295
|
+
revision: session.revision,
|
|
296
|
+
},
|
|
297
|
+
);
|
|
197
298
|
}
|
|
198
299
|
}
|
|
199
300
|
}
|
package/src/services/context.ts
CHANGED
|
@@ -25,7 +25,7 @@ export interface BackendSessionState {
|
|
|
25
25
|
canonicalProjectPath: string;
|
|
26
26
|
canonicalPathKey: string;
|
|
27
27
|
lockFilePath: string;
|
|
28
|
-
project: import('@openfairygui/core').UamProject;
|
|
28
|
+
project: import('@openfairygui/core/uam').UamProject;
|
|
29
29
|
revision: number;
|
|
30
30
|
lastSavedRevision: number;
|
|
31
31
|
dirty: boolean;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { ProjectReader,
|
|
1
|
+
import { ProjectReader, type FileSystem } from '@openfairygui/core/project-io';
|
|
2
|
+
import { liftDocumentToUamProject, normalizeUamProject } from '@openfairygui/core/uam';
|
|
2
3
|
import { failure, success, type BackendContext, type BackendSessionState } from './context.js';
|
|
3
4
|
import type { CacheService } from './cache-service.js';
|
|
4
5
|
import type { EventService } from './event-service.js';
|
|
@@ -20,7 +21,9 @@ function randomId(): string {
|
|
|
20
21
|
return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
|
|
21
22
|
}
|
|
22
23
|
|
|
23
|
-
function createCapabilityUnavailableError(
|
|
24
|
+
function createCapabilityUnavailableError(
|
|
25
|
+
capability: BackendCapabilityUnavailableError['capability'],
|
|
26
|
+
): BackendCapabilityUnavailableError {
|
|
24
27
|
const artifactCapability = capability === 'artifact.publish' || capability === 'artifact.restore';
|
|
25
28
|
return {
|
|
26
29
|
code: 'capability_unavailable',
|
|
@@ -34,7 +37,7 @@ function createCapabilityUnavailableError(capability: BackendCapabilityUnavailab
|
|
|
34
37
|
};
|
|
35
38
|
}
|
|
36
39
|
|
|
37
|
-
function createProjectReaderFileSystem(fileSystem: NonNullable<BackendContext['fileSystem']>):
|
|
40
|
+
function createProjectReaderFileSystem(fileSystem: NonNullable<BackendContext['fileSystem']>): FileSystem {
|
|
38
41
|
return {
|
|
39
42
|
readFile(filePath: string): Promise<string> {
|
|
40
43
|
return fileSystem.readFile(filePath);
|
|
@@ -79,7 +82,14 @@ export class RuntimeService {
|
|
|
79
82
|
private readonly jobService: JobService,
|
|
80
83
|
) {}
|
|
81
84
|
|
|
82
|
-
public async openSession(input: {
|
|
85
|
+
public async openSession(input: {
|
|
86
|
+
projectPath: string;
|
|
87
|
+
}): Promise<
|
|
88
|
+
BackendResult<
|
|
89
|
+
BackendSessionSnapshot,
|
|
90
|
+
InProcessLockConflictError | AdvisoryLockConflictError | BackendCapabilityUnavailableError
|
|
91
|
+
>
|
|
92
|
+
> {
|
|
83
93
|
const startedAt = Date.now();
|
|
84
94
|
if (!this.context.fileSystem) {
|
|
85
95
|
return failure('runtime', startedAt, createCapabilityUnavailableError('fileSystem'));
|
|
@@ -104,14 +114,18 @@ export class RuntimeService {
|
|
|
104
114
|
let advisoryLock: BackendFileHandle | null = null;
|
|
105
115
|
try {
|
|
106
116
|
advisoryLock = await fileSystem.openExclusive(lockFilePath);
|
|
107
|
-
await advisoryLock.writeFile(
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
117
|
+
await advisoryLock.writeFile(
|
|
118
|
+
JSON.stringify(
|
|
119
|
+
this.context.host?.lockMetadata?.({
|
|
120
|
+
canonicalPathKey,
|
|
121
|
+
canonicalProjectPath,
|
|
122
|
+
lockFilePath,
|
|
123
|
+
}) ?? {
|
|
124
|
+
createdAt: new Date().toISOString(),
|
|
125
|
+
canonicalPathKey,
|
|
126
|
+
},
|
|
127
|
+
),
|
|
128
|
+
);
|
|
115
129
|
await advisoryLock.close();
|
|
116
130
|
|
|
117
131
|
const reader = new ProjectReader(createProjectReaderFileSystem(fileSystem));
|
|
@@ -197,16 +211,21 @@ export class RuntimeService {
|
|
|
197
211
|
});
|
|
198
212
|
}
|
|
199
213
|
|
|
200
|
-
public async closeSession(
|
|
201
|
-
|
|
202
|
-
): Promise<BackendResult<{ sessionId: string; closed: true }, SessionNotFoundError>> {
|
|
214
|
+
public async closeSession(input: {
|
|
215
|
+
sessionId: string;
|
|
216
|
+
}): Promise<BackendResult<{ sessionId: string; closed: true }, SessionNotFoundError>> {
|
|
203
217
|
const startedAt = Date.now();
|
|
204
218
|
const session = this.context.sessions.get(input.sessionId);
|
|
205
219
|
if (!session || session.closed) {
|
|
206
220
|
return failure('runtime', startedAt, createSessionNotFoundError(input.sessionId));
|
|
207
221
|
}
|
|
208
222
|
|
|
209
|
-
this.eventService.emit({
|
|
223
|
+
this.eventService.emit({
|
|
224
|
+
kind: 'session.closeRequested',
|
|
225
|
+
sessionId: session.sessionId,
|
|
226
|
+
canonicalPathKey: session.canonicalPathKey,
|
|
227
|
+
revision: session.revision,
|
|
228
|
+
});
|
|
210
229
|
if (this.context.fileSystem && session.lockFilePath) {
|
|
211
230
|
await this.context.fileSystem.unlink(session.lockFilePath).catch(() => undefined);
|
|
212
231
|
}
|
|
@@ -216,12 +235,22 @@ export class RuntimeService {
|
|
|
216
235
|
this.context.sessionsByPath.delete(session.canonicalPathKey);
|
|
217
236
|
this.cacheService.removeSession(session.sessionId);
|
|
218
237
|
this.jobService.removeSession(session.sessionId);
|
|
219
|
-
this.eventService.emit({
|
|
238
|
+
this.eventService.emit({
|
|
239
|
+
kind: 'session.closed',
|
|
240
|
+
sessionId: session.sessionId,
|
|
241
|
+
canonicalPathKey: session.canonicalPathKey,
|
|
242
|
+
revision: session.revision,
|
|
243
|
+
});
|
|
220
244
|
this.eventService.removeSession(session.sessionId);
|
|
221
245
|
|
|
222
|
-
return success(
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
246
|
+
return success(
|
|
247
|
+
'runtime',
|
|
248
|
+
startedAt,
|
|
249
|
+
{
|
|
250
|
+
sessionId: session.sessionId,
|
|
251
|
+
closed: true,
|
|
252
|
+
},
|
|
253
|
+
{ sessionId: session.sessionId, revision: session.revision },
|
|
254
|
+
);
|
|
226
255
|
}
|
|
227
256
|
}
|