@happyvertical/smrt-core 0.46.0 → 0.47.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 (72) hide show
  1. package/AGENTS.md +38 -0
  2. package/dist/decorators/compatibility.d.ts +46 -0
  3. package/dist/decorators/compatibility.d.ts.map +1 -1
  4. package/dist/decorators/compatibility.js +48 -5
  5. package/dist/decorators/compatibility.js.map +1 -1
  6. package/dist/decorators/index.d.ts +119 -1
  7. package/dist/decorators/index.d.ts.map +1 -1
  8. package/dist/decorators/index.js +52 -8
  9. package/dist/decorators/index.js.map +1 -1
  10. package/dist/generators/custom-action.d.ts +379 -2
  11. package/dist/generators/custom-action.d.ts.map +1 -1
  12. package/dist/generators/custom-action.js +694 -17
  13. package/dist/generators/custom-action.js.map +1 -1
  14. package/dist/generators/index.d.ts +1 -1
  15. package/dist/generators/index.d.ts.map +1 -1
  16. package/dist/generators/index.js +2 -2
  17. package/dist/generators/preflight-route.d.ts +12 -10
  18. package/dist/generators/preflight-route.d.ts.map +1 -1
  19. package/dist/generators/preflight-route.js +46 -14
  20. package/dist/generators/preflight-route.js.map +1 -1
  21. package/dist/generators/rest.d.ts +44 -0
  22. package/dist/generators/rest.d.ts.map +1 -1
  23. package/dist/generators/rest.js +84 -9
  24. package/dist/generators/rest.js.map +1 -1
  25. package/dist/generators.js +2 -2
  26. package/dist/index.d.ts +2 -2
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +4 -4
  29. package/dist/knowledge.d.ts.map +1 -1
  30. package/dist/knowledge.js +59 -15
  31. package/dist/knowledge.js.map +1 -1
  32. package/dist/manifest/static-manifest.d.ts.map +1 -1
  33. package/dist/manifest/static-manifest.js +125 -53
  34. package/dist/manifest/static-manifest.js.map +1 -1
  35. package/dist/manifest/store.js +1 -1
  36. package/dist/manifest/store.js.map +1 -1
  37. package/dist/manifest.json +184 -53
  38. package/dist/postgres-permissions.d.ts +6 -1
  39. package/dist/postgres-permissions.d.ts.map +1 -1
  40. package/dist/postgres-permissions.js +78 -7
  41. package/dist/postgres-permissions.js.map +1 -1
  42. package/dist/registry/index.d.ts +1 -1
  43. package/dist/registry/index.d.ts.map +1 -1
  44. package/dist/registry/shared-state.d.ts +19 -0
  45. package/dist/registry/shared-state.d.ts.map +1 -1
  46. package/dist/registry/shared-state.js +16 -1
  47. package/dist/registry/shared-state.js.map +1 -1
  48. package/dist/registry.d.ts +136 -1
  49. package/dist/registry.d.ts.map +1 -1
  50. package/dist/registry.js +231 -1
  51. package/dist/registry.js.map +1 -1
  52. package/dist/scanner/manifest-generator.d.ts.map +1 -1
  53. package/dist/scanner/manifest-generator.js +18 -16
  54. package/dist/scanner/manifest-generator.js.map +1 -1
  55. package/dist/scanner/types.d.ts +59 -6
  56. package/dist/scanner/types.d.ts.map +1 -1
  57. package/dist/scanner/types.js.map +1 -1
  58. package/dist/smrt-knowledge.json +33 -32
  59. package/dist/vite-plugin/api-client-entries.d.ts.map +1 -1
  60. package/dist/vite-plugin/api-client-entries.js +10 -8
  61. package/dist/vite-plugin/api-client-entries.js.map +1 -1
  62. package/dist/vite-plugin/index.d.ts.map +1 -1
  63. package/dist/vite-plugin/index.js +1 -1
  64. package/dist/vite-plugin/index.js.map +1 -1
  65. package/dist/vite-plugin/sveltekit-generator.d.ts +28 -2
  66. package/dist/vite-plugin/sveltekit-generator.d.ts.map +1 -1
  67. package/dist/vite-plugin/sveltekit-generator.js +121 -51
  68. package/dist/vite-plugin/sveltekit-generator.js.map +1 -1
  69. package/dist/vite-plugin/web-collections.d.ts.map +1 -1
  70. package/dist/vite-plugin/web-collections.js +1 -1
  71. package/dist/vite-plugin/web-collections.js.map +1 -1
  72. package/package.json +10 -5
@@ -1,5 +1,5 @@
1
1
  import { MAX_LIST_LIMIT, buildDefaultListOrderBy } from "../query-bounds.js";
2
- import { CRUD_OPERATIONS, isCrudOperation, resolveCustomActionMetadata, resolveCustomActionNames } from "../generators/custom-action.js";
2
+ import { CRUD_OPERATIONS, createManifestClassNamePredicate, declaredTypeAcceptsDate, isCrudOperation, queryStringDecoderFor, resolveApiMethodExposure, resolveCustomActionMetadata, resolveCustomActionNames, resolveDeclaredScopeMismatch, resolveEffectiveActionMetadata } from "../generators/custom-action.js";
3
3
  import { isFrameworkBaseClass } from "../registry/framework-base-classes.js";
4
4
  import { generateConditionalGetRouteHelper } from "../generators/conditional-get.js";
5
5
  import { AUTO_GENERATED_ROUTE_HEADER } from "./route-header.js";
@@ -596,8 +596,12 @@ function extractRoutePathParamNames(pathSegments) {
596
596
  return pathSegments.filter((segment) => /^\[[^\]]+\]$/.test(segment)).map((segment) => segment.slice(1, -1)).filter(Boolean);
597
597
  }
598
598
  function resolveApiActionRouteConfig(actionName, actionDef, apiConfig, routeOptions = {}, defaultScope = actionDef.isStatic ? "collection" : "item") {
599
- const routeConfig = getApiConfigObject(apiConfig)?.routes?.[actionName];
600
- const pathSegments = normalizeCustomRoutePath(actionName, routeConfig?.path, routeOptions);
599
+ const effective = resolveEffectiveActionMetadata({
600
+ actionName,
601
+ method: actionDef,
602
+ apiConfig
603
+ });
604
+ const pathSegments = normalizeCustomRoutePath(actionName, effective.path, routeOptions);
601
605
  return {
602
606
  scope: resolveCustomActionMetadata({
603
607
  actionName,
@@ -605,7 +609,7 @@ function resolveApiActionRouteConfig(actionName, actionDef, apiConfig, routeOpti
605
609
  apiConfig,
606
610
  defaultScope
607
611
  }).scope,
608
- method: normalizeApiHttpMethod(routeConfig?.method),
612
+ method: normalizeApiHttpMethod(effective.httpMethod),
609
613
  pathSegments,
610
614
  pathParamNames: extractRoutePathParamNames(pathSegments)
611
615
  };
@@ -626,11 +630,15 @@ function hasSingleOptionsParameter(actionDef) {
626
630
  const parameters = Array.isArray(actionDef.parameters) ? actionDef.parameters : [];
627
631
  return parameters.length === 1 && parameters[0]?.name === "options";
628
632
  }
629
- function buildActionInvocationArgs(actionDef, optionsIdentifier = "options") {
633
+ function buildActionInvocationArgs(actionDef, optionsIdentifier = "options", httpMethod) {
630
634
  const parameters = Array.isArray(actionDef.parameters) ? actionDef.parameters : [];
631
635
  if (parameters.length === 0) return [];
632
636
  if (hasSingleOptionsParameter(actionDef)) return [optionsIdentifier];
633
- return parameters.map((parameter) => buildOptionsPropertyAccess(parameter.name));
637
+ return parameters.map((parameter, index) => {
638
+ const access = buildOptionsPropertyAccess(parameter.name);
639
+ const decoder = httpMethod === "GET" ? queryStringDecoderFor(parameter.type) : declaredTypeAcceptsDate(parameter.type) ? "toCustomActionDate" : void 0;
640
+ return decoder ? `${decoder}(${access}) as ActionArgs[${index}]` : access;
641
+ });
634
642
  }
635
643
  function buildObjectTypeProperty(propertyName) {
636
644
  if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(propertyName)) return propertyName;
@@ -1162,14 +1170,12 @@ async function generateRoutesForObject(projectRoot, className, objectDef, manife
1162
1170
  const itemRoute = generateItemRouteTemplate(projectRoot, className, objectDef, manifest, includedActions, options, join(routeDir, "[id]"));
1163
1171
  generatedRoutePaths.push(writeRoute(join(routeDir, "[id]"), "+server.ts", itemRoute));
1164
1172
  }
1165
- const customActions = Object.entries(objectDef.methods).filter(([name, method]) => !isCrudOperation(name) && method.isPublic && shouldIncludeInApi(name, apiConfig));
1173
+ const { exposed: customActions, rejected } = resolveApiCustomActions(objectDef, manifest, false);
1174
+ warnUnhostedActions(className, rejected);
1166
1175
  const actionSpecs = [];
1167
1176
  for (const [actionName, actionDef] of customActions) {
1168
1177
  const routeConfig = resolveApiActionRouteConfig(actionName, actionDef, apiConfig, { kebabRoutes: options.kebabRoutes });
1169
- if (routeConfig.scope === "collection" && !actionDef.isStatic) {
1170
- console.warn(`[smrt] Skipping ${className}.${actionName} - collection API routes require a static method`);
1171
- continue;
1172
- }
1178
+ warnDeclaredScopeMismatch(className, actionName, actionDef, apiConfig, routeConfig.scope);
1173
1179
  const actionBaseDir = routeConfig.scope === "collection" ? routeDir : join(routeDir, "[id]");
1174
1180
  actionSpecs.push({
1175
1181
  routeDir: join(actionBaseDir, ...routeConfig.pathSegments),
@@ -1200,15 +1206,13 @@ async function generateCollectionRoutesForObject(projectRoot, className, objectD
1200
1206
  const routeDir = join(projectRoot, options.routesDir, objectDef.collection);
1201
1207
  const lookupClassName = findItemClassRegistryKey(className, objectDef, manifest);
1202
1208
  const lookupObjectDef = findObjectDefByRegistryKey(manifest, lookupClassName);
1203
- const customActions = Object.entries(objectDef.methods).filter(([name, method]) => !isCrudOperation(name) && method.isPublic && shouldIncludeInApi(name, apiConfig));
1209
+ const { exposed: customActions, rejected } = resolveApiCustomActions(objectDef, manifest, true);
1210
+ warnUnhostedActions(className, rejected);
1204
1211
  if (customActions.length === 0) return generatedRoutePaths;
1205
1212
  const actionSpecs = [];
1206
1213
  for (const [actionName, actionDef] of customActions) {
1207
1214
  const routeConfig = resolveApiActionRouteConfig(actionName, actionDef, apiConfig, { kebabRoutes: options.kebabRoutes }, "collection");
1208
- if (routeConfig.scope !== "collection") {
1209
- console.warn(`[smrt] Skipping ${className}.${actionName} - collection class methods only support collection-scoped API routes`);
1210
- continue;
1211
- }
1215
+ warnDeclaredScopeMismatch(className, actionName, actionDef, apiConfig, routeConfig.scope);
1212
1216
  actionSpecs.push({
1213
1217
  routeDir: join(routeDir, ...routeConfig.pathSegments),
1214
1218
  spec: {
@@ -1246,49 +1250,103 @@ function resolveStandardCrudActions(apiConfig) {
1246
1250
  return crud;
1247
1251
  }
1248
1252
  /**
1249
- * Check if a custom action should be included in API.
1253
+ * Partition an object's custom (non-CRUD) methods into the ones the API
1254
+ * exposes and the ones it withholds, with a reason for each rejection.
1250
1255
  *
1251
- * Mirrors the CRUD inclusion path: intersect with `include` (when set), then
1252
- * subtract `exclude`. Both filters apply together, matching principle of least
1253
- * surprise for users supplying both lists.
1256
+ * The ONE place route generation, `resolveApiActionSet`, and the knowledge
1257
+ * artifact's API projection agree, by all three reading
1258
+ * {@link resolveApiMethodExposure}. Before #2686 each re-derived a subset and
1259
+ * could disagree — most damagingly, a gate applied only to the coherence
1260
+ * resolver would report a method unreachable while the emitter next door still
1261
+ * wrote its route file.
1254
1262
  */
1255
- function shouldIncludeInApi(actionName, apiConfig) {
1256
- if (apiConfig === false) return false;
1257
- if (apiConfig === true || apiConfig === void 0) return true;
1258
- if (typeof apiConfig === "object" && apiConfig !== null) {
1259
- const config = apiConfig;
1260
- const included = Array.isArray(config.include) ? config.include.includes(actionName) : true;
1261
- const excluded = Array.isArray(config.exclude) ? config.exclude.includes(actionName) : false;
1262
- return included && !excluded;
1263
+ function resolveApiCustomActions(objectDef, manifest, objectIsCollectionClass, wireability = {}) {
1264
+ const apiConfig = objectDef.decoratorConfig?.api;
1265
+ const isModelClassName = wireability.isModelClassName ?? createManifestClassNamePredicate(manifest);
1266
+ const exposed = [];
1267
+ const rejected = [];
1268
+ for (const [name, method] of Object.entries(objectDef.methods || {})) {
1269
+ const decision = resolveApiMethodExposure({
1270
+ actionName: name,
1271
+ method,
1272
+ apiConfig,
1273
+ isCollectionClass: objectIsCollectionClass,
1274
+ ...isModelClassName ? { isModelClassName } : {}
1275
+ });
1276
+ if (decision.exposed) {
1277
+ exposed.push([name, method]);
1278
+ continue;
1279
+ }
1280
+ if (decision.code === "crud-reserved" || decision.code === "not-public") continue;
1281
+ rejected.push([name, decision]);
1263
1282
  }
1264
- return true;
1283
+ return {
1284
+ exposed,
1285
+ rejected
1286
+ };
1287
+ }
1288
+ /**
1289
+ * Warn about the one rejection class that would be a structural failure rather
1290
+ * than a configuration choice: an action whose declared scope has no receiver
1291
+ * to bind. Both emitters carried this warning before #2686 moved the check into
1292
+ * the shared resolver, and the message text is preserved.
1293
+ *
1294
+ * It cannot currently fire — `resolveCustomActionMetadata` collapses a
1295
+ * contradicting scope back to the receiver-derived one, as it did before this
1296
+ * change; see `resolveActionReceiver` in `generators/custom-action.ts`. The
1297
+ * warning a developer actually sees for a contradicting declaration is
1298
+ * `warnDeclaredScopeMismatch` below.
1299
+ *
1300
+ * Every other rejection is silent here and reported with its reason through
1301
+ * the knowledge artifact instead, so a normal build is not buried in output
1302
+ * for the 2% of methods the heuristic legitimately withholds.
1303
+ */
1304
+ function warnUnhostedActions(className, rejected) {
1305
+ for (const [actionName, decision] of rejected) {
1306
+ if (decision.code !== "no-receiver") continue;
1307
+ console.warn(`[smrt] Skipping ${className}.${actionName} - ${decision.reason}`);
1308
+ }
1309
+ }
1310
+ /**
1311
+ * Report a declared `scope` that contradicts the method's actual receiver.
1312
+ *
1313
+ * The declaration is honored only where it AGREES; nothing in config can move
1314
+ * an instance method onto the class. Warning is the whole remedy — the route
1315
+ * is still emitted at the receiver-derived URL, so an author who expected the
1316
+ * other one learns why it is missing instead of silently getting nothing at
1317
+ * either address (#2686).
1318
+ */
1319
+ function warnDeclaredScopeMismatch(className, actionName, actionDef, apiConfig, effectiveScope) {
1320
+ const declared = resolveDeclaredScopeMismatch({
1321
+ actionName,
1322
+ method: actionDef,
1323
+ apiConfig,
1324
+ effectiveScope
1325
+ });
1326
+ if (!declared) return;
1327
+ console.warn(`[smrt] ${className}.${actionName} declares scope '${declared}' but its receiver is ${effectiveScope}-scoped, so the declared scope is ignored. A scope describes where a method already lives; it cannot move it. Make ${actionName} static to reach it on the collection, or drop the scope option.`);
1265
1328
  }
1266
1329
  /**
1267
1330
  * Compute the set of action names (standard CRUD + custom methods) that the
1268
1331
  * API exposes for a given object definition. This is the same resolution used
1269
1332
  * to drive route generation, exposed so coherence checks (e.g. CLI vs API)
1270
1333
  * can ask "what does the API expose?" without re-implementing the logic.
1334
+ *
1335
+ * Pass a class inventory: without one the wire-ability heuristic cannot tell a
1336
+ * model class from an options interface and therefore accepts both, so the
1337
+ * caller silently disagrees with the emitters on the LARGEST group of withheld
1338
+ * methods (see `WireabilityOptions.isModelClassName`). Supply either
1339
+ * `manifest`, or `wireability.isModelClassName` when the caller's inventory is
1340
+ * the live registry rather than a manifest — `@happyvertical/smrt-users`' CLI
1341
+ * resource listing is the second kind. Both stay optional only because this is
1342
+ * public API.
1271
1343
  */
1272
- function resolveApiActionSet(objectDef, manifest) {
1344
+ function resolveApiActionSet(objectDef, manifest, wireability = {}) {
1273
1345
  const apiConfig = objectDef.decoratorConfig?.api;
1274
1346
  if (apiConfig === false) return /* @__PURE__ */ new Set();
1275
1347
  const objectIsCollectionClass = manifest ? isCollectionManifestClass(manifest, objectDef) : isCollectionClass(objectDef);
1276
1348
  const actions = objectIsCollectionClass ? /* @__PURE__ */ new Set() : new Set(resolveStandardCrudActions(apiConfig));
1277
- for (const [name, method] of Object.entries(objectDef.methods || {})) {
1278
- if (isCrudOperation(name)) continue;
1279
- if (!method.isPublic) continue;
1280
- if (!shouldIncludeInApi(name, apiConfig)) continue;
1281
- const scope = resolveCustomActionMetadata({
1282
- actionName: name,
1283
- method,
1284
- apiConfig,
1285
- defaultScope: objectIsCollectionClass ? "collection" : method.isStatic ? "collection" : "item"
1286
- }).scope;
1287
- if (objectIsCollectionClass) {
1288
- if (scope !== "collection") continue;
1289
- } else if (scope === "collection" && !method.isStatic) continue;
1290
- actions.add(name);
1291
- }
1349
+ for (const [name] of resolveApiCustomActions(objectDef, manifest, objectIsCollectionClass, wireability).exposed) actions.add(name);
1292
1350
  return actions;
1293
1351
  }
1294
1352
  /**
@@ -1399,7 +1457,7 @@ function validateCliIncludeAgainstApi(manifest) {
1399
1457
  return typeof cliConfig === "object" && cliConfig !== null && Array.isArray(cliConfig.include) && cliConfig.include.length > 0;
1400
1458
  });
1401
1459
  if (violations.length === 0) return;
1402
- const messages = violations.flatMap(({ className, unreachable }) => unreachable.map((action) => `[smrt] ${className}.${action} is exposed as a CLI command but is not exposed via the api.\n Either:\n - Add '${action}' to api.include, or\n - Remove '${action}' from cli.include / add it to cli.exclude.\n The CLI invokes methods over HTTP; methods without API routes are unreachable.\n If this CLI is intentionally invoked in-process (no HTTP), set\n \`cli: { skipApiCheck: true }\` on the @smrt() decorator to acknowledge.`));
1460
+ const messages = violations.flatMap(({ className, unreachable }) => unreachable.map((action) => `[smrt] ${className}.${action} is exposed as a CLI command but is not exposed via the api.\n Either:\n - Decorate '${action}' with @method({ expose: true }) to route it, or\n - Add '${action}' to api.include, or\n - Remove '${action}' from cli.include / add it to cli.exclude.\n The CLI invokes methods over HTTP; methods without API routes are unreachable.\n A public method is routed by default only when every parameter can be\n built from JSON; @method({ expose: true }) overrides that for one method\n without widening api.include. See withheldSurfaces in the knowledge\n artifact for the reason this one was withheld (#2686).\n If this CLI is intentionally invoked in-process (no HTTP), set\n \`cli: { skipApiCheck: true }\` on the @smrt() decorator to acknowledge.`));
1403
1461
  throw new Error(messages.join("\n\n"));
1404
1462
  }
1405
1463
  /**
@@ -1687,9 +1745,21 @@ function generateActionRouteTemplate(projectRoot, routeDir, routeSpecs, objectDe
1687
1745
  const hostModelType = resolveObjectTypeReference(projectRoot, firstSpec.hostClassName, objectDef, options, routeDir);
1688
1746
  const typeImports = mergeImportStatements([lookupModelType.importStatement, hostModelType.importStatement]);
1689
1747
  if (routeSpecs.some((spec) => spec.hostType !== hostType || spec.lookupClassName !== lookupClassName || spec.routeConfig.scope !== routeConfig.scope)) throw new Error(`Cannot generate mixed custom route handlers for ${lookupClassName}. All handlers sharing a route path must target the same host type and scope.`);
1748
+ const decoderImports = /* @__PURE__ */ new Set();
1749
+ for (const spec of routeSpecs) {
1750
+ if (hasSingleOptionsParameter(spec.actionDef)) continue;
1751
+ for (const parameter of spec.actionDef.parameters ?? []) {
1752
+ const decoder = spec.routeConfig?.method === "GET" ? queryStringDecoderFor(parameter.type) : declaredTypeAcceptsDate(parameter.type) ? "toCustomActionDate" : void 0;
1753
+ if (decoder) decoderImports.add(decoder);
1754
+ }
1755
+ }
1690
1756
  const importBlock = [
1691
1757
  "import { error, json } from '@sveltejs/kit';",
1692
- "import { normalizeCustomActionFailure, normalizeTypedHttpError } from '@happyvertical/smrt-core';",
1758
+ `import { ${[
1759
+ "normalizeCustomActionFailure",
1760
+ "normalizeTypedHttpError",
1761
+ ...[...decoderImports].sort()
1762
+ ].join(", ")} } from '@happyvertical/smrt-core';`,
1693
1763
  hostType === "collection" || routeConfig.scope !== "collection" ? "import { getCollection } from '$lib/server/smrt';" : "import { ObjectRegistry } from '@happyvertical/smrt-core';",
1694
1764
  typeImports,
1695
1765
  "import type { RequestHandler } from './$types';"
@@ -1719,7 +1789,7 @@ function generateActionRouteHandler(lookupClassName, actionName, actionDef, rout
1719
1789
  const receiverExpression = actionDef.isStatic ? "CollectionClass" : "typedCollection";
1720
1790
  const staticTargetLoad = actionDef.isStatic ? ` const CollectionClass = typedCollection.constructor as typeof ${hostTypeName};\n` : "";
1721
1791
  const scopedOptions = buildScopedOptionsForTenantRead(actionDef, routeConfig, tenantScoped);
1722
- const invocationArgs = buildActionInvocationArgs(actionDef, scopedOptions.optionsIdentifier);
1792
+ const invocationArgs = buildActionInvocationArgs(actionDef, scopedOptions.optionsIdentifier, routeConfig.method);
1723
1793
  return `// Custom collection method: ${actionName}
1724
1794
  export const ${handlerName}: RequestHandler = async (${collectionHandlerArgs}) => {
1725
1795
  ${authGuardLine}
@@ -1751,7 +1821,7 @@ ${optionsLoad}${scopedOptions.source} let result: unknown;
1751
1821
  if (routeConfig.scope === "collection") {
1752
1822
  const optionsLoad = buildActionOptionsLoad(actionName, actionDef, routeConfig, hostTypeName, true);
1753
1823
  const scopedOptions = buildScopedOptionsForTenantRead(actionDef, routeConfig, tenantScoped);
1754
- const invocationArgs = buildActionInvocationArgs(actionDef, scopedOptions.optionsIdentifier);
1824
+ const invocationArgs = buildActionInvocationArgs(actionDef, scopedOptions.optionsIdentifier, routeConfig.method);
1755
1825
  return `// Custom collection action: ${actionName}
1756
1826
  export const ${handlerName}: RequestHandler = async (${collectionHandlerArgs}) => {
1757
1827
  ${authGuardLine}
@@ -1784,7 +1854,7 @@ ${optionsLoad}${scopedOptions.source} const ClassRef = registered.constructor a
1784
1854
  const item = await collection.get(
1785
1855
  readScope ? { id: params.id, ...readScope } : params.id,
1786
1856
  );` : " const item = await collection.get(params.id);";
1787
- const invocationArgs = buildActionInvocationArgs(actionDef);
1857
+ const invocationArgs = buildActionInvocationArgs(actionDef, "options", routeConfig.method);
1788
1858
  return `// Custom action: ${actionName}
1789
1859
  export const ${handlerName}: RequestHandler = async (${itemHandlerArgs}) => {
1790
1860
  ${authGuardLine}
@@ -1992,6 +2062,6 @@ function updateGeneratedRouteIgnoreBlock(gitignoreContent, generatedPaths) {
1992
2062
  return `${updatedContent}${managedBlock.join("\n")}\n`;
1993
2063
  }
1994
2064
  //#endregion
1995
- export { findCliApiCoherenceViolations, generateSvelteKitRoutes, methodNameToKebab, resolveApiActionRouteConfig, resolveApiActionSet, validateCliIncludeAgainstApi };
2065
+ export { findCliApiCoherenceViolations, generateSvelteKitRoutes, methodNameToKebab, resolveApiActionRouteConfig, resolveApiActionSet, resolveApiCustomActions, validateCliIncludeAgainstApi };
1996
2066
 
1997
2067
  //# sourceMappingURL=sveltekit-generator.js.map