@pylonts/dsl 1.1.21 → 1.1.22

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/curd.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { SchemaBase, Field } from './dsl.js';
2
2
  import { TableSchema } from './db.js';
3
3
  import { FrontAppSchema } from './project.js';
4
- import { ActionSchema } from './action.js';
4
+ import { PageActionSchema } from './page-action.js';
5
5
  import type { FilterSchema } from './filter.js';
6
6
  import { toKebabCase } from '@pylonts/core';
7
7
 
@@ -51,7 +51,7 @@ export interface CurdSchema extends SchemaBase {
51
51
  /** Sidebar menu section (group) this CRUD page belongs to. */
52
52
  section: string;
53
53
  /** Extra user actions on this page (beyond the standard CRUD). */
54
- actions?: ActionSchema[];
54
+ actions?: PageActionSchema[];
55
55
  /** Add/update/detail action pages. */
56
56
  actionPages?: {
57
57
  add?: ActionPage;
package/src/index.ts CHANGED
@@ -16,6 +16,8 @@ export * from './pattern.js';
16
16
  export * from './patterns/retry.js';
17
17
  export * from './flow.js';
18
18
  export * from './action.js';
19
+ export * from './journey.js';
20
+ export * from './page-action.js';
19
21
  export * from './event.js';
20
22
  export * from './component.js';
21
23
  export * from './convert.js';
@@ -31,6 +33,7 @@ export * from './aggregate.js';
31
33
  export * from './repository.js';
32
34
  export * from './domain-event.js';
33
35
  export * from './third-service.js';
36
+ export * from './task.js';
34
37
  export * from './token.js';
35
38
  export * from './field-rule.js';
36
39
  export * from './exception.js';
package/src/journey.ts ADDED
@@ -0,0 +1,61 @@
1
+ import { SchemaBase } from './dsl.js';
2
+ import type { ActionSchema } from './action.js';
3
+
4
+ // Journey definitions: a cross-app business journey — one data's story along
5
+ // the time axis. A journey is a plain running list (流水账) of actions:
6
+ // page → page → controller → third → db → task, in business order.
7
+ //
8
+ // Design principles:
9
+ // - The protagonist (the data the journey is about) is IMPLICIT — derived
10
+ // from the actions, never declared. Like a TV protagonist: no label on the
11
+ // forehead.
12
+ // - Blueprint first, anchored later: actions start as names (text), then
13
+ // upgrade to real references (page/controller/third/db/task instances).
14
+ // The name-vs-reference ratio is the refinement level.
15
+ // - Every action is `type + properties + data` (ActionSchema). No from/to
16
+ // state transitions, no branches — a journey is a single line.
17
+
18
+ /** One business journey: a goal-directed line of actions. */
19
+ export interface JourneySchema extends SchemaBase {
20
+ /** Chinese title of the journey. */
21
+ title: string;
22
+ /** The business goal the journey achieves (what it is for). */
23
+ goal: string;
24
+ /** The running list of actions, in business order. */
25
+ actions: ActionSchema[];
26
+ }
27
+
28
+ /**
29
+ * Defines a business journey. `name` is kebab-case (e.g. 'merchant-onboarding');
30
+ * the export symbol is the kebab-camel of the name (merchantOnboarding).
31
+ * File name is the name plus '.journey.ts' (journey_schema/merchant-onboarding.journey.ts).
32
+ */
33
+ export function defineJourney(options: {
34
+ name: string;
35
+ title: string;
36
+ goal: string;
37
+ actions: ActionSchema[];
38
+ description?: string;
39
+ }): JourneySchema {
40
+ if (!/^[a-z][a-z0-9-]*$/.test(options.name)) {
41
+ throw new Error(
42
+ `journey ${options.name}: name must be kebab-case (lowercase letters/digits/dashes)`,
43
+ );
44
+ }
45
+ if (!options.title) {
46
+ throw new Error(`journey ${options.name}: title is required`);
47
+ }
48
+ if (!options.goal) {
49
+ throw new Error(`journey ${options.name}: goal is required`);
50
+ }
51
+ if (!options.actions || options.actions.length === 0) {
52
+ throw new Error(`journey ${options.name}: actions must be non-empty (a journey is a line of actions)`);
53
+ }
54
+ return {
55
+ name: options.name,
56
+ title: options.title,
57
+ goal: options.goal,
58
+ description: options.description,
59
+ actions: options.actions,
60
+ };
61
+ }
package/src/navigation.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import type { RouteDataSchema } from './route.js';
2
- import type { ActionSchema } from './action.js';
2
+ import type { PageActionSchema } from './page-action.js';
3
3
  import { PageSchema } from './page.js';
4
4
 
5
5
  /** Navigation primitives: front-end routing actions that are not provider calls. */
6
- export interface NavigationAction extends ActionSchema {
6
+ export interface NavigationAction extends PageActionSchema {
7
7
  type: 'navigation';
8
8
  method: 'back' | 'push' | 'popup' | 'home';
9
9
  /** Target page for push navigation. */
@@ -0,0 +1,59 @@
1
+ import { SchemaBase } from './dsl.js';
2
+ import type { DtoField, DtoMessage, DtoArrayField, DtoObjectField } from './dto.js';
3
+ import type { RefSchema } from './ref.js';
4
+ import type { ControllerMethodSchema } from './controller.js';
5
+
6
+ // Page actions: what a user can do ON a page (e.g. submit, approve, reject).
7
+ // Distinguished from the cross-domain Action (journey): page actions live in
8
+ // the page skeleton (PageDef.actions / PageSchema.pageDef) and describe the
9
+ // page's own operation surface; journey actions describe what happens along a
10
+ // business line (page / controller / third / db / task). Subclasses use
11
+ // `type` as the discriminator.
12
+
13
+ /** An action a user can perform on a page (e.g. submit, approve, reject).
14
+ * Subclasses use `type` as the discriminator. */
15
+ export interface PageActionSchema extends SchemaBase {
16
+ type: string;
17
+ }
18
+
19
+ export function definePageAction(name: string, description?: string): PageActionSchema {
20
+ return { name, description, type: 'gesture' };
21
+ }
22
+
23
+ /** Parameter data source for a call argument. */
24
+ export type DataRef =
25
+ | { type: 'route'; key: string }
26
+ | { type: 'data'; key: string }
27
+ | { type: 'value'; value: unknown };
28
+
29
+ /** Create a route-parameter reference. */
30
+ export function route(key: string): DataRef {
31
+ return { type: 'route', key };
32
+ }
33
+
34
+ /** Create a page-data reference. */
35
+ export function data(key: string): DataRef {
36
+ return { type: 'data', key };
37
+ }
38
+
39
+ export interface CallAction extends PageActionSchema {
40
+ type: 'call';
41
+ func: ControllerMethodSchema;
42
+ args?: Record<string, DtoField | DtoMessage | DtoArrayField | DtoObjectField | RefSchema | string>;
43
+ }
44
+
45
+ export function call(func: ControllerMethodSchema, args?: Record<string, DtoField | DtoMessage | DtoArrayField | DtoObjectField | RefSchema | string>): CallAction {
46
+ return { name: func.name, type: 'call', func, args };
47
+ }
48
+
49
+ /** Assign a call's result to a page data field.
50
+ * React: setState({ [field]: await ... }). Mini-program: this.setData({ [field]: ... }). */
51
+ export interface SetDataAction extends PageActionSchema {
52
+ type: 'setData';
53
+ call: CallAction;
54
+ field: DtoField;
55
+ }
56
+
57
+ export function setData(call: CallAction, field: DtoField): SetDataAction {
58
+ return { name: 'setData', type: 'setData', call, field };
59
+ }
package/src/page-def.ts CHANGED
@@ -2,7 +2,7 @@ import type { ImportableSchemaBase, CollectionSchemaBase, SchemaBase } from './d
2
2
  import { DtoField } from './dto.js';
3
3
  import type { DtoMessage, DtoArrayField, DtoObjectField } from './dto.js';
4
4
  import type { ComponentSchema } from './component.js';
5
- import type { ActionSchema } from './action.js';
5
+ import type { PageActionSchema } from './page-action.js';
6
6
 
7
7
  export type { RouteDataSchema } from './route.js';
8
8
  export { defineRouteData } from './route.js';
@@ -14,24 +14,24 @@ export { defineRouteData } from './route.js';
14
14
  /** Page lifecycle events that trigger data loading. */
15
15
  export interface EventSchema extends SchemaBase {
16
16
  type: 'onLoad' | 'onShow' | 'onHide' | 'onPullDownRefresh' | 'onReachBottom';
17
- /** Actions that fire when this event occurs. */
18
- actions?: ActionSchema[];
17
+ /** Page actions that fire when this event occurs. */
18
+ actions?: PageActionSchema[];
19
19
  }
20
20
 
21
21
  export const events = {
22
- onLoad(actions?: ActionSchema[], description?: string): EventSchema {
22
+ onLoad(actions?: PageActionSchema[], description?: string): EventSchema {
23
23
  return { name: 'onLoad', type: 'onLoad', actions, description };
24
24
  },
25
- onShow(actions?: ActionSchema[], description?: string): EventSchema {
25
+ onShow(actions?: PageActionSchema[], description?: string): EventSchema {
26
26
  return { name: 'onShow', type: 'onShow', actions, description };
27
27
  },
28
- onHide(actions?: ActionSchema[], description?: string): EventSchema {
28
+ onHide(actions?: PageActionSchema[], description?: string): EventSchema {
29
29
  return { name: 'onHide', type: 'onHide', actions, description };
30
30
  },
31
- onPullDownRefresh(actions?: ActionSchema[], description?: string): EventSchema {
31
+ onPullDownRefresh(actions?: PageActionSchema[], description?: string): EventSchema {
32
32
  return { name: 'onPullDownRefresh', type: 'onPullDownRefresh', actions, description };
33
33
  },
34
- onReachBottom(actions?: ActionSchema[], description?: string): EventSchema {
34
+ onReachBottom(actions?: PageActionSchema[], description?: string): EventSchema {
35
35
  return { name: 'onReachBottom', type: 'onReachBottom', actions, description };
36
36
  },
37
37
  };
@@ -64,7 +64,7 @@ export interface PageDef extends ImportableSchemaBase {
64
64
  /** Lifecycle events that trigger data loading. */
65
65
  events: EventSchema[];
66
66
  /** Page actions (provider calls, navigation, etc.). */
67
- actions: ActionSchema[];
67
+ actions: PageActionSchema[];
68
68
  /** UI component declarations that make up the page skeleton. */
69
69
  components: ComponentSchema[];
70
70
  /** Whether the driver should generate page-level loading / error / empty state wrappers. */
package/src/page-flow.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { SchemaBase } from './dsl.js';
2
- import type { ActionSchema } from './action.js';
2
+ import type { PageActionSchema } from './page-action.js';
3
3
  import { Page, getRegisteredPages, clearRegisteredPages } from './page.js';
4
4
  import type { PageDef } from './page-def.js';
5
5
  import type { NavigationAction } from './navigation.js';
@@ -10,8 +10,8 @@ import type { NavigationAction } from './navigation.js';
10
10
  // models the flow graph between pages.
11
11
 
12
12
  export interface PageEdge extends SchemaBase {
13
- /** Trigger action; undefined = default path (success/normal). */
14
- when?: ActionSchema;
13
+ /** Trigger page action; undefined = default path (success/normal). */
14
+ when?: PageActionSchema;
15
15
  start: Page;
16
16
  end: Page;
17
17
  }
@@ -24,7 +24,7 @@ export interface PageFlow extends SchemaBase {
24
24
  edges: PageEdge[];
25
25
  }
26
26
 
27
- export function pageEdge(start: Page, end: Page, when?: ActionSchema, description?: string): PageEdge {
27
+ export function pageEdge(start: Page, end: Page, when?: PageActionSchema, description?: string): PageEdge {
28
28
  // Auto name for uniformity with SchemaBase; `when` stays the branch marker.
29
29
  return { name: `${start.name}->${end.name}`, start, end, when, description };
30
30
  }
package/src/popup.ts CHANGED
@@ -1,15 +1,15 @@
1
- import type { ActionSchema } from './action.js';
1
+ import type { PageActionSchema } from './page-action.js';
2
2
  import type { RefSchema } from './ref.js';
3
3
 
4
4
  /** Display a toast notification. React: toast/message UI. Mini-program: wx.showToast. */
5
- export interface ToastAction extends ActionSchema {
5
+ export interface ToastAction extends PageActionSchema {
6
6
  type: 'toast';
7
7
  message: string | RefSchema;
8
8
  icon?: 'success' | 'error' | 'loading' | 'none';
9
9
  }
10
10
 
11
11
  /** Display an alert dialog. React: modal. Mini-program: wx.showModal. */
12
- export interface AlertAction extends ActionSchema {
12
+ export interface AlertAction extends PageActionSchema {
13
13
  type: 'alert';
14
14
  title: string | RefSchema;
15
15
  content: string | RefSchema;
package/src/repository.ts CHANGED
@@ -1,35 +1,35 @@
1
- import { SchemaBase } from './dsl.js';
2
- import type { DomainAggregate } from './aggregate.js';
3
-
4
- /**
5
- * Aggregate-grained storage entry. The repository binds one aggregate and
6
- * exposes the three fixed operation skeletons (load / save / delete) that the
7
- * generator expands from the aggregate structure:
8
- *
9
- * save(order) = tx { rootDao.upsert + memberDao cascade by FK / extends }
10
- * load(id) = rootDao.get + memberDao by FK / extends
11
- * delete(id) = tx { memberDao delete + rootDao delete }
12
- *
13
- * Callers face the domain concept (Order), never the tables. DAO stays
14
- * single-table; Repository is the multi-table (aggregate) unit; Service is the
15
- * use-case unit. Inter-aggregate joins are prohibited — repositories only
16
- * reference each other by root ID.
17
- */
18
- export interface RepositorySchema extends SchemaBase {
19
- type: 'repository';
20
- /** The aggregate this repository persists (shared instance). */
21
- aggregate: DomainAggregate;
22
- }
23
-
24
- export function defineRepository(options: {
25
- name: string;
26
- aggregate: DomainAggregate;
27
- description?: string;
28
- }): RepositorySchema {
29
- return {
30
- type: 'repository',
31
- name: options.name,
32
- description: options.description,
33
- aggregate: options.aggregate,
34
- };
35
- }
1
+ import { SchemaBase } from './dsl.js';
2
+ import type { DomainAggregate } from './aggregate.js';
3
+
4
+ /**
5
+ * Aggregate-grained storage entry. The repository binds one aggregate and
6
+ * exposes the three fixed operation skeletons (load / save / delete) that the
7
+ * generator expands from the aggregate structure:
8
+ *
9
+ * save(order) = tx { rootDao.upsert + memberDao cascade by FK / extends }
10
+ * load(id) = rootDao.get + memberDao by FK / extends
11
+ * delete(id) = tx { memberDao delete + rootDao delete }
12
+ *
13
+ * Callers face the domain concept (Order), never the tables. DAO stays
14
+ * single-table; Repository is the multi-table (aggregate) unit; Service is the
15
+ * use-case unit. Inter-aggregate joins are prohibited — repositories only
16
+ * reference each other by root ID.
17
+ */
18
+ export interface RepositorySchema extends SchemaBase {
19
+ type: 'repository';
20
+ /** The aggregate this repository persists (shared instance). */
21
+ aggregate: DomainAggregate;
22
+ }
23
+
24
+ export function defineRepository(options: {
25
+ name: string;
26
+ aggregate: DomainAggregate;
27
+ description?: string;
28
+ }): RepositorySchema {
29
+ return {
30
+ type: 'repository',
31
+ name: options.name,
32
+ description: options.description,
33
+ aggregate: options.aggregate,
34
+ };
35
+ }
package/src/task.ts ADDED
@@ -0,0 +1,52 @@
1
+ import { CollectionSchemaBase } from './dsl.js';
2
+
3
+ // Task definitions: scheduled (timer) operations — the second member of the
4
+ // action closure (action = controller | task | third callback).
5
+ //
6
+ // A task is a contract: name + cron + what it does. It declares NO state
7
+ // changes — state changes are expressed by journey steps. Task implementation
8
+ // (scan logic, idempotency) lives in the implementation layer (e.g. pylon-flow
9
+ // steps).
10
+ //
11
+ // Task represents ONLY timers (cron-driven). Async/event-driven operations
12
+ // belong to the event system (EventObserver / EventNotifier, pending), not
13
+ // here.
14
+
15
+ export interface TaskSchema extends CollectionSchemaBase {
16
+ type: 'task';
17
+ /** Display label (Chinese) for the task. */
18
+ label: string;
19
+ /** Cron expression — the timer schedule. */
20
+ cron: string;
21
+ }
22
+
23
+ /**
24
+ * Defines a scheduled task. `name` must end with 'Task' (PascalCase,
25
+ * e.g. 'AutoRefundTask'); the export symbol equals the name. File name is the
26
+ * name minus the Task suffix, kebab-cased, plus '.task.ts'
27
+ * (AutoRefundTask → task_schema/auto-refund.task.ts).
28
+ */
29
+ export function defineTask(options: {
30
+ name: string;
31
+ label: string;
32
+ cron: string;
33
+ description?: string;
34
+ }): TaskSchema {
35
+ if (!/(Task)$/.test(options.name)) {
36
+ throw new Error(
37
+ `task ${options.name}: name must end with 'Task' (PascalCase, e.g. 'AutoRefundTask')`,
38
+ );
39
+ }
40
+ if (!options.cron || options.cron.trim() === '') {
41
+ throw new Error(
42
+ `task ${options.name}: cron is required (a task is a timer)`,
43
+ );
44
+ }
45
+ return {
46
+ type: 'task',
47
+ name: options.name,
48
+ label: options.label,
49
+ description: options.description,
50
+ cron: options.cron,
51
+ };
52
+ }