@happyvertical/smrt-core 0.43.10 → 0.44.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.
Files changed (81) hide show
  1. package/AGENTS.md +8 -8
  2. package/agents/generators.md +104 -0
  3. package/agents/revision-guard.md +66 -0
  4. package/agents/schema-paths.md +99 -0
  5. package/dist/cascade.d.ts +5 -9
  6. package/dist/cascade.d.ts.map +1 -1
  7. package/dist/cascade.js +65 -30
  8. package/dist/cascade.js.map +1 -1
  9. package/dist/embedded-write-queue.d.ts +8 -0
  10. package/dist/embedded-write-queue.d.ts.map +1 -1
  11. package/dist/embedded-write-queue.js +12 -2
  12. package/dist/embedded-write-queue.js.map +1 -1
  13. package/dist/generators/custom-action.d.ts.map +1 -1
  14. package/dist/generators/custom-action.js +1 -1
  15. package/dist/generators/custom-action.js.map +1 -1
  16. package/dist/generators/index.d.ts +1 -0
  17. package/dist/generators/index.d.ts.map +1 -1
  18. package/dist/generators/index.js +2 -1
  19. package/dist/generators/preflight-route.d.ts +151 -0
  20. package/dist/generators/preflight-route.d.ts.map +1 -0
  21. package/dist/generators/preflight-route.js +194 -0
  22. package/dist/generators/preflight-route.js.map +1 -0
  23. package/dist/generators/rest.d.ts +12 -0
  24. package/dist/generators/rest.d.ts.map +1 -1
  25. package/dist/generators/rest.js +12 -15
  26. package/dist/generators/rest.js.map +1 -1
  27. package/dist/generators.js +2 -1
  28. package/dist/index.d.ts +2 -1
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +5 -3
  31. package/dist/knowledge.d.ts +17 -1
  32. package/dist/knowledge.d.ts.map +1 -1
  33. package/dist/knowledge.js +33 -3
  34. package/dist/knowledge.js.map +1 -1
  35. package/dist/manifest/static-manifest.d.ts.map +1 -1
  36. package/dist/manifest/static-manifest.js +6 -2
  37. package/dist/manifest/static-manifest.js.map +1 -1
  38. package/dist/manifest/store.js +1 -1
  39. package/dist/manifest/store.js.map +1 -1
  40. package/dist/manifest.json +8 -2
  41. package/dist/migrations/differ.d.ts +49 -0
  42. package/dist/migrations/differ.d.ts.map +1 -1
  43. package/dist/migrations/differ.js +220 -4
  44. package/dist/migrations/differ.js.map +1 -1
  45. package/dist/migrations/orchestrate.d.ts +23 -0
  46. package/dist/migrations/orchestrate.d.ts.map +1 -1
  47. package/dist/migrations/orchestrate.js +12 -3
  48. package/dist/migrations/orchestrate.js.map +1 -1
  49. package/dist/object.d.ts +31 -1
  50. package/dist/object.d.ts.map +1 -1
  51. package/dist/object.js +57 -9
  52. package/dist/object.js.map +1 -1
  53. package/dist/registry/types.d.ts +8 -3
  54. package/dist/registry/types.d.ts.map +1 -1
  55. package/dist/revision-guard.d.ts +84 -0
  56. package/dist/revision-guard.d.ts.map +1 -0
  57. package/dist/revision-guard.js +120 -0
  58. package/dist/revision-guard.js.map +1 -0
  59. package/dist/schema/foreign-key-ddl.d.ts +7 -0
  60. package/dist/schema/foreign-key-ddl.d.ts.map +1 -1
  61. package/dist/schema/foreign-key-ddl.js +7 -1
  62. package/dist/schema/foreign-key-ddl.js.map +1 -1
  63. package/dist/schema/schema-manager.d.ts +15 -0
  64. package/dist/schema/schema-manager.d.ts.map +1 -1
  65. package/dist/schema/schema-manager.js +52 -9
  66. package/dist/schema/schema-manager.js.map +1 -1
  67. package/dist/schema/types.d.ts +10 -0
  68. package/dist/schema/types.d.ts.map +1 -1
  69. package/dist/schema/uuid-convergence.d.ts +142 -0
  70. package/dist/schema/uuid-convergence.d.ts.map +1 -0
  71. package/dist/schema/uuid-convergence.js +0 -0
  72. package/dist/schema/uuid-convergence.js.map +1 -0
  73. package/dist/smrt-knowledge.json +18 -9
  74. package/dist/utils/scanner-module.d.ts +44 -0
  75. package/dist/utils/scanner-module.d.ts.map +1 -1
  76. package/dist/vite-plugin/index.d.ts +14 -1
  77. package/dist/vite-plugin/index.d.ts.map +1 -1
  78. package/dist/vite-plugin/index.js +54 -2
  79. package/dist/vite-plugin/index.js.map +1 -1
  80. package/dist/vite-plugin.js +3 -2
  81. package/package.json +4 -4
@@ -0,0 +1,194 @@
1
+ import { ObjectRegistry } from "../registry.js";
2
+ import { PRIVATE_READ_CACHE_CONTROL } from "./conditional-get.js";
3
+ //#region src/generators/preflight-route.ts
4
+ /**
5
+ * Generated `_preflight` HTTP route for browser-plane playbook preflight
6
+ * (issue #2590).
7
+ *
8
+ * Handles `GET {basePath}/_preflight?key=<playbook key>` in the REST generator:
9
+ * an advisory, read-effect, idempotent report of what a caller's playbook would
10
+ * be allowed to do — *predicted*, never granted. Every step is authorized again
11
+ * where it executes; this route is capability **selection**, never
12
+ * authorization (epic #2585 invariant 2).
13
+ *
14
+ * ## `authMiddleware` is never invoked here
15
+ *
16
+ * Generated REST authorization is
17
+ * `authMiddleware?: (objectName, action) => (req) => Promise<Request | Response>`
18
+ * — request-bound, `Response`-returning rather than boolean, and free to consult
19
+ * session stores, rate-limit, or audit. It is not a dry-run predicate, and a
20
+ * synthetic-`Request` dry run of it would be a side effect the caller never
21
+ * asked for.
22
+ *
23
+ * That is enforced **structurally**, not by discipline:
24
+ * {@link PlaybookPreflightRouteOptions} has no `authMiddleware` member and no
25
+ * function-valued auth member of any kind. `rest.ts` passes the boolean
26
+ * `appAuthConfigured` and nothing else, so there is no handle in this module to
27
+ * invoke even by mistake. The app-auth layer is consequently reported as
28
+ * `unknown`, which is the honest answer.
29
+ *
30
+ * ## Why the provider is injected
31
+ *
32
+ * Resolution and verdict shaping live in `@happyvertical/smrt-playbooks`, which
33
+ * depends on this package. Core therefore owns the route and the static-layer
34
+ * facts (`ObjectRegistry` is core's), and takes the evaluator as a seam — the
35
+ * dependency stays one-way.
36
+ */
37
+ /** Path segment the preflight route is served at, under the API base path. */
38
+ var PLAYBOOK_PREFLIGHT_ROUTE_SEGMENT = "_preflight";
39
+ /**
40
+ * Capability classification of the preflight route: a read, safely repeatable,
41
+ * and closed-world. It is admitted by a default read-only browser exposure
42
+ * policy without an opt-in, because it reports on the system rather than
43
+ * changing it.
44
+ */
45
+ var PLAYBOOK_PREFLIGHT_CAPABILITY = Object.freeze({
46
+ effect: "read",
47
+ idempotent: true,
48
+ openWorld: false
49
+ });
50
+ function jsonResponse(body, status) {
51
+ return new Response(JSON.stringify(body), {
52
+ status,
53
+ headers: {
54
+ "Content-Type": "application/json",
55
+ "Cache-Control": PRIVATE_READ_CACHE_CONTROL
56
+ }
57
+ });
58
+ }
59
+ /**
60
+ * Serves `GET {basePath}/_preflight?key=<key>`.
61
+ *
62
+ * Unresolvable keys are the provider's concern: it returns the same uniform
63
+ * "unavailable" body for an unknown key as for an unauthorized one, and this
64
+ * route serves whatever it returns with an unconditional 200, so the HTTP layer
65
+ * adds no way to tell them apart either.
66
+ */
67
+ async function handlePlaybookPreflightRoute(req, options) {
68
+ if (!options.provider) return jsonResponse({ error: "Not found" }, 404);
69
+ if (req.method !== "GET") return jsonResponse({ error: "Method not allowed" }, 405);
70
+ const key = new URL(req.url).searchParams.get("key");
71
+ if (!key) return jsonResponse({ error: "Query parameter 'key' is required" }, 400);
72
+ return jsonResponse(await options.provider({
73
+ key,
74
+ plane: "browser",
75
+ ...options.permissions ? { permissions: options.permissions } : {},
76
+ appAuthConfigured: options.appAuthConfigured,
77
+ db: options.db
78
+ }), 200);
79
+ }
80
+ /**
81
+ * Resolves a qualified model reference (`@happyvertical/smrt-commerce:Order`)
82
+ * to the name the registry and the REST generator key configuration by.
83
+ *
84
+ * Returns `undefined` for anything this build does not register, so the caller
85
+ * fails closed rather than reporting on a model that has no route.
86
+ */
87
+ function resolveRegisteredObjectName(model) {
88
+ return (model.includes(":") ? ObjectRegistry.getClassByQualifiedName(model) : ObjectRegistry.getClass(model))?.name;
89
+ }
90
+ /**
91
+ * The HTTP method a REST action is served by.
92
+ *
93
+ * CRUD actions map to their fixed verbs. A custom action is served under the
94
+ * method its own route config declares (`api.routes[action].method`, defaulting
95
+ * to `POST` exactly as `dispatchCustomCollectionAction` does), because guessing
96
+ * `POST` for a declared `GET` action would make preflight report a false `deny`
97
+ * on a `public: 'read'` model — hiding a playbook the caller can actually run,
98
+ * which is the tool-listing case this exists to serve. Without an `objectName`
99
+ * to read the declaration from, the fail-closed `POST` remains.
100
+ */
101
+ function restMethodForApiAction(action, objectName) {
102
+ switch (action) {
103
+ case "list":
104
+ case "get": return "GET";
105
+ case "create": return "POST";
106
+ case "update": return "PUT";
107
+ case "delete": return "DELETE";
108
+ default: break;
109
+ }
110
+ if (!objectName) return "POST";
111
+ const apiConfig = ObjectRegistry.getConfig(objectName)?.api;
112
+ if (!apiConfig || typeof apiConfig !== "object" || !apiConfig.routes) return "POST";
113
+ return (apiConfig.routes[action]?.method ?? "POST").toUpperCase();
114
+ }
115
+ /**
116
+ * Whether `@smrt({ api })` exposes `action` on `objectName`.
117
+ *
118
+ * The generator's own action gate, lifted to a module function so preflight and
119
+ * the live route read the same rule from one place. Accepts any action name so
120
+ * a custom action is gated by the same `include`/`exclude` lists.
121
+ *
122
+ * @remarks An absent `objectName` returns `true` — fail-**open**, because the
123
+ * live route reaches this only after it has already resolved a model, and an
124
+ * unnamed object there means "not gated by this rule". A caller *predicting*
125
+ * rather than serving has no such guarantee and must reject an unresolvable
126
+ * model before asking (see `createRestPreflightLayerSource`).
127
+ */
128
+ function isApiActionEnabledForObject(objectName, action) {
129
+ if (!objectName) return true;
130
+ const apiConfig = ObjectRegistry.getConfig(objectName).api;
131
+ if (apiConfig === false) return false;
132
+ if (apiConfig && typeof apiConfig === "object") {
133
+ if (apiConfig.include && !apiConfig.include.includes(action)) return false;
134
+ if (apiConfig.exclude?.includes(action)) return false;
135
+ }
136
+ return true;
137
+ }
138
+ /**
139
+ * Whether `action` names a route the generated REST surface can actually
140
+ * dispatch on `objectName`.
141
+ *
142
+ * CRUD actions always have a route. A custom action exists only when the
143
+ * decorator declares it in `api.routes` — `dispatchCustomCollectionAction`
144
+ * iterates exactly that map, so an action absent from it can never execute no
145
+ * matter what `include`/`exclude` say. Exposure alone would report a typo'd or
146
+ * removed custom action as `allow`, and an agent would then start the earlier
147
+ * steps of a non-atomic playbook before dying on it — the failure preflight
148
+ * exists to prevent.
149
+ */
150
+ function isRestActionRoutable(objectName, action) {
151
+ if (!objectName) return false;
152
+ if (action === "list" || action === "get" || action === "create" || action === "update" || action === "delete") return true;
153
+ const apiConfig = ObjectRegistry.getConfig(objectName)?.api;
154
+ if (!apiConfig || typeof apiConfig !== "object" || !apiConfig.routes) return false;
155
+ return Object.hasOwn(apiConfig.routes, action);
156
+ }
157
+ /**
158
+ * Fail-closed authorization posture (#1540): true only when the object opts out
159
+ * of auth via `@smrt({ api: { public } })` — `true` for every method, `'read'`
160
+ * for safe (GET) methods only.
161
+ */
162
+ function isRestRoutePublic(objectName, method) {
163
+ if (!objectName) return false;
164
+ const apiConfig = ObjectRegistry.getConfig(objectName)?.api;
165
+ if (!apiConfig || typeof apiConfig !== "object") return false;
166
+ const publicAccess = apiConfig.public;
167
+ if (publicAccess === true) return true;
168
+ if (publicAccess === "read") return method.toUpperCase() === "GET";
169
+ return false;
170
+ }
171
+ /**
172
+ * Field-level read-permission slugs declared by `objectName`.
173
+ *
174
+ * Reads the model's own and inherited fields only — deliberately **not** the
175
+ * STI base-plus-descendants union used for cache policy. That union answers
176
+ * "does any variant carry a read-permission field?"; used here it would report
177
+ * a sibling variant's slugs as required for a step naming this concrete model.
178
+ */
179
+ function restFieldReadPermissions(objectName) {
180
+ if (!objectName) return [];
181
+ const registered = ObjectRegistry.getClass(objectName);
182
+ const className = registered?.qualifiedName ?? registered?.name ?? objectName;
183
+ const fields = registered?.inheritedFields ?? ObjectRegistry.getFields(className);
184
+ const slugs = /* @__PURE__ */ new Set();
185
+ for (const [, def] of fields) {
186
+ const slug = typeof def?.readPermission === "string" ? def.readPermission : typeof def?._meta?.readPermission === "string" ? def._meta.readPermission : void 0;
187
+ if (slug) slugs.add(slug);
188
+ }
189
+ return [...slugs].sort();
190
+ }
191
+ //#endregion
192
+ export { PLAYBOOK_PREFLIGHT_CAPABILITY, PLAYBOOK_PREFLIGHT_ROUTE_SEGMENT, handlePlaybookPreflightRoute, isApiActionEnabledForObject, isRestActionRoutable, isRestRoutePublic, resolveRegisteredObjectName, restFieldReadPermissions, restMethodForApiAction };
193
+
194
+ //# sourceMappingURL=preflight-route.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preflight-route.js","names":[],"sources":["../../src/generators/preflight-route.ts"],"sourcesContent":["/**\n * Generated `_preflight` HTTP route for browser-plane playbook preflight\n * (issue #2590).\n *\n * Handles `GET {basePath}/_preflight?key=<playbook key>` in the REST generator:\n * an advisory, read-effect, idempotent report of what a caller's playbook would\n * be allowed to do — *predicted*, never granted. Every step is authorized again\n * where it executes; this route is capability **selection**, never\n * authorization (epic #2585 invariant 2).\n *\n * ## `authMiddleware` is never invoked here\n *\n * Generated REST authorization is\n * `authMiddleware?: (objectName, action) => (req) => Promise<Request | Response>`\n * — request-bound, `Response`-returning rather than boolean, and free to consult\n * session stores, rate-limit, or audit. It is not a dry-run predicate, and a\n * synthetic-`Request` dry run of it would be a side effect the caller never\n * asked for.\n *\n * That is enforced **structurally**, not by discipline:\n * {@link PlaybookPreflightRouteOptions} has no `authMiddleware` member and no\n * function-valued auth member of any kind. `rest.ts` passes the boolean\n * `appAuthConfigured` and nothing else, so there is no handle in this module to\n * invoke even by mistake. The app-auth layer is consequently reported as\n * `unknown`, which is the honest answer.\n *\n * ## Why the provider is injected\n *\n * Resolution and verdict shaping live in `@happyvertical/smrt-playbooks`, which\n * depends on this package. Core therefore owns the route and the static-layer\n * facts (`ObjectRegistry` is core's), and takes the evaluator as a seam — the\n * dependency stays one-way.\n */\n\nimport { ObjectRegistry } from '../registry.js';\nimport { PRIVATE_READ_CACHE_CONTROL } from './conditional-get.js';\n\n/** Path segment the preflight route is served at, under the API base path. */\nexport const PLAYBOOK_PREFLIGHT_ROUTE_SEGMENT = '_preflight';\n\n/**\n * Capability classification of the preflight route: a read, safely repeatable,\n * and closed-world. It is admitted by a default read-only browser exposure\n * policy without an opt-in, because it reports on the system rather than\n * changing it.\n */\nexport const PLAYBOOK_PREFLIGHT_CAPABILITY = Object.freeze({\n effect: 'read',\n idempotent: true,\n openWorld: false,\n} as const);\n\n/**\n * The request handed to a preflight provider. Deliberately carries no request,\n * no headers, and no auth handle — only the caller-scoped facts the static\n * layers need.\n */\nexport interface PlaybookPreflightRouteRequest {\n /** The requested playbook key. */\n key: string;\n /** Always `'browser'` from this route. */\n plane: 'browser';\n /** The caller's published permission slugs, when the API context has them. */\n permissions?: Iterable<string>;\n /** Whether an app auth middleware is wired. A boolean; never the function. */\n appAuthConfigured: boolean;\n /** The generator's `APIContext.db`. */\n db?: unknown;\n}\n\n/**\n * Host-supplied browser-plane preflight evaluator, wired from\n * `@happyvertical/smrt-playbooks`. Returns a JSON-serializable report.\n */\nexport type PlaybookPreflightProvider = (\n request: PlaybookPreflightRouteRequest,\n) => Promise<unknown>;\n\n/**\n * Options for {@link handlePlaybookPreflightRoute}.\n *\n * There is intentionally no `authMiddleware` member — see the module docs.\n */\nexport interface PlaybookPreflightRouteOptions {\n provider?: PlaybookPreflightProvider;\n permissions?: Iterable<string>;\n appAuthConfigured: boolean;\n db?: unknown;\n}\n\nfunction jsonResponse(body: unknown, status: number): Response {\n return new Response(JSON.stringify(body), {\n status,\n headers: {\n 'Content-Type': 'application/json',\n 'Cache-Control': PRIVATE_READ_CACHE_CONTROL,\n },\n });\n}\n\n/**\n * Serves `GET {basePath}/_preflight?key=<key>`.\n *\n * Unresolvable keys are the provider's concern: it returns the same uniform\n * \"unavailable\" body for an unknown key as for an unauthorized one, and this\n * route serves whatever it returns with an unconditional 200, so the HTTP layer\n * adds no way to tell them apart either.\n */\nexport async function handlePlaybookPreflightRoute(\n req: Request,\n options: PlaybookPreflightRouteOptions,\n): Promise<Response> {\n if (!options.provider) {\n // Route not wired for this deployment. Says nothing about any key.\n return jsonResponse({ error: 'Not found' }, 404);\n }\n\n if (req.method !== 'GET') {\n return jsonResponse({ error: 'Method not allowed' }, 405);\n }\n\n const key = new URL(req.url).searchParams.get('key');\n if (!key) {\n return jsonResponse({ error: \"Query parameter 'key' is required\" }, 400);\n }\n\n const report = await options.provider({\n key,\n plane: 'browser',\n ...(options.permissions ? { permissions: options.permissions } : {}),\n appAuthConfigured: options.appAuthConfigured,\n db: options.db,\n });\n\n return jsonResponse(report, 200);\n}\n\n/**\n * Resolves a qualified model reference (`@happyvertical/smrt-commerce:Order`)\n * to the name the registry and the REST generator key configuration by.\n *\n * Returns `undefined` for anything this build does not register, so the caller\n * fails closed rather than reporting on a model that has no route.\n */\nexport function resolveRegisteredObjectName(model: string): string | undefined {\n const registered = model.includes(':')\n ? ObjectRegistry.getClassByQualifiedName(model)\n : ObjectRegistry.getClass(model);\n return registered?.name;\n}\n\n/**\n * The HTTP method a REST action is served by.\n *\n * CRUD actions map to their fixed verbs. A custom action is served under the\n * method its own route config declares (`api.routes[action].method`, defaulting\n * to `POST` exactly as `dispatchCustomCollectionAction` does), because guessing\n * `POST` for a declared `GET` action would make preflight report a false `deny`\n * on a `public: 'read'` model — hiding a playbook the caller can actually run,\n * which is the tool-listing case this exists to serve. Without an `objectName`\n * to read the declaration from, the fail-closed `POST` remains.\n */\nexport function restMethodForApiAction(\n action: string,\n objectName?: string,\n): string {\n switch (action) {\n case 'list':\n case 'get':\n return 'GET';\n case 'create':\n return 'POST';\n case 'update':\n return 'PUT';\n case 'delete':\n return 'DELETE';\n default:\n break;\n }\n\n if (!objectName) {\n return 'POST';\n }\n\n const apiConfig = ObjectRegistry.getConfig(objectName)?.api;\n if (!apiConfig || typeof apiConfig !== 'object' || !apiConfig.routes) {\n return 'POST';\n }\n\n const route = (apiConfig.routes as Record<string, { method?: string }>)[\n action\n ];\n return (route?.method ?? 'POST').toUpperCase();\n}\n\n/**\n * Whether `@smrt({ api })` exposes `action` on `objectName`.\n *\n * The generator's own action gate, lifted to a module function so preflight and\n * the live route read the same rule from one place. Accepts any action name so\n * a custom action is gated by the same `include`/`exclude` lists.\n *\n * @remarks An absent `objectName` returns `true` — fail-**open**, because the\n * live route reaches this only after it has already resolved a model, and an\n * unnamed object there means \"not gated by this rule\". A caller *predicting*\n * rather than serving has no such guarantee and must reject an unresolvable\n * model before asking (see `createRestPreflightLayerSource`).\n */\nexport function isApiActionEnabledForObject(\n objectName: string | undefined,\n action: string,\n): boolean {\n if (!objectName) {\n return true;\n }\n\n const apiConfig = ObjectRegistry.getConfig(objectName).api;\n\n if (apiConfig === false) {\n return false;\n }\n\n if (apiConfig && typeof apiConfig === 'object') {\n if (apiConfig.include && !apiConfig.include.includes(action)) {\n return false;\n }\n\n if (apiConfig.exclude?.includes(action)) {\n return false;\n }\n }\n\n return true;\n}\n\n/**\n * Whether `action` names a route the generated REST surface can actually\n * dispatch on `objectName`.\n *\n * CRUD actions always have a route. A custom action exists only when the\n * decorator declares it in `api.routes` — `dispatchCustomCollectionAction`\n * iterates exactly that map, so an action absent from it can never execute no\n * matter what `include`/`exclude` say. Exposure alone would report a typo'd or\n * removed custom action as `allow`, and an agent would then start the earlier\n * steps of a non-atomic playbook before dying on it — the failure preflight\n * exists to prevent.\n */\nexport function isRestActionRoutable(\n objectName: string | undefined,\n action: string,\n): boolean {\n if (!objectName) return false;\n if (\n action === 'list' ||\n action === 'get' ||\n action === 'create' ||\n action === 'update' ||\n action === 'delete'\n ) {\n return true;\n }\n\n const apiConfig = ObjectRegistry.getConfig(objectName)?.api;\n if (!apiConfig || typeof apiConfig !== 'object' || !apiConfig.routes) {\n return false;\n }\n\n return Object.hasOwn(apiConfig.routes as Record<string, unknown>, action);\n}\n\n/**\n * Fail-closed authorization posture (#1540): true only when the object opts out\n * of auth via `@smrt({ api: { public } })` — `true` for every method, `'read'`\n * for safe (GET) methods only.\n */\nexport function isRestRoutePublic(\n objectName: string | undefined,\n method: string,\n): boolean {\n if (!objectName) return false;\n const apiConfig = ObjectRegistry.getConfig(objectName)?.api;\n if (!apiConfig || typeof apiConfig !== 'object') return false;\n const publicAccess = (apiConfig as { public?: boolean | 'read' }).public;\n if (publicAccess === true) return true;\n if (publicAccess === 'read') return method.toUpperCase() === 'GET';\n return false;\n}\n\n/**\n * Field-level read-permission slugs declared by `objectName`.\n *\n * Reads the model's own and inherited fields only — deliberately **not** the\n * STI base-plus-descendants union used for cache policy. That union answers\n * \"does any variant carry a read-permission field?\"; used here it would report\n * a sibling variant's slugs as required for a step naming this concrete model.\n */\nexport function restFieldReadPermissions(\n objectName: string | undefined,\n): string[] {\n if (!objectName) return [];\n const registered = ObjectRegistry.getClass(objectName);\n const className = registered?.qualifiedName ?? registered?.name ?? objectName;\n const fields =\n registered?.inheritedFields ?? ObjectRegistry.getFields(className);\n const slugs = new Set<string>();\n for (const [, def] of fields) {\n const slug =\n typeof def?.readPermission === 'string'\n ? def.readPermission\n : typeof def?._meta?.readPermission === 'string'\n ? (def._meta.readPermission as string)\n : undefined;\n if (slug) slugs.add(slug);\n }\n return [...slugs].sort();\n}\n"],"mappings":"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"}
@@ -2,6 +2,7 @@ import { default as http } from 'node:http';
2
2
  import { SmrtCollection } from '../collection';
3
3
  import { SmrtObject } from '../object';
4
4
  import { RegisteredClass } from '../registry/types';
5
+ import { PlaybookPreflightProvider } from './preflight-route';
5
6
  export interface APIConfig {
6
7
  basePath?: string;
7
8
  enableCors?: boolean;
@@ -46,6 +47,17 @@ export interface APIConfig {
46
47
  * through this runtime server.
47
48
  */
48
49
  manifestHash?: string;
50
+ /**
51
+ * Browser-plane playbook preflight provider (#2590), wired from
52
+ * `@happyvertical/smrt-playbooks`. When omitted the `_preflight` route is not
53
+ * served at all.
54
+ *
55
+ * The provider receives only the requested key, the caller's published
56
+ * permission slugs, the database handle, and the boolean
57
+ * `appAuthConfigured`. It never receives {@link APIConfig.authMiddleware},
58
+ * which preflight must never invoke.
59
+ */
60
+ playbookPreflight?: PlaybookPreflightProvider;
49
61
  }
50
62
  export interface APIContext {
51
63
  db?: unknown;
@@ -1 +1 @@
1
- {"version":3,"file":"rest.d.ts","sourceRoot":"","sources":["../../src/generators/rest.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"rest.d.ts","sourceRoot":"","sources":["../../src/generators/rest.ts"],"names":[],"mappings":"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"}
@@ -7,6 +7,7 @@ import "../dispatch/index.js";
7
7
  import { canonicalReadRepresentation, computeTableVersionEtag, conditionalJsonResponse, ifNoneMatchHasConcreteMatch, ifNoneMatchSatisfied, resolveReadCacheControl, resolveTenantEtagDiscriminator, versionConditionalResponse, warnIfSharedCacheNeutralized, warnIfTenantScopedPublicRead } from "./conditional-get.js";
8
8
  import { handleChangesRoute } from "./changes-route.js";
9
9
  import { handleEventsRoute } from "./events-route.js";
10
+ import { handlePlaybookPreflightRoute, isApiActionEnabledForObject, isRestRoutePublic } from "./preflight-route.js";
10
11
  import { SYNC_APPLY_ROUTE_SEGMENTS, processSyncApplyBatch } from "../sync/apply.js";
11
12
  import { computeWebManifestHash } from "../vite-plugin/web-collections.js";
12
13
  import { normalizeTypedHttpError } from "./typed-http-error.js";
@@ -245,6 +246,15 @@ var APIGenerator = class {
245
246
  });
246
247
  return this.addCorsHeaders(response, req);
247
248
  }
249
+ if (url.pathname === `${this.config.basePath}/_preflight`) {
250
+ const response = await handlePlaybookPreflightRoute(req, {
251
+ provider: this.config.playbookPreflight,
252
+ ...this.context.permissions ? { permissions: this.context.permissions } : {},
253
+ appAuthConfigured: Boolean(this.config.authMiddleware),
254
+ db: this.resolveContextDb()
255
+ });
256
+ return this.addCorsHeaders(response, req);
257
+ }
248
258
  if (url.pathname.startsWith(this.config.basePath || "")) {
249
259
  const response = await this.handleObjectRoute(req, url);
250
260
  return this.addCorsHeaders(response, req);
@@ -333,23 +343,10 @@ var APIGenerator = class {
333
343
  * Everything else requires an `authMiddleware` to be configured.
334
344
  */
335
345
  isRoutePublic(objectName, method) {
336
- if (!objectName) return false;
337
- const apiConfig = ObjectRegistry.getConfig(objectName)?.api;
338
- if (!apiConfig || typeof apiConfig !== "object") return false;
339
- const publicAccess = apiConfig.public;
340
- if (publicAccess === true) return true;
341
- if (publicAccess === "read") return method.toUpperCase() === "GET";
342
- return false;
346
+ return isRestRoutePublic(objectName, method);
343
347
  }
344
348
  isApiActionEnabled(objectName, action) {
345
- if (!objectName) return true;
346
- const apiConfig = ObjectRegistry.getConfig(objectName).api;
347
- if (apiConfig === false) return false;
348
- if (apiConfig && typeof apiConfig === "object") {
349
- if (apiConfig.include && !apiConfig.include.includes(action)) return false;
350
- if (apiConfig.exclude?.includes(action)) return false;
351
- }
352
- return true;
349
+ return isApiActionEnabledForObject(objectName, action);
353
350
  }
354
351
  getCollectionObjectName(collection) {
355
352
  const itemClass = collection._itemClass || collection.constructor?._itemClass;