fastify-auto-sqlapi 0.1.10 → 0.1.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.md +1 -1
- package/AGENTS_BACKEND.md +76 -2
- package/AGENTS_FRONTEND.md +69 -545
- package/BREAKING_CHANGES.md +63 -0
- package/CHANGELOG.md +96 -0
- package/README.md +98 -11
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -1
- package/dist/lib/agent/manifest.d.ts +53 -0
- package/dist/lib/agent/manifest.d.ts.map +1 -0
- package/dist/lib/agent/manifest.js +153 -0
- package/dist/lib/agent/tool-schemas.d.ts +36 -0
- package/dist/lib/agent/tool-schemas.d.ts.map +1 -0
- package/dist/lib/agent/tool-schemas.js +47 -0
- package/dist/lib/cli/schema-codegen.d.ts.map +1 -1
- package/dist/lib/cli/schema-codegen.js +9 -1
- package/dist/lib/engine/bulk/bulk-delete.d.ts.map +1 -1
- package/dist/lib/engine/bulk/bulk-delete.js +5 -1
- package/dist/lib/engine/rest/delete.d.ts.map +1 -1
- package/dist/lib/engine/rest/delete.js +4 -1
- package/dist/lib/engine/rest/get.d.ts.map +1 -1
- package/dist/lib/engine/rest/get.js +4 -1
- package/dist/lib/engine/search/search.d.ts.map +1 -1
- package/dist/lib/engine/search/search.js +8 -2
- package/dist/lib/nullable.d.ts +17 -0
- package/dist/lib/nullable.d.ts.map +1 -0
- package/dist/lib/nullable.js +24 -0
- package/dist/lib/schema/search.d.ts +11 -9
- package/dist/lib/schema/search.d.ts.map +1 -1
- package/dist/lib/schema/search.js +27 -10
- package/dist/lib/table-helpers.d.ts.map +1 -1
- package/dist/lib/table-helpers.js +12 -2
- package/dist/routes/auto/agent-manifest.routes.d.ts +12 -0
- package/dist/routes/auto/agent-manifest.routes.d.ts.map +1 -0
- package/dist/routes/auto/agent-manifest.routes.js +30 -0
- package/dist/routes/auto/bulk-delete.routes.d.ts.map +1 -1
- package/dist/routes/auto/bulk-delete.routes.js +1 -0
- package/dist/routes/auto/delete.routes.d.ts.map +1 -1
- package/dist/routes/auto/delete.routes.js +1 -0
- package/dist/routes/auto/get.routes.d.ts.map +1 -1
- package/dist/routes/auto/get.routes.js +1 -0
- package/dist/routes/auto/plugin.d.ts.map +1 -1
- package/dist/routes/auto/plugin.js +4 -0
- package/dist/routes/auto/route-helpers.d.ts +7 -0
- package/dist/routes/auto/route-helpers.d.ts.map +1 -1
- package/dist/routes/auto/route-helpers.js +11 -0
- package/dist/routes/auto/search.routes.js +3 -3
- package/dist/types/plugin.d.ts +7 -0
- package/dist/types/plugin.d.ts.map +1 -1
- package/dist/types/table.d.ts +6 -0
- package/dist/types/table.d.ts.map +1 -1
- package/dist/types/table.js +8 -0
- package/docs/adr/0007-explicit-table-generation.md +39 -0
- package/docs/adr/0008-agent-client-interface.md +66 -0
- package/docs/adr/README.md +2 -0
- package/package.json +1 -1
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { SearchTableBodyPost, SearchTableQuery } from '../schema/search.js';
|
|
2
|
+
import { InsertTableBody } from '../schema/insert.js';
|
|
3
|
+
import { UpdateTableBody } from '../schema/update.js';
|
|
4
|
+
import { BulkUpsertTableBody } from '../schema/bulk-upsert.js';
|
|
5
|
+
import { BulkDeleteTableBody } from '../schema/bulk-delete.js';
|
|
6
|
+
import { tableOperations } from './manifest.js';
|
|
7
|
+
import { DEFAULT_MAX_ITEMS_PER_PAGE, DEFAULT_MAX_BULK_ITEMS } from '../../types.js';
|
|
8
|
+
/**
|
|
9
|
+
* Ready-to-use JSON Schemas for exposing a table's operations as strict LLM tools —
|
|
10
|
+
* the same TypeBox schemas the HTTP routes validate with (TypeBox objects ARE plain
|
|
11
|
+
* JSON Schema). Only the operations actually enabled for the table are included
|
|
12
|
+
* (`operations` whitelist, composite-PK rules). `get` and `delete` take a single PK
|
|
13
|
+
* path param and need no schema.
|
|
14
|
+
*
|
|
15
|
+
* Strict tools trade prompt size for provider-side validation; the lighter
|
|
16
|
+
* alternative is a loose tool plus the plugin's structured 400 as the retry signal
|
|
17
|
+
* (see "LLM / agent clients" in the README).
|
|
18
|
+
*/
|
|
19
|
+
export function agentToolSchemas(dbTables, tableName, opts) {
|
|
20
|
+
const tableConf = dbTables[tableName];
|
|
21
|
+
if (!tableConf) {
|
|
22
|
+
throw new Error(`Unknown table "${tableName}". Available: ${Object.keys(dbTables).join(', ')}`);
|
|
23
|
+
}
|
|
24
|
+
const ops = new Set(tableOperations(tableConf));
|
|
25
|
+
const out = {};
|
|
26
|
+
if (ops.has('search')) {
|
|
27
|
+
out.search = {
|
|
28
|
+
body: SearchTableBodyPost(dbTables, tableName),
|
|
29
|
+
querystring: SearchTableQuery(opts?.maxItemsPerPage ?? DEFAULT_MAX_ITEMS_PER_PAGE),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
if (ops.has('insert'))
|
|
33
|
+
out.insert = { body: InsertTableBody(dbTables, tableName) };
|
|
34
|
+
if (ops.has('update'))
|
|
35
|
+
out.update = { body: UpdateTableBody(dbTables, tableName) };
|
|
36
|
+
if (ops.has('bulkUpsert')) {
|
|
37
|
+
out.bulkUpsert = {
|
|
38
|
+
body: BulkUpsertTableBody(dbTables, tableName, opts?.maxBulkItems ?? DEFAULT_MAX_BULK_ITEMS),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
if (ops.has('bulkDelete')) {
|
|
42
|
+
out.bulkDelete = {
|
|
43
|
+
body: BulkDeleteTableBody(dbTables, tableName, opts?.maxBulkItems ?? DEFAULT_MAX_BULK_ITEMS),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
return out;
|
|
47
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema-codegen.d.ts","sourceRoot":"","sources":["../../../src/lib/cli/schema-codegen.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE3D,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,UAAU,EAAE;IAAE,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,iBAAiB,CAAC,EAAE,OAAO,CAAA;CAAE,GAC9F,MAAM,
|
|
1
|
+
{"version":3,"file":"schema-codegen.d.ts","sourceRoot":"","sources":["../../../src/lib/cli/schema-codegen.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE3D,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,UAAU,EAAE;IAAE,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,iBAAiB,CAAC,EAAE,OAAO,CAAA;CAAE,GAC9F,MAAM,CA0DR;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,QAAQ,CAqC1D;AAED,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC9B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC9B,OAAO,CAAC,EAAE,MAAM,EAAE,GACjB,MAAM,CA8CR"}
|
|
@@ -4,6 +4,12 @@ export function convertColType(udtName, columnInfo) {
|
|
|
4
4
|
const hasDefault = columnInfo.column_default || columnInfo.is_auto_increment;
|
|
5
5
|
const isNullable = columnInfo.is_nullable === 'YES';
|
|
6
6
|
const wrap = (t) => {
|
|
7
|
+
// Nullable columns need the type-array form (`Nullable(T)` → type: [..., 'null']):
|
|
8
|
+
// Type.Optional alone means "key may be absent", and serializing a NULL value against
|
|
9
|
+
// a plain type coerces it (0 / "" / false). A Type.Union with Null is no better —
|
|
10
|
+
// Ajv's coerceTypes corrupts null/0/"" through the union branches.
|
|
11
|
+
if (isNullable)
|
|
12
|
+
t = `Nullable(${t})`;
|
|
7
13
|
return hasDefault || isNullable ? `Type.Optional(${t})` : t;
|
|
8
14
|
};
|
|
9
15
|
switch (udtName) {
|
|
@@ -83,8 +89,10 @@ export function generateSchemaFile(schemaName, tableName, fields, colMap, primar
|
|
|
83
89
|
const fieldsStr = JSON.stringify(fields, null, 2).replace(/"(.*?)"\s*:\s*"Type(.*?)"/g, '$1: Type$2');
|
|
84
90
|
const colMapStr = JSON.stringify(colMap, null, 2);
|
|
85
91
|
const primaryKeyLine = primary?.length ? `\n primaryKey: ${JSON.stringify(primary)},` : '';
|
|
92
|
+
const usesNullable = Object.values(fields).some((t) => t.includes('Nullable('));
|
|
93
|
+
const valueImports = usesNullable ? 'Type, Nullable' : 'Type';
|
|
86
94
|
return `// Auto-generated by fastify-auto-sqlapi. Do not edit.
|
|
87
|
-
import {
|
|
95
|
+
import { ${valueImports} } from "fastify-auto-sqlapi";
|
|
88
96
|
import type { Static } from "fastify-auto-sqlapi";
|
|
89
97
|
|
|
90
98
|
const _Schema = ${fieldsStr};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bulk-delete.d.ts","sourceRoot":"","sources":["../../../../src/lib/engine/bulk/bulk-delete.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"bulk-delete.d.ts","sourceRoot":"","sources":["../../../../src/lib/engine/bulk/bulk-delete.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAE5E,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAuE5F"}
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { ConditionBuilder } from 'node-condition-builder';
|
|
2
2
|
import { buildTenantDeleteWhere, assertTenantOwnsAll } from '../../tenant.js';
|
|
3
|
-
import { primaryAsString } from '../../../types.js';
|
|
3
|
+
import { primaryAsString, isCompositePrimary } from '../../../types.js';
|
|
4
|
+
import { httpError } from '../../errors.js';
|
|
4
5
|
export async function bulkDeleteEngine(params) {
|
|
5
6
|
const { db, tableConf, ids, tenant, request } = params;
|
|
7
|
+
if (isCompositePrimary(tableConf.primary)) {
|
|
8
|
+
throw httpError(400, `Table "${tableConf.Schema.tableName}" has a composite primary key: bulk delete matches on "${primaryAsString(tableConf.primary)}" alone and could remove multiple rows per id. Use a custom query.`);
|
|
9
|
+
}
|
|
6
10
|
if (!ids.length)
|
|
7
11
|
return [];
|
|
8
12
|
const pk = primaryAsString(tableConf.primary);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"delete.d.ts","sourceRoot":"","sources":["../../../../src/lib/engine/rest/delete.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAY,MAAM,mBAAmB,CAAC;AAE9E,wBAAsB,YAAY,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,
|
|
1
|
+
{"version":3,"file":"delete.d.ts","sourceRoot":"","sources":["../../../../src/lib/engine/rest/delete.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAY,MAAM,mBAAmB,CAAC;AAE9E,wBAAsB,YAAY,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAuC9E"}
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { buildTenantDeleteWhere, assertTenantOwnsAll } from '../../tenant.js';
|
|
2
2
|
import { httpError } from '../../errors.js';
|
|
3
|
-
import { primaryAsString } from '../../../types.js';
|
|
3
|
+
import { primaryAsString, isCompositePrimary } from '../../../types.js';
|
|
4
4
|
export async function deleteEngine(params) {
|
|
5
5
|
const { db, tableConf, id, tenant, request } = params;
|
|
6
|
+
if (isCompositePrimary(tableConf.primary)) {
|
|
7
|
+
throw httpError(400, `Table "${tableConf.Schema.tableName}" has a composite primary key: delete-by-id would match on "${primaryAsString(tableConf.primary)}" alone and could remove multiple rows. Use a custom query.`);
|
|
8
|
+
}
|
|
6
9
|
const pk = primaryAsString(tableConf.primary);
|
|
7
10
|
const pkCol = tableConf.Schema.col(pk);
|
|
8
11
|
const tableName = tableConf.Schema.tableName;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get.d.ts","sourceRoot":"","sources":["../../../../src/lib/engine/rest/get.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAuB,MAAM,mBAAmB,CAAC;AAEnF,wBAAsB,SAAS,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC,
|
|
1
|
+
{"version":3,"file":"get.d.ts","sourceRoot":"","sources":["../../../../src/lib/engine/rest/get.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAuB,MAAM,mBAAmB,CAAC;AAEnF,wBAAsB,SAAS,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC,CAsCrE"}
|
|
@@ -3,9 +3,12 @@ import { camelcaseObject } from '../../naming.js';
|
|
|
3
3
|
import { buildTenantCondition, buildTenantJoin } from '../../tenant.js';
|
|
4
4
|
import { httpError } from '../../errors.js';
|
|
5
5
|
import { readableSelectColumns } from '../../read-access.js';
|
|
6
|
-
import { primaryAsString } from '../../../types.js';
|
|
6
|
+
import { primaryAsString, isCompositePrimary } from '../../../types.js';
|
|
7
7
|
export async function getEngine(params) {
|
|
8
8
|
const { db, tableConf, id, tenant } = params;
|
|
9
|
+
if (isCompositePrimary(tableConf.primary)) {
|
|
10
|
+
throw httpError(400, `Table "${tableConf.Schema.tableName}" has a composite primary key: get-by-id would match on "${primaryAsString(tableConf.primary)}" alone. Use search with all PK fields.`);
|
|
11
|
+
}
|
|
9
12
|
const pkCol = tableConf.Schema.col(primaryAsString(tableConf.primary));
|
|
10
13
|
const cb = new ConditionBuilder('AND', db.cbDialect);
|
|
11
14
|
// Table-qualified: the tenant scope may add a through-join, and a bare PK column
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../../../src/lib/engine/search/search.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EACV,QAAQ,EACR,YAAY,EACZ,YAAY,EAcb,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../../../src/lib/engine/search/search.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EACV,QAAQ,EACR,YAAY,EACZ,YAAY,EAcb,MAAM,mBAAmB,CAAC;AA8lC3B,wBAAsB,YAAY,CAChC,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,YAAY,GACnB,OAAO,CAAC,YAAY,CAAC,CA4HvB"}
|
|
@@ -219,7 +219,10 @@ function buildAggOrderExpr(db, dbTables, tableConf, alias, fn, field, joinGroup)
|
|
|
219
219
|
if (groupReq.aggregations.by && groupReq.aggregations.by !== joinField) {
|
|
220
220
|
err400(`Cannot order by aggregation on joinGroup with 'by' clause on non-FK column: ${alias} (grouped by '${groupReq.aggregations.by}', correlation FK is '${joinField}')`);
|
|
221
221
|
}
|
|
222
|
-
|
|
222
|
+
// Pass the join table's conf: without it validateSchemaField skips the readExclude
|
|
223
|
+
// check, and a HAVING-style condition on a hidden field becomes a 400-vs-200 oracle
|
|
224
|
+
// (executeJoinGroup's own validation is skipped when the main result set is empty).
|
|
225
|
+
const fieldCol = validateSchemaField(field, joinSchema, dbTables[joinSchema.tableName]);
|
|
223
226
|
const refs = extractJoinRefs(db, tableConf, joinDef);
|
|
224
227
|
// The subquery FROM is aliased so the correlation to the outer table survives a
|
|
225
228
|
// self-referencing relation; everything inside references the alias.
|
|
@@ -555,7 +558,10 @@ function buildByExpression(by, joinSchema, joinTableConf, db) {
|
|
|
555
558
|
err400(`Invalid 'by' specification: expected a field or computed name`);
|
|
556
559
|
}
|
|
557
560
|
if (by in joinSchema.fields) {
|
|
558
|
-
|
|
561
|
+
// validateSchemaField also enforces readExclude: GROUP BY on a hidden field
|
|
562
|
+
// would return its distinct values verbatim in `rows[].by`.
|
|
563
|
+
const col = validateSchemaField(by, joinSchema, joinTableConf);
|
|
564
|
+
return `${db.qi(joinSchema.tableName)}.${db.qi(col)}`;
|
|
559
565
|
}
|
|
560
566
|
const fn = joinTableConf?.computedFields?.[by];
|
|
561
567
|
if (fn) {
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type Static, type TSchema, type TUnsafe } from '@sinclair/typebox';
|
|
2
|
+
/**
|
|
3
|
+
* Mark a schema as nullable using the JSON-Schema type-array form
|
|
4
|
+
* (`{ type: ['integer', 'null'] }`).
|
|
5
|
+
*
|
|
6
|
+
* This is the only representation that behaves correctly under Fastify's default Ajv
|
|
7
|
+
* `coerceTypes`: with `Type.Union([T, Type.Null()])` a null body value is coerced by the
|
|
8
|
+
* first branch to 0/""/false — and with the Null branch first, a legitimate 0/""/false is
|
|
9
|
+
* coerced to null. With a type array no coercion happens when the value already matches
|
|
10
|
+
* one of the listed types. fast-json-stringify also serializes NULL correctly against a
|
|
11
|
+
* type array, while a bare `Type.Optional(T)` coerces NULL to the type's zero value.
|
|
12
|
+
*
|
|
13
|
+
* Schemas without a `type` keyword (e.g. `Type.Any()`) already admit null and are
|
|
14
|
+
* returned unchanged.
|
|
15
|
+
*/
|
|
16
|
+
export declare function Nullable<T extends TSchema>(schema: T): TUnsafe<Static<T> | null>;
|
|
17
|
+
//# sourceMappingURL=nullable.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nullable.d.ts","sourceRoot":"","sources":["../../src/lib/nullable.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,KAAK,MAAM,EAAE,KAAK,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAElF;;;;;;;;;;;;;GAaG;AACH,wBAAgB,QAAQ,CAAC,CAAC,SAAS,OAAO,EAAE,MAAM,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAMhF"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Type } from '@sinclair/typebox';
|
|
2
|
+
/**
|
|
3
|
+
* Mark a schema as nullable using the JSON-Schema type-array form
|
|
4
|
+
* (`{ type: ['integer', 'null'] }`).
|
|
5
|
+
*
|
|
6
|
+
* This is the only representation that behaves correctly under Fastify's default Ajv
|
|
7
|
+
* `coerceTypes`: with `Type.Union([T, Type.Null()])` a null body value is coerced by the
|
|
8
|
+
* first branch to 0/""/false — and with the Null branch first, a legitimate 0/""/false is
|
|
9
|
+
* coerced to null. With a type array no coercion happens when the value already matches
|
|
10
|
+
* one of the listed types. fast-json-stringify also serializes NULL correctly against a
|
|
11
|
+
* type array, while a bare `Type.Optional(T)` coerces NULL to the type's zero value.
|
|
12
|
+
*
|
|
13
|
+
* Schemas without a `type` keyword (e.g. `Type.Any()`) already admit null and are
|
|
14
|
+
* returned unchanged.
|
|
15
|
+
*/
|
|
16
|
+
export function Nullable(schema) {
|
|
17
|
+
const type = schema.type;
|
|
18
|
+
if (!type)
|
|
19
|
+
return schema;
|
|
20
|
+
const types = Array.isArray(type) ? type : [type];
|
|
21
|
+
if (types.includes('null'))
|
|
22
|
+
return schema;
|
|
23
|
+
return Type.Unsafe({ ...schema, type: [...types, 'null'] });
|
|
24
|
+
}
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { type TObject } from '@sinclair/typebox';
|
|
2
2
|
import type { DbTables } from '../../types.js';
|
|
3
3
|
export declare function SearchTableBodyPost(dbTables: DbTables, tableName: string): TObject;
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Querystring schema, parameterized on the plugin's `maxItemsPerPage`.
|
|
6
|
+
*
|
|
7
|
+
* The `itemsPerPage` default MUST NOT exceed the cap: Ajv injects the default before any
|
|
8
|
+
* runtime check runs, so a fixed default of 500 combined with `maxItemsPerPage: 100` would
|
|
9
|
+
* make every request without an explicit `itemsPerPage` fail with 400. The default is
|
|
10
|
+
* therefore `min(500, cap)` and the cap doubles as the schema `maximum` (structured 400).
|
|
11
|
+
*/
|
|
12
|
+
export declare function SearchTableQuery(maxItemsPerPage?: number): TObject;
|
|
13
|
+
/** Default-cap variant, kept for granular composition and backward compatibility. */
|
|
14
|
+
export declare const SearchTableQueryString: TObject<import("@sinclair/typebox").TProperties>;
|
|
13
15
|
export declare function SearchTableResponse(dbTables: DbTables, tableName: string): TObject;
|
|
14
16
|
//# sourceMappingURL=search.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../../src/lib/schema/search.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,KAAK,OAAO,EAAgB,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../../src/lib/schema/search.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,KAAK,OAAO,EAAgB,MAAM,mBAAmB,CAAC;AAIrE,OAAO,KAAK,EAAE,QAAQ,EAA4B,MAAM,gBAAgB,CAAC;AAyBzE,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAuHlF;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,eAAe,GAAE,MAAmC,GAAG,OAAO,CAe9F;AAED,qFAAqF;AACrF,eAAO,MAAM,sBAAsB,kDAAqB,CAAC;AAEzD,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAsElF"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Type } from '@sinclair/typebox';
|
|
2
2
|
import { ALLOWED_METHODS } from '../condition-methods.js';
|
|
3
3
|
import { readableFieldNames } from '../read-access.js';
|
|
4
|
+
import { DEFAULT_MAX_ITEMS_PER_PAGE } from '../../types.js';
|
|
4
5
|
/** Schema fields minus the ones hidden by `readExclude`. */
|
|
5
6
|
function readableFields(schema, tableConf) {
|
|
6
7
|
if (!tableConf?.readExclude?.length)
|
|
@@ -131,16 +132,32 @@ export function SearchTableBodyPost(dbTables, tableName) {
|
|
|
131
132
|
}
|
|
132
133
|
return Type.Object(bodyProperties);
|
|
133
134
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
135
|
+
/**
|
|
136
|
+
* Querystring schema, parameterized on the plugin's `maxItemsPerPage`.
|
|
137
|
+
*
|
|
138
|
+
* The `itemsPerPage` default MUST NOT exceed the cap: Ajv injects the default before any
|
|
139
|
+
* runtime check runs, so a fixed default of 500 combined with `maxItemsPerPage: 100` would
|
|
140
|
+
* make every request without an explicit `itemsPerPage` fail with 400. The default is
|
|
141
|
+
* therefore `min(500, cap)` and the cap doubles as the schema `maximum` (structured 400).
|
|
142
|
+
*/
|
|
143
|
+
export function SearchTableQuery(maxItemsPerPage = DEFAULT_MAX_ITEMS_PER_PAGE) {
|
|
144
|
+
return Type.Object({
|
|
145
|
+
orderBy: Type.Optional(Type.String()),
|
|
146
|
+
page: Type.Optional(Type.Integer({ minimum: 1 })),
|
|
147
|
+
itemsPerPage: Type.Optional(Type.Integer({
|
|
148
|
+
minimum: 1,
|
|
149
|
+
maximum: maxItemsPerPage,
|
|
150
|
+
default: Math.min(500, maxItemsPerPage),
|
|
151
|
+
})),
|
|
152
|
+
computeMin: Type.Optional(Type.String()),
|
|
153
|
+
computeMax: Type.Optional(Type.String()),
|
|
154
|
+
computeSum: Type.Optional(Type.String()),
|
|
155
|
+
computeAvg: Type.Optional(Type.String()),
|
|
156
|
+
// selectComputed list goes in the body (POST), not querystring — see SearchTableBodyPost.
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
/** Default-cap variant, kept for granular composition and backward compatibility. */
|
|
160
|
+
export const SearchTableQueryString = SearchTableQuery();
|
|
144
161
|
export function SearchTableResponse(dbTables, tableName) {
|
|
145
162
|
const tableConf = dbTables[tableName];
|
|
146
163
|
// Computed fields are present in main rows only when explicitly listed in
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"table-helpers.d.ts","sourceRoot":"","sources":["../../src/lib/table-helpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,KAAK,yBAAyB,EAAiC,MAAM,wBAAwB,CAAC;AACzH,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,KAAK,EACV,gBAAgB,EAChB,cAAc,EACd,aAAa,EACb,MAAM,EACP,MAAM,aAAa,CAAC;AAarB,KAAK,UAAU,CAAC,EAAE,IAAI,EAAE,SAAS,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,EAAE,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;AAExH,wBAAgB,eAAe,CAC7B,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,EAAE,SAAS,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,EAErE,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAC,EAC3B,YAAY,GAAE,EAAa,EAC3B,iBAAiB,CAAC,EAAE,CAClB,SAAS,EAAE,gBAAgB,EAC3B,OAAO,EAAE;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,MAAM,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,yBAAyB;CAAE,KAC3E,IAAI,GACR;IAAE,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAC;IAAC,OAAO,EAAE,aAAa,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,
|
|
1
|
+
{"version":3,"file":"table-helpers.d.ts","sourceRoot":"","sources":["../../src/lib/table-helpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,KAAK,yBAAyB,EAAiC,MAAM,wBAAwB,CAAC;AACzH,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,KAAK,EACV,gBAAgB,EAChB,cAAc,EACd,aAAa,EACb,MAAM,EACP,MAAM,aAAa,CAAC;AAarB,KAAK,UAAU,CAAC,EAAE,IAAI,EAAE,SAAS,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,EAAE,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;AAExH,wBAAgB,eAAe,CAC7B,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,EAAE,SAAS,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,EAErE,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAC,EAC3B,YAAY,GAAE,EAAa,EAC3B,iBAAiB,CAAC,EAAE,CAClB,SAAS,EAAE,gBAAgB,EAC3B,OAAO,EAAE;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,MAAM,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,yBAAyB;CAAE,KAC3E,IAAI,GACR;IAAE,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAC;IAAC,OAAO,EAAE,aAAa,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAsChG;AAED,wBAAgB,WAAW,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC3D,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,GAChB,MAAM,CAAC,CAAC,CAAC,CAMX;AAkED,MAAM,WAAW,oBAAoB;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,wBAAgB,aAAa,CAC3B,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAEjC,UAAU,EAAE,gBAAgB,CAAC,CAAC,CAAC,EAC/B,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC,EAAE,EAClD,UAAU,EAAE,gBAAgB,CAAC,CAAC,CAAC,EAC/B,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC,EAC3B,OAAO,CAAC,EAAE,oBAAoB,GAC7B,cAAc,CAShB;AAED,wBAAgB,eAAe,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/D,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAC,EAC3B,OAAO,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC,EAAE,GAC5B,CAAC,gBAAgB,EAAE,MAAM,EAAE,CAAC,CAE9B;AAED,wBAAgB,gBAAgB,CAC9B,GAAG,KAAK,EAAE,CAAC,gBAAgB,EAAE,MAAM,EAAE,CAAC,EAAE,GACvC,GAAG,CAAC,gBAAgB,EAAE,MAAM,EAAE,CAAC,CAEjC"}
|
|
@@ -17,8 +17,18 @@ export function exportTableInfo(Schema, extraFilters = {}, extendedCondition) {
|
|
|
17
17
|
// qualifier (a subquery alias) takes precedence over the table name.
|
|
18
18
|
const table = qi(qualifier ?? Schema.tableName, dialect);
|
|
19
19
|
for (const field of Object.keys(Schema.fields)) {
|
|
20
|
-
if (field in filterValues
|
|
21
|
-
|
|
20
|
+
if (!(field in filterValues) || filterValues[field] === undefined)
|
|
21
|
+
continue;
|
|
22
|
+
const col = `${table}.${qi(Schema.col(field), dialect)}`;
|
|
23
|
+
// An explicit null filters by IS NULL: equality with NULL never matches, and
|
|
24
|
+
// silently dropping the filter would return unfiltered results for a request
|
|
25
|
+
// that asked for "field is null".
|
|
26
|
+
if (filterValues[field] === null) {
|
|
27
|
+
// Second arg is CB's enable-guard: without `true` the call is a no-op.
|
|
28
|
+
condition.isNull(col, true);
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
condition.isEqual(col, filterValues[field]);
|
|
22
32
|
}
|
|
23
33
|
}
|
|
24
34
|
// extraFilters are handled exclusively by extendedCondition
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { FastifyInstance } from 'fastify';
|
|
2
|
+
import type { SqlApiPluginOptions } from '../../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* GET /agent/manifest → JSON description of every exposed table
|
|
5
|
+
* GET /agent/manifest.md → compact markdown for an LLM system prompt
|
|
6
|
+
*
|
|
7
|
+
* Opt-in via the `agentManifest` plugin option (like `swagger`). Runs behind the same
|
|
8
|
+
* global `onRequests` hooks as the data routes. The manifest is built once at startup —
|
|
9
|
+
* `DbTables` is static configuration.
|
|
10
|
+
*/
|
|
11
|
+
export default function agentManifestRoutes(fastify: FastifyInstance, options: SqlApiPluginOptions): Promise<void>;
|
|
12
|
+
//# sourceMappingURL=agent-manifest.routes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-manifest.routes.d.ts","sourceRoot":"","sources":["../../../src/routes/auto/agent-manifest.routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAE1D;;;;;;;GAOG;AACH,wBAA8B,mBAAmB,CAC/C,OAAO,EAAE,eAAe,EACxB,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,IAAI,CAAC,CAsBf"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { buildAgentManifest, renderAgentManifestMd } from '../../lib/agent/manifest.js';
|
|
2
|
+
/**
|
|
3
|
+
* GET /agent/manifest → JSON description of every exposed table
|
|
4
|
+
* GET /agent/manifest.md → compact markdown for an LLM system prompt
|
|
5
|
+
*
|
|
6
|
+
* Opt-in via the `agentManifest` plugin option (like `swagger`). Runs behind the same
|
|
7
|
+
* global `onRequests` hooks as the data routes. The manifest is built once at startup —
|
|
8
|
+
* `DbTables` is static configuration.
|
|
9
|
+
*/
|
|
10
|
+
export default async function agentManifestRoutes(fastify, options) {
|
|
11
|
+
const manifest = buildAgentManifest(options.DbTables);
|
|
12
|
+
const markdown = renderAgentManifestMd(manifest);
|
|
13
|
+
const onRequest = options.onRequests?.length ? options.onRequests : undefined;
|
|
14
|
+
fastify.get('/agent/manifest', {
|
|
15
|
+
...(onRequest ? { onRequest } : {}),
|
|
16
|
+
schema: {
|
|
17
|
+
tags: ['SqlAPI-agent'],
|
|
18
|
+
summary: 'Agent manifest (JSON)',
|
|
19
|
+
description: 'Machine-readable description of every exposed table: fields, operations, join aliases, computed fields. Pair with AGENTS_FRONTEND.md.',
|
|
20
|
+
},
|
|
21
|
+
}, async () => manifest);
|
|
22
|
+
fastify.get('/agent/manifest.md', {
|
|
23
|
+
...(onRequest ? { onRequest } : {}),
|
|
24
|
+
schema: {
|
|
25
|
+
tags: ['SqlAPI-agent'],
|
|
26
|
+
summary: 'Agent manifest (markdown)',
|
|
27
|
+
description: 'Compact markdown rendering of the manifest, designed for an LLM system prompt.',
|
|
28
|
+
},
|
|
29
|
+
}, async (_req, reply) => reply.type('text/markdown; charset=utf-8').send(markdown));
|
|
30
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bulk-delete.routes.d.ts","sourceRoot":"","sources":["../../../src/routes/auto/bulk-delete.routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAK/C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAE1D,wBAA8B,gBAAgB,CAC5C,OAAO,EAAE,eAAe,EACxB,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"bulk-delete.routes.d.ts","sourceRoot":"","sources":["../../../src/routes/auto/bulk-delete.routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAK/C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAE1D,wBAA8B,gBAAgB,CAC5C,OAAO,EAAE,eAAe,EACxB,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,IAAI,CAAC,CAmBf"}
|
|
@@ -5,6 +5,7 @@ import { DEFAULT_MAX_BULK_ITEMS } from '../../types.js';
|
|
|
5
5
|
export default async function bulkDeleteRoutes(fastify, options) {
|
|
6
6
|
await registerForAllTables(fastify, options, {
|
|
7
7
|
operation: 'bulkDelete',
|
|
8
|
+
singlePkOnly: true,
|
|
8
9
|
method: 'POST',
|
|
9
10
|
url: (tc) => `/bulk/${tc.Schema.tableName}/delete`,
|
|
10
11
|
successStatus: 200,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"delete.routes.d.ts","sourceRoot":"","sources":["../../../src/routes/auto/delete.routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAI/C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAE1D,wBAA8B,YAAY,CACxC,OAAO,EAAE,eAAe,EACxB,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"delete.routes.d.ts","sourceRoot":"","sources":["../../../src/routes/auto/delete.routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAI/C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAE1D,wBAA8B,YAAY,CACxC,OAAO,EAAE,eAAe,EACxB,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,IAAI,CAAC,CAqBf"}
|
|
@@ -4,6 +4,7 @@ import { registerForAllTables } from './route-helpers.js';
|
|
|
4
4
|
export default async function deleteRoutes(fastify, options) {
|
|
5
5
|
await registerForAllTables(fastify, options, {
|
|
6
6
|
operation: 'delete',
|
|
7
|
+
singlePkOnly: true,
|
|
7
8
|
method: 'DELETE',
|
|
8
9
|
url: (tc) => `/rest/${tc.Schema.tableName}/:id`,
|
|
9
10
|
successStatus: 200,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get.routes.d.ts","sourceRoot":"","sources":["../../../src/routes/auto/get.routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAG/C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAE1D,wBAA8B,SAAS,CACrC,OAAO,EAAE,eAAe,EACxB,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"get.routes.d.ts","sourceRoot":"","sources":["../../../src/routes/auto/get.routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAG/C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAE1D,wBAA8B,SAAS,CACrC,OAAO,EAAE,eAAe,EACxB,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,IAAI,CAAC,CAkBf"}
|
|
@@ -3,6 +3,7 @@ import { registerForAllTables } from './route-helpers.js';
|
|
|
3
3
|
export default async function getRoutes(fastify, options) {
|
|
4
4
|
await registerForAllTables(fastify, options, {
|
|
5
5
|
operation: 'get',
|
|
6
|
+
singlePkOnly: true,
|
|
6
7
|
method: 'GET',
|
|
7
8
|
url: (tc) => `/rest/${tc.Schema.tableName}/:id`,
|
|
8
9
|
successStatus: 200,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../src/routes/auto/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAgB,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../src/routes/auto/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAgB,MAAM,SAAS,CAAC;AAc7D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;kCAuD/C,eAAe,WACf,mBAAmB,KAC3B,OAAO,CAAC,IAAI,CAAC;AAHhB,wBA6DoC"}
|
|
@@ -10,6 +10,7 @@ import updateRoutes from './update.routes.js';
|
|
|
10
10
|
import deleteRoutes from './delete.routes.js';
|
|
11
11
|
import bulkUpsertRoutes from './bulk-upsert.routes.js';
|
|
12
12
|
import bulkDeleteRoutes from './bulk-delete.routes.js';
|
|
13
|
+
import agentManifestRoutes from './agent-manifest.routes.js';
|
|
13
14
|
// Schema combinators for which Ajv reports one error per failed branch plus a
|
|
14
15
|
// single combinator-level summary error, all targeting the same path.
|
|
15
16
|
const COMBINATOR_KEYWORDS = new Set(['anyOf', 'oneOf']);
|
|
@@ -97,5 +98,8 @@ export default fp(async function fastifyAutoSqlApi(fastify, options) {
|
|
|
97
98
|
await instance.register(deleteRoutes, routeOptions);
|
|
98
99
|
await instance.register(bulkUpsertRoutes, routeOptions);
|
|
99
100
|
await instance.register(bulkDeleteRoutes, routeOptions);
|
|
101
|
+
if (options.agentManifest) {
|
|
102
|
+
await instance.register(agentManifestRoutes, routeOptions);
|
|
103
|
+
}
|
|
100
104
|
}, { prefix });
|
|
101
105
|
}, { name: 'fastify-auto-sqlapi' });
|
|
@@ -12,6 +12,13 @@ interface RouteSchema {
|
|
|
12
12
|
export interface AutoRouteSpec {
|
|
13
13
|
/** Operation key matched against `ITable.operations` to decide whether to register the route. */
|
|
14
14
|
operation: TableOperation;
|
|
15
|
+
/**
|
|
16
|
+
* Set when the route addresses records by a single PK value (get, delete, bulkDelete).
|
|
17
|
+
* Tables with a composite primary key skip these routes: matching on the first PK column
|
|
18
|
+
* alone would touch every row sharing that value. Explicitly listing the operation in
|
|
19
|
+
* `ITable.operations` for such a table throws at registration.
|
|
20
|
+
*/
|
|
21
|
+
singlePkOnly?: boolean;
|
|
15
22
|
method: 'GET' | 'POST' | 'PUT' | 'DELETE';
|
|
16
23
|
/** URL builder receiving the table configuration to read `Schema.tableName`. */
|
|
17
24
|
url: (tableConf: ITable) => string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"route-helpers.d.ts","sourceRoot":"","sources":["../../../src/routes/auto/route-helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"route-helpers.d.ts","sourceRoot":"","sources":["../../../src/routes/auto/route-helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAG7E,OAAO,KAAK,EAAE,MAAM,EAAE,mBAAmB,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAE5F,KAAK,WAAW,GAAG,CAAC,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,YAAY,KAAK,OAAO,CAAC,IAAI,GAAG,YAAY,CAAC,CAAC;AAElG,wBAAgB,eAAe,CAAC,OAAO,EAAE,mBAAmB,EAAE,SAAS,EAAE,MAAM,GAAG,WAAW,EAAE,CAE9F;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAQlG;AAED,UAAU,WAAW;IAEnB,IAAI,CAAC,EAAE,GAAG,CAAC;IAEX,MAAM,CAAC,EAAE,GAAG,CAAC;IAEb,WAAW,CAAC,EAAE,GAAG,CAAC;IAElB,QAAQ,CAAC,EAAE,GAAG,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,iGAAiG;IACjG,SAAS,EAAE,cAAc,CAAC;IAC1B;;;;;OAKG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,CAAC;IAC1C,gFAAgF;IAChF,GAAG,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,CAAC;IACnC,aAAa,EAAE,GAAG,GAAG,GAAG,CAAC;IACzB,wIAAwI;IACxI,OAAO,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,WAAW,CAAC;IACnF,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,KAAK,MAAM,CAAC;IACpF;2EACuE;IACvE,MAAM,EAAE,CACN,OAAO,EAAE,eAAe,EACxB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,cAAc,EACvB,KAAK,EAAE,YAAY,KAChB,OAAO,CAAC,OAAO,CAAC,CAAC;CACvB;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,eAAe,EACxB,OAAO,EAAE,mBAAmB,EAC5B,IAAI,EAAE,aAAa,GAClB,OAAO,CAAC,IAAI,CAAC,CAkDf"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ensureSqlApiDecorator } from '../../lib/sql-api-decorator.js';
|
|
2
|
+
import { isCompositePrimary } from '../../types.js';
|
|
2
3
|
export function mergeOnRequests(options, tableConf) {
|
|
3
4
|
return [...(options.onRequests || []), ...(tableConf.onRequests || [])];
|
|
4
5
|
}
|
|
@@ -25,6 +26,16 @@ export async function registerForAllTables(fastify, options, spec) {
|
|
|
25
26
|
// operations acts as a whitelist; when omitted every operation is exposed.
|
|
26
27
|
if (tableConf.operations && !tableConf.operations.includes(spec.operation))
|
|
27
28
|
continue;
|
|
29
|
+
// Single-PK routes cannot address composite-PK tables — skip them (default) or,
|
|
30
|
+
// when the consumer explicitly asked for the operation, fail loudly at startup.
|
|
31
|
+
if (spec.singlePkOnly && isCompositePrimary(tableConf.primary)) {
|
|
32
|
+
if (tableConf.operations?.includes(spec.operation)) {
|
|
33
|
+
throw new Error(`Table "${tableName}" has a composite primary key (${tableConf.primary.join(', ')}): ` +
|
|
34
|
+
`operation "${spec.operation}" addresses records by a single PK value and cannot be exposed. ` +
|
|
35
|
+
`Remove it from "operations", or handle the table via search/update or a custom route.`);
|
|
36
|
+
}
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
28
39
|
const schemas = spec.schemas(DbTables, tableName, tableConf);
|
|
29
40
|
// Only include schema keys that are actually defined: Fastify checks for key
|
|
30
41
|
// *presence* (not undefined value) to decide whether to emit FSTWRN001.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { SearchTableBodyPost,
|
|
1
|
+
import { SearchTableBodyPost, SearchTableQuery, SearchTableResponse } from '../../lib/schema/search.js';
|
|
2
2
|
import { registerForAllTables } from './route-helpers.js';
|
|
3
3
|
import { httpError } from '../../lib/errors.js';
|
|
4
4
|
import { DEFAULT_MAX_ITEMS_PER_PAGE } from '../../types.js';
|
|
@@ -10,7 +10,7 @@ export default async function searchRoutes(fastify, options) {
|
|
|
10
10
|
successStatus: 200,
|
|
11
11
|
schemas: (db, table) => ({
|
|
12
12
|
body: SearchTableBodyPost(db, table),
|
|
13
|
-
querystring:
|
|
13
|
+
querystring: SearchTableQuery(options.maxItemsPerPage ?? DEFAULT_MAX_ITEMS_PER_PAGE),
|
|
14
14
|
response: SearchTableResponse(db, table),
|
|
15
15
|
}),
|
|
16
16
|
summary: 'Search',
|
|
@@ -45,7 +45,7 @@ export default async function searchRoutes(fastify, options) {
|
|
|
45
45
|
selectComputed: body.selectComputed,
|
|
46
46
|
orderBy: query.orderBy,
|
|
47
47
|
paginator: (query.page || query.itemsPerPage)
|
|
48
|
-
? { page: query.page || 1, itemsPerPage: query.itemsPerPage || 500 }
|
|
48
|
+
? { page: query.page || 1, itemsPerPage: query.itemsPerPage || Math.min(500, maxRows) }
|
|
49
49
|
: undefined,
|
|
50
50
|
computeMin: query.computeMin,
|
|
51
51
|
computeMax: query.computeMax,
|
package/dist/types/plugin.d.ts
CHANGED
|
@@ -28,6 +28,13 @@ export interface SqlApiPluginOptions {
|
|
|
28
28
|
* Defaults to {@link DEFAULT_MAX_BULK_ITEMS}.
|
|
29
29
|
*/
|
|
30
30
|
maxBulkItems?: number;
|
|
31
|
+
/**
|
|
32
|
+
* Register `GET /agent/manifest` (JSON) and `GET /agent/manifest.md` (markdown): a
|
|
33
|
+
* machine-readable description of every exposed table (fields, operations, join
|
|
34
|
+
* aliases, computed fields), for LLM/agent clients. Opt-in, like `swagger`. The
|
|
35
|
+
* routes run behind the same global `onRequests` hooks as the data routes.
|
|
36
|
+
*/
|
|
37
|
+
agentManifest?: boolean;
|
|
31
38
|
}
|
|
32
39
|
/** Default row cap for search (page size and no-paginator LIMIT). Override via `maxItemsPerPage`. */
|
|
33
40
|
export declare const DEFAULT_MAX_ITEMS_PER_PAGE = 1000;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/types/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC5D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,QAAQ,CAAC;IACnB,UAAU,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,YAAY,KAAK,OAAO,CAAC,IAAI,GAAG,YAAY,CAAC,CAAC,EAAE,CAAC;IAChG,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,GAAG,cAAc,CAAC;IACnC,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,QAAQ,GAAG,QAAQ,EAAE,GAAG,IAAI,GAAG,SAAS,GAC/E,OAAO,CAAC,QAAQ,GAAG,QAAQ,EAAE,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;IACtD,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/types/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC5D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,QAAQ,CAAC;IACnB,UAAU,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,YAAY,KAAK,OAAO,CAAC,IAAI,GAAG,YAAY,CAAC,CAAC,EAAE,CAAC;IAChG,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,GAAG,cAAc,CAAC;IACnC,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,QAAQ,GAAG,QAAQ,EAAE,GAAG,IAAI,GAAG,SAAS,GAC/E,OAAO,CAAC,QAAQ,GAAG,QAAQ,EAAE,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;IACtD,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,qGAAqG;AACrG,eAAO,MAAM,0BAA0B,OAAO,CAAC;AAE/C,8EAA8E;AAC9E,eAAO,MAAM,sBAAsB,OAAO,CAAC"}
|
package/dist/types/table.d.ts
CHANGED
|
@@ -128,5 +128,11 @@ export interface ITable<F extends Record<string, TSchema> = Record<string, TSche
|
|
|
128
128
|
}
|
|
129
129
|
export type DbTables = Record<string, ITable<any>>;
|
|
130
130
|
export declare function primaryAsString(pk: string | string[]): string;
|
|
131
|
+
/**
|
|
132
|
+
* True when the primary key spans more than one column. Operations that address a
|
|
133
|
+
* record by a single PK value (get, delete, bulkDelete) cannot work on such tables:
|
|
134
|
+
* matching on the first column alone would hit every row sharing that value.
|
|
135
|
+
*/
|
|
136
|
+
export declare function isCompositePrimary(pk: string | string[]): boolean;
|
|
131
137
|
export declare function primaryAsCols(pk: string | string[], colFn: (f: string) => string): string | string[];
|
|
132
138
|
//# sourceMappingURL=table.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"table.d.ts","sourceRoot":"","sources":["../../src/types/table.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"table.d.ts","sourceRoot":"","sources":["../../src/types/table.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,yBAAyB,EAAE,WAAW,IAAI,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAChI,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC5D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEpE,4FAA4F;AAC5F,MAAM,MAAM,cAAc,GACtB,QAAQ,GACR,KAAK,GACL,QAAQ,GACR,QAAQ,GACR,QAAQ,GACR,YAAY,GACZ,YAAY,CAAC;AAEjB,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,yBAAyB,CAAC,CAAC;AACrE,MAAM,MAAM,mBAAmB,GAAG,CAAC,SAAS,EAAE,gBAAgB,EAAE,OAAO,EAAE,YAAY,KAAK,IAAI,CAAC;AAC/F;;;;;;;;;;GAUG;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE,SAAS,EAAE,SAAS,CAAC,EAAE,MAAM,KAAK,gBAAgB,CAAC;AAEjH,MAAM,WAAW,MAAM,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACjF,OAAO,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC,EAAE,CAAC;IACnD,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAC;IAC5B,OAAO,EAAE,aAAa,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC;;;;;;;;OAQG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IACjD,gBAAgB,CAAC,EAAE,cAAc,EAAE,CAAC;IACpC,iBAAiB,CAAC,EAAE,cAAc,EAAE,CAAC;IACrC,SAAS,CAAC,EAAE,GAAG,CAAC,gBAAgB,EAAE,MAAM,EAAE,CAAC,CAAC;IAC5C,eAAe,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;IAC7D,QAAQ,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC;IAC1B,YAAY,CAAC,EAAE,eAAe,CAAC,CAAC,CAAC,CAAC;IAClC,YAAY,CAAC,EAAE,CACb,EAAE,EAAE,WAAW,EACf,GAAG,EAAE,cAAc,EACnB,MAAM,EAAE;SAAG,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,UAAU,GAAG,IAAI;KAAE,KAC1D,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB,YAAY,CAAC,EAAE,CACb,EAAE,EAAE,WAAW,EACf,GAAG,EAAE,cAAc,EACnB,MAAM,EAAE;SAAG,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,UAAU,GAAG,IAAI;KAAE,EAC7D,sBAAsB,CAAC,EAAE,OAAO,KAC7B,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,WAAW,CAAC,EAAE,CACZ,EAAE,EAAE,WAAW,EACf,GAAG,EAAE,cAAc,EACnB,MAAM,EAAE;SAAG,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;KAAE,EACzC,gBAAgB,CAAC,EAAE,OAAO,KACvB,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,CACZ,EAAE,EAAE,WAAW,EACf,GAAG,EAAE,cAAc,EACnB,MAAM,EAAE;SAAG,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,UAAU,GAAG,IAAI;KAAE,EAC7D,gBAAgB,CAAC,EAAE,OAAO,EAC1B,eAAe,CAAC,EAAE,OAAO,KACtB,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B;;;;;;;;;;;;OAYG;IACH,YAAY,CAAC,EAAE,CACb,EAAE,EAAE,WAAW,EACf,GAAG,EAAE,cAAc,EACnB,EAAE,EAAE,MAAM,GAAG,MAAM,KAChB,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B;;;OAGG;IACH,WAAW,CAAC,EAAE,CACZ,EAAE,EAAE,WAAW,EACf,GAAG,EAAE,cAAc,EACnB,EAAE,EAAE,MAAM,GAAG,MAAM,KAChB,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B;;;;;;;;OAQG;IACH,gBAAgB,CAAC,EAAE,CACjB,EAAE,EAAE,WAAW,EACf,GAAG,EAAE,cAAc,EACnB,GAAG,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,KACrB,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B;;;;OAIG;IACH,eAAe,CAAC,EAAE,CAChB,EAAE,EAAE,WAAW,EACf,GAAG,EAAE,cAAc,EACnB,UAAU,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,KAC5B,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B;;;;;;;;OAQG;IACH,UAAU,CAAC,EAAE,cAAc,EAAE,CAAC;IAC9B;;;;;;;;;;;OAWG;IACH,WAAW,CAAC,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC,EAAE,CAAC;IACnC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mBAAmB,CAAC,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC,EAAE,CAAC;IAC3C,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,UAAU,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,YAAY,KAAK,OAAO,CAAC,IAAI,GAAG,YAAY,CAAC,CAAC,EAAE,CAAC;IAChG,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AAGD,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;AAGnD,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,MAAM,CAE7D;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,OAAO,CAEjE;AAED,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,GAAG,MAAM,GAAG,MAAM,EAAE,CAEpG"}
|
package/dist/types/table.js
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
export function primaryAsString(pk) {
|
|
3
3
|
return Array.isArray(pk) ? pk[0] : pk;
|
|
4
4
|
}
|
|
5
|
+
/**
|
|
6
|
+
* True when the primary key spans more than one column. Operations that address a
|
|
7
|
+
* record by a single PK value (get, delete, bulkDelete) cannot work on such tables:
|
|
8
|
+
* matching on the first column alone would hit every row sharing that value.
|
|
9
|
+
*/
|
|
10
|
+
export function isCompositePrimary(pk) {
|
|
11
|
+
return Array.isArray(pk) && pk.length > 1;
|
|
12
|
+
}
|
|
5
13
|
export function primaryAsCols(pk, colFn) {
|
|
6
14
|
return Array.isArray(pk) ? pk.map(colFn) : colFn(pk);
|
|
7
15
|
}
|