import { SchemaBase } from './dsl.js'; import type { ActionSchema } from './action.js'; // Journey definitions: a cross-app business journey — one data's story along // the time axis. A journey is a plain running list (流水账) of actions: // page → page → controller → third → db → task, in business order. // // Design principles: // - The protagonist (the data the journey is about) is IMPLICIT — derived // from the actions, never declared. Like a TV protagonist: no label on the // forehead. // - Blueprint first, anchored later: actions start as names (text), then // upgrade to real references (page/controller/third/db/task instances). // The name-vs-reference ratio is the refinement level. // - Every action is `type + properties + data` (ActionSchema). No from/to // state transitions, no branches — a journey is a single line. /** One business journey: a goal-directed line of actions. */ export interface JourneySchema extends SchemaBase { /** Chinese title of the journey. */ title: string; /** The business goal the journey achieves (what it is for). */ goal: string; /** The running list of actions, in business order. */ actions: ActionSchema[]; } /** * Defines a business journey. `name` is kebab-case (e.g. 'merchant-onboarding'); * the export symbol is the kebab-camel of the name (merchantOnboarding). * File name is the name plus '.journey.ts' (journey_schema/merchant-onboarding.journey.ts). */ export function defineJourney(options: { name: string; title: string; goal: string; actions: ActionSchema[]; description?: string; }): JourneySchema { if (!/^[a-z][a-z0-9-]*$/.test(options.name)) { throw new Error( `journey ${options.name}: name must be kebab-case (lowercase letters/digits/dashes)`, ); } if (!options.title) { throw new Error(`journey ${options.name}: title is required`); } if (!options.goal) { throw new Error(`journey ${options.name}: goal is required`); } if (!options.actions || options.actions.length === 0) { throw new Error(`journey ${options.name}: actions must be non-empty (a journey is a line of actions)`); } return { name: options.name, title: options.title, goal: options.goal, description: options.description, actions: options.actions, }; }