@scenetest/scenes 0.1.0
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/LICENSE +21 -0
- package/dist/__tests__/devices.test.d.ts +2 -0
- package/dist/__tests__/devices.test.d.ts.map +1 -0
- package/dist/__tests__/devices.test.js +117 -0
- package/dist/__tests__/devices.test.js.map +1 -0
- package/dist/__tests__/dsl.test.d.ts +2 -0
- package/dist/__tests__/dsl.test.d.ts.map +1 -0
- package/dist/__tests__/dsl.test.js +385 -0
- package/dist/__tests__/dsl.test.js.map +1 -0
- package/dist/__tests__/markdown-scene.test.d.ts +2 -0
- package/dist/__tests__/markdown-scene.test.d.ts.map +1 -0
- package/dist/__tests__/markdown-scene.test.js +508 -0
- package/dist/__tests__/markdown-scene.test.js.map +1 -0
- package/dist/__tests__/reactive.test.d.ts +2 -0
- package/dist/__tests__/reactive.test.d.ts.map +1 -0
- package/dist/__tests__/reactive.test.js +383 -0
- package/dist/__tests__/reactive.test.js.map +1 -0
- package/dist/__tests__/swarm.test.d.ts +2 -0
- package/dist/__tests__/swarm.test.d.ts.map +1 -0
- package/dist/__tests__/swarm.test.js +214 -0
- package/dist/__tests__/swarm.test.js.map +1 -0
- package/dist/actor.d.ts +104 -0
- package/dist/actor.d.ts.map +1 -0
- package/dist/actor.js +527 -0
- package/dist/actor.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +273 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +21 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +120 -0
- package/dist/config.js.map +1 -0
- package/dist/devices.d.ts +55 -0
- package/dist/devices.d.ts.map +1 -0
- package/dist/devices.js +167 -0
- package/dist/devices.js.map +1 -0
- package/dist/dsl.d.ts +99 -0
- package/dist/dsl.d.ts.map +1 -0
- package/dist/dsl.js +247 -0
- package/dist/dsl.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/init.d.ts +9 -0
- package/dist/init.d.ts.map +1 -0
- package/dist/init.js +27 -0
- package/dist/init.js.map +1 -0
- package/dist/loader.d.ts +2 -0
- package/dist/loader.d.ts.map +1 -0
- package/dist/loader.js +10 -0
- package/dist/loader.js.map +1 -0
- package/dist/markdown-scene.d.ts +120 -0
- package/dist/markdown-scene.d.ts.map +1 -0
- package/dist/markdown-scene.js +452 -0
- package/dist/markdown-scene.js.map +1 -0
- package/dist/message-bus.d.ts +31 -0
- package/dist/message-bus.d.ts.map +1 -0
- package/dist/message-bus.js +74 -0
- package/dist/message-bus.js.map +1 -0
- package/dist/reactive.d.ts +267 -0
- package/dist/reactive.d.ts.map +1 -0
- package/dist/reactive.js +779 -0
- package/dist/reactive.js.map +1 -0
- package/dist/runner.d.ts +51 -0
- package/dist/runner.d.ts.map +1 -0
- package/dist/runner.js +306 -0
- package/dist/runner.js.map +1 -0
- package/dist/scene.d.ts +40 -0
- package/dist/scene.d.ts.map +1 -0
- package/dist/scene.js +110 -0
- package/dist/scene.js.map +1 -0
- package/dist/selectors.d.ts +57 -0
- package/dist/selectors.d.ts.map +1 -0
- package/dist/selectors.js +193 -0
- package/dist/selectors.js.map +1 -0
- package/dist/swarm.d.ts +64 -0
- package/dist/swarm.d.ts.map +1 -0
- package/dist/swarm.js +306 -0
- package/dist/swarm.js.map +1 -0
- package/dist/team-manager.d.ts +120 -0
- package/dist/team-manager.d.ts.map +1 -0
- package/dist/team-manager.js +267 -0
- package/dist/team-manager.js.map +1 -0
- package/dist/types.d.ts +653 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +61 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Markdown scene parser and loader.
|
|
3
|
+
*
|
|
4
|
+
* Parses `.spec.md` files into reactive flow registrations. The format is
|
|
5
|
+
* natural markdown — human-readable, GitHub-renderable, and executable:
|
|
6
|
+
*
|
|
7
|
+
* ```markdown
|
|
8
|
+
* # User friend requests
|
|
9
|
+
* ## new user signs up and gets a friend request
|
|
10
|
+
* new-user:
|
|
11
|
+
* - openTo /
|
|
12
|
+
* - see welcome-box
|
|
13
|
+
* - click continue-button
|
|
14
|
+
*
|
|
15
|
+
* primary-user:
|
|
16
|
+
* - openTo /friends
|
|
17
|
+
* - click main-navbar search
|
|
18
|
+
* - typeInto search-input [new-user.username]
|
|
19
|
+
* - see search-results-section
|
|
20
|
+
* - click friend-request-button
|
|
21
|
+
*
|
|
22
|
+
* new-user:
|
|
23
|
+
* - seeToast friend-request
|
|
24
|
+
* - see navbar notifications-badge
|
|
25
|
+
* - click
|
|
26
|
+
* - see notifications-menu-expanded new-friend-request
|
|
27
|
+
* - click
|
|
28
|
+
*
|
|
29
|
+
* ## old user re-activates account
|
|
30
|
+
* returning-user:
|
|
31
|
+
* - openTo /login
|
|
32
|
+
* - see login-form
|
|
33
|
+
* - typeInto email [self.email]
|
|
34
|
+
* - click submit
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* ## Format rules
|
|
38
|
+
*
|
|
39
|
+
* - `#` headings are **group names** (optional hierarchy/context)
|
|
40
|
+
* - `##` headings are **scene names** (each becomes a `flow()` registration)
|
|
41
|
+
* - If no `##` headings exist, `#` headings are promoted to scene names
|
|
42
|
+
* - `role-name:` switches the active actor for subsequent lines (like a screenplay cue)
|
|
43
|
+
* - `role-name: action args` is inline shorthand for a one-line actor block
|
|
44
|
+
* - Action lines map to the standard text DSL (see `dsl.ts`)
|
|
45
|
+
* - Lines may start with `- ` or `1. ` (markdown lists) for readability (stripped)
|
|
46
|
+
* - `// comment` lines become `console.log` during execution
|
|
47
|
+
* - Blank lines are ignored
|
|
48
|
+
* - `[namespace.field]` interpolation:
|
|
49
|
+
* - `[self.field]` — current actor's own fields
|
|
50
|
+
* - `[role-name.field]` — another actor's fields
|
|
51
|
+
* - `[team.field]` — team metadata
|
|
52
|
+
* - `[alias.field]` — aliased role (from macro args)
|
|
53
|
+
* - `if <selector>` followed by indented lines creates a conditional monitor
|
|
54
|
+
* - `macro-name` or `macro-name alias=role` invokes a registered macro
|
|
55
|
+
* - `waitFor <message>` blocks the actor until a bus message arrives
|
|
56
|
+
* - Bare `click` (no selector) clicks the current scope
|
|
57
|
+
*/
|
|
58
|
+
export interface MarkdownScene {
|
|
59
|
+
name: string;
|
|
60
|
+
group?: string;
|
|
61
|
+
blocks: ActorBlock[];
|
|
62
|
+
}
|
|
63
|
+
export interface ActorBlock {
|
|
64
|
+
role: string;
|
|
65
|
+
alias?: string;
|
|
66
|
+
actions: SceneAction[];
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Macro argument types.
|
|
70
|
+
*
|
|
71
|
+
* - `mapping`: alias=role mapping, e.g., `target=new-user` makes `[target.field]` resolve to new-user's fields
|
|
72
|
+
* - `literal`: plain string value for backward compatibility
|
|
73
|
+
*/
|
|
74
|
+
export type MacroArg = {
|
|
75
|
+
type: 'mapping';
|
|
76
|
+
alias: string;
|
|
77
|
+
role: string;
|
|
78
|
+
} | {
|
|
79
|
+
type: 'literal';
|
|
80
|
+
value: string;
|
|
81
|
+
};
|
|
82
|
+
export type SceneAction = {
|
|
83
|
+
type: 'action';
|
|
84
|
+
line: string;
|
|
85
|
+
} | {
|
|
86
|
+
type: 'comment';
|
|
87
|
+
text: string;
|
|
88
|
+
} | {
|
|
89
|
+
type: 'if';
|
|
90
|
+
selector: string;
|
|
91
|
+
actions: string[];
|
|
92
|
+
} | {
|
|
93
|
+
type: 'macro';
|
|
94
|
+
name: string;
|
|
95
|
+
args: MacroArg[];
|
|
96
|
+
};
|
|
97
|
+
/**
|
|
98
|
+
* Parse a `.spec.md` file into scene definitions.
|
|
99
|
+
*
|
|
100
|
+
* Handles both formats:
|
|
101
|
+
* - `#` groups + `##` scenes (recommended for multi-scene files)
|
|
102
|
+
* - `#` scenes only (for single-scene files or flat lists)
|
|
103
|
+
*/
|
|
104
|
+
export declare function parseMarkdownScenes(content: string, filePath: string): MarkdownScene[];
|
|
105
|
+
/**
|
|
106
|
+
* Register parsed markdown scenes as reactive flows.
|
|
107
|
+
*
|
|
108
|
+
* Each scene becomes a `flow()` call. Actors are created upfront so
|
|
109
|
+
* `[actor.field]` interpolation can reference any actor regardless of
|
|
110
|
+
* declaration order.
|
|
111
|
+
*/
|
|
112
|
+
export declare function registerMarkdownScenes(scenes: MarkdownScene[], filePath: string): void;
|
|
113
|
+
/**
|
|
114
|
+
* Load a `.spec.md` file: parse it and register all scenes as flows.
|
|
115
|
+
*
|
|
116
|
+
* Called by the runner when it discovers `.spec.md` files alongside
|
|
117
|
+
* `.spec.ts` files.
|
|
118
|
+
*/
|
|
119
|
+
export declare function loadMarkdownScene(filePath: string): Promise<void>;
|
|
120
|
+
//# sourceMappingURL=markdown-scene.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markdown-scene.d.ts","sourceRoot":"","sources":["../src/markdown-scene.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwDG;AAYH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,UAAU,EAAE,CAAA;CACrB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,WAAW,EAAE,CAAA;CACvB;AAED;;;;;GAKG;AACH,MAAM,MAAM,QAAQ,GAChB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAChD;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAA;AAEtC,MAAM,MAAM,WAAW,GACnB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAChC;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACjC;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,GACnD;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,QAAQ,EAAE,CAAA;CAAE,CAAA;AAMrD;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,GACf,aAAa,EAAE,CAgJjB;AAmLD;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,aAAa,EAAE,EACvB,QAAQ,EAAE,MAAM,GACf,IAAI,CA0GN;AAMD;;;;;GAKG;AACH,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAOvE"}
|
|
@@ -0,0 +1,452 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Markdown scene parser and loader.
|
|
3
|
+
*
|
|
4
|
+
* Parses `.spec.md` files into reactive flow registrations. The format is
|
|
5
|
+
* natural markdown — human-readable, GitHub-renderable, and executable:
|
|
6
|
+
*
|
|
7
|
+
* ```markdown
|
|
8
|
+
* # User friend requests
|
|
9
|
+
* ## new user signs up and gets a friend request
|
|
10
|
+
* new-user:
|
|
11
|
+
* - openTo /
|
|
12
|
+
* - see welcome-box
|
|
13
|
+
* - click continue-button
|
|
14
|
+
*
|
|
15
|
+
* primary-user:
|
|
16
|
+
* - openTo /friends
|
|
17
|
+
* - click main-navbar search
|
|
18
|
+
* - typeInto search-input [new-user.username]
|
|
19
|
+
* - see search-results-section
|
|
20
|
+
* - click friend-request-button
|
|
21
|
+
*
|
|
22
|
+
* new-user:
|
|
23
|
+
* - seeToast friend-request
|
|
24
|
+
* - see navbar notifications-badge
|
|
25
|
+
* - click
|
|
26
|
+
* - see notifications-menu-expanded new-friend-request
|
|
27
|
+
* - click
|
|
28
|
+
*
|
|
29
|
+
* ## old user re-activates account
|
|
30
|
+
* returning-user:
|
|
31
|
+
* - openTo /login
|
|
32
|
+
* - see login-form
|
|
33
|
+
* - typeInto email [self.email]
|
|
34
|
+
* - click submit
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* ## Format rules
|
|
38
|
+
*
|
|
39
|
+
* - `#` headings are **group names** (optional hierarchy/context)
|
|
40
|
+
* - `##` headings are **scene names** (each becomes a `flow()` registration)
|
|
41
|
+
* - If no `##` headings exist, `#` headings are promoted to scene names
|
|
42
|
+
* - `role-name:` switches the active actor for subsequent lines (like a screenplay cue)
|
|
43
|
+
* - `role-name: action args` is inline shorthand for a one-line actor block
|
|
44
|
+
* - Action lines map to the standard text DSL (see `dsl.ts`)
|
|
45
|
+
* - Lines may start with `- ` or `1. ` (markdown lists) for readability (stripped)
|
|
46
|
+
* - `// comment` lines become `console.log` during execution
|
|
47
|
+
* - Blank lines are ignored
|
|
48
|
+
* - `[namespace.field]` interpolation:
|
|
49
|
+
* - `[self.field]` — current actor's own fields
|
|
50
|
+
* - `[role-name.field]` — another actor's fields
|
|
51
|
+
* - `[team.field]` — team metadata
|
|
52
|
+
* - `[alias.field]` — aliased role (from macro args)
|
|
53
|
+
* - `if <selector>` followed by indented lines creates a conditional monitor
|
|
54
|
+
* - `macro-name` or `macro-name alias=role` invokes a registered macro
|
|
55
|
+
* - `waitFor <message>` blocks the actor until a bus message arrives
|
|
56
|
+
* - Bare `click` (no selector) clicks the current scope
|
|
57
|
+
*/
|
|
58
|
+
import path from 'path';
|
|
59
|
+
import { parseAction, applyDslAction, getMacro } from './dsl.js';
|
|
60
|
+
import { flow } from './reactive.js';
|
|
61
|
+
import { setCurrentFile } from './scene.js';
|
|
62
|
+
// ---------------------------------------------------------------------------
|
|
63
|
+
// Parser
|
|
64
|
+
// ---------------------------------------------------------------------------
|
|
65
|
+
/**
|
|
66
|
+
* Parse a `.spec.md` file into scene definitions.
|
|
67
|
+
*
|
|
68
|
+
* Handles both formats:
|
|
69
|
+
* - `#` groups + `##` scenes (recommended for multi-scene files)
|
|
70
|
+
* - `#` scenes only (for single-scene files or flat lists)
|
|
71
|
+
*/
|
|
72
|
+
export function parseMarkdownScenes(content, filePath) {
|
|
73
|
+
const lines = content.split('\n');
|
|
74
|
+
// First pass: determine heading strategy.
|
|
75
|
+
// If any ## heading exists, use # = group, ## = scene.
|
|
76
|
+
// Otherwise, # = scene (no groups).
|
|
77
|
+
const hasH2 = lines.some((l) => /^##\s/.test(l.trim()));
|
|
78
|
+
const scenes = [];
|
|
79
|
+
let currentGroup;
|
|
80
|
+
let currentScene = null;
|
|
81
|
+
let currentBlock = null;
|
|
82
|
+
for (let i = 0; i < lines.length; i++) {
|
|
83
|
+
const raw = lines[i];
|
|
84
|
+
const trimmed = raw.trim();
|
|
85
|
+
// Skip blank lines
|
|
86
|
+
if (!trimmed)
|
|
87
|
+
continue;
|
|
88
|
+
// ── Heading handling ──────────────────────────────────────────────
|
|
89
|
+
if (hasH2) {
|
|
90
|
+
// # = group, ## = scene
|
|
91
|
+
if (/^## /.test(trimmed)) {
|
|
92
|
+
const name = trimmed.slice(3).trim();
|
|
93
|
+
currentScene = { name, group: currentGroup, blocks: [] };
|
|
94
|
+
scenes.push(currentScene);
|
|
95
|
+
currentBlock = null;
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
if (/^# /.test(trimmed)) {
|
|
99
|
+
currentGroup = trimmed.slice(2).trim();
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
// No ## headings — # = scene
|
|
105
|
+
if (/^# /.test(trimmed)) {
|
|
106
|
+
const name = trimmed.slice(2).trim();
|
|
107
|
+
currentScene = { name, group: undefined, blocks: [] };
|
|
108
|
+
scenes.push(currentScene);
|
|
109
|
+
currentBlock = null;
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// ── Content lines (need an active scene) ──────────────────────────
|
|
114
|
+
// Auto-create scene if content appears before any heading
|
|
115
|
+
const earlyDecl = parseActorDeclaration(trimmed);
|
|
116
|
+
if (!currentScene && (earlyDecl || isActionLine(trimmed))) {
|
|
117
|
+
currentScene = {
|
|
118
|
+
name: path.basename(filePath, '.spec.md'),
|
|
119
|
+
group: undefined,
|
|
120
|
+
blocks: [],
|
|
121
|
+
};
|
|
122
|
+
scenes.push(currentScene);
|
|
123
|
+
// Fall through to process this line
|
|
124
|
+
}
|
|
125
|
+
if (!currentScene)
|
|
126
|
+
continue;
|
|
127
|
+
// ── Actor declaration ─────────────────────────────────────────────
|
|
128
|
+
// Format: `role-name:` or `role-name: action args`
|
|
129
|
+
// The first token before `:` is the role name (must not be a known action).
|
|
130
|
+
const actorDecl = earlyDecl ?? parseActorDeclaration(trimmed);
|
|
131
|
+
if (actorDecl) {
|
|
132
|
+
currentBlock = { role: actorDecl.role, actions: [] };
|
|
133
|
+
currentScene.blocks.push(currentBlock);
|
|
134
|
+
// If there's inline content after the colon, parse it as an action
|
|
135
|
+
if (actorDecl.rest) {
|
|
136
|
+
const actionLine = stripListPrefix(actorDecl.rest);
|
|
137
|
+
currentBlock.actions.push({ type: 'action', line: actionLine });
|
|
138
|
+
}
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
// Must have an actor block to add actions
|
|
142
|
+
if (!currentBlock)
|
|
143
|
+
continue;
|
|
144
|
+
// ── Comment ───────────────────────────────────────────────────────
|
|
145
|
+
if (trimmed.startsWith('//')) {
|
|
146
|
+
const text = trimmed.slice(2).trim();
|
|
147
|
+
if (text) {
|
|
148
|
+
currentBlock.actions.push({ type: 'comment', text });
|
|
149
|
+
}
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
// ── Conditional monitor (if <selector> + indented block) ──────────
|
|
153
|
+
if (trimmed.startsWith('if ') && !trimmed.startsWith('if(')) {
|
|
154
|
+
const selector = trimmed.slice(3).trim();
|
|
155
|
+
const subActions = [];
|
|
156
|
+
// Collect indented sub-actions
|
|
157
|
+
while (i + 1 < lines.length) {
|
|
158
|
+
const nextRaw = lines[i + 1];
|
|
159
|
+
const nextTrimmed = nextRaw.trim();
|
|
160
|
+
// Blank lines inside if block — skip but continue collecting
|
|
161
|
+
if (!nextTrimmed) {
|
|
162
|
+
i++;
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
// Indented line (2+ spaces from start of raw line) = sub-action
|
|
166
|
+
if (/^\s{2,}/.test(nextRaw) && nextTrimmed) {
|
|
167
|
+
const actionLine = stripListPrefix(nextTrimmed);
|
|
168
|
+
subActions.push(actionLine);
|
|
169
|
+
i++;
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
break;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
currentBlock.actions.push({ type: 'if', selector, actions: subActions });
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
// ── Macro invocation ────────────────────────────────────────────────
|
|
179
|
+
// A macro is any word that isn't a known DSL action, followed by
|
|
180
|
+
// optional `role <name>` or `team <name>` arguments:
|
|
181
|
+
// send-friend-request role best-friend-1
|
|
182
|
+
// signup-to-language team language-focus
|
|
183
|
+
const actionLine = stripListPrefix(trimmed);
|
|
184
|
+
const words = actionLine.split(/\s+/);
|
|
185
|
+
const firstWord = words[0];
|
|
186
|
+
if (firstWord && !KNOWN_ACTIONS.includes(firstWord)) {
|
|
187
|
+
const macroArgs = parseMacroArgs(words.slice(1));
|
|
188
|
+
currentBlock.actions.push({ type: 'macro', name: firstWord, args: macroArgs });
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
// ── Regular action line ───────────────────────────────────────────
|
|
192
|
+
currentBlock.actions.push({ type: 'action', line: actionLine });
|
|
193
|
+
}
|
|
194
|
+
return scenes;
|
|
195
|
+
}
|
|
196
|
+
// ---------------------------------------------------------------------------
|
|
197
|
+
// Helpers
|
|
198
|
+
// ---------------------------------------------------------------------------
|
|
199
|
+
/**
|
|
200
|
+
* Strip optional markdown list prefix from an action line.
|
|
201
|
+
* Supports unordered (`- `), ordered (`1. `, `2. `, etc.), and plain lines.
|
|
202
|
+
*/
|
|
203
|
+
function stripListPrefix(line) {
|
|
204
|
+
// Unordered: - action
|
|
205
|
+
if (line.startsWith('- '))
|
|
206
|
+
return line.slice(2);
|
|
207
|
+
// Ordered: 1. action, 2. action, 10. action, etc.
|
|
208
|
+
const orderedMatch = line.match(/^\d+\.\s+/);
|
|
209
|
+
if (orderedMatch)
|
|
210
|
+
return line.slice(orderedMatch[0].length);
|
|
211
|
+
return line;
|
|
212
|
+
}
|
|
213
|
+
/** Known DSL action verbs — used to distinguish actions from actor declarations */
|
|
214
|
+
const KNOWN_ACTIONS = [
|
|
215
|
+
'openTo', 'see', 'seeInView', 'notSee', 'seeText', 'seeToast', 'click',
|
|
216
|
+
'typeInto', 'check', 'select', 'wait', 'emit', 'waitFor',
|
|
217
|
+
'warnIf', 'up', 'prev', 'scrollToBottom', 'if',
|
|
218
|
+
];
|
|
219
|
+
/** Check if a line looks like a DSL action or macro invocation */
|
|
220
|
+
function isActionLine(line) {
|
|
221
|
+
const stripped = stripListPrefix(line);
|
|
222
|
+
const first = stripped.split(/\s/)[0];
|
|
223
|
+
// Any word could be either a known action or a macro name
|
|
224
|
+
return first !== undefined && first.length > 0;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Parse a line as an actor declaration.
|
|
228
|
+
*
|
|
229
|
+
* Actor declarations look like screenplay cues:
|
|
230
|
+
* - `role-name:` — block declaration (actions follow on subsequent lines)
|
|
231
|
+
* - `role-name: see foo` — inline declaration with first action on same line
|
|
232
|
+
*
|
|
233
|
+
* Returns null if the line is not an actor declaration (e.g. it's an action
|
|
234
|
+
* line or doesn't contain a colon in the right position).
|
|
235
|
+
*/
|
|
236
|
+
function parseActorDeclaration(line) {
|
|
237
|
+
// Match: word-chars (including hyphens) followed by a colon
|
|
238
|
+
const match = line.match(/^([\w][\w-]*):\s*(.*)$/);
|
|
239
|
+
if (!match)
|
|
240
|
+
return null;
|
|
241
|
+
const [, word, rest] = match;
|
|
242
|
+
// Don't treat known action verbs as actor declarations
|
|
243
|
+
if (KNOWN_ACTIONS.includes(word))
|
|
244
|
+
return null;
|
|
245
|
+
return { role: word, rest: rest.trim() };
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Parse macro arguments into typed MacroArg objects.
|
|
249
|
+
*
|
|
250
|
+
* Supports `alias=role` mappings that make `[alias.field]` resolve to the role's fields.
|
|
251
|
+
* Plain values without `=` are treated as literals for backward compatibility.
|
|
252
|
+
*
|
|
253
|
+
* @example
|
|
254
|
+
* parseMacroArgs(['target=new-user'])
|
|
255
|
+
* // => [{ type: 'mapping', alias: 'target', role: 'new-user' }]
|
|
256
|
+
*
|
|
257
|
+
* parseMacroArgs(['target=new-user', 'other=alice'])
|
|
258
|
+
* // => [{ type: 'mapping', alias: 'target', role: 'new-user' }, { type: 'mapping', alias: 'other', role: 'alice' }]
|
|
259
|
+
*/
|
|
260
|
+
function parseMacroArgs(words) {
|
|
261
|
+
const args = [];
|
|
262
|
+
for (const word of words) {
|
|
263
|
+
if (word.includes('=')) {
|
|
264
|
+
const eqIndex = word.indexOf('=');
|
|
265
|
+
const alias = word.slice(0, eqIndex);
|
|
266
|
+
const role = word.slice(eqIndex + 1);
|
|
267
|
+
if (alias && role) {
|
|
268
|
+
args.push({ type: 'mapping', alias, role });
|
|
269
|
+
}
|
|
270
|
+
else {
|
|
271
|
+
// Malformed mapping — treat as literal
|
|
272
|
+
args.push({ type: 'literal', value: word });
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
else {
|
|
276
|
+
args.push({ type: 'literal', value: word });
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
return args;
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Interpolate `[namespace.field]` references in an action line.
|
|
283
|
+
*
|
|
284
|
+
* Supported namespaces:
|
|
285
|
+
* - `[self.field]` — current actor's own fields
|
|
286
|
+
* - `[team.field]` — team metadata
|
|
287
|
+
* - `[role-name.field]` — another actor's fields
|
|
288
|
+
* - `[alias.field]` — aliased role (from macro args)
|
|
289
|
+
*
|
|
290
|
+
* @example
|
|
291
|
+
* // In an action line:
|
|
292
|
+
* interpolate('see user-card-[target.id]', { actors, self, aliases: new Map([['target', 'new-user']]) })
|
|
293
|
+
* // => 'see user-card-12345'
|
|
294
|
+
*/
|
|
295
|
+
function interpolate(line, ctx) {
|
|
296
|
+
return line.replace(/\[([\w][\w-]*)\.([\w]+)\]/g, (match, namespace, field) => {
|
|
297
|
+
// [self.field] — current actor's fields
|
|
298
|
+
if (namespace === 'self') {
|
|
299
|
+
if (!ctx.self) {
|
|
300
|
+
throw new Error(`Cannot use [self.${field}] — no current actor context`);
|
|
301
|
+
}
|
|
302
|
+
const value = ctx.self[field];
|
|
303
|
+
if (value === undefined) {
|
|
304
|
+
throw new Error(`[self.${field}] — actor has no field "${field}"`);
|
|
305
|
+
}
|
|
306
|
+
return String(value);
|
|
307
|
+
}
|
|
308
|
+
// [team.field] — team metadata
|
|
309
|
+
if (namespace === 'team') {
|
|
310
|
+
if (!ctx.team) {
|
|
311
|
+
throw new Error(`Cannot use [team.${field}] — no team context`);
|
|
312
|
+
}
|
|
313
|
+
const value = ctx.team[field];
|
|
314
|
+
if (value === undefined) {
|
|
315
|
+
throw new Error(`[team.${field}] — team has no field "${field}"`);
|
|
316
|
+
}
|
|
317
|
+
return String(value);
|
|
318
|
+
}
|
|
319
|
+
// Check for alias mapping first
|
|
320
|
+
const resolvedRole = ctx.aliases?.get(namespace) ?? namespace;
|
|
321
|
+
// Look up the actor
|
|
322
|
+
const actor = ctx.actors.get(resolvedRole);
|
|
323
|
+
if (!actor) {
|
|
324
|
+
const available = [...ctx.actors.keys()].join(', ');
|
|
325
|
+
const aliasNote = ctx.aliases?.has(namespace)
|
|
326
|
+
? ` (alias "${namespace}" -> "${resolvedRole}")`
|
|
327
|
+
: '';
|
|
328
|
+
throw new Error(`Unknown actor "${namespace}"${aliasNote} in [${namespace}.${field}] — available: ${available}`);
|
|
329
|
+
}
|
|
330
|
+
const value = actor[field];
|
|
331
|
+
if (value === undefined) {
|
|
332
|
+
throw new Error(`Actor "${resolvedRole}" has no field "${field}" (available: id, username, email, password, ...)`);
|
|
333
|
+
}
|
|
334
|
+
return String(value);
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
// ---------------------------------------------------------------------------
|
|
338
|
+
// Registration — convert parsed scenes into flow() registrations
|
|
339
|
+
// ---------------------------------------------------------------------------
|
|
340
|
+
/**
|
|
341
|
+
* Register parsed markdown scenes as reactive flows.
|
|
342
|
+
*
|
|
343
|
+
* Each scene becomes a `flow()` call. Actors are created upfront so
|
|
344
|
+
* `[actor.field]` interpolation can reference any actor regardless of
|
|
345
|
+
* declaration order.
|
|
346
|
+
*/
|
|
347
|
+
export function registerMarkdownScenes(scenes, filePath) {
|
|
348
|
+
for (const scene of scenes) {
|
|
349
|
+
flow(scene.name, ({ actor }) => {
|
|
350
|
+
// ── Phase 1: collect all unique roles and create actors ──────────
|
|
351
|
+
const actors = new Map();
|
|
352
|
+
for (const block of scene.blocks) {
|
|
353
|
+
if (!actors.has(block.role)) {
|
|
354
|
+
const a = actor(block.role);
|
|
355
|
+
actors.set(block.role, a);
|
|
356
|
+
}
|
|
357
|
+
if (block.alias && !actors.has(block.alias)) {
|
|
358
|
+
actors.set(block.alias, actors.get(block.role));
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
// ── Phase 2: apply actions to each actor block ──────────────────
|
|
362
|
+
for (const block of scene.blocks) {
|
|
363
|
+
const a = actors.get(block.alias || block.role);
|
|
364
|
+
// Base interpolation context for this actor block
|
|
365
|
+
const baseCtx = {
|
|
366
|
+
actors,
|
|
367
|
+
self: a,
|
|
368
|
+
// TODO: wire up team metadata when TeamManager provides it
|
|
369
|
+
team: undefined,
|
|
370
|
+
};
|
|
371
|
+
for (const action of block.actions) {
|
|
372
|
+
switch (action.type) {
|
|
373
|
+
case 'comment':
|
|
374
|
+
// Queue a console.log that executes during drain
|
|
375
|
+
a.do(async () => {
|
|
376
|
+
console.log(` // ${action.text}`);
|
|
377
|
+
});
|
|
378
|
+
break;
|
|
379
|
+
case 'action': {
|
|
380
|
+
const interpolated = interpolate(action.line, baseCtx);
|
|
381
|
+
const parsed = parseAction(interpolated);
|
|
382
|
+
applyDslAction(a, parsed);
|
|
383
|
+
break;
|
|
384
|
+
}
|
|
385
|
+
case 'if': {
|
|
386
|
+
const interpolatedSelector = interpolate(action.selector, baseCtx);
|
|
387
|
+
a.if(interpolatedSelector, (actor) => {
|
|
388
|
+
for (const subLine of action.actions) {
|
|
389
|
+
const interpolated = interpolate(subLine, {
|
|
390
|
+
...baseCtx,
|
|
391
|
+
self: actor,
|
|
392
|
+
});
|
|
393
|
+
const parsed = parseAction(interpolated);
|
|
394
|
+
applyDslAction(actor, parsed);
|
|
395
|
+
}
|
|
396
|
+
});
|
|
397
|
+
break;
|
|
398
|
+
}
|
|
399
|
+
case 'macro': {
|
|
400
|
+
const macro = getMacro(action.name);
|
|
401
|
+
if (!macro) {
|
|
402
|
+
throw new Error(`Macro not found: ${action.name} — define it with defineMacro('${action.name}', [...]) in a .ts file`);
|
|
403
|
+
}
|
|
404
|
+
// Build alias mappings from macro args
|
|
405
|
+
const aliases = new Map();
|
|
406
|
+
for (const arg of action.args) {
|
|
407
|
+
if (arg.type === 'mapping') {
|
|
408
|
+
// Verify the role exists
|
|
409
|
+
if (!actors.has(arg.role)) {
|
|
410
|
+
throw new Error(`Macro ${action.name}: unknown role "${arg.role}" in mapping "${arg.alias}=${arg.role}" — actors in this scene: ${[...actors.keys()].join(', ')}`);
|
|
411
|
+
}
|
|
412
|
+
aliases.set(arg.alias, arg.role);
|
|
413
|
+
}
|
|
414
|
+
// Literals are ignored for now — macros use [alias.field] syntax
|
|
415
|
+
}
|
|
416
|
+
// Context for macro interpolation includes aliases
|
|
417
|
+
const macroCtx = {
|
|
418
|
+
...baseCtx,
|
|
419
|
+
aliases,
|
|
420
|
+
};
|
|
421
|
+
// Apply macro actions inline (no await — flow model)
|
|
422
|
+
// Uses unified [namespace.field] syntax — no {{var}} substitution
|
|
423
|
+
for (const actionLine of macro) {
|
|
424
|
+
const interpolated = interpolate(actionLine, macroCtx);
|
|
425
|
+
const parsed = parseAction(interpolated);
|
|
426
|
+
applyDslAction(a, parsed);
|
|
427
|
+
}
|
|
428
|
+
break;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
// ---------------------------------------------------------------------------
|
|
437
|
+
// File loader — top-level entry point
|
|
438
|
+
// ---------------------------------------------------------------------------
|
|
439
|
+
/**
|
|
440
|
+
* Load a `.spec.md` file: parse it and register all scenes as flows.
|
|
441
|
+
*
|
|
442
|
+
* Called by the runner when it discovers `.spec.md` files alongside
|
|
443
|
+
* `.spec.ts` files.
|
|
444
|
+
*/
|
|
445
|
+
export async function loadMarkdownScene(filePath) {
|
|
446
|
+
const fs = await import('fs/promises');
|
|
447
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
448
|
+
setCurrentFile(filePath);
|
|
449
|
+
const scenes = parseMarkdownScenes(content, filePath);
|
|
450
|
+
registerMarkdownScenes(scenes, filePath);
|
|
451
|
+
}
|
|
452
|
+
//# sourceMappingURL=markdown-scene.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markdown-scene.js","sourceRoot":"","sources":["../src/markdown-scene.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Message bus for inter-actor coordination.
|
|
3
|
+
*
|
|
4
|
+
* Key behavior: Messages are "sticky" - they persist on the bus.
|
|
5
|
+
* If you listen AFTER a message was emitted, you still receive it (once).
|
|
6
|
+
* This prevents race conditions when declaring causality early in scenes.
|
|
7
|
+
*/
|
|
8
|
+
export declare class MessageBus {
|
|
9
|
+
private emittedMessages;
|
|
10
|
+
private listeners;
|
|
11
|
+
/**
|
|
12
|
+
* Emit a message to the bus.
|
|
13
|
+
* All current and future listeners for this message will be triggered.
|
|
14
|
+
*/
|
|
15
|
+
emit(message: string): void;
|
|
16
|
+
/**
|
|
17
|
+
* Wait for a message to be emitted.
|
|
18
|
+
* If the message was already emitted, resolves immediately.
|
|
19
|
+
*/
|
|
20
|
+
waitFor(message: string, timeout?: number): Promise<void>;
|
|
21
|
+
/**
|
|
22
|
+
* Check if a message has been emitted.
|
|
23
|
+
*/
|
|
24
|
+
hasEmitted(message: string): boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Clear all messages and listeners.
|
|
27
|
+
* Call this between scene runs.
|
|
28
|
+
*/
|
|
29
|
+
clear(): void;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=message-bus.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message-bus.d.ts","sourceRoot":"","sources":["../src/message-bus.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,eAAe,CAAoB;IAC3C,OAAO,CAAC,SAAS,CAAuC;IAExD;;;OAGG;IACH,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAa3B;;;OAGG;IACH,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,SAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAiCxD;;OAEG;IACH,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAIpC;;;OAGG;IACH,KAAK,IAAI,IAAI;CAId"}
|