@pylonts/dsl 1.1.4 → 1.1.5

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.
@@ -1,85 +1,85 @@
1
- import { FlowEdge, FlowNode, FlowSchema } from './flow.js';
2
- import { Page } from './page.js';
3
- import { PageEdge, PageFlow } from './page-flow.js';
4
-
5
- // Mermaid driver: converts a FlowSchema into a Mermaid flowchart (TD).
6
- // Node ids are hierarchical (n0, n0_0, n0_0_0, ...) so nested sub-flows stay
7
- // globally unique. A node with a sub-flow renders as a subgraph block whose
8
- // internals are rendered recursively. ok edges render as -->, conditional
9
- // edges render as -->|"WHEN"|.
10
-
11
- function escapeLabel(s: string): string {
12
- return s.replace(/"/g, '\\"').replace(/\n/g, '<br/>');
13
- }
14
-
15
- export function renderFlowMermaid(schema: FlowSchema): string {
16
- const lines: string[] = ['flowchart TD'];
17
- const ids = new Map<FlowNode, string>();
18
- renderFlow(schema, 'n', lines, ids);
19
- lines.push('');
20
- lines.push(' classDef start fill:#e6f4ea,stroke:#333,stroke-width:1px;');
21
- lines.push(` class ${ids.get(schema.start)} start;`);
22
- return lines.join('\n');
23
- }
24
-
25
- function renderFlow(schema: FlowSchema, prefix: string, lines: string[], ids: Map<FlowNode, string>): void {
26
- schema.nodes.forEach((n, i) => ids.set(n, `${prefix}${i}`));
27
- for (const n of schema.nodes) {
28
- const id = ids.get(n)!;
29
- if (n.flow) {
30
- lines.push(` subgraph ${id}["${escapeLabel(n.name)}"]`);
31
- renderFlow(n.flow, `${id}_`, lines, ids);
32
- lines.push(' end');
33
- } else {
34
- lines.push(` ${id}["${escapeLabel(n.name)}"]`);
35
- }
36
- }
37
- for (const e of schema.edges) {
38
- lines.push(renderEdge(e, ids));
39
- }
40
- }
41
-
42
- function renderEdge(e: FlowEdge, ids: Map<FlowNode, string>): string {
43
- const label = e.when ? `|"${escapeLabel(e.when)}"|` : '';
44
- return ` ${ids.get(e.start)} -->${label} ${ids.get(e.end)}`;
45
- }
46
-
47
- // Page-driven flow renderer: groups pages by their app into swimlane
48
- // subgraphs, then renders edges across the whole flow.
49
-
50
- export function renderPageFlowMermaid(schema: PageFlow): string {
51
- const lines: string[] = ['flowchart TD'];
52
- const ids = new Map<Page, string>();
53
-
54
- const byApp = new Map<string, Page[]>();
55
- for (const p of schema.pages) {
56
- const list = byApp.get(p.app.name);
57
- if (!list) {
58
- byApp.set(p.app.name, [p]);
59
- } else {
60
- list.push(p);
61
- }
62
- }
63
-
64
- let appIdx = 0;
65
- for (const [appName, pages] of byApp) {
66
- lines.push(` subgraph app${appIdx}["${escapeLabel(appName)}"]`);
67
- pages.forEach((p, i) => ids.set(p, `app${appIdx}_p${i}`));
68
- for (const p of pages) {
69
- const display = p.label ?? p.name;
70
- lines.push(` ${ids.get(p)}["${escapeLabel(display)}"]`);
71
- }
72
- appIdx++;
73
- lines.push(' end');
74
- }
75
-
76
- for (const e of schema.edges) {
77
- const label = e.when ? `|"${escapeLabel(e.when.name)}"|` : '';
78
- lines.push(` ${ids.get(e.start)} -->${label} ${ids.get(e.end)}`);
79
- }
80
-
81
- lines.push('');
82
- lines.push(' classDef start fill:#e6f4ea,stroke:#333,stroke-width:1px;');
83
- lines.push(` class ${ids.get(schema.start)} start;`);
84
- return lines.join('\n');
1
+ import { FlowEdge, FlowNode, FlowSchema } from './flow.js';
2
+ import { Page } from './page.js';
3
+ import { PageEdge, PageFlow } from './page-flow.js';
4
+
5
+ // Mermaid driver: converts a FlowSchema into a Mermaid flowchart (TD).
6
+ // Node ids are hierarchical (n0, n0_0, n0_0_0, ...) so nested sub-flows stay
7
+ // globally unique. A node with a sub-flow renders as a subgraph block whose
8
+ // internals are rendered recursively. ok edges render as -->, conditional
9
+ // edges render as -->|"WHEN"|.
10
+
11
+ function escapeLabel(s: string): string {
12
+ return s.replace(/"/g, '\\"').replace(/\n/g, '<br/>');
13
+ }
14
+
15
+ export function renderFlowMermaid(schema: FlowSchema): string {
16
+ const lines: string[] = ['flowchart TD'];
17
+ const ids = new Map<FlowNode, string>();
18
+ renderFlow(schema, 'n', lines, ids);
19
+ lines.push('');
20
+ lines.push(' classDef start fill:#e6f4ea,stroke:#333,stroke-width:1px;');
21
+ lines.push(` class ${ids.get(schema.start)} start;`);
22
+ return lines.join('\n');
23
+ }
24
+
25
+ function renderFlow(schema: FlowSchema, prefix: string, lines: string[], ids: Map<FlowNode, string>): void {
26
+ schema.nodes.forEach((n, i) => ids.set(n, `${prefix}${i}`));
27
+ for (const n of schema.nodes) {
28
+ const id = ids.get(n)!;
29
+ if (n.flow) {
30
+ lines.push(` subgraph ${id}["${escapeLabel(n.name)}"]`);
31
+ renderFlow(n.flow, `${id}_`, lines, ids);
32
+ lines.push(' end');
33
+ } else {
34
+ lines.push(` ${id}["${escapeLabel(n.name)}"]`);
35
+ }
36
+ }
37
+ for (const e of schema.edges) {
38
+ lines.push(renderEdge(e, ids));
39
+ }
40
+ }
41
+
42
+ function renderEdge(e: FlowEdge, ids: Map<FlowNode, string>): string {
43
+ const label = e.when ? `|"${escapeLabel(e.when)}"|` : '';
44
+ return ` ${ids.get(e.start)} -->${label} ${ids.get(e.end)}`;
45
+ }
46
+
47
+ // Page-driven flow renderer: groups pages by their app into swimlane
48
+ // subgraphs, then renders edges across the whole flow.
49
+
50
+ export function renderPageFlowMermaid(schema: PageFlow): string {
51
+ const lines: string[] = ['flowchart TD'];
52
+ const ids = new Map<Page, string>();
53
+
54
+ const byApp = new Map<string, Page[]>();
55
+ for (const p of schema.pages) {
56
+ const list = byApp.get(p.app.name);
57
+ if (!list) {
58
+ byApp.set(p.app.name, [p]);
59
+ } else {
60
+ list.push(p);
61
+ }
62
+ }
63
+
64
+ let appIdx = 0;
65
+ for (const [appName, pages] of byApp) {
66
+ lines.push(` subgraph app${appIdx}["${escapeLabel(appName)}"]`);
67
+ pages.forEach((p, i) => ids.set(p, `app${appIdx}_p${i}`));
68
+ for (const p of pages) {
69
+ const display = p.label ?? p.name;
70
+ lines.push(` ${ids.get(p)}["${escapeLabel(display)}"]`);
71
+ }
72
+ appIdx++;
73
+ lines.push(' end');
74
+ }
75
+
76
+ for (const e of schema.edges) {
77
+ const label = e.when ? `|"${escapeLabel(e.when.name)}"|` : '';
78
+ lines.push(` ${ids.get(e.start)} -->${label} ${ids.get(e.end)}`);
79
+ }
80
+
81
+ lines.push('');
82
+ lines.push(' classDef start fill:#e6f4ea,stroke:#333,stroke-width:1px;');
83
+ lines.push(` class ${ids.get(schema.start)} start;`);
84
+ return lines.join('\n');
85
85
  }
package/src/mock.ts CHANGED
@@ -1,12 +1,12 @@
1
- /**
2
- * Pure-data mock descriptor. Stored on DTO fields, consumed by pylonts lint mock
3
- * or MockRegistry.resolve. No function references — serializable and gen-readable.
4
- *
5
- * @example { fn: 'abc', count: 20 }
6
- * @example { fn: 'amt', min: 10, max: 500 }
7
- * @example { fn: 'phone' }
8
- */
9
- export interface MockDescriptor {
10
- fn: string;
11
- [args: string]: unknown;
12
- }
1
+ /**
2
+ * Pure-data mock descriptor. Stored on DTO fields, consumed by pylonts lint mock
3
+ * or MockRegistry.resolve. No function references — serializable and gen-readable.
4
+ *
5
+ * @example { fn: 'abc', count: 20 }
6
+ * @example { fn: 'amt', min: 10, max: 500 }
7
+ * @example { fn: 'phone' }
8
+ */
9
+ export interface MockDescriptor {
10
+ fn: string;
11
+ [args: string]: unknown;
12
+ }
@@ -1,4 +1,4 @@
1
- import { Field } from './dsl.js';
1
+ import { Field, rateScale } from './dsl.js';
2
2
  import { ForeignKey, Index, TableSchema } from './db.js';
3
3
 
4
4
  // MySQL driver: converts a TableSchema into a CREATE TABLE statement.
@@ -21,6 +21,8 @@ function columnType(field: Field): string {
21
21
  return 'BIGINT';
22
22
  case 'decimal':
23
23
  return `DECIMAL(${field.precision}, ${field.scale})`;
24
+ case 'rate':
25
+ return `DECIMAL(5, ${rateScale(field.unit)})`;
24
26
  case 'boolean':
25
27
  return 'TINYINT(1)';
26
28
  case 'date':
@@ -43,7 +45,7 @@ function renderDefault(field: Field): string {
43
45
  // NULL, etc.) — render bare, no quotes.
44
46
  if (/^[A-Z]/.test(field.default)) return ` DEFAULT ${field.default}`;
45
47
  // Numeric columns take a bare literal, not a quoted one.
46
- if (field.type === 'integer' || field.type === 'bigint' || field.type === 'decimal' || field.type === 'boolean') {
48
+ if (field.type === 'integer' || field.type === 'bigint' || field.type === 'decimal' || field.type === 'rate' || field.type === 'boolean') {
47
49
  return ` DEFAULT ${field.default}`;
48
50
  }
49
51
  return ` DEFAULT '${field.default}'`;
package/src/navigation.ts CHANGED
@@ -1,29 +1,29 @@
1
- import type { RouteDataSchema } from './route.js';
2
- import type { ActionSchema } from './action.js';
3
- import { PageSchema } from './page.js';
4
-
5
- /** Navigation primitives: front-end routing actions that are not provider calls. */
6
- export interface NavigationAction extends ActionSchema {
7
- type: 'navigation';
8
- method: 'back' | 'push' | 'popup' | 'home';
9
- /** Target page for push navigation. */
10
- target?: PageSchema;
11
- /** Route params type for push navigation. Auto-derived from target.params if omitted. */
12
- params?: RouteDataSchema;
13
- }
14
-
15
- export const navigation = {
16
- back(description?: string): NavigationAction {
17
- return { name: 'back', type: 'navigation', method: 'back', description };
18
- },
19
- push(options: { target: PageSchema; params?: RouteDataSchema; description?: string }): NavigationAction {
20
- const { target, params, description } = options;
21
- return { name: 'push', type: 'navigation', method: 'push', target, params: params ?? target.params, description };
22
- },
23
- popup(description?: string): NavigationAction {
24
- return { name: 'popup', type: 'navigation', method: 'popup', description };
25
- },
26
- home(description?: string): NavigationAction {
27
- return { name: 'home', type: 'navigation', method: 'home', description };
28
- },
1
+ import type { RouteDataSchema } from './route.js';
2
+ import type { ActionSchema } from './action.js';
3
+ import { PageSchema } from './page.js';
4
+
5
+ /** Navigation primitives: front-end routing actions that are not provider calls. */
6
+ export interface NavigationAction extends ActionSchema {
7
+ type: 'navigation';
8
+ method: 'back' | 'push' | 'popup' | 'home';
9
+ /** Target page for push navigation. */
10
+ target?: PageSchema;
11
+ /** Route params type for push navigation. Auto-derived from target.params if omitted. */
12
+ params?: RouteDataSchema;
13
+ }
14
+
15
+ export const navigation = {
16
+ back(description?: string): NavigationAction {
17
+ return { name: 'back', type: 'navigation', method: 'back', description };
18
+ },
19
+ push(options: { target: PageSchema; params?: RouteDataSchema; description?: string }): NavigationAction {
20
+ const { target, params, description } = options;
21
+ return { name: 'push', type: 'navigation', method: 'push', target, params: params ?? target.params, description };
22
+ },
23
+ popup(description?: string): NavigationAction {
24
+ return { name: 'popup', type: 'navigation', method: 'popup', description };
25
+ },
26
+ home(description?: string): NavigationAction {
27
+ return { name: 'home', type: 'navigation', method: 'home', description };
28
+ },
29
29
  };
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
  }