fastify-auto-sqlapi 0.2.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. package/AGENTS.md +50 -0
  2. package/AGENTS_BACKEND.md +312 -9
  3. package/AGENTS_FRONTEND.md +1 -1
  4. package/BREAKING_CHANGES.md +132 -0
  5. package/CHANGELOG.md +170 -0
  6. package/README.md +255 -877
  7. package/dist/bin/generate-schema.js +49 -2
  8. package/dist/index.d.ts +2 -2
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +1 -1
  11. package/dist/lib/agent/manifest.d.ts +2 -0
  12. package/dist/lib/agent/manifest.d.ts.map +1 -1
  13. package/dist/lib/agent/manifest.js +4 -0
  14. package/dist/lib/cli/mysql-introspect.d.ts.map +1 -1
  15. package/dist/lib/cli/mysql-introspect.js +15 -5
  16. package/dist/lib/cli/pg-introspect.d.ts.map +1 -1
  17. package/dist/lib/cli/pg-introspect.js +53 -14
  18. package/dist/lib/cli/schema-codegen.d.ts +2 -1
  19. package/dist/lib/cli/schema-codegen.d.ts.map +1 -1
  20. package/dist/lib/cli/schema-codegen.js +17 -4
  21. package/dist/lib/cli/tables-codegen.d.ts +4 -0
  22. package/dist/lib/cli/tables-codegen.d.ts.map +1 -1
  23. package/dist/lib/cli/tables-codegen.js +125 -27
  24. package/dist/lib/db.d.ts +6 -1
  25. package/dist/lib/db.d.ts.map +1 -1
  26. package/dist/lib/db.js +8 -2
  27. package/dist/lib/engine/bulk/bulk-upsert.d.ts.map +1 -1
  28. package/dist/lib/engine/bulk/bulk-upsert.js +6 -5
  29. package/dist/lib/engine/read-hooks.d.ts +19 -0
  30. package/dist/lib/engine/read-hooks.d.ts.map +1 -0
  31. package/dist/lib/engine/read-hooks.js +20 -0
  32. package/dist/lib/engine/rest/get.d.ts.map +1 -1
  33. package/dist/lib/engine/rest/get.js +10 -6
  34. package/dist/lib/engine/rest/insert.js +1 -1
  35. package/dist/lib/engine/rest/update.d.ts.map +1 -1
  36. package/dist/lib/engine/rest/update.js +15 -11
  37. package/dist/lib/engine/search/join-families.d.ts +11 -2
  38. package/dist/lib/engine/search/join-families.d.ts.map +1 -1
  39. package/dist/lib/engine/search/join-families.js +11 -13
  40. package/dist/lib/engine/search/search.d.ts.map +1 -1
  41. package/dist/lib/engine/search/search.js +14 -7
  42. package/dist/lib/engine/validate.d.ts +3 -3
  43. package/dist/lib/engine/validate.d.ts.map +1 -1
  44. package/dist/lib/engine/write-helpers.d.ts +7 -3
  45. package/dist/lib/engine/write-helpers.d.ts.map +1 -1
  46. package/dist/lib/engine/write-helpers.js +29 -30
  47. package/dist/lib/errors.d.ts +24 -0
  48. package/dist/lib/errors.d.ts.map +1 -1
  49. package/dist/lib/errors.js +59 -0
  50. package/dist/lib/schema/bulk-upsert.d.ts.map +1 -1
  51. package/dist/lib/schema/bulk-upsert.js +2 -1
  52. package/dist/lib/schema/get.d.ts +14 -0
  53. package/dist/lib/schema/get.d.ts.map +1 -0
  54. package/dist/lib/schema/get.js +18 -0
  55. package/dist/lib/schema/helpers.d.ts +21 -3
  56. package/dist/lib/schema/helpers.d.ts.map +1 -1
  57. package/dist/lib/schema/helpers.js +37 -5
  58. package/dist/lib/schema/insert.d.ts.map +1 -1
  59. package/dist/lib/schema/insert.js +7 -3
  60. package/dist/lib/schema/search.d.ts.map +1 -1
  61. package/dist/lib/schema/search.js +3 -11
  62. package/dist/lib/schema/update.d.ts.map +1 -1
  63. package/dist/lib/schema/update.js +7 -3
  64. package/dist/lib/sql-api.d.ts +9 -9
  65. package/dist/lib/sql-api.d.ts.map +1 -1
  66. package/dist/lib/sql-api.js +2 -1
  67. package/dist/lib/table-helpers.d.ts.map +1 -1
  68. package/dist/lib/table-helpers.js +169 -0
  69. package/dist/lib/tenant.d.ts +43 -8
  70. package/dist/lib/tenant.d.ts.map +1 -1
  71. package/dist/lib/tenant.js +169 -54
  72. package/dist/lib/write-access.d.ts +36 -0
  73. package/dist/lib/write-access.d.ts.map +1 -0
  74. package/dist/lib/write-access.js +58 -0
  75. package/dist/routes/auto/get.routes.d.ts.map +1 -1
  76. package/dist/routes/auto/get.routes.js +3 -2
  77. package/dist/routes/auto/plugin.d.ts.map +1 -1
  78. package/dist/routes/auto/plugin.js +8 -2
  79. package/dist/routes/auto/route-helpers.d.ts.map +1 -1
  80. package/dist/routes/auto/route-helpers.js +13 -4
  81. package/dist/routes/setup-swagger.d.ts.map +1 -0
  82. package/dist/{lib → routes}/sql-api-decorator.d.ts +1 -1
  83. package/dist/routes/sql-api-decorator.d.ts.map +1 -0
  84. package/dist/{lib → routes}/sql-api-decorator.js +4 -4
  85. package/dist/types/cli.d.ts +27 -1
  86. package/dist/types/cli.d.ts.map +1 -1
  87. package/dist/types/dml.d.ts +6 -4
  88. package/dist/types/dml.d.ts.map +1 -1
  89. package/dist/types/plugin.d.ts +22 -3
  90. package/dist/types/plugin.d.ts.map +1 -1
  91. package/dist/types/request.d.ts +24 -0
  92. package/dist/types/request.d.ts.map +1 -0
  93. package/dist/types/request.js +1 -0
  94. package/dist/types/schema.d.ts +15 -0
  95. package/dist/types/schema.d.ts.map +1 -1
  96. package/dist/types/search.d.ts +3 -0
  97. package/dist/types/search.d.ts.map +1 -1
  98. package/dist/types/table.d.ts +84 -11
  99. package/dist/types/table.d.ts.map +1 -1
  100. package/dist/types/tenant.d.ts +26 -2
  101. package/dist/types/tenant.d.ts.map +1 -1
  102. package/dist/types/validation.d.ts +3 -3
  103. package/dist/types/validation.d.ts.map +1 -1
  104. package/dist/types.d.ts +1 -0
  105. package/dist/types.d.ts.map +1 -1
  106. package/dist/types.js +1 -0
  107. package/docs/GLOSSARY.md +3 -1
  108. package/docs/adr/0004-updates-always-open.md +2 -1
  109. package/docs/adr/0006-raw-db-errors.md +2 -1
  110. package/docs/adr/0010-joins-do-not-run-route-guards.md +4 -1
  111. package/docs/adr/0013-sanitized-db-errors.md +114 -0
  112. package/docs/adr/0014-what-follows-a-write-join.md +84 -0
  113. package/docs/adr/0015-non-writable-columns.md +86 -0
  114. package/docs/adr/0016-a-view-is-a-table-config.md +92 -0
  115. package/docs/adr/0017-the-schema-is-the-contract.md +85 -0
  116. package/docs/adr/0018-one-seam-to-the-http-framework.md +68 -0
  117. package/docs/adr/README.md +6 -0
  118. package/package.json +1 -1
  119. package/dist/lib/setup-swagger.d.ts.map +0 -1
  120. package/dist/lib/sql-api-decorator.d.ts.map +0 -1
  121. /package/dist/{lib → routes}/setup-swagger.d.ts +0 -0
  122. /package/dist/{lib → routes}/setup-swagger.js +0 -0
@@ -1,4 +1,5 @@
1
- import { ensureSqlApiDecorator } from '../../lib/sql-api-decorator.js';
1
+ import { ensureSqlApiDecorator } from '../sql-api-decorator.js';
2
+ import { sanitizeRouteError } from '../../lib/errors.js';
2
3
  import { isCompositePrimary } from '../../types.js';
3
4
  function mergeOnRequests(options, tableConf) {
4
5
  return [...(options.onRequests || []), ...(tableConf.onRequests || [])];
@@ -75,9 +76,17 @@ export async function registerForAllTables(fastify, options, spec) {
75
76
  schema: buildRouteSchema(tableName, tableConf, spec, schemas),
76
77
  onRequest: mergeOnRequests(options, tableConf),
77
78
  handler: async (request, reply) => {
78
- const result = await spec.handle(fastify, tableName, tableConf, request, reply);
79
- if (!reply.sent)
80
- reply.status(spec.successStatus).send(result);
79
+ try {
80
+ const result = await spec.handle(fastify, tableName, tableConf, request, reply);
81
+ if (!reply.sent)
82
+ reply.status(spec.successStatus).send(result);
83
+ }
84
+ catch (err) {
85
+ const safe = sanitizeRouteError(err, request.id, options.exposeDebugInfo);
86
+ if (safe !== err)
87
+ request.log.error({ err }, 'sqlapi: unhandled error');
88
+ throw safe;
89
+ }
81
90
  },
82
91
  });
83
92
  }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup-swagger.d.ts","sourceRoot":"","sources":["../../src/routes/setup-swagger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,KAAK,EAAE,mBAAmB,EAAkB,MAAM,aAAa,CAAC;AAEvE,wBAAsB,YAAY,CAChC,OAAO,EAAE,eAAe,EACxB,OAAO,EAAE,IAAI,CAAC,mBAAmB,EAAE,SAAS,CAAC,GAC5C,OAAO,CAAC,IAAI,CAAC,CA6Bf"}
@@ -1,5 +1,5 @@
1
1
  import type { FastifyInstance } from 'fastify';
2
- import { type SqlApi } from './sql-api.js';
2
+ import { type SqlApi } from '../lib/sql-api.js';
3
3
  import type { SqlApiPluginOptions } from '../types.js';
4
4
  declare module 'fastify' {
5
5
  interface FastifyInstance {
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sql-api-decorator.d.ts","sourceRoot":"","sources":["../../src/routes/sql-api-decorator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAI/C,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC9D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAEvD,OAAO,QAAQ,SAAS,CAAC;IACvB,UAAU,eAAe;QACvB,MAAM,EAAE,MAAM,CAAC;KAChB;CACF;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,eAAe,EACxB,OAAO,EAAE,mBAAmB,GAC3B,IAAI,CA2BN"}
@@ -1,7 +1,7 @@
1
- import { createQueryClient } from './db.js';
2
- import { pgQueryable } from './adapters/pg-adapter.js';
3
- import { mysqlQueryable } from './adapters/mysql-adapter.js';
4
- import { createSqlApi } from './sql-api.js';
1
+ import { createQueryClient } from '../lib/db.js';
2
+ import { pgQueryable } from '../lib/adapters/pg-adapter.js';
3
+ import { mysqlQueryable } from '../lib/adapters/mysql-adapter.js';
4
+ import { createSqlApi } from '../lib/sql-api.js';
5
5
  /**
6
6
  * Decorates `fastify.sqlApi` (lazy getter) on the given instance unless the
7
7
  * decorator is already present in scope. The main plugin calls this at the
@@ -19,8 +19,30 @@ export interface ColumnInfo {
19
19
  is_nullable: string;
20
20
  /** True when the column is part of the table's PRIMARY KEY (from information_schema). */
21
21
  is_primary?: boolean;
22
- /** True for auto-generated columns without a visible default (mysql AUTO_INCREMENT). */
22
+ /**
23
+ * True for columns the database fills in by itself without a visible `column_default`:
24
+ * MySQL `AUTO_INCREMENT` and PostgreSQL `GENERATED ... AS IDENTITY`. Both behave like a
25
+ * default — omit the column and the value appears — so the generated field is Optional and
26
+ * the CLI lists it under `excludeFromCreation`.
27
+ */
23
28
  is_auto_increment?: boolean;
29
+ /**
30
+ * True for a column computed from an expression: `GENERATED ALWAYS AS (<expr>)`, STORED on
31
+ * PostgreSQL, VIRTUAL or STORED on MySQL. Unlike an identity column this one is never
32
+ * writable at all — naming it in an INSERT or an UPDATE is an error on both engines — so it
33
+ * is read-only for the whole API, not just at creation time.
34
+ */
35
+ is_generated?: boolean;
36
+ /**
37
+ * True when the relation is a view rather than a base table — `information_schema.tables`
38
+ * on both engines, plus `pg_class.relkind = 'm'` for PostgreSQL materialized views, which
39
+ * `information_schema` does not describe at all.
40
+ *
41
+ * A view is still a table config: what changes is what may be assumed about it. It carries
42
+ * no PRIMARY KEY constraint, so `is_primary` is false on every column, and only a view
43
+ * simple enough for the engine to make updatable accepts a write.
44
+ */
45
+ is_view?: boolean;
24
46
  }
25
47
  export interface TableMap {
26
48
  [schemaName: string]: {
@@ -29,6 +51,10 @@ export interface TableMap {
29
51
  colMap: Record<string, string>;
30
52
  /** PRIMARY KEY fields (camelCase), in table column order. Empty when unknown. */
31
53
  primary: string[];
54
+ /** Database-computed fields (camelCase), in table column order. Never writable. */
55
+ generated: string[];
56
+ /** True when this relation is a view (or a materialized view), not a base table. */
57
+ isView: boolean;
32
58
  };
33
59
  }
34
60
  //# sourceMappingURL=cli.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/types/cli.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,UAAU;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,yFAAyF;IACzF,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,wFAAwF;IACxF,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,MAAM,WAAW,QAAQ;IACvB,CAAC,UAAU,EAAE,MAAM,GAAG;QACpB,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC/B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC/B,iFAAiF;QACjF,OAAO,EAAE,MAAM,EAAE,CAAC;KACnB,CAAC;CACH"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/types/cli.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,UAAU;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,yFAAyF;IACzF,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;;;OAKG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;;;;;;OAQG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,QAAQ;IACvB,CAAC,UAAU,EAAE,MAAM,GAAG;QACpB,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC/B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC/B,iFAAiF;QACjF,OAAO,EAAE,MAAM,EAAE,CAAC;QAClB,mFAAmF;QACnF,SAAS,EAAE,MAAM,EAAE,CAAC;QACpB,oFAAoF;QACpF,MAAM,EAAE,OAAO,CAAC;KACjB,CAAC;CACH"}
@@ -1,4 +1,4 @@
1
- import type { FastifyRequest } from 'fastify';
1
+ import type { ApiRequest } from './request.js';
2
2
  import type { QueryClient } from '../lib/db.js';
3
3
  import type { ITable, DbTables } from './table.js';
4
4
  import type { TenantContext } from './tenant.js';
@@ -9,7 +9,7 @@ interface DmlBaseParams {
9
9
  }
10
10
  interface DmlWriteBaseParams extends DmlBaseParams {
11
11
  dbTables: DbTables;
12
- request: FastifyRequest;
12
+ request: ApiRequest;
13
13
  }
14
14
  export interface InsertParams extends DmlWriteBaseParams {
15
15
  record: Record<string, unknown>;
@@ -31,13 +31,15 @@ export interface UpdateResult {
31
31
  }
32
32
  export interface GetParams extends DmlBaseParams {
33
33
  id: string | number;
34
+ /** Handed to the `afterRead` hook; absent for a programmatic caller that did not pass one. */
35
+ request?: ApiRequest;
34
36
  }
35
37
  export interface GetResult {
36
38
  main: Record<string, unknown>;
37
39
  }
38
40
  export interface DeleteParams extends DmlBaseParams {
39
41
  id: string | number;
40
- request?: FastifyRequest;
42
+ request?: ApiRequest;
41
43
  }
42
44
  export interface DeleteResult {
43
45
  main: Record<string, unknown>;
@@ -57,7 +59,7 @@ export interface BulkUpsertResult {
57
59
  }
58
60
  export interface BulkDeleteParams extends DmlBaseParams {
59
61
  ids: (string | number)[];
60
- request?: FastifyRequest;
62
+ request?: ApiRequest;
61
63
  }
62
64
  export interface BulkDeleteResult {
63
65
  main: Record<string, unknown>;
@@ -1 +1 @@
1
- {"version":3,"file":"dml.d.ts","sourceRoot":"","sources":["../../src/types/dml.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"dml.d.ts","sourceRoot":"","sources":["../../src/types/dml.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import type { FastifyRequest, FastifyReply } from 'fastify';
1
+ import type { ApiRequest, ApiReply } from './request.js';
2
2
  import type { DialectName } from '../lib/dialect.js';
3
3
  import type { DbTables } from './table.js';
4
4
  import type { TenantId } from './tenant.js';
@@ -10,11 +10,11 @@ export interface SwaggerOptions {
10
10
  }
11
11
  export interface SqlApiPluginOptions {
12
12
  DbTables: DbTables;
13
- onRequests?: ((request: FastifyRequest, reply: FastifyReply) => Promise<void | FastifyReply>)[];
13
+ onRequests?: ((request: ApiRequest, reply: ApiReply) => Promise<void | ApiReply>)[];
14
14
  prefix?: string;
15
15
  swagger?: boolean | SwaggerOptions;
16
16
  dialect?: DialectName;
17
- getTenantId?: (request: FastifyRequest) => TenantId | TenantId[] | null | undefined | Promise<TenantId | TenantId[] | null | undefined>;
17
+ getTenantId?: (request: ApiRequest) => TenantId | TenantId[] | null | undefined | Promise<TenantId | TenantId[] | null | undefined>;
18
18
  debug?: boolean;
19
19
  /**
20
20
  * Hard cap on the search page size (the `itemsPerPage` query param), and the row LIMIT applied
@@ -35,6 +35,25 @@ export interface SqlApiPluginOptions {
35
35
  * routes run behind the same global `onRequests` hooks as the data routes.
36
36
  */
37
37
  agentManifest?: boolean;
38
+ /**
39
+ * Attach a `debugInfo` payload — the driver message, its `code`/`constraint`/`detail`,
40
+ * the stack — to the `500` an error the plugin did not raise itself produces (a
41
+ * constraint violation, a hook throwing without a `statusCode`).
42
+ *
43
+ * **Development only**: it hands whoever triggers an error a description of the schema,
44
+ * and the auto routes are open by default (ADR 0002). Off unless set. It is additive —
45
+ * `statusCode`, `error`, `message` and `requestId` keep the shape they have in production,
46
+ * so client-side error handling does not fork between environments.
47
+ *
48
+ * Without it the response carries no database detail, and the original error is still
49
+ * reachable: logged through `request.log.error` under the same `reqId` the body reports as
50
+ * `requestId`, and attached as the `cause` of the error a consumer `setErrorHandler`
51
+ * receives — which is where policy such as unique-violation → `409` belongs (ADR 0006).
52
+ *
53
+ * Nothing derives this: not `debug` (whoever registers the plugin decides that one, and
54
+ * may wire it to an environment variable), not `NODE_ENV`. See ADR 0013.
55
+ */
56
+ exposeDebugInfo?: boolean;
38
57
  }
39
58
  /** Default row cap for search (page size and no-paginator LIMIT). Override via `maxItemsPerPage`. */
40
59
  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;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"}
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/types/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACzD,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,UAAU,EAAE,KAAK,EAAE,QAAQ,KAAK,OAAO,CAAC,IAAI,GAAG,QAAQ,CAAC,CAAC,EAAE,CAAC;IACpF,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,UAAU,KAAK,QAAQ,GAAG,QAAQ,EAAE,GAAG,IAAI,GAAG,SAAS,GAC3E,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;IACxB;;;;;;;;;;;;;;;;;OAiBG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,qGAAqG;AACrG,eAAO,MAAM,0BAA0B,OAAO,CAAC;AAE/C,8EAA8E;AAC9E,eAAO,MAAM,sBAAsB,OAAO,CAAC"}
@@ -0,0 +1,24 @@
1
+ import type { FastifyRequest, FastifyReply } from 'fastify';
2
+ /**
3
+ * The request the plugin carries from a route down to consumer code.
4
+ *
5
+ * Every hook, `validate` and `getTenantId` receive it; **nothing inside `src/lib/` ever reads a
6
+ * property off it**. It is an opaque token that the HTTP layer produces and consumer code
7
+ * consumes, and the engines only forward — which is why the engines, the schema builders and
8
+ * the write pipeline hold no dependency on a web framework at all, only on this name.
9
+ *
10
+ * That is the point of the alias: it is the single seam where the plugin's core meets Fastify.
11
+ * A second HTTP adapter — or a core published apart from one — changes this file and nothing
12
+ * else, and `.dependency-cruiser.mjs` keeps it that way by refusing a `fastify` import anywhere
13
+ * under `src/lib/` or `src/types/` but here.
14
+ *
15
+ * It is a plain alias rather than a generic parameter on `ITable`: that type appears bare in
16
+ * over a hundred places, and threading `<Req>` through all of them — plus `DbTables`, plus
17
+ * every `*Params` — would change the public type surface today for a benefit that only
18
+ * materialises once the core ships as its own package. See
19
+ * [ADR 0018](../../docs/adr/0018-one-seam-to-the-http-framework.md).
20
+ */
21
+ export type ApiRequest = FastifyRequest;
22
+ /** The reply, which only the HTTP layer's own options need to name. */
23
+ export type ApiReply = FastifyReply;
24
+ //# sourceMappingURL=request.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request.d.ts","sourceRoot":"","sources":["../../src/types/request.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE5D;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,MAAM,UAAU,GAAG,cAAc,CAAC;AAExC,uEAAuE;AACvE,MAAM,MAAM,QAAQ,GAAG,YAAY,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -8,5 +8,20 @@ export interface SchemaDefinition<T = Record<string, TSchema>> {
8
8
  partialValidation: TObject;
9
9
  /** PRIMARY KEY fields (camelCase) as introspected from the DB. Set by generated schemas. */
10
10
  primaryKey?: string[];
11
+ /**
12
+ * Fields (camelCase) the database computes from an expression — `GENERATED ALWAYS AS (...)`.
13
+ * Set by generated schemas. They are readable like any other column but every write path
14
+ * drops them: the engine rejects a value for them, so offering the field could only ever
15
+ * turn a client mistake into a driver error.
16
+ */
17
+ generatedFields?: string[];
18
+ /**
19
+ * True when the underlying relation is a view. Set by generated schemas.
20
+ *
21
+ * A view is a table config like any other — this only records that the database had no
22
+ * PRIMARY KEY to report, so an absent `primaryKey` means "not declared" rather than
23
+ * "this data has no key".
24
+ */
25
+ isView?: boolean;
11
26
  }
12
27
  //# sourceMappingURL=schema.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/types/schema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAE1D,MAAM,WAAW,gBAAgB,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAC3D,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,MAAM,EAAE,CAAC,CAAC;IACV,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,4FAA4F;IAC5F,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB"}
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/types/schema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAE1D,MAAM,WAAW,gBAAgB,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAC3D,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,MAAM,EAAE,CAAC,CAAC;IACV,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,4FAA4F;IAC5F,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB;;;;;OAKG;IACH,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB"}
@@ -1,3 +1,4 @@
1
+ import type { ApiRequest } from './request.js';
1
2
  import type { QueryClient } from '../lib/db.js';
2
3
  import type { ITable, FilterRecord } from './table.js';
3
4
  import type { TenantContext } from './tenant.js';
@@ -35,6 +36,8 @@ export interface SearchParams {
35
36
  * sets it from `maxItemsPerPage`.
36
37
  */
37
38
  maxRows?: number;
39
+ /** Handed to the `afterRead` hook; absent for a programmatic caller that did not pass one. */
40
+ request?: ApiRequest;
38
41
  }
39
42
  export interface PaginationResult {
40
43
  total: number;
@@ -1 +1 @@
1
- {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/types/search.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AACvD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AACnF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEvD,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,WAAW,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,UAAU,CAAC,EAAE,eAAe,EAAE,CAAC;IAC/B,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IAC9C,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IAChD,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IAC7C,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IAC5C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACnD,SAAS,EAAE,SAAS,CAAC;CACtB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAChC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;IACzD,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;IACrD,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACpD,UAAU,CAAC,EAAE,gBAAgB,CAAC;CAC/B"}
1
+ {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/types/search.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AACvD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AACnF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEvD,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,WAAW,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,UAAU,CAAC,EAAE,eAAe,EAAE,CAAC;IAC/B,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IAC9C,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IAChD,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IAC7C,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IAC5C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,8FAA8F;IAC9F,OAAO,CAAC,EAAE,UAAU,CAAC;CACtB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACnD,SAAS,EAAE,SAAS,CAAC;CACtB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAChC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;IACzD,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;IACrD,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACpD,UAAU,CAAC,EAAE,gBAAgB,CAAC;CAC/B"}
@@ -1,6 +1,6 @@
1
1
  import type { Expression, ConditionBuilder, ConditionValueOrUndefined, DialectName as CbDialect } from 'node-condition-builder';
2
2
  import type { TSchema, Static } from '@sinclair/typebox';
3
- import type { FastifyRequest, FastifyReply } from 'fastify';
3
+ import type { ApiRequest, ApiReply } from './request.js';
4
4
  import type { QueryClient } from '../lib/db.js';
5
5
  import type { SchemaDefinition } from './schema.js';
6
6
  import type { ComputedFieldFn } from './computed.js';
@@ -23,6 +23,14 @@ export type ExtendedConditionFn = (condition: ConditionBuilder, filters: FilterR
23
23
  * resolve against the OUTER query.
24
24
  */
25
25
  export type TableFilterFn = (filters: FilterRecord, dialect?: CbDialect, qualifier?: string) => ConditionBuilder;
26
+ /** Which read path produced the rows an `afterRead` hook is handed. */
27
+ export type ReadSource = 'get' | 'search' | 'joinMultiple' | 'joinLeft';
28
+ /** Where the rows an `afterRead` hook receives came from. */
29
+ export interface AfterReadContext {
30
+ source: ReadSource;
31
+ /** The relation's alias, on the two join sources; absent on `get` and `search`. */
32
+ alias?: string;
33
+ }
26
34
  export interface ITable<F extends Record<string, TSchema> = Record<string, TSchema>> {
27
35
  primary: (string & keyof F) | (string & keyof F)[];
28
36
  Schema: SchemaDefinition<F>;
@@ -41,16 +49,37 @@ export interface ITable<F extends Record<string, TSchema> = Record<string, TSche
41
49
  allowedReadJoins?: JoinDefinition[];
42
50
  allowedWriteJoins?: JoinDefinition[];
43
51
  upsertMap?: Map<SchemaDefinition, string[]>;
52
+ /**
53
+ * Narrow a field's generated type where the DB could only describe it loosely: a column
54
+ * Postgres calls `text` is an email, a UUID, a URL; an `integer` has a range. The generated
55
+ * schema is as precise as `information_schema` allows, and this is where the rest goes.
56
+ *
57
+ * **Write bodies only.** An override is a rule about what this API accepts from now on, and
58
+ * the rows already in the table were written before it existed: a column narrowed to
59
+ * `format: 'email'` today holds whatever was accepted last year. Narrowing the response
60
+ * would publish a promise about data nobody can retroactively make true, so responses and
61
+ * the `filters` map stay on the generated type.
62
+ *
63
+ * The override is taken **verbatim**, because on the request side the schema is the rule: no
64
+ * `Type.Optional` means the field is mandatory and no `Nullable` means an explicit `null` is
65
+ * rejected, whatever the column allows. That is how a column the database had to leave
66
+ * nullable, because it was added to a populated table, is made mandatory going forward.
67
+ *
68
+ * It is not a re-typing mechanism. Nothing stops an override from turning a `number` into a
69
+ * `string`, but the value crossing the wire is still whatever the driver read from the
70
+ * column, so the declaration would simply be false. Transforming a value between storage and
71
+ * API is what `afterRead` (out) and `beforeInsert`/`beforeUpdate` (in) are for.
72
+ */
44
73
  schemaOverrides?: Partial<Record<string & keyof F, TSchema>>;
45
74
  validate?: ValidatorFn<F>;
46
75
  validateBulk?: BulkValidatorFn<F>;
47
- beforeInsert?: (db: QueryClient, req: FastifyRequest, record: {
76
+ beforeInsert?: (db: QueryClient, req: ApiRequest, record: {
48
77
  [K in keyof F]?: Static<F[K]> | Expression | null;
49
78
  }) => Promise<void>;
50
- beforeUpdate?: (db: QueryClient, req: FastifyRequest, fields: {
79
+ beforeUpdate?: (db: QueryClient, req: ApiRequest, fields: {
51
80
  [K in keyof F]?: Static<F[K]> | Expression | null;
52
81
  }, secondaryFieldsFetcher?: unknown) => void | Promise<void>;
53
- afterInsert?: (db: QueryClient, req: FastifyRequest, record: {
82
+ afterInsert?: (db: QueryClient, req: ApiRequest, record: {
54
83
  [K in keyof F]?: Static<F[K]>;
55
84
  }, secondaryRecords?: unknown) => Promise<void>;
56
85
  /**
@@ -59,9 +88,34 @@ export interface ITable<F extends Record<string, TSchema> = Record<string, TSche
59
88
  * adapter supports transactions). Receives the camelCase input record (including the
60
89
  * PK) plus the secondaries/deletions results, mirroring `afterInsert`.
61
90
  */
62
- afterUpdate?: (db: QueryClient, req: FastifyRequest, record: {
91
+ afterUpdate?: (db: QueryClient, req: ApiRequest, record: {
63
92
  [K in keyof F]?: Static<F[K]> | Expression | null;
64
93
  }, secondaryRecords?: unknown, deletionRecords?: unknown) => void | Promise<void>;
94
+ /**
95
+ * Runs on the rows a read returned, once per result set, before they become the response.
96
+ * The read-side counterpart of `beforeInsert`/`beforeUpdate`: the place to turn a stored
97
+ * representation back into the API one (decrypting a column, unpacking a blob).
98
+ *
99
+ * `rows` are camelCase records and are mutated **in place** — the return value is ignored.
100
+ * Rows cannot be added or removed: the pagination `COUNT` has already run, so a hook that
101
+ * dropped rows would report a `total` that does not match what it returned. Narrowing what
102
+ * a caller sees is `filters`, `tenantScope` and `readExclude`; hiding a field is
103
+ * `readExclude`.
104
+ *
105
+ * Declared on the table that OWNS the column, and it follows a join: the hook runs whenever
106
+ * that table's rows surface, including through a `joinMultiple` or `joinLeft` declared on
107
+ * another table (`ctx.source` and `ctx.alias` say which). It does not run on `joinGroup`,
108
+ * whose rows are aggregates rather than table rows.
109
+ *
110
+ * Not called when the read returned no rows. Whatever the hook writes is serialized against
111
+ * the response schema, so a transform that changes a field's JSON type needs a matching
112
+ * `schemaOverrides` entry — without it `fast-json-stringify` coerces the value silently.
113
+ *
114
+ * `req` is the request that triggered the read. Same caveat as `beforeDelete`:
115
+ * present through the auto-generated HTTP routes, `undefined` when a programmatic caller
116
+ * invokes `sqlApi.search()`/`sqlApi.get()` without passing one.
117
+ */
118
+ afterRead?: (db: QueryClient, req: ApiRequest | undefined, rows: Record<string, unknown>[], ctx: AfterReadContext) => void | Promise<void>;
65
119
  /**
66
120
  * Runs before a single record is deleted (DELETE /rest/:id). Throw to abort the
67
121
  * deletion (the thrown error's `statusCode`/`message` are surfaced to the client).
@@ -70,17 +124,17 @@ export interface ITable<F extends Record<string, TSchema> = Record<string, TSche
70
124
  * For tenant-scoped tables the hook only runs once ownership has been verified, so it
71
125
  * never fires for rows the caller cannot access.
72
126
  *
73
- * `req` is the Fastify request that triggered the operation. It is always present when
127
+ * `req` is the request that triggered the operation. It is always present when
74
128
  * the delete comes through the auto-generated HTTP route. It is `undefined` only if you
75
129
  * call `sqlApi.delete(table, id)` programmatically without passing a request — in that
76
130
  * case any hook that reads request context (e.g. `req.user`) must guard for it.
77
131
  */
78
- beforeDelete?: (db: QueryClient, req: FastifyRequest, id: string | number) => void | Promise<void>;
132
+ beforeDelete?: (db: QueryClient, req: ApiRequest, id: string | number) => void | Promise<void>;
79
133
  /**
80
134
  * Runs after a single record has been deleted (DELETE /rest/:id). Not called when the
81
135
  * record was not found (404). Same `req` caveat as `beforeDelete`.
82
136
  */
83
- afterDelete?: (db: QueryClient, req: FastifyRequest, id: string | number) => void | Promise<void>;
137
+ afterDelete?: (db: QueryClient, req: ApiRequest, id: string | number) => void | Promise<void>;
84
138
  /**
85
139
  * Bulk counterpart of `beforeDelete`, invoked ONCE with all ids before a bulk delete
86
140
  * (POST /bulk/:table/delete). Called once — not per id — to preserve the single-query
@@ -90,13 +144,13 @@ export interface ITable<F extends Record<string, TSchema> = Record<string, TSche
90
144
  * Same `req` caveat as `beforeDelete`: present via the HTTP route, `undefined` only when
91
145
  * `sqlApi.bulkDelete(table, ids)` is called programmatically without a request.
92
146
  */
93
- beforeBulkDelete?: (db: QueryClient, req: FastifyRequest, ids: (string | number)[]) => void | Promise<void>;
147
+ beforeBulkDelete?: (db: QueryClient, req: ApiRequest, ids: (string | number)[]) => void | Promise<void>;
94
148
  /**
95
149
  * Bulk counterpart of `afterDelete`, invoked ONCE after a bulk delete with the ids that
96
150
  * were ACTUALLY deleted (which may be a subset of the requested ids). Not called when
97
151
  * nothing was deleted. Same `req` caveat as `beforeBulkDelete`.
98
152
  */
99
- afterBulkDelete?: (db: QueryClient, req: FastifyRequest, deletedIds: (string | number)[]) => void | Promise<void>;
153
+ afterBulkDelete?: (db: QueryClient, req: ApiRequest, deletedIds: (string | number)[]) => void | Promise<void>;
100
154
  /**
101
155
  * Whitelist of auto-generated HTTP routes for this table. When omitted, ALL operations
102
156
  * are exposed (search, get, insert, update, delete, bulkUpsert, bulkDelete) — the
@@ -120,10 +174,29 @@ export interface ITable<F extends Record<string, TSchema> = Record<string, TSche
120
174
  * password hash). Primary-key fields cannot be excluded.
121
175
  */
122
176
  readExclude?: (string & keyof F)[];
177
+ /**
178
+ * Fields that no write may carry: removed from the insert, update and bulk-upsert bodies
179
+ * (so sending one is a 400, not a driver error) and dropped again in the engines, which
180
+ * `sqlApi.*` reaches without the HTTP schemas. Reads are untouched — the field stays
181
+ * projected, filterable and orderable.
182
+ *
183
+ * It is the write-side counterpart of `readExclude`, and it exists for columns the database
184
+ * refuses to be told about: a `GENERATED ALWAYS AS (<expr>)` column is already excluded
185
+ * automatically through the Schema's `generatedFields`, and this key covers the rest — a
186
+ * column a trigger owns, one a migration is about to drop.
187
+ *
188
+ * Not a per-caller permission: it is static and applies to everyone, admins included.
189
+ * Field-level rules that depend on who is asking belong in `beforeUpdate` (silent strip) or
190
+ * `validate` (loud 400). Not `excludeFromCreation` either — that one whitelists client input
191
+ * on insert only, and deliberately lets a hook assign the field afterwards.
192
+ *
193
+ * The primary key cannot be excluded: the update body identifies the row with it.
194
+ */
195
+ writeExclude?: (string & keyof F)[];
123
196
  defaultOrder?: string;
124
197
  excludeFromCreation?: (string & keyof F)[];
125
198
  distinctResults?: boolean;
126
- onRequests?: ((request: FastifyRequest, reply: FastifyReply) => Promise<void | FastifyReply>)[];
199
+ onRequests?: ((request: ApiRequest, reply: ApiReply) => Promise<void | ApiReply>)[];
127
200
  tenantScope?: TenantScope;
128
201
  }
129
202
  export type DbTables = Record<string, ITable<any>>;
@@ -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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"}
@@ -11,9 +11,33 @@ export interface TenantScopeIndirect {
11
11
  foreignField: string;
12
12
  };
13
13
  }
14
- export type TenantScope = TenantScopeDirect | TenantScopeIndirect;
14
+ /**
15
+ * A row owned by several parties, visible to any of them: a message (`sender_id` /
16
+ * `recipient_id`), a transfer (`from_account_id` / `to_account_id`), a shift swap. Reads test
17
+ * every listed column with an OR, so a row matches when *any* of them holds a tenant id.
18
+ *
19
+ * Writes cannot auto-inject a tenant the way a single-column scope does — the plugin has no way
20
+ * to know which party the caller is meant to be — so the payload must anchor the row itself:
21
+ * see `assertTenantAnchor`. Columns are DB column names, like `TenantScopeDirect.column`.
22
+ *
23
+ * Not combinable with `column`/`through`: an entry reached through a parent FK would need its
24
+ * own join per entry, and is rejected at `defineTable` time rather than half-supported.
25
+ */
26
+ export interface TenantScopeAnyOf {
27
+ anyOf: string[];
28
+ }
29
+ export type TenantScope = TenantScopeDirect | TenantScopeIndirect | TenantScopeAnyOf;
30
+ /**
31
+ * Who the caller is, for this request — plus how the table currently being addressed is scoped.
32
+ *
33
+ * `ids` is request-level: it is resolved once from `getTenantId` and stays valid for every table
34
+ * the request touches. `scope` is table-level and therefore **optional**: the addressed table may
35
+ * declare no scope of its own while a table it reaches through a relation does. Absent `scope`
36
+ * means "no filtering on this table", never "no tenant" — `tenantForTable` re-targets the same
37
+ * ids onto a related table's own scope.
38
+ */
15
39
  export interface TenantContext {
16
40
  ids: TenantId[];
17
- scope: TenantScope;
41
+ scope?: TenantScope;
18
42
  }
19
43
  //# sourceMappingURL=tenant.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tenant.d.ts","sourceRoot":"","sources":["../../src/types/tenant.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEpD,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,MAAM,CAAC;AAEvC,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE;QACP,MAAM,EAAE,gBAAgB,CAAC;QACzB,UAAU,EAAE,MAAM,CAAC;QACnB,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC;CACH;AAED,MAAM,MAAM,WAAW,GAAG,iBAAiB,GAAG,mBAAmB,CAAC;AAElE,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,QAAQ,EAAE,CAAC;IAChB,KAAK,EAAE,WAAW,CAAC;CACpB"}
1
+ {"version":3,"file":"tenant.d.ts","sourceRoot":"","sources":["../../src/types/tenant.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEpD,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,MAAM,CAAC;AAEvC,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE;QACP,MAAM,EAAE,gBAAgB,CAAC;QACzB,UAAU,EAAE,MAAM,CAAC;QACnB,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC;CACH;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,MAAM,MAAM,WAAW,GAAG,iBAAiB,GAAG,mBAAmB,GAAG,gBAAgB,CAAC;AAErF;;;;;;;;GAQG;AACH,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,QAAQ,EAAE,CAAC;IAChB,KAAK,CAAC,EAAE,WAAW,CAAC;CACrB"}
@@ -1,5 +1,5 @@
1
1
  import type { TSchema, Static } from '@sinclair/typebox';
2
- import type { FastifyRequest } from 'fastify';
2
+ import type { ApiRequest } from './request.js';
3
3
  import type { QueryClient } from '../lib/db.js';
4
4
  /**
5
5
  * Validation error tuple: [field, code] or [field, code, message].
@@ -8,7 +8,7 @@ import type { QueryClient } from '../lib/db.js';
8
8
  * - message: human-readable description (defaults to code if omitted)
9
9
  */
10
10
  export type ValidationError = [field: string, code: string] | [field: string, code: string, message: string];
11
- export type ValidatorFn<F extends Record<string, TSchema> = Record<string, TSchema>> = (db: QueryClient, req: FastifyRequest, main: {
11
+ export type ValidatorFn<F extends Record<string, TSchema> = Record<string, TSchema>> = (db: QueryClient, req: ApiRequest, main: {
12
12
  [K in keyof F]?: Static<F[K]>;
13
13
  }, secondaries?: Record<string, Record<string, unknown>[]>) => Promise<ValidationError[]> | ValidationError[];
14
14
  export interface BulkValidatorItem<F extends Record<string, TSchema> = Record<string, TSchema>> {
@@ -17,5 +17,5 @@ export interface BulkValidatorItem<F extends Record<string, TSchema> = Record<st
17
17
  };
18
18
  secondaries?: Record<string, Record<string, unknown>[]>;
19
19
  }
20
- export type BulkValidatorFn<F extends Record<string, TSchema> = Record<string, TSchema>> = (db: QueryClient, req: FastifyRequest, items: BulkValidatorItem<F>[]) => Promise<ValidationError[]> | ValidationError[];
20
+ export type BulkValidatorFn<F extends Record<string, TSchema> = Record<string, TSchema>> = (db: QueryClient, req: ApiRequest, items: BulkValidatorItem<F>[]) => Promise<ValidationError[]> | ValidationError[];
21
21
  //# sourceMappingURL=validation.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/types/validation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAC9C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD;;;;;GAKG;AACH,MAAM,MAAM,eAAe,GACvB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,GAC7B,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;AAEnD,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CACrF,EAAE,EAAE,WAAW,EACf,GAAG,EAAE,cAAc,EACnB,IAAI,EAAE;KAAG,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAAE,EACvC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,KACpD,OAAO,CAAC,eAAe,EAAE,CAAC,GAAG,eAAe,EAAE,CAAC;AAEpD,MAAM,WAAW,iBAAiB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAC5F,IAAI,EAAE;SAAG,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;KAAE,CAAC;IACxC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;CACzD;AAED,MAAM,MAAM,eAAe,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CACzF,EAAE,EAAE,WAAW,EACf,GAAG,EAAE,cAAc,EACnB,KAAK,EAAE,iBAAiB,CAAC,CAAC,CAAC,EAAE,KAC1B,OAAO,CAAC,eAAe,EAAE,CAAC,GAAG,eAAe,EAAE,CAAC"}
1
+ {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/types/validation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD;;;;;GAKG;AACH,MAAM,MAAM,eAAe,GACvB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,GAC7B,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;AAEnD,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CACrF,EAAE,EAAE,WAAW,EACf,GAAG,EAAE,UAAU,EACf,IAAI,EAAE;KAAG,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAAE,EACvC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,KACpD,OAAO,CAAC,eAAe,EAAE,CAAC,GAAG,eAAe,EAAE,CAAC;AAEpD,MAAM,WAAW,iBAAiB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAC5F,IAAI,EAAE;SAAG,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;KAAE,CAAC;IACxC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;CACzD;AAED,MAAM,MAAM,eAAe,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CACzF,EAAE,EAAE,WAAW,EACf,GAAG,EAAE,UAAU,EACf,KAAK,EAAE,iBAAiB,CAAC,CAAC,CAAC,EAAE,KAC1B,OAAO,CAAC,eAAe,EAAE,CAAC,GAAG,eAAe,EAAE,CAAC"}
package/dist/types.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export type { DialectName } from './lib/dialect.js';
2
+ export * from './types/request.js';
2
3
  export * from './types/cli.js';
3
4
  export * from './types/db.js';
4
5
  export * from './types/schema.js';
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAEA,YAAY,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,iBAAiB,CAAC;AAChC,cAAc,uBAAuB,CAAC;AACtC,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,gBAAgB,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAEA,YAAY,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,iBAAiB,CAAC;AAChC,cAAc,uBAAuB,CAAC;AACtC,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,gBAAgB,CAAC"}
package/dist/types.js CHANGED
@@ -1,3 +1,4 @@
1
+ export * from './types/request.js';
1
2
  export * from './types/cli.js';
2
3
  export * from './types/db.js';
3
4
  export * from './types/schema.js';
package/docs/GLOSSARY.md CHANGED
@@ -19,7 +19,9 @@ existing concept makes search fail and the concept get built twice.
19
19
  | Extra filter | Non-column search filter declared per table. (not: custom filter) |
20
20
  | Extended condition | Consumer-supplied SQL fragment merged into a search WHERE. |
21
21
  | Dotted notation | `orderBy`/condition key counted in parts: 1-part `field`, 2-part `alias.field`, 3-part `alias.fn.field`. (not: parti) |
22
- | Tenant scope | Row-level isolation: `direct` (own column) or `indirect` (via FK). |
22
+ | Tenant scope | Row-level isolation: `direct` (own column), `indirect` (via FK) or `anyOf`. |
23
+ | `anyOf` scope | Tenant scope over several owner columns: the row is visible to any of them. |
24
+ | Anchor | The owner column an `anyOf` write names to tie a new row to the caller. |
23
25
  | Engine | Dialect-agnostic function performing one operation. (not: service, handler) |
24
26
  | Route | Fastify plugin exposing an engine over HTTP. (not: controller) |
25
27
  | Dialect | Per-DB SQL differences (`postgres` \| `mysql`). |
@@ -1,6 +1,7 @@
1
1
  # 0004. Updates are always open — no `excludeFromUpdate`
2
2
 
3
- - **Status**: accepted
3
+ - **Status**: accepted (narrowed by [0015](./0015-non-writable-columns.md) for columns the
4
+ database itself refuses to be written)
4
5
  - **Date**: 2026-07-23
5
6
 
6
7
  ## Context