flowstride 1.0.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.
Files changed (62) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +183 -0
  3. package/dist/adapters/api/undici.d.ts +46 -0
  4. package/dist/adapters/api/undici.js +118 -0
  5. package/dist/adapters/web/playwright.d.ts +51 -0
  6. package/dist/adapters/web/playwright.js +1108 -0
  7. package/dist/cli/commands/init.d.ts +1 -0
  8. package/dist/cli/commands/init.js +77 -0
  9. package/dist/cli/commands/report.d.ts +1 -0
  10. package/dist/cli/commands/report.js +78 -0
  11. package/dist/cli/commands/run.d.ts +1 -0
  12. package/dist/cli/commands/run.js +158 -0
  13. package/dist/cli/index.d.ts +2 -0
  14. package/dist/cli/index.js +39 -0
  15. package/dist/parser/lexer.d.ts +29 -0
  16. package/dist/parser/lexer.js +425 -0
  17. package/dist/parser/parser.d.ts +41 -0
  18. package/dist/parser/parser.js +573 -0
  19. package/dist/parser/resolver.d.ts +5 -0
  20. package/dist/parser/resolver.js +19 -0
  21. package/dist/parser/validator.d.ts +4 -0
  22. package/dist/parser/validator.js +37 -0
  23. package/dist/parser/variable_resolver.d.ts +7 -0
  24. package/dist/parser/variable_resolver.js +33 -0
  25. package/dist/runner/data_generators.d.ts +55 -0
  26. package/dist/runner/data_generators.js +233 -0
  27. package/dist/runner/dom_stripper.d.ts +4 -0
  28. package/dist/runner/dom_stripper.js +61 -0
  29. package/dist/runner/events.d.ts +15 -0
  30. package/dist/runner/events.js +20 -0
  31. package/dist/runner/flow_orchestrator.d.ts +15 -0
  32. package/dist/runner/flow_orchestrator.js +216 -0
  33. package/dist/runner/flow_worker.d.ts +53 -0
  34. package/dist/runner/flow_worker.js +1112 -0
  35. package/dist/runner/persistent_vault.d.ts +12 -0
  36. package/dist/runner/persistent_vault.js +84 -0
  37. package/dist/runner/plugin_manager.d.ts +13 -0
  38. package/dist/runner/plugin_manager.js +164 -0
  39. package/dist/runner/temporary_store.d.ts +8 -0
  40. package/dist/runner/temporary_store.js +22 -0
  41. package/dist/runner/worker_entry.d.ts +1 -0
  42. package/dist/runner/worker_entry.js +63 -0
  43. package/dist/sdk/api_proxy.d.ts +8 -0
  44. package/dist/sdk/api_proxy.js +28 -0
  45. package/dist/sdk/db_proxy.d.ts +3 -0
  46. package/dist/sdk/db_proxy.js +15 -0
  47. package/dist/sessions/manager.d.ts +17 -0
  48. package/dist/sessions/manager.js +74 -0
  49. package/dist/sessions/types.d.ts +39 -0
  50. package/dist/sessions/types.js +2 -0
  51. package/dist/sessions/vault.d.ts +8 -0
  52. package/dist/sessions/vault.js +69 -0
  53. package/dist/test_data/commerce.json +341 -0
  54. package/dist/test_data/geography.json +1298 -0
  55. package/dist/test_data/identities.json +1420 -0
  56. package/dist/test_data/phone_data.json +27 -0
  57. package/dist/test_data/utility.json +246 -0
  58. package/dist/types/index.d.ts +104 -0
  59. package/dist/types/index.js +2 -0
  60. package/dist/utils/redactor.d.ts +5 -0
  61. package/dist/utils/redactor.js +53 -0
  62. package/package.json +54 -0
@@ -0,0 +1 @@
1
+ export declare function initAction(): Promise<void>;
@@ -0,0 +1,77 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.initAction = initAction;
40
+ const fs = __importStar(require("fs"));
41
+ const path = __importStar(require("path"));
42
+ const chalk_1 = __importDefault(require("chalk"));
43
+ async function initAction() {
44
+ const root = process.cwd();
45
+ const folders = ["flows", "reports", "plugins"];
46
+ const sampleFlowPath = path.join(root, "flows", "welcome.flow");
47
+ console.log(chalk_1.default.cyan("\n🚀 Flowstride: Initialising new workspace..."));
48
+ folders.forEach((folder) => {
49
+ const dirPath = path.join(root, folder);
50
+ if (!fs.existsSync(dirPath)) {
51
+ fs.mkdirSync(dirPath);
52
+ console.log(`${chalk_1.default.green("✔")} Created ${folder}/`);
53
+ }
54
+ else {
55
+ console.log(`${chalk_1.default.yellow("ℹ")} ${folder}/ already exists, skipping.`);
56
+ }
57
+ });
58
+ const welcomeContent = `Feature: Getting Started
59
+
60
+ Scenario: Verify Flowstride is Ready
61
+
62
+ Given "User lands on the sample page";
63
+ flow.open "https://example.com";
64
+
65
+ When "User confirms Welcome!";
66
+ flow.expect text "Welcome!";
67
+
68
+ Then "Verify the main heading is visible";
69
+ flow.expect visible "h1";
70
+ `;
71
+ if (!fs.existsSync(sampleFlowPath)) {
72
+ fs.writeFileSync(sampleFlowPath, welcomeContent);
73
+ console.log(`${chalk_1.default.green("✔")} Created flows/welcome.flow`);
74
+ }
75
+ console.log(chalk_1.default.bold.green("\n✨ Workspace ready!"));
76
+ console.log(`Try running: ${chalk_1.default.cyan("flowstride run flows/welcome.flow")}\n`);
77
+ }
@@ -0,0 +1 @@
1
+ export declare function reportAction(): Promise<void>;
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.reportAction = reportAction;
37
+ const child_process_1 = require("child_process");
38
+ const path = __importStar(require("path"));
39
+ const fs = __importStar(require("fs"));
40
+ async function reportAction() {
41
+ console.log("📊 Flowstride: Launching Debug Dashboard...");
42
+ const targetProjectPath = process.cwd();
43
+ const latestReportPath = path.join(targetProjectPath, "reports", "latest");
44
+ const cliInstallPath = path.resolve(__dirname, "../../..");
45
+ const reportPackagePath = path.join(cliInstallPath, "packages", "flowstride_report");
46
+ const publicPath = path.join(reportPackagePath, "public");
47
+ const runDataPath = path.join(latestReportPath, "run-data.json");
48
+ if (!fs.existsSync(runDataPath)) {
49
+ console.error('❌ Error: No run data found. Run "flowstride run" first to generate a report.');
50
+ return;
51
+ }
52
+ if (!fs.existsSync(publicPath)) {
53
+ fs.mkdirSync(publicPath, { recursive: true });
54
+ }
55
+ fs.copyFileSync(runDataPath, path.join(publicPath, "run-data.json"));
56
+ const sourceScreenshots = path.join(latestReportPath, "screenshots");
57
+ const targetScreenshots = path.join(publicPath, "screenshots");
58
+ if (fs.existsSync(sourceScreenshots)) {
59
+ if (!fs.existsSync(targetScreenshots)) {
60
+ fs.mkdirSync(targetScreenshots, { recursive: true });
61
+ }
62
+ const files = fs.readdirSync(sourceScreenshots);
63
+ for (const file of files) {
64
+ fs.copyFileSync(path.join(sourceScreenshots, file), path.join(targetScreenshots, file));
65
+ }
66
+ console.log(`📸 Synced ${files.length} screenshots to dashboard.`);
67
+ }
68
+ try {
69
+ console.log("Serving report at http://localhost:5173...");
70
+ (0, child_process_1.execSync)("npm run dev", {
71
+ cwd: reportPackagePath,
72
+ stdio: "inherit",
73
+ });
74
+ }
75
+ catch (error) {
76
+ console.error("❌ CLI Error: Could not launch report dashboard.");
77
+ }
78
+ }
@@ -0,0 +1 @@
1
+ export declare function runAction(targetPath: string | undefined, options: Record<string, any>): Promise<void>;
@@ -0,0 +1,158 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.runAction = runAction;
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ const dotenv = __importStar(require("dotenv"));
40
+ const os = __importStar(require("os"));
41
+ const flow_worker_1 = require("../../runner/flow_worker");
42
+ const flow_orchestrator_1 = require("../../runner/flow_orchestrator");
43
+ const playwright_1 = require("../../adapters/web/playwright");
44
+ const undici_1 = require("../../adapters/api/undici");
45
+ const temporary_store_1 = require("../../runner/temporary_store");
46
+ const manager_1 = require("../../sessions/manager");
47
+ const persistent_vault_1 = require("../../runner/persistent_vault");
48
+ const plugin_manager_1 = require("../../runner/plugin_manager");
49
+ const api_proxy_1 = require("../../sdk/api_proxy");
50
+ async function runAction(targetPath, options) {
51
+ const root = process.cwd();
52
+ const profileEnvPath = options.env
53
+ ? path.resolve(root, `.env.${options.env}`)
54
+ : null;
55
+ const defaultEnvPath = path.resolve(root, ".env");
56
+ if (profileEnvPath) {
57
+ if (fs.existsSync(profileEnvPath)) {
58
+ dotenv.config({ path: profileEnvPath });
59
+ console.log(`[Environment]: Loaded profile "${options.env}"`);
60
+ }
61
+ else {
62
+ console.warn(`[Environment Warning]: --env ${options.env} was passed, but .env.${options.env} does not exist.`);
63
+ }
64
+ }
65
+ if (fs.existsSync(defaultEnvPath)) {
66
+ dotenv.config({ path: defaultEnvPath });
67
+ if (!options.env) {
68
+ console.log(`[Environment]: Loaded default .env file`);
69
+ }
70
+ }
71
+ const isCI = !!(process.env.CI ||
72
+ process.env.GITHUB_ACTIONS ||
73
+ process.env.GITLAB_CI ||
74
+ process.env.CIRCLECI ||
75
+ process.env.JENKINS_URL);
76
+ if (isCI) {
77
+ console.log("\n[CI/CD]: Environment detected. Engaging Smart Automation Behaviors...");
78
+ }
79
+ const resolvedPath = targetPath
80
+ ? path.resolve(root, targetPath)
81
+ : path.resolve(root, "flows");
82
+ if (!fs.existsSync(resolvedPath)) {
83
+ console.error(`\n[Error]: Cannot find target path "${resolvedPath}".`);
84
+ console.log(`[Info]: Try running 'flowstride init' to create a workspace first.\n`);
85
+ process.exit(1);
86
+ }
87
+ console.log(`\n[Flowstride]: Starting execution suite at "${resolvedPath}"...`);
88
+ const configPath = path.resolve(root, "flowstride.config.json");
89
+ let localConfig = {};
90
+ if (fs.existsSync(configPath)) {
91
+ try {
92
+ const fileContent = fs.readFileSync(configPath, "utf-8");
93
+ localConfig = JSON.parse(fileContent);
94
+ console.log(`[Config]: Loaded project configuration from flowstride.config.json`);
95
+ }
96
+ catch (e) {
97
+ console.warn(`[Warning]: Could not parse flowstride.config.json: ${e.message}`);
98
+ }
99
+ }
100
+ const config = {
101
+ headless: isCI ? true : (options.headless ?? localConfig.headless ?? true),
102
+ timeout: localConfig.timeout ?? 30000,
103
+ bail: options.bail ?? localConfig.bail ?? false,
104
+ baseUrl: process.env.BASE_URL ?? localConfig.baseUrl,
105
+ defaultDialogBehavior: isCI
106
+ ? "dismiss"
107
+ : (localConfig.defaultDialogBehavior ?? "dismiss"),
108
+ pluginsDir: localConfig.pluginsDir,
109
+ };
110
+ if (isCI) {
111
+ console.log(` > Headless Mode: ${config.headless ? "Forced ON" : "OFF"}`);
112
+ console.log(` > Dialog Protection: ${(config.defaultDialogBehavior || "dismiss").toUpperCase()}`);
113
+ console.log(` > Artifact Auto-Staging: ENABLED\n`);
114
+ }
115
+ let workerCount = options.workers ? parseInt(options.workers, 10) : 1;
116
+ if (isNaN(workerCount) || workerCount < 1) {
117
+ console.error(`\n[Error]: Invalid worker count "${options.workers}". Must be an integer greater than 0.`);
118
+ process.exit(1);
119
+ }
120
+ const maxSafeWorkers = Math.max(1, os.cpus().length - 1);
121
+ if (workerCount > maxSafeWorkers) {
122
+ console.log(`\n[Warning]: You requested ${workerCount} workers, but your CPU only safely supports ${maxSafeWorkers}.`);
123
+ console.log(`[Warning]: Auto-capping to ${maxSafeWorkers} workers to prevent system thrashing and crashes.\n`);
124
+ workerCount = maxSafeWorkers;
125
+ }
126
+ const web = new playwright_1.WebAdapter(config);
127
+ const api = new undici_1.ApiAdapter(config);
128
+ const store = new temporary_store_1.TemporaryStore();
129
+ const apiProxy = new api_proxy_1.ApiProxy(api);
130
+ const pluginManager = new plugin_manager_1.PluginManager(config, web, store, apiProxy);
131
+ const vault = new persistent_vault_1.PersistentVault(root);
132
+ const sessions = new manager_1.SessionManager(web, api, vault);
133
+ const worker = new flow_worker_1.FlowWorker(config, web, api, store, sessions, pluginManager);
134
+ const orchestrator = new flow_orchestrator_1.FlowOrchestrator(worker, workerCount, config);
135
+ try {
136
+ await orchestrator.start(resolvedPath);
137
+ console.log("\n[Flowstride]: Execution suite completed successfully.");
138
+ if (isCI) {
139
+ const artifactsDir = path.resolve(root, "flowstride-artifacts");
140
+ if (!fs.existsSync(artifactsDir)) {
141
+ fs.mkdirSync(artifactsDir, { recursive: true });
142
+ }
143
+ console.log(`[CI Artifacts]: Test environment artifacts auto-staged to ./flowstride-artifacts/`);
144
+ process.exit(0);
145
+ }
146
+ else {
147
+ console.log("\n[Dashboard]: Server remains active for Replay and Debugging. Press Ctrl+C to exit.");
148
+ await new Promise(() => { });
149
+ }
150
+ }
151
+ catch (error) {
152
+ console.error("\n[Fatal Error]:", error.message);
153
+ if (isCI) {
154
+ console.log(`[CI Alert]: Terminating pipeline with exit code 1 to block deployment.`);
155
+ }
156
+ process.exit(1);
157
+ }
158
+ }
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const commander_1 = require("commander");
5
+ const run_1 = require("./commands/run");
6
+ const report_1 = require("./commands/report");
7
+ const init_1 = require("./commands/init");
8
+ const program = new commander_1.Command();
9
+ program
10
+ .name("flowstride")
11
+ .description("Flowstride: The Enterprise Flow-First Automation CLI")
12
+ .version("1.0.0");
13
+ program
14
+ .command("init")
15
+ .description("Initialise a new Flowstride workspace in the current directory")
16
+ .action(async () => {
17
+ await (0, init_1.initAction)();
18
+ });
19
+ program
20
+ .command("run")
21
+ .description("Execute .flow automation journeys")
22
+ .argument("[path]", "Path to .flow files or directory (defaults to ./flows)")
23
+ .option("-b, --bail", "Stop execution on first failure", false)
24
+ .option("--headed", "Run browser in headed mode", false)
25
+ .option("--headless <boolean>", "Run browser in headless mode", (val) => val === "true", true)
26
+ .option("--workers <number>", "Number of parallel workers to spawn (Pro feature)")
27
+ .action(async (path, options) => {
28
+ if (options.headed) {
29
+ options.headless = false;
30
+ }
31
+ await (0, run_1.runAction)(path, options);
32
+ });
33
+ program
34
+ .command("report")
35
+ .description("Launch the interactive Debug Dashboard")
36
+ .action(async () => {
37
+ await (0, report_1.reportAction)();
38
+ });
39
+ program.parse(process.argv);
@@ -0,0 +1,29 @@
1
+ export declare enum TokenType {
2
+ FEATURE = "FEATURE",
3
+ SCENARIO = "SCENARIO",
4
+ BLOCK = "BLOCK",
5
+ COMMAND = "COMMAND",
6
+ MODIFIER = "MODIFIER",
7
+ SPATIAL = "SPATIAL",
8
+ PREPOSITION = "PREPOSITION",
9
+ ELEMENT = "ELEMENT",
10
+ STRING = "STRING",
11
+ LET = "LET",
12
+ ASSIGN = "ASSIGN",
13
+ ALIAS_COMMAND = "ALIAS_COMMAND",
14
+ JSON_PAYLOAD = "JSON_PAYLOAD",
15
+ IDENTIFIER = "IDENTIFIER",
16
+ EOF = "EOF"
17
+ }
18
+ export interface Token {
19
+ type: TokenType;
20
+ value: string;
21
+ line: number;
22
+ }
23
+ export declare class Lexer {
24
+ private position;
25
+ private line;
26
+ private input;
27
+ constructor(rawInput: string);
28
+ tokenise(): Token[];
29
+ }