fastify-auto-sqlapi 0.1.9 → 0.1.11
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 +3 -1
- package/AGENTS_BACKEND.md +102 -3
- package/AGENTS_FRONTEND.md +69 -545
- package/BREAKING_CHANGES.md +63 -0
- package/CHANGELOG.md +100 -0
- package/README.md +190 -25
- 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/write-helpers.d.ts +18 -4
- package/dist/lib/engine/write-helpers.d.ts.map +1 -1
- package/dist/lib/engine/write-helpers.js +31 -10
- 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/0001-no-orm-raw-sql.md +38 -0
- package/docs/adr/0002-open-by-default.md +37 -0
- package/docs/adr/0003-bulk-not-transactional.md +35 -0
- package/docs/adr/0004-updates-always-open.md +46 -0
- package/docs/adr/0005-insert-pipeline-sanitize-before-hooks.md +51 -0
- package/docs/adr/0006-raw-db-errors.md +34 -0
- package/docs/adr/0007-explicit-table-generation.md +39 -0
- package/docs/adr/README.md +50 -0
- package/package.json +2 -1
|
@@ -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":"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"}
|
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
|
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# 0001. No ORM — raw SQL with minimal dependencies
|
|
2
|
+
|
|
3
|
+
- **Status**: accepted
|
|
4
|
+
- **Date**: 2026-07-23 (foundational decision, predates this record)
|
|
5
|
+
|
|
6
|
+
## Context
|
|
7
|
+
|
|
8
|
+
Auto-generating CRUD APIs from database tables is the territory of ORMs and heavy
|
|
9
|
+
API-builder frameworks. Adopting one would have provided models, migrations and query
|
|
10
|
+
building for free — at the price of a large dependency tree, an abstraction layer between
|
|
11
|
+
the consumer and their SQL, and lock-in to the ORM's schema model.
|
|
12
|
+
|
|
13
|
+
## Decision
|
|
14
|
+
|
|
15
|
+
The plugin talks to the database with raw SQL through thin driver adapters (`pg`,
|
|
16
|
+
`mysql2`), composing WHERE clauses with `node-condition-builder` and validating with
|
|
17
|
+
TypeBox. No ORM, no query-builder DSL, no migration system. Runtime dependencies are kept
|
|
18
|
+
to the minimum, and the plugin re-exports its building blocks (TypeBox, ConditionBuilder)
|
|
19
|
+
so consumers build on the same tools instead of adding parallel ones.
|
|
20
|
+
|
|
21
|
+
## Alternatives considered
|
|
22
|
+
|
|
23
|
+
- **Build on an ORM (Prisma, Drizzle, Sequelize, …)** — rejected: large dependency
|
|
24
|
+
surface, a second schema definition to keep in sync with the DB, and the ORM's
|
|
25
|
+
abstractions leak into the public API. The CLI introspection (`information_schema`)
|
|
26
|
+
gives the same "schema from the DB" ergonomics without the layer.
|
|
27
|
+
- **Hand-rolled query-builder DSL** — rejected: reinvents an ORM incrementally.
|
|
28
|
+
`node-condition-builder` covers the one genuinely composable part (conditions);
|
|
29
|
+
everything else is plain SQL the maintainer can read and debug.
|
|
30
|
+
|
|
31
|
+
## Consequences
|
|
32
|
+
|
|
33
|
+
- The engine owns dialect differences (placeholders, quoting, RETURNING vs insertId)
|
|
34
|
+
explicitly — see `src/lib/dialect.ts` and the adapters.
|
|
35
|
+
- Consumers get a framework approach: tools are provided (SqlApi, QueryClient,
|
|
36
|
+
ConditionBuilder), product structure is not imposed.
|
|
37
|
+
- Features an ORM would give for free (migrations, lazy relations, identity maps) are
|
|
38
|
+
intentionally out of scope.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# 0002. Open by default — no imposed auth model
|
|
2
|
+
|
|
3
|
+
- **Status**: accepted
|
|
4
|
+
- **Date**: 2026-07-23 (confirmed by the maintainer in June 2026)
|
|
5
|
+
|
|
6
|
+
## Context
|
|
7
|
+
|
|
8
|
+
Registering the plugin with no configuration exposes every operation (read and write,
|
|
9
|
+
including bulk delete) on every table in `DbTables`. Security reviews repeatedly flag this
|
|
10
|
+
as a vulnerability and propose secure-by-default behavior: routes disabled until
|
|
11
|
+
explicitly enabled, or a mandatory auth hook.
|
|
12
|
+
|
|
13
|
+
## Decision
|
|
14
|
+
|
|
15
|
+
The plugin stays open by default. Authentication and authorization are the consumer's
|
|
16
|
+
job, wired through the tools provided for it: `onRequests` (global or per-table request
|
|
17
|
+
hooks), `operations` (per-table whitelist of registered routes), and `tenantScope`
|
|
18
|
+
(tenant isolation). The README carries a prominent warning in its Security section.
|
|
19
|
+
|
|
20
|
+
## Alternatives considered
|
|
21
|
+
|
|
22
|
+
- **Deny-by-default routes** (`operations` required, empty = nothing exposed) — rejected:
|
|
23
|
+
it turns the five-minute quick start into configuration boilerplate, and the plugin's
|
|
24
|
+
primary use case is rapid internal API scaffolding where the consumer adds auth at the
|
|
25
|
+
Fastify level anyway.
|
|
26
|
+
- **Mandatory auth callback** (refuse to register without an `onRequests`) — rejected:
|
|
27
|
+
imposes an auth model on consumers who legitimately handle auth outside the plugin
|
|
28
|
+
(gateway, network isolation, Fastify-level hooks on the parent scope).
|
|
29
|
+
|
|
30
|
+
## Consequences
|
|
31
|
+
|
|
32
|
+
- **You must lock it down before exposing it.** The responsibility is documented, not
|
|
33
|
+
enforced.
|
|
34
|
+
- Security reviews will keep flagging this; the answer is this ADR plus the README
|
|
35
|
+
Security section, not a behavior change.
|
|
36
|
+
- `operations` gates HTTP routes only — the programmatic `sqlApi.*` methods remain
|
|
37
|
+
available to consumer code, by design.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# 0003. Bulk operations are not transactional
|
|
2
|
+
|
|
3
|
+
- **Status**: accepted
|
|
4
|
+
- **Date**: 2026-07-23 (confirmed by the maintainer in June 2026)
|
|
5
|
+
|
|
6
|
+
## Context
|
|
7
|
+
|
|
8
|
+
Single insert and update run inside a transaction: main record, secondaries, deletions
|
|
9
|
+
and after-hooks are atomic (`QueryClient.withTransaction`, degrading gracefully when the
|
|
10
|
+
adapter has no `connect()`). Bulk upsert and bulk delete do not — a failure partway
|
|
11
|
+
through leaves the earlier items committed. Reviews periodically report this as an
|
|
12
|
+
inconsistency and propose wrapping bulk operations in a transaction.
|
|
13
|
+
|
|
14
|
+
## Decision
|
|
15
|
+
|
|
16
|
+
Bulk operations stay non-transactional. They are a shortcut for issuing many single
|
|
17
|
+
operations efficiently, not an atomic batch primitive: "all or nothing" is explicitly not
|
|
18
|
+
part of their contract.
|
|
19
|
+
|
|
20
|
+
## Alternatives considered
|
|
21
|
+
|
|
22
|
+
- **Wrap each bulk request in one transaction** — rejected: changes the semantics from
|
|
23
|
+
"N independent operations" to "one atomic batch", which is a different feature. Large
|
|
24
|
+
batches would also hold long transactions with the associated lock footprint.
|
|
25
|
+
- **Opt-in `transactional: true` flag** — rejected for now: adds API surface for a need
|
|
26
|
+
no consumer has expressed; a consumer needing atomicity can call the programmatic
|
|
27
|
+
`sqlApi.*` methods inside their own `withTransaction`.
|
|
28
|
+
|
|
29
|
+
## Consequences
|
|
30
|
+
|
|
31
|
+
- A failed bulk request may have partially applied. Consumers that care must either
|
|
32
|
+
retry idempotently (upsert semantics make this natural) or manage their own
|
|
33
|
+
transaction via the programmatic API.
|
|
34
|
+
- Bulk delete reports the ids actually deleted (`afterBulkDelete` receives the actual
|
|
35
|
+
subset), which is the honest contract for a non-atomic batch.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# 0004. Updates are always open — no `excludeFromUpdate`
|
|
2
|
+
|
|
3
|
+
- **Status**: accepted
|
|
4
|
+
- **Date**: 2026-07-23
|
|
5
|
+
|
|
6
|
+
## Context
|
|
7
|
+
|
|
8
|
+
`excludeFromCreation` strips client-supplied fields from inserts. A symmetric
|
|
9
|
+
`excludeFromUpdate` was proposed to statically block fields on the update path (e.g.
|
|
10
|
+
`createdAt`, `isAdmin`), partly to close the asymmetry where a field excluded from
|
|
11
|
+
creation can still be modified via `PUT`.
|
|
12
|
+
|
|
13
|
+
## Decision
|
|
14
|
+
|
|
15
|
+
No `excludeFromUpdate`. Every Schema field is updatable by default, and
|
|
16
|
+
`excludeFromCreation` is defined as an **ergonomics tool for creation** (auto-generated
|
|
17
|
+
PKs, DB-default columns — its original purpose was handling autoincrement validation),
|
|
18
|
+
**not** a field-level security mechanism.
|
|
19
|
+
|
|
20
|
+
Field-level update rules are product logic the plugin must not decide: a super admin may
|
|
21
|
+
legitimately fix an `updatedAt`; in one product users may move themselves across tenants,
|
|
22
|
+
in another an admin moves others, in a third nobody moves anyone. A static list cannot
|
|
23
|
+
express any of this — real authorization is contextual (session, role, record state).
|
|
24
|
+
Consumers encode their rules in `beforeUpdate` (silent strip) or `validate` (loud 400),
|
|
25
|
+
or move privileged transitions to dedicated endpoints and keep them off the auto routes
|
|
26
|
+
via `operations`.
|
|
27
|
+
|
|
28
|
+
## Alternatives considered
|
|
29
|
+
|
|
30
|
+
- **`excludeFromUpdate` list** — rejected: the main risk is the *false sense of
|
|
31
|
+
security*. It only covers "nobody, ever, via API", while the dangerous real-world cases
|
|
32
|
+
(`isAdmin`, roles, ownership) are session-dependent — someone would use the list for
|
|
33
|
+
them and believe the problem solved.
|
|
34
|
+
- **`immutable: [...]` / per-field `writable` modes** — rejected: a bigger redesign of
|
|
35
|
+
the write model for the same limited static semantics.
|
|
36
|
+
|
|
37
|
+
## Consequences
|
|
38
|
+
|
|
39
|
+
- Sensitive flags in the Schema are updatable through the auto routes until the consumer
|
|
40
|
+
adds a hook. This is documented prominently (README Security → "Field-level update
|
|
41
|
+
rules are product logic", AGENTS_BACKEND.md pattern "Protect sensitive fields on
|
|
42
|
+
update").
|
|
43
|
+
- The tenant column is the one deliberate exception, and only under opt-in `tenantScope`:
|
|
44
|
+
tenant-scoped callers cannot re-tenant records (isolation contract), while admin
|
|
45
|
+
callers (`getTenantId` → `null`) remain unrestricted — consistent with "the plugin does
|
|
46
|
+
not decide product rules".
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# 0005. Insert pipeline: client payload sanitized before `beforeInsert`
|
|
2
|
+
|
|
3
|
+
- **Status**: accepted
|
|
4
|
+
- **Date**: 2026-07-23
|
|
5
|
+
|
|
6
|
+
## Context
|
|
7
|
+
|
|
8
|
+
Until v0.1.9 the insert pipeline applied `excludeFromCreation` *after* the `beforeInsert`
|
|
9
|
+
hook, so a value the hook assigned to an excluded field was silently stripped from the
|
|
10
|
+
INSERT. This broke the documented server-generated-id pattern (TEXT PK in
|
|
11
|
+
`excludeFromCreation`, id generated in `beforeInsert` → not-null violation) and
|
|
12
|
+
contradicted the documented contract that hook mutations propagate to the SQL. The same
|
|
13
|
+
ordering flaw let an excluded FK column erase the engine's own FK auto-fill on secondary
|
|
14
|
+
records.
|
|
15
|
+
|
|
16
|
+
## Decision
|
|
17
|
+
|
|
18
|
+
Exclusion is a whitelist on **client input** and runs at the client boundary. The insert
|
|
19
|
+
pipeline order is, for both single insert and bulk upsert:
|
|
20
|
+
|
|
21
|
+
1. `validate` — receives the payload **as sent by the client** (documented contract);
|
|
22
|
+
2. `excludeFromCreation` strip;
|
|
23
|
+
3. `beforeInsert` — hook mutations, including on excluded fields, reach the SQL;
|
|
24
|
+
4. conversion to DB column format.
|
|
25
|
+
|
|
26
|
+
For secondary records, exclusion runs **before** the engine's FK auto-fill, so the
|
|
27
|
+
injected parent key always survives.
|
|
28
|
+
|
|
29
|
+
Server-side writers are ordered by trust: client input is sanitized first, then hooks
|
|
30
|
+
mutate freely, then engine-enforced values (tenant injection/validation) run last and
|
|
31
|
+
fail loudly on conflict rather than stripping silently.
|
|
32
|
+
|
|
33
|
+
## Alternatives considered
|
|
34
|
+
|
|
35
|
+
- **Strip after the hook** (previous behavior) — rejected: silently discards
|
|
36
|
+
server-generated values; the only thing it "protected" was the ability of a hook to
|
|
37
|
+
accidentally write an excluded field, which is server code and trusted.
|
|
38
|
+
- **Strip before `validate` too** — rejected: `validate` is documented to receive the
|
|
39
|
+
record as sent by the client; changing its input would break that contract for no
|
|
40
|
+
gain.
|
|
41
|
+
|
|
42
|
+
## Consequences
|
|
43
|
+
|
|
44
|
+
- The pattern `excludeFromCreation: ['id']` + id-generating `beforeInsert` works as
|
|
45
|
+
documented; consumers no longer need the `schemaOverrides` workaround.
|
|
46
|
+
- Behavior change (noted in the CHANGELOG): a hook that assigns an excluded field now
|
|
47
|
+
writes it, where it was previously discarded.
|
|
48
|
+
- `afterInsert` no longer sees client-supplied values on excluded fields — it sees what
|
|
49
|
+
was actually written.
|
|
50
|
+
- Locked by unit tests (single, bulk, upsert, secondaries, validate-ordering) and by a
|
|
51
|
+
dialect-parametric integration suite reproducing the original failure.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# 0006. Raw DB errors surface as 500 — no SQLSTATE→HTTP mapping
|
|
2
|
+
|
|
3
|
+
- **Status**: accepted
|
|
4
|
+
- **Date**: 2026-07-23 (confirmed by the maintainer in June 2026)
|
|
5
|
+
|
|
6
|
+
## Context
|
|
7
|
+
|
|
8
|
+
Constraint violations (unique, foreign key, not-null) bubble up from the driver and reach
|
|
9
|
+
the client as a `500` carrying the raw database message. A review proposed mapping
|
|
10
|
+
SQLSTATE codes to HTTP semantics — unique violation → `409`, FK/not-null violation →
|
|
11
|
+
`400` — with sanitized messages.
|
|
12
|
+
|
|
13
|
+
## Decision
|
|
14
|
+
|
|
15
|
+
No SQLSTATE mapping in the engine. Errors the plugin itself detects (validation, missing
|
|
16
|
+
PK, not found, tenant violations) throw proper `4xx` via `httpError`; everything coming
|
|
17
|
+
from the database is passed through unmapped.
|
|
18
|
+
|
|
19
|
+
## Alternatives considered
|
|
20
|
+
|
|
21
|
+
- **SQLSTATE→HTTP mapping layer** — rejected: the mapping is dialect-specific
|
|
22
|
+
(PostgreSQL SQLSTATE vs MySQL errno), inevitably partial, and encodes product decisions
|
|
23
|
+
(is a duplicate a conflict to surface or an internal bug?) the plugin should not make.
|
|
24
|
+
- **Sanitizing DB messages** — rejected as a plugin default: hiding details is an
|
|
25
|
+
environment policy, and Fastify already gives consumers the hook for it.
|
|
26
|
+
|
|
27
|
+
## Consequences
|
|
28
|
+
|
|
29
|
+
- Constraint violations look like server errors (`500`) to API clients, and driver
|
|
30
|
+
messages may leak schema details (table/column/constraint names). Consumers who care
|
|
31
|
+
set a Fastify `setErrorHandler` — one place, their policy, their status codes.
|
|
32
|
+
- A consumer-facing "friendly duplicate error" is expected to be handled in `validate`
|
|
33
|
+
(explicit pre-check) or in the consumer's error handler, not by the engine guessing
|
|
34
|
+
intent from SQLSTATE.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# 0007. Table generation is explicit — `--all` is a migration helper, not the default
|
|
2
|
+
|
|
3
|
+
- **Status**: accepted
|
|
4
|
+
- **Date**: 2026-07-23
|
|
5
|
+
|
|
6
|
+
## Context
|
|
7
|
+
|
|
8
|
+
`sqlapi-generate-schema` with no arguments introspects every table, while
|
|
9
|
+
`sqlapi-generate-tables` with no arguments prints usage and exits, requiring `--all` or
|
|
10
|
+
explicit table names. A review flagged the opposite defaults as an inconsistency to
|
|
11
|
+
harmonize.
|
|
12
|
+
|
|
13
|
+
## Decision
|
|
14
|
+
|
|
15
|
+
The asymmetry is deliberate and stays. The two generators have different roles:
|
|
16
|
+
|
|
17
|
+
- **Schemas** are disposable mirrors of the database — regenerating all of them is always
|
|
18
|
+
correct, so "all" is the natural default.
|
|
19
|
+
- **Table files are the consumer's working set**: which tables become API endpoints is a
|
|
20
|
+
deliberate choice, and it is legitimate to keep tables out of `DbTables`. The intended
|
|
21
|
+
flow is *"I added a table, I generate its Table file explicitly"*
|
|
22
|
+
(`sqlapi-generate-tables customer`). If the default were "all", every run would
|
|
23
|
+
resurrect Table files the consumer had deliberately deleted, forcing them to delete the
|
|
24
|
+
same files again and again. `--all` exists only as a bootstrap helper when introducing
|
|
25
|
+
the plugin into an existing codebase.
|
|
26
|
+
|
|
27
|
+
## Alternatives considered
|
|
28
|
+
|
|
29
|
+
- **Default to `--all` (mirror generate-schema)** — rejected: re-creates deleted Table
|
|
30
|
+
files on every run, fighting the consumer's curation of the exposed surface.
|
|
31
|
+
- **Tracking deleted tables** (a skip-list) — rejected: state to maintain for a problem
|
|
32
|
+
explicit invocation solves for free.
|
|
33
|
+
|
|
34
|
+
## Consequences
|
|
35
|
+
|
|
36
|
+
- `sqlapi-generate-tables` keeps requiring `--all` or table names; running it bare prints
|
|
37
|
+
usage and exits non-zero.
|
|
38
|
+
- The generator never overwrites existing Table files, so both the explicit and the
|
|
39
|
+
`--all` flow are safe to re-run.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Architecture Decision Records
|
|
2
|
+
|
|
3
|
+
Deliberate design decisions of `fastify-auto-sqlapi`, with their rationale and the
|
|
4
|
+
alternatives that were considered and rejected.
|
|
5
|
+
|
|
6
|
+
**If you are an agent or a contributor about to "fix" one of the behaviors below: read the
|
|
7
|
+
matching ADR first.** These are not gaps or oversights — proposals that re-litigate a
|
|
8
|
+
decision recorded here should either reference new arguments the ADR does not address, or
|
|
9
|
+
be dropped.
|
|
10
|
+
|
|
11
|
+
ADRs are immutable: a decision is changed by adding a new ADR that supersedes the old one,
|
|
12
|
+
not by editing history.
|
|
13
|
+
|
|
14
|
+
## Index
|
|
15
|
+
|
|
16
|
+
| # | Decision |
|
|
17
|
+
|---|----------|
|
|
18
|
+
| [0001](./0001-no-orm-raw-sql.md) | No ORM — raw SQL with minimal dependencies |
|
|
19
|
+
| [0002](./0002-open-by-default.md) | Open by default — no imposed auth model |
|
|
20
|
+
| [0003](./0003-bulk-not-transactional.md) | Bulk operations are not transactional |
|
|
21
|
+
| [0004](./0004-updates-always-open.md) | Updates are always open — no `excludeFromUpdate` |
|
|
22
|
+
| [0005](./0005-insert-pipeline-sanitize-before-hooks.md) | Insert pipeline: client payload sanitized before `beforeInsert` |
|
|
23
|
+
| [0006](./0006-raw-db-errors.md) | Raw DB errors surface as 500 — no SQLSTATE→HTTP mapping |
|
|
24
|
+
| [0007](./0007-explicit-table-generation.md) | Table generation is explicit — `--all` is a migration helper, not the default |
|
|
25
|
+
|
|
26
|
+
## When to write one
|
|
27
|
+
|
|
28
|
+
Only when a decision was genuinely debated, or is counter-intuitive enough that a reasonable
|
|
29
|
+
outsider would mistake it for a bug. Plain conventions belong in [AGENTS.md](../../AGENTS.md).
|
|
30
|
+
|
|
31
|
+
## Format
|
|
32
|
+
|
|
33
|
+
```markdown
|
|
34
|
+
# NNNN. Title (imperative statement of the decision)
|
|
35
|
+
|
|
36
|
+
- **Status**: accepted | superseded by NNNN
|
|
37
|
+
- **Date**: YYYY-MM-DD
|
|
38
|
+
|
|
39
|
+
## Context
|
|
40
|
+
What problem or tension forced a choice.
|
|
41
|
+
|
|
42
|
+
## Decision
|
|
43
|
+
What was decided, stated plainly.
|
|
44
|
+
|
|
45
|
+
## Alternatives considered
|
|
46
|
+
Each rejected option and the reason it lost.
|
|
47
|
+
|
|
48
|
+
## Consequences
|
|
49
|
+
What follows — including the costs accepted knowingly.
|
|
50
|
+
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fastify-auto-sqlapi",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.11",
|
|
4
4
|
"description": "Fastify plugin for automatic CRUD API generation from PostgreSQL, MySQL and MariaDB tables",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
},
|
|
19
19
|
"files": [
|
|
20
20
|
"dist",
|
|
21
|
+
"docs",
|
|
21
22
|
"AGENTS.md",
|
|
22
23
|
"AGENTS_BACKEND.md",
|
|
23
24
|
"AGENTS_FRONTEND.md",
|