fastify-auto-sqlapi 0.1.14 → 0.2.1

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 (101) hide show
  1. package/AGENTS_BACKEND.md +52 -7
  2. package/BREAKING_CHANGES.md +86 -5
  3. package/CHANGELOG.md +57 -0
  4. package/README.md +110 -3
  5. package/dist/bin/generate-schema.js +89 -77
  6. package/dist/bin/generate-tables.js +72 -62
  7. package/dist/bin/utils.d.ts.map +1 -1
  8. package/dist/bin/utils.js +47 -33
  9. package/dist/index.d.ts +1 -1
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/lib/agent/manifest.d.ts +3 -2
  12. package/dist/lib/agent/manifest.d.ts.map +1 -1
  13. package/dist/lib/agent/manifest.js +102 -80
  14. package/dist/lib/cli/mysql-introspect.d.ts +4 -9
  15. package/dist/lib/cli/mysql-introspect.d.ts.map +1 -1
  16. package/dist/lib/cli/mysql-introspect.js +8 -7
  17. package/dist/lib/cli/pg-introspect.d.ts.map +1 -1
  18. package/dist/lib/cli/pg-introspect.js +0 -1
  19. package/dist/lib/cli/schema-codegen.d.ts +3 -0
  20. package/dist/lib/cli/schema-codegen.d.ts.map +1 -1
  21. package/dist/lib/cli/schema-codegen.js +3 -0
  22. package/dist/lib/cli/tables-codegen.d.ts +8 -1
  23. package/dist/lib/cli/tables-codegen.d.ts.map +1 -1
  24. package/dist/lib/cli/tables-codegen.js +100 -118
  25. package/dist/lib/condition-methods.d.ts +14 -4
  26. package/dist/lib/condition-methods.d.ts.map +1 -1
  27. package/dist/lib/condition-methods.js +18 -4
  28. package/dist/lib/db.d.ts +7 -2
  29. package/dist/lib/db.d.ts.map +1 -1
  30. package/dist/lib/db.js +24 -6
  31. package/dist/lib/dialect.d.ts +2 -1
  32. package/dist/lib/dialect.d.ts.map +1 -1
  33. package/dist/lib/engine/bulk/bulk-upsert.d.ts.map +1 -1
  34. package/dist/lib/engine/bulk/bulk-upsert.js +44 -36
  35. package/dist/lib/engine/rest/delete.d.ts.map +1 -1
  36. package/dist/lib/engine/rest/get.d.ts.map +1 -1
  37. package/dist/lib/engine/rest/get.js +5 -4
  38. package/dist/lib/engine/rest/insert.d.ts.map +1 -1
  39. package/dist/lib/engine/rest/insert.js +1 -1
  40. package/dist/lib/engine/rest/update.d.ts.map +1 -1
  41. package/dist/lib/engine/rest/update.js +26 -19
  42. package/dist/lib/engine/search/aggregations.d.ts +33 -0
  43. package/dist/lib/engine/search/aggregations.d.ts.map +1 -0
  44. package/dist/lib/engine/search/aggregations.js +109 -0
  45. package/dist/lib/engine/search/conditions.d.ts +31 -0
  46. package/dist/lib/engine/search/conditions.d.ts.map +1 -0
  47. package/dist/lib/engine/search/conditions.js +131 -0
  48. package/dist/lib/engine/search/fields.d.ts +49 -0
  49. package/dist/lib/engine/search/fields.d.ts.map +1 -0
  50. package/dist/lib/engine/search/fields.js +142 -0
  51. package/dist/lib/engine/search/join-families.d.ts +35 -0
  52. package/dist/lib/engine/search/join-families.d.ts.map +1 -0
  53. package/dist/lib/engine/search/join-families.js +345 -0
  54. package/dist/lib/engine/search/joins.d.ts +55 -0
  55. package/dist/lib/engine/search/joins.d.ts.map +1 -0
  56. package/dist/lib/engine/search/joins.js +81 -0
  57. package/dist/lib/engine/search/order-by.d.ts +35 -0
  58. package/dist/lib/engine/search/order-by.d.ts.map +1 -0
  59. package/dist/lib/engine/search/order-by.js +104 -0
  60. package/dist/lib/engine/search/search.d.ts.map +1 -1
  61. package/dist/lib/engine/search/search.js +106 -791
  62. package/dist/lib/engine/validate.d.ts +0 -5
  63. package/dist/lib/engine/validate.d.ts.map +1 -1
  64. package/dist/lib/engine/write-helpers.d.ts +25 -17
  65. package/dist/lib/engine/write-helpers.d.ts.map +1 -1
  66. package/dist/lib/engine/write-helpers.js +86 -44
  67. package/dist/lib/errors.d.ts +24 -0
  68. package/dist/lib/errors.d.ts.map +1 -1
  69. package/dist/lib/errors.js +59 -0
  70. package/dist/lib/read-access.d.ts +12 -1
  71. package/dist/lib/read-access.d.ts.map +1 -1
  72. package/dist/lib/read-access.js +20 -4
  73. package/dist/lib/schema/helpers.d.ts +0 -15
  74. package/dist/lib/schema/helpers.d.ts.map +1 -1
  75. package/dist/lib/schema/helpers.js +7 -4
  76. package/dist/lib/schema/search.d.ts.map +1 -1
  77. package/dist/lib/schema/search.js +107 -128
  78. package/dist/lib/sql-api.d.ts.map +1 -1
  79. package/dist/lib/table-helpers.d.ts.map +1 -1
  80. package/dist/lib/table-helpers.js +48 -0
  81. package/dist/lib/tenant.d.ts +66 -5
  82. package/dist/lib/tenant.d.ts.map +1 -1
  83. package/dist/lib/tenant.js +209 -48
  84. package/dist/routes/auto/plugin.d.ts.map +1 -1
  85. package/dist/routes/auto/plugin.js +6 -0
  86. package/dist/routes/auto/route-helpers.d.ts +0 -7
  87. package/dist/routes/auto/route-helpers.d.ts.map +1 -1
  88. package/dist/routes/auto/route-helpers.js +58 -33
  89. package/dist/routes/auto/search.routes.js +0 -1
  90. package/dist/types/plugin.d.ts +19 -0
  91. package/dist/types/plugin.d.ts.map +1 -1
  92. package/dist/types/table.d.ts +1 -1
  93. package/dist/types/tenant.d.ts +26 -2
  94. package/dist/types/tenant.d.ts.map +1 -1
  95. package/docs/GLOSSARY.md +30 -0
  96. package/docs/adr/0006-raw-db-errors.md +2 -1
  97. package/docs/adr/0010-joins-do-not-run-route-guards.md +4 -1
  98. package/docs/adr/0012-anti-duplication-tooling.md +59 -0
  99. package/docs/adr/0013-sanitized-db-errors.md +114 -0
  100. package/docs/adr/README.md +4 -1
  101. package/package.json +29 -2
@@ -40,6 +40,65 @@ export function tableOperations(tableConf) {
40
40
  return [...requested];
41
41
  return requested.filter((op) => !SINGLE_PK_OPERATIONS.has(op));
42
42
  }
43
+ /** Field descriptors: type, plus the flags an agent needs to build a valid payload. */
44
+ function manifestFields(tableConf) {
45
+ const schema = tableConf.Schema;
46
+ const required = new Set((schema.validation.required) ?? []);
47
+ const readExcluded = new Set(tableConf.readExclude ?? []);
48
+ const fields = {};
49
+ for (const [name, fieldSchema] of Object.entries(schema.fields)) {
50
+ const f = { type: shortType(fieldSchema) };
51
+ if (required.has(name))
52
+ f.required = true;
53
+ if (isNullable(fieldSchema))
54
+ f.nullable = true;
55
+ if (readExcluded.has(name))
56
+ f.writeOnly = true;
57
+ fields[name] = f;
58
+ }
59
+ return fields;
60
+ }
61
+ function manifestJoins(joins) {
62
+ return (joins ?? []).map((j) => ({
63
+ alias: j.alias,
64
+ table: j.joinSchema.tableName,
65
+ kind: j.unique ? 'N:1' : '1:N',
66
+ }));
67
+ }
68
+ function manifestExtraFilters(tableConf) {
69
+ const entries = Object.entries(tableConf.extraFilters ?? {});
70
+ if (entries.length === 0)
71
+ return undefined;
72
+ return Object.fromEntries(entries.map(([n, s]) => [n, shortType(s)]));
73
+ }
74
+ /** One table's entry. Optional keys are omitted entirely when empty, to keep the prompt small. */
75
+ function manifestTable(tableConf) {
76
+ const entry = {
77
+ table: tableConf.Schema.tableName,
78
+ primary: tableConf.primary,
79
+ operations: tableOperations(tableConf),
80
+ fields: manifestFields(tableConf),
81
+ };
82
+ const computed = computedTypes(tableConf);
83
+ const extraFilters = manifestExtraFilters(tableConf);
84
+ const readJoins = manifestJoins(tableConf.allowedReadJoins);
85
+ const writeJoins = manifestJoins(tableConf.allowedWriteJoins);
86
+ if (tableConf.excludeFromCreation?.length)
87
+ entry.serverGenerated = [...tableConf.excludeFromCreation];
88
+ if (computed)
89
+ entry.computed = computed;
90
+ if (extraFilters)
91
+ entry.extraFilters = extraFilters;
92
+ if (readJoins.length)
93
+ entry.readJoins = readJoins;
94
+ if (writeJoins.length)
95
+ entry.writeJoins = writeJoins;
96
+ if (tableConf.tenantScope)
97
+ entry.tenantScoped = true;
98
+ if (tableConf.defaultOrder)
99
+ entry.defaultOrder = tableConf.defaultOrder;
100
+ return entry;
101
+ }
43
102
  /**
44
103
  * Build the machine-readable description of every table exposed by this deployment:
45
104
  * fields (type/required/nullable), enabled operations, join aliases, computed fields,
@@ -50,55 +109,7 @@ export function tableOperations(tableConf) {
50
109
  export function buildAgentManifest(dbTables) {
51
110
  const tables = {};
52
111
  for (const [key, tableConf] of Object.entries(dbTables)) {
53
- const schema = tableConf.Schema;
54
- const required = new Set((schema.validation.required) ?? []);
55
- const readExcluded = new Set(tableConf.readExclude ?? []);
56
- const fields = {};
57
- for (const [name, fieldSchema] of Object.entries(schema.fields)) {
58
- const f = { type: shortType(fieldSchema) };
59
- if (required.has(name))
60
- f.required = true;
61
- if (isNullable(fieldSchema))
62
- f.nullable = true;
63
- if (readExcluded.has(name))
64
- f.writeOnly = true;
65
- fields[name] = f;
66
- }
67
- const readJoins = (tableConf.allowedReadJoins ?? []).map((j) => ({
68
- alias: j.alias,
69
- table: j.joinSchema.tableName,
70
- kind: j.unique ? 'N:1' : '1:N',
71
- }));
72
- const writeJoins = (tableConf.allowedWriteJoins ?? []).map((j) => ({
73
- alias: j.alias,
74
- table: j.joinSchema.tableName,
75
- kind: j.unique ? 'N:1' : '1:N',
76
- }));
77
- const extraFilters = Object.keys(tableConf.extraFilters ?? {}).length
78
- ? Object.fromEntries(Object.entries(tableConf.extraFilters).map(([n, s]) => [n, shortType(s)]))
79
- : undefined;
80
- const entry = {
81
- table: schema.tableName,
82
- primary: tableConf.primary,
83
- operations: tableOperations(tableConf),
84
- fields,
85
- };
86
- if (tableConf.excludeFromCreation?.length)
87
- entry.serverGenerated = [...tableConf.excludeFromCreation];
88
- const computed = computedTypes(tableConf);
89
- if (computed)
90
- entry.computed = computed;
91
- if (extraFilters)
92
- entry.extraFilters = extraFilters;
93
- if (readJoins.length)
94
- entry.readJoins = readJoins;
95
- if (writeJoins.length)
96
- entry.writeJoins = writeJoins;
97
- if (tableConf.tenantScope)
98
- entry.tenantScoped = true;
99
- if (tableConf.defaultOrder)
100
- entry.defaultOrder = tableConf.defaultOrder;
101
- tables[key] = entry;
112
+ tables[key] = manifestTable(tableConf);
102
113
  }
103
114
  return { tables };
104
115
  }
@@ -119,37 +130,48 @@ export function renderAgentManifestMd(manifest) {
119
130
  '',
120
131
  ];
121
132
  for (const t of Object.values(manifest.tables)) {
122
- const pk = Array.isArray(t.primary) ? t.primary.join('+') : t.primary;
123
- const flags = t.tenantScoped ? ' [tenant-scoped]' : '';
124
- lines.push(`## ${t.table} PK:${pk} ops:${t.operations.join(',')}${flags}`);
125
- const fieldParts = Object.entries(t.fields).map(([name, f]) => {
126
- let s = `${name}:${f.type}`;
127
- if (f.required)
128
- s += '!';
129
- if (f.nullable)
130
- s += '?';
131
- if (f.writeOnly)
132
- s += '(writeOnly)';
133
- return s;
134
- });
135
- lines.push(`fields: ${fieldParts.join(', ')}`);
136
- if (t.serverGenerated)
137
- lines.push(`serverGenerated: ${t.serverGenerated.join(', ')}`);
138
- if (t.computed) {
139
- lines.push(`computed: ${Object.entries(t.computed).map(([n, ty]) => `${n}:${ty}`).join(', ')}`);
140
- }
141
- if (t.extraFilters) {
142
- lines.push(`extraFilters: ${Object.entries(t.extraFilters).map(([n, ty]) => `${n}:${ty}`).join(', ')}`);
143
- }
144
- if (t.readJoins) {
145
- lines.push(`readJoins: ${t.readJoins.map((j) => `${j.alias}→${j.table}(${j.kind})`).join(', ')}`);
146
- }
147
- if (t.writeJoins) {
148
- lines.push(`writeJoins: ${t.writeJoins.map((j) => `${j.alias}→${j.table}`).join(', ')}`);
149
- }
150
- if (t.defaultOrder)
151
- lines.push(`defaultOrder: ${t.defaultOrder}`);
152
- lines.push('');
133
+ lines.push(...renderTableMd(t));
153
134
  }
154
135
  return lines.join('\n');
155
136
  }
137
+ /** `name:type` pairs — the compact form used throughout the manifest. */
138
+ function namedTypes(map) {
139
+ return Object.entries(map).map(([n, ty]) => `${n}:${ty}`).join(', ');
140
+ }
141
+ /** A field's type with its notation suffixes: `!` required, `?` nullable, `(writeOnly)`. */
142
+ function fieldNotation(f) {
143
+ let s = f.type;
144
+ if (f.required)
145
+ s += '!';
146
+ if (f.nullable)
147
+ s += '?';
148
+ if (f.writeOnly)
149
+ s += '(writeOnly)';
150
+ return s;
151
+ }
152
+ function renderTableMd(t) {
153
+ const pk = Array.isArray(t.primary) ? t.primary.join('+') : t.primary;
154
+ const flags = t.tenantScoped ? ' [tenant-scoped]' : '';
155
+ const ops = t.operations.join(',');
156
+ const lines = [`## ${t.table} PK:${pk} ops:${ops}${flags}`];
157
+ const fieldParts = Object.entries(t.fields).map(([name, f]) => `${name}:${fieldNotation(f)}`);
158
+ lines.push(`fields: ${fieldParts.join(', ')}`);
159
+ if (t.serverGenerated)
160
+ lines.push(`serverGenerated: ${t.serverGenerated.join(', ')}`);
161
+ if (t.computed)
162
+ lines.push(`computed: ${namedTypes(t.computed)}`);
163
+ if (t.extraFilters)
164
+ lines.push(`extraFilters: ${namedTypes(t.extraFilters)}`);
165
+ if (t.readJoins) {
166
+ const readJoins = t.readJoins.map((j) => `${j.alias}→${j.table}(${j.kind})`);
167
+ lines.push(`readJoins: ${readJoins.join(', ')}`);
168
+ }
169
+ if (t.writeJoins) {
170
+ const writeJoins = t.writeJoins.map((j) => `${j.alias}→${j.table}`);
171
+ lines.push(`writeJoins: ${writeJoins.join(', ')}`);
172
+ }
173
+ if (t.defaultOrder)
174
+ lines.push(`defaultOrder: ${t.defaultOrder}`);
175
+ lines.push('');
176
+ return lines;
177
+ }
@@ -1,16 +1,11 @@
1
1
  import type { ColumnInfo } from '../../types.js';
2
- export declare function buildMysqlConnectionConfig(): {
2
+ export interface MysqlConnectionConfig {
3
3
  host: string;
4
4
  port: number;
5
5
  user: string;
6
6
  password: string;
7
7
  database: string;
8
- };
9
- export declare function introspectMysqlTables(connectionConfig: {
10
- host: string;
11
- port: number;
12
- user: string;
13
- password: string;
14
- database: string;
15
- }, schema: string): Promise<ColumnInfo[]>;
8
+ }
9
+ export declare function buildMysqlConnectionConfig(): MysqlConnectionConfig;
10
+ export declare function introspectMysqlTables(connectionConfig: MysqlConnectionConfig, schema: string): Promise<ColumnInfo[]>;
16
11
  //# sourceMappingURL=mysql-introspect.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"mysql-introspect.d.ts","sourceRoot":"","sources":["../../../src/lib/cli/mysql-introspect.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAEjD,wBAAgB,0BAA0B,IAAI;IAC5C,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAmBA;AAkDD,wBAAsB,qBAAqB,CACzC,gBAAgB,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,EAClG,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,UAAU,EAAE,CAAC,CA2BvB"}
1
+ {"version":3,"file":"mysql-introspect.d.ts","sourceRoot":"","sources":["../../../src/lib/cli/mysql-introspect.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAEjD,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAoCD,wBAAgB,0BAA0B,IAAI,qBAAqB,CAmBlE;AAkDD,wBAAsB,qBAAqB,CACzC,gBAAgB,EAAE,qBAAqB,EACvC,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,UAAU,EAAE,CAAC,CA4BvB"}
@@ -66,7 +66,6 @@ function mapMysqlType(dataType) {
66
66
  }
67
67
  }
68
68
  export async function introspectMysqlTables(connectionConfig, schema) {
69
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
70
69
  const mysql2 = loadOptionalDependency('mysql2/promise', 'npm install mysql2');
71
70
  const connection = await mysql2.createConnection(connectionConfig);
72
71
  try {
@@ -74,13 +73,15 @@ export async function introspectMysqlTables(connectionConfig, schema) {
74
73
  FROM information_schema.columns
75
74
  WHERE table_schema = ?
76
75
  ORDER BY table_name, ordinal_position`, [schema]);
77
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
76
+ // `||` is kept as-is from the untyped version: this change types the driver, it does
77
+ // not alter behaviour. The trailing fallbacks only satisfy ColumnInfo, which cannot
78
+ // hold `undefined`.
78
79
  return rows.map((row) => ({
79
- table_name: row.TABLE_NAME || row.table_name,
80
- column_name: row.COLUMN_NAME || row.column_name,
81
- udt_name: mapMysqlType(row.DATA_TYPE || row.data_type),
82
- column_default: row.COLUMN_DEFAULT || row.column_default,
83
- is_nullable: row.IS_NULLABLE || row.is_nullable,
80
+ table_name: row.TABLE_NAME || row.table_name || '',
81
+ column_name: row.COLUMN_NAME || row.column_name || '',
82
+ udt_name: mapMysqlType(row.DATA_TYPE || row.data_type || ''),
83
+ column_default: row.COLUMN_DEFAULT || row.column_default || null,
84
+ is_nullable: row.IS_NULLABLE || row.is_nullable || '',
84
85
  is_primary: (row.COLUMN_KEY || row.column_key) === 'PRI',
85
86
  is_auto_increment: String(row.EXTRA || row.extra || '').includes('auto_increment'),
86
87
  }));
@@ -1 +1 @@
1
- {"version":3,"file":"pg-introspect.d.ts","sourceRoot":"","sources":["../../../src/lib/cli/pg-introspect.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAEjD,wBAAgB,qBAAqB,IAAI,MAAM,CAY9C;AAED,wBAAsB,gBAAgB,CACpC,gBAAgB,EAAE,MAAM,EACxB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,UAAU,EAAE,CAAC,CA8BvB"}
1
+ {"version":3,"file":"pg-introspect.d.ts","sourceRoot":"","sources":["../../../src/lib/cli/pg-introspect.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAmBjD,wBAAgB,qBAAqB,IAAI,MAAM,CAY9C;AAED,wBAAsB,gBAAgB,CACpC,gBAAgB,EAAE,MAAM,EACxB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,UAAU,EAAE,CAAC,CA6BvB"}
@@ -11,7 +11,6 @@ export function buildConnectionString() {
11
11
  return `postgres://${user}:${password}@${host}:${port}/${db}`;
12
12
  }
13
13
  export async function introspectTables(connectionString, schema) {
14
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
15
14
  const pg = loadOptionalDependency('pg', 'npm install pg');
16
15
  const client = new pg.Client({ connectionString });
17
16
  try {
@@ -1,4 +1,7 @@
1
1
  import type { ColumnInfo, TableMap } from '../../types.js';
2
+ /** Maps a DB column type to its TypeBox schema expression.
3
+ * @testonly Exported only so unit tests can exercise it directly.
4
+ */
2
5
  export declare function convertColType(udtName: string, columnInfo: {
3
6
  column_default: string | null;
4
7
  is_nullable: string;
@@ -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,CA0DR;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,QAAQ,CAqC1D;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,QAAQ,EAAE,aAAa,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAoBzF;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"}
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;;GAEG;AACH,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;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,QAAQ,EAAE,aAAa,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAoBzF;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"}
@@ -1,4 +1,7 @@
1
1
  import { toCamelCase, toSchemaName } from '../naming.js';
2
+ /** Maps a DB column type to its TypeBox schema expression.
3
+ * @testonly Exported only so unit tests can exercise it directly.
4
+ */
2
5
  export function convertColType(udtName, columnInfo) {
3
6
  // AUTO_INCREMENT (mysql) behaves like a default: the DB generates the value.
4
7
  const hasDefault = columnInfo.column_default || columnInfo.is_auto_increment;
@@ -6,15 +6,22 @@ export interface ParsedSchema {
6
6
  /** PRIMARY KEY fields declared in the schema file (`primaryKey: [...]`), when present. */
7
7
  primary?: string[];
8
8
  }
9
- export interface DetectedRelation {
9
+ interface DetectedRelation {
10
10
  parentSchemaName: string;
11
11
  parentField: string;
12
12
  childSchemaName: string;
13
13
  childField: string;
14
14
  }
15
15
  export declare function parseSchemaFile(content: string): ParsedSchema | null;
16
+ /** Infers FK relations between parsed schemas.
17
+ * @testonly Exported only so unit tests can exercise it directly.
18
+ */
16
19
  export declare function detectRelations(schemas: ParsedSchema[]): DetectedRelation[];
17
20
  export declare function generateSingleTableFile(schema: ParsedSchema, allSchemas: ParsedSchema[]): string;
18
21
  export declare function generateDbTablesIndex(schemas: ParsedSchema[]): string;
22
+ /** Legacy single-file DbTables generator.
23
+ * @testonly Exported only so unit tests can exercise it directly.
24
+ */
19
25
  export declare function generateTablesFile(schemas: ParsedSchema[]): string;
26
+ export {};
20
27
  //# sourceMappingURL=tables-codegen.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tables-codegen.d.ts","sourceRoot":"","sources":["../../../src/lib/cli/tables-codegen.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,0FAA0F;IAC1F,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;CACpB;AAID,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAuCpE;AAuCD,wBAAgB,eAAe,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,gBAAgB,EAAE,CAwB3E;AAID,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,GAAG,MAAM,CA6HhG;AAID,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,MAAM,CAqBrE;AAID,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,MAAM,CA6FlE"}
1
+ {"version":3,"file":"tables-codegen.d.ts","sourceRoot":"","sources":["../../../src/lib/cli/tables-codegen.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,0FAA0F;IAC1F,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,UAAU,gBAAgB;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;CACpB;AAID,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAuCpE;AAyCD;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,gBAAgB,EAAE,CAwB3E;AAyED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,GAAG,MAAM,CAgFhG;AAID,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,MAAM,CAqBrE;AA8CD;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,MAAM,CAoClE"}
@@ -1,13 +1,13 @@
1
1
  import { toUnderscore } from '../naming.js';
2
2
  // ─── Parsing ─────────────────────────────────────────────────
3
3
  export function parseSchemaFile(content) {
4
- const tableNameMatch = content.match(/tableName:\s*"([^"]+)"/);
4
+ const tableNameMatch = /tableName:\s*"([^"]+)"/.exec(content);
5
5
  if (!tableNameMatch)
6
6
  return null;
7
- const exportMatch = content.match(/export const (Schema\w+)\s*=\s*Schema/);
7
+ const exportMatch = /export const (Schema\w+)\s*=\s*Schema/.exec(content);
8
8
  if (!exportMatch)
9
9
  return null;
10
- const schemaBlockMatch = content.match(/const _Schema\s*=\s*\{([\s\S]*?)};/);
10
+ const schemaBlockMatch = /const _Schema\s*=\s*\{([\s\S]*?)};/.exec(content);
11
11
  if (!schemaBlockMatch)
12
12
  return null;
13
13
  const fields = [];
@@ -22,7 +22,7 @@ export function parseSchemaFile(content) {
22
22
  return null;
23
23
  // primaryKey: ["code"] — emitted by generate-schema from DB introspection
24
24
  let primary;
25
- const pkMatch = content.match(/primaryKey:\s*\[([^\]]*)\]/);
25
+ const pkMatch = /primaryKey:\s*\[([^\]]*)\]/.exec(content);
26
26
  if (pkMatch) {
27
27
  primary = pkMatch[1]
28
28
  .split(',')
@@ -64,12 +64,18 @@ function pkFirst(pk) {
64
64
  }
65
65
  /** Render the `primary:` value for the defineTable template. */
66
66
  function formatPrimary(pk) {
67
- return Array.isArray(pk) ? `[${pk.map((p) => `'${p}'`).join(', ')}]` : `'${pk}'`;
67
+ if (!Array.isArray(pk))
68
+ return `'${pk}'`;
69
+ const quoted = pk.map((p) => `'${p}'`);
70
+ return `[${quoted.join(', ')}]`;
68
71
  }
69
72
  /** All PK fields as an array, for "non-PK field" lookups in example snippets. */
70
73
  function pkFields(pk) {
71
74
  return Array.isArray(pk) ? pk : [pk];
72
75
  }
76
+ /** Infers FK relations between parsed schemas.
77
+ * @testonly Exported only so unit tests can exercise it directly.
78
+ */
73
79
  export function detectRelations(schemas) {
74
80
  const tableBySnakeName = new Map();
75
81
  for (const schema of schemas) {
@@ -96,36 +102,68 @@ export function detectRelations(schemas) {
96
102
  return relations;
97
103
  }
98
104
  // ─── Single Table File Generation ────────────────────────────
99
- export function generateSingleTableFile(schema, allSchemas) {
100
- const relations = detectRelations(allSchemas);
101
- const relsByParent = new Map();
102
- const relsByChild = new Map();
105
+ /** Relations grouped by the schema they hang off, in both directions. */
106
+ function indexRelations(relations) {
107
+ const byParent = new Map();
108
+ const byChild = new Map();
103
109
  for (const rel of relations) {
104
- const arr = relsByParent.get(rel.parentSchemaName) || [];
105
- arr.push(rel);
106
- relsByParent.set(rel.parentSchemaName, arr);
107
- const childArr = relsByChild.get(rel.childSchemaName) || [];
110
+ const parentArr = byParent.get(rel.parentSchemaName) ?? [];
111
+ parentArr.push(rel);
112
+ byParent.set(rel.parentSchemaName, parentArr);
113
+ const childArr = byChild.get(rel.childSchemaName) ?? [];
108
114
  childArr.push(rel);
109
- relsByChild.set(rel.childSchemaName, childArr);
115
+ byChild.set(rel.childSchemaName, childArr);
110
116
  }
111
- const { pk, autoIncrement } = detectPrimaryKey(schema);
112
- const tableVarName = 'Table' + schema.schemaName.replace(/^Schema/, '');
113
- const parentRels = relsByParent.get(schema.schemaName) || [];
114
- const childRels = relsByChild.get(schema.schemaName) || [];
115
- // Collect related schema names for commented imports
116
- const relatedSchemas = new Set();
117
- for (const rel of parentRels) {
118
- if (rel.childSchemaName !== schema.schemaName)
119
- relatedSchemas.add(rel.childSchemaName);
120
- if (rel.parentSchemaName !== schema.schemaName)
121
- relatedSchemas.add(rel.parentSchemaName);
117
+ return { byParent, byChild };
118
+ }
119
+ /** Schemas on the other end of a relation — emitted as commented-out imports. */
120
+ function collectRelatedSchemas(ownName, ...relationGroups) {
121
+ const related = new Set();
122
+ for (const rels of relationGroups) {
123
+ for (const rel of rels) {
124
+ if (rel.childSchemaName !== ownName)
125
+ related.add(rel.childSchemaName);
126
+ if (rel.parentSchemaName !== ownName)
127
+ related.add(rel.parentSchemaName);
128
+ }
122
129
  }
123
- for (const rel of childRels) {
124
- if (rel.parentSchemaName !== schema.schemaName)
125
- relatedSchemas.add(rel.parentSchemaName);
126
- if (rel.childSchemaName !== schema.schemaName)
127
- relatedSchemas.add(rel.childSchemaName);
130
+ return related;
131
+ }
132
+ /** A plausible `schemaOverrides` example: an email format if there is one, else a minLength. */
133
+ function schemaOverridesLine(schema, pk) {
134
+ const emailField = schema.fields.find((f) => f.toLowerCase().includes('email'));
135
+ if (emailField) {
136
+ return ` // schemaOverrides: { ${emailField}: Type.String({ format: 'email' }) },`;
137
+ }
138
+ const stringField = schema.fields.find((f) => !pkFields(pk).includes(f) && schema.fieldTypes[f]?.includes('String'));
139
+ return stringField
140
+ ? ` // schemaOverrides: { ${stringField}: Type.String({ minLength: 1 }) },`
141
+ : ` // schemaOverrides: {},`;
142
+ }
143
+ /** Indirect scope when the table hangs off a parent, direct scope otherwise. */
144
+ function tenantScopeLine(childRels) {
145
+ const rel = childRels[0];
146
+ if (!rel)
147
+ return ` // tenantScope: { column: 'tenant_col' },`;
148
+ return ` // tenantScope: { column: 'tenant_col', through: { schema: ${rel.parentSchemaName}, localField: '${rel.childField}', foreignField: '${rel.parentField}' } },`;
149
+ }
150
+ function allowedReadJoinsLines(parentRels) {
151
+ if (parentRels.length === 0)
152
+ return [` // allowedReadJoins: [],`];
153
+ const lines = [` // allowedReadJoins: [`];
154
+ for (const rel of parentRels) {
155
+ lines.push(` // buildRelation(${rel.parentSchemaName}, '${rel.parentField}', ${rel.childSchemaName}, '${rel.childField}'),`);
128
156
  }
157
+ lines.push(` // ],`);
158
+ return lines;
159
+ }
160
+ export function generateSingleTableFile(schema, allSchemas) {
161
+ const { byParent, byChild } = indexRelations(detectRelations(allSchemas));
162
+ const { pk, autoIncrement } = detectPrimaryKey(schema);
163
+ const tableVarName = 'Table' + schema.schemaName.replace(/^Schema/, '');
164
+ const parentRels = byParent.get(schema.schemaName) ?? [];
165
+ const childRels = byChild.get(schema.schemaName) ?? [];
166
+ const relatedSchemas = collectRelatedSchemas(schema.schemaName, parentRels, childRels);
129
167
  const lines = [];
130
168
  // Imports
131
169
  lines.push(`import {defineTable, exportTableInfo, Type} from 'fastify-auto-sqlapi';`);
@@ -165,31 +203,10 @@ export function generateSingleTableFile(schema, allSchemas) {
165
203
  }
166
204
  // Hide columns from every read (writes still accept them, e.g. a password hash)
167
205
  lines.push(` // readExclude: [],`);
168
- if (parentRels.length > 0) {
169
- lines.push(` // allowedReadJoins: [`);
170
- for (const rel of parentRels) {
171
- lines.push(` // buildRelation(${rel.parentSchemaName}, '${rel.parentField}', ${rel.childSchemaName}, '${rel.childField}'),`);
172
- }
173
- lines.push(` // ],`);
174
- }
175
- else {
176
- lines.push(` // allowedReadJoins: [],`);
177
- }
178
- lines.push(` // upsertMap: buildUpsertRules(buildUpsertRule(Schema, [${pkFields(pk).map((p) => `'${p}'`).join(', ')}])),`);
179
- // schemaOverrides: suggest email format if email field exists, otherwise minLength on first string field
180
- const emailField = schema.fields.find(f => f.toLowerCase().includes('email'));
181
- if (emailField) {
182
- lines.push(` // schemaOverrides: { ${emailField}: Type.String({ format: 'email' }) },`);
183
- }
184
- else {
185
- const stringField = schema.fields.find(f => !pkFields(pk).includes(f) && schema.fieldTypes[f]?.includes('String'));
186
- if (stringField) {
187
- lines.push(` // schemaOverrides: { ${stringField}: Type.String({ minLength: 1 }) },`);
188
- }
189
- else {
190
- lines.push(` // schemaOverrides: {},`);
191
- }
192
- }
206
+ lines.push(...allowedReadJoinsLines(parentRels));
207
+ const upsertPk = pkFields(pk).map((p) => `'${p}'`).join(', ');
208
+ lines.push(` // upsertMap: buildUpsertRules(buildUpsertRule(Schema, [${upsertPk}])),`);
209
+ lines.push(schemaOverridesLine(schema, pk));
193
210
  // validate: use first non-PK field for example
194
211
  const exampleField = schema.fields.find(f => !pkFields(pk).includes(f)) || schema.fields[0];
195
212
  lines.push(` validate: async (db, req, main, secondaries) => {`);
@@ -199,13 +216,7 @@ export function generateSingleTableFile(schema, allSchemas) {
199
216
  lines.push(` },`);
200
217
  lines.push(` // beforeInsert: async (db, req, record) => {},`);
201
218
  lines.push(` // beforeUpdate: async (db, req, fields) => {},`);
202
- if (childRels.length > 0) {
203
- const rel = childRels[0];
204
- lines.push(` // tenantScope: { column: 'tenant_col', through: { schema: ${rel.parentSchemaName}, localField: '${rel.childField}', foreignField: '${rel.parentField}' } },`);
205
- }
206
- else {
207
- lines.push(` // tenantScope: { column: 'tenant_col' },`);
208
- }
219
+ lines.push(tenantScopeLine(childRels));
209
220
  lines.push(`});`);
210
221
  lines.push(``);
211
222
  return lines.join('\n');
@@ -230,18 +241,33 @@ export function generateDbTablesIndex(schemas) {
230
241
  return lines.join('\n');
231
242
  }
232
243
  // ─── Code Generation (legacy) ───────────────────────────────
244
+ /** One `defineTable({...})` block for the legacy single-file generator. */
245
+ function legacyTableBlock(schema, parentRels, childRels) {
246
+ const { pk, autoIncrement } = detectPrimaryKey(schema);
247
+ const tableVarName = 'Table' + schema.schemaName.replace(/^Schema/, '');
248
+ const upsertPk = pkFields(pk).map((p) => `'${p}'`).join(', ');
249
+ const exampleField = schema.fields.find((f) => !pkFields(pk).includes(f)) || schema.fields[0];
250
+ const lines = [
251
+ ``,
252
+ `// ─── ${schema.tableName} ──────────────────────────────`,
253
+ `// Fields: ${schema.fields.join(', ')}`,
254
+ `const ${tableVarName} = defineTable({`,
255
+ ` primary: ${formatPrimary(pk)},`,
256
+ ` ...exportTableInfo(${schema.schemaName}),`,
257
+ ` defaultOrder: '${pkFirst(pk)}',`,
258
+ autoIncrement
259
+ ? ` excludeFromCreation: ['${pkFirst(pk)}'],`
260
+ : ` // excludeFromCreation: [],`,
261
+ ];
262
+ lines.push(...allowedReadJoinsLines(parentRels));
263
+ lines.push(` // upsertMap: buildUpsertRules(buildUpsertRule(${schema.schemaName}, [${upsertPk}])),`, ` // schemaOverrides: {},`, ` validate: async (db, req, main, secondaries) => {`, ` const errors: ValidationError[] = [];`, ` // if (!main.${exampleField}) errors.push(['${exampleField}', 'required']);`, ` return errors;`, ` },`, ` // beforeInsert: async (db, req, record) => {},`, ` // beforeUpdate: async (db, req, fields) => {},`, tenantScopeLine(childRels), `});`);
264
+ return lines;
265
+ }
266
+ /** Legacy single-file DbTables generator.
267
+ * @testonly Exported only so unit tests can exercise it directly.
268
+ */
233
269
  export function generateTablesFile(schemas) {
234
- const relations = detectRelations(schemas);
235
- const relsByParent = new Map();
236
- const relsByChild = new Map();
237
- for (const rel of relations) {
238
- const arr = relsByParent.get(rel.parentSchemaName) || [];
239
- arr.push(rel);
240
- relsByParent.set(rel.parentSchemaName, arr);
241
- const childArr = relsByChild.get(rel.childSchemaName) || [];
242
- childArr.push(rel);
243
- relsByChild.set(rel.childSchemaName, childArr);
244
- }
270
+ const { byParent, byChild } = indexRelations(detectRelations(schemas));
245
271
  const lines = [];
246
272
  // Imports
247
273
  lines.push(`import {defineTable, exportTableInfo, Type} from 'fastify-auto-sqlapi';`);
@@ -253,51 +279,7 @@ export function generateTablesFile(schemas) {
253
279
  }
254
280
  // Table definitions
255
281
  for (const schema of schemas) {
256
- const { pk, autoIncrement } = detectPrimaryKey(schema);
257
- const tableVarName = 'Table' + schema.schemaName.replace(/^Schema/, '');
258
- const parentRels = relsByParent.get(schema.schemaName) || [];
259
- lines.push(``);
260
- lines.push(`// ─── ${schema.tableName} ──────────────────────────────`);
261
- lines.push(`// Fields: ${schema.fields.join(', ')}`);
262
- lines.push(`const ${tableVarName} = defineTable({`);
263
- lines.push(` primary: ${formatPrimary(pk)},`);
264
- lines.push(` ...exportTableInfo(${schema.schemaName}),`);
265
- lines.push(` defaultOrder: '${pkFirst(pk)}',`);
266
- if (autoIncrement) {
267
- lines.push(` excludeFromCreation: ['${pkFirst(pk)}'],`);
268
- }
269
- else {
270
- lines.push(` // excludeFromCreation: [],`);
271
- }
272
- if (parentRels.length > 0) {
273
- lines.push(` // allowedReadJoins: [`);
274
- for (const rel of parentRels) {
275
- lines.push(` // buildRelation(${rel.parentSchemaName}, '${rel.parentField}', ${rel.childSchemaName}, '${rel.childField}'),`);
276
- }
277
- lines.push(` // ],`);
278
- }
279
- else {
280
- lines.push(` // allowedReadJoins: [],`);
281
- }
282
- lines.push(` // upsertMap: buildUpsertRules(buildUpsertRule(${schema.schemaName}, [${pkFields(pk).map((p) => `'${p}'`).join(', ')}])),`);
283
- lines.push(` // schemaOverrides: {},`);
284
- const exampleFieldLegacy = schema.fields.find(f => !pkFields(pk).includes(f)) || schema.fields[0];
285
- lines.push(` validate: async (db, req, main, secondaries) => {`);
286
- lines.push(` const errors: ValidationError[] = [];`);
287
- lines.push(` // if (!main.${exampleFieldLegacy}) errors.push(['${exampleFieldLegacy}', 'required']);`);
288
- lines.push(` return errors;`);
289
- lines.push(` },`);
290
- lines.push(` // beforeInsert: async (db, req, record) => {},`);
291
- lines.push(` // beforeUpdate: async (db, req, fields) => {},`);
292
- const childRels = relsByChild.get(schema.schemaName) || [];
293
- if (childRels.length > 0) {
294
- const rel = childRels[0];
295
- lines.push(` // tenantScope: { column: 'tenant_col', through: { schema: ${rel.parentSchemaName}, localField: '${rel.childField}', foreignField: '${rel.parentField}' } },`);
296
- }
297
- else {
298
- lines.push(` // tenantScope: { column: 'tenant_col' },`);
299
- }
300
- lines.push(`});`);
282
+ lines.push(...legacyTableBlock(schema, byParent.get(schema.schemaName) ?? [], byChild.get(schema.schemaName) ?? []));
301
283
  }
302
284
  // DbTables export
303
285
  lines.push(``);
@@ -1,11 +1,21 @@
1
- export declare const SINGLE_VALUE_METHODS: readonly ["isEqual", "isNotEqual", "isGreater", "isNotGreater", "isGreaterOrEqual", "isNotGreaterOrEqual", "isLess", "isNotLess", "isLessOrEqual", "isNotLessOrEqual", "isLike", "isNotLike", "isILike", "isNotILike"];
2
- export declare const BETWEEN_METHODS: readonly ["isBetween", "isNotBetween"];
3
- export declare const IN_METHODS: readonly ["isIn", "isNotIn"];
4
- export declare const NULL_METHODS: readonly ["isNull", "isNotNull"];
5
1
  export declare const ALLOWED_METHODS: readonly ["isEqual", "isNotEqual", "isGreater", "isNotGreater", "isGreaterOrEqual", "isNotGreaterOrEqual", "isLess", "isNotLess", "isLessOrEqual", "isNotLessOrEqual", "isLike", "isNotLike", "isILike", "isNotILike", "isBetween", "isNotBetween", "isIn", "isNotIn", "isNull", "isNotNull"];
6
2
  export declare const SINGLE_VALUE_SET: Set<string>;
7
3
  export declare const BETWEEN_SET: Set<string>;
8
4
  export declare const IN_SET: Set<string>;
9
5
  export declare const NULL_SET: Set<string>;
10
6
  export declare const ALLOWED_SET: Set<string>;
7
+ /**
8
+ * Caps on how much work one search request may ask for.
9
+ *
10
+ * Every dotted condition and every 3-part `orderBy` token becomes its own correlated subquery,
11
+ * so an uncapped list turns a single request into arbitrarily many — `maxItemsPerPage` bounds
12
+ * the rows returned, not the work done to find them. They live here, in the data module both
13
+ * the engine and the TypeBox schema builders already share, so the request schema advertises
14
+ * exactly the limit the engine enforces.
15
+ */
16
+ export declare const MAX_CONDITIONS = 100;
17
+ /** Token cap the engine applies to `orderBy`, counted over its comma-separated parts. */
18
+ export declare const MAX_ORDER_BY_PARTS = 20;
19
+ /** Character cap the request schema applies to `orderBy` — generous for MAX_ORDER_BY_PARTS. */
20
+ export declare const MAX_ORDER_BY_LENGTH = 1024;
11
21
  //# sourceMappingURL=condition-methods.d.ts.map