@happyvertical/smrt-core 0.40.69 → 0.41.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.
- package/AGENTS.md +29 -4
- package/README.md +20 -1
- package/agents/change-feed.md +1 -1
- package/agents/query-bounds.md +45 -0
- package/agents/schema-paths.md +786 -0
- package/dist/browser.d.ts +1 -0
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +5 -3
- package/dist/cascade.d.ts +120 -0
- package/dist/cascade.d.ts.map +1 -0
- package/dist/cascade.js +430 -0
- package/dist/cascade.js.map +1 -0
- package/dist/change-feed.d.ts +34 -2
- package/dist/change-feed.d.ts.map +1 -1
- package/dist/change-feed.js +52 -11
- package/dist/change-feed.js.map +1 -1
- package/dist/class.d.ts +36 -3
- package/dist/class.d.ts.map +1 -1
- package/dist/class.js +87 -9
- package/dist/class.js.map +1 -1
- package/dist/collection-cache.js +0 -0
- package/dist/collection-cache.js.map +1 -1
- package/dist/collection.d.ts +130 -2
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +290 -57
- package/dist/collection.js.map +1 -1
- package/dist/config.d.ts +10 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js.map +1 -1
- package/dist/database.d.ts +8 -0
- package/dist/database.d.ts.map +1 -1
- package/dist/database.js +16 -8
- package/dist/database.js.map +1 -1
- package/dist/db-errors.d.ts +105 -0
- package/dist/db-errors.d.ts.map +1 -0
- package/dist/db-errors.js +382 -0
- package/dist/db-errors.js.map +1 -0
- package/dist/decorators/index.d.ts +80 -6
- package/dist/decorators/index.d.ts.map +1 -1
- package/dist/decorators/index.js +102 -12
- package/dist/decorators/index.js.map +1 -1
- package/dist/dispatch/bus.d.ts.map +1 -1
- package/dist/dispatch/bus.js +4 -3
- package/dist/dispatch/bus.js.map +1 -1
- package/dist/dispatch/collections/Dispatches.d.ts.map +1 -1
- package/dist/dispatch/collections/Dispatches.js +19 -4
- package/dist/dispatch/collections/Dispatches.js.map +1 -1
- package/dist/dispatch/types.d.ts +5 -0
- package/dist/dispatch/types.d.ts.map +1 -1
- package/dist/embedded-write-queue.d.ts +46 -0
- package/dist/embedded-write-queue.d.ts.map +1 -0
- package/dist/embedded-write-queue.js +66 -0
- package/dist/embedded-write-queue.js.map +1 -0
- package/dist/embeddings/storage.d.ts +7 -0
- package/dist/embeddings/storage.d.ts.map +1 -1
- package/dist/embeddings/storage.js +29 -12
- package/dist/embeddings/storage.js.map +1 -1
- package/dist/errors.d.ts +31 -3
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +34 -2
- package/dist/errors.js.map +1 -1
- package/dist/generators/changes-route.d.ts.map +1 -1
- package/dist/generators/changes-route.js +6 -3
- package/dist/generators/changes-route.js.map +1 -1
- package/dist/generators/mcp-runtime-template.d.ts +8 -0
- package/dist/generators/mcp-runtime-template.d.ts.map +1 -1
- package/dist/generators/mcp-runtime-template.js +38 -4
- package/dist/generators/mcp-runtime-template.js.map +1 -1
- package/dist/generators/mcp.d.ts +16 -0
- package/dist/generators/mcp.d.ts.map +1 -1
- package/dist/generators/mcp.js +41 -3
- package/dist/generators/mcp.js.map +1 -1
- package/dist/generators/rest.d.ts +22 -0
- package/dist/generators/rest.d.ts.map +1 -1
- package/dist/generators/rest.js +34 -3
- package/dist/generators/rest.js.map +1 -1
- package/dist/hierarchical.js +1 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -5
- package/dist/interceptors.d.ts +21 -0
- package/dist/interceptors.d.ts.map +1 -1
- package/dist/interceptors.js +27 -1
- package/dist/interceptors.js.map +1 -1
- package/dist/manifest/generator.d.ts.map +1 -1
- package/dist/manifest/generator.js +4 -7
- package/dist/manifest/generator.js.map +1 -1
- package/dist/manifest/static-manifest.js +10 -10
- package/dist/manifest/static-manifest.js.map +1 -1
- package/dist/manifest/store.js +1 -1
- package/dist/manifest/store.js.map +1 -1
- package/dist/manifest.json +19 -19
- package/dist/migrations/differ.d.ts +211 -9
- package/dist/migrations/differ.d.ts.map +1 -1
- package/dist/migrations/differ.js +613 -50
- package/dist/migrations/differ.js.map +1 -1
- package/dist/migrations/generator.d.ts +31 -4
- package/dist/migrations/generator.d.ts.map +1 -1
- package/dist/migrations/generator.js +49 -5
- package/dist/migrations/generator.js.map +1 -1
- package/dist/migrations/index.d.ts +4 -2
- package/dist/migrations/index.d.ts.map +1 -1
- package/dist/migrations/index.js +6 -3
- package/dist/migrations/minor-units.d.ts +162 -0
- package/dist/migrations/minor-units.d.ts.map +1 -0
- package/dist/migrations/minor-units.js +381 -0
- package/dist/migrations/minor-units.js.map +1 -0
- package/dist/migrations/orchestrate.js +35 -6
- package/dist/migrations/orchestrate.js.map +1 -1
- package/dist/migrations/sqlite-rebuild.d.ts +142 -0
- package/dist/migrations/sqlite-rebuild.d.ts.map +1 -0
- package/dist/migrations/sqlite-rebuild.js +514 -0
- package/dist/migrations/sqlite-rebuild.js.map +1 -0
- package/dist/migrations/tracker.d.ts +114 -1
- package/dist/migrations/tracker.d.ts.map +1 -1
- package/dist/migrations/tracker.js +331 -16
- package/dist/migrations/tracker.js.map +1 -1
- package/dist/migrations/types.d.ts +19 -4
- package/dist/migrations/types.d.ts.map +1 -1
- package/dist/migrations.js +6 -3
- package/dist/object.d.ts +142 -10
- package/dist/object.d.ts.map +1 -1
- package/dist/object.js +196 -41
- package/dist/object.js.map +1 -1
- package/dist/postgres-timeouts.d.ts +240 -0
- package/dist/postgres-timeouts.d.ts.map +1 -0
- package/dist/postgres-timeouts.js +204 -0
- package/dist/postgres-timeouts.js.map +1 -0
- package/dist/query-bounds.d.ts +101 -0
- package/dist/query-bounds.d.ts.map +1 -0
- package/dist/query-bounds.js +177 -0
- package/dist/query-bounds.js.map +1 -0
- package/dist/registry/class-registration.d.ts.map +1 -1
- package/dist/registry/class-registration.js +3 -1
- package/dist/registry/class-registration.js.map +1 -1
- package/dist/registry/manifest-field-merge.d.ts +12 -0
- package/dist/registry/manifest-field-merge.d.ts.map +1 -1
- package/dist/registry/manifest-field-merge.js +14 -2
- package/dist/registry/manifest-field-merge.js.map +1 -1
- package/dist/registry/schema-builder.d.ts +22 -1
- package/dist/registry/schema-builder.d.ts.map +1 -1
- package/dist/registry/schema-builder.js +205 -165
- package/dist/registry/schema-builder.js.map +1 -1
- package/dist/registry/types.d.ts +35 -3
- package/dist/registry/types.d.ts.map +1 -1
- package/dist/registry.d.ts +41 -46
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +61 -83
- package/dist/registry.js.map +1 -1
- package/dist/scanner/manifest-generator.d.ts +45 -0
- package/dist/scanner/manifest-generator.d.ts.map +1 -1
- package/dist/scanner/manifest-generator.js +92 -28
- package/dist/scanner/manifest-generator.js.map +1 -1
- package/dist/scanner/types.d.ts +5 -0
- package/dist/scanner/types.d.ts.map +1 -1
- package/dist/scanner/types.js.map +1 -1
- package/dist/schema/conflict-target.d.ts +104 -0
- package/dist/schema/conflict-target.d.ts.map +1 -0
- package/dist/schema/conflict-target.js +129 -0
- package/dist/schema/conflict-target.js.map +1 -0
- package/dist/schema/ddl/base-strategy.d.ts.map +1 -1
- package/dist/schema/ddl/base-strategy.js +2 -2
- package/dist/schema/ddl/base-strategy.js.map +1 -1
- package/dist/schema/ddl/duckdb-strategy.d.ts.map +1 -1
- package/dist/schema/ddl/duckdb-strategy.js +2 -1
- package/dist/schema/ddl/duckdb-strategy.js.map +1 -1
- package/dist/schema/ddl/postgres-strategy.d.ts.map +1 -1
- package/dist/schema/ddl/postgres-strategy.js +12 -1
- package/dist/schema/ddl/postgres-strategy.js.map +1 -1
- package/dist/schema/generator.d.ts +307 -6
- package/dist/schema/generator.d.ts.map +1 -1
- package/dist/schema/generator.js +510 -87
- package/dist/schema/generator.js.map +1 -1
- package/dist/schema/index-utils.d.ts +120 -0
- package/dist/schema/index-utils.d.ts.map +1 -1
- package/dist/schema/index-utils.js +242 -1
- package/dist/schema/index-utils.js.map +1 -1
- package/dist/schema/index.d.ts +3 -0
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/index.js +4 -1
- package/dist/schema/live-parity.d.ts +90 -0
- package/dist/schema/live-parity.d.ts.map +1 -0
- package/dist/schema/live-parity.js +602 -0
- package/dist/schema/live-parity.js.map +1 -0
- package/dist/schema/manifest-schema.d.ts +121 -0
- package/dist/schema/manifest-schema.d.ts.map +1 -0
- package/dist/schema/manifest-schema.js +267 -0
- package/dist/schema/manifest-schema.js.map +1 -0
- package/dist/schema/schema-aggregator.d.ts +24 -10
- package/dist/schema/schema-aggregator.d.ts.map +1 -1
- package/dist/schema/schema-aggregator.js +35 -90
- package/dist/schema/schema-aggregator.js.map +1 -1
- package/dist/schema/system-table-shapes.d.ts +65 -0
- package/dist/schema/system-table-shapes.d.ts.map +1 -0
- package/dist/schema/system-table-shapes.js +187 -0
- package/dist/schema/system-table-shapes.js.map +1 -0
- package/dist/schema/types.d.ts +103 -4
- package/dist/schema/types.d.ts.map +1 -1
- package/dist/schema/utils.d.ts +2 -1
- package/dist/schema/utils.d.ts.map +1 -1
- package/dist/schema/utils.js +5 -3
- package/dist/schema/utils.js.map +1 -1
- package/dist/schema.js +4 -1
- package/dist/smrt-knowledge.json +20 -8
- package/dist/sync/apply.d.ts.map +1 -1
- package/dist/sync/apply.js +9 -16
- package/dist/sync/apply.js.map +1 -1
- package/dist/system/compatibility.d.ts +42 -0
- package/dist/system/compatibility.d.ts.map +1 -1
- package/dist/system/compatibility.js +182 -9
- package/dist/system/compatibility.js.map +1 -1
- package/dist/system/index.d.ts +1 -0
- package/dist/system/index.d.ts.map +1 -1
- package/dist/system/index.js +3 -2
- package/dist/system/retention.d.ts +237 -0
- package/dist/system/retention.d.ts.map +1 -0
- package/dist/system/retention.js +497 -0
- package/dist/system/retention.js.map +1 -0
- package/dist/system/schema.d.ts +100 -15
- package/dist/system/schema.d.ts.map +1 -1
- package/dist/system/schema.js +81 -45
- package/dist/system/schema.js.map +1 -1
- package/dist/system/types.d.ts +0 -2
- package/dist/system/types.d.ts.map +1 -1
- package/dist/testing/database.d.ts.map +1 -1
- package/dist/testing/database.js +1 -0
- package/dist/testing/database.js.map +1 -1
- package/dist/vite-plugin/index.d.ts.map +1 -1
- package/dist/vite-plugin/index.js +4 -9
- package/dist/vite-plugin/index.js.map +1 -1
- package/dist/vite-plugin/sveltekit-generator.d.ts.map +1 -1
- package/dist/vite-plugin/sveltekit-generator.js +71 -5
- package/dist/vite-plugin/sveltekit-generator.js.map +1 -1
- package/dist/vite-plugin/web-collections.d.ts.map +1 -1
- package/dist/vite-plugin/web-collections.js +6 -4
- package/dist/vite-plugin/web-collections.js.map +1 -1
- package/package.json +5 -5
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { MAX_LIST_LIMIT, buildDefaultListOrderBy } from "../query-bounds.js";
|
|
1
2
|
import { resolveCustomActionMetadata } from "../generators/custom-action.js";
|
|
2
3
|
import { generateConditionalGetRouteHelper } from "../generators/conditional-get.js";
|
|
3
4
|
import { AUTO_GENERATED_ROUTE_HEADER } from "./route-header.js";
|
|
@@ -410,6 +411,72 @@ function needsRouteTenantContext(objectDef) {
|
|
|
410
411
|
return isTenantScoped(objectDef) || usesPrincipalContext(objectDef);
|
|
411
412
|
}
|
|
412
413
|
/**
|
|
414
|
+
* Emit the page-bounds guard and the deterministic ordering constant for a
|
|
415
|
+
* generated list route (#2367).
|
|
416
|
+
*
|
|
417
|
+
* The route used to parse its own bounds with
|
|
418
|
+
* `Number(url.searchParams.get('limit')) || 50`, which has three faults: it
|
|
419
|
+
* accepts `NaN` from any non-numeric input and binds it (PostgreSQL answers
|
|
420
|
+
* `invalid input syntax for type bigint` — a 500 for a caller typo), it folds a
|
|
421
|
+
* deliberate `limit=0` into 50, and it has no ceiling, so `?limit=100000000` is
|
|
422
|
+
* a full table scan plus a full hydration pass on a public endpoint.
|
|
423
|
+
*
|
|
424
|
+
* The helper is emitted into the route rather than imported from
|
|
425
|
+
* `@happyvertical/smrt-core` so the generated file stays readable on its own and
|
|
426
|
+
* the bound is visible at the call site; the constants come from core so there
|
|
427
|
+
* is still one source of truth.
|
|
428
|
+
*/
|
|
429
|
+
function generateListBoundsHelper(objectDef) {
|
|
430
|
+
const orderBy = resolveDefaultListOrderBy(objectDef);
|
|
431
|
+
return `
|
|
432
|
+
/** Deterministic list ordering (#2367); matching index is #2363. */
|
|
433
|
+
const LIST_ORDER_BY = ${JSON.stringify(orderBy)};
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* Parse \`?limit\`/\`?offset\` (#2367): default when absent, 400 when malformed,
|
|
437
|
+
* clamped to ${MAX_LIST_LIMIT} when oversized.
|
|
438
|
+
*/
|
|
439
|
+
function listBounds(url: URL): { limit: number; offset: number } {
|
|
440
|
+
const parse = (name: 'limit' | 'offset', fallback: number, max?: number): number => {
|
|
441
|
+
const raw = url.searchParams.get(name);
|
|
442
|
+
if (raw === null || raw === '') {
|
|
443
|
+
return max === undefined ? fallback : Math.min(fallback, max);
|
|
444
|
+
}
|
|
445
|
+
if (!/^\\d+$/.test(raw.trim())) {
|
|
446
|
+
throw error(400, \`Invalid \${name}: '\${raw}' is not a non-negative integer.\`);
|
|
447
|
+
}
|
|
448
|
+
const value = Number(raw.trim());
|
|
449
|
+
if (!Number.isSafeInteger(value)) {
|
|
450
|
+
throw error(400, \`Invalid \${name}: '\${raw}' is not a non-negative integer.\`);
|
|
451
|
+
}
|
|
452
|
+
return max === undefined ? value : Math.min(value, max);
|
|
453
|
+
};
|
|
454
|
+
|
|
455
|
+
return {
|
|
456
|
+
limit: parse('limit', 50, ${MAX_LIST_LIMIT}),
|
|
457
|
+
offset: parse('offset', 0),
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
`;
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* Deterministic default ordering for a generated list route (#2367).
|
|
464
|
+
*
|
|
465
|
+
* The list route pages with `LIMIT`/`OFFSET` and previously emitted no
|
|
466
|
+
* `ORDER BY` at all. That is not pagination: without an ordering PostgreSQL may
|
|
467
|
+
* return rows in any order and is free to pick a different one per query, so
|
|
468
|
+
* page 2 can repeat rows from page 1 and skip others entirely — silently, and
|
|
469
|
+
* only under concurrency or a plan change, which is why it survived review.
|
|
470
|
+
*
|
|
471
|
+
* `created_at DESC` alone is not enough (rows created in the same tick tie), so
|
|
472
|
+
* the primary key follows as a total-order tiebreak. The matching index is
|
|
473
|
+
* #2363.
|
|
474
|
+
*/
|
|
475
|
+
function resolveDefaultListOrderBy(objectDef) {
|
|
476
|
+
const customPrimaryKey = Object.entries(objectDef.fields ?? {}).find(([, fieldDef]) => fieldDef?._meta?.primaryKey === true || fieldDef?.primaryKey === true)?.[0];
|
|
477
|
+
return buildDefaultListOrderBy(customPrimaryKey);
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
413
480
|
* Emit the tenant-context establishment + fail-closed read-scope helpers for
|
|
414
481
|
* `@TenantScoped` objects (#1540 facet 2; #1782). Generated handlers call
|
|
415
482
|
* `establishTenantContext(locals)` after the auth guard so every query runs
|
|
@@ -1315,7 +1382,7 @@ ${serializerImports ? `${serializerImports}\n` : ""}import { getCollection } fro
|
|
|
1315
1382
|
${hasPost ? "import { normalizeTypedHttpError } from '@happyvertical/smrt-core';\n" : ""}
|
|
1316
1383
|
${modelType.importStatement ? `${modelType.importStatement}\n` : ""}import type { RequestHandler } from './$types';
|
|
1317
1384
|
// Note: ${className} is auto-registered by the Vite plugin scanner
|
|
1318
|
-
${generateAuthGuardHelper(objectDef, manifest)}${needsRouteTenantContext(objectDef) ? generateTenantContextHelper(usesPrincipalContext(objectDef), isTenantScoped(objectDef)) : ""}${hasPost ? generateWritablePolicyHelper(objectDef) : ""}${hasPost ? generateTypedRouteErrorHelper() : ""}${hasGet ? generateConditionalGetRouteHelper(objectDef.decoratorConfig?.api, {
|
|
1385
|
+
${generateAuthGuardHelper(objectDef, manifest)}${needsRouteTenantContext(objectDef) ? generateTenantContextHelper(usesPrincipalContext(objectDef), isTenantScoped(objectDef)) : ""}${hasPost ? generateWritablePolicyHelper(objectDef) : ""}${hasPost ? generateTypedRouteErrorHelper() : ""}${hasGet ? generateListBoundsHelper(objectDef) : ""}${hasGet ? generateConditionalGetRouteHelper(objectDef.decoratorConfig?.api, {
|
|
1319
1386
|
tenantScoped: isTenantScoped(objectDef),
|
|
1320
1387
|
permissionScoped: listUsesPermissionScopedBody,
|
|
1321
1388
|
modelName: className,
|
|
@@ -1323,16 +1390,15 @@ ${generateAuthGuardHelper(objectDef, manifest)}${needsRouteTenantContext(objectD
|
|
|
1323
1390
|
manifestHash: webManifestHash
|
|
1324
1391
|
}) : ""}`;
|
|
1325
1392
|
const listAndCount = isTenantScoped(objectDef) ? ` const readScope = tenantReadScope();
|
|
1326
|
-
const items = await collection.list({ limit, offset, where: readScope });
|
|
1327
|
-
const count = await collection.count({ where: readScope });` : ` const items = await collection.list({ limit, offset });
|
|
1393
|
+
const items = await collection.list({ limit, offset, orderBy: LIST_ORDER_BY, where: readScope });
|
|
1394
|
+
const count = await collection.count({ where: readScope });` : ` const items = await collection.list({ limit, offset, orderBy: LIST_ORDER_BY });
|
|
1328
1395
|
const count = await collection.count();`;
|
|
1329
1396
|
const getHandler = hasGet ? `
|
|
1330
1397
|
// List all ${className.toLowerCase()}s
|
|
1331
1398
|
export const GET: RequestHandler = async ({ locals, url, request }) => {
|
|
1332
1399
|
${routeGuardPreamble(objectDef, false)}
|
|
1333
1400
|
const publicJsonOptions = getPublicJsonOptions(locals);
|
|
1334
|
-
const limit =
|
|
1335
|
-
const offset = Number(url.searchParams.get('offset')) || 0;
|
|
1401
|
+
const { limit, offset } = listBounds(url);
|
|
1336
1402
|
|
|
1337
1403
|
${generateCollectionLoad(className, { typeName: modelType.typeName })}
|
|
1338
1404
|
${listUsesSerializer ? `${listAndCount}
|