@jay-framework/stack-server-runtime 0.17.4 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/index.d.ts +49 -10
  2. package/dist/index.js +181 -23
  3. package/package.json +13 -13
package/dist/index.d.ts CHANGED
@@ -129,15 +129,6 @@ interface InstanceSlowRenderResult {
129
129
  * Shared between preRenderJayHtml (pre-render path) and handleDirectRequest (direct path).
130
130
  */
131
131
  declare function slowRenderInstances(discovered: DiscoveredHeadlessInstance[], headlessInstanceComponents: HeadlessInstanceComponent[]): Promise<InstanceSlowRenderResult | undefined>;
132
- /**
133
- * Validate that forEach headless instances do not have a slow phase.
134
- *
135
- * Components with slowlyRender cannot be used inside forEach because
136
- * forEach items are only known at request time, after slow rendering completes.
137
- *
138
- * @returns Array of validation error messages (empty if all valid)
139
- */
140
- declare function validateForEachInstances(forEachInstances: ForEachHeadlessInstance[], headlessInstanceComponents: HeadlessInstanceComponent[]): string[];
141
132
 
142
133
  declare function renderFastChangingData(pageParams: object, pageProps: PageProps, carryForward: object, parts: Array<DevServerPagePart>, instancePhaseData?: InstancePhaseData, forEachInstances?: ForEachHeadlessInstance[], headlessInstanceComponents?: HeadlessInstanceComponent[], mergedSlowViewState?: object, query?: Record<string, string>, cookies?: Record<string, string>): Promise<AnyFastRenderResult>;
143
134
 
@@ -992,6 +983,11 @@ interface RouteIndexEntry {
992
983
  path: string;
993
984
  description?: string;
994
985
  }
986
+ /** CLI command entry in plugins-index.yaml (DL#142) */
987
+ interface CommandIndexEntry {
988
+ name: string;
989
+ description?: string;
990
+ }
995
991
  /** Entry for plugins-index.yaml (Design Log #85) */
996
992
  interface PluginsIndexEntry {
997
993
  name: string;
@@ -1005,6 +1001,8 @@ interface PluginsIndexEntry {
1005
1001
  contexts?: ContextIndexEntry[];
1006
1002
  /** Plugin-provided routes (DL#130) */
1007
1003
  routes?: RouteIndexEntry[];
1004
+ /** CLI commands (DL#142) */
1005
+ commands?: CommandIndexEntry[];
1008
1006
  }
1009
1007
  interface PluginsIndex {
1010
1008
  plugins: PluginsIndexEntry[];
@@ -1267,4 +1265,45 @@ declare function serializeHeadTags(tags: HeadTag[]): string;
1267
1265
  */
1268
1266
  declare function parseCookies(cookieHeader: string | null | undefined): Record<string, string>;
1269
1267
 
1270
- export { type ActionDiscoveryOptions, type ActionDiscoveryResult, type ActionErrorResponse, type ActionExecutionResult, type ActionIndexEntry, type ActionMetadata, ActionRegistry, type ActionSchema, type ContextIndexEntry, type DevServerPagePart, DevSlowlyChangingPhase, type GenerateClientScriptOptions, type HeadlessInstanceComponent, type InstancePhaseData, type InstanceSlowRenderResult, type LoadedPageParts, type MaterializeContractsOptions, type MaterializeResult, type PluginActionDiscoveryOptions, type PluginClientInitInfo, type PluginContractEntry, type PluginInitDiscoveryOptions, type PluginReferencesContext, type PluginReferencesHandler, type PluginReferencesResult, type PluginScanOptions, type PluginSetupContext, type PluginSetupHandler, type PluginSetupResult, type PluginWithInit, type PluginWithReferences, type PluginWithSetup, type PluginsIndex, type PluginsIndexEntry, type ProjectClientInitInfo, type RegisteredAction, type RegisteredActionBase, type RegisteredActionEntry, type RegisteredStreamAction, type RouteIndexEntry, type ScannedPlugin, type ScriptFragments, type ServiceIndexEntry, SlowRenderCache, type SlowRenderCacheEntry, type SlowlyChangingPhase, type ViteSSRLoader, actionRegistry, buildAutomationWrap, buildScriptFragments, clearActionRegistry, clearClientInitData, clearLifecycleCallbacks, clearServerElementCache, clearServiceRegistry, discoverAllPluginActions, discoverAndRegisterActions, discoverPluginActions, discoverPluginsWithInit, discoverPluginsWithReferences, discoverPluginsWithSetup, executeAction, executePluginReferences, executePluginServerInits, executePluginSetup, generateClientScript, generateFrozenPageHtml, generatePromiseReconstruction, generateSSRPageHtml, getActionCacheHeaders, getClientInitData, getClientInitDataForKey, getRegisteredAction, getRegisteredActionNames, getService, getServiceRegistry, hasAction, hasService, invalidateServerElementCache, listContracts, loadActionMetadata, loadPageParts, materializeContracts, mergeHeadTags, onInit, onShutdown, parseActionMetadata, parseCookies, preparePluginClientInits, registerAction, registerService, renderFastChangingData, resolveActionMetadataPath, resolveServices, resolveViewStatePromises, runInitCallbacks, runLoadParams, runShutdownCallbacks, scanPlugins, serializeHeadTags, setClientInitData, slowRenderInstances, sortPluginsByDependencies, tagIdentityKey, validateForEachInstances };
1268
+ /**
1269
+ * Plugin CLI Commands (Design Log #142)
1270
+ *
1271
+ * Plugins expose CLI commands via `commands` in plugin.yaml.
1272
+ * Each command has a `makeCliCommand` handler and an optional `.jay-command` metadata file.
1273
+ * Run via `jay-stack run <plugin>/<command>`.
1274
+ */
1275
+
1276
+ interface CommandMetadata {
1277
+ name: string;
1278
+ description?: string;
1279
+ inputSchema?: Record<string, string>;
1280
+ }
1281
+ interface DiscoveredCommand {
1282
+ pluginName: string;
1283
+ pluginPath: string;
1284
+ packageName: string;
1285
+ isLocal: boolean;
1286
+ commandName: string;
1287
+ handlerExport: string;
1288
+ pluginModule?: string;
1289
+ metadata?: CommandMetadata;
1290
+ metadataPath?: string;
1291
+ }
1292
+ interface CommandFlag {
1293
+ flag: string;
1294
+ description: string;
1295
+ required: boolean;
1296
+ type: 'string' | 'boolean' | 'number';
1297
+ }
1298
+ declare function discoverPluginCommands(options: {
1299
+ projectRoot: string;
1300
+ verbose?: boolean;
1301
+ pluginFilter?: string;
1302
+ }): Promise<DiscoveredCommand[]>;
1303
+ declare function commandSchemaToFlags(inputSchema: Record<string, string>): CommandFlag[];
1304
+ declare function parseInputFromFlags(rawOptions: Record<string, any>, schema: Record<string, string>): Record<string, any>;
1305
+ declare function executePluginCommand(command: DiscoveredCommand, input: Record<string, any>, viteServer?: ViteSSRLoader): Promise<{
1306
+ success: boolean;
1307
+ }>;
1308
+
1309
+ export { type ActionDiscoveryOptions, type ActionDiscoveryResult, type ActionErrorResponse, type ActionExecutionResult, type ActionIndexEntry, type ActionMetadata, ActionRegistry, type ActionSchema, type CommandFlag, type CommandIndexEntry, type CommandMetadata, type ContextIndexEntry, type DevServerPagePart, DevSlowlyChangingPhase, type DiscoveredCommand, type GenerateClientScriptOptions, type HeadlessInstanceComponent, type InstancePhaseData, type InstanceSlowRenderResult, type LoadedPageParts, type MaterializeContractsOptions, type MaterializeResult, type PluginActionDiscoveryOptions, type PluginClientInitInfo, type PluginContractEntry, type PluginInitDiscoveryOptions, type PluginReferencesContext, type PluginReferencesHandler, type PluginReferencesResult, type PluginScanOptions, type PluginSetupContext, type PluginSetupHandler, type PluginSetupResult, type PluginWithInit, type PluginWithReferences, type PluginWithSetup, type PluginsIndex, type PluginsIndexEntry, type ProjectClientInitInfo, type RegisteredAction, type RegisteredActionBase, type RegisteredActionEntry, type RegisteredStreamAction, type RouteIndexEntry, type ScannedPlugin, type ScriptFragments, type ServiceIndexEntry, SlowRenderCache, type SlowRenderCacheEntry, type SlowlyChangingPhase, type ViteSSRLoader, actionRegistry, buildAutomationWrap, buildScriptFragments, clearActionRegistry, clearClientInitData, clearLifecycleCallbacks, clearServerElementCache, clearServiceRegistry, commandSchemaToFlags, discoverAllPluginActions, discoverAndRegisterActions, discoverPluginActions, discoverPluginCommands, discoverPluginsWithInit, discoverPluginsWithReferences, discoverPluginsWithSetup, executeAction, executePluginCommand, executePluginReferences, executePluginServerInits, executePluginSetup, generateClientScript, generateFrozenPageHtml, generatePromiseReconstruction, generateSSRPageHtml, getActionCacheHeaders, getClientInitData, getClientInitDataForKey, getRegisteredAction, getRegisteredActionNames, getService, getServiceRegistry, hasAction, hasService, invalidateServerElementCache, listContracts, loadActionMetadata, loadPageParts, materializeContracts, mergeHeadTags, onInit, onShutdown, parseActionMetadata, parseCookies, parseInputFromFlags, preparePluginClientInits, registerAction, registerService, renderFastChangingData, resolveActionMetadataPath, resolveServices, resolveViewStatePromises, runInitCallbacks, runLoadParams, runShutdownCallbacks, scanPlugins, serializeHeadTags, setClientInitData, slowRenderInstances, sortPluginsByDependencies, tagIdentityKey };
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@ var __publicField = (obj, key, value) => {
4
4
  __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
5
5
  return value;
6
6
  };
7
- import { phaseOutput, isJayAction, isJayStreamAction } from "@jay-framework/fullstack-component";
7
+ import { phaseOutput, isJayAction, isJayStreamAction, isJayCliCommand } from "@jay-framework/fullstack-component";
8
8
  import "prettier";
9
9
  import "js-beautify";
10
10
  import fs$1 from "fs";
@@ -1293,22 +1293,6 @@ async function slowRenderInstances(discovered, headlessInstanceComponents) {
1293
1293
  instancePhaseData: { discovered: discoveredForFast, carryForwards, slowViewStates }
1294
1294
  };
1295
1295
  }
1296
- function validateForEachInstances(forEachInstances, headlessInstanceComponents) {
1297
- const componentByContractName = /* @__PURE__ */ new Map();
1298
- for (const comp of headlessInstanceComponents) {
1299
- componentByContractName.set(comp.contractName, comp);
1300
- }
1301
- const validations = [];
1302
- for (const instance of forEachInstances) {
1303
- const comp = componentByContractName.get(instance.contractName);
1304
- if (comp?.compDefinition.slowlyRender) {
1305
- validations.push(
1306
- `<jay:${instance.contractName}> inside forEach has a slow rendering phase. Headless components with slow phases cannot be used inside forEach because forEach items are only known at request time, after slow rendering completes. Use slowForEach instead, or remove the slow phase from the component.`
1307
- );
1308
- }
1309
- }
1310
- return validations;
1311
- }
1312
1296
  class ActionRegistry {
1313
1297
  constructor() {
1314
1298
  __publicField(this, "actions", /* @__PURE__ */ new Map());
@@ -1325,7 +1309,9 @@ class ActionRegistry {
1325
1309
  cacheOptions: action.cacheOptions,
1326
1310
  services: action.services,
1327
1311
  handler: action.handler,
1328
- ...action.acceptsFiles && { acceptsFiles: true }
1312
+ ...action.acceptsFiles && {
1313
+ acceptsFiles: true
1314
+ }
1329
1315
  };
1330
1316
  this.actions.set(action.actionName, entry);
1331
1317
  }
@@ -1483,7 +1469,9 @@ class ActionRegistry {
1483
1469
  isStreaming: true,
1484
1470
  services: action.services,
1485
1471
  handler: action.handler,
1486
- ...action.acceptsFiles && { acceptsFiles: true }
1472
+ ...action.acceptsFiles && {
1473
+ acceptsFiles: true
1474
+ }
1487
1475
  };
1488
1476
  this.actions.set(action.actionName, entry);
1489
1477
  }
@@ -2583,6 +2571,24 @@ async function materializeContracts(options, services = /* @__PURE__ */ new Map(
2583
2571
  ...r.description && { description: r.description }
2584
2572
  }));
2585
2573
  }
2574
+ if (manifest.commands?.length) {
2575
+ entry.commands = manifest.commands.map((c) => {
2576
+ let description;
2577
+ if (c.command) {
2578
+ try {
2579
+ const cmdPath = path.resolve(plugin.pluginPath, c.command);
2580
+ const cmdContent = fs.readFileSync(cmdPath, "utf-8");
2581
+ const parsed = YAML.parse(cmdContent);
2582
+ description = parsed?.description;
2583
+ } catch {
2584
+ }
2585
+ }
2586
+ return {
2587
+ name: c.name,
2588
+ ...description && { description }
2589
+ };
2590
+ });
2591
+ }
2586
2592
  pluginsIndexMap.set(plugin.name, entry);
2587
2593
  }
2588
2594
  if (!dynamicOnly && manifest.contracts) {
@@ -2718,7 +2724,8 @@ async function materializeContracts(options, services = /* @__PURE__ */ new Map(
2718
2724
  ...data.actions && data.actions.length > 0 && { actions: data.actions },
2719
2725
  ...data.services?.length && { services: data.services },
2720
2726
  ...data.contexts?.length && { contexts: data.contexts },
2721
- ...data.routes?.length && { routes: data.routes }
2727
+ ...data.routes?.length && { routes: data.routes },
2728
+ ...data.commands?.length && { commands: data.commands }
2722
2729
  }))
2723
2730
  };
2724
2731
  fs.mkdirSync(outputDir, { recursive: true });
@@ -2781,6 +2788,24 @@ async function listContracts(options) {
2781
2788
  ...r.description && { description: r.description }
2782
2789
  }));
2783
2790
  }
2791
+ if (manifest.commands?.length) {
2792
+ entry.commands = manifest.commands.map((c) => {
2793
+ let description;
2794
+ if (c.command) {
2795
+ try {
2796
+ const cmdPath = path.resolve(plugin.pluginPath, c.command);
2797
+ const cmdContent = fs.readFileSync(cmdPath, "utf-8");
2798
+ const parsed = YAML.parse(cmdContent);
2799
+ description = parsed?.description;
2800
+ } catch {
2801
+ }
2802
+ }
2803
+ return {
2804
+ name: c.name,
2805
+ ...description && { description }
2806
+ };
2807
+ });
2808
+ }
2784
2809
  pluginsMap.set(plugin.name, entry);
2785
2810
  }
2786
2811
  if (!dynamicOnly && manifest.contracts) {
@@ -2828,7 +2853,8 @@ async function listContracts(options) {
2828
2853
  contracts: data.contracts,
2829
2854
  ...data.services?.length && { services: data.services },
2830
2855
  ...data.contexts?.length && { contexts: data.contexts },
2831
- ...data.routes?.length && { routes: data.routes }
2856
+ ...data.routes?.length && { routes: data.routes },
2857
+ ...data.commands?.length && { commands: data.commands }
2832
2858
  }))
2833
2859
  };
2834
2860
  }
@@ -2970,6 +2996,135 @@ function parseCookies(cookieHeader) {
2970
2996
  }
2971
2997
  return cookies;
2972
2998
  }
2999
+ async function discoverPluginCommands(options) {
3000
+ const { projectRoot, verbose, pluginFilter } = options;
3001
+ const allPlugins = await scanPlugins({
3002
+ projectRoot,
3003
+ verbose,
3004
+ discoverTransitive: true
3005
+ });
3006
+ const commands = [];
3007
+ for (const [packageName, plugin] of allPlugins) {
3008
+ if (!plugin.manifest.commands || plugin.manifest.commands.length === 0)
3009
+ continue;
3010
+ if (pluginFilter && plugin.name !== pluginFilter && packageName !== pluginFilter) {
3011
+ continue;
3012
+ }
3013
+ for (const cmd of plugin.manifest.commands) {
3014
+ let metadata;
3015
+ let metadataPath;
3016
+ if (cmd.command) {
3017
+ metadataPath = path.resolve(plugin.pluginPath, cmd.command);
3018
+ metadata = loadCommandMetadata(metadataPath);
3019
+ }
3020
+ commands.push({
3021
+ pluginName: plugin.name,
3022
+ pluginPath: plugin.pluginPath,
3023
+ packageName: plugin.packageName,
3024
+ isLocal: plugin.isLocal,
3025
+ commandName: cmd.name,
3026
+ handlerExport: cmd.name,
3027
+ pluginModule: plugin.manifest.module,
3028
+ metadata,
3029
+ metadataPath
3030
+ });
3031
+ if (verbose) {
3032
+ getLogger().info(`[Commands] Found ${plugin.name}/${cmd.name}`);
3033
+ }
3034
+ }
3035
+ }
3036
+ return commands;
3037
+ }
3038
+ function loadCommandMetadata(filePath) {
3039
+ try {
3040
+ const content = fs.readFileSync(filePath, "utf-8");
3041
+ return YAML.parse(content);
3042
+ } catch {
3043
+ return void 0;
3044
+ }
3045
+ }
3046
+ function commandSchemaToFlags(inputSchema) {
3047
+ const flags = [];
3048
+ for (const [field, type] of Object.entries(inputSchema)) {
3049
+ const isOptional = field.endsWith("?");
3050
+ const cleanName = isOptional ? field.slice(0, -1) : field;
3051
+ const kebabName = camelToKebab(cleanName);
3052
+ const cleanType = type.toLowerCase().trim();
3053
+ const isBoolean = cleanType === "boolean";
3054
+ flags.push({
3055
+ flag: isBoolean ? `--${kebabName}` : `--${kebabName} <value>`,
3056
+ description: "",
3057
+ required: !isOptional,
3058
+ type: cleanType === "number" ? "number" : isBoolean ? "boolean" : "string"
3059
+ });
3060
+ }
3061
+ return flags;
3062
+ }
3063
+ function camelToKebab(str) {
3064
+ return str.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
3065
+ }
3066
+ function parseInputFromFlags(rawOptions, schema) {
3067
+ const input = {};
3068
+ for (const [field, type] of Object.entries(schema)) {
3069
+ const isOptional = field.endsWith("?");
3070
+ const cleanName = isOptional ? field.slice(0, -1) : field;
3071
+ const kebabName = camelToKebab(cleanName);
3072
+ const value = rawOptions[kebabName];
3073
+ if (value === void 0) {
3074
+ if (!isOptional) {
3075
+ throw new Error(`Missing required flag: --${kebabName}`);
3076
+ }
3077
+ continue;
3078
+ }
3079
+ const cleanType = type.toLowerCase().trim();
3080
+ if (cleanType === "number") {
3081
+ const parsed = Number(value);
3082
+ if (isNaN(parsed))
3083
+ throw new Error(`Flag --${kebabName} must be a number`);
3084
+ input[cleanName] = parsed;
3085
+ } else if (cleanType === "boolean") {
3086
+ input[cleanName] = value === true || value === "true";
3087
+ } else {
3088
+ input[cleanName] = String(value);
3089
+ }
3090
+ }
3091
+ return input;
3092
+ }
3093
+ async function executePluginCommand(command, input, viteServer) {
3094
+ const cliCommand = await loadCommandHandler(command, viteServer);
3095
+ const services = resolveServices(cliCommand.services);
3096
+ return cliCommand.handler(input, ...services);
3097
+ }
3098
+ async function loadCommandHandler(command, viteServer) {
3099
+ let module;
3100
+ if (command.isLocal) {
3101
+ const moduleFile = command.pluginModule || "index";
3102
+ const modulePath = path.resolve(command.pluginPath, moduleFile);
3103
+ if (viteServer) {
3104
+ module = await viteServer.ssrLoadModule(modulePath);
3105
+ } else {
3106
+ module = await import(modulePath);
3107
+ }
3108
+ } else {
3109
+ if (viteServer) {
3110
+ module = await viteServer.ssrLoadModule(command.packageName);
3111
+ } else {
3112
+ module = await import(command.packageName);
3113
+ }
3114
+ }
3115
+ for (const [, exported] of Object.entries(module)) {
3116
+ if (isJayCliCommand(exported) && exported.commandName === command.commandName) {
3117
+ return exported;
3118
+ }
3119
+ }
3120
+ const byName = module[command.handlerExport];
3121
+ if (byName && isJayCliCommand(byName)) {
3122
+ return byName;
3123
+ }
3124
+ throw new Error(
3125
+ `CLI command "${command.commandName}" not found as export in "${command.isLocal ? command.pluginPath : command.packageName}". Available exports: ${Object.keys(module).join(", ")}`
3126
+ );
3127
+ }
2973
3128
  export {
2974
3129
  ActionRegistry,
2975
3130
  DevSlowlyChangingPhase,
@@ -2982,13 +3137,16 @@ export {
2982
3137
  clearLifecycleCallbacks,
2983
3138
  clearServerElementCache,
2984
3139
  clearServiceRegistry,
3140
+ commandSchemaToFlags,
2985
3141
  discoverAllPluginActions,
2986
3142
  discoverAndRegisterActions,
2987
3143
  discoverPluginActions,
3144
+ discoverPluginCommands,
2988
3145
  discoverPluginsWithInit,
2989
3146
  discoverPluginsWithReferences,
2990
3147
  discoverPluginsWithSetup,
2991
3148
  executeAction,
3149
+ executePluginCommand,
2992
3150
  executePluginReferences,
2993
3151
  executePluginServerInits,
2994
3152
  executePluginSetup,
@@ -3015,6 +3173,7 @@ export {
3015
3173
  onShutdown,
3016
3174
  parseActionMetadata,
3017
3175
  parseCookies,
3176
+ parseInputFromFlags,
3018
3177
  preparePluginClientInits,
3019
3178
  registerAction,
3020
3179
  registerService,
@@ -3030,6 +3189,5 @@ export {
3030
3189
  setClientInitData,
3031
3190
  slowRenderInstances,
3032
3191
  sortPluginsByDependencies,
3033
- tagIdentityKey,
3034
- validateForEachInstances
3192
+ tagIdentityKey
3035
3193
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jay-framework/stack-server-runtime",
3
- "version": "0.17.4",
3
+ "version": "0.18.0",
4
4
  "license": "Apache-2.0",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.mts",
@@ -26,21 +26,21 @@
26
26
  "test:watch": "vitest"
27
27
  },
28
28
  "dependencies": {
29
- "@jay-framework/compiler-jay-html": "^0.17.4",
30
- "@jay-framework/compiler-shared": "^0.17.4",
31
- "@jay-framework/component": "^0.17.4",
32
- "@jay-framework/fullstack-component": "^0.17.4",
33
- "@jay-framework/logger": "^0.17.4",
34
- "@jay-framework/runtime": "^0.17.4",
35
- "@jay-framework/ssr-runtime": "^0.17.4",
36
- "@jay-framework/stack-route-scanner": "^0.17.4",
37
- "@jay-framework/view-state-merge": "^0.17.4",
29
+ "@jay-framework/compiler-jay-html": "^0.18.0",
30
+ "@jay-framework/compiler-shared": "^0.18.0",
31
+ "@jay-framework/component": "^0.18.0",
32
+ "@jay-framework/fullstack-component": "^0.18.0",
33
+ "@jay-framework/logger": "^0.18.0",
34
+ "@jay-framework/runtime": "^0.18.0",
35
+ "@jay-framework/ssr-runtime": "^0.18.0",
36
+ "@jay-framework/stack-route-scanner": "^0.18.0",
37
+ "@jay-framework/view-state-merge": "^0.18.0",
38
38
  "yaml": "^2.3.4"
39
39
  },
40
40
  "devDependencies": {
41
- "@jay-framework/dev-environment": "^0.17.4",
42
- "@jay-framework/jay-cli": "^0.17.4",
43
- "@jay-framework/stack-client-runtime": "^0.17.4",
41
+ "@jay-framework/dev-environment": "^0.18.0",
42
+ "@jay-framework/jay-cli": "^0.18.0",
43
+ "@jay-framework/stack-client-runtime": "^0.18.0",
44
44
  "@types/express": "^5.0.2",
45
45
  "@types/node": "^22.15.21",
46
46
  "nodemon": "^3.0.3",