@openfairygui/backend 0.2.0-alpha.9 → 0.2.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openfairygui/backend",
3
- "version": "0.2.0-alpha.9",
3
+ "version": "0.2.0",
4
4
  "description": "FairyGUI Headless Authoring SDK — stateful backend runtime and session services.",
5
5
  "author": "OpenFairyGUI Contributors",
6
6
  "license": "MIT",
@@ -52,15 +52,16 @@
52
52
  "runtime"
53
53
  ],
54
54
  "dependencies": {
55
- "@openfairygui/core": "0.2.0-alpha.9",
56
- "@openfairygui/functions": "0.2.0-alpha.8"
55
+ "@openfairygui/core": "0.2.0",
56
+ "@openfairygui/functions": "0.2.0"
57
57
  },
58
58
  "devDependencies": {
59
59
  "ava": "^7.0.0",
60
- "tsx": "^4.0.0"
60
+ "tsx": "^4.0.0",
61
+ "@openfairygui/test-utils": "0.2.0-alpha.10"
61
62
  },
62
63
  "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"
64
+ "build": "tsdown",
65
+ "build:watch": "tsdown --watch"
65
66
  }
66
67
  }
package/src/index.ts CHANGED
@@ -1,21 +1,29 @@
1
1
  export {
2
- BackendRuntime,
2
+ BACKEND_CAPABILITY_SCHEMA_VERSION,
3
+ BACKEND_COMPATIBILITY_POLICY,
4
+ BACKEND_CONTRACT_VERSION,
5
+ type BackendDiagnostic,
6
+ type BackendMessage,
7
+ type BackendResponseMeta,
8
+ type BackendStage,
9
+ } from './contracts.js';
10
+ export {
3
11
  type AdvisoryLockConflictError,
4
12
  type ApplySessionTransactionInput,
5
13
  type BackendArtifactBridgeCapability,
6
14
  type BackendCacheEntry,
7
15
  type BackendCacheSnapshot,
16
+ type BackendCapabilities,
8
17
  type BackendCapabilityManifest,
9
18
  type BackendCapabilityUnavailableError,
10
- type BackendCapabilities,
11
19
  type BackendError,
12
20
  type BackendEvent,
13
21
  type BackendEventKind,
14
22
  type BackendFailure,
15
- type BackendFileHandle,
16
23
  type BackendFileStat,
17
24
  type BackendFileSystem,
18
25
  type BackendHostAdapter,
26
+ type BackendJobErrors,
19
27
  type BackendJobKind,
20
28
  type BackendJobListSnapshot,
21
29
  type BackendJobListStatusFilter,
@@ -24,11 +32,13 @@ export {
24
32
  type BackendJobProgress,
25
33
  type BackendJobSnapshot,
26
34
  type BackendJobStatus,
27
- type BackendJobErrors,
35
+ type BackendProjectSessionStorage,
28
36
  type BackendResult,
37
+ BackendRuntime,
38
+ type BackendRuntimeOptions,
39
+ type BackendSessionLock,
29
40
  type BackendSessionSnapshot,
30
41
  type BackendSuccess,
31
- type BackendRuntimeOptions,
32
42
  type CacheRefreshFailedError,
33
43
  type CancelJobInput,
34
44
  type EventCursorInvalidError,
@@ -43,25 +53,16 @@ export {
43
53
  type MaterializeValidationFailedError,
44
54
  type MaterializeWriteFailedError,
45
55
  type OpenProjectSessionInput,
46
- type BackendProjectSessionStorage,
47
56
  type RefreshCacheInput,
48
- type SaveSessionInput,
49
57
  type SavePartialFailureError,
58
+ type SaveSessionInput,
50
59
  type SessionNotFoundError,
51
60
  type SessionStaleWriteError,
61
+ type UamFidelityUnsupportedError,
52
62
  } from './runtime.js';
53
63
  export {
54
- createBackendStorageFileSystem,
55
64
  type BackendAsyncStorageAdapter,
56
65
  type BackendStorageFileSystem,
57
66
  type BackendStorageStatLike,
67
+ createBackendStorageFileSystem,
58
68
  } from './storage.js';
59
- export {
60
- type BackendDiagnostic,
61
- type BackendMessage,
62
- type BackendResponseMeta,
63
- type BackendStage,
64
- BACKEND_CAPABILITY_SCHEMA_VERSION,
65
- BACKEND_COMPATIBILITY_POLICY,
66
- BACKEND_CONTRACT_VERSION,
67
- } from './contracts.js';
package/src/node.ts CHANGED
@@ -2,11 +2,11 @@ import fs from 'node:fs/promises';
2
2
  import path from 'node:path';
3
3
  import {
4
4
  BackendRuntime,
5
- type BackendFileHandle,
6
5
  type BackendFileStat,
7
6
  type BackendFileSystem,
8
7
  type BackendHostAdapter,
9
8
  type BackendRuntimeOptions,
9
+ type BackendSessionLock,
10
10
  } from './runtime.js';
11
11
 
12
12
  export function createNodeBackendFileSystem(): BackendFileSystem {
@@ -40,20 +40,36 @@ export function createNodeBackendFileSystem(): BackendFileSystem {
40
40
  return path.resolve(filePath);
41
41
  }
42
42
  },
43
- async openExclusive(filePath: string): Promise<BackendFileHandle> {
43
+ async acquireSessionLock(filePath: string): Promise<BackendSessionLock> {
44
44
  const handle = await fs.open(filePath, 'wx');
45
+ let closed = false;
46
+ let released = false;
47
+ const closeHandle = async (): Promise<void> => {
48
+ if (closed) return;
49
+ await handle.close();
50
+ closed = true;
51
+ };
45
52
  return {
46
- writeFile(content: string): Promise<void> {
47
- return handle.writeFile(content, 'utf-8');
53
+ async writeMetadata(content: string): Promise<void> {
54
+ await handle.writeFile(content, 'utf-8');
55
+ await closeHandle();
48
56
  },
49
- close(): Promise<void> {
50
- return handle.close();
57
+ async release(): Promise<void> {
58
+ if (released) return;
59
+ await closeHandle();
60
+ await fs.unlink(filePath).catch((error: NodeJS.ErrnoException) => {
61
+ if (error.code !== 'ENOENT') throw error;
62
+ });
63
+ released = true;
51
64
  },
52
65
  };
53
66
  },
54
67
  unlink(filePath: string): Promise<void> {
55
68
  return fs.unlink(filePath);
56
69
  },
70
+ rmdir(dirPath: string): Promise<void> {
71
+ return fs.rmdir(dirPath);
72
+ },
57
73
  join(...paths: string[]): string {
58
74
  return path.join(...paths);
59
75
  },
@@ -86,11 +102,11 @@ export function createNodeBackendRuntime(options: BackendRuntimeOptions = {}): B
86
102
  });
87
103
  }
88
104
 
89
- export { BackendRuntime };
90
105
  export type {
91
- BackendFileHandle,
92
106
  BackendFileStat,
93
107
  BackendFileSystem,
94
108
  BackendHostAdapter,
95
109
  BackendRuntimeOptions,
110
+ BackendSessionLock,
96
111
  } from './runtime.js';
112
+ export { BackendRuntime };
@@ -0,0 +1,126 @@
1
+ import {
2
+ UAM_SUPPORTED_MATERIALIZATION_SCOPE,
3
+ UAM_SUPPORTED_TRANSACTION_SCOPE,
4
+ } from '@openfairygui/core/uam';
5
+ import {
6
+ BACKEND_CAPABILITY_SCHEMA_VERSION,
7
+ BACKEND_COMPATIBILITY_POLICY,
8
+ BACKEND_CONTRACT_VERSION,
9
+ } from '../contracts.js';
10
+ import { createRuntimePathPolicy } from '../path-policy.js';
11
+ import { createArtifactCapabilities } from '../services/artifact-service.js';
12
+ import type { BackendArtifactBridgeCapability, BackendCapabilities } from './contracts.js';
13
+
14
+ const BACKEND_METHODS = [
15
+ 'getCapabilities',
16
+ 'openSession',
17
+ 'openProjectSession',
18
+ 'getSession',
19
+ 'applyTransaction',
20
+ 'saveSession',
21
+ 'materializeSession',
22
+ 'closeSession',
23
+ 'getEvents',
24
+ 'getJob',
25
+ 'listJobs',
26
+ 'cancelJob',
27
+ 'getCacheSnapshot',
28
+ 'refreshCache',
29
+ ] as const;
30
+
31
+ const ARTIFACT_BRIDGE_CAPABILITY = {
32
+ available: false,
33
+ requiredHost: 'node',
34
+ executionBoundary: 'external-bridge',
35
+ bridgeEntrypoint: '@openfairygui/backend/node',
36
+ reason: 'publish/restore require explicit Node-hosted filesystem and artifact execution.',
37
+ } as const satisfies BackendArtifactBridgeCapability;
38
+
39
+ export function createCapabilities(): BackendCapabilities {
40
+ return {
41
+ contractVersion: BACKEND_CONTRACT_VERSION,
42
+ capabilitySchemaVersion: BACKEND_CAPABILITY_SCHEMA_VERSION,
43
+ transactionKernelOwner: '@openfairygui/core',
44
+ appSeamOwner: '@openfairygui/functions',
45
+ runtimeOwner: '@openfairygui/backend',
46
+ methods: BACKEND_METHODS,
47
+ read: {
48
+ capabilitySnapshot: true,
49
+ sessionSnapshot: true,
50
+ },
51
+ authoring: {
52
+ applyTransaction: true,
53
+ saveSession: true,
54
+ resourceKinds: [...UAM_SUPPORTED_MATERIALIZATION_SCOPE.resourceKinds],
55
+ nodeKinds: [...UAM_SUPPORTED_MATERIALIZATION_SCOPE.nodeKinds],
56
+ gearKinds: [...UAM_SUPPORTED_MATERIALIZATION_SCOPE.gearKinds],
57
+ transactionScope: {
58
+ resourceKinds: [...UAM_SUPPORTED_TRANSACTION_SCOPE.resourceKinds],
59
+ nodeKinds: [...UAM_SUPPORTED_TRANSACTION_SCOPE.nodeKinds],
60
+ gearKinds: [...UAM_SUPPORTED_TRANSACTION_SCOPE.gearKinds],
61
+ },
62
+ unsupported: ['artifact.publish', 'artifact.restore'],
63
+ },
64
+ artifact: createArtifactCapabilities(),
65
+ manifest: {
66
+ browserSafe: true,
67
+ rootEntrypoint: '@openfairygui/backend',
68
+ nodeEntrypoint: '@openfairygui/backend/node',
69
+ adapters: {
70
+ fileSystem: {
71
+ injected: true,
72
+ requiredFor: ['openSession', 'saveSession', 'materializeSession'],
73
+ },
74
+ projectStorage: {
75
+ injected: true,
76
+ browserSafe: true,
77
+ requiredFor: ['openProjectSession.writeback', 'saveSession', 'materializeSession'],
78
+ adapterFactory: 'createBackendStorageFileSystem',
79
+ },
80
+ host: {
81
+ injected: true,
82
+ requiredFor: ['advisoryLockMetadata'],
83
+ },
84
+ },
85
+ executionBoundaries: {
86
+ projectSession: 'in-process-browser-safe',
87
+ fileBackedSession: 'adapter-backed',
88
+ artifactPublish: ARTIFACT_BRIDGE_CAPABILITY,
89
+ artifactRestore: ARTIFACT_BRIDGE_CAPABILITY,
90
+ },
91
+ diagnostics: {
92
+ stableCodes: true,
93
+ errorDiagnosticMirror: true,
94
+ },
95
+ },
96
+ compatibilityPolicy: BACKEND_COMPATIBILITY_POLICY,
97
+ runtime: {
98
+ sessionRuntime: true,
99
+ advisoryLocking: true,
100
+ coordinatedSave: true,
101
+ atomicSave: false,
102
+ staleRevisionProtection: true,
103
+ pathPolicy: createRuntimePathPolicy(),
104
+ events: {
105
+ polling: true,
106
+ subscriptions: false,
107
+ retentionLimit: 1000,
108
+ sequenceScope: 'runtime',
109
+ },
110
+ jobs: {
111
+ inMemory: true,
112
+ cooperativeCancel: true,
113
+ persistent: false,
114
+ supportedKinds: ['cache.refresh'],
115
+ artifactJobs: false,
116
+ completedRetentionLimit: 100,
117
+ },
118
+ cache: {
119
+ derivedReadOnly: true,
120
+ keyedBy: 'canonicalPathKey',
121
+ sourceOfTruth: false,
122
+ refreshMethod: 'refreshCache',
123
+ },
124
+ },
125
+ };
126
+ }