nvent 0.5.15 → 1.0.0-alpha.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.
Files changed (149) hide show
  1. package/dist/module.d.mts +181 -8
  2. package/dist/module.json +1 -1
  3. package/dist/module.mjs +1257 -1196
  4. package/dist/runtime/app/composables/useFunctionCall.d.ts +17 -0
  5. package/dist/runtime/app/composables/useFunctionCall.js +25 -0
  6. package/dist/runtime/app/composables/useNventStream.d.ts +23 -0
  7. package/dist/runtime/app/composables/useNventStream.js +58 -0
  8. package/dist/runtime/nitro/plugins/00.iii-lifecycle.d.ts +19 -0
  9. package/dist/runtime/nitro/plugins/00.iii-lifecycle.js +62 -0
  10. package/dist/runtime/nitro/plugins/01.iii-worker.d.ts +15 -0
  11. package/dist/runtime/nitro/plugins/01.iii-worker.js +91 -0
  12. package/dist/runtime/nitro/routes/stream-proxy.d.ts +17 -0
  13. package/dist/runtime/nitro/routes/stream-proxy.js +45 -0
  14. package/dist/runtime/nitro/utils/console.d.ts +32 -0
  15. package/dist/runtime/nitro/utils/console.js +95 -0
  16. package/dist/runtime/nitro/utils/defineFunction.d.ts +378 -3
  17. package/dist/runtime/nitro/utils/defineFunction.js +224 -4
  18. package/dist/runtime/nitro/utils/engine.d.ts +25 -0
  19. package/dist/runtime/nitro/utils/engine.js +130 -0
  20. package/dist/runtime/nitro/utils/nventDir.d.ts +1 -0
  21. package/dist/runtime/nitro/utils/nventDir.js +20 -0
  22. package/dist/runtime/nitro/utils/useIii.d.ts +23 -0
  23. package/dist/runtime/nitro/utils/useIii.js +14 -0
  24. package/dist/runtime/nitro/utils/workers/node.d.ts +24 -0
  25. package/dist/runtime/nitro/utils/workers/node.js +57 -0
  26. package/dist/runtime/nitro/utils/workers/python.d.ts +67 -0
  27. package/dist/runtime/nitro/utils/workers/python.js +220 -0
  28. package/dist/runtime/python/nvent.py +208 -0
  29. package/dist/runtime/python/worker_runtime.py +659 -0
  30. package/dist/runtime/virtual.d.ts +17 -0
  31. package/dist/types.d.mts +5 -1
  32. package/package.json +11 -14
  33. package/dist/runtime/adapters/base/index.d.ts +0 -6
  34. package/dist/runtime/adapters/base/index.js +0 -1
  35. package/dist/runtime/adapters/base/store-validator.d.ts +0 -48
  36. package/dist/runtime/adapters/base/store-validator.js +0 -147
  37. package/dist/runtime/adapters/builtin/file-queue.d.ts +0 -67
  38. package/dist/runtime/adapters/builtin/file-queue.js +0 -499
  39. package/dist/runtime/adapters/builtin/file-store.d.ts +0 -32
  40. package/dist/runtime/adapters/builtin/file-store.js +0 -206
  41. package/dist/runtime/adapters/builtin/file-stream.d.ts +0 -39
  42. package/dist/runtime/adapters/builtin/file-stream.js +0 -56
  43. package/dist/runtime/adapters/builtin/index.d.ts +0 -10
  44. package/dist/runtime/adapters/builtin/index.js +0 -5
  45. package/dist/runtime/adapters/builtin/memory-queue.d.ts +0 -52
  46. package/dist/runtime/adapters/builtin/memory-queue.js +0 -243
  47. package/dist/runtime/adapters/builtin/memory-store.d.ts +0 -69
  48. package/dist/runtime/adapters/builtin/memory-store.js +0 -357
  49. package/dist/runtime/adapters/builtin/memory-stream.d.ts +0 -21
  50. package/dist/runtime/adapters/builtin/memory-stream.js +0 -56
  51. package/dist/runtime/adapters/factory.d.ts +0 -31
  52. package/dist/runtime/adapters/factory.js +0 -135
  53. package/dist/runtime/adapters/index.d.ts +0 -8
  54. package/dist/runtime/adapters/index.js +0 -3
  55. package/dist/runtime/adapters/interfaces/index.d.ts +0 -11
  56. package/dist/runtime/adapters/interfaces/index.js +0 -3
  57. package/dist/runtime/adapters/interfaces/queue.d.ts +0 -155
  58. package/dist/runtime/adapters/interfaces/queue.js +0 -0
  59. package/dist/runtime/adapters/interfaces/store.d.ts +0 -299
  60. package/dist/runtime/adapters/interfaces/store.js +0 -0
  61. package/dist/runtime/adapters/interfaces/stream.d.ts +0 -62
  62. package/dist/runtime/adapters/interfaces/stream.js +0 -0
  63. package/dist/runtime/adapters/registry.d.ts +0 -85
  64. package/dist/runtime/adapters/registry.js +0 -161
  65. package/dist/runtime/config/index.d.ts +0 -23
  66. package/dist/runtime/config/index.js +0 -200
  67. package/dist/runtime/config/types.d.ts +0 -439
  68. package/dist/runtime/config/types.js +0 -0
  69. package/dist/runtime/events/eventBus.d.ts +0 -20
  70. package/dist/runtime/events/eventBus.js +0 -35
  71. package/dist/runtime/events/types.d.ts +0 -144
  72. package/dist/runtime/events/types.js +0 -0
  73. package/dist/runtime/events/utils/scheduleTrigger.d.ts +0 -8
  74. package/dist/runtime/events/utils/scheduleTrigger.js +0 -71
  75. package/dist/runtime/events/utils/stallDetector.d.ts +0 -83
  76. package/dist/runtime/events/utils/stallDetector.js +0 -318
  77. package/dist/runtime/events/utils/triggerRuntime.d.ts +0 -58
  78. package/dist/runtime/events/utils/triggerRuntime.js +0 -212
  79. package/dist/runtime/events/wiring/flowWiring.d.ts +0 -34
  80. package/dist/runtime/events/wiring/flowWiring.js +0 -1258
  81. package/dist/runtime/events/wiring/registry.d.ts +0 -19
  82. package/dist/runtime/events/wiring/registry.js +0 -43
  83. package/dist/runtime/events/wiring/stateWiring.d.ts +0 -37
  84. package/dist/runtime/events/wiring/stateWiring.js +0 -92
  85. package/dist/runtime/events/wiring/streamWiring.d.ts +0 -36
  86. package/dist/runtime/events/wiring/streamWiring.js +0 -156
  87. package/dist/runtime/events/wiring/triggerWiring.d.ts +0 -21
  88. package/dist/runtime/events/wiring/triggerWiring.js +0 -422
  89. package/dist/runtime/nitro/plugins/00.adapters.d.ts +0 -14
  90. package/dist/runtime/nitro/plugins/00.adapters.js +0 -73
  91. package/dist/runtime/nitro/plugins/01.ws-lifecycle.d.ts +0 -5
  92. package/dist/runtime/nitro/plugins/01.ws-lifecycle.js +0 -69
  93. package/dist/runtime/nitro/plugins/02.workers.d.ts +0 -2
  94. package/dist/runtime/nitro/plugins/02.workers.js +0 -92
  95. package/dist/runtime/nitro/plugins/03.triggers.d.ts +0 -12
  96. package/dist/runtime/nitro/plugins/03.triggers.js +0 -55
  97. package/dist/runtime/nitro/routes/webhook.await.d.ts +0 -23
  98. package/dist/runtime/nitro/routes/webhook.await.js +0 -112
  99. package/dist/runtime/nitro/routes/webhook.trigger.d.ts +0 -86
  100. package/dist/runtime/nitro/routes/webhook.trigger.js +0 -73
  101. package/dist/runtime/nitro/utils/adapters.d.ts +0 -66
  102. package/dist/runtime/nitro/utils/adapters.js +0 -51
  103. package/dist/runtime/nitro/utils/awaitPatterns/event.d.ts +0 -15
  104. package/dist/runtime/nitro/utils/awaitPatterns/event.js +0 -128
  105. package/dist/runtime/nitro/utils/awaitPatterns/index.d.ts +0 -28
  106. package/dist/runtime/nitro/utils/awaitPatterns/index.js +0 -55
  107. package/dist/runtime/nitro/utils/awaitPatterns/schedule.d.ts +0 -16
  108. package/dist/runtime/nitro/utils/awaitPatterns/schedule.js +0 -83
  109. package/dist/runtime/nitro/utils/awaitPatterns/time.d.ts +0 -15
  110. package/dist/runtime/nitro/utils/awaitPatterns/time.js +0 -71
  111. package/dist/runtime/nitro/utils/awaitPatterns/webhook.d.ts +0 -15
  112. package/dist/runtime/nitro/utils/awaitPatterns/webhook.js +0 -128
  113. package/dist/runtime/nitro/utils/defineFunctionConfig.d.ts +0 -378
  114. package/dist/runtime/nitro/utils/defineFunctionConfig.js +0 -3
  115. package/dist/runtime/nitro/utils/defineHooks.d.ts +0 -95
  116. package/dist/runtime/nitro/utils/defineHooks.js +0 -9
  117. package/dist/runtime/nitro/utils/registerAdapter.d.ts +0 -59
  118. package/dist/runtime/nitro/utils/registerAdapter.js +0 -13
  119. package/dist/runtime/nitro/utils/useAwait.d.ts +0 -83
  120. package/dist/runtime/nitro/utils/useAwait.js +0 -169
  121. package/dist/runtime/nitro/utils/useEventManager.d.ts +0 -15
  122. package/dist/runtime/nitro/utils/useEventManager.js +0 -26
  123. package/dist/runtime/nitro/utils/useFlow.d.ts +0 -66
  124. package/dist/runtime/nitro/utils/useFlow.js +0 -310
  125. package/dist/runtime/nitro/utils/useHookRegistry.d.ts +0 -40
  126. package/dist/runtime/nitro/utils/useHookRegistry.js +0 -25
  127. package/dist/runtime/nitro/utils/useNventLogger.d.ts +0 -42
  128. package/dist/runtime/nitro/utils/useNventLogger.js +0 -54
  129. package/dist/runtime/nitro/utils/useRunContext.d.ts +0 -6
  130. package/dist/runtime/nitro/utils/useRunContext.js +0 -102
  131. package/dist/runtime/nitro/utils/useStreamTopics.d.ts +0 -83
  132. package/dist/runtime/nitro/utils/useStreamTopics.js +0 -94
  133. package/dist/runtime/nitro/utils/useTrigger.d.ts +0 -150
  134. package/dist/runtime/nitro/utils/useTrigger.js +0 -311
  135. package/dist/runtime/nitro/utils/wsPeerManager.d.ts +0 -44
  136. package/dist/runtime/nitro/utils/wsPeerManager.js +0 -32
  137. package/dist/runtime/scheduler/index.d.ts +0 -33
  138. package/dist/runtime/scheduler/index.js +0 -42
  139. package/dist/runtime/scheduler/scheduler.d.ts +0 -132
  140. package/dist/runtime/scheduler/scheduler.js +0 -799
  141. package/dist/runtime/scheduler/types.d.ts +0 -122
  142. package/dist/runtime/scheduler/types.js +0 -0
  143. package/dist/runtime/worker/node/runner.d.ts +0 -95
  144. package/dist/runtime/worker/node/runner.js +0 -272
  145. package/dist/runtime/worker/python/get_config.py +0 -64
  146. package/dist/runtime/worker/system/awaitHandlers.d.ts +0 -27
  147. package/dist/runtime/worker/system/awaitHandlers.js +0 -230
  148. package/dist/runtime/worker/system/index.d.ts +0 -24
  149. package/dist/runtime/worker/system/index.js +0 -39
package/dist/module.mjs CHANGED
@@ -1,1169 +1,1140 @@
1
- import { dirname, join, extname, relative } from 'node:path';
2
- import { useLogger, defineNuxtModule, createResolver, addTemplate, addTypeTemplate, addServerPlugin, addServerHandler, addServerImports, updateTemplates } from '@nuxt/kit';
3
- import defu from 'defu';
4
- import { existsSync, realpathSync, readFileSync } from 'node:fs';
1
+ import { join, dirname, relative, basename } from 'node:path';
2
+ import { defineNuxtModule, createResolver, hasNuxtModule, addTemplate, addServerPlugin, addServerHandler, addServerImports, addImports, updateTemplates } from '@nuxt/kit';
3
+ import { existsSync, mkdirSync, chmodSync, createWriteStream, unlinkSync, writeFileSync, readFileSync, copyFileSync } from 'node:fs';
4
+ import { exec, execFileSync, spawn } from 'node:child_process';
5
+ import { promisify } from 'node:util';
6
+ import { pipeline } from 'node:stream/promises';
7
+ import { stringifyYAML } from 'confbox';
5
8
  import { globby } from 'globby';
6
- import { pathToFileURL, fileURLToPath } from 'node:url';
7
- import { readFile } from 'node:fs/promises';
8
- import { parseModule } from 'magicast';
9
- import { spawn } from 'node:child_process';
9
+ import { genString } from 'knitwork';
10
+ import { PythonWorkersOrchestrator } from '../dist/runtime/nitro/utils/workers/python.js';
11
+ import { createEngineManager } from '../dist/runtime/nitro/utils/engine.js';
12
+ import { ConsoleManager } from '../dist/runtime/nitro/utils/console.js';
10
13
  import chokidar from 'chokidar';
11
- import { join as join$1 } from 'pathe';
12
14
  import { debounce } from 'perfect-debounce';
13
- import { normalizeModuleOptions, getRedisStorageConfig, toRuntimeConfig } from '../dist/runtime/config/index.js';
14
15
 
15
- async function loadJsConfig(absPath) {
16
- const cacheBust = `?t=${Date.now()}`;
17
- const mod = await import(pathToFileURL(absPath).href + cacheBust);
18
- const cfg = mod?.config;
19
- const queueName = cfg && typeof cfg.queue === "object" && cfg.queue ? cfg.queue?.name : void 0;
20
- const isolate = cfg?.runner?.ts?.isolate || cfg?.runner?.isolate || cfg?.isolate;
21
- const runtype = isolate === "task" ? "task" : isolate === "inprocess" ? "inprocess" : void 0;
22
- const flowCfg = cfg?.flow;
23
- let flow;
24
- if (flowCfg) {
25
- const subscribes = Array.isArray(flowCfg.subscribes) ? flowCfg.subscribes : typeof flowCfg.subscribes === "string" ? [flowCfg.subscribes] : void 0;
26
- const names = Array.isArray(flowCfg.name) ? flowCfg.name.filter((s) => typeof s === "string" && s.length > 0) : typeof flowCfg.name === "string" && flowCfg.name.length > 0 ? [flowCfg.name] : [];
27
- if (names.length) {
28
- flow = {
29
- names,
30
- role: flowCfg.role,
31
- step: flowCfg.step,
32
- emits: flowCfg.emits,
33
- subscribes,
34
- triggers: flowCfg.triggers,
35
- stepTimeout: flowCfg.stepTimeout,
36
- awaitBefore: flowCfg.awaitBefore,
37
- awaitAfter: flowCfg.awaitAfter
38
- };
39
- }
16
+ const LogLevels = {
17
+ fatal: 0,
18
+ error: 0,
19
+ warn: 1,
20
+ log: 2,
21
+ info: 3,
22
+ success: 3,
23
+ fail: 3,
24
+ debug: 4,
25
+ trace: 5,
26
+ verbose: Number.POSITIVE_INFINITY
27
+ };
28
+ const LogTypes = {
29
+ // Silent
30
+ silent: {
31
+ level: -1
32
+ },
33
+ // Level 0
34
+ fatal: {
35
+ level: LogLevels.fatal
36
+ },
37
+ error: {
38
+ level: LogLevels.error
39
+ },
40
+ // Level 1
41
+ warn: {
42
+ level: LogLevels.warn
43
+ },
44
+ // Level 2
45
+ log: {
46
+ level: LogLevels.log
47
+ },
48
+ // Level 3
49
+ info: {
50
+ level: LogLevels.info
51
+ },
52
+ success: {
53
+ level: LogLevels.success
54
+ },
55
+ fail: {
56
+ level: LogLevels.fail
57
+ },
58
+ ready: {
59
+ level: LogLevels.info
60
+ },
61
+ start: {
62
+ level: LogLevels.info
63
+ },
64
+ box: {
65
+ level: LogLevels.info
66
+ },
67
+ // Level 4
68
+ debug: {
69
+ level: LogLevels.debug
70
+ },
71
+ // Level 5
72
+ trace: {
73
+ level: LogLevels.trace
74
+ },
75
+ // Verbose
76
+ verbose: {
77
+ level: LogLevels.verbose
78
+ }
79
+ };
80
+
81
+ function isPlainObject$1(value) {
82
+ if (value === null || typeof value !== "object") {
83
+ return false;
40
84
  }
41
- const queueCfg = cfg?.queue && typeof cfg.queue === "object" ? {
42
- name: cfg.queue.name,
43
- defaultJobOptions: cfg.queue.defaultJobOptions,
44
- prefix: cfg.queue.prefix,
45
- limiter: cfg.queue.limiter
46
- } : void 0;
47
- const workerCfg = cfg?.worker && typeof cfg.worker === "object" ? { ...cfg.worker } : void 0;
48
- const hasDefaultExport = !!(mod && mod.default);
49
- const hasHooks = !!(mod && typeof mod.onAwaitRegister === "function" || mod && typeof mod.onAwaitResolve === "function" || mod && typeof mod.onAwaitTimeout === "function");
50
- return { queueName, flow, runtype, queue: queueCfg, worker: workerCfg, hasDefaultExport, hasHooks };
85
+ const prototype = Object.getPrototypeOf(value);
86
+ if (prototype !== null && prototype !== Object.prototype && Object.getPrototypeOf(prototype) !== null) {
87
+ return false;
88
+ }
89
+ if (Symbol.iterator in value) {
90
+ return false;
91
+ }
92
+ if (Symbol.toStringTag in value) {
93
+ return Object.prototype.toString.call(value) === "[object Module]";
94
+ }
95
+ return true;
51
96
  }
52
97
 
53
- async function loadTsConfig(absPath) {
54
- try {
55
- const source = await readFile(absPath, "utf-8");
56
- const mod = parseModule(source);
57
- const hasDefaultExport = !!mod.exports.default;
58
- const configExport = mod.exports.config;
59
- if (!configExport) {
60
- return { hasDefaultExport };
98
+ function _defu(baseObject, defaults, namespace = ".", merger) {
99
+ if (!isPlainObject$1(defaults)) {
100
+ return _defu(baseObject, {}, namespace);
101
+ }
102
+ const object = Object.assign({}, defaults);
103
+ for (const key in baseObject) {
104
+ if (key === "__proto__" || key === "constructor") {
105
+ continue;
61
106
  }
62
- const cfg = extractConfigValue(configExport);
63
- const queueName = cfg && typeof cfg.queue === "object" && cfg.queue ? cfg.queue?.name : void 0;
64
- const isolate = cfg?.runner?.ts?.isolate || cfg?.runner?.isolate || cfg?.isolate;
65
- const runtype = isolate === "task" ? "task" : isolate === "inprocess" ? "inprocess" : void 0;
66
- const flowCfg = cfg?.flow;
67
- let flow;
68
- if (flowCfg) {
69
- const subscribes = Array.isArray(flowCfg.subscribes) ? flowCfg.subscribes : typeof flowCfg.subscribes === "string" ? [flowCfg.subscribes] : void 0;
70
- const names = Array.isArray(flowCfg.name) ? flowCfg.name.filter((s) => typeof s === "string" && s.length > 0) : typeof flowCfg.name === "string" && flowCfg.name.length > 0 ? [flowCfg.name] : [];
71
- if (names.length) {
72
- flow = {
73
- names,
74
- role: flowCfg.role,
75
- step: flowCfg.step,
76
- emits: flowCfg.emits,
77
- subscribes,
78
- triggers: flowCfg.triggers,
79
- stepTimeout: flowCfg.stepTimeout,
80
- awaitBefore: flowCfg.awaitBefore,
81
- awaitAfter: flowCfg.awaitAfter
82
- };
83
- }
107
+ const value = baseObject[key];
108
+ if (value === null || value === void 0) {
109
+ continue;
110
+ }
111
+ if (Array.isArray(value) && Array.isArray(object[key])) {
112
+ object[key] = [...value, ...object[key]];
113
+ } else if (isPlainObject$1(value) && isPlainObject$1(object[key])) {
114
+ object[key] = _defu(
115
+ value,
116
+ object[key],
117
+ (namespace ? `${namespace}.` : "") + key.toString());
118
+ } else {
119
+ object[key] = value;
84
120
  }
85
- const queueCfg = cfg?.queue && typeof cfg.queue === "object" ? {
86
- name: cfg.queue.name,
87
- defaultJobOptions: cfg.queue.defaultJobOptions,
88
- prefix: cfg.queue.prefix,
89
- limiter: cfg.queue.limiter
90
- } : void 0;
91
- const workerCfg = cfg?.worker && typeof cfg.worker === "object" ? { ...cfg.worker } : void 0;
92
- const hasHooks = !!(mod.exports.onAwaitRegister || mod.exports.onAwaitResolve || mod.exports.onAwaitTimeout);
93
- return { queueName, flow, runtype, queue: queueCfg, worker: workerCfg, hasDefaultExport, hasHooks };
94
- } catch (error) {
95
- throw new Error(`Failed to parse config from ${absPath}: ${error}`);
96
121
  }
122
+ return object;
123
+ }
124
+ function createDefu(merger) {
125
+ return (...arguments_) => (
126
+ // eslint-disable-next-line unicorn/no-array-reduce
127
+ arguments_.reduce((p, c) => _defu(p, c, ""), {})
128
+ );
129
+ }
130
+ const defu = createDefu();
131
+
132
+ function isPlainObject(obj) {
133
+ return Object.prototype.toString.call(obj) === "[object Object]";
97
134
  }
98
- function extractConfigValue(value) {
99
- if (value && typeof value === "object" && "$ast" in value) {
100
- return astToValue(value.$ast);
135
+ function isLogObj(arg) {
136
+ if (!isPlainObject(arg)) {
137
+ return false;
138
+ }
139
+ if (!arg.message && !arg.args) {
140
+ return false;
141
+ }
142
+ if (arg.stack) {
143
+ return false;
101
144
  }
102
- return value;
145
+ return true;
103
146
  }
104
- function astToValue(node) {
105
- if (!node) return void 0;
106
- switch (node.type) {
107
- case "CallExpression":
108
- if (node.arguments?.length > 0) {
109
- return astToValue(node.arguments[0]);
110
- }
111
- return void 0;
112
- case "ObjectExpression":
113
- return node.properties?.reduce((obj, prop) => {
114
- if (prop.type === "ObjectProperty" || prop.type === "Property") {
115
- const key = prop.key.type === "Identifier" ? prop.key.name : prop.key.value;
116
- obj[key] = astToValue(prop.value);
147
+
148
+ let paused = false;
149
+ const queue = [];
150
+ class Consola {
151
+ options;
152
+ _lastLog;
153
+ _mockFn;
154
+ /**
155
+ * Creates an instance of Consola with specified options or defaults.
156
+ *
157
+ * @param {Partial<ConsolaOptions>} [options={}] - Configuration options for the Consola instance.
158
+ */
159
+ constructor(options = {}) {
160
+ const types = options.types || LogTypes;
161
+ this.options = defu(
162
+ {
163
+ ...options,
164
+ defaults: { ...options.defaults },
165
+ level: _normalizeLogLevel(options.level, types),
166
+ reporters: [...options.reporters || []]
167
+ },
168
+ {
169
+ types: LogTypes,
170
+ throttle: 1e3,
171
+ throttleMin: 5,
172
+ formatOptions: {
173
+ date: true,
174
+ colors: false,
175
+ compact: true
117
176
  }
118
- return obj;
119
- }, {}) || {};
120
- case "ArrayExpression":
121
- return node.elements?.map((el) => astToValue(el)) || [];
122
- case "Literal":
123
- case "StringLiteral":
124
- case "NumericLiteral":
125
- case "BooleanLiteral":
126
- return node.value;
127
- case "Identifier":
128
- return void 0;
129
- case "TemplateLiteral":
130
- if (node.expressions?.length === 0 && node.quasis?.length === 1) {
131
- return node.quasis[0].value.cooked;
132
177
  }
133
- return void 0;
134
- default:
135
- return void 0;
178
+ );
179
+ for (const type in types) {
180
+ const defaults = {
181
+ type,
182
+ ...this.options.defaults,
183
+ ...types[type]
184
+ };
185
+ this[type] = this._wrapLogFn(defaults);
186
+ this[type].raw = this._wrapLogFn(
187
+ defaults,
188
+ true
189
+ );
190
+ }
191
+ if (this.options.mockFn) {
192
+ this.mockTypes();
193
+ }
194
+ this._lastLog = {};
136
195
  }
137
- }
138
-
139
- async function loadPyConfig(absPath, logger) {
140
- const moduleDir = dirname(fileURLToPath(import.meta.url));
141
- const helper = join(moduleDir, "..", "..", "runtime", "worker", "python", "get_config.py");
142
- let pyConfig;
143
- if (existsSync(helper)) {
144
- pyConfig = await new Promise((resolve) => {
145
- try {
146
- const child = spawn("python3", [helper, absPath], {
147
- stdio: ["ignore", "pipe", "pipe", "pipe"],
148
- env: { ...process.env, NODE_CHANNEL_FD: "3" }
149
- });
150
- let dataBuf = "";
151
- const fd = child.stdio[3];
152
- const stream = fd || child.stdout;
153
- stream.setEncoding("utf-8");
154
- stream.on("data", (chunk) => {
155
- dataBuf += chunk;
156
- });
157
- child.on("error", () => resolve(void 0));
158
- child.on("close", () => {
159
- const line = dataBuf.split("\n").find((l) => l.trim().length > 0);
160
- if (!line) return resolve(void 0);
161
- try {
162
- resolve(JSON.parse(line));
163
- } catch {
164
- resolve(void 0);
165
- }
166
- });
167
- } catch {
168
- resolve(void 0);
196
+ /**
197
+ * Gets the current log level of the Consola instance.
198
+ *
199
+ * @returns {number} The current log level.
200
+ */
201
+ get level() {
202
+ return this.options.level;
203
+ }
204
+ /**
205
+ * Sets the minimum log level that will be output by the instance.
206
+ *
207
+ * @param {number} level - The new log level to set.
208
+ */
209
+ set level(level) {
210
+ this.options.level = _normalizeLogLevel(
211
+ level,
212
+ this.options.types,
213
+ this.options.level
214
+ );
215
+ }
216
+ /**
217
+ * Displays a prompt to the user and returns the response.
218
+ * Throw an error if `prompt` is not supported by the current configuration.
219
+ *
220
+ * @template T
221
+ * @param {string} message - The message to display in the prompt.
222
+ * @param {T} [opts] - Optional options for the prompt. See {@link PromptOptions}.
223
+ * @returns {promise<T>} A promise that infer with the prompt options. See {@link PromptOptions}.
224
+ */
225
+ prompt(message, opts) {
226
+ if (!this.options.prompt) {
227
+ throw new Error("prompt is not supported!");
228
+ }
229
+ return this.options.prompt(message, opts);
230
+ }
231
+ /**
232
+ * Creates a new instance of Consola, inheriting options from the current instance, with possible overrides.
233
+ *
234
+ * @param {Partial<ConsolaOptions>} options - Optional overrides for the new instance. See {@link ConsolaOptions}.
235
+ * @returns {ConsolaInstance} A new Consola instance. See {@link ConsolaInstance}.
236
+ */
237
+ create(options) {
238
+ const instance = new Consola({
239
+ ...this.options,
240
+ ...options
241
+ });
242
+ if (this._mockFn) {
243
+ instance.mockTypes(this._mockFn);
244
+ }
245
+ return instance;
246
+ }
247
+ /**
248
+ * Creates a new Consola instance with the specified default log object properties.
249
+ *
250
+ * @param {InputLogObject} defaults - Default properties to include in any log from the new instance. See {@link InputLogObject}.
251
+ * @returns {ConsolaInstance} A new Consola instance. See {@link ConsolaInstance}.
252
+ */
253
+ withDefaults(defaults) {
254
+ return this.create({
255
+ ...this.options,
256
+ defaults: {
257
+ ...this.options.defaults,
258
+ ...defaults
169
259
  }
170
260
  });
171
- } else {
172
- logger?.warn?.("Python helper not found, skipping config extraction:", helper);
173
- }
174
- let queueName;
175
- let flow;
176
- if (pyConfig) {
177
- queueName = pyConfig?.queue && typeof pyConfig.queue === "object" ? pyConfig.queue?.name : void 0;
178
- const flowCfg = pyConfig?.flow;
179
- if (flowCfg && typeof flowCfg === "object") {
180
- const subscribes = Array.isArray(flowCfg.subscribes) ? flowCfg.subscribes : typeof flowCfg.subscribes === "string" ? [flowCfg.subscribes] : void 0;
181
- const names = Array.isArray(flowCfg.name) ? flowCfg.name.filter((s) => typeof s === "string" && s.length > 0) : typeof flowCfg.name === "string" && flowCfg.name.length > 0 ? [flowCfg.name] : [];
182
- if (names.length) {
183
- flow = {
184
- names,
185
- role: flowCfg.role,
186
- step: flowCfg.step,
187
- emits: flowCfg.emits,
188
- subscribes,
189
- triggers: flowCfg.triggers,
190
- stepTimeout: flowCfg.stepTimeout,
191
- awaitBefore: flowCfg.awaitBefore,
192
- awaitAfter: flowCfg.awaitAfter
193
- };
261
+ }
262
+ /**
263
+ * Creates a new Consola instance with a specified tag, which will be included in every log.
264
+ *
265
+ * @param {string} tag - The tag to include in each log of the new instance.
266
+ * @returns {ConsolaInstance} A new Consola instance. See {@link ConsolaInstance}.
267
+ */
268
+ withTag(tag) {
269
+ return this.withDefaults({
270
+ tag: this.options.defaults.tag ? this.options.defaults.tag + ":" + tag : tag
271
+ });
272
+ }
273
+ /**
274
+ * Adds a custom reporter to the Consola instance.
275
+ * Reporters will be called for each log message, depending on their implementation and log level.
276
+ *
277
+ * @param {ConsolaReporter} reporter - The reporter to add. See {@link ConsolaReporter}.
278
+ * @returns {Consola} The current Consola instance.
279
+ */
280
+ addReporter(reporter) {
281
+ this.options.reporters.push(reporter);
282
+ return this;
283
+ }
284
+ /**
285
+ * Removes a custom reporter from the Consola instance.
286
+ * If no reporter is specified, all reporters will be removed.
287
+ *
288
+ * @param {ConsolaReporter} reporter - The reporter to remove. See {@link ConsolaReporter}.
289
+ * @returns {Consola} The current Consola instance.
290
+ */
291
+ removeReporter(reporter) {
292
+ if (reporter) {
293
+ const i = this.options.reporters.indexOf(reporter);
294
+ if (i !== -1) {
295
+ return this.options.reporters.splice(i, 1);
194
296
  }
297
+ } else {
298
+ this.options.reporters.splice(0);
195
299
  }
300
+ return this;
196
301
  }
197
- return { queueName, flow, hasDefaultExport: true };
198
- }
199
-
200
- async function scanWorkers(layers, functionsDir = "functions") {
201
- const logger = useLogger();
202
- const workerByVirtualPath = /* @__PURE__ */ new Map();
203
- const seenFiles = /* @__PURE__ */ new Set();
204
- const flowSources = [];
205
- for (const layer of layers) {
206
- const base = join(layer.serverDir, functionsDir);
207
- if (!existsSync(base)) continue;
208
- const files = await globby("**/*.{ts,js,mjs,cjs,mts,cts,py}", { cwd: base, dot: false });
209
- for (const rel of files) {
210
- const abs = join(base, rel);
211
- try {
212
- let key = abs;
213
- try {
214
- key = realpathSync(abs);
215
- } catch {
216
- }
217
- key = key.replace(/\\/g, "/");
218
- if (seenFiles.has(key)) continue;
219
- seenFiles.add(key);
220
- const ext = extname(abs).toLowerCase();
221
- const id = rel.replace(/\.[^.]+$/, "");
222
- let queueName;
223
- let flow;
224
- if (ext === ".js" || ext === ".mjs" || ext === ".cjs") {
225
- const meta = await loadJsConfig(abs);
226
- flow = meta.flow;
227
- const hasDefaultExport = !!meta.hasDefaultExport;
228
- if (hasDefaultExport) {
229
- const kind = "ts";
230
- const virtualPath = relative(layer.serverDir, abs).replace(/\\/g, "/");
231
- queueName = String(meta.queue?.name || meta.queueName || (id.split("/").pop() || id));
232
- workerByVirtualPath.set(virtualPath, {
233
- id,
234
- name: queueName,
235
- kind,
236
- filePath: virtualPath,
237
- absPath: abs,
238
- exportName: "default",
239
- queue: {
240
- name: queueName,
241
- defaultJobOptions: meta.queue?.defaultJobOptions,
242
- prefix: meta.queue?.prefix,
243
- limiter: meta.queue?.limiter
244
- },
245
- worker: meta.worker,
246
- flow,
247
- runtype: meta.runtype
248
- });
249
- }
250
- } else if (ext === ".py") {
251
- const meta = await loadPyConfig(abs, logger);
252
- flow = meta.flow;
253
- const kind = "py";
254
- const virtualPath = relative(layer.serverDir, abs).replace(/\\/g, "/");
255
- queueName = String(meta.queue?.name || meta.queueName || (id.split("/").pop() || id));
256
- workerByVirtualPath.set(virtualPath, {
257
- id,
258
- name: queueName,
259
- kind,
260
- filePath: virtualPath,
261
- absPath: abs,
262
- exportName: "default",
263
- queue: {
264
- name: queueName,
265
- defaultJobOptions: meta.queue?.defaultJobOptions,
266
- prefix: meta.queue?.prefix,
267
- limiter: meta.queue?.limiter
268
- },
269
- flow
270
- });
271
- } else {
272
- try {
273
- const meta = await loadTsConfig(abs);
274
- flow = meta.flow;
275
- const hasDefaultExport = !!meta.hasDefaultExport;
276
- if (hasDefaultExport) {
277
- const kind = "ts";
278
- const virtualPath = relative(layer.serverDir, abs).replace(/\\/g, "/");
279
- queueName = String(meta.queue?.name || meta.queueName || (id.split("/").pop() || id));
280
- workerByVirtualPath.set(virtualPath, {
281
- id,
282
- name: queueName,
283
- kind,
284
- filePath: virtualPath,
285
- absPath: abs,
286
- exportName: "default",
287
- queue: {
288
- name: queueName,
289
- defaultJobOptions: meta.queue?.defaultJobOptions,
290
- prefix: meta.queue?.prefix,
291
- limiter: meta.queue?.limiter
292
- },
293
- worker: meta.worker,
294
- flow,
295
- runtype: meta.runtype
296
- });
297
- }
298
- } catch (err) {
299
- logger.warn("Failed to load TS worker (jiti):", rel, err);
300
- }
301
- }
302
- if (!queueName) {
303
- queueName = id.split("/").pop() || id;
304
- }
305
- if (flow) {
306
- flowSources.push({ flow, queue: String(queueName), id });
307
- }
308
- } catch (e) {
309
- logger.warn("Failed to load worker file:", rel, e);
302
+ /**
303
+ * Replaces all reporters of the Consola instance with the specified array of reporters.
304
+ *
305
+ * @param {ConsolaReporter[]} reporters - The new reporters to set. See {@link ConsolaReporter}.
306
+ * @returns {Consola} The current Consola instance.
307
+ */
308
+ setReporters(reporters) {
309
+ this.options.reporters = Array.isArray(reporters) ? reporters : [reporters];
310
+ return this;
311
+ }
312
+ wrapAll() {
313
+ this.wrapConsole();
314
+ this.wrapStd();
315
+ }
316
+ restoreAll() {
317
+ this.restoreConsole();
318
+ this.restoreStd();
319
+ }
320
+ /**
321
+ * Overrides console methods with Consola logging methods for consistent logging.
322
+ */
323
+ wrapConsole() {
324
+ for (const type in this.options.types) {
325
+ if (!console["__" + type]) {
326
+ console["__" + type] = console[type];
310
327
  }
328
+ console[type] = this[type].raw;
311
329
  }
312
330
  }
313
- const workers = Array.from(workerByVirtualPath.values());
314
- return { workers, flowSources };
315
- }
316
-
317
- function buildFlows(flowSources) {
318
- const flows = {};
319
- const eventIndex = {};
320
- const seenFlowKeys = /* @__PURE__ */ new Set();
321
- for (const src of flowSources) {
322
- const { flow: f, queue, id } = src;
323
- if (!f?.names?.length || !f.step) continue;
324
- const rawSteps = Array.isArray(f.step) ? f.step : [f.step];
325
- const steps = rawSteps.filter((s) => typeof s === "string" && s.length > 0);
326
- if (steps.length === 0) continue;
327
- for (const flowId of f.names) {
328
- if (!flows[flowId]) flows[flowId] = { steps: {} };
329
- const bucket = flows[flowId] = flows[flowId] || { steps: {} };
330
- if (f.role === "entry") {
331
- const mainStep = steps[0];
332
- const key = `${flowId}:${f.role}:${mainStep}`;
333
- if (!seenFlowKeys.has(key)) {
334
- seenFlowKeys.add(key);
335
- bucket.entry = {
336
- step: mainStep,
337
- queue,
338
- workerId: id,
339
- emits: f.emits,
340
- stepTimeout: f.stepTimeout,
341
- awaitBefore: f.awaitBefore,
342
- awaitAfter: f.awaitAfter
343
- };
344
- }
345
- for (const s of steps.slice(1)) {
346
- const skey = `${flowId}:step:${s}`;
347
- if (seenFlowKeys.has(skey)) continue;
348
- seenFlowKeys.add(skey);
349
- bucket.steps[s] = {
350
- queue,
351
- workerId: id,
352
- subscribes: f.subscribes,
353
- emits: f.emits,
354
- stepTimeout: f.stepTimeout,
355
- awaitBefore: f.awaitBefore,
356
- awaitAfter: f.awaitAfter
357
- };
358
- }
359
- } else {
360
- for (const s of steps) {
361
- const skey = `${flowId}:${f.role}:${s}`;
362
- if (seenFlowKeys.has(skey)) continue;
363
- seenFlowKeys.add(skey);
364
- bucket.steps[s] = {
365
- queue,
366
- workerId: id,
367
- subscribes: f.subscribes,
368
- emits: f.emits,
369
- stepTimeout: f.stepTimeout,
370
- awaitBefore: f.awaitBefore,
371
- awaitAfter: f.awaitAfter
372
- };
331
+ /**
332
+ * Restores the original console methods, removing Consola overrides.
333
+ */
334
+ restoreConsole() {
335
+ for (const type in this.options.types) {
336
+ if (console["__" + type]) {
337
+ console[type] = console["__" + type];
338
+ delete console["__" + type];
339
+ }
340
+ }
341
+ }
342
+ /**
343
+ * Overrides standard output and error streams to redirect them through Consola.
344
+ */
345
+ wrapStd() {
346
+ this._wrapStream(this.options.stdout, "log");
347
+ this._wrapStream(this.options.stderr, "log");
348
+ }
349
+ _wrapStream(stream, type) {
350
+ if (!stream) {
351
+ return;
352
+ }
353
+ if (!stream.__write) {
354
+ stream.__write = stream.write;
355
+ }
356
+ stream.write = (data) => {
357
+ this[type].raw(String(data).trim());
358
+ };
359
+ }
360
+ /**
361
+ * Restores the original standard output and error streams, removing the Consola redirection.
362
+ */
363
+ restoreStd() {
364
+ this._restoreStream(this.options.stdout);
365
+ this._restoreStream(this.options.stderr);
366
+ }
367
+ _restoreStream(stream) {
368
+ if (!stream) {
369
+ return;
370
+ }
371
+ if (stream.__write) {
372
+ stream.write = stream.__write;
373
+ delete stream.__write;
374
+ }
375
+ }
376
+ /**
377
+ * Pauses logging, queues incoming logs until resumed.
378
+ */
379
+ pauseLogs() {
380
+ paused = true;
381
+ }
382
+ /**
383
+ * Resumes logging, processing any queued logs.
384
+ */
385
+ resumeLogs() {
386
+ paused = false;
387
+ const _queue = queue.splice(0);
388
+ for (const item of _queue) {
389
+ item[0]._logFn(item[1], item[2]);
390
+ }
391
+ }
392
+ /**
393
+ * Replaces logging methods with mocks if a mock function is provided.
394
+ *
395
+ * @param {ConsolaOptions["mockFn"]} mockFn - The function to use for mocking logging methods. See {@link ConsolaOptions["mockFn"]}.
396
+ */
397
+ mockTypes(mockFn) {
398
+ const _mockFn = mockFn || this.options.mockFn;
399
+ this._mockFn = _mockFn;
400
+ if (typeof _mockFn !== "function") {
401
+ return;
402
+ }
403
+ for (const type in this.options.types) {
404
+ this[type] = _mockFn(type, this.options.types[type]) || this[type];
405
+ this[type].raw = this[type];
406
+ }
407
+ }
408
+ _wrapLogFn(defaults, isRaw) {
409
+ return (...args) => {
410
+ if (paused) {
411
+ queue.push([this, defaults, args, isRaw]);
412
+ return;
413
+ }
414
+ return this._logFn(defaults, args, isRaw);
415
+ };
416
+ }
417
+ _logFn(defaults, args, isRaw) {
418
+ if ((defaults.level || 0) > this.level) {
419
+ return false;
420
+ }
421
+ const logObj = {
422
+ date: /* @__PURE__ */ new Date(),
423
+ args: [],
424
+ ...defaults,
425
+ level: _normalizeLogLevel(defaults.level, this.options.types)
426
+ };
427
+ if (!isRaw && args.length === 1 && isLogObj(args[0])) {
428
+ Object.assign(logObj, args[0]);
429
+ } else {
430
+ logObj.args = [...args];
431
+ }
432
+ if (logObj.message) {
433
+ logObj.args.unshift(logObj.message);
434
+ delete logObj.message;
435
+ }
436
+ if (logObj.additional) {
437
+ if (!Array.isArray(logObj.additional)) {
438
+ logObj.additional = logObj.additional.split("\n");
439
+ }
440
+ logObj.args.push("\n" + logObj.additional.join("\n"));
441
+ delete logObj.additional;
442
+ }
443
+ logObj.type = typeof logObj.type === "string" ? logObj.type.toLowerCase() : "log";
444
+ logObj.tag = typeof logObj.tag === "string" ? logObj.tag : "";
445
+ const resolveLog = (newLog = false) => {
446
+ const repeated = (this._lastLog.count || 0) - this.options.throttleMin;
447
+ if (this._lastLog.object && repeated > 0) {
448
+ const args2 = [...this._lastLog.object.args];
449
+ if (repeated > 1) {
450
+ args2.push(`(repeated ${repeated} times)`);
373
451
  }
452
+ this._log({ ...this._lastLog.object, args: args2 });
453
+ this._lastLog.count = 1;
454
+ }
455
+ if (newLog) {
456
+ this._lastLog.object = logObj;
457
+ this._log(logObj);
374
458
  }
375
- if (f.subscribes) {
376
- for (const kind of f.subscribes) {
377
- if (!eventIndex[kind]) eventIndex[kind] = [];
378
- for (const s of steps) {
379
- eventIndex[kind].push({ flowId, step: s, queue, workerId: id });
459
+ };
460
+ clearTimeout(this._lastLog.timeout);
461
+ const diffTime = this._lastLog.time && logObj.date ? logObj.date.getTime() - this._lastLog.time.getTime() : 0;
462
+ this._lastLog.time = logObj.date;
463
+ if (diffTime < this.options.throttle) {
464
+ try {
465
+ const serializedLog = JSON.stringify([
466
+ logObj.type,
467
+ logObj.tag,
468
+ logObj.args
469
+ ]);
470
+ const isSameLog = this._lastLog.serialized === serializedLog;
471
+ this._lastLog.serialized = serializedLog;
472
+ if (isSameLog) {
473
+ this._lastLog.count = (this._lastLog.count || 0) + 1;
474
+ if (this._lastLog.count > this.options.throttleMin) {
475
+ this._lastLog.timeout = setTimeout(
476
+ resolveLog,
477
+ this.options.throttle
478
+ );
479
+ return;
380
480
  }
381
481
  }
482
+ } catch {
382
483
  }
383
484
  }
485
+ resolveLog(true);
486
+ }
487
+ _log(logObj) {
488
+ for (const reporter of this.options.reporters) {
489
+ reporter.log(logObj, {
490
+ options: this.options
491
+ });
492
+ }
384
493
  }
385
- return { flows, eventIndex };
386
494
  }
387
-
388
- function mergeWorkerConfig(worker, defaults) {
389
- const merged = { ...worker };
390
- if (defaults.queue || worker.queue) {
391
- merged.queue = defu(
392
- worker.queue || {},
393
- defaults.queue || {}
394
- );
495
+ function _normalizeLogLevel(input, types = {}, defaultLevel = 3) {
496
+ if (input === void 0) {
497
+ return defaultLevel;
395
498
  }
396
- if (defaults.worker || worker.worker) {
397
- merged.worker = defu(
398
- worker.worker || {},
399
- defaults.worker || {}
400
- );
499
+ if (typeof input === "number") {
500
+ return input;
501
+ }
502
+ if (types[input] && types[input].level !== void 0) {
503
+ return types[input].level;
401
504
  }
402
- return merged;
505
+ return defaultLevel;
403
506
  }
404
- function mergeAllWorkerConfigs(workers, defaults) {
405
- return workers.map((worker) => mergeWorkerConfig(worker, defaults));
507
+ Consola.prototype.add = Consola.prototype.addReporter;
508
+ Consola.prototype.remove = Consola.prototype.removeReporter;
509
+ Consola.prototype.clear = Consola.prototype.removeReporter;
510
+ Consola.prototype.withScope = Consola.prototype.withTag;
511
+ Consola.prototype.mock = Consola.prototype.mockTypes;
512
+ Consola.prototype.pause = Consola.prototype.pauseLogs;
513
+ Consola.prototype.resume = Consola.prototype.resumeLogs;
514
+ function createConsola$1(options = {}) {
515
+ return new Consola(options);
406
516
  }
407
517
 
408
- function parseSubscription(token) {
409
- const [prefix, ...rest] = token.split(":");
410
- if (rest.length > 0) {
411
- const type = prefix;
412
- return { type, value: rest.join(":") };
518
+ class BrowserReporter {
519
+ options;
520
+ defaultColor;
521
+ levelColorMap;
522
+ typeColorMap;
523
+ constructor(options) {
524
+ this.options = { ...options };
525
+ this.defaultColor = "#7f8c8d";
526
+ this.levelColorMap = {
527
+ 0: "#c0392b",
528
+ // Red
529
+ 1: "#f39c12",
530
+ // Yellow
531
+ 3: "#00BCD4"
532
+ // Cyan
533
+ };
534
+ this.typeColorMap = {
535
+ success: "#2ecc71"
536
+ // Green
537
+ };
413
538
  }
414
- return { type: "implicit", value: token };
415
- }
416
- function findEmitter(token, entryStep, steps, entry) {
417
- const { type, value } = parseSubscription(token);
418
- if (entryStep && entry) {
419
- const entryEmits = entry.emits || [];
420
- if (entryEmits.includes(token) || entryEmits.includes(value)) {
421
- return entryStep;
539
+ _getLogFn(level) {
540
+ if (level < 1) {
541
+ return console.__error || console.error;
422
542
  }
423
- }
424
- for (const [stepName, step] of Object.entries(steps)) {
425
- const emits = step.emits || [];
426
- if (emits.includes(token)) {
427
- return stepName;
543
+ if (level === 1) {
544
+ return console.__warn || console.warn;
428
545
  }
429
- switch (type) {
430
- case "step":
431
- if (stepName === value) return stepName;
432
- break;
433
- case "queue":
434
- if (step.queue === value) return stepName;
435
- break;
436
- case "worker":
437
- if (step.workerId === value) return stepName;
438
- break;
439
- case "implicit":
440
- if (stepName === value || step.queue === value || emits.includes(value)) {
441
- return stepName;
442
- }
443
- break;
546
+ return console.__log || console.log;
547
+ }
548
+ log(logObj) {
549
+ const consoleLogFn = this._getLogFn(logObj.level);
550
+ const type = logObj.type === "log" ? "" : logObj.type;
551
+ const tag = logObj.tag || "";
552
+ const color = this.typeColorMap[logObj.type] || this.levelColorMap[logObj.level] || this.defaultColor;
553
+ const style = `
554
+ background: ${color};
555
+ border-radius: 0.5em;
556
+ color: white;
557
+ font-weight: bold;
558
+ padding: 2px 0.5em;
559
+ `;
560
+ const badge = `%c${[tag, type].filter(Boolean).join(":")}`;
561
+ if (typeof logObj.args[0] === "string") {
562
+ consoleLogFn(
563
+ `${badge}%c ${logObj.args[0]}`,
564
+ style,
565
+ // Empty string as style resets to default console style
566
+ "",
567
+ ...logObj.args.slice(1)
568
+ );
569
+ } else {
570
+ consoleLogFn(badge, style, ...logObj.args);
444
571
  }
445
572
  }
446
- return null;
447
573
  }
448
- function buildDependencyGraph(entryStep, steps, entry) {
449
- const dependencies = {};
450
- for (const [stepName, step] of Object.entries(steps)) {
451
- const deps = /* @__PURE__ */ new Set();
452
- const subscribes = step.subscribes || [];
453
- for (const token of subscribes) {
454
- const emitter = findEmitter(token, entryStep, steps, entry);
455
- if (emitter && emitter !== stepName) {
456
- deps.add(emitter);
574
+
575
+ function createConsola(options = {}) {
576
+ const consola2 = createConsola$1({
577
+ reporters: options.reporters || [new BrowserReporter({})],
578
+ prompt(message, options2 = {}) {
579
+ if (options2.type === "confirm") {
580
+ return Promise.resolve(confirm(message));
457
581
  }
458
- }
459
- dependencies[stepName] = Array.from(deps);
582
+ return Promise.resolve(prompt(message));
583
+ },
584
+ ...options
585
+ });
586
+ return consola2;
587
+ }
588
+ const consola = createConsola();
589
+
590
+ const execAsync$1 = promisify(exec);
591
+ const logger$1 = consola.withTag("nvent:iii-install");
592
+ const GITHUB_RELEASE_BASE$1 = "https://github.com/iii-hq/iii/releases/download";
593
+ function getPlatformAsset(version) {
594
+ const platform = process.platform;
595
+ const arch = process.arch;
596
+ const tag = toReleaseTag$1(version);
597
+ if (platform === "linux" && arch === "x64") {
598
+ return { url: `${GITHUB_RELEASE_BASE$1}/${tag}/iii-x86_64-unknown-linux-gnu.tar.gz`, ext: "tar.gz" };
599
+ }
600
+ if (platform === "linux" && arch === "arm64") {
601
+ return { url: `${GITHUB_RELEASE_BASE$1}/${tag}/iii-aarch64-unknown-linux-gnu.tar.gz`, ext: "tar.gz" };
602
+ }
603
+ if (platform === "darwin" && arch === "x64") {
604
+ return { url: `${GITHUB_RELEASE_BASE$1}/${tag}/iii-x86_64-apple-darwin.tar.gz`, ext: "tar.gz" };
605
+ }
606
+ if (platform === "darwin" && arch === "arm64") {
607
+ return { url: `${GITHUB_RELEASE_BASE$1}/${tag}/iii-aarch64-apple-darwin.tar.gz`, ext: "tar.gz" };
608
+ }
609
+ if (platform === "win32" && arch === "x64") {
610
+ return { url: `${GITHUB_RELEASE_BASE$1}/${tag}/iii-x86_64-pc-windows-msvc.zip`, ext: "zip" };
460
611
  }
461
- return dependencies;
612
+ if (platform === "win32" && arch === "arm64") {
613
+ return { url: `${GITHUB_RELEASE_BASE$1}/${tag}/iii-aarch64-pc-windows-msvc.zip`, ext: "zip" };
614
+ }
615
+ throw new Error(`Unsupported platform: ${platform}/${arch}`);
462
616
  }
463
- function calculateLevels(entryStep, dependencies) {
464
- const levels = {};
465
- const visited = /* @__PURE__ */ new Set();
466
- const visiting = /* @__PURE__ */ new Set();
467
- function visit(stepName) {
468
- if (visited.has(stepName)) {
469
- return levels[stepName] ?? 0;
470
- }
471
- if (visiting.has(stepName)) {
472
- console.warn(`Circular dependency detected involving step: ${stepName}`);
473
- return 0;
474
- }
475
- visiting.add(stepName);
476
- const deps = dependencies[stepName] || [];
477
- let maxDepLevel = -1;
478
- if (stepName === entryStep) {
479
- maxDepLevel = -1;
480
- } else if (deps.length === 0 && entryStep) {
481
- maxDepLevel = 0;
482
- } else {
483
- for (const dep of deps) {
484
- const depLevel = visit(dep);
485
- maxDepLevel = Math.max(maxDepLevel, depLevel);
486
- }
487
- }
488
- levels[stepName] = maxDepLevel + 1;
489
- visiting.delete(stepName);
490
- visited.add(stepName);
491
- return levels[stepName];
617
+ function semverFromTag$1(tag) {
618
+ return tag.replace(/^.*\/v?/, "").replace(/^v/, "");
619
+ }
620
+ function toReleaseTag$1(version) {
621
+ if (version.includes("/")) return version;
622
+ return `v${version.replace(/^v/, "")}`;
623
+ }
624
+ async function fetchLatestVersion() {
625
+ const res = await fetch("https://api.github.com/repos/iii-hq/iii/releases/latest", {
626
+ headers: { "Accept": "application/vnd.github+json", "X-GitHub-Api-Version": "2022-11-28" }
627
+ });
628
+ if (res.status === 403 || res.status === 429) {
629
+ const retryAfter = res.headers.get("retry-after") ?? res.headers.get("x-ratelimit-reset");
630
+ throw new RateLimitError$1(`GitHub API rate limit exceeded${retryAfter ? ` (retry after ${retryAfter})` : ""}`);
631
+ }
632
+ if (!res.ok) throw new Error(`Failed to fetch latest iii version: ${res.statusText}`);
633
+ const data = await res.json();
634
+ return data.tag_name;
635
+ }
636
+ let RateLimitError$1 = class RateLimitError extends Error {
637
+ };
638
+ async function getCurrentVersion(binaryPath) {
639
+ try {
640
+ const { stdout } = await execAsync$1(`"${binaryPath}" --version`);
641
+ const match = stdout.trim().match(/(\d+\.\d+\.\d+)/);
642
+ return match?.[1] ?? null;
643
+ } catch {
644
+ return null;
645
+ }
646
+ }
647
+ async function downloadAndExtract(assetUrl, ext, binDir, logLevel = "warn") {
648
+ const archivePath = join(binDir, ext === "zip" ? "iii.zip" : "iii.tar.gz");
649
+ if (logLevel === "info") logger$1.info(`Downloading iii engine from ${assetUrl}`);
650
+ const res = await fetch(assetUrl);
651
+ if (!res.ok) throw new Error(`Failed to download iii engine: ${res.status} ${res.statusText}`);
652
+ if (!res.body) throw new Error("Response body is empty");
653
+ const fileStream = createWriteStream(archivePath);
654
+ await pipeline(res.body, fileStream);
655
+ if (logLevel === "info") logger$1.info("Extracting archive...");
656
+ if (ext === "tar.gz") {
657
+ await execAsync$1(`tar -xzf "${archivePath}" -C "${binDir}"`);
658
+ } else {
659
+ await execAsync$1(`unzip -o "${archivePath}" -d "${binDir}"`);
492
660
  }
493
- const allSteps = Object.keys(dependencies);
494
- for (const step of allSteps) {
495
- visit(step);
661
+ try {
662
+ unlinkSync(archivePath);
663
+ } catch {
496
664
  }
497
- return levels;
498
665
  }
499
- function findTriggeredSteps(stepName, step, allSteps) {
500
- const emits = step.emits || [];
501
- const triggered = /* @__PURE__ */ new Set();
502
- for (const [targetName, targetStep] of Object.entries(allSteps)) {
503
- if (targetName === stepName) continue;
504
- const subscribes = targetStep.subscribes || [];
505
- for (const token of subscribes) {
506
- const { type, value } = parseSubscription(token);
507
- let matches = false;
508
- switch (type) {
509
- case "step":
510
- matches = stepName === value;
511
- break;
512
- case "queue":
513
- matches = step.queue === value;
514
- break;
515
- case "worker":
516
- matches = step.workerId === value;
517
- break;
518
- case "implicit":
519
- matches = stepName === value || step.queue === value || emits.includes(value);
520
- break;
521
- }
522
- if (matches || emits.includes(token)) {
523
- triggered.add(targetName);
666
+ async function ensureIiiEngine(options) {
667
+ const { binDir } = options;
668
+ const logLevel = options.logLevel ?? "warn";
669
+ const binaryName = process.platform === "win32" ? "iii.exe" : "iii";
670
+ const binaryPath = join(binDir, binaryName);
671
+ let version = options.version;
672
+ if (!version || version === "latest") {
673
+ if (logLevel === "info") logger$1.info("Resolving latest iii engine version...");
674
+ try {
675
+ version = await fetchLatestVersion();
676
+ if (logLevel === "info") logger$1.info(`Latest iii engine version: ${version}`);
677
+ } catch (err) {
678
+ if (err instanceof RateLimitError$1) {
679
+ if (existsSync(binaryPath)) {
680
+ logger$1.warn(`[nvent] ${err.message} \u2014 using existing binary at ${binaryPath}`);
681
+ return binaryPath;
682
+ }
683
+ throw new Error(`${err.message} and no existing iii binary found. Specify a version in nvent config (e.g. version: '0.8.0') to avoid GitHub API calls.`);
524
684
  }
685
+ throw err;
525
686
  }
526
687
  }
527
- return Array.from(triggered);
528
- }
529
- function getAwaitDefaultTimeout(awaitConfig) {
530
- if (!awaitConfig) return 0;
531
- if (awaitConfig.timeout && awaitConfig.timeout > 0) {
532
- return awaitConfig.timeout;
533
- }
534
- const type = awaitConfig.type;
535
- switch (type) {
536
- case "webhook":
537
- return 24 * 60 * 60 * 1e3;
538
- // 24 hours (matches flow.awaitDefaults.webhookTimeout)
539
- case "event":
540
- return 24 * 60 * 60 * 1e3;
541
- // 24 hours (matches flow.awaitDefaults.eventTimeout)
542
- case "time":
543
- return 0;
544
- // No timeout for time awaits by default
545
- case "schedule":
546
- return 0;
547
- // No timeout for schedule awaits by default
548
- default:
549
- return 0;
688
+ if (existsSync(binaryPath)) {
689
+ const currentVersion = await getCurrentVersion(binaryPath);
690
+ const wantedSemver = semverFromTag$1(version);
691
+ if (currentVersion === wantedSemver) {
692
+ if (logLevel === "info") logger$1.info(`iii engine v${wantedSemver} already installed at ${binaryPath}`);
693
+ return binaryPath;
694
+ }
695
+ if (logLevel === "info") logger$1.info(`iii engine version mismatch (have v${currentVersion}, want v${wantedSemver}), reinstalling...`);
696
+ }
697
+ if (!existsSync(binDir)) {
698
+ mkdirSync(binDir, { recursive: true });
699
+ }
700
+ const asset = getPlatformAsset(version);
701
+ await downloadAndExtract(asset.url, asset.ext, binDir, logLevel);
702
+ if (process.platform !== "win32") {
703
+ chmodSync(binaryPath, 493);
550
704
  }
705
+ const installedVersion = await getCurrentVersion(binaryPath);
706
+ if (logLevel === "info") logger$1.info(`iii engine v${installedVersion} installed at ${binaryPath}`);
707
+ return binaryPath;
551
708
  }
552
- function getStepAwaitTimeout(step) {
553
- let timeout = 0;
554
- if (step.awaitBefore) {
555
- timeout += getAwaitDefaultTimeout(step.awaitBefore);
709
+
710
+ const execAsync = promisify(exec);
711
+ const logger = consola.withTag("nvent:console");
712
+ const GITHUB_RELEASE_BASE = "https://github.com/iii-hq/iii/releases/download";
713
+ function getConsolePlatformAsset(version) {
714
+ const platform = process.platform;
715
+ const arch = process.arch;
716
+ const tag = toReleaseTag(version);
717
+ const base = `${GITHUB_RELEASE_BASE}/${tag}`;
718
+ if (platform === "linux" && arch === "x64") {
719
+ return { url: `${base}/iii-console-x86_64-unknown-linux-gnu.tar.gz`, ext: "tar.gz" };
720
+ }
721
+ if (platform === "linux" && arch === "arm64") {
722
+ return { url: `${base}/iii-console-aarch64-unknown-linux-gnu.tar.gz`, ext: "tar.gz" };
556
723
  }
557
- if (step.awaitAfter) {
558
- timeout += getAwaitDefaultTimeout(step.awaitAfter);
724
+ if (platform === "darwin" && arch === "x64") {
725
+ return { url: `${base}/iii-console-x86_64-apple-darwin.tar.gz`, ext: "tar.gz" };
726
+ }
727
+ if (platform === "darwin" && arch === "arm64") {
728
+ return { url: `${base}/iii-console-aarch64-apple-darwin.tar.gz`, ext: "tar.gz" };
729
+ }
730
+ if (platform === "win32" && arch === "x64") {
731
+ return { url: `${base}/iii-console-x86_64-pc-windows-msvc.zip`, ext: "zip" };
732
+ }
733
+ if (platform === "win32" && arch === "arm64") {
734
+ return { url: `${base}/iii-console-aarch64-pc-windows-msvc.zip`, ext: "zip" };
735
+ }
736
+ throw new Error(`Unsupported platform for iii-console: ${platform}/${arch}`);
737
+ }
738
+ function semverFromTag(tag) {
739
+ return tag.replace(/^.*\/v?/, "").replace(/^v/, "");
740
+ }
741
+ function toReleaseTag(version) {
742
+ if (version.includes("/")) return version;
743
+ return `v${version.replace(/^v/, "")}`;
744
+ }
745
+ async function fetchLatestConsoleVersion() {
746
+ const res = await fetch("https://api.github.com/repos/iii-hq/iii/releases/latest", {
747
+ headers: { "Accept": "application/vnd.github+json", "X-GitHub-Api-Version": "2022-11-28" }
748
+ });
749
+ if (res.status === 403 || res.status === 429) {
750
+ const retryAfter = res.headers.get("retry-after") ?? res.headers.get("x-ratelimit-reset");
751
+ throw new RateLimitError(`GitHub API rate limit exceeded${retryAfter ? ` (retry after ${retryAfter})` : ""}`);
559
752
  }
560
- return timeout;
753
+ if (!res.ok) throw new Error(`Failed to fetch latest iii version: ${res.statusText}`);
754
+ const data = await res.json();
755
+ return data.tag_name;
561
756
  }
562
- function getStepExecutionTimeout(step, config) {
563
- if (step.stepTimeout !== void 0) {
564
- return step.stepTimeout;
757
+ class RateLimitError extends Error {
758
+ }
759
+ async function getCurrentConsoleVersion(binaryPath) {
760
+ try {
761
+ const { stdout } = await execAsync(`"${binaryPath}" --version`);
762
+ const match = stdout.trim().match(/(\d+\.\d+\.\d+)/);
763
+ return match?.[1] ?? null;
764
+ } catch {
765
+ return null;
565
766
  }
566
- if (config?.flow?.stepTimeout !== void 0) {
567
- return config.flow.stepTimeout;
767
+ }
768
+ async function downloadAndExtractConsole(assetUrl, ext, binDir, logLevel = "warn") {
769
+ const archivePath = join(binDir, ext === "zip" ? "iii-console.zip" : "iii-console.tar.gz");
770
+ if (logLevel === "info") logger.info(`Downloading iii-console from ${assetUrl}`);
771
+ const res = await fetch(assetUrl);
772
+ if (!res.ok) throw new Error(`Failed to download iii-console: ${res.status} ${res.statusText}`);
773
+ if (!res.body) throw new Error("Response body is empty");
774
+ const fileStream = createWriteStream(archivePath);
775
+ await pipeline(res.body, fileStream);
776
+ if (logLevel === "info") logger.info("Extracting iii-console archive...");
777
+ if (ext === "tar.gz") {
778
+ await execAsync(`tar -xzf "${archivePath}" -C "${binDir}"`);
779
+ } else {
780
+ await execAsync(`unzip -o "${archivePath}" -d "${binDir}"`);
568
781
  }
569
- if (config?.queue?.defaultJobOptions?.timeout !== void 0) {
570
- return config.queue.defaultJobOptions.timeout;
782
+ try {
783
+ unlinkSync(archivePath);
784
+ } catch {
571
785
  }
572
- return void 0;
573
786
  }
574
- function calculateFlowStallTimeout(steps, levels) {
575
- const DEFAULT_STALL_TIMEOUT = 30 * 60 * 1e3;
576
- const MIN_BUFFER = 5 * 60 * 1e3;
577
- const BUFFER_PERCENTAGE = 0.1;
578
- const DEFAULT_STEP_TIMEOUT = 5 * 60 * 1e3;
579
- const levelTimeouts = [];
580
- for (const levelSteps of levels) {
581
- let maxLevelTimeout = 0;
582
- for (const stepName of levelSteps) {
583
- const step = steps[stepName];
584
- if (!step) continue;
585
- const stepExecTimeout = step.stepTimeout ?? DEFAULT_STEP_TIMEOUT;
586
- const awaitTimeout = getStepAwaitTimeout(step);
587
- const totalStepTimeout = stepExecTimeout + awaitTimeout;
588
- if (totalStepTimeout > 0) {
589
- maxLevelTimeout = Math.max(maxLevelTimeout, totalStepTimeout);
787
+ async function ensureIiiConsole(options) {
788
+ const { binDir } = options;
789
+ const logLevel = options.logLevel ?? "warn";
790
+ const binaryName = process.platform === "win32" ? "iii-console.exe" : "iii-console";
791
+ const binaryPath = join(binDir, binaryName);
792
+ let version = options.version;
793
+ if (!version || version === "latest") {
794
+ if (logLevel === "info") logger.info("Resolving latest iii-console version...");
795
+ try {
796
+ version = await fetchLatestConsoleVersion();
797
+ if (logLevel === "info") logger.info(`Latest iii-console version: ${version}`);
798
+ } catch (err) {
799
+ if (err instanceof RateLimitError) {
800
+ if (existsSync(binaryPath)) {
801
+ logger.warn(`[nvent] ${err.message} \u2014 using existing iii-console binary at ${binaryPath}`);
802
+ return binaryPath;
803
+ }
804
+ throw new Error(`${err.message} and no existing iii-console binary found. Specify a version in nvent config (e.g. console: { version: '0.8.0' }) to avoid GitHub API calls.`);
590
805
  }
806
+ throw err;
591
807
  }
592
- if (maxLevelTimeout > 0) {
593
- levelTimeouts.push(maxLevelTimeout);
808
+ }
809
+ if (existsSync(binaryPath)) {
810
+ const current = await getCurrentConsoleVersion(binaryPath);
811
+ const wantedSemver = semverFromTag(version);
812
+ if (current === wantedSemver) {
813
+ if (logLevel === "info") logger.info(`iii-console v${wantedSemver} already installed`);
814
+ return binaryPath;
594
815
  }
816
+ if (logLevel === "info") logger.info(`iii-console version mismatch (installed: ${current}, wanted: ${wantedSemver}) \u2014 updating`);
595
817
  }
596
- if (levelTimeouts.length === 0) {
597
- return DEFAULT_STALL_TIMEOUT;
818
+ if (!existsSync(binDir)) {
819
+ mkdirSync(binDir, { recursive: true });
598
820
  }
599
- const totalAwaitTimeout = levelTimeouts.reduce((sum, timeout) => sum + timeout, 0);
600
- const buffer = Math.max(totalAwaitTimeout * BUFFER_PERCENTAGE, MIN_BUFFER);
601
- const calculatedTimeout = totalAwaitTimeout + buffer;
602
- if (calculatedTimeout > DEFAULT_STALL_TIMEOUT * 2) {
603
- console.log(
604
- `[flow-analyzer] Flow stall timeout calculated across ${levelTimeouts.length} levels: ${calculatedTimeout / 1e3}s (total time: ${totalAwaitTimeout / 1e3}s, level timeouts: [${levelTimeouts.map((t) => `${t / 1e3}s`).join(", ")}])`
605
- );
821
+ const asset = getConsolePlatformAsset(version);
822
+ await downloadAndExtractConsole(asset.url, asset.ext, binDir, logLevel);
823
+ if (!existsSync(binaryPath)) {
824
+ throw new Error(`iii-console binary not found at ${binaryPath} after extraction`);
606
825
  }
607
- return calculatedTimeout;
826
+ chmodSync(binaryPath, 493);
827
+ if (logLevel === "info") logger.info(`iii-console ${version} installed at ${binaryPath}`);
828
+ return binaryPath;
829
+ }
830
+
831
+ function defaultIiiEngineConfig() {
832
+ return {
833
+ wsPort: 49134,
834
+ httpPort: 3111,
835
+ streamPort: 3112,
836
+ modules: {
837
+ state: true,
838
+ queue: true,
839
+ cron: true,
840
+ observability: true,
841
+ stream: true
842
+ }
843
+ };
844
+ }
845
+ function defaultStateAdapter(stateDir) {
846
+ return {
847
+ class: "modules::state::adapters::KvStore",
848
+ config: { store_method: "file_based", file_path: "./data/state_store" }
849
+ };
850
+ }
851
+ function defaultQueueAdapter(queueDir) {
852
+ return {
853
+ class: "modules::queue::BuiltinQueueAdapter",
854
+ config: { store_method: "file_based", file_path: "./data/queue_store" }
855
+ };
856
+ }
857
+ function defaultCronAdapter() {
858
+ return { class: "modules::cron::KvCronAdapter" };
608
859
  }
609
- function analyzeFlow(flow, config) {
610
- const entryStepName = flow.entry?.step;
611
- const steps = flow.steps || {};
612
- const dependencies = buildDependencyGraph(entryStepName, steps, flow.entry);
613
- const levels = calculateLevels(entryStepName, dependencies);
614
- const analyzedSteps = {};
615
- if (flow.entry && entryStepName) {
616
- const hasAwaitPattern = !!(flow.entry.awaitBefore || flow.entry.awaitAfter);
617
- const entryStep = {
618
- queue: flow.entry.queue,
619
- workerId: flow.entry.workerId,
620
- emits: flow.entry.emits,
621
- stepTimeout: flow.entry.stepTimeout,
622
- // Include stepTimeout from flow metadata
623
- awaitBefore: flow.entry.awaitBefore,
624
- awaitAfter: flow.entry.awaitAfter,
625
- name: entryStepName,
626
- dependsOn: [],
627
- triggers: findTriggeredSteps(entryStepName, flow.entry, steps),
628
- level: 0,
629
- hasAwaitPattern
860
+ function defaultStreamAdapter() {
861
+ return { class: "modules::stream::adapters::KvStore" };
862
+ }
863
+ function generateIiiConfigYaml(cfg) {
864
+ const modules = [];
865
+ const restApiConfig = {
866
+ port: cfg.restApi?.port ?? cfg.httpPort
867
+ };
868
+ if (cfg.restApi?.host) restApiConfig.host = cfg.restApi.host;
869
+ if (cfg.restApi?.default_timeout != null) restApiConfig.default_timeout = cfg.restApi.default_timeout;
870
+ if (cfg.restApi?.concurrency_request_limit != null) restApiConfig.concurrency_request_limit = cfg.restApi.concurrency_request_limit;
871
+ modules.push({ class: "modules::api::RestApiModule", config: restApiConfig });
872
+ if (cfg.modules.state !== false) {
873
+ const adapter = cfg.state?.adapter ?? defaultStateAdapter();
874
+ modules.push({ class: "modules::state::StateModule", config: { adapter } });
875
+ }
876
+ if (cfg.modules.queue !== false) {
877
+ const adapter = cfg.queue?.adapter ?? defaultQueueAdapter();
878
+ const queueModCfg = { adapter };
879
+ const queueConfigs = cfg.queue?.queue_configs;
880
+ if (queueConfigs && Object.keys(queueConfigs).length > 0) {
881
+ queueModCfg.queue_configs = queueConfigs;
882
+ }
883
+ modules.push({ class: "modules::queue::QueueModule", config: queueModCfg });
884
+ }
885
+ if (cfg.modules.cron !== false) {
886
+ const adapter = cfg.cron?.adapter ?? defaultCronAdapter();
887
+ modules.push({ class: "modules::cron::CronModule", config: { adapter } });
888
+ }
889
+ if (cfg.modules.stream !== false) {
890
+ const streamCfg = {
891
+ port: cfg.stream?.port ?? cfg.streamPort
630
892
  };
631
- entryStep.stepTimeout = getStepExecutionTimeout(entryStep, config);
632
- analyzedSteps[entryStepName] = entryStep;
633
- }
634
- for (const [stepName, step] of Object.entries(steps)) {
635
- const hasAwaitPattern = !!(step.awaitBefore || step.awaitAfter);
636
- const analyzedStep = {
637
- ...step,
638
- name: stepName,
639
- dependsOn: dependencies[stepName] || [],
640
- triggers: findTriggeredSteps(stepName, step, steps),
641
- level: levels[stepName] ?? 1,
642
- hasAwaitPattern
643
- // stepTimeout from ...step spread above (per-function config)
893
+ if (cfg.stream?.host) streamCfg.host = cfg.stream.host;
894
+ if (cfg.stream?.auth_function !== void 0) streamCfg.auth_function = cfg.stream.auth_function;
895
+ const streamAdapter = cfg.stream?.adapter ?? defaultStreamAdapter();
896
+ streamCfg.adapter = streamAdapter;
897
+ modules.push({ class: "modules::stream::StreamModule", config: streamCfg });
898
+ }
899
+ if (cfg.modules.observability !== false) {
900
+ const oCfg = cfg.observability ?? {};
901
+ const otelConfig = {
902
+ enabled: oCfg.enabled ?? true,
903
+ service_name: oCfg.service_name ?? "nvent",
904
+ exporter: oCfg.exporter ?? "memory"
644
905
  };
645
- analyzedStep.stepTimeout = getStepExecutionTimeout(analyzedStep, config);
646
- analyzedSteps[stepName] = analyzedStep;
647
- }
648
- const maxLevel = Math.max(0, ...Object.values(levels));
649
- const levelGroups = Array.from({ length: maxLevel + 1 }, () => []);
650
- if (entryStepName && flow.entry) {
651
- levelGroups[0]?.push(entryStepName);
652
- }
653
- for (const [stepName, level] of Object.entries(levels)) {
654
- if (stepName === entryStepName) continue;
655
- const levelArray = levelGroups[level];
656
- if (levelArray) {
657
- levelArray.push(stepName);
658
- }
906
+ if (oCfg.service_version) otelConfig.service_version = oCfg.service_version;
907
+ if (oCfg.service_namespace) otelConfig.service_namespace = oCfg.service_namespace;
908
+ if (oCfg.endpoint) otelConfig.endpoint = oCfg.endpoint;
909
+ if (oCfg.sampling_ratio != null) otelConfig.sampling_ratio = oCfg.sampling_ratio;
910
+ if (oCfg.memory_max_spans != null) otelConfig.memory_max_spans = oCfg.memory_max_spans;
911
+ if (oCfg.metrics_enabled != null) otelConfig.metrics_enabled = oCfg.metrics_enabled;
912
+ if (oCfg.metrics_exporter) otelConfig.metrics_exporter = oCfg.metrics_exporter;
913
+ if (oCfg.metrics_retention_seconds != null) otelConfig.metrics_retention_seconds = oCfg.metrics_retention_seconds;
914
+ if (oCfg.metrics_max_count != null) otelConfig.metrics_max_count = oCfg.metrics_max_count;
915
+ if (oCfg.logs_enabled != null) otelConfig.logs_enabled = oCfg.logs_enabled;
916
+ if (oCfg.logs_exporter) otelConfig.logs_exporter = oCfg.logs_exporter;
917
+ if (oCfg.logs_max_count != null) otelConfig.logs_max_count = oCfg.logs_max_count;
918
+ if (oCfg.logs_retention_seconds != null) otelConfig.logs_retention_seconds = oCfg.logs_retention_seconds;
919
+ if (oCfg.logs_batch_size != null) otelConfig.logs_batch_size = oCfg.logs_batch_size;
920
+ if (oCfg.logs_flush_interval_ms != null) otelConfig.logs_flush_interval_ms = oCfg.logs_flush_interval_ms;
921
+ if (oCfg.logs_sampling_ratio != null) otelConfig.logs_sampling_ratio = oCfg.logs_sampling_ratio;
922
+ if (oCfg.logs_console_output != null) otelConfig.logs_console_output = oCfg.logs_console_output;
923
+ if (oCfg.level) otelConfig.level = oCfg.level;
924
+ if (oCfg.format) otelConfig.format = oCfg.format;
925
+ modules.push({ class: "modules::observability::OtelModule", config: otelConfig });
659
926
  }
660
- const stallTimeout = calculateFlowStallTimeout(analyzedSteps, levelGroups);
661
- const awaitSteps = Object.values(analyzedSteps).filter((s) => s.hasAwaitPattern);
662
- const totalTimeout = awaitSteps.reduce((sum, s) => {
663
- let stepTimeout = 0;
664
- if (s.awaitBefore?.timeout) stepTimeout += s.awaitBefore.timeout;
665
- if (s.awaitAfter?.timeout) stepTimeout += s.awaitAfter.timeout;
666
- return sum + stepTimeout;
667
- }, 0);
668
- const awaitPatterns = awaitSteps.length > 0 ? {
669
- steps: awaitSteps.map((s) => s.name),
670
- beforeCount: awaitSteps.filter((s) => s.awaitBefore).length,
671
- afterCount: awaitSteps.filter((s) => s.awaitAfter).length,
672
- totalTimeout
673
- } : void 0;
927
+ return `# Auto-generated by nvent \u2014 do not edit manually
928
+ ` + stringifyYAML({ port: cfg.wsPort, modules });
929
+ }
930
+ function writeIiiConfig(outputPath, cfg) {
931
+ mkdirSync(dirname(outputPath), { recursive: true });
932
+ writeFileSync(outputPath, generateIiiConfigYaml(cfg), "utf-8");
933
+ }
934
+ function buildEngineConfig(iiiOpts) {
935
+ const queueConfigs = iiiOpts.queue?.queueConfigs;
936
+ const hasQueueConfigs = queueConfigs != null && Object.keys(queueConfigs).length > 0;
674
937
  return {
675
- id: flow.id,
676
- entry: flow.entry,
677
- steps: analyzedSteps,
678
- levels: levelGroups,
679
- maxLevel,
680
- stallTimeout,
681
- awaitPatterns
938
+ ...defaultIiiEngineConfig(),
939
+ wsPort: iiiOpts.wsPort ?? 49134,
940
+ httpPort: iiiOpts.httpPort ?? 3111,
941
+ streamPort: iiiOpts.streamPort ?? 3112,
942
+ modules: { state: true, queue: true, cron: true, observability: true, stream: true, ...iiiOpts.modules },
943
+ state: iiiOpts.state ? { adapter: mapStateAdapter(iiiOpts.state) } : void 0,
944
+ queue: iiiOpts.queue ? {
945
+ adapter: mapQueueAdapter(iiiOpts.queue?.adapter),
946
+ queue_configs: hasQueueConfigs ? Object.fromEntries(
947
+ Object.entries(queueConfigs).map(([name, cfg]) => [
948
+ name,
949
+ { type: cfg.type, concurrency: cfg.concurrency, max_retries: cfg.maxRetries, backoff_ms: cfg.backoffMs, message_group_field: cfg.messageGroupField }
950
+ ])
951
+ ) : void 0
952
+ } : void 0,
953
+ cron: iiiOpts.cron ? { adapter: mapCronAdapter(iiiOpts.cron) } : void 0,
954
+ stream: iiiOpts.stream ? {
955
+ host: iiiOpts.stream.host,
956
+ auth_function: iiiOpts.stream.authFunction,
957
+ adapter: mapStreamAdapter(iiiOpts.stream)
958
+ } : void 0,
959
+ restApi: iiiOpts.restApi ? {
960
+ host: iiiOpts.restApi.host,
961
+ default_timeout: iiiOpts.restApi.defaultTimeout,
962
+ concurrency_request_limit: iiiOpts.restApi.concurrencyRequestLimit
963
+ } : void 0,
964
+ observability: iiiOpts.observability ? {
965
+ enabled: iiiOpts.observability.enabled,
966
+ service_name: iiiOpts.observability.serviceName,
967
+ service_version: iiiOpts.observability.serviceVersion,
968
+ service_namespace: iiiOpts.observability.serviceNamespace,
969
+ exporter: iiiOpts.observability.exporter,
970
+ endpoint: iiiOpts.observability.endpoint,
971
+ sampling_ratio: iiiOpts.observability.samplingRatio,
972
+ memory_max_spans: iiiOpts.observability.memoryMaxSpans,
973
+ metrics_enabled: iiiOpts.observability.metricsEnabled,
974
+ metrics_exporter: iiiOpts.observability.metricsExporter,
975
+ metrics_retention_seconds: iiiOpts.observability.metricsRetentionSeconds,
976
+ metrics_max_count: iiiOpts.observability.metricsMaxCount,
977
+ logs_enabled: iiiOpts.observability.logsEnabled,
978
+ logs_exporter: iiiOpts.observability.logsExporter,
979
+ logs_max_count: iiiOpts.observability.logsMaxCount,
980
+ logs_retention_seconds: iiiOpts.observability.logsRetentionSeconds,
981
+ logs_batch_size: iiiOpts.observability.logsBatchSize,
982
+ logs_flush_interval_ms: iiiOpts.observability.logsFlushIntervalMs,
983
+ logs_sampling_ratio: iiiOpts.observability.logsSamplingRatio,
984
+ logs_console_output: iiiOpts.observability.logsConsoleOutput,
985
+ level: iiiOpts.observability.level,
986
+ format: iiiOpts.observability.format
987
+ } : void 0
682
988
  };
683
989
  }
684
-
685
- async function compileRegistryFromServerWorkers(layers, queuesDir = "queues", defaults) {
686
- const { workers, flowSources } = await scanWorkers(layers, queuesDir);
687
- const mergedWorkers = defaults ? mergeAllWorkerConfigs(workers, defaults) : workers;
688
- const { flows, eventIndex } = buildFlows(flowSources);
689
- const compiled = {
690
- workers: mergedWorkers,
691
- flows,
692
- eventIndex
990
+ function mapStateAdapter(a) {
991
+ if (!a?.adapter) return void 0;
992
+ if (a.adapter.type === "redis")
993
+ return { class: "modules::state::adapters::RedisAdapter", config: { redis_url: a.adapter.redisUrl } };
994
+ return {
995
+ class: "modules::state::adapters::KvStore",
996
+ config: { store_method: a.adapter.storeMethod, file_path: a.adapter.filePath }
693
997
  };
694
- return compiled;
695
998
  }
696
-
697
- const logger = useLogger("nvent");
698
- function watchQueueFiles(options) {
699
- const { nuxt, layerInfos, queuesDir, onRefresh } = options;
700
- const dirsToWatch = layerInfos.map((layer) => {
701
- const serverDir = layer.serverDir || join$1(layer.rootDir, "server");
702
- return join$1(serverDir, queuesDir);
703
- }).filter(Boolean);
704
- if (dirsToWatch.length === 0) {
705
- logger.warn("No queue directories found to watch");
706
- return;
707
- }
708
- logger.info("Watching queue directories:", dirsToWatch);
709
- const watcher = chokidar.watch(dirsToWatch, {
710
- ignoreInitial: true,
711
- persistent: true,
712
- ignorePermissionErrors: true,
713
- ignored: [
714
- "**/node_modules/**",
715
- "**/__pycache__/**",
716
- "**/.git/**",
717
- "**/dist/**",
718
- "**/.nuxt/**"
719
- ],
720
- // Watch only specific file types
721
- awaitWriteFinish: {
722
- stabilityThreshold: 100,
723
- pollInterval: 100
724
- }
725
- });
726
- logger.info("Chokidar watcher initialized");
727
- logger.info("Watched patterns:", dirsToWatch.map((dir) => `${dir}/**/*.{ts,js,py}`));
728
- watcher.on("ready", () => {
729
- const watched = watcher.getWatched();
730
- const fileCount = Object.values(watched).reduce((sum, files) => sum + files.length, 0);
731
- logger.success(`Queue file watcher ready - watching ${fileCount} files`);
732
- });
733
- const debouncedRefresh = debounce(onRefresh, 150);
734
- watcher.on("all", (event, path) => {
735
- logger.info(`Watcher event: ${event} - ${path}`);
736
- });
737
- watcher.on("add", async (path) => {
738
- logger.info(`Queue file added: ${path}`);
739
- await debouncedRefresh("add", path);
740
- });
741
- watcher.on("change", async (path) => {
742
- logger.info(`Queue file changed: ${path}`);
743
- await debouncedRefresh("change", path);
744
- });
745
- watcher.on("unlink", async (path) => {
746
- logger.info(`Queue file removed: ${path}`);
747
- await debouncedRefresh("unlink", path);
748
- });
749
- watcher.on("error", (error) => {
750
- logger.error("Watcher error:", error);
751
- });
752
- nuxt.hook("close", () => {
753
- logger.info("Closing queue file watcher");
754
- watcher.close();
755
- });
756
- return watcher;
999
+ function mapQueueAdapter(a) {
1000
+ if (!a) return void 0;
1001
+ if (a.type === "redis")
1002
+ return { class: "modules::queue::RedisAdapter", config: { redis_url: a.redisUrl } };
1003
+ if (a.type === "rabbitmq")
1004
+ return { class: "modules::queue::RabbitMQAdapter", config: { amqp_url: a.amqpUrl } };
1005
+ return {
1006
+ class: "modules::queue::BuiltinQueueAdapter",
1007
+ config: { store_method: a.storeMethod, file_path: a.filePath }
1008
+ };
1009
+ }
1010
+ function mapCronAdapter(a) {
1011
+ if (!a?.adapter) return void 0;
1012
+ if (a.adapter.type === "redis")
1013
+ return { class: "modules::cron::RedisCronAdapter", config: { redis_url: a.adapter.redisUrl } };
1014
+ return { class: "modules::cron::KvCronAdapter" };
1015
+ }
1016
+ function mapStreamAdapter(a) {
1017
+ if (!a?.adapter) return void 0;
1018
+ if (a.adapter.type === "redis")
1019
+ return { class: "modules::stream::adapters::RedisAdapter", config: { redis_url: a.adapter.redisUrl } };
1020
+ return {
1021
+ class: "modules::stream::adapters::KvStore",
1022
+ config: { store_method: a.adapter.storeMethod, file_path: a.adapter.filePath }
1023
+ };
757
1024
  }
758
1025
 
759
- function analyzeTriggerDefinitions(workers) {
760
- const triggers = [];
761
- const seenTriggers = /* @__PURE__ */ new Set();
762
- for (const worker of workers) {
763
- const triggerDef = worker.flow?.triggers?.define;
764
- if (!triggerDef) continue;
765
- if (seenTriggers.has(triggerDef.name)) continue;
766
- seenTriggers.add(triggerDef.name);
767
- triggers.push({
768
- name: triggerDef.name,
769
- type: triggerDef.type,
770
- scope: triggerDef.scope || "flow",
771
- displayName: triggerDef.displayName,
772
- description: triggerDef.description,
773
- source: `function:${worker.name}`,
774
- expectedSubscribers: triggerDef.expectedSubscribers,
775
- webhook: triggerDef.webhook,
776
- schedule: triggerDef.schedule,
777
- config: triggerDef.config,
778
- registeredAt: (/* @__PURE__ */ new Date()).toISOString(),
779
- registeredBy: "code"
780
- });
781
- }
782
- return triggers;
1026
+ function filePathToFunctionId(relPath) {
1027
+ const noExt = relPath.replace(/\.[a-zA-Z]+$/, "");
1028
+ return noExt.split(/[\\/]/).join("::");
783
1029
  }
784
- function analyzeTriggerSubscriptions(workers) {
785
- const subscriptions = [];
786
- for (const worker of workers) {
787
- if (!worker.flow?.triggers?.subscribe) continue;
788
- const flowNames = Array.isArray(worker.flow.names) ? worker.flow.names : [worker.flow.names];
789
- for (const flowName of flowNames) {
790
- for (const triggerName of worker.flow.triggers.subscribe) {
791
- subscriptions.push({
792
- triggerName,
793
- flowName,
794
- mode: worker.flow.triggers.mode || "auto",
795
- source: "config",
796
- registeredAt: (/* @__PURE__ */ new Date()).toISOString()
797
- });
798
- }
799
- }
1030
+ function detectPythonStandalone(absPath) {
1031
+ try {
1032
+ const src = readFileSync(absPath, "utf-8");
1033
+ return /['"]standalone['"]\s*:\s*True/.test(src);
1034
+ } catch {
1035
+ return false;
800
1036
  }
801
- return subscriptions;
802
1037
  }
803
- function buildTriggerIndex(subscriptions) {
804
- const triggerToFlows = /* @__PURE__ */ new Map();
805
- const flowToTriggers = /* @__PURE__ */ new Map();
806
- for (const sub of subscriptions) {
807
- if (!triggerToFlows.has(sub.triggerName)) {
808
- triggerToFlows.set(sub.triggerName, /* @__PURE__ */ new Set());
1038
+ async function scanFunctions(opts) {
1039
+ const { layerInfos, functionsDir = "functions" } = opts;
1040
+ const functions = [];
1041
+ const pythonFunctions = [];
1042
+ for (const layer of layerInfos) {
1043
+ const serverDir = layer.serverDir || join(layer.rootDir, "server");
1044
+ const fnDir = join(serverDir, functionsDir);
1045
+ if (!existsSync(fnDir)) continue;
1046
+ const [jsFiles, pyFiles] = await Promise.all([
1047
+ globby(["**/*.{ts,js,mts,mjs}"], {
1048
+ cwd: fnDir,
1049
+ absolute: false,
1050
+ ignore: ["**/*.d.ts", "**/*.test.*", "**/*.spec.*"]
1051
+ }),
1052
+ globby(["**/*.py"], {
1053
+ cwd: fnDir,
1054
+ absolute: false,
1055
+ ignore: ["**/__pycache__/**", "**/*.pyc"]
1056
+ })
1057
+ ]);
1058
+ for (const file of jsFiles) {
1059
+ functions.push({ id: filePathToFunctionId(file), absPath: join(fnDir, file), relativePath: file });
809
1060
  }
810
- triggerToFlows.get(sub.triggerName).add(sub.flowName);
811
- if (!flowToTriggers.has(sub.flowName)) {
812
- flowToTriggers.set(sub.flowName, /* @__PURE__ */ new Set());
1061
+ for (const file of pyFiles) {
1062
+ const absPath = join(fnDir, file);
1063
+ const standalone = detectPythonStandalone(absPath);
1064
+ pythonFunctions.push({ id: filePathToFunctionId(file), absPath, relativePath: file, standalone });
813
1065
  }
814
- flowToTriggers.get(sub.flowName).add(sub.triggerName);
815
1066
  }
816
- return { triggerToFlows, flowToTriggers };
817
- }
818
-
819
- function generateRegistryTemplate(registry) {
820
- return `// auto-generated by nvent
821
- export const registry = ${JSON.stringify(registry, null, 2)};
822
-
823
- export const useFunctionRegistry = () => registry;
824
-
825
- export default useFunctionRegistry;
826
- `;
1067
+ return { functions, pythonFunctions };
827
1068
  }
828
- function generateHandlersTemplate(registry) {
829
- const regWorkers = registry?.workers || [];
830
- const lines = [];
831
- const entries = [];
832
- regWorkers.filter((w) => w?.kind === "ts" && (w?.runtype ? w.runtype !== "task" : registry?.runner?.ts?.isolate !== "task")).forEach((w, i) => {
833
- const varName = `h${i}`;
834
- const moduleVar = `m${i}`;
835
- const src = w && (w.absPath || w.abs || w.cwd && w.file && join(w.cwd, w.file) || w.entry || w.path) || "";
836
- if (!src) return;
837
- const importPath = String(src);
838
- lines.push(`import ${varName} from '${importPath}'`);
839
- lines.push(`import * as ${moduleVar} from '${importPath}'`);
840
- const queue = String(w?.queue?.name || `w${i}`);
841
- const id = String(w?.id || `w${i}`);
842
- const absPath = importPath;
843
- entries.push(`{ queue: '${queue}', id: '${id}', absPath: '${absPath}', handler: ${varName}, module: ${moduleVar} }`);
1069
+ function generateIiiRegistryTemplate(scanned, pythonPathRewrite) {
1070
+ const lines = ["// auto-generated by nvent \u2014 do not edit", ""];
1071
+ lines.push(`function _stepEntry(ns, fallbackName, absPath) {`);
1072
+ lines.push(` const def = ns.default`);
1073
+ lines.push(` const meta = ns.meta`);
1074
+ lines.push(` const triggers = ns.triggers`);
1075
+ lines.push(` if (def?.__nventStep) {`);
1076
+ lines.push(` const name = def.name ?? fallbackName`);
1077
+ lines.push(` return { name, description: def.description, handler: def.handler, triggers: (def.triggers ?? []).map(t => ({ ...t, function_id: name })), enqueues: def.enqueues ?? [], flows: def.flows ?? [], stream: def.stream, filePath: absPath }`);
1078
+ lines.push(` }`);
1079
+ lines.push(` const name = meta?.name ?? fallbackName`);
1080
+ lines.push(` return { name, description: meta?.description, handler: def, triggers: (triggers ?? []).map(t => ({ ...t, function_id: name })), enqueues: [], flows: [], stream: undefined, filePath: absPath }`);
1081
+ lines.push(`}`);
1082
+ lines.push("");
1083
+ const entries = scanned.functions.map((fn, i) => {
1084
+ const ns = `fn${i}`;
1085
+ lines.push(`import * as ${ns} from ${genString(fn.absPath)}`);
1086
+ return `_stepEntry(${ns}, ${genString(fn.id)}, ${genString(fn.absPath)})`;
844
1087
  });
845
- return `// auto-generated by nvent
846
- ${lines.join("\n")}
847
-
848
- export const handlers = [
849
- ${entries.join(",\n ")}
850
- ]
851
-
852
- export const useWorkerHandlers = () => handlers;
853
-
854
- export default useWorkerHandlers;
855
- `;
1088
+ lines.push("");
1089
+ lines.push(`export const registry = {`);
1090
+ lines.push(` functions: [${entries.join(", ")}],`);
1091
+ lines.push(` get triggers() { return this.functions.flatMap(f => f.triggers ?? []) },`);
1092
+ lines.push(`}`);
1093
+ lines.push("");
1094
+ lines.push("export default registry");
1095
+ lines.push("");
1096
+ lines.push(`export const pythonFunctions = ${JSON.stringify(scanned.pythonFunctions.map((fn) => ({
1097
+ id: fn.id,
1098
+ absPath: pythonPathRewrite?.get(fn.absPath) ?? fn.absPath,
1099
+ standalone: fn.standalone
1100
+ })))}`);
1101
+ lines.push("");
1102
+ return lines.join("\n");
856
1103
  }
857
- function generateAnalyzedFlowsTemplate(registry) {
858
- const flows = registry?.flows || {};
859
- const workers = registry?.workers || [];
860
- const workerMetaMap = /* @__PURE__ */ new Map();
861
- for (const worker of workers) {
862
- workerMetaMap.set(worker.id, {
863
- runtime: worker.kind === "py" ? "python" : "nodejs",
864
- runtype: worker.runtype,
865
- queue: worker.queue,
866
- worker: worker.worker,
867
- emits: worker.flow?.emits
1104
+
1105
+ async function installPythonRequirements(requirementsPath, pythonBin, logLevel) {
1106
+ if (!existsSync(requirementsPath)) return;
1107
+ if (logLevel !== "none") console.log(`[nvent] Installing Python requirements from ${requirementsPath}`);
1108
+ return new Promise((resolve) => {
1109
+ const proc = spawn(pythonBin, ["-m", "pip", "install", "-r", requirementsPath, "--quiet"], {
1110
+ stdio: ["ignore", "pipe", "pipe"]
1111
+ });
1112
+ let stderr = "";
1113
+ proc.stderr?.on("data", (d) => {
1114
+ stderr += d.toString();
1115
+ });
1116
+ proc.on("exit", (code) => {
1117
+ if (code !== 0 && logLevel !== "none") console.error(`[nvent] pip install failed (code=${code}):
1118
+ ${stderr.trim()}`);
1119
+ else if (code === 0 && logLevel !== "none") console.log("[nvent] Python requirements installed");
1120
+ resolve();
1121
+ });
1122
+ proc.on("error", (err) => {
1123
+ if (logLevel !== "none") console.error(`[nvent] pip install error: ${err.message}`);
1124
+ resolve();
868
1125
  });
869
- }
870
- const analyzedFlows = Object.entries(flows).map(([id, meta]) => {
871
- const workerMeta = meta?.entry ? workerMetaMap.get(meta.entry.workerId) : void 0;
872
- const entry = meta?.entry ? {
873
- ...meta.entry,
874
- runtime: workerMeta?.runtime,
875
- runtype: workerMeta?.runtype,
876
- emits: workerMeta?.emits
877
- } : void 0;
878
- const steps = meta?.steps ? Object.fromEntries(
879
- Object.entries(meta.steps).map(([stepName, stepData]) => {
880
- const stepWorkerMeta = workerMetaMap.get(stepData.workerId);
881
- return [
882
- stepName,
883
- {
884
- ...stepData,
885
- runtime: stepWorkerMeta?.runtime,
886
- runtype: stepWorkerMeta?.runtype,
887
- emits: stepWorkerMeta?.emits
888
- }
889
- ];
890
- })
891
- ) : {};
892
- const flowMeta = {
893
- id,
894
- entry,
895
- steps
896
- };
897
- const analyzed = analyzeFlow(flowMeta, registry.config);
898
- return {
899
- ...flowMeta,
900
- analyzed: {
901
- levels: analyzed.levels,
902
- maxLevel: analyzed.maxLevel,
903
- stallTimeout: analyzed.stallTimeout,
904
- awaitPatterns: analyzed.awaitPatterns,
905
- steps: analyzed.steps
906
- }
907
- };
908
1126
  });
909
- return `// auto-generated by nvent
910
- export const analyzedFlows = ${JSON.stringify(analyzedFlows, null, 2)};
911
-
912
- export const useAnalyzedFlows = () => analyzedFlows;
913
-
914
- export default useAnalyzedFlows;
915
- `;
916
- }
917
- function generateTriggerRegistryTemplate(registry) {
918
- const workers = registry?.workers || [];
919
- const triggers = analyzeTriggerDefinitions(workers);
920
- const subscriptions = analyzeTriggerSubscriptions(workers);
921
- const index = buildTriggerIndex(subscriptions);
922
- const triggerToFlows = {};
923
- for (const [trigger, subs] of index.triggerToFlows.entries()) {
924
- triggerToFlows[trigger] = Array.from(subs);
925
- }
926
- const flowToTriggers = {};
927
- for (const [flow, triggers2] of index.flowToTriggers.entries()) {
928
- flowToTriggers[flow] = Array.from(triggers2);
929
- }
930
- const triggerRegistry = {
931
- triggers,
932
- subscriptions,
933
- index: {
934
- triggerToFlows,
935
- flowToTriggers
936
- },
937
- compiledAt: (/* @__PURE__ */ new Date()).toISOString()
938
- };
939
- return `// auto-generated by nvent
940
- export const triggerRegistry = ${JSON.stringify(triggerRegistry, null, 2)};
941
-
942
- export const useTriggerRegistry = () => triggerRegistry;
943
-
944
- export default useTriggerRegistry;
945
- `;
946
- }
947
- function generateAdapterTypesTemplate(resolverFn) {
948
- return `// Auto-generated adapter type definitions
949
- // External adapter packages can import these types
950
-
951
- // Queue Adapter
952
- export type {
953
- QueueAdapter,
954
- JobInput,
955
- Job,
956
- JobsQuery,
957
- JobOptions,
958
- JobState,
959
- ScheduleOptions,
960
- JobCounts,
961
- QueueEvent,
962
- WorkerHandler,
963
- WorkerContext,
964
- WorkerOptions,
965
- } from ${JSON.stringify(resolverFn("./runtime/adapters/interfaces/queue"))}
966
-
967
- // Stream Adapter
968
- export type {
969
- StreamAdapter,
970
- StreamEvent,
971
- SubscribeOptions,
972
- SubscriptionHandle,
973
- } from ${JSON.stringify(resolverFn("./runtime/adapters/interfaces/stream"))}
974
-
975
- // Store Adapter
976
- export type {
977
- StoreAdapter,
978
- EventRecord,
979
- EventReadOptions,
980
- EventSubscription,
981
- ListOptions,
982
- } from ${JSON.stringify(resolverFn("./runtime/adapters/interfaces/store"))}
983
-
984
- // Flow Types
985
- export type {
986
- FlowStats,
987
- StartFlowResult,
988
- CancelFlowResult,
989
- RestartFlowResult,
990
- RunningFlow,
991
- FlowComposable,
992
- } from ${JSON.stringify(resolverFn("./runtime/nitro/utils/useFlow"))}
993
-
994
- // Runner Context Types
995
- export type {
996
- QueueJob,
997
- RunLogger,
998
- RunState,
999
- RunContextFlow,
1000
- RunContext,
1001
- NodeHandler,
1002
- } from ${JSON.stringify(resolverFn("./runtime/worker/node/runner"))}
1003
-
1004
- // Event Types
1005
- export type {
1006
- EventType,
1007
- BaseEvent,
1008
- StepEvent,
1009
- FlowStartEvent,
1010
- FlowCompletedEvent,
1011
- FlowFailedEvent,
1012
- FlowCancelEvent,
1013
- FlowStalledEvent,
1014
- StepStartedEvent,
1015
- StepCompletedEvent,
1016
- StepFailedEvent,
1017
- StepRetryEvent,
1018
- LogEvent,
1019
- EmitEvent,
1020
- StateEvent,
1021
- FlowEvent,
1022
- } from ${JSON.stringify(resolverFn("./runtime/events/types"))}
1023
-
1024
- // Adapter Registry
1025
- export type { AdapterRegistry } from ${JSON.stringify(resolverFn("./runtime/adapters/registry"))}`;
1026
1127
  }
1027
-
1028
- function getServerImports(resolverFn, buildDir) {
1029
- return [
1030
- // Generated templates
1031
- {
1032
- name: "useFunctionRegistry",
1033
- as: "$useFunctionRegistry",
1034
- from: resolverFn(buildDir + "/function-registry")
1035
- },
1036
- {
1037
- name: "useWorkerHandlers",
1038
- as: "$useWorkerHandlers",
1039
- from: resolverFn(buildDir + "/worker-handlers")
1040
- },
1041
- {
1042
- name: "useAnalyzedFlows",
1043
- as: "$useAnalyzedFlows",
1044
- from: resolverFn(buildDir + "/analyzed-flows")
1045
- },
1046
- {
1047
- name: "useTriggerRegistry",
1048
- as: "$useTriggerRegistry",
1049
- from: resolverFn(buildDir + "/trigger-registry")
1050
- },
1051
- // Core utilities for user code
1052
- {
1053
- name: "defineFunctionConfig",
1054
- from: resolverFn("./runtime/nitro/utils/defineFunctionConfig")
1055
- },
1056
- {
1057
- name: "defineFunction",
1058
- from: resolverFn("./runtime/nitro/utils/defineFunction")
1059
- },
1060
- // Composables users may need in server code
1061
- {
1062
- name: "useEventManager",
1063
- from: resolverFn("./runtime/nitro/utils/useEventManager")
1064
- },
1065
- {
1066
- name: "usePeerManager",
1067
- from: resolverFn("./runtime/nitro/utils/wsPeerManager")
1068
- },
1069
- {
1070
- name: "useNventLogger",
1071
- from: resolverFn("./runtime/nitro/utils/useNventLogger")
1072
- },
1073
- {
1074
- name: "useHookRegistry",
1075
- from: resolverFn("./runtime/nitro/utils/useHookRegistry")
1076
- },
1077
- {
1078
- name: "useAwait",
1079
- from: resolverFn("./runtime/nitro/utils/useAwait")
1080
- },
1081
- {
1082
- name: "useRunContext",
1083
- from: resolverFn("./runtime/nitro/utils/useRunContext")
1084
- },
1085
- {
1086
- name: "defineAwaitRegisterHook",
1087
- from: resolverFn("./runtime/nitro/utils/defineHooks")
1088
- },
1089
- {
1090
- name: "defineAwaitResolveHook",
1091
- from: resolverFn("./runtime/nitro/utils/defineHooks")
1092
- },
1093
- {
1094
- name: "defineAwaitTimeoutHook",
1095
- from: resolverFn("./runtime/nitro/utils/defineHooks")
1096
- },
1097
- // Adapter composables
1098
- {
1099
- name: "useQueueAdapter",
1100
- from: resolverFn("./runtime/nitro/utils/adapters")
1101
- },
1102
- {
1103
- name: "useStoreAdapter",
1104
- from: resolverFn("./runtime/nitro/utils/adapters")
1105
- },
1106
- {
1107
- name: "useStreamAdapter",
1108
- from: resolverFn("./runtime/nitro/utils/adapters")
1109
- },
1110
- {
1111
- name: "useStateAdapter",
1112
- from: resolverFn("./runtime/nitro/utils/adapters")
1113
- },
1114
- {
1115
- name: "getAdapters",
1116
- from: resolverFn("./runtime/nitro/utils/adapters")
1117
- },
1118
- {
1119
- name: "setAdapters",
1120
- from: resolverFn("./runtime/nitro/utils/adapters")
1121
- },
1122
- // Runtime utilities
1123
- {
1124
- name: "useStreamTopics",
1125
- from: resolverFn("./runtime/nitro/utils/useStreamTopics")
1126
- },
1127
- {
1128
- name: "useTrigger",
1129
- from: resolverFn("./runtime/nitro/utils/useTrigger")
1130
- },
1131
- {
1132
- name: "useFlow",
1133
- from: resolverFn("./runtime/nitro/utils/useFlow")
1134
- },
1135
- // Scheduler
1136
- {
1137
- name: "useScheduler",
1138
- from: resolverFn("./runtime/scheduler")
1139
- },
1140
- {
1141
- name: "initializeScheduler",
1142
- from: resolverFn("./runtime/scheduler")
1143
- },
1144
- {
1145
- name: "shutdownScheduler",
1146
- from: resolverFn("./runtime/scheduler")
1147
- },
1148
- // Adapter registration utilities for external modules
1149
- {
1150
- name: "registerQueueAdapter",
1151
- from: resolverFn("./runtime/nitro/utils/registerAdapter")
1152
- },
1153
- {
1154
- name: "registerStreamAdapter",
1155
- from: resolverFn("./runtime/nitro/utils/registerAdapter")
1156
- },
1157
- {
1158
- name: "registerStoreAdapter",
1159
- from: resolverFn("./runtime/nitro/utils/registerAdapter")
1160
- },
1161
- // Adapter base utilities for external adapter packages
1162
- {
1163
- name: "createStoreValidator",
1164
- from: resolverFn("./runtime/adapters/base/store-validator")
1165
- }
1166
- ];
1128
+ function installNventPyToSitePackages(pythonBin, nventPyContent) {
1129
+ if (!nventPyContent) return;
1130
+ try {
1131
+ const sitePackages = execFileSync(pythonBin, [
1132
+ "-c",
1133
+ "import site; dirs = site.getsitepackages(); print(dirs[0])"
1134
+ ], { encoding: "utf-8", timeout: 5e3 }).trim();
1135
+ writeFileSync(join(sitePackages, "nvent.py"), nventPyContent, "utf-8");
1136
+ } catch {
1137
+ }
1167
1138
  }
1168
1139
 
1169
1140
  const packageJson = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf-8"));
@@ -1172,140 +1143,230 @@ const meta = {
1172
1143
  version: packageJson.version,
1173
1144
  configKey: "nvent"
1174
1145
  };
1175
- const module$1 = defineNuxtModule().with({
1146
+ const III_REGISTRY_TEMPLATE = "iii-registry.mjs";
1147
+ const module$1 = defineNuxtModule({
1176
1148
  meta,
1177
1149
  defaults: {},
1150
+ moduleDependencies: {
1151
+ "@nvent-addon/app": {
1152
+ optional: true
1153
+ }
1154
+ },
1178
1155
  async setup(options, nuxt) {
1179
1156
  const { resolve } = createResolver(import.meta.url);
1180
- const userConfig = nuxt.options[meta.configKey] || {};
1181
- const mergedOptions = { ...userConfig, ...options };
1182
- const config = normalizeModuleOptions(mergedOptions);
1183
- nuxt.hook("nitro:config", (nitro) => {
1184
- const redisConfig = getRedisStorageConfig(config);
1185
- nitro.storage = defu(nitro.storage || {}, {
1186
- redis: {
1187
- driver: "redis",
1188
- ...redisConfig
1189
- // base namespace handled in provider; keep storage base default
1190
- }
1191
- });
1192
- nitro.experimental = defu(nitro.experimental || {}, {
1193
- websocket: true
1194
- });
1195
- });
1196
- const runtimeConfig = nuxt.options.runtimeConfig;
1197
- runtimeConfig.nvent = defu(toRuntimeConfig(config), runtimeConfig.nvent || {});
1198
- if (!runtimeConfig.nvent) runtimeConfig.nvent = {};
1199
- runtimeConfig.nvent.rootDir = nuxt.options.rootDir;
1157
+ const PYTHON_RUNTIME_SRC = resolve("./runtime/python/worker_runtime.py");
1158
+ const PYTHON_NVENT_HELPER_SRC = resolve("./runtime/python/nvent.py");
1159
+ const userConfig = nuxt.options[meta.configKey] ?? {};
1160
+ const opts = { ...userConfig, ...options };
1161
+ const iiiOpts = opts.iii ?? {};
1162
+ const functionsDir = opts.functions?.dir ?? "functions";
1163
+ const pythonBin = opts.functions?.python?.devPath ? join(nuxt.options.rootDir, opts.functions.python.devPath) : "python3";
1164
+ const skipPython = opts.functions?.python?.skip ?? false;
1165
+ const wsUrl = iiiOpts.wsUrl ?? "ws://localhost:49134";
1166
+ const mode = iiiOpts.mode ?? "local";
1167
+ const managed = iiiOpts.managed ?? mode === "local";
1168
+ const version = iiiOpts.version ?? "latest";
1169
+ const logLevel = iiiOpts.logLevel ?? "warn";
1170
+ const consoleCfg = typeof iiiOpts.console === "object" ? iiiOpts.console : {};
1171
+ installNventPyToSitePackages(pythonBin, readFileSync(PYTHON_NVENT_HELPER_SRC, "utf-8"));
1172
+ if (hasNuxtModule("@nvent-addon/app")) {
1173
+ const nventOpts = nuxt.options;
1174
+ nventOpts.nventapp ??= {};
1175
+ nventOpts.nventapp.route ??= opts.app?.enabled !== false;
1176
+ if (opts.app?.routePath) nventOpts.nventapp.routePath ??= opts.app.routePath;
1177
+ if (opts.app?.layout !== void 0) nventOpts.nventapp.layout ??= opts.app.layout;
1178
+ }
1179
+ const engineCfg = buildEngineConfig(iiiOpts);
1180
+ const engineConfigYaml = generateIiiConfigYaml(engineCfg);
1181
+ writeIiiConfig(join(nuxt.options.buildDir, "iii-config.yaml"), engineCfg);
1182
+ const rc = nuxt.options.runtimeConfig;
1183
+ rc.nvent = {
1184
+ ...rc.nvent ?? {},
1185
+ iii: {
1186
+ wsUrl,
1187
+ httpPort: engineCfg.httpPort,
1188
+ httpHost: iiiOpts.httpHost ?? "localhost",
1189
+ wsPort: engineCfg.wsPort,
1190
+ streamPort: engineCfg.streamPort,
1191
+ managed,
1192
+ mode,
1193
+ version,
1194
+ modules: engineCfg.modules,
1195
+ logLevel,
1196
+ // YAML config embedded at build time so the production lifecycle plugin can
1197
+ // write iii-config.yaml without needing confbox or rebuilding from options.
1198
+ engineConfigYaml
1199
+ },
1200
+ python: {
1201
+ runtimeContent: skipPython ? "" : readFileSync(PYTHON_RUNTIME_SRC, "utf-8"),
1202
+ nventHelperContent: skipPython ? "" : readFileSync(PYTHON_NVENT_HELPER_SRC, "utf-8"),
1203
+ skip: skipPython
1204
+ },
1205
+ console: {
1206
+ enabled: !!iiiOpts.console,
1207
+ version: consoleCfg.version ?? "",
1208
+ port: consoleCfg.port ?? 3113,
1209
+ host: consoleCfg.host ?? "localhost",
1210
+ flow: consoleCfg.flow ?? true
1211
+ }
1212
+ };
1200
1213
  const layerInfos = nuxt.options._layers.map((l) => ({
1201
1214
  rootDir: l.config.rootDir,
1202
- serverDir: l.config?.serverDir || join(l.config.rootDir, "server")
1215
+ serverDir: l.config?.serverDir ?? join(l.config.rootDir, "server")
1203
1216
  }));
1204
- const { worker, ...queueOptions } = config.queue;
1205
- const defaultConfigs = {
1206
- queue: queueOptions,
1207
- worker
1208
- };
1209
- const compiledRegistry = await compileRegistryFromServerWorkers(layerInfos, config.dir || "functions", defaultConfigs);
1210
- const compiledWithMeta = defu(compiledRegistry, {
1211
- version: 1,
1212
- compiledAt: (/* @__PURE__ */ new Date()).toISOString(),
1213
- provider: { name: config.queue.adapter === "postgres" ? "pgboss" : "bullmq" },
1214
- logger: { name: "console", level: "info" },
1215
- runner: { ts: { isolate: "inprocess" }, py: { enabled: false, cmd: "python3", importMode: "file" } },
1216
- flows: {},
1217
- eventIndex: {},
1218
- config: {
1219
- flow: config.flow,
1220
- queue: config.queue
1217
+ let lastScanned = await scanFunctions({ layerInfos, functionsDir });
1218
+ let pythonPathRewrite;
1219
+ if (!nuxt.options.dev && !skipPython && lastScanned.pythonFunctions.length > 0) {
1220
+ pythonPathRewrite = /* @__PURE__ */ new Map();
1221
+ for (const fn of lastScanned.pythonFunctions) {
1222
+ for (const layer of layerInfos) {
1223
+ const fnDir = join(layer.serverDir, functionsDir);
1224
+ if (fn.absPath.startsWith(fnDir)) {
1225
+ pythonPathRewrite.set(fn.absPath, relative(fnDir, fn.absPath));
1226
+ break;
1227
+ }
1228
+ }
1221
1229
  }
1222
- });
1223
- const compiledSnapshot = JSON.parse(JSON.stringify(compiledWithMeta));
1224
- let lastCompiledRegistry = compiledSnapshot;
1225
- const REGISTRY_TEMPLATE = "function-registry.mjs";
1226
- const HANDLERS_TEMPLATE = "worker-handlers.mjs";
1227
- const ANALYZED_FLOWS_TEMPLATE = "analyzed-flows.mjs";
1228
- const TRIGGER_REGISTRY_TEMPLATE = "trigger-registry.mjs";
1229
- for (const templateName of [REGISTRY_TEMPLATE, HANDLERS_TEMPLATE, ANALYZED_FLOWS_TEMPLATE, TRIGGER_REGISTRY_TEMPLATE]) {
1230
- const templatePath = resolve(nuxt.options.buildDir, templateName);
1231
- nuxt.options.build.transpile.push(templatePath);
1232
1230
  }
1233
1231
  addTemplate({
1234
- filename: REGISTRY_TEMPLATE,
1235
- write: true,
1236
- getContents: () => generateRegistryTemplate(lastCompiledRegistry)
1237
- });
1238
- addTemplate({
1239
- filename: HANDLERS_TEMPLATE,
1240
- write: true,
1241
- getContents: () => generateHandlersTemplate(lastCompiledRegistry)
1242
- });
1243
- addTemplate({
1244
- filename: ANALYZED_FLOWS_TEMPLATE,
1245
- write: true,
1246
- getContents: () => generateAnalyzedFlowsTemplate(lastCompiledRegistry)
1247
- });
1248
- addTemplate({
1249
- filename: TRIGGER_REGISTRY_TEMPLATE,
1232
+ filename: III_REGISTRY_TEMPLATE,
1250
1233
  write: true,
1251
- getContents: () => generateTriggerRegistryTemplate(lastCompiledRegistry)
1234
+ getContents: () => generateIiiRegistryTemplate(lastScanned, pythonPathRewrite)
1252
1235
  });
1253
- addTypeTemplate({
1254
- filename: "types/nvent-adapters.d.ts",
1255
- getContents: () => generateAdapterTypesTemplate(resolve)
1256
- });
1257
- nuxt.options.alias["#nvent/adapters"] = resolve(nuxt.options.buildDir, "types/nvent-adapters");
1258
- addServerPlugin(resolve("./runtime/nitro/plugins/00.adapters"));
1259
- addServerPlugin(resolve("./runtime/nitro/plugins/01.ws-lifecycle"));
1260
- addServerPlugin(resolve("./runtime/nitro/plugins/02.workers"));
1261
- addServerPlugin(resolve("./runtime/nitro/plugins/03.triggers"));
1262
- addServerHandler({
1263
- route: "/api/_webhook/await/:flowName/:runId/:stepName",
1264
- handler: resolve("./runtime/nitro/routes/webhook.await")
1265
- });
1266
- addServerHandler({
1267
- route: "/api/_webhook/trigger/:triggerName",
1268
- handler: resolve("./runtime/nitro/routes/webhook.trigger")
1269
- });
1270
- addServerImports(getServerImports(resolve, nuxt.options.buildDir));
1271
- const refreshRegistry = async (reason, changedPath) => {
1272
- const functionsDir = config.dir || "functions";
1273
- const updatedRegistry = await compileRegistryFromServerWorkers(layerInfos, functionsDir, defaultConfigs);
1274
- lastCompiledRegistry = JSON.parse(JSON.stringify(defu(updatedRegistry, {
1275
- version: 1,
1276
- compiledAt: (/* @__PURE__ */ new Date()).toISOString(),
1277
- provider: { name: config.queue.adapter === "postgres" ? "pgboss" : "bullmq" },
1278
- logger: { name: "console", level: "info" },
1279
- runner: { ts: { isolate: "inprocess" }, py: { enabled: false, cmd: "python3", importMode: "file" } },
1280
- flows: {},
1281
- eventIndex: {},
1282
- config: {
1283
- flow: config.flow,
1284
- queue: config.queue
1285
- }
1286
- })));
1287
- console.log(`[nvent] registry refreshed (${reason})`, changedPath || "");
1288
- console.log(`[nvent] new registry has ${lastCompiledRegistry.workers?.length || 0} workers`);
1289
- console.log(`[nvent] new registry compiled at: ${lastCompiledRegistry.compiledAt}`);
1290
- await updateTemplates({
1291
- filter: (template) => {
1292
- const match = template.filename === REGISTRY_TEMPLATE || template.filename === HANDLERS_TEMPLATE || template.filename === ANALYZED_FLOWS_TEMPLATE || template.filename === TRIGGER_REGISTRY_TEMPLATE;
1293
- if (match) {
1294
- console.log(`[nvent] updating template: ${template.filename}`);
1295
- }
1296
- return match;
1297
- }
1298
- });
1299
- console.log(`[nvent] templates updated`);
1236
+ const registryTemplatePath = resolve(nuxt.options.buildDir, III_REGISTRY_TEMPLATE);
1237
+ nuxt.options.alias["#nvent/iii-registry"] = registryTemplatePath;
1238
+ nuxt.options.build.transpile.push(registryTemplatePath);
1239
+ addServerPlugin(resolve("./runtime/nitro/plugins/00.iii-lifecycle"));
1240
+ addServerPlugin(resolve("./runtime/nitro/plugins/01.iii-worker"));
1241
+ const nitroOpts = nuxt.options.nitro ??= {};
1242
+ nitroOpts.routeRules ??= {};
1243
+ nitroOpts.routeRules["/functions/**"] = {
1244
+ proxy: `http://${iiiOpts.httpHost ?? "localhost"}:${engineCfg.httpPort}/**`
1300
1245
  };
1246
+ nitroOpts.experimental ??= {};
1247
+ nitroOpts.experimental.websocket = true;
1248
+ addServerHandler({ route: "/stream/**", handler: resolve("./runtime/nitro/routes/stream-proxy") });
1249
+ addServerImports([
1250
+ { from: resolve("./runtime/nitro/utils/useIii"), name: "useIii" },
1251
+ { from: resolve("./runtime/nitro/utils/useIii"), name: "useIiiHealth" },
1252
+ { from: resolve("./runtime/nitro/utils/useIii"), name: "getContext" },
1253
+ { from: resolve("./runtime/nitro/utils/defineFunction"), name: "defineFunction" },
1254
+ { from: resolve("./runtime/nitro/utils/defineFunction"), name: "FunctionContext" }
1255
+ ]);
1256
+ addImports([
1257
+ { from: resolve("./runtime/app/composables/useFunctionCall"), name: "useFunctionCall" },
1258
+ { from: resolve("./runtime/app/composables/useNventStream"), name: "useNventStream" }
1259
+ ]);
1301
1260
  if (nuxt.options.dev) {
1302
- const functionsDir = config.dir || "functions";
1303
- watchQueueFiles({
1304
- nuxt,
1305
- layerInfos,
1306
- queuesDir: functionsDir,
1307
- onRefresh: refreshRegistry
1308
- });
1261
+ const nventDir = join(nuxt.options.rootDir, "node_modules", ".nvent");
1262
+ const binDir = join(nventDir, "bin");
1263
+ if (managed && mode === "local") {
1264
+ const binaryPath = await ensureIiiEngine({ binDir, version, logLevel });
1265
+ const consoleBinaryPath = iiiOpts.console ? await ensureIiiConsole({ binDir, version: consoleCfg.version ?? version, logLevel }) : void 0;
1266
+ const nventConfigPath = join(nventDir, "iii-config.yaml");
1267
+ writeIiiConfig(nventConfigPath, engineCfg);
1268
+ const engine = createEngineManager({
1269
+ binaryPath,
1270
+ configPath: nventConfigPath,
1271
+ httpPort: engineCfg.httpPort,
1272
+ wsPort: engineCfg.wsPort,
1273
+ logLevel
1274
+ });
1275
+ await engine.start();
1276
+ nuxt.hook("close", async () => {
1277
+ await engine.stop();
1278
+ });
1279
+ if (consoleBinaryPath) {
1280
+ const consoleManager = new ConsoleManager({
1281
+ binaryPath: consoleBinaryPath,
1282
+ port: consoleCfg.port ?? 3113,
1283
+ enginePort: engineCfg.httpPort,
1284
+ bridgePort: engineCfg.wsPort,
1285
+ flow: consoleCfg.flow ?? true,
1286
+ logLevel
1287
+ });
1288
+ await consoleManager.start();
1289
+ nuxt.hook("close", async () => {
1290
+ await consoleManager.stop();
1291
+ });
1292
+ }
1293
+ }
1294
+ if (!skipPython) {
1295
+ const nventDir2 = join(nuxt.options.rootDir, "node_modules", ".nvent");
1296
+ for (const reqPath of [
1297
+ join(nuxt.options.rootDir, "requirements.txt"),
1298
+ join(nuxt.options.rootDir, "server", "requirements.txt")
1299
+ ]) {
1300
+ await installPythonRequirements(reqPath, pythonBin, logLevel);
1301
+ }
1302
+ const workersDir = join(nventDir2, "workers");
1303
+ const pythonOrchestrator = new PythonWorkersOrchestrator(
1304
+ workersDir,
1305
+ readFileSync(PYTHON_RUNTIME_SRC, "utf-8"),
1306
+ readFileSync(PYTHON_NVENT_HELPER_SRC, "utf-8"),
1307
+ wsUrl,
1308
+ pythonBin,
1309
+ logLevel
1310
+ );
1311
+ await pythonOrchestrator.start(lastScanned.pythonFunctions);
1312
+ nuxt.hook("close", async () => {
1313
+ await pythonOrchestrator.stop();
1314
+ });
1315
+ nuxt.__nventPythonOrchestrator = pythonOrchestrator;
1316
+ }
1317
+ const dirsToWatch = layerInfos.map(
1318
+ (l) => join(l.serverDir ?? join(l.rootDir, "server"), functionsDir)
1319
+ );
1320
+ const refresh = debounce(async (changedPath) => {
1321
+ lastScanned = await scanFunctions({ layerInfos, functionsDir });
1322
+ await updateTemplates({ filter: (t) => t.filename === III_REGISTRY_TEMPLATE });
1323
+ console.log(`[nvent] registry refreshed${changedPath ? ` (${changedPath})` : ""}`);
1324
+ if (!skipPython && changedPath?.endsWith(".py")) {
1325
+ const orchestrator = nuxt.__nventPythonOrchestrator;
1326
+ await orchestrator?.onFileChanged(changedPath, lastScanned.pythonFunctions);
1327
+ }
1328
+ }, 200);
1329
+ chokidar.watch(dirsToWatch, {
1330
+ ignoreInitial: true,
1331
+ ignored: ["**/node_modules/**", "**/.nuxt/**", "**/__pycache__/**", "**/*.pyc"],
1332
+ awaitWriteFinish: { stabilityThreshold: 100, pollInterval: 100 }
1333
+ }).on("all", (_event, path) => refresh(path));
1334
+ } else {
1335
+ if (managed && mode === "local") {
1336
+ const binDir = join(nuxt.options.rootDir, "node_modules", ".nvent", "bin");
1337
+ const binaryPath = await ensureIiiEngine({ binDir, version, logLevel });
1338
+ const consoleBinaryPath = iiiOpts.console ? await ensureIiiConsole({ binDir, version: consoleCfg.version ?? version, logLevel }) : void 0;
1339
+ nuxt.hook("nitro:build:public-assets", async (nitro) => {
1340
+ const outputNventDir = join(nitro.options.output.dir, "nvent");
1341
+ const outputBinDir = join(outputNventDir, "bin");
1342
+ mkdirSync(outputBinDir, { recursive: true });
1343
+ copyFileSync(binaryPath, join(outputBinDir, basename(binaryPath)));
1344
+ if (consoleBinaryPath) copyFileSync(consoleBinaryPath, join(outputBinDir, basename(consoleBinaryPath)));
1345
+ writeFileSync(join(outputNventDir, "iii-config.yaml"), engineConfigYaml, "utf-8");
1346
+ console.log("[nvent] Engine binaries + config copied to .output/nvent/");
1347
+ });
1348
+ }
1349
+ if (!skipPython) {
1350
+ nuxt.hook("nitro:build:public-assets", async (nitro) => {
1351
+ const outputNventDir = join(nitro.options.output.dir, "nvent");
1352
+ const workersDir = join(outputNventDir, "workers");
1353
+ mkdirSync(workersDir, { recursive: true });
1354
+ copyFileSync(PYTHON_RUNTIME_SRC, join(workersDir, "_runtime.py"));
1355
+ copyFileSync(PYTHON_NVENT_HELPER_SRC, join(workersDir, "nvent.py"));
1356
+ for (const fn of lastScanned.pythonFunctions) {
1357
+ for (const layer of layerInfos) {
1358
+ const fnDir = join(layer.serverDir, functionsDir);
1359
+ if (fn.absPath.startsWith(fnDir)) {
1360
+ const dest = join(outputNventDir, "functions", relative(fnDir, fn.absPath));
1361
+ mkdirSync(join(dest, ".."), { recursive: true });
1362
+ copyFileSync(fn.absPath, dest);
1363
+ break;
1364
+ }
1365
+ }
1366
+ }
1367
+ console.log("[nvent] Python worker files copied to .output/nvent/");
1368
+ });
1369
+ }
1309
1370
  }
1310
1371
  }
1311
1372
  });