@happyvertical/smrt-core 0.51.10 → 0.51.12
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 +82 -0
- package/agents/generators.md +3 -1
- package/agents/schema-paths.md +19 -6
- package/dist/change-feed-sensitivity.d.ts +155 -0
- package/dist/change-feed-sensitivity.d.ts.map +1 -0
- package/dist/change-feed-sensitivity.js +139 -0
- package/dist/change-feed-sensitivity.js.map +1 -0
- package/dist/change-feed.d.ts +37 -10
- package/dist/change-feed.d.ts.map +1 -1
- package/dist/change-feed.js +128 -12
- package/dist/change-feed.js.map +1 -1
- package/dist/change-signals.d.ts.map +1 -1
- package/dist/change-signals.js +3 -0
- package/dist/change-signals.js.map +1 -1
- package/dist/class.d.ts +1 -1
- package/dist/class.d.ts.map +1 -1
- package/dist/class.js +4 -2
- package/dist/class.js.map +1 -1
- package/dist/decorators/index.d.ts +19 -0
- package/dist/decorators/index.d.ts.map +1 -1
- package/dist/decorators/index.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- 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/migrations/differ.d.ts +64 -5
- package/dist/migrations/differ.d.ts.map +1 -1
- package/dist/migrations/differ.js +270 -37
- package/dist/migrations/differ.js.map +1 -1
- package/dist/migrations/index.d.ts +1 -1
- package/dist/migrations/index.d.ts.map +1 -1
- package/dist/migrations/index.js +2 -2
- package/dist/migrations/sqlite-rebuild.d.ts +41 -0
- package/dist/migrations/sqlite-rebuild.d.ts.map +1 -1
- package/dist/migrations/sqlite-rebuild.js +68 -13
- package/dist/migrations/sqlite-rebuild.js.map +1 -1
- package/dist/migrations.js +2 -2
- package/dist/registry/class-registration.d.ts +1 -1
- package/dist/registry/class-registration.d.ts.map +1 -1
- package/dist/registry/class-registration.js +50 -4
- package/dist/registry/class-registration.js.map +1 -1
- package/dist/registry/db-cache-key.d.ts +47 -0
- package/dist/registry/db-cache-key.d.ts.map +1 -0
- package/dist/registry/db-cache-key.js +201 -0
- package/dist/registry/db-cache-key.js.map +1 -0
- package/dist/registry/schema-builder.d.ts.map +1 -1
- package/dist/registry/schema-builder.js +6 -1
- package/dist/registry/schema-builder.js.map +1 -1
- package/dist/registry/types.d.ts +43 -0
- package/dist/registry/types.d.ts.map +1 -1
- package/dist/registry.d.ts +17 -1
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +20 -8
- package/dist/registry.js.map +1 -1
- package/dist/scanner/types.d.ts +2 -0
- package/dist/scanner/types.d.ts.map +1 -1
- package/dist/scanner/types.js.map +1 -1
- package/dist/schema/backfill.d.ts +16 -0
- package/dist/schema/backfill.d.ts.map +1 -0
- package/dist/schema/backfill.js +26 -0
- package/dist/schema/backfill.js.map +1 -0
- package/dist/schema/generator.d.ts.map +1 -1
- package/dist/schema/generator.js +3 -0
- package/dist/schema/generator.js.map +1 -1
- package/dist/schema/types.d.ts +20 -1
- package/dist/schema/types.d.ts.map +1 -1
- package/dist/smrt-knowledge.json +9 -9
- package/dist/vite-plugin/dev-plane-route.js +1 -1
- package/dist/vite-plugin/dev-plane-route.js.map +1 -1
- package/dist/vite-plugin/sveltekit-generator.d.ts.map +1 -1
- package/dist/vite-plugin/sveltekit-generator.js +54 -7
- package/dist/vite-plugin/sveltekit-generator.js.map +1 -1
- package/package.json +10 -10
|
@@ -237,20 +237,33 @@ function collectReadPermissionFields(objectDef, manifest) {
|
|
|
237
237
|
return result;
|
|
238
238
|
}
|
|
239
239
|
/**
|
|
240
|
+
* The collection segment of generated operation permission slugs. Mirrors the
|
|
241
|
+
* smrt-users `PermissionCatalogService` derivation: an explicit
|
|
242
|
+
* `@smrt({ collection })` wins, then the manifest collection.
|
|
243
|
+
*/
|
|
244
|
+
function resolvePermissionCollection(objectDef) {
|
|
245
|
+
const configured = objectDef.decoratorConfig?.collection;
|
|
246
|
+
return typeof configured === "string" && configured.length > 0 ? configured : objectDef.collection;
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
240
249
|
* Emit the fail-closed authorization guard injected into generated route files
|
|
241
250
|
* (#1540, 2c). Every generated CRUD/action handler calls `requireRouteAuth`,
|
|
242
251
|
* which throws 401 unless the route is `public` or `locals` carries an
|
|
243
252
|
* authenticated principal. Mirrors the knowledge-route `isKnowledgeAdmin`
|
|
244
253
|
* precedent. Mutating verbs require auth even when reads are public.
|
|
245
254
|
*/
|
|
246
|
-
function generateAuthGuardHelper(objectDef, manifest) {
|
|
255
|
+
function generateAuthGuardHelper(objectDef, manifest, permissionObjectDef = objectDef) {
|
|
247
256
|
const publicAccess = getApiPublicAccess(objectDef.decoratorConfig?.api);
|
|
248
257
|
const readPermissionFields = collectReadPermissionFields(objectDef, manifest);
|
|
258
|
+
const permissionCollection = resolvePermissionCollection(permissionObjectDef);
|
|
249
259
|
return `
|
|
250
260
|
// Fail-closed authorization (#1540): generated routes require an authenticated
|
|
251
261
|
// principal on \`locals\` unless explicitly marked \`@smrt({ api: { public } })\`.
|
|
252
262
|
const PUBLIC_ACCESS: boolean | 'read' = ${JSON.stringify(publicAccess)};
|
|
253
263
|
const READ_PERMISSION_FIELDS: Array<[string, string]> = ${JSON.stringify(readPermissionFields)};
|
|
264
|
+
// Operation permissions (#2977): mutating handlers require
|
|
265
|
+
// \`<collection>.<action>\`, matching the smrt-users permission catalog slug.
|
|
266
|
+
const PERMISSION_COLLECTION = ${JSON.stringify(permissionCollection)};
|
|
254
267
|
|
|
255
268
|
interface PublicJsonOptions {
|
|
256
269
|
permissions?: Iterable<string>;
|
|
@@ -282,6 +295,34 @@ function requireRouteAuth(locals: unknown, mutating: boolean): void {
|
|
|
282
295
|
}
|
|
283
296
|
}
|
|
284
297
|
|
|
298
|
+
function hasGrantedPermission(value: unknown, permission: string): boolean {
|
|
299
|
+
if (Array.isArray(value)) return value.includes(permission);
|
|
300
|
+
if (value instanceof Set) return value.has(permission);
|
|
301
|
+
return false;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Fail-closed write authorization (#2977): a mutating handler requires the
|
|
305
|
+
// \`<collection>.<action>\` operation permission in the principal's session
|
|
306
|
+
// permission snapshot (\`locals.permissions\` from smrt-users, or the
|
|
307
|
+
// smrt-tenancy \`locals.tenantContext\`). A missing snapshot denies. Only an
|
|
308
|
+
// explicit tenancy super-admin bypass skips the check; \`public: true\` routes
|
|
309
|
+
// keep their unauthenticated semantics.
|
|
310
|
+
function requireRoutePermission(locals: unknown, action: string): void {
|
|
311
|
+
if (PUBLIC_ACCESS === true) return;
|
|
312
|
+
const l = readJsonRecord(locals);
|
|
313
|
+
const tenantContext = readJsonRecord(l.tenantContext);
|
|
314
|
+
if (tenantContext.superAdminBypass === true) return;
|
|
315
|
+
const permission = \`\${PERMISSION_COLLECTION}.\${action}\`;
|
|
316
|
+
const granted =
|
|
317
|
+
hasGrantedPermission(l.permissions, permission) ||
|
|
318
|
+
hasGrantedPermission(l.permissionSet, permission) ||
|
|
319
|
+
hasGrantedPermission(l.smrtPermissions, permission) ||
|
|
320
|
+
hasGrantedPermission(tenantContext.permissions, permission);
|
|
321
|
+
if (!granted) {
|
|
322
|
+
throw error(403, 'Permission denied');
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
285
326
|
function getPublicJsonOptions(locals: unknown): PublicJsonOptions {
|
|
286
327
|
const l = readJsonRecord(locals);
|
|
287
328
|
const permissions = l.permissions ?? l.permissionSet ?? l.smrtPermissions;
|
|
@@ -560,8 +601,12 @@ function tenantReadOptionsScope(): { tenantId: string | null } | undefined {
|
|
|
560
601
|
* The per-handler guard preamble: the fail-closed auth check (#1540 2c) plus, for
|
|
561
602
|
* tenant-scoped objects, tenant-context establishment (#1540 facet 2).
|
|
562
603
|
*/
|
|
563
|
-
function routeGuardPreamble(objectDef, mutating) {
|
|
604
|
+
function routeGuardPreamble(objectDef, mutating, permissionAction) {
|
|
564
605
|
const lines = [` requireRouteAuth(locals, ${mutating});`];
|
|
606
|
+
if (mutating) {
|
|
607
|
+
if (!permissionAction) throw new Error("Mutating generated routes require a permission action");
|
|
608
|
+
lines.push(` requireRoutePermission(locals, ${JSON.stringify(permissionAction)});`);
|
|
609
|
+
}
|
|
565
610
|
if (needsRouteTenantContext(objectDef)) lines.push(" establishTenantContext(locals);");
|
|
566
611
|
return lines.join("\n");
|
|
567
612
|
}
|
|
@@ -1713,7 +1758,7 @@ ${listAndCount}
|
|
|
1713
1758
|
const postHandler = hasPost ? `
|
|
1714
1759
|
// Create new ${className.toLowerCase()}
|
|
1715
1760
|
export const POST: RequestHandler = async ({ locals, request }) => {
|
|
1716
|
-
${routeGuardPreamble(objectDef, true)}
|
|
1761
|
+
${routeGuardPreamble(objectDef, true, "create")}
|
|
1717
1762
|
const publicJsonOptions = getPublicJsonOptions(locals);
|
|
1718
1763
|
const body: unknown = await request.json();
|
|
1719
1764
|
const data = applyWritablePolicy(body);
|
|
@@ -1835,7 +1880,7 @@ ${generateNotFoundError(className)}
|
|
|
1835
1880
|
const putHandler = hasPut ? `
|
|
1836
1881
|
// Update ${simpleClassName.toLowerCase()}
|
|
1837
1882
|
export const PUT: RequestHandler = async ({ locals, params, request }) => {
|
|
1838
|
-
${routeGuardPreamble(objectDef, true)}
|
|
1883
|
+
${routeGuardPreamble(objectDef, true, "update")}
|
|
1839
1884
|
const publicJsonOptions = getPublicJsonOptions(locals);
|
|
1840
1885
|
${generateCollectionLoad(className, { typeName: modelType.typeName })}
|
|
1841
1886
|
const item = await collection.get(params.id);
|
|
@@ -1859,7 +1904,7 @@ ${serializers.itemSerializerName ? `
|
|
|
1859
1904
|
const deleteHandler = hasDelete ? `
|
|
1860
1905
|
// Delete ${simpleClassName.toLowerCase()}
|
|
1861
1906
|
export const DELETE: RequestHandler = async ({ locals, params }) => {
|
|
1862
|
-
${routeGuardPreamble(objectDef, true)}
|
|
1907
|
+
${routeGuardPreamble(objectDef, true, "delete")}
|
|
1863
1908
|
${generateCollectionLoad(className, { typeName: modelType.typeName })}
|
|
1864
1909
|
const item = await collection.get(params.id);
|
|
1865
1910
|
${generateNotFoundError(className)}
|
|
@@ -1914,13 +1959,15 @@ function generateActionRouteTemplate(projectRoot, routeDir, routeSpecs, objectDe
|
|
|
1914
1959
|
// DO NOT EDIT - changes will be overwritten
|
|
1915
1960
|
|
|
1916
1961
|
${importBlock}
|
|
1917
|
-
${generateAuthGuardHelper(objectDef, semanticManifest)}${needsTenantContext ? generateTenantContextHelper(principalContext, tenantScoped) : ""}
|
|
1962
|
+
${generateAuthGuardHelper(objectDef, semanticManifest, hostType === "collection" && firstSpec.lookupObjectDef && isCollectionManifestClass(semanticManifest, objectDef) ? firstSpec.lookupObjectDef : objectDef)}${needsTenantContext ? generateTenantContextHelper(principalContext, tenantScoped) : ""}
|
|
1918
1963
|
${generateTypedRouteErrorHelper()}
|
|
1919
1964
|
${handlers}`;
|
|
1920
1965
|
}
|
|
1921
1966
|
function generateActionRouteHandler(lookupClassName, actionName, actionDef, routeConfig, hostType, tenantScoped, needsTenantContext, lookupTypeName, hostTypeName) {
|
|
1922
1967
|
const handlerName = routeConfig.method;
|
|
1923
|
-
const
|
|
1968
|
+
const mutating = routeConfig.method !== "GET";
|
|
1969
|
+
const guardLines = [` requireRouteAuth(locals, ${mutating});`];
|
|
1970
|
+
if (mutating) guardLines.push(` requireRoutePermission(locals, ${JSON.stringify(actionName)});`);
|
|
1924
1971
|
if (needsTenantContext) guardLines.push(" establishTenantContext(locals);");
|
|
1925
1972
|
const authGuardLine = guardLines.join("\n");
|
|
1926
1973
|
const needsRequest = actionDef.parameters.length > 0;
|