nvent 1.0.0-alpha.2 → 1.0.0-alpha.5

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 (33) hide show
  1. package/dist/module.d.mts +158 -0
  2. package/dist/module.json +1 -1
  3. package/dist/module.mjs +210 -55
  4. package/dist/runtime/app/composables/useFunctionCall.d.ts +1 -1
  5. package/dist/runtime/app/composables/useIii.d.ts +24 -0
  6. package/dist/runtime/app/composables/useIii.js +9 -0
  7. package/dist/runtime/app/composables/useIiiStream.d.ts +22 -0
  8. package/dist/runtime/app/composables/{useNventStream.js → useIiiStream.js} +4 -9
  9. package/dist/runtime/app/plugins/iii.client.d.ts +13 -0
  10. package/dist/runtime/app/plugins/iii.client.js +9 -0
  11. package/dist/runtime/nitro/plugins/01.iii-worker.js +49 -0
  12. package/dist/runtime/nitro/routes/browser-proxy.d.ts +13 -0
  13. package/dist/runtime/nitro/routes/browser-proxy.js +55 -0
  14. package/dist/runtime/nitro/routes/stream-proxy.d.ts +2 -2
  15. package/dist/runtime/nitro/routes/stream-proxy.js +2 -2
  16. package/dist/runtime/nitro/server.d.ts +14 -0
  17. package/dist/runtime/nitro/server.js +3 -0
  18. package/dist/runtime/nitro/utils/browserAuthToken.d.ts +10 -0
  19. package/dist/runtime/nitro/utils/browserAuthToken.js +32 -0
  20. package/dist/runtime/nitro/utils/defineFunction.d.ts +63 -258
  21. package/dist/runtime/nitro/utils/defineFunction.js +12 -221
  22. package/dist/runtime/nitro/utils/engine.d.ts +11 -0
  23. package/dist/runtime/nitro/utils/engine.js +79 -17
  24. package/dist/runtime/nitro/utils/useIii.d.ts +10 -11
  25. package/dist/runtime/nitro/utils/useIii.js +2 -3
  26. package/dist/runtime/nitro/utils/workers/node.d.ts +17 -10
  27. package/dist/runtime/nitro/utils/workers/node.js +25 -51
  28. package/dist/runtime/nitro/utils/workers/python.js +13 -12
  29. package/dist/runtime/python/nvent.py +288 -160
  30. package/dist/runtime/python/worker_runtime.py +141 -44
  31. package/dist/runtime/virtual.d.ts +23 -5
  32. package/package.json +9 -8
  33. package/dist/runtime/app/composables/useNventStream.d.ts +0 -23
package/dist/module.d.mts CHANGED
@@ -38,9 +38,13 @@ interface NventIiiOptions {
38
38
  type: 'kv';
39
39
  storeMethod?: 'file_based' | 'in_memory';
40
40
  filePath?: string;
41
+ saveIntervalMs?: number;
41
42
  } | {
42
43
  type: 'redis';
43
44
  redisUrl?: string;
45
+ } | {
46
+ type: 'bridge';
47
+ bridgeUrl: string;
44
48
  };
45
49
  };
46
50
  /**
@@ -52,12 +56,26 @@ interface NventIiiOptions {
52
56
  type: 'builtin';
53
57
  storeMethod?: 'file_based' | 'in_memory';
54
58
  filePath?: string;
59
+ saveIntervalMs?: number;
60
+ maxAttempts?: number;
61
+ backoffMs?: number;
62
+ concurrency?: number;
63
+ pollIntervalMs?: number;
64
+ /** Processing order. 'concurrent' (parallel) or 'fifo' (sequential). Default: 'concurrent' */
65
+ mode?: 'concurrent' | 'fifo';
55
66
  } | {
56
67
  type: 'redis';
57
68
  redisUrl?: string;
58
69
  } | {
59
70
  type: 'rabbitmq';
60
71
  amqpUrl?: string;
72
+ maxAttempts?: number;
73
+ prefetchCount?: number;
74
+ /** 'standard' or 'quorum' (HA replicated). Default: 'standard' */
75
+ queueMode?: 'standard' | 'quorum';
76
+ } | {
77
+ type: 'bridge';
78
+ bridgeUrl: string;
61
79
  };
62
80
  queueConfigs?: Record<string, {
63
81
  type?: 'standard' | 'fifo';
@@ -72,6 +90,11 @@ interface NventIiiOptions {
72
90
  cron?: {
73
91
  adapter?: {
74
92
  type: 'kv';
93
+ lockTtlMs?: number;
94
+ lockIndex?: string;
95
+ storeMethod?: 'file_based' | 'in_memory';
96
+ filePath?: string;
97
+ saveIntervalMs?: number;
75
98
  } | {
76
99
  type: 'redis';
77
100
  redisUrl?: string;
@@ -85,9 +108,13 @@ interface NventIiiOptions {
85
108
  type: 'kv';
86
109
  storeMethod?: 'file_based' | 'in_memory';
87
110
  filePath?: string;
111
+ saveIntervalMs?: number;
88
112
  } | {
89
113
  type: 'redis';
90
114
  redisUrl?: string;
115
+ } | {
116
+ type: 'bridge';
117
+ bridgeUrl: string;
91
118
  };
92
119
  };
93
120
  /** REST API module extra settings */
@@ -95,6 +122,13 @@ interface NventIiiOptions {
95
122
  host?: string;
96
123
  defaultTimeout?: number;
97
124
  concurrencyRequestLimit?: number;
125
+ /** CORS configuration for browser clients */
126
+ cors?: {
127
+ /** Origins allowed to make requests. Use '*' for any, or list specific domains. */
128
+ allowedOrigins?: string[];
129
+ /** HTTP methods permitted for cross-origin requests. */
130
+ allowedMethods?: string[];
131
+ };
98
132
  };
99
133
  /** OtelModule (observability) configuration */
100
134
  observability?: {
@@ -124,6 +158,36 @@ interface NventIiiOptions {
124
158
  logsFlushIntervalMs?: number;
125
159
  logsSamplingRatio?: number;
126
160
  logsConsoleOutput?: boolean;
161
+ /** Advanced sampling rules — override samplingRatio for matched operations. */
162
+ sampling?: {
163
+ default?: number;
164
+ parentBased?: boolean;
165
+ rules?: Array<{
166
+ operation?: string;
167
+ service?: string;
168
+ rate: number;
169
+ }>;
170
+ rateLimit?: {
171
+ maxTracesPerSecond?: number;
172
+ };
173
+ };
174
+ /** Alert rules — trigger a webhook or function when a metric crosses a threshold. */
175
+ alerts?: Array<{
176
+ name: string;
177
+ metric: string;
178
+ threshold: number;
179
+ operator: '>' | '>=' | '<' | '<=' | '==' | '!=';
180
+ windowSeconds: number;
181
+ enabled?: boolean;
182
+ cooldownSeconds?: number;
183
+ action: {
184
+ type: 'webhook';
185
+ url: string;
186
+ } | {
187
+ type: 'function';
188
+ path: string;
189
+ };
190
+ }>;
127
191
  level?: 'trace' | 'debug' | 'info' | 'warn' | 'error';
128
192
  format?: 'default' | 'json';
129
193
  };
@@ -147,10 +211,104 @@ interface NventIiiOptions {
147
211
  /** Enable the Flow visualization page */
148
212
  flow?: boolean;
149
213
  };
214
+ /** PubSub worker — topic-based event fanout across functions. */
215
+ pubsub?: {
216
+ adapter?: {
217
+ type: 'local';
218
+ } | {
219
+ type: 'redis';
220
+ redisUrl?: string;
221
+ };
222
+ };
223
+ /**
224
+ * HTTP Functions worker — enables outbound HTTP calls from the engine.
225
+ * Required for functions registered with HttpInvocationConfig.
226
+ */
227
+ httpFunctions?: {
228
+ security?: {
229
+ /** URL patterns allowed for outbound requests. Use '*' to allow all. */
230
+ urlAllowlist?: string[];
231
+ /** Block requests to private/internal IP ranges (SSRF prevention). Default: true */
232
+ blockPrivateIps?: boolean;
233
+ /** Require HTTPS for all outbound requests. Default: true */
234
+ requireHttps?: boolean;
235
+ };
236
+ };
237
+ /**
238
+ * Additional iii-worker-manager instance with RBAC.
239
+ * nvent uses this automatically when browser SDK is enabled.
240
+ * Can also be configured manually for custom auth scenarios.
241
+ */
242
+ workerManager?: {
243
+ rbac?: {
244
+ /** Port for the RBAC worker manager. Default: 49135 */
245
+ port?: number;
246
+ /** Function ID called on every WebSocket upgrade for auth. */
247
+ authFunctionId?: string;
248
+ /** Functions the connecting worker is allowed to invoke (patterns supported). */
249
+ exposeFunctions?: string[];
250
+ /** Function invoked before each handler — enrich or audit requests. */
251
+ middlewareFunctionId?: string;
252
+ /** Allow workers to register their own function handlers. Default: true */
253
+ allowFunctionRegistration?: boolean;
254
+ /** Allow workers to register new trigger types. Default: false */
255
+ allowTriggerTypeRegistration?: boolean;
256
+ /** Prefix prepended to every function the worker registers. */
257
+ functionRegistrationPrefix?: string;
258
+ /** TTL for signed browser auth tokens generated by the Nuxt proxy. Default: 120 */
259
+ tokenTtlSeconds?: number;
260
+ /** Allow browser connections without a custom resolver returning user context. Default: true */
261
+ allowAnonymous?: boolean;
262
+ /** Optional path (relative to project root) to a custom browser auth resolver module. */
263
+ authResolverPath?: string;
264
+ };
265
+ };
266
+ /**
267
+ * Bridge client workers — connects this engine to remote iii instances.
268
+ * Each entry creates an `iii-bridge` worker in the config.
269
+ */
270
+ bridge?: Array<{
271
+ url: string;
272
+ serviceId: string;
273
+ serviceName?: string;
274
+ expose?: Array<{
275
+ localFunction: string;
276
+ remoteFunction?: string;
277
+ }>;
278
+ forward?: Array<{
279
+ localFunction: string;
280
+ remoteFunction: string;
281
+ timeoutMs?: number;
282
+ }>;
283
+ }>;
284
+ /**
285
+ * Exec workers — spawns external processes alongside the engine.
286
+ * Each entry creates an `iii-exec` worker in the config.
287
+ */
288
+ exec?: Array<{
289
+ watch?: string[];
290
+ exec: string[];
291
+ }>;
292
+ /** Anonymous usage telemetry. Set enabled: false to opt out. */
293
+ telemetry?: {
294
+ enabled?: boolean;
295
+ apiKey?: string;
296
+ sdkApiKey?: string;
297
+ heartbeatIntervalSecs?: number;
298
+ };
150
299
  };
151
300
  functions?: {
152
301
  /** Directory under server/ where functions are, relative to serverDir (default: 'functions') */
153
302
  dir?: string;
303
+ /**
304
+ * Function ID prefix for this layer's functions.
305
+ * Set in a layer's `nuxt.config.ts` to namespace its functions.
306
+ * Priority: this value → `$meta.name` → package.json name → directory name.
307
+ * Set to `''` (empty string) to explicitly opt out of any prefix.
308
+ * Has no effect in the root project (always un-prefixed).
309
+ * Example: 'myorg::auth'
310
+ */
311
+ prefix?: string;
154
312
  python?: {
155
313
  /**
156
314
  * Path to the Python executable used **in development only** (e.g. a virtualenv).
package/dist/module.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nvent",
3
- "version": "1.0.0-alpha.2",
3
+ "version": "1.0.0-alpha.5",
4
4
  "configKey": "nvent",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
package/dist/module.mjs CHANGED
@@ -1,6 +1,7 @@
1
- import { join, dirname, relative, basename } from 'node:path';
2
- import { defineNuxtModule, createResolver, hasNuxtModule, addTemplate, addServerPlugin, addServerHandler, addServerImports, addImports, updateTemplates } from '@nuxt/kit';
1
+ import { join, dirname, basename, relative, parse } from 'node:path';
2
+ import { defineNuxtModule, createResolver, extendViteConfig, hasNuxtModule, addTemplate, addServerPlugin, addServerHandler, addServerImports, addImports, addPlugin, updateTemplates } from '@nuxt/kit';
3
3
  import { existsSync, mkdirSync, chmodSync, createWriteStream, unlinkSync, writeFileSync, readFileSync, copyFileSync } from 'node:fs';
4
+ import { randomUUID } from 'node:crypto';
4
5
  import { exec, execFileSync, spawn } from 'node:child_process';
5
6
  import { promisify } from 'node:util';
6
7
  import { pipeline } from 'node:stream/promises';
@@ -844,34 +845,36 @@ function defaultIiiEngineConfig() {
844
845
  }
845
846
  function defaultStateAdapter(stateDir) {
846
847
  return {
847
- class: "modules::state::adapters::KvStore",
848
+ name: "kv",
848
849
  config: { store_method: "file_based", file_path: "./data/state_store" }
849
850
  };
850
851
  }
851
852
  function defaultQueueAdapter(queueDir) {
852
853
  return {
853
- class: "modules::queue::BuiltinQueueAdapter",
854
+ name: "builtin",
854
855
  config: { store_method: "file_based", file_path: "./data/queue_store" }
855
856
  };
856
857
  }
857
858
  function defaultCronAdapter() {
858
- return { class: "modules::cron::KvCronAdapter" };
859
+ return { name: "kv" };
859
860
  }
860
861
  function defaultStreamAdapter() {
861
- return { class: "modules::stream::adapters::KvStore" };
862
+ return { name: "kv" };
862
863
  }
863
864
  function generateIiiConfigYaml(cfg) {
864
- const modules = [];
865
+ const workers = [];
866
+ workers.push({ name: "iii-worker-manager", config: { port: cfg.wsPort } });
865
867
  const restApiConfig = {
866
868
  port: cfg.restApi?.port ?? cfg.httpPort
867
869
  };
868
870
  if (cfg.restApi?.host) restApiConfig.host = cfg.restApi.host;
869
871
  if (cfg.restApi?.default_timeout != null) restApiConfig.default_timeout = cfg.restApi.default_timeout;
870
872
  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 });
873
+ if (cfg.restApi?.cors) restApiConfig.cors = cfg.restApi.cors;
874
+ workers.push({ name: "iii-http", config: restApiConfig });
872
875
  if (cfg.modules.state !== false) {
873
876
  const adapter = cfg.state?.adapter ?? defaultStateAdapter();
874
- modules.push({ class: "modules::state::StateModule", config: { adapter } });
877
+ workers.push({ name: "iii-state", config: { adapter } });
875
878
  }
876
879
  if (cfg.modules.queue !== false) {
877
880
  const adapter = cfg.queue?.adapter ?? defaultQueueAdapter();
@@ -880,11 +883,11 @@ function generateIiiConfigYaml(cfg) {
880
883
  if (queueConfigs && Object.keys(queueConfigs).length > 0) {
881
884
  queueModCfg.queue_configs = queueConfigs;
882
885
  }
883
- modules.push({ class: "modules::queue::QueueModule", config: queueModCfg });
886
+ workers.push({ name: "iii-queue", config: queueModCfg });
884
887
  }
885
888
  if (cfg.modules.cron !== false) {
886
889
  const adapter = cfg.cron?.adapter ?? defaultCronAdapter();
887
- modules.push({ class: "modules::cron::CronModule", config: { adapter } });
890
+ workers.push({ name: "iii-cron", config: { adapter } });
888
891
  }
889
892
  if (cfg.modules.stream !== false) {
890
893
  const streamCfg = {
@@ -894,7 +897,7 @@ function generateIiiConfigYaml(cfg) {
894
897
  if (cfg.stream?.auth_function !== void 0) streamCfg.auth_function = cfg.stream.auth_function;
895
898
  const streamAdapter = cfg.stream?.adapter ?? defaultStreamAdapter();
896
899
  streamCfg.adapter = streamAdapter;
897
- modules.push({ class: "modules::stream::StreamModule", config: streamCfg });
900
+ workers.push({ name: "iii-stream", config: streamCfg });
898
901
  }
899
902
  if (cfg.modules.observability !== false) {
900
903
  const oCfg = cfg.observability ?? {};
@@ -922,10 +925,34 @@ function generateIiiConfigYaml(cfg) {
922
925
  if (oCfg.logs_console_output != null) otelConfig.logs_console_output = oCfg.logs_console_output;
923
926
  if (oCfg.level) otelConfig.level = oCfg.level;
924
927
  if (oCfg.format) otelConfig.format = oCfg.format;
925
- modules.push({ class: "modules::observability::OtelModule", config: otelConfig });
928
+ if (oCfg.sampling) otelConfig.sampling = oCfg.sampling;
929
+ if (oCfg.alerts?.length) otelConfig.alerts = oCfg.alerts;
930
+ workers.push({ name: "iii-observability", config: otelConfig });
931
+ }
932
+ if (cfg.modules.pubsub) {
933
+ const pubsubCfg = {};
934
+ if (cfg.pubsub?.adapter) pubsubCfg.adapter = cfg.pubsub.adapter;
935
+ workers.push({ name: "iii-pubsub", config: Object.keys(pubsubCfg).length ? pubsubCfg : void 0 });
936
+ }
937
+ if (cfg.modules.httpFunctions) {
938
+ const hfCfg = {};
939
+ if (cfg.httpFunctions?.security) hfCfg.security = cfg.httpFunctions.security;
940
+ workers.push({ name: "iii-http-functions", config: Object.keys(hfCfg).length ? hfCfg : void 0 });
941
+ }
942
+ for (const bridgeCfg of cfg.bridge ?? []) {
943
+ workers.push({ name: "iii-bridge", config: bridgeCfg });
944
+ }
945
+ for (const execCfg of cfg.exec ?? []) {
946
+ workers.push({ name: "iii-exec", config: execCfg });
947
+ }
948
+ if (cfg.workerManagerRbac) {
949
+ workers.push({ name: "iii-worker-manager", config: cfg.workerManagerRbac });
950
+ }
951
+ if (cfg.telemetry) {
952
+ workers.push({ name: "iii-telemetry", config: cfg.telemetry });
926
953
  }
927
954
  return `# Auto-generated by nvent \u2014 do not edit manually
928
- ` + stringifyYAML({ port: cfg.wsPort, modules });
955
+ ` + stringifyYAML({ workers });
929
956
  }
930
957
  function writeIiiConfig(outputPath, cfg) {
931
958
  mkdirSync(dirname(outputPath), { recursive: true });
@@ -939,7 +966,7 @@ function buildEngineConfig(iiiOpts) {
939
966
  wsPort: iiiOpts.wsPort ?? 49134,
940
967
  httpPort: iiiOpts.httpPort ?? 3111,
941
968
  streamPort: iiiOpts.streamPort ?? 3112,
942
- modules: { state: true, queue: true, cron: true, observability: true, stream: true, ...iiiOpts.modules },
969
+ modules: { state: true, queue: true, cron: true, observability: true, stream: true, pubsub: false, httpFunctions: false, exec: false, telemetry: false, ...iiiOpts.modules },
943
970
  state: iiiOpts.state ? { adapter: mapStateAdapter(iiiOpts.state) } : void 0,
944
971
  queue: iiiOpts.queue ? {
945
972
  adapter: mapQueueAdapter(iiiOpts.queue?.adapter),
@@ -959,7 +986,44 @@ function buildEngineConfig(iiiOpts) {
959
986
  restApi: iiiOpts.restApi ? {
960
987
  host: iiiOpts.restApi.host,
961
988
  default_timeout: iiiOpts.restApi.defaultTimeout,
962
- concurrency_request_limit: iiiOpts.restApi.concurrencyRequestLimit
989
+ concurrency_request_limit: iiiOpts.restApi.concurrencyRequestLimit,
990
+ cors: iiiOpts.restApi.cors ? {
991
+ allowed_origins: iiiOpts.restApi.cors.allowedOrigins,
992
+ allowed_methods: iiiOpts.restApi.cors.allowedMethods
993
+ } : void 0
994
+ } : void 0,
995
+ pubsub: iiiOpts.pubsub ? { adapter: mapPubSubAdapter(iiiOpts.pubsub) } : void 0,
996
+ httpFunctions: iiiOpts.httpFunctions ? {
997
+ security: iiiOpts.httpFunctions.security ? {
998
+ url_allowlist: iiiOpts.httpFunctions.security.urlAllowlist,
999
+ block_private_ips: iiiOpts.httpFunctions.security.blockPrivateIps,
1000
+ require_https: iiiOpts.httpFunctions.security.requireHttps
1001
+ } : void 0
1002
+ } : void 0,
1003
+ workerManagerRbac: iiiOpts.workerManager?.rbac ? {
1004
+ port: iiiOpts.workerManager.rbac.port ?? 49135,
1005
+ rbac: {
1006
+ auth_function_id: iiiOpts.workerManager.rbac.authFunctionId ?? "nvent::browser::auth",
1007
+ expose_functions: iiiOpts.workerManager.rbac.exposeFunctions,
1008
+ middleware_function_id: iiiOpts.workerManager.rbac.middlewareFunctionId,
1009
+ allow_function_registration: iiiOpts.workerManager.rbac.allowFunctionRegistration,
1010
+ allow_trigger_type_registration: iiiOpts.workerManager.rbac.allowTriggerTypeRegistration,
1011
+ function_registration_prefix: iiiOpts.workerManager.rbac.functionRegistrationPrefix
1012
+ }
1013
+ } : void 0,
1014
+ bridge: iiiOpts.bridge?.map((b) => ({
1015
+ url: b.url,
1016
+ service_id: b.serviceId,
1017
+ service_name: b.serviceName,
1018
+ expose: b.expose?.map((e) => ({ local_function: e.localFunction, remote_function: e.remoteFunction })),
1019
+ forward: b.forward?.map((f) => ({ local_function: f.localFunction, remote_function: f.remoteFunction, timeout_ms: f.timeoutMs }))
1020
+ })),
1021
+ exec: iiiOpts.exec?.map((e) => ({ watch: e.watch, exec: e.exec })),
1022
+ telemetry: iiiOpts.telemetry ? {
1023
+ enabled: iiiOpts.telemetry.enabled,
1024
+ api_key: iiiOpts.telemetry.apiKey,
1025
+ sdk_api_key: iiiOpts.telemetry.sdkApiKey,
1026
+ heartbeat_interval_secs: iiiOpts.telemetry.heartbeatIntervalSecs
963
1027
  } : void 0,
964
1028
  observability: iiiOpts.observability ? {
965
1029
  enabled: iiiOpts.observability.enabled,
@@ -982,6 +1046,24 @@ function buildEngineConfig(iiiOpts) {
982
1046
  logs_flush_interval_ms: iiiOpts.observability.logsFlushIntervalMs,
983
1047
  logs_sampling_ratio: iiiOpts.observability.logsSamplingRatio,
984
1048
  logs_console_output: iiiOpts.observability.logsConsoleOutput,
1049
+ sampling: iiiOpts.observability.sampling ? {
1050
+ default: iiiOpts.observability.sampling.default,
1051
+ parent_based: iiiOpts.observability.sampling.parentBased,
1052
+ rules: iiiOpts.observability.sampling.rules,
1053
+ rate_limit: iiiOpts.observability.sampling.rateLimit ? {
1054
+ max_traces_per_second: iiiOpts.observability.sampling.rateLimit.maxTracesPerSecond
1055
+ } : void 0
1056
+ } : void 0,
1057
+ alerts: iiiOpts.observability.alerts?.map((a) => ({
1058
+ name: a.name,
1059
+ metric: a.metric,
1060
+ threshold: a.threshold,
1061
+ operator: a.operator,
1062
+ window_seconds: a.windowSeconds,
1063
+ enabled: a.enabled,
1064
+ cooldown_seconds: a.cooldownSeconds,
1065
+ action: a.action
1066
+ })),
985
1067
  level: iiiOpts.observability.level,
986
1068
  format: iiiOpts.observability.format
987
1069
  } : void 0
@@ -989,39 +1071,60 @@ function buildEngineConfig(iiiOpts) {
989
1071
  }
990
1072
  function mapStateAdapter(a) {
991
1073
  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 } };
1074
+ if (a.adapter.type === "redis") return { name: "redis", config: { redis_url: a.adapter.redisUrl } };
1075
+ if (a.adapter.type === "bridge") return { name: "bridge", config: { bridge_url: a.adapter.bridgeUrl } };
994
1076
  return {
995
- class: "modules::state::adapters::KvStore",
996
- config: { store_method: a.adapter.storeMethod, file_path: a.adapter.filePath }
1077
+ name: "kv",
1078
+ config: { store_method: a.adapter.storeMethod, file_path: a.adapter.filePath, save_interval_ms: a.adapter.saveIntervalMs }
997
1079
  };
998
1080
  }
999
1081
  function mapQueueAdapter(a) {
1000
1082
  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 } };
1083
+ if (a.type === "redis") return { name: "redis", config: { redis_url: a.redisUrl } };
1084
+ if (a.type === "rabbitmq") return { name: "rabbitmq", config: { amqp_url: a.amqpUrl, max_attempts: a.maxAttempts, prefetch_count: a.prefetchCount, queue_mode: a.queueMode } };
1085
+ if (a.type === "bridge") return { name: "bridge", config: { bridge_url: a.bridgeUrl } };
1005
1086
  return {
1006
- class: "modules::queue::BuiltinQueueAdapter",
1007
- config: { store_method: a.storeMethod, file_path: a.filePath }
1087
+ name: "builtin",
1088
+ config: {
1089
+ store_method: a.storeMethod,
1090
+ file_path: a.filePath,
1091
+ save_interval_ms: a.saveIntervalMs,
1092
+ max_attempts: a.maxAttempts,
1093
+ backoff_ms: a.backoffMs,
1094
+ concurrency: a.concurrency,
1095
+ poll_interval_ms: a.pollIntervalMs,
1096
+ mode: a.mode
1097
+ }
1008
1098
  };
1009
1099
  }
1010
1100
  function mapCronAdapter(a) {
1011
1101
  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" };
1102
+ if (a.adapter.type === "redis") return { name: "redis", config: { redis_url: a.adapter.redisUrl } };
1103
+ return {
1104
+ name: "kv",
1105
+ config: {
1106
+ lock_ttl_ms: a.adapter.lockTtlMs,
1107
+ lock_index: a.adapter.lockIndex,
1108
+ store_method: a.adapter.storeMethod,
1109
+ file_path: a.adapter.filePath,
1110
+ save_interval_ms: a.adapter.saveIntervalMs
1111
+ }
1112
+ };
1015
1113
  }
1016
1114
  function mapStreamAdapter(a) {
1017
1115
  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 } };
1116
+ if (a.adapter.type === "redis") return { name: "redis", config: { redis_url: a.adapter.redisUrl } };
1117
+ if (a.adapter.type === "bridge") return { name: "bridge", config: { bridge_url: a.adapter.bridgeUrl } };
1020
1118
  return {
1021
- class: "modules::stream::adapters::KvStore",
1022
- config: { store_method: a.adapter.storeMethod, file_path: a.adapter.filePath }
1119
+ name: "kv",
1120
+ config: { store_method: a.adapter.storeMethod, file_path: a.adapter.filePath, save_interval_ms: a.adapter.saveIntervalMs }
1023
1121
  };
1024
1122
  }
1123
+ function mapPubSubAdapter(a) {
1124
+ if (!a?.adapter) return void 0;
1125
+ if (a.adapter.type === "redis") return { name: "redis", config: { redis_url: a.adapter.redisUrl } };
1126
+ return { name: "local" };
1127
+ }
1025
1128
 
1026
1129
  function filePathToFunctionId(relPath) {
1027
1130
  const noExt = relPath.replace(/\.[a-zA-Z]+$/, "");
@@ -1042,6 +1145,7 @@ async function scanFunctions(opts) {
1042
1145
  for (const layer of layerInfos) {
1043
1146
  const serverDir = layer.serverDir || join(layer.rootDir, "server");
1044
1147
  const fnDir = join(serverDir, functionsDir);
1148
+ const prefix = layer.prefix ? `${layer.prefix}::` : "";
1045
1149
  if (!existsSync(fnDir)) continue;
1046
1150
  const [jsFiles, pyFiles] = await Promise.all([
1047
1151
  globby(["**/*.{ts,js,mts,mjs}"], {
@@ -1056,34 +1160,27 @@ async function scanFunctions(opts) {
1056
1160
  })
1057
1161
  ]);
1058
1162
  for (const file of jsFiles) {
1059
- functions.push({ id: filePathToFunctionId(file), absPath: join(fnDir, file), relativePath: file });
1163
+ functions.push({ id: `${prefix}${filePathToFunctionId(file)}`, absPath: join(fnDir, file), relativePath: file });
1060
1164
  }
1061
1165
  for (const file of pyFiles) {
1062
1166
  const absPath = join(fnDir, file);
1063
1167
  const standalone = detectPythonStandalone(absPath);
1064
- pythonFunctions.push({ id: filePathToFunctionId(file), absPath, relativePath: file, standalone });
1168
+ pythonFunctions.push({ id: `${prefix}${filePathToFunctionId(file)}`, absPath, relativePath: file, standalone });
1065
1169
  }
1066
1170
  }
1067
1171
  return { functions, pythonFunctions };
1068
1172
  }
1069
1173
  function generateIiiRegistryTemplate(scanned, pythonPathRewrite) {
1070
1174
  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 }`);
1175
+ lines.push(`function _entry(ns, id, absPath) {`);
1176
+ lines.push(` const fn = ns.default`);
1177
+ lines.push(` return { id, description: fn.description, handler: fn.handler, triggers: (fn.triggers ?? []).map(t => ({ ...t, function_id: id })), request_format: fn.request_format, response_format: fn.response_format, filePath: absPath }`);
1081
1178
  lines.push(`}`);
1082
1179
  lines.push("");
1083
1180
  const entries = scanned.functions.map((fn, i) => {
1084
1181
  const ns = `fn${i}`;
1085
1182
  lines.push(`import * as ${ns} from ${genString(fn.absPath)}`);
1086
- return `_stepEntry(${ns}, ${genString(fn.id)}, ${genString(fn.absPath)})`;
1183
+ return `_entry(${ns}, ${genString(fn.id)}, ${genString(fn.absPath)})`;
1087
1184
  });
1088
1185
  lines.push("");
1089
1186
  lines.push(`export const registry = {`);
@@ -1102,11 +1199,29 @@ function generateIiiRegistryTemplate(scanned, pythonPathRewrite) {
1102
1199
  return lines.join("\n");
1103
1200
  }
1104
1201
 
1202
+ function writePyrightConfig(options) {
1203
+ try {
1204
+ const { rootDir, devPath, includePaths } = options;
1205
+ const binDir = dirname(devPath);
1206
+ const venvDir = dirname(binDir);
1207
+ const venv = basename(venvDir);
1208
+ const venvPath = dirname(venvDir) || ".";
1209
+ const include = includePaths.map((p) => relative(rootDir, p)).filter((p) => !p.startsWith(".."));
1210
+ const config = { venvPath, venv };
1211
+ if (include.length > 0) config.include = include;
1212
+ writeFileSync(
1213
+ join(rootDir, "pyrightconfig.json"),
1214
+ JSON.stringify(config, null, 2) + "\n",
1215
+ "utf-8"
1216
+ );
1217
+ } catch {
1218
+ }
1219
+ }
1105
1220
  async function installPythonRequirements(requirementsPath, pythonBin, logLevel) {
1106
1221
  if (!existsSync(requirementsPath)) return;
1107
1222
  if (logLevel !== "none") console.log(`[nvent] Installing Python requirements from ${requirementsPath}`);
1108
1223
  return new Promise((resolve) => {
1109
- const proc = spawn(pythonBin, ["-m", "pip", "install", "-r", requirementsPath, "--quiet"], {
1224
+ const proc = spawn(pythonBin, ["-m", "pip", "install", "-r", requirementsPath, "--upgrade", "--quiet"], {
1110
1225
  stdio: ["ignore", "pipe", "pipe"]
1111
1226
  });
1112
1227
  let stderr = "";
@@ -1156,6 +1271,11 @@ const module$1 = defineNuxtModule({
1156
1271
  const { resolve } = createResolver(import.meta.url);
1157
1272
  const PYTHON_RUNTIME_SRC = resolve("./runtime/python/worker_runtime.py");
1158
1273
  const PYTHON_NVENT_HELPER_SRC = resolve("./runtime/python/nvent.py");
1274
+ extendViteConfig((config) => {
1275
+ config.optimizeDeps ||= {};
1276
+ config.optimizeDeps.include ||= [];
1277
+ config.optimizeDeps.include.push("iii-browser-sdk");
1278
+ });
1159
1279
  const userConfig = nuxt.options[meta.configKey] ?? {};
1160
1280
  const opts = { ...userConfig, ...options };
1161
1281
  const iiiOpts = opts.iii ?? {};
@@ -1188,11 +1308,20 @@ const module$1 = defineNuxtModule({
1188
1308
  httpHost: iiiOpts.httpHost ?? "localhost",
1189
1309
  wsPort: engineCfg.wsPort,
1190
1310
  streamPort: engineCfg.streamPort,
1311
+ browserPort: engineCfg.workerManagerRbac?.port ?? iiiOpts.workerManager?.rbac?.port ?? 49135,
1191
1312
  managed,
1192
1313
  mode,
1193
1314
  version,
1194
1315
  modules: engineCfg.modules,
1195
1316
  logLevel,
1317
+ browserAuthFunctionId: iiiOpts.workerManager?.rbac?.authFunctionId ?? "nvent::browser::auth",
1318
+ browserAuth: {
1319
+ // Signed, short-lived token is minted by /_iii/browser and validated by nvent::browser::auth.
1320
+ secret: process.env.NVENT_BROWSER_AUTH_SECRET ?? randomUUID(),
1321
+ tokenTtlSeconds: iiiOpts.workerManager?.rbac?.tokenTtlSeconds ?? 120,
1322
+ allowAnonymous: iiiOpts.workerManager?.rbac?.allowAnonymous ?? true,
1323
+ authResolverPath: iiiOpts.workerManager?.rbac?.authResolverPath
1324
+ },
1196
1325
  // YAML config embedded at build time so the production lifecycle plugin can
1197
1326
  // write iii-config.yaml without needing confbox or rebuilding from options.
1198
1327
  engineConfigYaml
@@ -1210,9 +1339,27 @@ const module$1 = defineNuxtModule({
1210
1339
  flow: consoleCfg.flow ?? true
1211
1340
  }
1212
1341
  };
1213
- const layerInfos = nuxt.options._layers.map((l) => ({
1342
+ function resolveLayerPrefix(layer, isRoot) {
1343
+ if (isRoot) return void 0;
1344
+ const layerCfg = layer.config;
1345
+ const explicit = layerCfg?.nvent?.functions?.prefix;
1346
+ if (typeof explicit === "string") return explicit || void 0;
1347
+ const metaName = layerCfg?.$meta?.name;
1348
+ if (metaName) return metaName;
1349
+ const pkgPath = join(layer.config.rootDir, "package.json");
1350
+ if (existsSync(pkgPath)) {
1351
+ try {
1352
+ const pkgName = JSON.parse(readFileSync(pkgPath, "utf-8")).name ?? "";
1353
+ if (pkgName) return pkgName.split("/").pop().replace(/^nuxt-/, "");
1354
+ } catch {
1355
+ }
1356
+ }
1357
+ return parse(layer.config.rootDir).name;
1358
+ }
1359
+ const layerInfos = nuxt.options._layers.map((l, i) => ({
1214
1360
  rootDir: l.config.rootDir,
1215
- serverDir: l.config?.serverDir ?? join(l.config.rootDir, "server")
1361
+ serverDir: l.config?.serverDir ?? join(l.config.rootDir, "server"),
1362
+ prefix: resolveLayerPrefix(l, i === 0)
1216
1363
  }));
1217
1364
  let lastScanned = await scanFunctions({ layerInfos, functionsDir });
1218
1365
  let pythonPathRewrite;
@@ -1245,19 +1392,27 @@ const module$1 = defineNuxtModule({
1245
1392
  };
1246
1393
  nitroOpts.experimental ??= {};
1247
1394
  nitroOpts.experimental.websocket = true;
1248
- addServerHandler({ route: "/stream/**", handler: resolve("./runtime/nitro/routes/stream-proxy") });
1395
+ addServerHandler({ route: "/_iii/stream/**", handler: resolve("./runtime/nitro/routes/stream-proxy") });
1396
+ addServerHandler({ route: "/_iii/browser", handler: resolve("./runtime/nitro/routes/browser-proxy") });
1249
1397
  addServerImports([
1250
1398
  { from: resolve("./runtime/nitro/utils/useIii"), name: "useIii" },
1251
1399
  { 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" }
1400
+ { from: resolve("./runtime/nitro/utils/defineFunction"), name: "defineFunction" }
1255
1401
  ]);
1402
+ nuxt.options.alias["#nvent/server"] = resolve("./runtime/nitro/server");
1256
1403
  addImports([
1257
- { from: resolve("./runtime/app/composables/useFunctionCall"), name: "useFunctionCall" },
1258
- { from: resolve("./runtime/app/composables/useNventStream"), name: "useNventStream" }
1404
+ { from: resolve("./runtime/app/composables/useIiiStream"), name: "useIiiStream" },
1405
+ { from: resolve("./runtime/app/composables/useIii"), name: "useIii" }
1259
1406
  ]);
1407
+ addPlugin({ src: resolve("./runtime/app/plugins/iii.client"), mode: "client" });
1260
1408
  if (nuxt.options.dev) {
1409
+ if (!skipPython && opts.functions?.python?.devPath) {
1410
+ writePyrightConfig({
1411
+ rootDir: nuxt.options.rootDir,
1412
+ devPath: opts.functions.python.devPath,
1413
+ includePaths: layerInfos.map((l) => join(l.serverDir, functionsDir))
1414
+ });
1415
+ }
1261
1416
  const nventDir = join(nuxt.options.rootDir, "node_modules", ".nvent");
1262
1417
  const binDir = join(nventDir, "bin");
1263
1418
  if (managed && mode === "local") {