@openfairygui/backend 0.2.0 → 0.3.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/runtime.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { ApplyUamTransactionAppError } from '@openfairygui/functions/uam';
2
+ import type { ProjectValidationReport } from '@openfairygui/core';
2
3
  import type { PathPolicyViolationError } from './path-policy.js';
3
4
  import { AuthoringService } from './services/authoring-service.js';
4
5
  import { CacheService } from './services/cache-service.js';
@@ -21,6 +22,7 @@ import type {
21
22
  BackendJobNotCancellableError,
22
23
  BackendJobNotFoundError,
23
24
  BackendJobSnapshot,
25
+ BackendProjectOutline,
24
26
  BackendResult,
25
27
  BackendRuntimeOptions,
26
28
  BackendSessionSnapshot,
@@ -31,6 +33,8 @@ import type {
31
33
  GetEventsInput,
32
34
  GetEventsSnapshot,
33
35
  GetJobInput,
36
+ GetProjectOutlineInput,
37
+ ValidateSessionInput,
34
38
  InProcessLockConflictError,
35
39
  ListJobsInput,
36
40
  MaterializeSessionInput,
@@ -113,6 +117,18 @@ export class BackendRuntime {
113
117
  return this.readService.getSession(input);
114
118
  }
115
119
 
120
+ public getProjectOutline(
121
+ input: GetProjectOutlineInput,
122
+ ): BackendResult<BackendProjectOutline, SessionNotFoundError> {
123
+ return this.readService.getProjectOutline(input);
124
+ }
125
+
126
+ public validateSession(
127
+ input: ValidateSessionInput,
128
+ ): BackendResult<ProjectValidationReport, SessionNotFoundError> {
129
+ return this.readService.validateSession(input);
130
+ }
131
+
116
132
  public async applyTransaction(
117
133
  input: ApplySessionTransactionInput,
118
134
  ): Promise<
@@ -29,6 +29,8 @@ export interface BackendSessionState {
29
29
  sessionLock: BackendSessionLock | null;
30
30
  fileSystem?: BackendFileSystem;
31
31
  project: import('@openfairygui/core/uam').UamProject;
32
+ readDiagnostics: import('@openfairygui/core').ProjectDiagnostic[];
33
+ readComplete: boolean;
32
34
  uamFidelity: 'full' | 'unsupported';
33
35
  revision: number;
34
36
  lastSavedRevision: number;
@@ -1,6 +1,55 @@
1
- import { failure, success, type BackendContext } from './context.js';
2
- import type { BackendCapabilities, BackendResult, BackendSessionSnapshot, SessionNotFoundError } from '../runtime.js';
1
+ import { failure, success, type BackendContext, type BackendSessionState } from './context.js';
2
+ import type {
3
+ BackendCapabilities,
4
+ BackendProjectOutline,
5
+ BackendResult,
6
+ BackendSessionSnapshot,
7
+ GetProjectOutlineInput,
8
+ SessionNotFoundError,
9
+ } from '../runtime.js';
3
10
  import { createSessionNotFoundError, toSessionSnapshot } from './session-utils.js';
11
+ import { validateProject } from '@openfairygui/functions';
12
+ import type { ProjectValidationReport } from '@openfairygui/core';
13
+
14
+ function toProjectOutline(session: BackendSessionState): BackendProjectOutline {
15
+ const project = session.project;
16
+ // ponytail: full outline is O(project size); add filters or pagination only if payload size becomes a measured problem.
17
+ return {
18
+ sessionId: session.sessionId,
19
+ revision: session.revision,
20
+ projectId: project.projectId,
21
+ projectType: project.projectType,
22
+ version: project.version,
23
+ branches: [...project.branches],
24
+ packages: project.packages.map((pkg) => ({
25
+ id: pkg.id,
26
+ name: pkg.name,
27
+ branchNames: [...pkg.branchNames],
28
+ folders: pkg.folders.map((folder) => ({ branch: folder.branch, path: folder.path })),
29
+ resources: pkg.resources.map((resource) => ({
30
+ id: resource.id,
31
+ name: resource.name,
32
+ path: resource.path,
33
+ kind: resource.kind,
34
+ branch: resource.branch,
35
+ ...(resource.kind === 'component' ? {
36
+ component: {
37
+ displayList: resource.component.displayList.map((node) => ({
38
+ id: node.id,
39
+ name: node.name,
40
+ kind: node.kind,
41
+ })),
42
+ controllers: resource.component.controllers.map((controller) => ({
43
+ name: controller.name,
44
+ pages: controller.pages.map((page) => ({ id: page.id, name: page.name })),
45
+ })),
46
+ transitions: resource.component.transitions.map((transition) => ({ name: transition.name })),
47
+ },
48
+ } : {}),
49
+ })),
50
+ })),
51
+ };
52
+ }
4
53
 
5
54
  export class ReadService {
6
55
  public constructor(private readonly context: BackendContext) {}
@@ -20,4 +69,38 @@ export class ReadService {
20
69
  revision: session.revision,
21
70
  });
22
71
  }
72
+
73
+ public getProjectOutline(
74
+ input: GetProjectOutlineInput,
75
+ ): BackendResult<BackendProjectOutline, SessionNotFoundError> {
76
+ const startedAt = Date.now();
77
+ const session = this.context.sessions.get(input.sessionId);
78
+ if (!session || session.closed) {
79
+ return failure('read', startedAt, createSessionNotFoundError(input.sessionId));
80
+ }
81
+ return success('read', startedAt, toProjectOutline(session), {
82
+ sessionId: session.sessionId,
83
+ revision: session.revision,
84
+ });
85
+ }
86
+
87
+ public validateSession(
88
+ input: { sessionId: string },
89
+ ): BackendResult<ProjectValidationReport, SessionNotFoundError> {
90
+ const startedAt = Date.now();
91
+ const session = this.context.sessions.get(input.sessionId);
92
+ if (!session || session.closed) {
93
+ return failure('read', startedAt, createSessionNotFoundError(input.sessionId));
94
+ }
95
+ const report = validateProject(session.project, {
96
+ readDiagnostics: session.readDiagnostics,
97
+ complete: session.readComplete,
98
+ validateSources: true,
99
+ });
100
+ return success('read', startedAt, report, {
101
+ sessionId: session.sessionId,
102
+ revision: session.revision,
103
+ diagnostics: report.diagnostics.map(({ code, message, severity, path }) => ({ code, message, severity, path })),
104
+ });
105
+ }
23
106
  }
@@ -220,7 +220,9 @@ export class RuntimeService {
220
220
  ),
221
221
  );
222
222
  const reader = new ProjectReader(createProjectReaderFileSystem(fileSystem));
223
- const document = await reader.read(fairyPath, { hydrateResourceBytes: true });
223
+ const read = await reader.readDetailed(fairyPath, { hydrateResourceBytes: true });
224
+ if (!read.document) throw new Error(read.diagnostics[0]?.message ?? `Unable to read project: ${fairyPath}`);
225
+ const document = read.document;
224
226
  const project = liftDocumentToUamProject(document);
225
227
  const sessionId = randomId();
226
228
  const session: BackendSessionState = {
@@ -232,6 +234,8 @@ export class RuntimeService {
232
234
  sessionLock,
233
235
  fileSystem,
234
236
  project,
237
+ readDiagnostics: read.diagnostics,
238
+ readComplete: read.complete,
235
239
  uamFidelity: (await hasFullUamFidelity(document, project)) ? 'full' : 'unsupported',
236
240
  revision: 0,
237
241
  lastSavedRevision: 0,
@@ -305,6 +309,8 @@ export class RuntimeService {
305
309
  sessionLock: null,
306
310
  fileSystem: storage?.fileSystem,
307
311
  project: normalizeUamProject(input.project),
312
+ readDiagnostics: [],
313
+ readComplete: true,
308
314
  uamFidelity: 'full',
309
315
  revision: 0,
310
316
  lastSavedRevision: 0,