@pylonts/dsl 1.1.4 → 1.1.6

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.
Files changed (78) hide show
  1. package/dist/controller.d.ts +27 -0
  2. package/dist/controller.js +11 -0
  3. package/dist/convert.d.ts +18 -6
  4. package/dist/convert.js +12 -2
  5. package/dist/curd.d.ts +0 -2
  6. package/dist/curd.js +7 -0
  7. package/dist/dao.d.ts +111 -2
  8. package/dist/dao.js +28 -2
  9. package/dist/db.js +3 -0
  10. package/dist/dsl.d.ts +24 -1
  11. package/dist/dsl.js +16 -0
  12. package/dist/dto.d.ts +6 -2
  13. package/dist/dto.js +20 -9
  14. package/dist/endpoint.d.ts +15 -0
  15. package/dist/endpoint.js +3 -0
  16. package/dist/exception.d.ts +14 -0
  17. package/dist/exception.js +9 -0
  18. package/dist/field-rule.d.ts +20 -0
  19. package/dist/field-rule.js +19 -0
  20. package/dist/flow.d.ts +24 -2
  21. package/dist/flow.js +16 -4
  22. package/dist/index.d.ts +7 -0
  23. package/dist/index.js +9 -0
  24. package/dist/mermaid-driver.js +32 -3
  25. package/dist/method.d.ts +11 -0
  26. package/dist/method.js +3 -0
  27. package/dist/mysql-driver.js +8 -1
  28. package/dist/provider.d.ts +6 -11
  29. package/dist/provider.js +2 -2
  30. package/dist/service.d.ts +16 -6
  31. package/dist/service.js +13 -2
  32. package/dist/third-service.d.ts +75 -0
  33. package/dist/third-service.js +96 -0
  34. package/dist/typebox-driver.d.ts +6 -0
  35. package/dist/typebox-driver.js +76 -14
  36. package/dist/utils.d.ts +25 -10
  37. package/dist/utils.js +28 -11
  38. package/docs/curd.md +110 -110
  39. package/docs/dto.md +9 -2
  40. package/docs/table.md +135 -135
  41. package/docs/third-service.md +122 -0
  42. package/package.json +4 -1
  43. package/src/action.ts +10 -10
  44. package/src/asset.ts +62 -62
  45. package/src/bases.ts +29 -29
  46. package/src/component.ts +21 -21
  47. package/src/controller.ts +40 -0
  48. package/src/convert.ts +34 -7
  49. package/src/curd.ts +7 -2
  50. package/src/dao.ts +162 -3
  51. package/src/db.ts +5 -0
  52. package/src/dsl.ts +49 -1
  53. package/src/dto.ts +25 -9
  54. package/src/endpoint.ts +18 -0
  55. package/src/event.ts +12 -12
  56. package/src/exception.ts +28 -0
  57. package/src/field-rule.ts +47 -0
  58. package/src/flow.ts +143 -103
  59. package/src/index.ts +11 -1
  60. package/src/mermaid-driver.ts +31 -3
  61. package/src/method.ts +20 -0
  62. package/src/mock.ts +12 -12
  63. package/src/mysql-driver.ts +8 -2
  64. package/src/navigation.ts +28 -28
  65. package/src/page-def.ts +79 -79
  66. package/src/page-flow.ts +153 -153
  67. package/src/page.ts +76 -76
  68. package/src/popup.ts +25 -25
  69. package/src/project.ts +97 -97
  70. package/src/provider.ts +7 -12
  71. package/src/ref.ts +18 -18
  72. package/src/route.ts +11 -11
  73. package/src/service.ts +28 -6
  74. package/src/third-service.ts +186 -0
  75. package/src/typebox-driver.ts +264 -192
  76. package/src/utils.ts +54 -17
  77. package/dist/check-inheritance.d.ts +0 -9
  78. package/dist/check-inheritance.js +0 -58
package/src/page-def.ts CHANGED
@@ -1,80 +1,80 @@
1
- import type { ImportableSchemaBase, CollectionSchemaBase, SchemaBase } from './dsl.js';
2
- import { DtoField } from './dto.js';
3
- import type { DtoMessage, DtoArrayField, DtoObjectField } from './dto.js';
4
- import type { ComponentSchema } from './component.js';
5
- import type { ActionSchema } from './action.js';
6
-
7
- export type { RouteDataSchema } from './route.js';
8
- export { defineRouteData } from './route.js';
9
-
10
- // PageDef: a page skeleton declaration. Describes what data a page needs
11
- // and where each field comes from (route params, provider calls, or literals).
12
- // Driver identifies the source by inspecting each field's .schema back-reference.
13
-
14
- /** Page lifecycle events that trigger data loading. */
15
- export interface EventSchema extends SchemaBase {
16
- type: 'onLoad' | 'onShow' | 'onHide' | 'onPullDownRefresh' | 'onReachBottom';
17
- /** Actions that fire when this event occurs. */
18
- actions?: ActionSchema[];
19
- }
20
-
21
- export const events = {
22
- onLoad(actions?: ActionSchema[], description?: string): EventSchema {
23
- return { name: 'onLoad', type: 'onLoad', actions, description };
24
- },
25
- onShow(actions?: ActionSchema[], description?: string): EventSchema {
26
- return { name: 'onShow', type: 'onShow', actions, description };
27
- },
28
- onHide(actions?: ActionSchema[], description?: string): EventSchema {
29
- return { name: 'onHide', type: 'onHide', actions, description };
30
- },
31
- onPullDownRefresh(actions?: ActionSchema[], description?: string): EventSchema {
32
- return { name: 'onPullDownRefresh', type: 'onPullDownRefresh', actions, description };
33
- },
34
- onReachBottom(actions?: ActionSchema[], description?: string): EventSchema {
35
- return { name: 'onReachBottom', type: 'onReachBottom', actions, description };
36
- },
37
- };
38
-
39
- /** Page data: a named collection of fields that defines the page's data shape. */
40
- export interface PageDataSchema extends CollectionSchemaBase {
41
- type: 'pageData';
42
- fields: Record<string, DtoField | DtoMessage | DtoArrayField | DtoObjectField>;
43
- }
44
-
45
- export function definePageData(fields: PageDataSchema['fields']): PageDataSchema {
46
- // Page data has no meaningful identity of its own; the page name already
47
- // scopes it, so the schema name defaults to 'data'.
48
- const data: PageDataSchema = { name: 'data', type: 'pageData', fields };
49
- // Write back field names (same convention as buildMessage) so defineRef
50
- // produces refs with a real field name instead of ''.
51
- for (const key of Object.keys(data.fields)) {
52
- const df = data.fields[key];
53
- if (df instanceof DtoField) {
54
- df.name = key;
55
- df.schema = data;
56
- }
57
- }
58
- return data;
59
- }
60
-
61
- /** Page skeleton definition. */
62
- export interface PageDef extends ImportableSchemaBase {
63
- data: PageDataSchema;
64
- /** Lifecycle events that trigger data loading. */
65
- events: EventSchema[];
66
- /** Page actions (provider calls, navigation, etc.). */
67
- actions: ActionSchema[];
68
- /** UI component declarations that make up the page skeleton. */
69
- components: ComponentSchema[];
70
- /** Whether the driver should generate page-level loading / error / empty state wrappers. */
71
- handleStates?: boolean;
72
- }
73
-
74
- /** Define a page skeleton. */
75
- export function definePageDef(
76
- name: string,
77
- schema: Omit<PageDef, 'name'>,
78
- ): PageDef {
79
- return { name, ...schema };
1
+ import type { ImportableSchemaBase, CollectionSchemaBase, SchemaBase } from './dsl.js';
2
+ import { DtoField } from './dto.js';
3
+ import type { DtoMessage, DtoArrayField, DtoObjectField } from './dto.js';
4
+ import type { ComponentSchema } from './component.js';
5
+ import type { ActionSchema } from './action.js';
6
+
7
+ export type { RouteDataSchema } from './route.js';
8
+ export { defineRouteData } from './route.js';
9
+
10
+ // PageDef: a page skeleton declaration. Describes what data a page needs
11
+ // and where each field comes from (route params, provider calls, or literals).
12
+ // Driver identifies the source by inspecting each field's .schema back-reference.
13
+
14
+ /** Page lifecycle events that trigger data loading. */
15
+ export interface EventSchema extends SchemaBase {
16
+ type: 'onLoad' | 'onShow' | 'onHide' | 'onPullDownRefresh' | 'onReachBottom';
17
+ /** Actions that fire when this event occurs. */
18
+ actions?: ActionSchema[];
19
+ }
20
+
21
+ export const events = {
22
+ onLoad(actions?: ActionSchema[], description?: string): EventSchema {
23
+ return { name: 'onLoad', type: 'onLoad', actions, description };
24
+ },
25
+ onShow(actions?: ActionSchema[], description?: string): EventSchema {
26
+ return { name: 'onShow', type: 'onShow', actions, description };
27
+ },
28
+ onHide(actions?: ActionSchema[], description?: string): EventSchema {
29
+ return { name: 'onHide', type: 'onHide', actions, description };
30
+ },
31
+ onPullDownRefresh(actions?: ActionSchema[], description?: string): EventSchema {
32
+ return { name: 'onPullDownRefresh', type: 'onPullDownRefresh', actions, description };
33
+ },
34
+ onReachBottom(actions?: ActionSchema[], description?: string): EventSchema {
35
+ return { name: 'onReachBottom', type: 'onReachBottom', actions, description };
36
+ },
37
+ };
38
+
39
+ /** Page data: a named collection of fields that defines the page's data shape. */
40
+ export interface PageDataSchema extends CollectionSchemaBase {
41
+ type: 'pageData';
42
+ fields: Record<string, DtoField | DtoMessage | DtoArrayField | DtoObjectField>;
43
+ }
44
+
45
+ export function definePageData(fields: PageDataSchema['fields']): PageDataSchema {
46
+ // Page data has no meaningful identity of its own; the page name already
47
+ // scopes it, so the schema name defaults to 'data'.
48
+ const data: PageDataSchema = { name: 'data', type: 'pageData', fields };
49
+ // Write back field names (same convention as buildMessage) so defineRef
50
+ // produces refs with a real field name instead of ''.
51
+ for (const key of Object.keys(data.fields)) {
52
+ const df = data.fields[key];
53
+ if (df instanceof DtoField) {
54
+ df.name = key;
55
+ df.schema = data;
56
+ }
57
+ }
58
+ return data;
59
+ }
60
+
61
+ /** Page skeleton definition. */
62
+ export interface PageDef extends ImportableSchemaBase {
63
+ data: PageDataSchema;
64
+ /** Lifecycle events that trigger data loading. */
65
+ events: EventSchema[];
66
+ /** Page actions (provider calls, navigation, etc.). */
67
+ actions: ActionSchema[];
68
+ /** UI component declarations that make up the page skeleton. */
69
+ components: ComponentSchema[];
70
+ /** Whether the driver should generate page-level loading / error / empty state wrappers. */
71
+ handleStates?: boolean;
72
+ }
73
+
74
+ /** Define a page skeleton. */
75
+ export function definePageDef(
76
+ name: string,
77
+ schema: Omit<PageDef, 'name'>,
78
+ ): PageDef {
79
+ return { name, ...schema };
80
80
  }
package/src/page-flow.ts CHANGED
@@ -1,154 +1,154 @@
1
- import { SchemaBase } from './dsl.js';
2
- import type { ActionSchema } from './action.js';
3
- import { Page, getRegisteredPages, clearRegisteredPages } from './page.js';
4
- import type { PageDef } from './page-def.js';
5
- import type { NavigationAction } from './navigation.js';
6
-
7
- // Page-driven flow: every node is a page, and a page belongs to an app.
8
- // Leaf nodes are pages too — a journey starts at a page and ends at a page.
9
- // Page definitions (PageSchema/Page/actions) live in page.ts; this file
10
- // models the flow graph between pages.
11
-
12
- export interface PageEdge extends SchemaBase {
13
- /** Trigger action; undefined = default path (success/normal). */
14
- when?: ActionSchema;
15
- start: Page;
16
- end: Page;
17
- }
18
-
19
- export interface PageFlow extends SchemaBase {
20
- /** Entry page. */
21
- start: Page;
22
- /** All pages explicitly declared by the caller. */
23
- pages: Page[];
24
- edges: PageEdge[];
25
- }
26
-
27
- export function pageEdge(start: Page, end: Page, when?: ActionSchema, description?: string): PageEdge {
28
- // Auto name for uniformity with SchemaBase; `when` stays the branch marker.
29
- return { name: `${start.name}->${end.name}`, start, end, when, description };
30
- }
31
-
32
- export function definePageFlow(
33
- name: string,
34
- schema: {
35
- start: Page;
36
- pages: Page[];
37
- edges: PageEdge[];
38
- description?: string;
39
- },
40
- ): PageFlow {
41
- const { start, pages, edges: explicitEdges, description } = schema;
42
-
43
- // Resolve the back action from any page's actions list (PageSchema at runtime)
44
- const backAction = findBackAction(pages);
45
-
46
- // Auto-generate back edges: for every explicit edge A→B (not itself a back),
47
- // if B has no explicit back edge and no existing edge B→A, add B→A with back.
48
- const hasExplicitBack = new Set<Page>();
49
- for (const e of explicitEdges) {
50
- if (e.when && e.when.name === 'back') hasExplicitBack.add(e.start);
51
- }
52
- const hasReverse = new Set<string>();
53
- for (const e of explicitEdges) {
54
- hasReverse.add(`${e.start.name}->${e.end.name}`);
55
- }
56
- const generatedBackEdges: PageEdge[] = [];
57
- if (backAction) {
58
- for (const e of explicitEdges) {
59
- if (e.when && e.when.name === 'back') continue;
60
- if (hasExplicitBack.has(e.end)) continue;
61
- if (hasReverse.has(`${e.end.name}->${e.start.name}`)) continue;
62
- generatedBackEdges.push({
63
- name: `${e.end.name}->${e.start.name}`,
64
- start: e.end,
65
- end: e.start,
66
- when: backAction,
67
- description: 'auto-generated back edge',
68
- });
69
- hasExplicitBack.add(e.end);
70
- }
71
- }
72
- const edges = [...explicitEdges, ...generatedBackEdges];
73
-
74
- // Validate: every registered page must be in the flow's pages
75
- const registered = getRegisteredPages();
76
- const orphaned: string[] = [];
77
- for (const rp of registered) {
78
- // Compare by object identity — caller passes the same reference
79
- if (!pages.includes(rp as unknown as Page)) {
80
- orphaned.push(rp.name);
81
- }
82
- }
83
- if (orphaned.length > 0) {
84
- throw new Error(
85
- `[definePageFlow "${name}"] orphaned pages (defined but not in flow): ${orphaned.join(', ')}`,
86
- );
87
- }
88
- clearRegisteredPages();
89
-
90
- // Validate: start must be in pages
91
- if (!pages.includes(start)) {
92
- throw new Error(
93
- `[definePageFlow "${name}"] start page "${start.name}" not in pages`,
94
- );
95
- }
96
-
97
- // Validate: every page appears in at least one edge
98
- const inEdge = new Set<Page>();
99
- for (const e of edges) {
100
- inEdge.add(e.start);
101
- inEdge.add(e.end);
102
- }
103
- const isolated: string[] = [];
104
- for (const p of pages) {
105
- if (!inEdge.has(p)) isolated.push(p.name);
106
- }
107
- if (isolated.length > 0) {
108
- throw new Error(
109
- `[definePageFlow "${name}"] isolated pages (no edges): ${isolated.join(', ')}`,
110
- );
111
- }
112
-
113
- // Validate: all pages reachable from start
114
- const adj = new Map<Page, Page[]>();
115
- for (const p of pages) adj.set(p, []);
116
- for (const e of edges) {
117
- const neighbors = adj.get(e.start);
118
- if (neighbors) neighbors.push(e.end);
119
- }
120
- const visited = new Set<Page>();
121
- const stack: Page[] = [start];
122
- while (stack.length > 0) {
123
- const cur = stack.pop()!;
124
- if (visited.has(cur)) continue;
125
- visited.add(cur);
126
- for (const next of adj.get(cur) ?? []) {
127
- if (!visited.has(next)) stack.push(next);
128
- }
129
- }
130
- const unreachable: string[] = [];
131
- for (const p of pages) {
132
- if (!visited.has(p)) unreachable.push(p.name);
133
- }
134
- if (unreachable.length > 0) {
135
- throw new Error(
136
- `[definePageFlow "${name}"] unreachable pages: ${unreachable.join(', ')}`,
137
- );
138
- }
139
-
140
- return { name, description, start, pages, edges };
141
- }
142
-
143
- /** Walk all pages' pageDef.actions (PageSchema runtime shape) to find a NavigationAction with method 'back'. */
144
- function findBackAction(pages: Page[]): NavigationAction | undefined {
145
- for (const p of pages) {
146
- const pageDef = (p as unknown as Record<string, unknown>).pageDef as PageDef | undefined;
147
- if (!pageDef?.actions) continue;
148
- for (const a of pageDef.actions) {
149
- const na = a as NavigationAction;
150
- if (na.type === 'navigation' && na.method === 'back') return na;
151
- }
152
- }
153
- return undefined;
1
+ import { SchemaBase } from './dsl.js';
2
+ import type { ActionSchema } from './action.js';
3
+ import { Page, getRegisteredPages, clearRegisteredPages } from './page.js';
4
+ import type { PageDef } from './page-def.js';
5
+ import type { NavigationAction } from './navigation.js';
6
+
7
+ // Page-driven flow: every node is a page, and a page belongs to an app.
8
+ // Leaf nodes are pages too — a journey starts at a page and ends at a page.
9
+ // Page definitions (PageSchema/Page/actions) live in page.ts; this file
10
+ // models the flow graph between pages.
11
+
12
+ export interface PageEdge extends SchemaBase {
13
+ /** Trigger action; undefined = default path (success/normal). */
14
+ when?: ActionSchema;
15
+ start: Page;
16
+ end: Page;
17
+ }
18
+
19
+ export interface PageFlow extends SchemaBase {
20
+ /** Entry page. */
21
+ start: Page;
22
+ /** All pages explicitly declared by the caller. */
23
+ pages: Page[];
24
+ edges: PageEdge[];
25
+ }
26
+
27
+ export function pageEdge(start: Page, end: Page, when?: ActionSchema, description?: string): PageEdge {
28
+ // Auto name for uniformity with SchemaBase; `when` stays the branch marker.
29
+ return { name: `${start.name}->${end.name}`, start, end, when, description };
30
+ }
31
+
32
+ export function definePageFlow(
33
+ name: string,
34
+ schema: {
35
+ start: Page;
36
+ pages: Page[];
37
+ edges: PageEdge[];
38
+ description?: string;
39
+ },
40
+ ): PageFlow {
41
+ const { start, pages, edges: explicitEdges, description } = schema;
42
+
43
+ // Resolve the back action from any page's actions list (PageSchema at runtime)
44
+ const backAction = findBackAction(pages);
45
+
46
+ // Auto-generate back edges: for every explicit edge A→B (not itself a back),
47
+ // if B has no explicit back edge and no existing edge B→A, add B→A with back.
48
+ const hasExplicitBack = new Set<Page>();
49
+ for (const e of explicitEdges) {
50
+ if (e.when && e.when.name === 'back') hasExplicitBack.add(e.start);
51
+ }
52
+ const hasReverse = new Set<string>();
53
+ for (const e of explicitEdges) {
54
+ hasReverse.add(`${e.start.name}->${e.end.name}`);
55
+ }
56
+ const generatedBackEdges: PageEdge[] = [];
57
+ if (backAction) {
58
+ for (const e of explicitEdges) {
59
+ if (e.when && e.when.name === 'back') continue;
60
+ if (hasExplicitBack.has(e.end)) continue;
61
+ if (hasReverse.has(`${e.end.name}->${e.start.name}`)) continue;
62
+ generatedBackEdges.push({
63
+ name: `${e.end.name}->${e.start.name}`,
64
+ start: e.end,
65
+ end: e.start,
66
+ when: backAction,
67
+ description: 'auto-generated back edge',
68
+ });
69
+ hasExplicitBack.add(e.end);
70
+ }
71
+ }
72
+ const edges = [...explicitEdges, ...generatedBackEdges];
73
+
74
+ // Validate: every registered page must be in the flow's pages
75
+ const registered = getRegisteredPages();
76
+ const orphaned: string[] = [];
77
+ for (const rp of registered) {
78
+ // Compare by object identity — caller passes the same reference
79
+ if (!pages.includes(rp as unknown as Page)) {
80
+ orphaned.push(rp.name);
81
+ }
82
+ }
83
+ if (orphaned.length > 0) {
84
+ throw new Error(
85
+ `[definePageFlow "${name}"] orphaned pages (defined but not in flow): ${orphaned.join(', ')}`,
86
+ );
87
+ }
88
+ clearRegisteredPages();
89
+
90
+ // Validate: start must be in pages
91
+ if (!pages.includes(start)) {
92
+ throw new Error(
93
+ `[definePageFlow "${name}"] start page "${start.name}" not in pages`,
94
+ );
95
+ }
96
+
97
+ // Validate: every page appears in at least one edge
98
+ const inEdge = new Set<Page>();
99
+ for (const e of edges) {
100
+ inEdge.add(e.start);
101
+ inEdge.add(e.end);
102
+ }
103
+ const isolated: string[] = [];
104
+ for (const p of pages) {
105
+ if (!inEdge.has(p)) isolated.push(p.name);
106
+ }
107
+ if (isolated.length > 0) {
108
+ throw new Error(
109
+ `[definePageFlow "${name}"] isolated pages (no edges): ${isolated.join(', ')}`,
110
+ );
111
+ }
112
+
113
+ // Validate: all pages reachable from start
114
+ const adj = new Map<Page, Page[]>();
115
+ for (const p of pages) adj.set(p, []);
116
+ for (const e of edges) {
117
+ const neighbors = adj.get(e.start);
118
+ if (neighbors) neighbors.push(e.end);
119
+ }
120
+ const visited = new Set<Page>();
121
+ const stack: Page[] = [start];
122
+ while (stack.length > 0) {
123
+ const cur = stack.pop()!;
124
+ if (visited.has(cur)) continue;
125
+ visited.add(cur);
126
+ for (const next of adj.get(cur) ?? []) {
127
+ if (!visited.has(next)) stack.push(next);
128
+ }
129
+ }
130
+ const unreachable: string[] = [];
131
+ for (const p of pages) {
132
+ if (!visited.has(p)) unreachable.push(p.name);
133
+ }
134
+ if (unreachable.length > 0) {
135
+ throw new Error(
136
+ `[definePageFlow "${name}"] unreachable pages: ${unreachable.join(', ')}`,
137
+ );
138
+ }
139
+
140
+ return { name, description, start, pages, edges };
141
+ }
142
+
143
+ /** Walk all pages' pageDef.actions (PageSchema runtime shape) to find a NavigationAction with method 'back'. */
144
+ function findBackAction(pages: Page[]): NavigationAction | undefined {
145
+ for (const p of pages) {
146
+ const pageDef = (p as unknown as Record<string, unknown>).pageDef as PageDef | undefined;
147
+ if (!pageDef?.actions) continue;
148
+ for (const a of pageDef.actions) {
149
+ const na = a as NavigationAction;
150
+ if (na.type === 'navigation' && na.method === 'back') return na;
151
+ }
152
+ }
153
+ return undefined;
154
154
  }
package/src/page.ts CHANGED
@@ -1,77 +1,77 @@
1
- import { SchemaBase } from './dsl.js';
2
- import { FrontAppSchema } from './project.js';
3
- import type { PageDef } from './page-def.js';
4
- import type { RouteDataSchema } from './route.js';
5
-
6
- // Page definitions: standalone page schemas and the page node type used by
7
- // page-driven flows. Kept separate from page-flow.ts (the flow graph itself).
8
-
9
- // Module-level registry: every definePage/defineTabPage call registers its
10
- // result so definePageFlow can check that no page is left out.
11
- const _pageRegistry = new Set<PageSchema>();
12
-
13
- export function getRegisteredPages(): ReadonlySet<PageSchema> {
14
- return _pageRegistry;
15
- }
16
-
17
- export function clearRegisteredPages(): void {
18
- _pageRegistry.clear();
19
- }
20
-
21
- /** Standalone page definition. A page is a shared value object: it belongs to
22
- * exactly one frontend app. Actions live in the page skeleton (PageDef.actions),
23
- * not on the page itself — the page only carries what the flow/topology needs. */
24
- export interface PageSchema extends SchemaBase {
25
- /** Short display name for topology diagrams. */
26
- label: string;
27
- /** The frontend app this page belongs to (shared instance from project.config). */
28
- app: FrontAppSchema;
29
- /** Full page skeleton definition (optional). */
30
- pageDef?: PageDef;
31
- /** Route params this page expects (e.g. detail page expects productId). */
32
- params?: RouteDataSchema;
33
- }
34
-
35
- export function definePage(schema: {
36
- name: string;
37
- label: string;
38
- description?: string;
39
- app: FrontAppSchema;
40
- pageDef?: PageDef;
41
- params?: RouteDataSchema;
42
- }): PageSchema {
43
- const p: PageSchema = { ...schema };
44
- _pageRegistry.add(p);
45
- return p;
46
- }
47
-
48
- /** Page with bottom tab navigation. tabs collects the child pages reachable via tab switch. */
49
- export interface TabPageSchema extends PageSchema {
50
- tabs: PageSchema[];
51
- }
52
-
53
- export function defineTabPage(schema: {
54
- name: string;
55
- label: string;
56
- description?: string;
57
- app: FrontAppSchema;
58
- tabs: PageSchema[];
59
- pageDef?: PageDef;
60
- params?: RouteDataSchema;
61
- }): TabPageSchema {
62
- const p: TabPageSchema = { ...schema };
63
- _pageRegistry.add(p);
64
- return p;
65
- }
66
-
67
- /** A page node in a page-driven flow: every node is a page, and a page belongs to an app. */
68
- export interface Page extends SchemaBase {
69
- /** Short display name for topology diagrams. */
70
- label?: string;
71
- /** The frontend app this page belongs to (shared instance from project.config). */
72
- app: FrontAppSchema;
73
- }
74
-
75
- export function page(app: FrontAppSchema, name: string, description?: string, label?: string): Page {
76
- return { name, label, app, description };
1
+ import { SchemaBase } from './dsl.js';
2
+ import { FrontAppSchema } from './project.js';
3
+ import type { PageDef } from './page-def.js';
4
+ import type { RouteDataSchema } from './route.js';
5
+
6
+ // Page definitions: standalone page schemas and the page node type used by
7
+ // page-driven flows. Kept separate from page-flow.ts (the flow graph itself).
8
+
9
+ // Module-level registry: every definePage/defineTabPage call registers its
10
+ // result so definePageFlow can check that no page is left out.
11
+ const _pageRegistry = new Set<PageSchema>();
12
+
13
+ export function getRegisteredPages(): ReadonlySet<PageSchema> {
14
+ return _pageRegistry;
15
+ }
16
+
17
+ export function clearRegisteredPages(): void {
18
+ _pageRegistry.clear();
19
+ }
20
+
21
+ /** Standalone page definition. A page is a shared value object: it belongs to
22
+ * exactly one frontend app. Actions live in the page skeleton (PageDef.actions),
23
+ * not on the page itself — the page only carries what the flow/topology needs. */
24
+ export interface PageSchema extends SchemaBase {
25
+ /** Short display name for topology diagrams. */
26
+ label: string;
27
+ /** The frontend app this page belongs to (shared instance from project.config). */
28
+ app: FrontAppSchema;
29
+ /** Full page skeleton definition (optional). */
30
+ pageDef?: PageDef;
31
+ /** Route params this page expects (e.g. detail page expects productId). */
32
+ params?: RouteDataSchema;
33
+ }
34
+
35
+ export function definePage(schema: {
36
+ name: string;
37
+ label: string;
38
+ description?: string;
39
+ app: FrontAppSchema;
40
+ pageDef?: PageDef;
41
+ params?: RouteDataSchema;
42
+ }): PageSchema {
43
+ const p: PageSchema = { ...schema };
44
+ _pageRegistry.add(p);
45
+ return p;
46
+ }
47
+
48
+ /** Page with bottom tab navigation. tabs collects the child pages reachable via tab switch. */
49
+ export interface TabPageSchema extends PageSchema {
50
+ tabs: PageSchema[];
51
+ }
52
+
53
+ export function defineTabPage(schema: {
54
+ name: string;
55
+ label: string;
56
+ description?: string;
57
+ app: FrontAppSchema;
58
+ tabs: PageSchema[];
59
+ pageDef?: PageDef;
60
+ params?: RouteDataSchema;
61
+ }): TabPageSchema {
62
+ const p: TabPageSchema = { ...schema };
63
+ _pageRegistry.add(p);
64
+ return p;
65
+ }
66
+
67
+ /** A page node in a page-driven flow: every node is a page, and a page belongs to an app. */
68
+ export interface Page extends SchemaBase {
69
+ /** Short display name for topology diagrams. */
70
+ label?: string;
71
+ /** The frontend app this page belongs to (shared instance from project.config). */
72
+ app: FrontAppSchema;
73
+ }
74
+
75
+ export function page(app: FrontAppSchema, name: string, description?: string, label?: string): Page {
76
+ return { name, label, app, description };
77
77
  }