@happyvertical/smrt-core 0.44.1 → 0.45.1
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.
- package/agents/change-feed.md +3 -2
- package/dist/browser.js +2 -1
- package/dist/change-feed.d.ts +60 -1
- package/dist/change-feed.d.ts.map +1 -1
- package/dist/change-feed.js +464 -27
- package/dist/change-feed.js.map +1 -1
- package/dist/change-signals.d.ts.map +1 -1
- package/dist/change-signals.js +13 -11
- package/dist/change-signals.js.map +1 -1
- package/dist/generators/cli.d.ts.map +1 -1
- package/dist/generators/cli.js +10 -18
- package/dist/generators/cli.js.map +1 -1
- package/dist/generators/custom-action.d.ts +216 -0
- package/dist/generators/custom-action.d.ts.map +1 -1
- package/dist/generators/custom-action.js +256 -1
- package/dist/generators/custom-action.js.map +1 -1
- package/dist/generators/index.d.ts +1 -1
- package/dist/generators/index.d.ts.map +1 -1
- package/dist/generators/index.js +2 -2
- package/dist/generators/mcp.d.ts.map +1 -1
- package/dist/generators/mcp.js +14 -39
- package/dist/generators/mcp.js.map +1 -1
- package/dist/generators/rest.d.ts.map +1 -1
- package/dist/generators/rest.js +2 -0
- package/dist/generators/rest.js.map +1 -1
- package/dist/generators/tool-schema.d.ts.map +1 -1
- package/dist/generators/tool-schema.js +2 -8
- package/dist/generators/tool-schema.js.map +1 -1
- package/dist/generators.js +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -3
- package/dist/knowledge.d.ts.map +1 -1
- package/dist/knowledge.js +283 -35
- package/dist/knowledge.js.map +1 -1
- package/dist/manifest/static-manifest.js +1 -1
- package/dist/manifest/static-manifest.js.map +1 -1
- package/dist/manifest/store.js +1 -1
- package/dist/manifest.json +1 -1
- package/dist/registry/framework-base-classes.d.ts +10 -0
- package/dist/registry/framework-base-classes.d.ts.map +1 -0
- package/dist/registry/framework-base-classes.js +92 -0
- package/dist/registry/framework-base-classes.js.map +1 -0
- package/dist/registry/schema-builder.d.ts.map +1 -1
- package/dist/registry/schema-builder.js +2 -0
- package/dist/registry/schema-builder.js.map +1 -1
- package/dist/registry/types.d.ts +27 -6
- package/dist/registry/types.d.ts.map +1 -1
- package/dist/registry.d.ts +1 -0
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +2 -1
- package/dist/registry.js.map +1 -1
- package/dist/scanner/manifest-generator.d.ts +46 -16
- package/dist/scanner/manifest-generator.d.ts.map +1 -1
- package/dist/scanner/manifest-generator.js +198 -45
- package/dist/scanner/manifest-generator.js.map +1 -1
- package/dist/smrt-knowledge.json +5 -5
- package/dist/system/bootstrap.d.ts.map +1 -1
- package/dist/system/bootstrap.js +2 -1
- package/dist/system/bootstrap.js.map +1 -1
- package/dist/system/schema.d.ts +75 -2
- package/dist/system/schema.d.ts.map +1 -1
- package/dist/system/schema.js +326 -4
- package/dist/system/schema.js.map +1 -1
- package/dist/vite-plugin/index.d.ts +10 -0
- package/dist/vite-plugin/index.d.ts.map +1 -1
- package/dist/vite-plugin/index.js +52 -23
- package/dist/vite-plugin/index.js.map +1 -1
- package/dist/vite-plugin/resources-route.d.ts +18 -0
- package/dist/vite-plugin/resources-route.d.ts.map +1 -0
- package/dist/vite-plugin/resources-route.js +154 -0
- package/dist/vite-plugin/resources-route.js.map +1 -0
- package/dist/vite-plugin/sveltekit-generator.d.ts +43 -7
- package/dist/vite-plugin/sveltekit-generator.d.ts.map +1 -1
- package/dist/vite-plugin/sveltekit-generator.js +90 -17
- package/dist/vite-plugin/sveltekit-generator.js.map +1 -1
- package/dist/vite-plugin/sync-apply-route.d.ts.map +1 -1
- package/dist/vite-plugin/sync-apply-route.js +2 -0
- package/dist/vite-plugin/sync-apply-route.js.map +1 -1
- package/dist/vite-plugin/web-collections.d.ts.map +1 -1
- package/dist/vite-plugin/web-collections.js +28 -5
- package/dist/vite-plugin/web-collections.js.map +1 -1
- package/package.json +4 -4
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { AUTO_GENERATED_ROUTE_HEADER } from "./route-header.js";
|
|
2
|
+
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { createRequire } from "node:module";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { pathToFileURL } from "node:url";
|
|
6
|
+
//#region src/vite-plugin/resources-route.ts
|
|
7
|
+
/**
|
|
8
|
+
* SvelteKit `_resources` route generation — CLI discovery endpoint (#2663;
|
|
9
|
+
* parent epic #2655).
|
|
10
|
+
*
|
|
11
|
+
* Emits `{routesDir}/_resources/+server.ts`: `GET /api/_resources`, the
|
|
12
|
+
* endpoint the distributable CLI (`@happyvertical/smrt-app-cli`) reads to
|
|
13
|
+
* learn which resources and commands it can invoke. Every other generated
|
|
14
|
+
* route lives in `sveltekit-generator.ts` or a sibling emitter; this one is
|
|
15
|
+
* kept in its own module for the same reason `changes-route.ts` and
|
|
16
|
+
* `events-route.ts` are — a one-line registration hook in the shared
|
|
17
|
+
* generator file.
|
|
18
|
+
*
|
|
19
|
+
* ## The dependency constraint
|
|
20
|
+
*
|
|
21
|
+
* The handler this route delegates to, `createResourceListHandler`, lives in
|
|
22
|
+
* `@happyvertical/smrt-users` — and `smrt-users` depends on `smrt-core`, not
|
|
23
|
+
* the reverse, so core cannot import it directly. That is not a problem for
|
|
24
|
+
* *emitting* the import: the generated file is written into the consumer's
|
|
25
|
+
* own `src/routes/`, where `@happyvertical/smrt-users` resolves through the
|
|
26
|
+
* consumer's own dependency graph, exactly like the `@happyvertical/smrt-tenancy`
|
|
27
|
+
* import `sveltekit-generator.ts`'s `generateTenantContextHelper` already
|
|
28
|
+
* emits into tenant-scoped routes (see the comment above that function). The
|
|
29
|
+
* only question is *when* it is safe to emit — a consumer that never took a
|
|
30
|
+
* dependency on `smrt-users` must never receive a route whose import cannot
|
|
31
|
+
* resolve, or a working build turns red.
|
|
32
|
+
*
|
|
33
|
+
* ## Detection mechanism
|
|
34
|
+
*
|
|
35
|
+
* Unlike the tenancy guard — which reads a semantic signal already present in
|
|
36
|
+
* the manifest (whether any object is `@TenantScoped`, which itself implies
|
|
37
|
+
* the app depends on `smrt-tenancy`) — nothing in the manifest indicates
|
|
38
|
+
* whether a consumer has taken on `smrt-users`. Objects don't declare an
|
|
39
|
+
* auth dependency; the app does, at the package level.
|
|
40
|
+
*
|
|
41
|
+
* So this route uses a **resolvable-dependency check**: does the exact
|
|
42
|
+
* subpath the generated file imports — `@happyvertical/smrt-users/sveltekit`
|
|
43
|
+
* — resolve from the consumer's `projectRoot`, via a `createRequire` rooted
|
|
44
|
+
* at the consumer's own `package.json`? That is stronger than reading
|
|
45
|
+
* `package.json`'s `dependencies` block (which can list a package that was
|
|
46
|
+
* never installed) and, unlike a single hardcoded
|
|
47
|
+
* `node_modules/@happyvertical/smrt-users/package.json` path, it uses
|
|
48
|
+
* Node's real module resolution — so it also finds the package when a
|
|
49
|
+
* workspace hoists shared dependencies to an ancestor `node_modules` (npm/
|
|
50
|
+
* yarn workspaces, or pnpm with `node-linker=hoisted`) rather than
|
|
51
|
+
* installing them directly under the consumer's own `projectRoot`.
|
|
52
|
+
* Resolving the `/sveltekit` subpath specifically, rather than the
|
|
53
|
+
* package's bare `package.json`, matters too: `smrt-users`'s `exports` map
|
|
54
|
+
* does not list `./package.json`, so resolving that path would throw
|
|
55
|
+
* `ERR_PACKAGE_PATH_NOT_EXPORTED` even when the package is genuinely
|
|
56
|
+
* installed — resolving the subpath the route actually imports sidesteps
|
|
57
|
+
* that and checks precisely the thing that must succeed at build time.
|
|
58
|
+
*
|
|
59
|
+
* An explicit `sveltekit: { resourcesRoute: { enabled: false } }` escape
|
|
60
|
+
* hatch is also available, matching `changesRoute`/`eventsRoute`, for a
|
|
61
|
+
* consumer that has `smrt-users` installed but wants to keep hand-writing
|
|
62
|
+
* (or omit) `_resources`.
|
|
63
|
+
*
|
|
64
|
+
* ## Preserving a hand-written route
|
|
65
|
+
*
|
|
66
|
+
* Every consumer today hand-writes this route (the reference implementation
|
|
67
|
+
* in the issue). `clearGeneratedRouteFiles` (in `sveltekit-generator.ts`)
|
|
68
|
+
* already only deletes `+server.ts` files that start with
|
|
69
|
+
* {@link AUTO_GENERATED_ROUTE_HEADER}, so a hand-written `_resources` route —
|
|
70
|
+
* which never carries that header — survives the pre-generation sweep
|
|
71
|
+
* untouched. This emitter additionally checks `existsSync` on the target
|
|
72
|
+
* path before writing: by the time it runs, anything still there is
|
|
73
|
+
* necessarily hand-written (a generator-owned copy would already have been
|
|
74
|
+
* swept), so its mere presence is sufficient to skip generation and leave it
|
|
75
|
+
* alone.
|
|
76
|
+
*
|
|
77
|
+
* Once a consumer's `_resources` route IS generator-owned (no hand-written
|
|
78
|
+
* file present, `smrt-users` resolvable), its path is generator-owned for
|
|
79
|
+
* `.gitignore` purposes too — it is included in `generatedRoutePaths` like
|
|
80
|
+
* any other emitted route, so `updateGitignore` puts it in the managed
|
|
81
|
+
* block. A project that migrated from a hand-written route to a fully
|
|
82
|
+
* generated one may still carry a stale `# Application-owned rule` comment
|
|
83
|
+
* above the same literal path from before the migration; `updateGitignore`
|
|
84
|
+
* only ever touches its own managed block, so that stale comment is left as
|
|
85
|
+
* project history for the consumer to clean up — the same way any other
|
|
86
|
+
* manual `.gitignore` edit outside the managed block is preserved.
|
|
87
|
+
*/
|
|
88
|
+
/**
|
|
89
|
+
* Whether `@happyvertical/smrt-users/sveltekit` — the exact subpath the
|
|
90
|
+
* generated route imports — is resolvable from the consumer at
|
|
91
|
+
* `projectRoot`. See the module doc for why this check (rather than reading
|
|
92
|
+
* `package.json`'s declared `dependencies`, or checking one hardcoded
|
|
93
|
+
* physical path) is the chosen signal.
|
|
94
|
+
*/
|
|
95
|
+
function consumerHasSmrtUsers(projectRoot) {
|
|
96
|
+
try {
|
|
97
|
+
createRequire(pathToFileURL(join(projectRoot, "package.json")).href).resolve("@happyvertical/smrt-users/sveltekit");
|
|
98
|
+
return true;
|
|
99
|
+
} catch {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Generate the `_resources/+server.ts` route. Returns true when a route was
|
|
105
|
+
* written. Disabled with `sveltekit: { resourcesRoute: { enabled: false } }`;
|
|
106
|
+
* skipped (with a log line) when `@happyvertical/smrt-users` is not
|
|
107
|
+
* resolvable from the consumer, or when a hand-written route already
|
|
108
|
+
* occupies the path.
|
|
109
|
+
*/
|
|
110
|
+
function generateResourcesRoute(projectRoot, options) {
|
|
111
|
+
if (options.resourcesRoute?.enabled === false) return false;
|
|
112
|
+
if (!consumerHasSmrtUsers(projectRoot)) {
|
|
113
|
+
console.log("[smrt] Skipping _resources route - @happyvertical/smrt-users is not resolvable; createResourceListHandler would not resolve in the consumer build");
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
const routeDir = join(projectRoot, options.routesDir, "_resources");
|
|
117
|
+
const filePath = join(routeDir, "+server.ts");
|
|
118
|
+
const existingServerFile = ["+server.ts", "+server.js"].find((name) => existsSync(join(routeDir, name)));
|
|
119
|
+
if (existingServerFile) {
|
|
120
|
+
console.log(`[smrt] Preserving hand-written _resources route (${existingServerFile}, not generator-owned)`);
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
if (!existsSync(routeDir)) mkdirSync(routeDir, { recursive: true });
|
|
124
|
+
writeFileSync(filePath, generateResourcesRouteTemplate(options), "utf-8");
|
|
125
|
+
console.log(`[smrt] Generated: ${filePath}`);
|
|
126
|
+
return true;
|
|
127
|
+
}
|
|
128
|
+
function generateResourcesRouteTemplate(options) {
|
|
129
|
+
return `${AUTO_GENERATED_ROUTE_HEADER}
|
|
130
|
+
// DO NOT EDIT - changes will be overwritten
|
|
131
|
+
//
|
|
132
|
+
// GET /api/_resources — CLI discovery endpoint (#2663). Returns the auth-aware
|
|
133
|
+
// list of resources and commands the distributable CLI
|
|
134
|
+
// (@happyvertical/smrt-app-cli) can invoke, derived from ObjectRegistry by
|
|
135
|
+
// createResourceListHandler. Subsumes the route every consumer previously
|
|
136
|
+
// hand-wrote (see the issue for the reference implementation this replaces).
|
|
137
|
+
|
|
138
|
+
import { createResourceListHandler } from '@happyvertical/smrt-users/sveltekit';
|
|
139
|
+
// Side effect: registers @smrt() classes in ObjectRegistry before the
|
|
140
|
+
// handler walks it (mirrors the generated CRUD/changes/events routes'
|
|
141
|
+
// $lib/server/smrt import).
|
|
142
|
+
import '$lib/server/smrt';
|
|
143
|
+
|
|
144
|
+
export const GET = createResourceListHandler({
|
|
145
|
+
ensureRegistry: async () => {
|
|
146
|
+
await import('$lib/server/smrt');
|
|
147
|
+
},${options.kebabRoutes ? "\n kebabRoutes: true," : ""}
|
|
148
|
+
});
|
|
149
|
+
`;
|
|
150
|
+
}
|
|
151
|
+
//#endregion
|
|
152
|
+
export { consumerHasSmrtUsers, generateResourcesRoute };
|
|
153
|
+
|
|
154
|
+
//# sourceMappingURL=resources-route.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resources-route.js","names":[],"sources":["../../src/vite-plugin/resources-route.ts"],"sourcesContent":["/**\n * SvelteKit `_resources` route generation — CLI discovery endpoint (#2663;\n * parent epic #2655).\n *\n * Emits `{routesDir}/_resources/+server.ts`: `GET /api/_resources`, the\n * endpoint the distributable CLI (`@happyvertical/smrt-app-cli`) reads to\n * learn which resources and commands it can invoke. Every other generated\n * route lives in `sveltekit-generator.ts` or a sibling emitter; this one is\n * kept in its own module for the same reason `changes-route.ts` and\n * `events-route.ts` are — a one-line registration hook in the shared\n * generator file.\n *\n * ## The dependency constraint\n *\n * The handler this route delegates to, `createResourceListHandler`, lives in\n * `@happyvertical/smrt-users` — and `smrt-users` depends on `smrt-core`, not\n * the reverse, so core cannot import it directly. That is not a problem for\n * *emitting* the import: the generated file is written into the consumer's\n * own `src/routes/`, where `@happyvertical/smrt-users` resolves through the\n * consumer's own dependency graph, exactly like the `@happyvertical/smrt-tenancy`\n * import `sveltekit-generator.ts`'s `generateTenantContextHelper` already\n * emits into tenant-scoped routes (see the comment above that function). The\n * only question is *when* it is safe to emit — a consumer that never took a\n * dependency on `smrt-users` must never receive a route whose import cannot\n * resolve, or a working build turns red.\n *\n * ## Detection mechanism\n *\n * Unlike the tenancy guard — which reads a semantic signal already present in\n * the manifest (whether any object is `@TenantScoped`, which itself implies\n * the app depends on `smrt-tenancy`) — nothing in the manifest indicates\n * whether a consumer has taken on `smrt-users`. Objects don't declare an\n * auth dependency; the app does, at the package level.\n *\n * So this route uses a **resolvable-dependency check**: does the exact\n * subpath the generated file imports — `@happyvertical/smrt-users/sveltekit`\n * — resolve from the consumer's `projectRoot`, via a `createRequire` rooted\n * at the consumer's own `package.json`? That is stronger than reading\n * `package.json`'s `dependencies` block (which can list a package that was\n * never installed) and, unlike a single hardcoded\n * `node_modules/@happyvertical/smrt-users/package.json` path, it uses\n * Node's real module resolution — so it also finds the package when a\n * workspace hoists shared dependencies to an ancestor `node_modules` (npm/\n * yarn workspaces, or pnpm with `node-linker=hoisted`) rather than\n * installing them directly under the consumer's own `projectRoot`.\n * Resolving the `/sveltekit` subpath specifically, rather than the\n * package's bare `package.json`, matters too: `smrt-users`'s `exports` map\n * does not list `./package.json`, so resolving that path would throw\n * `ERR_PACKAGE_PATH_NOT_EXPORTED` even when the package is genuinely\n * installed — resolving the subpath the route actually imports sidesteps\n * that and checks precisely the thing that must succeed at build time.\n *\n * An explicit `sveltekit: { resourcesRoute: { enabled: false } }` escape\n * hatch is also available, matching `changesRoute`/`eventsRoute`, for a\n * consumer that has `smrt-users` installed but wants to keep hand-writing\n * (or omit) `_resources`.\n *\n * ## Preserving a hand-written route\n *\n * Every consumer today hand-writes this route (the reference implementation\n * in the issue). `clearGeneratedRouteFiles` (in `sveltekit-generator.ts`)\n * already only deletes `+server.ts` files that start with\n * {@link AUTO_GENERATED_ROUTE_HEADER}, so a hand-written `_resources` route —\n * which never carries that header — survives the pre-generation sweep\n * untouched. This emitter additionally checks `existsSync` on the target\n * path before writing: by the time it runs, anything still there is\n * necessarily hand-written (a generator-owned copy would already have been\n * swept), so its mere presence is sufficient to skip generation and leave it\n * alone.\n *\n * Once a consumer's `_resources` route IS generator-owned (no hand-written\n * file present, `smrt-users` resolvable), its path is generator-owned for\n * `.gitignore` purposes too — it is included in `generatedRoutePaths` like\n * any other emitted route, so `updateGitignore` puts it in the managed\n * block. A project that migrated from a hand-written route to a fully\n * generated one may still carry a stale `# Application-owned rule` comment\n * above the same literal path from before the migration; `updateGitignore`\n * only ever touches its own managed block, so that stale comment is left as\n * project history for the consumer to clean up — the same way any other\n * manual `.gitignore` edit outside the managed block is preserved.\n */\n\nimport { existsSync, mkdirSync, writeFileSync } from 'node:fs';\nimport { createRequire } from 'node:module';\nimport { join } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport { AUTO_GENERATED_ROUTE_HEADER } from './route-header.js';\nimport type { SvelteKitOptions } from './sveltekit-generator.js';\n\n/**\n * Whether `@happyvertical/smrt-users/sveltekit` — the exact subpath the\n * generated route imports — is resolvable from the consumer at\n * `projectRoot`. See the module doc for why this check (rather than reading\n * `package.json`'s declared `dependencies`, or checking one hardcoded\n * physical path) is the chosen signal.\n */\nexport function consumerHasSmrtUsers(projectRoot: string): boolean {\n try {\n const consumerRequire = createRequire(\n pathToFileURL(join(projectRoot, 'package.json')).href,\n );\n consumerRequire.resolve('@happyvertical/smrt-users/sveltekit');\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Generate the `_resources/+server.ts` route. Returns true when a route was\n * written. Disabled with `sveltekit: { resourcesRoute: { enabled: false } }`;\n * skipped (with a log line) when `@happyvertical/smrt-users` is not\n * resolvable from the consumer, or when a hand-written route already\n * occupies the path.\n */\nexport function generateResourcesRoute(\n projectRoot: string,\n options: SvelteKitOptions,\n): boolean {\n if (options.resourcesRoute?.enabled === false) {\n return false;\n }\n\n if (!consumerHasSmrtUsers(projectRoot)) {\n console.log(\n '[smrt] Skipping _resources route - @happyvertical/smrt-users is not ' +\n 'resolvable; createResourceListHandler would not resolve in the ' +\n 'consumer build',\n );\n return false;\n }\n\n const routeDir = join(projectRoot, options.routesDir, '_resources');\n const filePath = join(routeDir, '+server.ts');\n\n // By this point in generation, clearGeneratedRouteFiles has already swept\n // any previously generator-owned copy at this path — anything still here\n // is necessarily hand-written. Preserve it rather than clobbering a\n // consumer's customized discovery route. Check every server-module\n // extension SvelteKit resolves by default (`.js` and `.ts` — see\n // https://svelte.dev/docs/kit/configuration#moduleExtensions), not only\n // `.ts`: a hand-written `+server.js` is invisible to a `.ts`-only check,\n // so the generator would write `+server.ts` alongside it and SvelteKit\n // would then refuse the route with \"Multiple endpoint files found\".\n const existingServerFile = ['+server.ts', '+server.js'].find((name) =>\n existsSync(join(routeDir, name)),\n );\n if (existingServerFile) {\n console.log(\n `[smrt] Preserving hand-written _resources route (${existingServerFile}, not generator-owned)`,\n );\n return false;\n }\n\n if (!existsSync(routeDir)) {\n mkdirSync(routeDir, { recursive: true });\n }\n writeFileSync(filePath, generateResourcesRouteTemplate(options), 'utf-8');\n console.log(`[smrt] Generated: ${filePath}`);\n return true;\n}\n\nfunction generateResourcesRouteTemplate(options: SvelteKitOptions): string {\n const kebabRoutesOption = options.kebabRoutes ? '\\n kebabRoutes: true,' : '';\n\n return `${AUTO_GENERATED_ROUTE_HEADER}\n// DO NOT EDIT - changes will be overwritten\n//\n// GET /api/_resources — CLI discovery endpoint (#2663). Returns the auth-aware\n// list of resources and commands the distributable CLI\n// (@happyvertical/smrt-app-cli) can invoke, derived from ObjectRegistry by\n// createResourceListHandler. Subsumes the route every consumer previously\n// hand-wrote (see the issue for the reference implementation this replaces).\n\nimport { createResourceListHandler } from '@happyvertical/smrt-users/sveltekit';\n// Side effect: registers @smrt() classes in ObjectRegistry before the\n// handler walks it (mirrors the generated CRUD/changes/events routes'\n// $lib/server/smrt import).\nimport '$lib/server/smrt';\n\nexport const GET = createResourceListHandler({\n ensureRegistry: async () => {\n await import('$lib/server/smrt');\n },${kebabRoutesOption}\n});\n`;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgGA,SAAgB,qBAAqB,aAA8B;CACjE,IAAI;EAIF,cAFE,cAAc,KAAK,aAAa,cAAc,CAAC,CAAC,CAAC,IAEnD,CAAA,CAAgB,QAAQ,qCAAqC;EAC7D,OAAO;CACT,QAAQ;EACN,OAAO;CACT;AACF;;;;;;;;AASA,SAAgB,uBACd,aACA,SACS;CACT,IAAI,QAAQ,gBAAgB,YAAY,OACtC,OAAO;CAGT,IAAI,CAAC,qBAAqB,WAAW,GAAG;EACtC,QAAQ,IACN,mJAGF;EACA,OAAO;CACT;CAEA,MAAM,WAAW,KAAK,aAAa,QAAQ,WAAW,YAAY;CAClE,MAAM,WAAW,KAAK,UAAU,YAAY;CAW5C,MAAM,qBAAqB,CAAC,cAAc,YAAY,CAAC,CAAC,MAAM,SAC5D,WAAW,KAAK,UAAU,IAAI,CAAC,CACjC;CACA,IAAI,oBAAoB;EACtB,QAAQ,IACN,oDAAoD,mBAAmB,uBACzE;EACA,OAAO;CACT;CAEA,IAAI,CAAC,WAAW,QAAQ,GACtB,UAAU,UAAU,EAAE,WAAW,KAAK,CAAC;CAEzC,cAAc,UAAU,+BAA+B,OAAO,GAAG,OAAO;CACxE,QAAQ,IAAI,qBAAqB,UAAU;CAC3C,OAAO;AACT;AAEA,SAAS,+BAA+B,SAAmC;CAGzE,OAAO,GAAG,4BAA4B;;;;;;;;;;;;;;;;;;MAFZ,QAAQ,cAAc,2BAA2B,GAoBrD;;;AAGxB"}
|
|
@@ -43,6 +43,16 @@ export interface SvelteKitOptions {
|
|
|
43
43
|
allowedOrigins?: string[];
|
|
44
44
|
allowCredentials?: boolean;
|
|
45
45
|
};
|
|
46
|
+
/**
|
|
47
|
+
* CLI discovery `_resources` route generation (#2663). Enabled by default,
|
|
48
|
+
* but only ever emitted when `@happyvertical/smrt-users` is resolvable
|
|
49
|
+
* from the consumer (see `resources-route.ts` for the detection
|
|
50
|
+
* mechanism) — set `enabled: false` to opt out even when it is present. A
|
|
51
|
+
* pre-existing hand-written `_resources` route is always preserved.
|
|
52
|
+
*/
|
|
53
|
+
resourcesRoute?: {
|
|
54
|
+
enabled?: boolean;
|
|
55
|
+
};
|
|
46
56
|
}
|
|
47
57
|
export interface ResolvedApiActionRouteConfig {
|
|
48
58
|
scope: 'item' | 'collection';
|
|
@@ -88,19 +98,45 @@ export interface CliApiCoherenceViolation {
|
|
|
88
98
|
unreachable: string[];
|
|
89
99
|
}
|
|
90
100
|
/**
|
|
91
|
-
* Inspect a manifest and return classes whose
|
|
92
|
-
*
|
|
93
|
-
* `cli:
|
|
101
|
+
* Inspect a manifest and return classes whose effective CLI command set —
|
|
102
|
+
* `cli.include`/`cli.exclude` when spelled out, or (#2638) the same "every
|
|
103
|
+
* public method minus exclude" default `cli: true`/`cli: {}` resolves to —
|
|
104
|
+
* references a command not exposed via the API. Classes that opt out via
|
|
105
|
+
* `cli: { skipApiCheck: true }` are skipped, and so is a class with
|
|
106
|
+
* `cli: false` (no CLI surface at all).
|
|
94
107
|
*
|
|
95
108
|
* Throws nothing; returns the violation list so callers can choose to throw
|
|
96
109
|
* or warn.
|
|
97
110
|
*/
|
|
98
111
|
export declare function findCliApiCoherenceViolations(manifest: SmartObjectManifest): CliApiCoherenceViolation[];
|
|
99
112
|
/**
|
|
100
|
-
* Throw if any class in the manifest has `cli.include`
|
|
101
|
-
* reachable via the API. Default build-time gate; opt-out
|
|
102
|
-
* `cli: { skipApiCheck: true }` (or globally via the vite
|
|
103
|
-
* `validateCliApiCoherence: false`).
|
|
113
|
+
* Throw if any class in the manifest has an EXPLICIT `cli.include` command
|
|
114
|
+
* that isn't reachable via the API. Default build-time gate; opt-out
|
|
115
|
+
* per-class via `cli: { skipApiCheck: true }` (or globally via the vite
|
|
116
|
+
* plugin option `validateCliApiCoherence: false`).
|
|
117
|
+
*
|
|
118
|
+
* This is deliberately narrower than `findCliApiCoherenceViolations` above.
|
|
119
|
+
* That function is now fully correct for the broader `cli: true`/`cli: {}`
|
|
120
|
+
* default surface too (#2638: same "every public method minus exclude"
|
|
121
|
+
* resolution `listCommands()` uses, so a bare `cli: true` is inspected the
|
|
122
|
+
* same as an explicit `include`). But `smrtPlugin()`'s default
|
|
123
|
+
* `validateCliApiCoherence: true` calls this THROWING gate unconditionally
|
|
124
|
+
* from every consuming package's own build (`configResolved`,
|
|
125
|
+
* `vite-plugin/index.ts`) -- and a full-monorepo build during #2638
|
|
126
|
+
* development showed that broader surface has thousands of pre-existing
|
|
127
|
+
* public getters/business-logic methods (e.g.
|
|
128
|
+
* `@happyvertical/smrt-ads:AdGroup.hasStarted`,
|
|
129
|
+
* `@happyvertical/smrt-tags:Tag.getParent`) across ~28 packages that were
|
|
130
|
+
* never meant to be REST-reachable and have no API route today. That
|
|
131
|
+
* over-exposure is real, pre-existing, and unrelated to #2638's framework-
|
|
132
|
+
* lifecycle-method defect -- flipping this gate to enforce it unconditionally
|
|
133
|
+
* would break every one of those packages' builds today, which is a
|
|
134
|
+
* repo-wide triage effort (add routes, or an explicit `cli.exclude`/
|
|
135
|
+
* `skipApiCheck` per owner) this PR did not undertake and is out of its
|
|
136
|
+
* file boundary. So the gate keeps its pre-#2638 blast radius -- classes
|
|
137
|
+
* that spell out a non-empty `cli.include` -- while `findCliApiCoherenceViolations`
|
|
138
|
+
* is complete and available (and covered by its own tests) for the broader
|
|
139
|
+
* check once that pre-existing surface is triaged.
|
|
104
140
|
*/
|
|
105
141
|
export declare function validateCliIncludeAgainstApi(manifest: SmartObjectManifest): void;
|
|
106
142
|
//# sourceMappingURL=sveltekit-generator.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sveltekit-generator.d.ts","sourceRoot":"","sources":["../../src/vite-plugin/sveltekit-generator.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAWH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"sveltekit-generator.d.ts","sourceRoot":"","sources":["../../src/vite-plugin/sveltekit-generator.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAWH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAYvE,OAAO,KAAK,EAGV,aAAa,EAEd,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAEV,qBAAqB,EACrB,mBAAmB,EACpB,MAAM,kBAAkB,CAAC;AAa1B,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,iFAAiF;IACjF,SAAS,CAAC,EAAE,qBAAqB,CAAC;IAClC;;;OAGG;IACH,YAAY,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IACrC;;;;;;;;;;;;;;OAcG;IACH,WAAW,CAAC,EAAE;QACZ,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;QAC1B,gBAAgB,CAAC,EAAE,OAAO,CAAC;KAC5B,CAAC;IACF;;;;;;OAMG;IACH,cAAc,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;CACxC;AAYD,MAAM,WAAW,4BAA4B;IAC3C,KAAK,EAAE,MAAM,GAAG,YAAY,CAAC;IAC7B,MAAM,EAAE,aAAa,CAAC;IACtB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,cAAc,EAAE,MAAM,EAAE,CAAC;CAC1B;AAqxBD;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAKtD;AA+BD,wBAAgB,2BAA2B,CACzC,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,EACjC,SAAS,EAAE,OAAO,EAClB,YAAY,GAAE;IAAE,WAAW,CAAC,EAAE,OAAO,CAAA;CAAO,EAC5C,YAAY,GAAE,MAAM,GAAG,YAEb,GACT,4BAA4B,CAwB9B;AAiUD;;GAEG;AACH,wBAAsB,uBAAuB,CAC3C,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,mBAAmB,EAC7B,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,IAAI,CAAC,CAqGf;AAy0BD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,qBAAqB,EAChC,QAAQ,CAAC,EAAE,mBAAmB,GAC7B,GAAG,CAAC,MAAM,CAAC,CA6Cb;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAuDD;;;;;;;;;;GAUG;AACH,wBAAgB,6BAA6B,CAC3C,QAAQ,EAAE,mBAAmB,GAC5B,wBAAwB,EAAE,CAsB5B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,mBAAmB,GAC5B,IAAI,CA2BN"}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { MAX_LIST_LIMIT, buildDefaultListOrderBy } from "../query-bounds.js";
|
|
2
|
-
import { resolveCustomActionMetadata } from "../generators/custom-action.js";
|
|
2
|
+
import { resolveCustomActionMetadata, resolveCustomActionNames } from "../generators/custom-action.js";
|
|
3
|
+
import { isFrameworkBaseClass } from "../registry/framework-base-classes.js";
|
|
3
4
|
import { generateConditionalGetRouteHelper } from "../generators/conditional-get.js";
|
|
4
5
|
import { AUTO_GENERATED_ROUTE_HEADER } from "./route-header.js";
|
|
5
6
|
import { generateChangesRoute } from "./changes-route.js";
|
|
6
7
|
import { generateEventsRoute } from "./events-route.js";
|
|
8
|
+
import { generateResourcesRoute } from "./resources-route.js";
|
|
7
9
|
import { generateSyncApplyRoute } from "./sync-apply-route.js";
|
|
8
10
|
import { computeWebManifestHash, isCollectionManifestClass, resolveCollectionItemObject, resolveCollectionItemTypeName } from "./web-collections.js";
|
|
9
11
|
import { existsSync, mkdirSync, readFileSync, readdirSync, unlinkSync, writeFileSync } from "node:fs";
|
|
@@ -764,6 +766,7 @@ async function generateSvelteKitRoutes(projectRoot, manifest, options) {
|
|
|
764
766
|
let generatedCount = 0;
|
|
765
767
|
let skippedCollections = 0;
|
|
766
768
|
for (const [className, objectDef] of Object.entries(manifest.objects)) {
|
|
769
|
+
if (isFrameworkBaseClass(objectDef.className, objectDef.packageName)) continue;
|
|
767
770
|
if (isCollectionManifestClass(manifest, objectDef)) {
|
|
768
771
|
const collectionRoutePaths = await generateCollectionRoutesForObject(projectRoot, className, objectDef, manifest, options);
|
|
769
772
|
generatedRoutePaths.push(...collectionRoutePaths);
|
|
@@ -781,6 +784,7 @@ async function generateSvelteKitRoutes(projectRoot, manifest, options) {
|
|
|
781
784
|
if (generateSyncApplyRoute(projectRoot, manifest, options)) generatedRoutePaths.push(join(projectRoot, options.routesDir, "sync", "apply", "+server.ts"));
|
|
782
785
|
if (generateChangesRoute(projectRoot, manifest, options)) generatedRoutePaths.push(join(projectRoot, options.routesDir, "_changes", "+server.ts"));
|
|
783
786
|
if (generateEventsRoute(projectRoot, manifest, options, computeWebManifestHash(manifest))) generatedRoutePaths.push(join(projectRoot, options.routesDir, "_events", "+server.ts"));
|
|
787
|
+
if (generateResourcesRoute(projectRoot, options)) generatedRoutePaths.push(join(projectRoot, options.routesDir, "_resources", "+server.ts"));
|
|
784
788
|
updateGitignore(projectRoot, generatedRoutePaths);
|
|
785
789
|
const skippedMsg = skippedCollections > 0 ? ` (skipped ${skippedCollections} collection classes)` : "";
|
|
786
790
|
console.log(`[smrt] Generated routes for ${generatedCount} SMRT objects${skippedMsg}`);
|
|
@@ -1295,9 +1299,54 @@ function resolveApiActionSet(objectDef, manifest) {
|
|
|
1295
1299
|
return actions;
|
|
1296
1300
|
}
|
|
1297
1301
|
/**
|
|
1298
|
-
*
|
|
1299
|
-
*
|
|
1300
|
-
*
|
|
1302
|
+
* Resolve the effective CLI command set for an object, for the cli↔api
|
|
1303
|
+
* coherence lint. Two distinct resolutions, chosen by whether `cli.include`
|
|
1304
|
+
* is spelled out:
|
|
1305
|
+
*
|
|
1306
|
+
* **Explicit `cli.include`** (preserves the lint's exact pre-#2638
|
|
1307
|
+
* algorithm): the literal `include − exclude`, verbatim, whether or not an
|
|
1308
|
+
* entry resolves to a real CRUD verb or a scanned public method. This is
|
|
1309
|
+
* deliberate, not an oversight -- an `include` entry that names a typo, a
|
|
1310
|
+
* getter (never in the manifest's `methods` map), or a private/protected
|
|
1311
|
+
* method (the scanner drops these rather than recording `isPublic: false`,
|
|
1312
|
+
* so nothing else catches it) must still surface as "unreachable" so the
|
|
1313
|
+
* build fails loudly on the config mistake, the same way it always has.
|
|
1314
|
+
* Resolving it instead through `resolveCustomActionNames` -- which only
|
|
1315
|
+
* iterates real scanned methods -- would silently drop such an entry with no
|
|
1316
|
+
* error at all (final review, #2638).
|
|
1317
|
+
*
|
|
1318
|
+
* **Bare `cli: true`/`cli: {}`** (#2638, the new case this lint now
|
|
1319
|
+
* inspects): every public custom method minus exclude, exactly the "every
|
|
1320
|
+
* public method minus exclude" resolution `CLIGenerator.listCommands()`
|
|
1321
|
+
* already applies (via `resolveCustomActionNames`, reused not
|
|
1322
|
+
* reimplemented) -- lifecycle methods excluded, same as everywhere else.
|
|
1323
|
+
* CRUD verbs are NOT checked in this branch: a class that closes its API
|
|
1324
|
+
* entirely (`api: false`) while keeping a default-open, CLI/MCP-only admin
|
|
1325
|
+
* surface (`cli: true`/omitted) is a common, intentional combination (see
|
|
1326
|
+
* `cli.skipApiCheck`'s own "in-process, no HTTP" doc comment); blanket-
|
|
1327
|
+
* checking CRUD reachability for every such class would flag that existing,
|
|
1328
|
+
* legitimate pattern across the whole codebase, not just the #2638 defect
|
|
1329
|
+
* (confirmed by running this against the full monorepo build during
|
|
1330
|
+
* development — it did exactly that).
|
|
1331
|
+
*/
|
|
1332
|
+
function resolveCliActionSet(objectDef) {
|
|
1333
|
+
const cliConfig = objectDef.decoratorConfig?.cli;
|
|
1334
|
+
if (cliConfig === false) return /* @__PURE__ */ new Set();
|
|
1335
|
+
const included = typeof cliConfig === "object" ? cliConfig.include : void 0;
|
|
1336
|
+
const excluded = typeof cliConfig === "object" && Array.isArray(cliConfig.exclude) ? cliConfig.exclude : [];
|
|
1337
|
+
if (included) return new Set(included.filter((cmd) => !excluded.includes(cmd)));
|
|
1338
|
+
return resolveCustomActionNames(Object.entries(objectDef.methods || {}), {
|
|
1339
|
+
include: void 0,
|
|
1340
|
+
exclude: excluded
|
|
1341
|
+
}, STANDARD_API_ACTIONS);
|
|
1342
|
+
}
|
|
1343
|
+
/**
|
|
1344
|
+
* Inspect a manifest and return classes whose effective CLI command set —
|
|
1345
|
+
* `cli.include`/`cli.exclude` when spelled out, or (#2638) the same "every
|
|
1346
|
+
* public method minus exclude" default `cli: true`/`cli: {}` resolves to —
|
|
1347
|
+
* references a command not exposed via the API. Classes that opt out via
|
|
1348
|
+
* `cli: { skipApiCheck: true }` are skipped, and so is a class with
|
|
1349
|
+
* `cli: false` (no CLI surface at all).
|
|
1301
1350
|
*
|
|
1302
1351
|
* Throws nothing; returns the violation list so callers can choose to throw
|
|
1303
1352
|
* or warn.
|
|
@@ -1306,14 +1355,12 @@ function findCliApiCoherenceViolations(manifest) {
|
|
|
1306
1355
|
const violations = [];
|
|
1307
1356
|
for (const [className, objectDef] of Object.entries(manifest.objects)) {
|
|
1308
1357
|
const cliConfig = objectDef.decoratorConfig?.cli;
|
|
1309
|
-
if (
|
|
1310
|
-
if (cliConfig.skipApiCheck) continue;
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
const effectiveCliCommands = cliConfig.include.filter((cmd) => !cliExclude.includes(cmd));
|
|
1314
|
-
if (effectiveCliCommands.length === 0) continue;
|
|
1358
|
+
if (cliConfig === false) continue;
|
|
1359
|
+
if (typeof cliConfig === "object" && cliConfig.skipApiCheck) continue;
|
|
1360
|
+
const effectiveCliCommands = resolveCliActionSet(objectDef);
|
|
1361
|
+
if (effectiveCliCommands.size === 0) continue;
|
|
1315
1362
|
const apiActionSet = resolveApiActionSet(objectDef, manifest);
|
|
1316
|
-
const unreachable = effectiveCliCommands.filter((action) => !apiActionSet.has(action));
|
|
1363
|
+
const unreachable = [...effectiveCliCommands].filter((action) => !apiActionSet.has(action)).sort();
|
|
1317
1364
|
if (unreachable.length > 0) violations.push({
|
|
1318
1365
|
className,
|
|
1319
1366
|
unreachable
|
|
@@ -1322,15 +1369,41 @@ function findCliApiCoherenceViolations(manifest) {
|
|
|
1322
1369
|
return violations;
|
|
1323
1370
|
}
|
|
1324
1371
|
/**
|
|
1325
|
-
* Throw if any class in the manifest has `cli.include`
|
|
1326
|
-
* reachable via the API. Default build-time gate; opt-out
|
|
1327
|
-
* `cli: { skipApiCheck: true }` (or globally via the vite
|
|
1328
|
-
* `validateCliApiCoherence: false`).
|
|
1372
|
+
* Throw if any class in the manifest has an EXPLICIT `cli.include` command
|
|
1373
|
+
* that isn't reachable via the API. Default build-time gate; opt-out
|
|
1374
|
+
* per-class via `cli: { skipApiCheck: true }` (or globally via the vite
|
|
1375
|
+
* plugin option `validateCliApiCoherence: false`).
|
|
1376
|
+
*
|
|
1377
|
+
* This is deliberately narrower than `findCliApiCoherenceViolations` above.
|
|
1378
|
+
* That function is now fully correct for the broader `cli: true`/`cli: {}`
|
|
1379
|
+
* default surface too (#2638: same "every public method minus exclude"
|
|
1380
|
+
* resolution `listCommands()` uses, so a bare `cli: true` is inspected the
|
|
1381
|
+
* same as an explicit `include`). But `smrtPlugin()`'s default
|
|
1382
|
+
* `validateCliApiCoherence: true` calls this THROWING gate unconditionally
|
|
1383
|
+
* from every consuming package's own build (`configResolved`,
|
|
1384
|
+
* `vite-plugin/index.ts`) -- and a full-monorepo build during #2638
|
|
1385
|
+
* development showed that broader surface has thousands of pre-existing
|
|
1386
|
+
* public getters/business-logic methods (e.g.
|
|
1387
|
+
* `@happyvertical/smrt-ads:AdGroup.hasStarted`,
|
|
1388
|
+
* `@happyvertical/smrt-tags:Tag.getParent`) across ~28 packages that were
|
|
1389
|
+
* never meant to be REST-reachable and have no API route today. That
|
|
1390
|
+
* over-exposure is real, pre-existing, and unrelated to #2638's framework-
|
|
1391
|
+
* lifecycle-method defect -- flipping this gate to enforce it unconditionally
|
|
1392
|
+
* would break every one of those packages' builds today, which is a
|
|
1393
|
+
* repo-wide triage effort (add routes, or an explicit `cli.exclude`/
|
|
1394
|
+
* `skipApiCheck` per owner) this PR did not undertake and is out of its
|
|
1395
|
+
* file boundary. So the gate keeps its pre-#2638 blast radius -- classes
|
|
1396
|
+
* that spell out a non-empty `cli.include` -- while `findCliApiCoherenceViolations`
|
|
1397
|
+
* is complete and available (and covered by its own tests) for the broader
|
|
1398
|
+
* check once that pre-existing surface is triaged.
|
|
1329
1399
|
*/
|
|
1330
1400
|
function validateCliIncludeAgainstApi(manifest) {
|
|
1331
|
-
const violations = findCliApiCoherenceViolations(manifest)
|
|
1401
|
+
const violations = findCliApiCoherenceViolations(manifest).filter(({ className }) => {
|
|
1402
|
+
const cliConfig = manifest.objects[className]?.decoratorConfig?.cli;
|
|
1403
|
+
return typeof cliConfig === "object" && Array.isArray(cliConfig.include) && cliConfig.include.length > 0;
|
|
1404
|
+
});
|
|
1332
1405
|
if (violations.length === 0) return;
|
|
1333
|
-
const messages = violations.flatMap(({ className, unreachable }) => unreachable.map((action) => `[smrt] ${className}.${action} is
|
|
1406
|
+
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.`));
|
|
1334
1407
|
throw new Error(messages.join("\n\n"));
|
|
1335
1408
|
}
|
|
1336
1409
|
/**
|