@lynxwall/cucumber-tsflow 7.7.1 → 7.7.2

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.
@@ -33,4 +33,6 @@ export interface LoaderWorkerResponse {
33
33
  loadedFiles?: string[];
34
34
  /** Error message if something went wrong */
35
35
  error?: string;
36
+ /** Per-file errors that were caught but did not kill the worker */
37
+ fileErrors?: string[];
36
38
  }
@@ -11,9 +11,165 @@
11
11
  * Cucumber step/hook registration — only BindingRegistry population occurs.
12
12
  */
13
13
  Object.defineProperty(exports, "__esModule", { value: true });
14
- // Shim 'window' for libraries that expect a browser environment in worker threads
14
+ // Shim browser globals for libraries that expect a browser environment in worker threads.
15
+ // Workers are a constrained Node.js environment — many libraries probe for browser APIs
16
+ // during module evaluation. These stubs prevent "Cannot read properties of undefined" errors.
17
+ // Aligned with the globals expected by @uis/testing-bdd (uis-jest setup).
15
18
  if (typeof globalThis.window === 'undefined') {
16
- globalThis.window = globalThis;
19
+ const noop = () => { };
20
+ const noopObj = new Proxy({}, { get: () => noop });
21
+ // Helper: safely set a global property (some like `navigator` are getter-only in modern Node)
22
+ const safeDefine = (target, key, value) => {
23
+ try {
24
+ Object.defineProperty(target, key, { value, writable: true, configurable: true });
25
+ }
26
+ catch {
27
+ // Property may be non-configurable — ignore and move on
28
+ }
29
+ };
30
+ // Minimal location stub — covers window.location.href / .origin / .protocol etc.
31
+ const locationStub = {
32
+ href: '',
33
+ origin: '',
34
+ protocol: 'file:',
35
+ host: '',
36
+ hostname: '',
37
+ port: '',
38
+ pathname: '',
39
+ search: '',
40
+ hash: '',
41
+ assign: noop,
42
+ replace: noop,
43
+ reload: noop,
44
+ toString: () => ''
45
+ };
46
+ // Minimal document stub — covers document.createElement, querySelector, etc.
47
+ const documentStub = {
48
+ createElement: () => noopObj,
49
+ createElementNS: () => noopObj,
50
+ createTextNode: () => noopObj,
51
+ createDocumentFragment: () => noopObj,
52
+ createComment: () => noopObj,
53
+ getElementById: () => null,
54
+ getElementsByClassName: () => [],
55
+ getElementsByTagName: () => [],
56
+ querySelector: () => null,
57
+ querySelectorAll: () => [],
58
+ head: noopObj,
59
+ body: noopObj,
60
+ documentElement: noopObj,
61
+ addEventListener: noop,
62
+ removeEventListener: noop,
63
+ dispatchEvent: noop,
64
+ cookie: ''
65
+ };
66
+ // Minimal navigator stub
67
+ const navigatorStub = {
68
+ userAgent: 'node',
69
+ platform: process.platform,
70
+ language: 'en',
71
+ languages: ['en']
72
+ };
73
+ // Fake localStorage — matches @uis/testing-bdd FakeLocalStorage
74
+ const localStorageStub = {
75
+ store: {},
76
+ clear() {
77
+ this.store = {};
78
+ },
79
+ getItem(key) {
80
+ return this.store[key] ?? null;
81
+ },
82
+ setItem(key, value) {
83
+ this.store[key] = value;
84
+ },
85
+ removeItem(key) {
86
+ delete this.store[key];
87
+ },
88
+ get length() {
89
+ return Object.keys(this.store).length;
90
+ },
91
+ key() {
92
+ return null;
93
+ }
94
+ };
95
+ // matchMedia stub — matches @uis/testing-bdd mock
96
+ const matchMediaStub = (query) => ({
97
+ matches: false,
98
+ media: query,
99
+ onchange: null,
100
+ addListener: noop,
101
+ removeListener: noop,
102
+ addEventListener: noop,
103
+ removeEventListener: noop,
104
+ dispatchEvent: noop
105
+ });
106
+ // Minimal Element prototype for scrollIntoView etc.
107
+ class HTMLElementStub {
108
+ scrollIntoView = noop;
109
+ getBoundingClientRect = () => ({
110
+ top: 0,
111
+ left: 0,
112
+ bottom: 0,
113
+ right: 0,
114
+ width: 0,
115
+ height: 0,
116
+ x: 0,
117
+ y: 0
118
+ });
119
+ }
120
+ class ElementStub extends HTMLElementStub {
121
+ }
122
+ // Minimal XMLSerializer stub
123
+ class XMLSerializerStub {
124
+ serializeToString() {
125
+ return '';
126
+ }
127
+ }
128
+ // Build the window shim
129
+ const windowShim = {};
130
+ for (const key of Object.getOwnPropertyNames(globalThis)) {
131
+ try {
132
+ windowShim[key] = globalThis[key];
133
+ }
134
+ catch {
135
+ // skip non-readable properties
136
+ }
137
+ }
138
+ windowShim.location = locationStub;
139
+ windowShim.document = documentStub;
140
+ windowShim.navigator = navigatorStub;
141
+ windowShim.localStorage = localStorageStub;
142
+ windowShim.self = windowShim;
143
+ windowShim.top = windowShim;
144
+ windowShim.parent = windowShim;
145
+ windowShim.addEventListener = noop;
146
+ windowShim.removeEventListener = noop;
147
+ windowShim.dispatchEvent = noop;
148
+ windowShim.requestAnimationFrame = noop;
149
+ windowShim.cancelAnimationFrame = noop;
150
+ windowShim.getComputedStyle = () => noopObj;
151
+ windowShim.matchMedia = matchMediaStub;
152
+ windowShim.open = noop;
153
+ windowShim.close = noop;
154
+ windowShim.focus = noop;
155
+ windowShim.blur = noop;
156
+ windowShim.scroll = noop;
157
+ windowShim.scrollTo = noop;
158
+ windowShim.scrollBy = noop;
159
+ windowShim.CustomEvent = class CustomEvent extends Event {
160
+ };
161
+ windowShim.HTMLElement = HTMLElementStub;
162
+ windowShim.Element = ElementStub;
163
+ windowShim.XMLSerializer = XMLSerializerStub;
164
+ // Set globals on globalThis
165
+ safeDefine(globalThis, 'window', windowShim);
166
+ safeDefine(globalThis, 'document', documentStub);
167
+ safeDefine(globalThis, 'navigator', navigatorStub);
168
+ safeDefine(globalThis, 'location', locationStub);
169
+ safeDefine(globalThis, 'localStorage', localStorageStub);
170
+ safeDefine(globalThis, 'self', windowShim);
171
+ safeDefine(globalThis, 'matchMedia', matchMediaStub);
172
+ safeDefine(globalThis, 'XMLSerializer', XMLSerializerStub);
17
173
  }
18
174
  const node_worker_threads_1 = require("node:worker_threads");
19
175
  const node_module_1 = require("node:module");
@@ -25,25 +181,36 @@ async function processMessage(message) {
25
181
  if (message.type !== 'LOAD') {
26
182
  return;
27
183
  }
184
+ const fileErrors = [];
28
185
  try {
29
186
  // Set the experimental decorators flag before loading support code
30
187
  global.experimentalDecorators = message.experimentalDecorators;
31
188
  process.env.CUCUMBER_EXPERIMENTAL_DECORATORS = String(message.experimentalDecorators);
32
- // Load require modules (transpiler setup)
189
+ // Load require modules (transpiler setup) — these are critical, fail fast
33
190
  for (const modulePath of message.requireModules) {
34
191
  require(modulePath);
35
192
  }
36
- // Load support files via require (CJS)
37
- for (const filePath of message.requirePaths) {
38
- require(filePath);
39
- }
40
- // Register ESM loaders
193
+ // Register ESM loaders also critical for import phase
41
194
  for (const specifier of message.loaders) {
42
195
  (0, node_module_1.register)(specifier, (0, node_url_1.pathToFileURL)('./'));
43
196
  }
44
- // Load support files via import (ESM)
197
+ // Load support files via require (CJS) — per-file isolation
198
+ for (const filePath of message.requirePaths) {
199
+ try {
200
+ require(filePath);
201
+ }
202
+ catch (err) {
203
+ fileErrors.push(`CJS ${filePath}: ${err.message || String(err)}`);
204
+ }
205
+ }
206
+ // Load support files via import (ESM) — per-file isolation
45
207
  for (const filePath of message.importPaths) {
46
- await import((0, node_url_1.pathToFileURL)(filePath).toString());
208
+ try {
209
+ await import((0, node_url_1.pathToFileURL)(filePath).toString());
210
+ }
211
+ catch (err) {
212
+ fileErrors.push(`ESM ${filePath}: ${err.message || String(err)}`);
213
+ }
47
214
  }
48
215
  // Extract descriptors from the binding registry
49
216
  const { BindingRegistry } = require('../bindings/binding-registry');
@@ -53,14 +220,17 @@ async function processMessage(message) {
53
220
  const response = {
54
221
  type: 'LOADED',
55
222
  descriptors,
56
- loadedFiles
223
+ loadedFiles,
224
+ fileErrors: fileErrors.length > 0 ? fileErrors : undefined
57
225
  };
58
226
  node_worker_threads_1.parentPort.postMessage(response);
59
227
  }
60
228
  catch (err) {
229
+ // Critical failure (transpiler setup / loader registration / registry extraction)
61
230
  const response = {
62
231
  type: 'ERROR',
63
- error: err.message || String(err)
232
+ error: err.message || String(err),
233
+ fileErrors: fileErrors.length > 0 ? fileErrors : undefined
64
234
  };
65
235
  node_worker_threads_1.parentPort.postMessage(response);
66
236
  }
@@ -1 +1 @@
1
- {"version":3,"file":"loader-worker.js","sourceRoot":"","sources":["../../src/api/loader-worker.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;GAUG;;AAEH,kFAAkF;AAClF,IAAI,OAAO,UAAU,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;IAC7C,UAAsC,CAAC,MAAM,GAAG,UAAU,CAAC;AAC7D,CAAC;AAED,6DAA6D;AAC7D,6CAAuC;AACvC,uCAAyC;AACzC,oCAAkC;AAElC,gFAAgF;AAC/E,MAAc,CAAC,eAAe,GAAG,IAAI,CAAC;AA4BvC,KAAK,UAAU,cAAc,CAAC,OAA4B;IACzD,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QAC7B,OAAO;IACR,CAAC;IAED,IAAI,CAAC;QACJ,mEAAmE;QACnE,MAAM,CAAC,sBAAsB,GAAG,OAAO,CAAC,sBAAsB,CAAC;QAC/D,OAAO,CAAC,GAAG,CAAC,gCAAgC,GAAG,MAAM,CAAC,OAAO,CAAC,sBAAsB,CAAC,CAAC;QAEtF,0CAA0C;QAC1C,KAAK,MAAM,UAAU,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;YACjD,OAAO,CAAC,UAAU,CAAC,CAAC;QACrB,CAAC;QAED,uCAAuC;QACvC,KAAK,MAAM,QAAQ,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;YAC7C,OAAO,CAAC,QAAQ,CAAC,CAAC;QACnB,CAAC;QAED,uBAAuB;QACvB,KAAK,MAAM,SAAS,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;YACzC,IAAA,sBAAQ,EAAC,SAAS,EAAE,IAAA,wBAAa,EAAC,IAAI,CAAC,CAAC,CAAC;QAC1C,CAAC;QAED,sCAAsC;QACtC,KAAK,MAAM,QAAQ,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;YAC5C,MAAM,MAAM,CAAC,IAAA,wBAAa,EAAC,QAAQ,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC;QAClD,CAAC;QAED,gDAAgD;QAChD,MAAM,EAAE,eAAe,EAAE,GAAG,OAAO,CAAC,8BAA8B,CAAC,CAAC;QACpE,MAAM,QAAQ,GAAG,eAAe,CAAC,QAAQ,CAAC;QAC1C,MAAM,WAAW,GAAG,QAAQ,CAAC,aAAa,EAAE,CAAC;QAC7C,MAAM,WAAW,GAAa,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,wBAAwB,EAAE,CAAC,CAAC;QAE9E,MAAM,QAAQ,GAAyB;YACtC,IAAI,EAAE,QAAQ;YACd,WAAW;YACX,WAAW;SACX,CAAC;QACF,gCAAW,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;IACnC,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QACnB,MAAM,QAAQ,GAAyB;YACtC,IAAI,EAAE,OAAO;YACb,KAAK,EAAE,GAAG,CAAC,OAAO,IAAI,MAAM,CAAC,GAAG,CAAC;SACjC,CAAC;QACF,gCAAW,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;IACnC,CAAC;AACF,CAAC;AAED,uEAAuE;AACvE,IAAI,gCAAU,IAAI,gCAAU,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;IAC9C,cAAc,CAAC,gCAAiC,CAAC,CAAC;AACnD,CAAC;AAED,4CAA4C;AAC5C,gCAAU,EAAE,EAAE,CAAC,SAAS,EAAE,CAAC,GAAwB,EAAE,EAAE,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC","sourcesContent":["/**\r\n * loader-worker.ts\r\n *\r\n * Runs inside a worker_threads Worker. Receives support-code file paths and\r\n * transpiler configuration, loads the files (warming the transpiler cache),\r\n * extracts serializable binding descriptors from the BindingRegistry, and\r\n * posts them back to the main thread.\r\n *\r\n * The __LOADER_WORKER global flag causes the binding decorator to skip\r\n * Cucumber step/hook registration — only BindingRegistry population occurs.\r\n */\r\n\r\n// Shim 'window' for libraries that expect a browser environment in worker threads\r\nif (typeof globalThis.window === 'undefined') {\r\n\t(globalThis as Record<string, unknown>).window = globalThis;\r\n}\r\n\r\nimport { parentPort, workerData } from 'node:worker_threads';\r\nimport { register } from 'node:module';\r\nimport { pathToFileURL } from 'node:url';\r\nimport 'polyfill-symbol-metadata';\r\n\r\n// Signal that we're in a loader-worker context so decorators skip Cucumber APIs\r\n(global as any).__LOADER_WORKER = true;\r\n\r\n/** Message types sent from main thread to worker */\r\nexport interface LoaderWorkerRequest {\r\n\ttype: 'LOAD';\r\n\t/** Absolute paths to load via require() */\r\n\trequirePaths: string[];\r\n\t/** Absolute paths to load via import() */\r\n\timportPaths: string[];\r\n\t/** Modules to require before support code (transpiler setup etc.) */\r\n\trequireModules: string[];\r\n\t/** ESM loaders to register before importing */\r\n\tloaders: string[];\r\n\t/** Whether to use experimental decorators */\r\n\texperimentalDecorators: boolean;\r\n}\r\n\r\n/** Message types sent from worker back to main thread */\r\nexport interface LoaderWorkerResponse {\r\n\ttype: 'LOADED' | 'ERROR';\r\n\t/** Serializable binding descriptors extracted from the registry */\r\n\tdescriptors?: import('../bindings/step-binding').SerializableBindingDescriptor[];\r\n\t/** Source files that were successfully loaded */\r\n\tloadedFiles?: string[];\r\n\t/** Error message if something went wrong */\r\n\terror?: string;\r\n}\r\n\r\nasync function processMessage(message: LoaderWorkerRequest): Promise<void> {\r\n\tif (message.type !== 'LOAD') {\r\n\t\treturn;\r\n\t}\r\n\r\n\ttry {\r\n\t\t// Set the experimental decorators flag before loading support code\r\n\t\tglobal.experimentalDecorators = message.experimentalDecorators;\r\n\t\tprocess.env.CUCUMBER_EXPERIMENTAL_DECORATORS = String(message.experimentalDecorators);\r\n\r\n\t\t// Load require modules (transpiler setup)\r\n\t\tfor (const modulePath of message.requireModules) {\r\n\t\t\trequire(modulePath);\r\n\t\t}\r\n\r\n\t\t// Load support files via require (CJS)\r\n\t\tfor (const filePath of message.requirePaths) {\r\n\t\t\trequire(filePath);\r\n\t\t}\r\n\r\n\t\t// Register ESM loaders\r\n\t\tfor (const specifier of message.loaders) {\r\n\t\t\tregister(specifier, pathToFileURL('./'));\r\n\t\t}\r\n\r\n\t\t// Load support files via import (ESM)\r\n\t\tfor (const filePath of message.importPaths) {\r\n\t\t\tawait import(pathToFileURL(filePath).toString());\r\n\t\t}\r\n\r\n\t\t// Extract descriptors from the binding registry\r\n\t\tconst { BindingRegistry } = require('../bindings/binding-registry');\r\n\t\tconst registry = BindingRegistry.instance;\r\n\t\tconst descriptors = registry.toDescriptors();\r\n\t\tconst loadedFiles: string[] = Array.from(registry.getDescriptorSourceFiles());\r\n\r\n\t\tconst response: LoaderWorkerResponse = {\r\n\t\t\ttype: 'LOADED',\r\n\t\t\tdescriptors,\r\n\t\t\tloadedFiles\r\n\t\t};\r\n\t\tparentPort!.postMessage(response);\r\n\t} catch (err: any) {\r\n\t\tconst response: LoaderWorkerResponse = {\r\n\t\t\ttype: 'ERROR',\r\n\t\t\terror: err.message || String(err)\r\n\t\t};\r\n\t\tparentPort!.postMessage(response);\r\n\t}\r\n}\r\n\r\n// Handle initial workerData (if files are passed at construction time)\r\nif (workerData && workerData.type === 'LOAD') {\r\n\tprocessMessage(workerData as LoaderWorkerRequest);\r\n}\r\n\r\n// Handle messages posted after construction\r\nparentPort?.on('message', (msg: LoaderWorkerRequest) => processMessage(msg));\r\n"]}
1
+ {"version":3,"file":"loader-worker.js","sourceRoot":"","sources":["../../src/api/loader-worker.ts"],"names":[],"mappings":"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","sourcesContent":["/**\r\n * loader-worker.ts\r\n *\r\n * Runs inside a worker_threads Worker. Receives support-code file paths and\r\n * transpiler configuration, loads the files (warming the transpiler cache),\r\n * extracts serializable binding descriptors from the BindingRegistry, and\r\n * posts them back to the main thread.\r\n *\r\n * The __LOADER_WORKER global flag causes the binding decorator to skip\r\n * Cucumber step/hook registration — only BindingRegistry population occurs.\r\n */\r\n\r\n// Shim browser globals for libraries that expect a browser environment in worker threads.\r\n// Workers are a constrained Node.js environment — many libraries probe for browser APIs\r\n// during module evaluation. These stubs prevent \"Cannot read properties of undefined\" errors.\r\n// Aligned with the globals expected by @uis/testing-bdd (uis-jest setup).\r\nif (typeof globalThis.window === 'undefined') {\r\n\tconst noop = (): void => {};\r\n\tconst noopObj = new Proxy({}, { get: () => noop });\r\n\r\n\t// Helper: safely set a global property (some like `navigator` are getter-only in modern Node)\r\n\tconst safeDefine = (target: object, key: string, value: unknown): void => {\r\n\t\ttry {\r\n\t\t\tObject.defineProperty(target, key, { value, writable: true, configurable: true });\r\n\t\t} catch {\r\n\t\t\t// Property may be non-configurable — ignore and move on\r\n\t\t}\r\n\t};\r\n\r\n\t// Minimal location stub — covers window.location.href / .origin / .protocol etc.\r\n\tconst locationStub = {\r\n\t\thref: '',\r\n\t\torigin: '',\r\n\t\tprotocol: 'file:',\r\n\t\thost: '',\r\n\t\thostname: '',\r\n\t\tport: '',\r\n\t\tpathname: '',\r\n\t\tsearch: '',\r\n\t\thash: '',\r\n\t\tassign: noop,\r\n\t\treplace: noop,\r\n\t\treload: noop,\r\n\t\ttoString: () => ''\r\n\t};\r\n\r\n\t// Minimal document stub — covers document.createElement, querySelector, etc.\r\n\tconst documentStub = {\r\n\t\tcreateElement: (): unknown => noopObj,\r\n\t\tcreateElementNS: (): unknown => noopObj,\r\n\t\tcreateTextNode: (): unknown => noopObj,\r\n\t\tcreateDocumentFragment: (): unknown => noopObj,\r\n\t\tcreateComment: (): unknown => noopObj,\r\n\t\tgetElementById: (): null => null,\r\n\t\tgetElementsByClassName: (): unknown[] => [],\r\n\t\tgetElementsByTagName: (): unknown[] => [],\r\n\t\tquerySelector: (): null => null,\r\n\t\tquerySelectorAll: (): unknown[] => [],\r\n\t\thead: noopObj,\r\n\t\tbody: noopObj,\r\n\t\tdocumentElement: noopObj,\r\n\t\taddEventListener: noop,\r\n\t\tremoveEventListener: noop,\r\n\t\tdispatchEvent: noop,\r\n\t\tcookie: ''\r\n\t};\r\n\r\n\t// Minimal navigator stub\r\n\tconst navigatorStub = {\r\n\t\tuserAgent: 'node',\r\n\t\tplatform: process.platform,\r\n\t\tlanguage: 'en',\r\n\t\tlanguages: ['en']\r\n\t};\r\n\r\n\t// Fake localStorage — matches @uis/testing-bdd FakeLocalStorage\r\n\tconst localStorageStub = {\r\n\t\tstore: {} as Record<string, string>,\r\n\t\tclear(): void {\r\n\t\t\tthis.store = {};\r\n\t\t},\r\n\t\tgetItem(key: string): string | null {\r\n\t\t\treturn this.store[key] ?? null;\r\n\t\t},\r\n\t\tsetItem(key: string, value: string): void {\r\n\t\t\tthis.store[key] = value;\r\n\t\t},\r\n\t\tremoveItem(key: string): void {\r\n\t\t\tdelete this.store[key];\r\n\t\t},\r\n\t\tget length(): number {\r\n\t\t\treturn Object.keys(this.store).length;\r\n\t\t},\r\n\t\tkey(): null {\r\n\t\t\treturn null;\r\n\t\t}\r\n\t};\r\n\r\n\t// matchMedia stub — matches @uis/testing-bdd mock\r\n\tconst matchMediaStub = (query: string): Record<string, unknown> => ({\r\n\t\tmatches: false,\r\n\t\tmedia: query,\r\n\t\tonchange: null,\r\n\t\taddListener: noop,\r\n\t\tremoveListener: noop,\r\n\t\taddEventListener: noop,\r\n\t\tremoveEventListener: noop,\r\n\t\tdispatchEvent: noop\r\n\t});\r\n\r\n\t// Minimal Element prototype for scrollIntoView etc.\r\n\tclass HTMLElementStub {\r\n\t\tscrollIntoView = noop;\r\n\t\tgetBoundingClientRect = (): Record<string, number> => ({\r\n\t\t\ttop: 0,\r\n\t\t\tleft: 0,\r\n\t\t\tbottom: 0,\r\n\t\t\tright: 0,\r\n\t\t\twidth: 0,\r\n\t\t\theight: 0,\r\n\t\t\tx: 0,\r\n\t\t\ty: 0\r\n\t\t});\r\n\t}\r\n\tclass ElementStub extends HTMLElementStub {}\r\n\r\n\t// Minimal XMLSerializer stub\r\n\tclass XMLSerializerStub {\r\n\t\tserializeToString(): string {\r\n\t\t\treturn '';\r\n\t\t}\r\n\t}\r\n\r\n\t// Build the window shim\r\n\tconst windowShim: Record<string, unknown> = {};\r\n\tfor (const key of Object.getOwnPropertyNames(globalThis)) {\r\n\t\ttry {\r\n\t\t\t(windowShim as Record<string, unknown>)[key] = (globalThis as Record<string, unknown>)[key];\r\n\t\t} catch {\r\n\t\t\t// skip non-readable properties\r\n\t\t}\r\n\t}\r\n\r\n\twindowShim.location = locationStub;\r\n\twindowShim.document = documentStub;\r\n\twindowShim.navigator = navigatorStub;\r\n\twindowShim.localStorage = localStorageStub;\r\n\twindowShim.self = windowShim;\r\n\twindowShim.top = windowShim;\r\n\twindowShim.parent = windowShim;\r\n\twindowShim.addEventListener = noop;\r\n\twindowShim.removeEventListener = noop;\r\n\twindowShim.dispatchEvent = noop;\r\n\twindowShim.requestAnimationFrame = noop;\r\n\twindowShim.cancelAnimationFrame = noop;\r\n\twindowShim.getComputedStyle = () => noopObj;\r\n\twindowShim.matchMedia = matchMediaStub;\r\n\twindowShim.open = noop;\r\n\twindowShim.close = noop;\r\n\twindowShim.focus = noop;\r\n\twindowShim.blur = noop;\r\n\twindowShim.scroll = noop;\r\n\twindowShim.scrollTo = noop;\r\n\twindowShim.scrollBy = noop;\r\n\twindowShim.CustomEvent = class CustomEvent extends Event {};\r\n\twindowShim.HTMLElement = HTMLElementStub;\r\n\twindowShim.Element = ElementStub;\r\n\twindowShim.XMLSerializer = XMLSerializerStub;\r\n\r\n\t// Set globals on globalThis\r\n\tsafeDefine(globalThis, 'window', windowShim);\r\n\tsafeDefine(globalThis, 'document', documentStub);\r\n\tsafeDefine(globalThis, 'navigator', navigatorStub);\r\n\tsafeDefine(globalThis, 'location', locationStub);\r\n\tsafeDefine(globalThis, 'localStorage', localStorageStub);\r\n\tsafeDefine(globalThis, 'self', windowShim);\r\n\tsafeDefine(globalThis, 'matchMedia', matchMediaStub);\r\n\tsafeDefine(globalThis, 'XMLSerializer', XMLSerializerStub);\r\n}\r\n\r\nimport { parentPort, workerData } from 'node:worker_threads';\r\nimport { register } from 'node:module';\r\nimport { pathToFileURL } from 'node:url';\r\nimport 'polyfill-symbol-metadata';\r\n\r\n// Signal that we're in a loader-worker context so decorators skip Cucumber APIs\r\n(global as any).__LOADER_WORKER = true;\r\n\r\n/** Message types sent from main thread to worker */\r\nexport interface LoaderWorkerRequest {\r\n\ttype: 'LOAD';\r\n\t/** Absolute paths to load via require() */\r\n\trequirePaths: string[];\r\n\t/** Absolute paths to load via import() */\r\n\timportPaths: string[];\r\n\t/** Modules to require before support code (transpiler setup etc.) */\r\n\trequireModules: string[];\r\n\t/** ESM loaders to register before importing */\r\n\tloaders: string[];\r\n\t/** Whether to use experimental decorators */\r\n\texperimentalDecorators: boolean;\r\n}\r\n\r\n/** Message types sent from worker back to main thread */\r\nexport interface LoaderWorkerResponse {\r\n\ttype: 'LOADED' | 'ERROR';\r\n\t/** Serializable binding descriptors extracted from the registry */\r\n\tdescriptors?: import('../bindings/step-binding').SerializableBindingDescriptor[];\r\n\t/** Source files that were successfully loaded */\r\n\tloadedFiles?: string[];\r\n\t/** Error message if something went wrong */\r\n\terror?: string;\r\n\t/** Per-file errors that were caught but did not kill the worker */\r\n\tfileErrors?: string[];\r\n}\r\n\r\nasync function processMessage(message: LoaderWorkerRequest): Promise<void> {\r\n\tif (message.type !== 'LOAD') {\r\n\t\treturn;\r\n\t}\r\n\r\n\tconst fileErrors: string[] = [];\r\n\r\n\ttry {\r\n\t\t// Set the experimental decorators flag before loading support code\r\n\t\tglobal.experimentalDecorators = message.experimentalDecorators;\r\n\t\tprocess.env.CUCUMBER_EXPERIMENTAL_DECORATORS = String(message.experimentalDecorators);\r\n\r\n\t\t// Load require modules (transpiler setup) — these are critical, fail fast\r\n\t\tfor (const modulePath of message.requireModules) {\r\n\t\t\trequire(modulePath);\r\n\t\t}\r\n\r\n\t\t// Register ESM loaders — also critical for import phase\r\n\t\tfor (const specifier of message.loaders) {\r\n\t\t\tregister(specifier, pathToFileURL('./'));\r\n\t\t}\r\n\r\n\t\t// Load support files via require (CJS) — per-file isolation\r\n\t\tfor (const filePath of message.requirePaths) {\r\n\t\t\ttry {\r\n\t\t\t\trequire(filePath);\r\n\t\t\t} catch (err: any) {\r\n\t\t\t\tfileErrors.push(`CJS ${filePath}: ${err.message || String(err)}`);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// Load support files via import (ESM) — per-file isolation\r\n\t\tfor (const filePath of message.importPaths) {\r\n\t\t\ttry {\r\n\t\t\t\tawait import(pathToFileURL(filePath).toString());\r\n\t\t\t} catch (err: any) {\r\n\t\t\t\tfileErrors.push(`ESM ${filePath}: ${err.message || String(err)}`);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// Extract descriptors from the binding registry\r\n\t\tconst { BindingRegistry } = require('../bindings/binding-registry');\r\n\t\tconst registry = BindingRegistry.instance;\r\n\t\tconst descriptors = registry.toDescriptors();\r\n\t\tconst loadedFiles: string[] = Array.from(registry.getDescriptorSourceFiles());\r\n\r\n\t\tconst response: LoaderWorkerResponse = {\r\n\t\t\ttype: 'LOADED',\r\n\t\t\tdescriptors,\r\n\t\t\tloadedFiles,\r\n\t\t\tfileErrors: fileErrors.length > 0 ? fileErrors : undefined\r\n\t\t};\r\n\t\tparentPort!.postMessage(response);\r\n\t} catch (err: any) {\r\n\t\t// Critical failure (transpiler setup / loader registration / registry extraction)\r\n\t\tconst response: LoaderWorkerResponse = {\r\n\t\t\ttype: 'ERROR',\r\n\t\t\terror: err.message || String(err),\r\n\t\t\tfileErrors: fileErrors.length > 0 ? fileErrors : undefined\r\n\t\t};\r\n\t\tparentPort!.postMessage(response);\r\n\t}\r\n}\r\n\r\n// Handle initial workerData (if files are passed at construction time)\r\nif (workerData && workerData.type === 'LOAD') {\r\n\tprocessMessage(workerData as LoaderWorkerRequest);\r\n}\r\n\r\n// Handle messages posted after construction\r\nparentPort?.on('message', (msg: LoaderWorkerRequest) => processMessage(msg));\r\n"]}
@@ -64,6 +64,7 @@ async function parallelPreload(options) {
64
64
  const allDescriptors = [];
65
65
  const allFiles = new Set();
66
66
  let errors = 0;
67
+ let fileWarnings = 0;
67
68
  for (const result of results) {
68
69
  if (result.status === 'fulfilled') {
69
70
  const response = result.value;
@@ -74,10 +75,22 @@ async function parallelPreload(options) {
74
75
  if (response.loadedFiles) {
75
76
  response.loadedFiles.forEach(f => allFiles.add(f));
76
77
  }
78
+ // Per-file errors are non-fatal — the file just didn't contribute to the cache
79
+ if (response.fileErrors && response.fileErrors.length > 0) {
80
+ fileWarnings += response.fileErrors.length;
81
+ for (const fe of response.fileErrors) {
82
+ logger.checkpoint('File skipped during preload', { detail: fe });
83
+ }
84
+ }
77
85
  }
78
86
  else {
79
87
  errors++;
80
88
  logger.error('Worker reported error', new Error(response.error || 'Unknown worker error'));
89
+ if (response.fileErrors) {
90
+ for (const fe of response.fileErrors) {
91
+ logger.checkpoint('File skipped during preload', { detail: fe });
92
+ }
93
+ }
81
94
  }
82
95
  }
83
96
  else {
@@ -90,11 +103,15 @@ async function parallelPreload(options) {
90
103
  durationMs,
91
104
  descriptorCount: allDescriptors.length,
92
105
  fileCount: allFiles.size,
93
- errors
106
+ errors,
107
+ fileWarnings
94
108
  });
95
109
  if (errors > 0) {
96
110
  logger.checkpoint('Some workers failed — main thread will do full load as fallback');
97
111
  }
112
+ if (fileWarnings > 0) {
113
+ logger.checkpoint(`${fileWarnings} file(s) skipped during preload — these will be loaded by the main thread`);
114
+ }
98
115
  return {
99
116
  descriptors: allDescriptors,
100
117
  loadedFiles: Array.from(allFiles),
@@ -1 +1 @@
1
- {"version":3,"file":"parallel-loader.js","sourceRoot":"","sources":["../../src/api/parallel-loader.ts"],"names":[],"mappings":";;;;;AAmDA,0CAoFC;AAvID;;;;;;;;;;GAUG;AACH,6DAA6C;AAC7C,qCAA+C;AAC/C,0DAA6B;AAG7B,0DAAsD;AAEtD,MAAM,MAAM,GAAG,IAAA,4BAAY,EAAC,iBAAiB,CAAC,CAAC;AA0B/C;;;;;;GAMG;AACI,KAAK,UAAU,eAAe,CAAC,OAA4B;IACjE,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;IAEhC,MAAM,OAAO,GAAG,kBAAkB,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;IACxD,MAAM,CAAC,UAAU,CAAC,2BAA2B,EAAE;QAC9C,OAAO;QACP,YAAY,EAAE,OAAO,CAAC,YAAY,CAAC,MAAM;QACzC,WAAW,EAAE,OAAO,CAAC,WAAW,CAAC,MAAM;KACvC,CAAC,CAAC;IAEH,wDAAwD;IACxD,MAAM,aAAa,GAAG,eAAe,CAAC,OAAO,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;IACrE,MAAM,YAAY,GAAG,eAAe,CAAC,OAAO,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IAEnE,uDAAuD;IACvD,MAAM,YAAY,GAAG,mBAAmB,EAAE,CAAC;IAC3C,MAAM,CAAC,UAAU,CAAC,wBAAwB,EAAE,EAAE,YAAY,EAAE,CAAC,CAAC;IAE9D,iBAAiB;IACjB,MAAM,cAAc,GAAoC,EAAE,CAAC;IAE3D,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,EAAE,CAAC,EAAE,EAAE,CAAC;QAClC,kDAAkD;QAClD,IAAI,aAAa,CAAC,CAAC,CAAC,CAAC,MAAM,KAAK,CAAC,IAAI,YAAY,CAAC,CAAC,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACnE,SAAS;QACV,CAAC;QAED,MAAM,OAAO,GAAwB;YACpC,IAAI,EAAE,MAAM;YACZ,YAAY,EAAE,aAAa,CAAC,CAAC,CAAC;YAC9B,WAAW,EAAE,YAAY,CAAC,CAAC,CAAC;YAC5B,cAAc,EAAE,OAAO,CAAC,cAAc;YACtC,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,sBAAsB,EAAE,OAAO,CAAC,sBAAsB;SACtD,CAAC;QAEF,cAAc,CAAC,IAAI,CAAC,SAAS,CAAC,YAAY,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC;IAC1D,CAAC;IAED,oBAAoB;IACpB,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;IAEzD,kBAAkB;IAClB,MAAM,cAAc,GAAoC,EAAE,CAAC;IAC3D,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAC;IACnC,IAAI,MAAM,GAAG,CAAC,CAAC;IAEf,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC9B,IAAI,MAAM,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;YACnC,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC;YAC9B,IAAI,QAAQ,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBAChC,IAAI,QAAQ,CAAC,WAAW,EAAE,CAAC;oBAC1B,cAAc,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,WAAW,CAAC,CAAC;gBAC9C,CAAC;gBACD,IAAI,QAAQ,CAAC,WAAW,EAAE,CAAC;oBAC1B,QAAQ,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;gBACpD,CAAC;YACF,CAAC;iBAAM,CAAC;gBACP,MAAM,EAAE,CAAC;gBACT,MAAM,CAAC,KAAK,CAAC,uBAAuB,EAAE,IAAI,KAAK,CAAC,QAAQ,CAAC,KAAK,IAAI,sBAAsB,CAAC,CAAC,CAAC;YAC5F,CAAC;QACF,CAAC;aAAM,CAAC;YACP,MAAM,EAAE,CAAC;YACT,MAAM,CAAC,KAAK,CAAC,yBAAyB,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;QACxD,CAAC;IACF,CAAC;IAED,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,CAAC;IACzD,MAAM,CAAC,UAAU,CAAC,4BAA4B,EAAE;QAC/C,UAAU;QACV,eAAe,EAAE,cAAc,CAAC,MAAM;QACtC,SAAS,EAAE,QAAQ,CAAC,IAAI;QACxB,MAAM;KACN,CAAC,CAAC;IAEH,IAAI,MAAM,GAAG,CAAC,EAAE,CAAC;QAChB,MAAM,CAAC,UAAU,CAAC,iEAAiE,CAAC,CAAC;IACtF,CAAC;IAED,OAAO;QACN,WAAW,EAAE,cAAc;QAC3B,WAAW,EAAE,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC;QACjC,UAAU;KACV,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAS,SAAS,CAAC,YAAoB,EAAE,OAA4B,EAAE,KAAa;IACnF,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACtC,MAAM,MAAM,GAAG,IAAI,4BAAM,CAAC,YAAY,EAAE;YACvC,UAAU,EAAE,OAAO;SACnB,CAAC,CAAC;QAEH,IAAI,OAAO,GAAG,KAAK,CAAC;QAEpB,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,GAAyB,EAAE,EAAE;YAClD,IAAI,CAAC,OAAO,EAAE,CAAC;gBACd,OAAO,GAAG,IAAI,CAAC;gBACf,MAAM,CAAC,UAAU,CAAC,UAAU,KAAK,YAAY,EAAE,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;gBACnE,MAAM,CAAC,SAAS,EAAE,CAAC;gBACnB,OAAO,CAAC,GAAG,CAAC,CAAC;YACd,CAAC;QACF,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,CAAC,EAAE;YACxB,IAAI,CAAC,OAAO,EAAE,CAAC;gBACd,OAAO,GAAG,IAAI,CAAC;gBACf,MAAM,CAAC,KAAK,CAAC,UAAU,KAAK,QAAQ,EAAE,GAAG,CAAC,CAAC;gBAC3C,MAAM,CAAC,GAAG,CAAC,CAAC;YACb,CAAC;QACF,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE;YACxB,IAAI,CAAC,OAAO,EAAE,CAAC;gBACd,OAAO,GAAG,IAAI,CAAC;gBACf,IAAI,IAAI,KAAK,CAAC,EAAE,CAAC;oBAChB,MAAM,CAAC,IAAI,KAAK,CAAC,UAAU,KAAK,qBAAqB,IAAI,EAAE,CAAC,CAAC,CAAC;gBAC/D,CAAC;YACF,CAAC;QACF,CAAC,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,SAAS,kBAAkB,CAAC,WAA6B;IACxD,IAAI,OAAO,WAAW,KAAK,QAAQ,EAAE,CAAC;QACrC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC;IACjC,CAAC;IACD,yEAAyE;IACzE,OAAO,IAAI,CAAC,GAAG,CAAC,IAAA,8BAAoB,GAAE,EAAE,CAAC,CAAC,CAAC;AAC5C,CAAC;AAED;;GAEG;AACH,SAAS,eAAe,CAAI,KAAU,EAAE,OAAe;IACtD,MAAM,MAAM,GAAU,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,GAAQ,EAAE,CAAC,EAAE,CAAC,CAAC;IACrE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACvC,MAAM,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACpC,CAAC;IACD,OAAO,MAAM,CAAC;AACf,CAAC;AAED;;;GAGG;AACH,SAAS,mBAAmB;IAC3B,6DAA6D;IAC7D,MAAM,OAAO,GAAG,mBAAI,CAAC,OAAO,CAAC,SAAS,EAAE,kBAAkB,CAAC,CAAC;IAC5D,OAAO,OAAO,CAAC;AAChB,CAAC","sourcesContent":["/**\r\n * parallel-loader.ts\r\n *\r\n * Orchestrates parallel pre-warming of transpiler caches using worker_threads.\r\n * Each worker loads a subset of support-code files, warming the transpiler's\r\n * on-disk cache. After all workers finish, the main thread performs the\r\n * authoritative load (which hits warm caches and runs much faster).\r\n *\r\n * The descriptors returned by workers are used for validation — the main\r\n * thread's BindingRegistry is the canonical source of truth.\r\n */\r\nimport { Worker } from 'node:worker_threads';\r\nimport { availableParallelism } from 'node:os';\r\nimport path from 'node:path';\r\nimport type { LoaderWorkerRequest, LoaderWorkerResponse } from './loader-worker';\r\nimport type { SerializableBindingDescriptor } from '../bindings/step-binding';\r\nimport { createLogger } from '../utils/tsflow-logger';\r\n\r\nconst logger = createLogger('parallel-loader');\r\n\r\nexport interface ParallelLoadOptions {\r\n\t/** Absolute require-paths for CJS support files */\r\n\trequirePaths: string[];\r\n\t/** Absolute import-paths for ESM support files */\r\n\timportPaths: string[];\r\n\t/** Modules to require before support code (transpiler hooks) */\r\n\trequireModules: string[];\r\n\t/** ESM loaders to register */\r\n\tloaders: string[];\r\n\t/** Whether experimental decorators are enabled */\r\n\texperimentalDecorators: boolean;\r\n\t/** Number of threads (true = auto, number = explicit) */\r\n\tthreadCount: boolean | number;\r\n}\r\n\r\nexport interface ParallelLoadResult {\r\n\t/** All descriptors collected across workers (for validation) */\r\n\tdescriptors: SerializableBindingDescriptor[];\r\n\t/** Unique source files loaded across all workers */\r\n\tloadedFiles: string[];\r\n\t/** Time spent in parallel phase (ms) */\r\n\tdurationMs: number;\r\n}\r\n\r\n/**\r\n * Pre-warm transpiler caches by loading support files in parallel worker threads.\r\n *\r\n * Each worker receives a subset of files, loads them (which triggers transpilation),\r\n * and returns serializable binding descriptors. The transpiler cache on disk is now\r\n * warm for the main thread's subsequent load.\r\n */\r\nexport async function parallelPreload(options: ParallelLoadOptions): Promise<ParallelLoadResult> {\r\n\tconst start = performance.now();\r\n\r\n\tconst threads = resolveThreadCount(options.threadCount);\r\n\tlogger.checkpoint('Starting parallel preload', {\r\n\t\tthreads,\r\n\t\trequirePaths: options.requirePaths.length,\r\n\t\timportPaths: options.importPaths.length\r\n\t});\r\n\r\n\t// Split files across workers — round-robin distribution\r\n\tconst requireChunks = chunkRoundRobin(options.requirePaths, threads);\r\n\tconst importChunks = chunkRoundRobin(options.importPaths, threads);\r\n\r\n\t// Resolve the worker script path (compiled JS in lib/)\r\n\tconst workerScript = resolveWorkerScript();\r\n\tlogger.checkpoint('Worker script resolved', { workerScript });\r\n\r\n\t// Launch workers\r\n\tconst workerPromises: Promise<LoaderWorkerResponse>[] = [];\r\n\r\n\tfor (let i = 0; i < threads; i++) {\r\n\t\t// Only launch a worker if it has files to process\r\n\t\tif (requireChunks[i].length === 0 && importChunks[i].length === 0) {\r\n\t\t\tcontinue;\r\n\t\t}\r\n\r\n\t\tconst request: LoaderWorkerRequest = {\r\n\t\t\ttype: 'LOAD',\r\n\t\t\trequirePaths: requireChunks[i],\r\n\t\t\timportPaths: importChunks[i],\r\n\t\t\trequireModules: options.requireModules,\r\n\t\t\tloaders: options.loaders,\r\n\t\t\texperimentalDecorators: options.experimentalDecorators\r\n\t\t};\r\n\r\n\t\tworkerPromises.push(runWorker(workerScript, request, i));\r\n\t}\r\n\r\n\t// Await all workers\r\n\tconst results = await Promise.allSettled(workerPromises);\r\n\r\n\t// Collect results\r\n\tconst allDescriptors: SerializableBindingDescriptor[] = [];\r\n\tconst allFiles = new Set<string>();\r\n\tlet errors = 0;\r\n\r\n\tfor (const result of results) {\r\n\t\tif (result.status === 'fulfilled') {\r\n\t\t\tconst response = result.value;\r\n\t\t\tif (response.type === 'LOADED') {\r\n\t\t\t\tif (response.descriptors) {\r\n\t\t\t\t\tallDescriptors.push(...response.descriptors);\r\n\t\t\t\t}\r\n\t\t\t\tif (response.loadedFiles) {\r\n\t\t\t\t\tresponse.loadedFiles.forEach(f => allFiles.add(f));\r\n\t\t\t\t}\r\n\t\t\t} else {\r\n\t\t\t\terrors++;\r\n\t\t\t\tlogger.error('Worker reported error', new Error(response.error || 'Unknown worker error'));\r\n\t\t\t}\r\n\t\t} else {\r\n\t\t\terrors++;\r\n\t\t\tlogger.error('Worker promise rejected', result.reason);\r\n\t\t}\r\n\t}\r\n\r\n\tconst durationMs = Math.round(performance.now() - start);\r\n\tlogger.checkpoint('Parallel preload completed', {\r\n\t\tdurationMs,\r\n\t\tdescriptorCount: allDescriptors.length,\r\n\t\tfileCount: allFiles.size,\r\n\t\terrors\r\n\t});\r\n\r\n\tif (errors > 0) {\r\n\t\tlogger.checkpoint('Some workers failed — main thread will do full load as fallback');\r\n\t}\r\n\r\n\treturn {\r\n\t\tdescriptors: allDescriptors,\r\n\t\tloadedFiles: Array.from(allFiles),\r\n\t\tdurationMs\r\n\t};\r\n}\r\n\r\n/**\r\n * Run a single loader-worker and return its response.\r\n */\r\nfunction runWorker(workerScript: string, request: LoaderWorkerRequest, index: number): Promise<LoaderWorkerResponse> {\r\n\treturn new Promise((resolve, reject) => {\r\n\t\tconst worker = new Worker(workerScript, {\r\n\t\t\tworkerData: request\r\n\t\t});\r\n\r\n\t\tlet settled = false;\r\n\r\n\t\tworker.on('message', (msg: LoaderWorkerResponse) => {\r\n\t\t\tif (!settled) {\r\n\t\t\t\tsettled = true;\r\n\t\t\t\tlogger.checkpoint(`Worker ${index} completed`, { type: msg.type });\r\n\t\t\t\tworker.terminate();\r\n\t\t\t\tresolve(msg);\r\n\t\t\t}\r\n\t\t});\r\n\r\n\t\tworker.on('error', err => {\r\n\t\t\tif (!settled) {\r\n\t\t\t\tsettled = true;\r\n\t\t\t\tlogger.error(`Worker ${index} error`, err);\r\n\t\t\t\treject(err);\r\n\t\t\t}\r\n\t\t});\r\n\r\n\t\tworker.on('exit', code => {\r\n\t\t\tif (!settled) {\r\n\t\t\t\tsettled = true;\r\n\t\t\t\tif (code !== 0) {\r\n\t\t\t\t\treject(new Error(`Worker ${index} exited with code ${code}`));\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t});\r\n\t});\r\n}\r\n\r\n/**\r\n * Resolve the number of threads to use.\r\n * - true: use availableParallelism() capped at 4\r\n * - number: use that exact count (min 1)\r\n */\r\nfunction resolveThreadCount(threadCount: boolean | number): number {\r\n\tif (typeof threadCount === 'number') {\r\n\t\treturn Math.max(1, threadCount);\r\n\t}\r\n\t// Auto-detect: use available parallelism, capped at a reasonable default\r\n\treturn Math.min(availableParallelism(), 4);\r\n}\r\n\r\n/**\r\n * Distribute items round-robin across N buckets.\r\n */\r\nfunction chunkRoundRobin<T>(items: T[], buckets: number): T[][] {\r\n\tconst chunks: T[][] = Array.from({ length: buckets }, (): T[] => []);\r\n\tfor (let i = 0; i < items.length; i++) {\r\n\t\tchunks[i % buckets].push(items[i]);\r\n\t}\r\n\treturn chunks;\r\n}\r\n\r\n/**\r\n * Resolve the path to the compiled loader-worker script.\r\n * In development (src), this file is adjacent; in production (lib/), it's compiled.\r\n */\r\nfunction resolveWorkerScript(): string {\r\n\t// Try the compiled lib path first, falling back to __dirname\r\n\tconst libPath = path.resolve(__dirname, 'loader-worker.js');\r\n\treturn libPath;\r\n}\r\n"]}
1
+ {"version":3,"file":"parallel-loader.js","sourceRoot":"","sources":["../../src/api/parallel-loader.ts"],"names":[],"mappings":"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","sourcesContent":["/**\r\n * parallel-loader.ts\r\n *\r\n * Orchestrates parallel pre-warming of transpiler caches using worker_threads.\r\n * Each worker loads a subset of support-code files, warming the transpiler's\r\n * on-disk cache. After all workers finish, the main thread performs the\r\n * authoritative load (which hits warm caches and runs much faster).\r\n *\r\n * The descriptors returned by workers are used for validation — the main\r\n * thread's BindingRegistry is the canonical source of truth.\r\n */\r\nimport { Worker } from 'node:worker_threads';\r\nimport { availableParallelism } from 'node:os';\r\nimport path from 'node:path';\r\nimport type { LoaderWorkerRequest, LoaderWorkerResponse } from './loader-worker';\r\nimport type { SerializableBindingDescriptor } from '../bindings/step-binding';\r\nimport { createLogger } from '../utils/tsflow-logger';\r\n\r\nconst logger = createLogger('parallel-loader');\r\n\r\nexport interface ParallelLoadOptions {\r\n\t/** Absolute require-paths for CJS support files */\r\n\trequirePaths: string[];\r\n\t/** Absolute import-paths for ESM support files */\r\n\timportPaths: string[];\r\n\t/** Modules to require before support code (transpiler hooks) */\r\n\trequireModules: string[];\r\n\t/** ESM loaders to register */\r\n\tloaders: string[];\r\n\t/** Whether experimental decorators are enabled */\r\n\texperimentalDecorators: boolean;\r\n\t/** Number of threads (true = auto, number = explicit) */\r\n\tthreadCount: boolean | number;\r\n}\r\n\r\nexport interface ParallelLoadResult {\r\n\t/** All descriptors collected across workers (for validation) */\r\n\tdescriptors: SerializableBindingDescriptor[];\r\n\t/** Unique source files loaded across all workers */\r\n\tloadedFiles: string[];\r\n\t/** Time spent in parallel phase (ms) */\r\n\tdurationMs: number;\r\n}\r\n\r\n/**\r\n * Pre-warm transpiler caches by loading support files in parallel worker threads.\r\n *\r\n * Each worker receives a subset of files, loads them (which triggers transpilation),\r\n * and returns serializable binding descriptors. The transpiler cache on disk is now\r\n * warm for the main thread's subsequent load.\r\n */\r\nexport async function parallelPreload(options: ParallelLoadOptions): Promise<ParallelLoadResult> {\r\n\tconst start = performance.now();\r\n\r\n\tconst threads = resolveThreadCount(options.threadCount);\r\n\tlogger.checkpoint('Starting parallel preload', {\r\n\t\tthreads,\r\n\t\trequirePaths: options.requirePaths.length,\r\n\t\timportPaths: options.importPaths.length\r\n\t});\r\n\r\n\t// Split files across workers — round-robin distribution\r\n\tconst requireChunks = chunkRoundRobin(options.requirePaths, threads);\r\n\tconst importChunks = chunkRoundRobin(options.importPaths, threads);\r\n\r\n\t// Resolve the worker script path (compiled JS in lib/)\r\n\tconst workerScript = resolveWorkerScript();\r\n\tlogger.checkpoint('Worker script resolved', { workerScript });\r\n\r\n\t// Launch workers\r\n\tconst workerPromises: Promise<LoaderWorkerResponse>[] = [];\r\n\r\n\tfor (let i = 0; i < threads; i++) {\r\n\t\t// Only launch a worker if it has files to process\r\n\t\tif (requireChunks[i].length === 0 && importChunks[i].length === 0) {\r\n\t\t\tcontinue;\r\n\t\t}\r\n\r\n\t\tconst request: LoaderWorkerRequest = {\r\n\t\t\ttype: 'LOAD',\r\n\t\t\trequirePaths: requireChunks[i],\r\n\t\t\timportPaths: importChunks[i],\r\n\t\t\trequireModules: options.requireModules,\r\n\t\t\tloaders: options.loaders,\r\n\t\t\texperimentalDecorators: options.experimentalDecorators\r\n\t\t};\r\n\r\n\t\tworkerPromises.push(runWorker(workerScript, request, i));\r\n\t}\r\n\r\n\t// Await all workers\r\n\tconst results = await Promise.allSettled(workerPromises);\r\n\r\n\t// Collect results\r\n\tconst allDescriptors: SerializableBindingDescriptor[] = [];\r\n\tconst allFiles = new Set<string>();\r\n\tlet errors = 0;\r\n\tlet fileWarnings = 0;\r\n\r\n\tfor (const result of results) {\r\n\t\tif (result.status === 'fulfilled') {\r\n\t\t\tconst response = result.value;\r\n\t\t\tif (response.type === 'LOADED') {\r\n\t\t\t\tif (response.descriptors) {\r\n\t\t\t\t\tallDescriptors.push(...response.descriptors);\r\n\t\t\t\t}\r\n\t\t\t\tif (response.loadedFiles) {\r\n\t\t\t\t\tresponse.loadedFiles.forEach(f => allFiles.add(f));\r\n\t\t\t\t}\r\n\t\t\t\t// Per-file errors are non-fatal — the file just didn't contribute to the cache\r\n\t\t\t\tif (response.fileErrors && response.fileErrors.length > 0) {\r\n\t\t\t\t\tfileWarnings += response.fileErrors.length;\r\n\t\t\t\t\tfor (const fe of response.fileErrors) {\r\n\t\t\t\t\t\tlogger.checkpoint('File skipped during preload', { detail: fe });\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t} else {\r\n\t\t\t\terrors++;\r\n\t\t\t\tlogger.error('Worker reported error', new Error(response.error || 'Unknown worker error'));\r\n\t\t\t\tif (response.fileErrors) {\r\n\t\t\t\t\tfor (const fe of response.fileErrors) {\r\n\t\t\t\t\t\tlogger.checkpoint('File skipped during preload', { detail: fe });\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t} else {\r\n\t\t\terrors++;\r\n\t\t\tlogger.error('Worker promise rejected', result.reason);\r\n\t\t}\r\n\t}\r\n\r\n\tconst durationMs = Math.round(performance.now() - start);\r\n\tlogger.checkpoint('Parallel preload completed', {\r\n\t\tdurationMs,\r\n\t\tdescriptorCount: allDescriptors.length,\r\n\t\tfileCount: allFiles.size,\r\n\t\terrors,\r\n\t\tfileWarnings\r\n\t});\r\n\r\n\tif (errors > 0) {\r\n\t\tlogger.checkpoint('Some workers failed — main thread will do full load as fallback');\r\n\t}\r\n\tif (fileWarnings > 0) {\r\n\t\tlogger.checkpoint(`${fileWarnings} file(s) skipped during preload — these will be loaded by the main thread`);\r\n\t}\r\n\r\n\treturn {\r\n\t\tdescriptors: allDescriptors,\r\n\t\tloadedFiles: Array.from(allFiles),\r\n\t\tdurationMs\r\n\t};\r\n}\r\n\r\n/**\r\n * Run a single loader-worker and return its response.\r\n */\r\nfunction runWorker(workerScript: string, request: LoaderWorkerRequest, index: number): Promise<LoaderWorkerResponse> {\r\n\treturn new Promise((resolve, reject) => {\r\n\t\tconst worker = new Worker(workerScript, {\r\n\t\t\tworkerData: request\r\n\t\t});\r\n\r\n\t\tlet settled = false;\r\n\r\n\t\tworker.on('message', (msg: LoaderWorkerResponse) => {\r\n\t\t\tif (!settled) {\r\n\t\t\t\tsettled = true;\r\n\t\t\t\tlogger.checkpoint(`Worker ${index} completed`, { type: msg.type });\r\n\t\t\t\tworker.terminate();\r\n\t\t\t\tresolve(msg);\r\n\t\t\t}\r\n\t\t});\r\n\r\n\t\tworker.on('error', err => {\r\n\t\t\tif (!settled) {\r\n\t\t\t\tsettled = true;\r\n\t\t\t\tlogger.error(`Worker ${index} error`, err);\r\n\t\t\t\treject(err);\r\n\t\t\t}\r\n\t\t});\r\n\r\n\t\tworker.on('exit', code => {\r\n\t\t\tif (!settled) {\r\n\t\t\t\tsettled = true;\r\n\t\t\t\tif (code !== 0) {\r\n\t\t\t\t\treject(new Error(`Worker ${index} exited with code ${code}`));\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t});\r\n\t});\r\n}\r\n\r\n/**\r\n * Resolve the number of threads to use.\r\n * - true: use availableParallelism() capped at 4\r\n * - number: use that exact count (min 1)\r\n */\r\nfunction resolveThreadCount(threadCount: boolean | number): number {\r\n\tif (typeof threadCount === 'number') {\r\n\t\treturn Math.max(1, threadCount);\r\n\t}\r\n\t// Auto-detect: use available parallelism, capped at a reasonable default\r\n\treturn Math.min(availableParallelism(), 4);\r\n}\r\n\r\n/**\r\n * Distribute items round-robin across N buckets.\r\n */\r\nfunction chunkRoundRobin<T>(items: T[], buckets: number): T[][] {\r\n\tconst chunks: T[][] = Array.from({ length: buckets }, (): T[] => []);\r\n\tfor (let i = 0; i < items.length; i++) {\r\n\t\tchunks[i % buckets].push(items[i]);\r\n\t}\r\n\treturn chunks;\r\n}\r\n\r\n/**\r\n * Resolve the path to the compiled loader-worker script.\r\n * In development (src), this file is adjacent; in production (lib/), it's compiled.\r\n */\r\nfunction resolveWorkerScript(): string {\r\n\t// Try the compiled lib path first, falling back to __dirname\r\n\tconst libPath = path.resolve(__dirname, 'loader-worker.js');\r\n\treturn libPath;\r\n}\r\n"]}
package/lib/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const version = "7.7.1";
1
+ export declare const version = "7.7.2";
package/lib/version.js CHANGED
@@ -2,5 +2,5 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.version = void 0;
4
4
  // Generated by genversion.
5
- exports.version = '7.7.1';
5
+ exports.version = '7.7.2';
6
6
  //# sourceMappingURL=version.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":";;;AAAA,2BAA2B;AACd,QAAA,OAAO,GAAG,OAAO,CAAA","sourcesContent":["// Generated by genversion.\nexport const version = '7.7.1'\n"]}
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":";;;AAAA,2BAA2B;AACd,QAAA,OAAO,GAAG,OAAO,CAAA","sourcesContent":["// Generated by genversion.\nexport const version = '7.7.2'\n"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@lynxwall/cucumber-tsflow",
3
3
  "description": "Provides 'specflow' like bindings for CucumberJS 12.7.0 in TypeScript 5.9+.",
4
- "version": "7.7.1",
4
+ "version": "7.7.2",
5
5
  "author": "Lonnie Wall <lynxdev@lynxwall.com>",
6
6
  "license": "MIT",
7
7
  "keywords": [