@happyvertical/smrt-core 0.49.8 → 0.50.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 (37) hide show
  1. package/dist/dispatch/bus.d.ts.map +1 -1
  2. package/dist/dispatch/bus.js +15 -12
  3. package/dist/dispatch/bus.js.map +1 -1
  4. package/dist/dispatch/collections/DispatchSubscriptions.d.ts +7 -2
  5. package/dist/dispatch/collections/DispatchSubscriptions.d.ts.map +1 -1
  6. package/dist/dispatch/collections/DispatchSubscriptions.js +9 -8
  7. package/dist/dispatch/collections/DispatchSubscriptions.js.map +1 -1
  8. package/dist/dispatch/collections/Dispatches.d.ts +12 -2
  9. package/dist/dispatch/collections/Dispatches.d.ts.map +1 -1
  10. package/dist/dispatch/collections/Dispatches.js +14 -8
  11. package/dist/dispatch/collections/Dispatches.js.map +1 -1
  12. package/dist/dispatch/types.d.ts +16 -1
  13. package/dist/dispatch/types.d.ts.map +1 -1
  14. package/dist/manifest/static-manifest.js +1 -1
  15. package/dist/manifest/static-manifest.js.map +1 -1
  16. package/dist/manifest/store.js +1 -1
  17. package/dist/manifest.json +1 -1
  18. package/dist/migrations/backfill-tracker.d.ts +7 -0
  19. package/dist/migrations/backfill-tracker.d.ts.map +1 -1
  20. package/dist/migrations/backfill-tracker.js +20 -1
  21. package/dist/migrations/backfill-tracker.js.map +1 -1
  22. package/dist/registry/types.d.ts +21 -1
  23. package/dist/registry/types.d.ts.map +1 -1
  24. package/dist/smrt-knowledge.json +3 -3
  25. package/dist/system/bootstrap.d.ts +23 -0
  26. package/dist/system/bootstrap.d.ts.map +1 -1
  27. package/dist/system/bootstrap.js +52 -17
  28. package/dist/system/bootstrap.js.map +1 -1
  29. package/dist/vite-plugin/index.d.ts +3 -1
  30. package/dist/vite-plugin/index.d.ts.map +1 -1
  31. package/dist/vite-plugin/index.js +9 -2
  32. package/dist/vite-plugin/index.js.map +1 -1
  33. package/dist/vite-plugin/sveltekit-generator.d.ts +26 -5
  34. package/dist/vite-plugin/sveltekit-generator.d.ts.map +1 -1
  35. package/dist/vite-plugin/sveltekit-generator.js +48 -14
  36. package/dist/vite-plugin/sveltekit-generator.js.map +1 -1
  37. package/package.json +10 -10
@@ -1,5 +1,5 @@
1
1
  import { MAX_LIST_LIMIT, buildDefaultListOrderBy } from "../query-bounds.js";
2
- import { CRUD_OPERATIONS, createManifestClassNamePredicate, declaredTypeAcceptsDate, isCrudOperation, queryStringDecoderFor, resolveApiMethodExposure, resolveCustomActionMetadata, resolveCustomActionNames, resolveDeclaredScopeMismatch, resolveEffectiveActionMetadata } from "../generators/custom-action.js";
2
+ import { CRUD_OPERATIONS, createManifestClassNamePredicate, declaredTypeAcceptsDate, isCrudOperation, isFrameworkLifecycleMethod, 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";
@@ -1501,26 +1501,51 @@ function resolveCliActionSet(objectDef) {
1501
1501
  * Inspect a manifest and return classes whose effective CLI command set —
1502
1502
  * `cli.include`/`cli.exclude` when spelled out, or (#2638) the same "every
1503
1503
  * public method minus exclude" default `cli: true`/`cli: {}` resolves to —
1504
- * references a command not exposed via the API. Classes that opt out via
1505
- * `cli: { skipApiCheck: true }` are skipped, and so is a class with
1506
- * `cli: false` (no CLI surface at all).
1504
+ * references a command not exposed via the API. `cli: { skipApiCheck: true }`
1505
+ * skips the whole class; `cli: { skipApiCheck: [...] }` (smrt#2857) skips
1506
+ * only the named commands, leaving every other command in the class's
1507
+ * effective set checked. So does a class with `cli: false` (no CLI surface
1508
+ * at all).
1507
1509
  *
1508
- * Throws nothing; returns the violation list so callers can choose to throw
1509
- * or warn.
1510
+ * A name in the array form that isn't a CRUD verb or a scanned public
1511
+ * method on the class is reported back via `invalidSkipApiCheck` on the
1512
+ * violation entry -- see `validateCliIncludeAgainstApi`, which always
1513
+ * throws for it regardless of that gate's narrower `cli.include` filter.
1514
+ *
1515
+ * Throws nothing else; returns the violation list so callers can choose to
1516
+ * throw or warn.
1510
1517
  */
1511
1518
  function findCliApiCoherenceViolations(manifest) {
1512
1519
  const violations = [];
1513
1520
  for (const [className, objectDef] of Object.entries(manifest.objects)) {
1514
1521
  const cliConfig = objectDef.decoratorConfig?.cli;
1515
1522
  if (cliConfig === false) continue;
1516
- if (typeof cliConfig === "object" && cliConfig !== null && cliConfig.skipApiCheck) continue;
1523
+ const skipApiCheck = typeof cliConfig === "object" && cliConfig !== null ? cliConfig.skipApiCheck : void 0;
1524
+ if (skipApiCheck === true) continue;
1517
1525
  const effectiveCliCommands = resolveCliActionSet(objectDef);
1518
- if (effectiveCliCommands.size === 0) continue;
1526
+ let invalidSkipApiCheck = [];
1527
+ let skipNames = /* @__PURE__ */ new Set();
1528
+ if (Array.isArray(skipApiCheck) && skipApiCheck.length > 0) {
1529
+ const knownNames = /* @__PURE__ */ new Set([...CRUD_OPERATIONS, ...Object.entries(objectDef.methods || {}).filter(([, method]) => method.isPublic).map(([name]) => name)]);
1530
+ const hasExplicitInclude = typeof cliConfig === "object" && cliConfig !== null && Array.isArray(cliConfig.include);
1531
+ const isInEffectiveSurface = (name) => effectiveCliCommands.has(name) || !hasExplicitInclude && (CRUD_OPERATIONS.includes(name) || isFrameworkLifecycleMethod(name));
1532
+ invalidSkipApiCheck = skipApiCheck.filter((name) => !knownNames.has(name) || !isInEffectiveSurface(name)).sort();
1533
+ skipNames = new Set(skipApiCheck.filter((name) => effectiveCliCommands.has(name)));
1534
+ }
1535
+ if (effectiveCliCommands.size === 0) {
1536
+ if (invalidSkipApiCheck.length > 0) violations.push({
1537
+ className,
1538
+ unreachable: [],
1539
+ invalidSkipApiCheck
1540
+ });
1541
+ continue;
1542
+ }
1519
1543
  const apiActionSet = resolveApiActionSet(objectDef, manifest);
1520
- const unreachable = [...effectiveCliCommands].filter((action) => !apiActionSet.has(action)).sort();
1521
- if (unreachable.length > 0) violations.push({
1544
+ const unreachable = [...effectiveCliCommands].filter((action) => !apiActionSet.has(action) && !skipNames.has(action)).sort();
1545
+ if (unreachable.length > 0 || invalidSkipApiCheck.length > 0) violations.push({
1522
1546
  className,
1523
- unreachable
1547
+ unreachable,
1548
+ ...invalidSkipApiCheck.length > 0 ? { invalidSkipApiCheck } : {}
1524
1549
  });
1525
1550
  }
1526
1551
  return violations;
@@ -1556,12 +1581,21 @@ function findCliApiCoherenceViolations(manifest) {
1556
1581
  * check once that pre-existing surface is triaged.
1557
1582
  */
1558
1583
  function validateCliIncludeAgainstApi(manifest) {
1559
- const violations = findCliApiCoherenceViolations(manifest).filter(({ className }) => {
1584
+ const allViolations = findCliApiCoherenceViolations(manifest);
1585
+ const invalidSkipApiCheckMessages = allViolations.flatMap(({ className, invalidSkipApiCheck }) => (invalidSkipApiCheck ?? []).map((name) => `[smrt] ${className}: cli.skipApiCheck names '${name}', which isn't a\n CRUD verb or a scanned public method on this class, or isn't part of\n its current cli.include/cli.exclude surface (a typo, or a stale\n entry left behind after the command was dropped from cli.include).\n Fix the typo, or remove '${name}' from cli.skipApiCheck -- an\n unrecognized or stale name grants no exemption at all.`));
1586
+ const violations = allViolations.filter(({ className, unreachable }) => {
1587
+ if (unreachable.length === 0) return false;
1560
1588
  const cliConfig = manifest.objects[className]?.decoratorConfig?.cli;
1561
1589
  return typeof cliConfig === "object" && cliConfig !== null && Array.isArray(cliConfig.include) && cliConfig.include.length > 0;
1562
1590
  });
1563
- if (violations.length === 0) return;
1564
- 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.`));
1591
+ if (violations.length === 0 && invalidSkipApiCheckMessages.length === 0) return;
1592
+ const messages = [...invalidSkipApiCheckMessages, ...violations.flatMap(({ className, unreachable }) => {
1593
+ const methods = manifest.objects[className]?.methods ?? {};
1594
+ const isRealCommandName = (name) => CRUD_OPERATIONS.includes(name) || methods[name]?.isPublic === true;
1595
+ return unreachable.map((action) => {
1596
+ return `[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 }\`${isRealCommandName(action) ? ` (or \`skipApiCheck: ['${action}']\` to acknowledge only this command)` : ""} on the\n @smrt() decorator.`;
1597
+ });
1598
+ })];
1565
1599
  throw new Error(messages.join("\n\n"));
1566
1600
  }
1567
1601
  /**