@an5/orm 1.0.9 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/README.md +10 -3
  2. package/dist/config.d.ts +9 -1
  3. package/dist/config.js +13 -18
  4. package/dist/generator/src/api.d.ts +35 -0
  5. package/dist/generator/src/api.js +58 -0
  6. package/dist/generator/src/config.d.ts +162 -0
  7. package/dist/generator/src/config.js +354 -0
  8. package/dist/generator/src/dialect.d.ts +88 -0
  9. package/dist/generator/src/dialect.js +318 -0
  10. package/dist/generator/src/dotnet-generator.d.ts +8 -0
  11. package/dist/generator/src/dotnet-generator.js +282 -101
  12. package/dist/generator/src/field-types.d.ts +96 -0
  13. package/dist/generator/src/field-types.js +433 -0
  14. package/dist/generator/src/golang-generator.d.ts +36 -0
  15. package/dist/generator/src/golang-generator.js +1229 -0
  16. package/dist/generator/src/index.js +69 -36
  17. package/dist/generator/src/metadata-generator.js +2 -1
  18. package/dist/generator/src/parser.d.ts +28 -2
  19. package/dist/generator/src/parser.js +92 -90
  20. package/dist/generator/src/push-apply.d.ts +37 -0
  21. package/dist/generator/src/push-apply.js +109 -0
  22. package/dist/generator/src/push-schema.d.ts +93 -0
  23. package/dist/generator/src/push-schema.js +189 -0
  24. package/dist/generator/src/python-generator.d.ts +19 -1
  25. package/dist/generator/src/python-generator.js +147 -64
  26. package/dist/generator/src/rust-generator.d.ts +38 -0
  27. package/dist/generator/src/rust-generator.js +971 -0
  28. package/dist/generator/src/suggest.d.ts +17 -0
  29. package/dist/generator/src/suggest.js +45 -0
  30. package/dist/generator/src/type-kinds.d.ts +27 -0
  31. package/dist/generator/src/type-kinds.js +72 -0
  32. package/dist/generator/src/types.d.ts +33 -0
  33. package/dist/generator/src/types.js +39 -0
  34. package/dist/migration-core.d.ts +16 -1
  35. package/dist/migration-core.js +60 -21
  36. package/dist/orm.d.ts +147 -0
  37. package/dist/orm.js +1654 -0
  38. package/dist/provider-support.d.ts +27 -0
  39. package/dist/provider-support.js +50 -0
  40. package/generator/src/api.ts +45 -0
  41. package/generator/src/config.ts +496 -0
  42. package/generator/src/dialect.ts +415 -0
  43. package/generator/src/dotnet-generator.ts +277 -88
  44. package/generator/src/field-types.ts +473 -0
  45. package/generator/src/golang-generator.ts +89 -64
  46. package/generator/src/index.ts +71 -36
  47. package/generator/src/metadata-generator.ts +2 -2
  48. package/generator/src/parser.ts +122 -90
  49. package/generator/src/push-apply.ts +148 -0
  50. package/generator/src/push-schema.ts +250 -0
  51. package/generator/src/python-generator.ts +118 -62
  52. package/generator/src/rust-generator.ts +974 -0
  53. package/generator/src/suggest.ts +41 -0
  54. package/generator/src/type-kinds.ts +78 -0
  55. package/generator/src/types.ts +48 -0
  56. package/package.json +26 -6
@@ -0,0 +1,17 @@
1
+ /**
2
+ * "Did you mean" for typos.
3
+ *
4
+ * Lives on its own because two callers need it and neither should own it:
5
+ * the config loader (a mistyped option key) and the field type registry (a
6
+ * type written for the wrong database). Both want the nearest candidate out
7
+ * of a known list, or nothing at all when the input is not close enough that
8
+ * a suggestion would be honest.
9
+ */
10
+ /** Levenshtein distance between two strings. */
11
+ export declare function editDistance(a: string, b: string): number;
12
+ /**
13
+ * The candidate closest to `input`, or null when none is close enough to be a
14
+ * plausible typo. Comparing case-insensitively keeps `nvarchar` from suggesting
15
+ * something unrelated just because of capitalisation.
16
+ */
17
+ export declare function suggest(input: string, candidates: readonly string[]): string | null;
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ /**
3
+ * "Did you mean" for typos.
4
+ *
5
+ * Lives on its own because two callers need it and neither should own it:
6
+ * the config loader (a mistyped option key) and the field type registry (a
7
+ * type written for the wrong database). Both want the nearest candidate out
8
+ * of a known list, or nothing at all when the input is not close enough that
9
+ * a suggestion would be honest.
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.editDistance = editDistance;
13
+ exports.suggest = suggest;
14
+ /** Levenshtein distance between two strings. */
15
+ function editDistance(a, b) {
16
+ const rows = [];
17
+ for (let i = 0; i <= a.length; i++)
18
+ rows.push([i, ...new Array(b.length).fill(0)]);
19
+ for (let j = 0; j <= b.length; j++)
20
+ rows[0][j] = j;
21
+ for (let i = 1; i <= a.length; i++) {
22
+ for (let j = 1; j <= b.length; j++) {
23
+ const cost = a[i - 1] === b[j - 1] ? 0 : 1;
24
+ rows[i][j] = Math.min(rows[i - 1][j] + 1, rows[i][j - 1] + 1, rows[i - 1][j - 1] + cost);
25
+ }
26
+ }
27
+ return rows[a.length][b.length];
28
+ }
29
+ /**
30
+ * The candidate closest to `input`, or null when none is close enough to be a
31
+ * plausible typo. Comparing case-insensitively keeps `nvarchar` from suggesting
32
+ * something unrelated just because of capitalisation.
33
+ */
34
+ function suggest(input, candidates) {
35
+ let best = null;
36
+ let bestDistance = Infinity;
37
+ for (const candidate of candidates) {
38
+ const distance = editDistance(input.toLowerCase(), candidate.toLowerCase());
39
+ if (distance < bestDistance) {
40
+ bestDistance = distance;
41
+ best = candidate;
42
+ }
43
+ }
44
+ return best !== null && bestDistance <= Math.max(2, Math.floor(input.length / 3)) ? best : null;
45
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * What a field *is*, for the generators that need a real type.
3
+ *
4
+ * The parser gives every generator the same TypeScript type, and that type loses
5
+ * what a language needs: `INT`, `FLOAT` and `DECIMAL` all arrive as `number`, so
6
+ * the Go client generated every numeric column as a `string`, and the Python, Rust
7
+ * and .NET clients turned a `DECIMAL` into an integer type. The declared type is
8
+ * right there in `Field.sqlType`, so it decides — with the TypeScript type as the
9
+ * fallback for metadata generated before that was recorded.
10
+ */
11
+ import { Provider } from './field-types';
12
+ /** The categories a generated language type can be built from. */
13
+ export type FieldKind = 'int' | 'bigint' | 'float' | 'bool' | 'date' | 'bytes' | 'json' | 'vector' | 'string';
14
+ export interface KindInput {
15
+ /** The TypeScript type the parser produced, e.g. `number | bigint`. */
16
+ type: string;
17
+ /** The type as written in the schema, e.g. `DECIMAL(10,2)`. */
18
+ sqlType?: string | undefined;
19
+ }
20
+ /**
21
+ * The category a field belongs to.
22
+ *
23
+ * `TIMESTAMP` is the reason `provider` is a parameter: it is a rowversion on SQL
24
+ * Server and a point in time everywhere else, and the same word has to produce
25
+ * bytes in one and a date in the other.
26
+ */
27
+ export declare function fieldKind(field: KindInput, provider?: Provider | undefined): FieldKind;
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.fieldKind = fieldKind;
4
+ const INT = ['INT', 'INTEGER', 'SMALLINT', 'TINYINT', 'MEDIUMINT', 'INT2', 'INT4', 'SERIAL', 'SMALLSERIAL', 'YEAR'];
5
+ const BIGINT = ['BIGINT', 'INT8', 'BIGSERIAL', 'UNSIGNED BIG INT'];
6
+ const FLOAT = ['FLOAT', 'REAL', 'DOUBLE', 'DOUBLE PRECISION', 'DECIMAL', 'NUMERIC', 'MONEY', 'SMALLMONEY', 'FIXED'];
7
+ const BOOL = ['BIT', 'BOOL', 'BOOLEAN'];
8
+ const DATE = [
9
+ 'DATE', 'DATETIME', 'DATETIME2', 'SMALLDATETIME', 'DATETIMEOFFSET',
10
+ 'TIME', 'TIMETZ', 'TIMESTAMPTZ', 'TIME WITH TIME ZONE', 'TIME WITHOUT TIME ZONE',
11
+ 'TIMESTAMP WITH TIME ZONE', 'TIMESTAMP WITHOUT TIME ZONE',
12
+ ];
13
+ const BYTES = ['BINARY', 'VARBINARY', 'IMAGE', 'BLOB', 'TINYBLOB', 'MEDIUMBLOB', 'LONGBLOB', 'BYTEA', 'ROWVERSION', 'BYTES'];
14
+ const JSON = ['JSON', 'JSONB', 'SQL_VARIANT'];
15
+ /** `DECIMAL(10,2)` → `DECIMAL`. */
16
+ function declaredBase(sqlType) {
17
+ const withoutParams = sqlType.replace(/\([^)]*\)/g, ' ').trim();
18
+ return withoutParams.replace(/\s+/g, ' ').toUpperCase();
19
+ }
20
+ /**
21
+ * The category a field belongs to.
22
+ *
23
+ * `TIMESTAMP` is the reason `provider` is a parameter: it is a rowversion on SQL
24
+ * Server and a point in time everywhere else, and the same word has to produce
25
+ * bytes in one and a date in the other.
26
+ */
27
+ function fieldKind(field, provider) {
28
+ const base = field.sqlType ? declaredBase(field.sqlType) : '';
29
+ if (base) {
30
+ if (base === 'TIMESTAMP')
31
+ return provider === 'mssql' ? 'bytes' : 'date';
32
+ if (INT.includes(base))
33
+ return 'int';
34
+ if (BIGINT.includes(base))
35
+ return 'bigint';
36
+ if (FLOAT.includes(base))
37
+ return 'float';
38
+ if (BOOL.includes(base))
39
+ return 'bool';
40
+ if (DATE.includes(base) || base === 'TIMESTAMP')
41
+ return 'date';
42
+ if (BYTES.includes(base))
43
+ return 'bytes';
44
+ if (JSON.includes(base))
45
+ return 'json';
46
+ if (base === 'VECTOR')
47
+ return 'vector';
48
+ return 'string';
49
+ }
50
+ // No declared type: fall back to what the TypeScript type can still tell us. A
51
+ // plain `number` is an integer here, which is what every generator assumed
52
+ // before `sqlType` was available.
53
+ const raw = field.type.trim().toLowerCase();
54
+ // Checked before the brackets are stripped, or `number[] | string` — the vector
55
+ // type — would read as a number.
56
+ if (raw.includes('[]'))
57
+ return 'vector';
58
+ const ts = raw.replace(/[?\]]/g, '').trim();
59
+ if (ts.startsWith('number | bigint'))
60
+ return 'bigint';
61
+ if (ts.startsWith('number'))
62
+ return 'int';
63
+ if (ts.startsWith('date'))
64
+ return 'date';
65
+ if (ts.startsWith('bool'))
66
+ return 'bool';
67
+ if (ts.startsWith('buffer'))
68
+ return 'bytes';
69
+ if (ts === 'any')
70
+ return 'json';
71
+ return 'string';
72
+ }
@@ -1,3 +1,4 @@
1
+ import { Provider } from './field-types';
1
2
  export interface Field {
2
3
  name: string;
3
4
  type: string;
@@ -15,13 +16,45 @@ export interface Relation {
15
16
  foreignKey: string;
16
17
  localKey: string;
17
18
  relationName: string;
19
+ /** From a trailing @description("...") on the relation line. */
20
+ description?: string;
18
21
  }
19
22
  export interface Model {
20
23
  name: string;
21
24
  tableName: string;
25
+ /** Empty for a database with no schemas, or when `@@schema("")` says so. */
22
26
  schemaName: string;
27
+ /**
28
+ * The database this schema targets. Set by `SchemaParser`, and what decides how a
29
+ * table name is written — see `bracketedTableName`.
30
+ */
31
+ provider?: Provider | undefined;
23
32
  fields: Field[];
24
33
  relations: Relation[];
25
34
  compoundUniques?: string[][];
26
35
  description?: string;
27
36
  }
37
+ /**
38
+ * The table name as it goes into the generated metadata.
39
+ *
40
+ * SQL Server keeps the bracketed form it has always had. Every other provider gets
41
+ * the bare name, with the schema as a plain prefix, because the brackets are not
42
+ * portable: `[dbo].[users]` is invalid SQL in PostgreSQL, and on SQLite it fails with
43
+ * "no such table: dbo.users" — which is why the adapters had to strip it afterwards.
44
+ * A bare name is correct everywhere once the adapter quotes it, and the adapters
45
+ * already quote per dialect (`quote()` in `@an5/adapters`, `An5DbContext.Quote()` in
46
+ * the .NET client), so the one form that needs no translation is the one emitted.
47
+ *
48
+ * `dbo` is SQL Server's default schema and only SQL Server's: with `@@schema("")` —
49
+ * an empty schema, meant for databases that have no schema concept — the prefix has
50
+ * to go, because `[].[users]` is not valid SQL. The `[${schemaName}].[${tableName}]`
51
+ * formula used to be written out in six places, so every dialect got `[dbo]`.
52
+ */
53
+ export declare function bracketedTableName(model: Model): string;
54
+ /**
55
+ * The table name as `schema.table`, for clients that pass a string instead of SQL.
56
+ *
57
+ * Unquoted for the same reason as `bracketedTableName`: these strings go to the same
58
+ * per-dialect quoting.
59
+ */
60
+ export declare function dottedTableName(model: Model): string;
@@ -1,2 +1,41 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.bracketedTableName = bracketedTableName;
4
+ exports.dottedTableName = dottedTableName;
5
+ /**
6
+ * The table name as it goes into the generated metadata.
7
+ *
8
+ * SQL Server keeps the bracketed form it has always had. Every other provider gets
9
+ * the bare name, with the schema as a plain prefix, because the brackets are not
10
+ * portable: `[dbo].[users]` is invalid SQL in PostgreSQL, and on SQLite it fails with
11
+ * "no such table: dbo.users" — which is why the adapters had to strip it afterwards.
12
+ * A bare name is correct everywhere once the adapter quotes it, and the adapters
13
+ * already quote per dialect (`quote()` in `@an5/adapters`, `An5DbContext.Quote()` in
14
+ * the .NET client), so the one form that needs no translation is the one emitted.
15
+ *
16
+ * `dbo` is SQL Server's default schema and only SQL Server's: with `@@schema("")` —
17
+ * an empty schema, meant for databases that have no schema concept — the prefix has
18
+ * to go, because `[].[users]` is not valid SQL. The `[${schemaName}].[${tableName}]`
19
+ * formula used to be written out in six places, so every dialect got `[dbo]`.
20
+ */
21
+ function bracketedTableName(model) {
22
+ const qualified = model.schemaName
23
+ ? `${model.schemaName}.${model.tableName}`
24
+ : model.tableName;
25
+ if ((model.provider ?? 'mssql') !== 'mssql')
26
+ return qualified;
27
+ return model.schemaName
28
+ ? `[${model.schemaName}].[${model.tableName}]`
29
+ : `[${model.tableName}]`;
30
+ }
31
+ /**
32
+ * The table name as `schema.table`, for clients that pass a string instead of SQL.
33
+ *
34
+ * Unquoted for the same reason as `bracketedTableName`: these strings go to the same
35
+ * per-dialect quoting.
36
+ */
37
+ function dottedTableName(model) {
38
+ return model.schemaName
39
+ ? `${model.schemaName}.${model.tableName}`
40
+ : model.tableName;
41
+ }
@@ -1,3 +1,4 @@
1
+ import { Provider } from './generator/src/field-types';
1
2
  export interface SchemaField {
2
3
  name: string;
3
4
  sqlType: string;
@@ -16,6 +17,11 @@ export interface SchemaIndexDefinition {
16
17
  }
17
18
  export interface SchemaModel {
18
19
  name: string;
20
+ /**
21
+ * The table to create, schema-qualified when the model named a schema other than
22
+ * `dbo`. `dbo` stays implicit so an unqualified model produces the same SQL it
23
+ * always has.
24
+ */
19
25
  tableName: string;
20
26
  fields: SchemaField[];
21
27
  compoundUniques: Array<string[] | SchemaIndexDefinition>;
@@ -70,7 +76,16 @@ export declare function buildAlterColumnWarnings(previousSqlType: string, nextSq
70
76
  export declare function buildAlterColumnPreflightSql(tableName: string, columnName: string, previousSqlType: string, nextSqlType: string, previousNullable: boolean, nextNullable: boolean): string[];
71
77
  export declare function buildAddColumnPreflightSql(tableName: string, field: SchemaField): string[];
72
78
  export declare function buildUniqueConstraintPreflightSql(tableName: string, fields: string[]): string[];
73
- export declare function parseSchemaText(text: string): SchemaModel[];
79
+ /**
80
+ * Reads a `.an5` schema into models to compare against the database.
81
+ *
82
+ * `provider` decides what counts as a column and what counts as a relation; it
83
+ * defaults to SQL Server. A type the provider does not have is reported rather
84
+ * than skipped — skipping it means the migration silently never mentions that
85
+ * column. A token matching a model in the schema is a relation, so that
86
+ * comparison waits until the whole schema has been read.
87
+ */
88
+ export declare function parseSchemaText(text: string, provider?: Provider): SchemaModel[];
74
89
  export declare function buildCreateTableSql(model: SchemaModel): string;
75
90
  export declare function buildIndexDiff(model: SchemaModel, artifacts: TableArtifacts, ops: MigrationOp[]): void;
76
91
  export declare function generateColumnDiff(model: SchemaModel, dbColumns: DbColumn[], ops: MigrationOp[]): void;
@@ -21,15 +21,7 @@ exports.parseMigrationSections = parseMigrationSections;
21
21
  exports.splitSqlBatches = splitSqlBatches;
22
22
  exports.parseRollbackSelection = parseRollbackSelection;
23
23
  exports.parseMigrationCommandOptions = parseMigrationCommandOptions;
24
- const AN5_TYPES = new Set([
25
- 'NVARCHAR', 'VARCHAR', 'CHAR', 'NCHAR', 'TEXT', 'NTEXT', 'XML',
26
- 'INT', 'SMALLINT', 'TINYINT', 'BIGINT', 'FLOAT', 'REAL', 'DECIMAL', 'NUMERIC',
27
- 'MONEY', 'SMALLMONEY', 'BIT',
28
- 'DATETIME', 'DATETIME2', 'SMALLDATETIME', 'DATE', 'TIME', 'DATETIMEOFFSET',
29
- 'VARBINARY', 'BINARY', 'IMAGE',
30
- 'UNIQUEIDENTIFIER', 'SQL_VARIANT', 'ROWVERSION',
31
- 'HIERARCHYID', 'GEOGRAPHY', 'GEOMETRY', 'VECTOR',
32
- ]);
24
+ const field_types_1 = require("./generator/src/field-types");
33
25
  function parseSqlType(raw) {
34
26
  const match = raw.match(/^(\w+)/);
35
27
  return match?.[1]?.toUpperCase() ?? raw.toUpperCase();
@@ -264,8 +256,21 @@ function buildUniqueConstraintPreflightSql(tableName, fields) {
264
256
  )
265
257
  THROW 51000, 'an5 migration preflight failed: ${escapeSqlString(label)} has duplicate values for a UNIQUE constraint.', 1`];
266
258
  }
267
- function parseSchemaText(text) {
259
+ /**
260
+ * Reads a `.an5` schema into models to compare against the database.
261
+ *
262
+ * `provider` decides what counts as a column and what counts as a relation; it
263
+ * defaults to SQL Server. A type the provider does not have is reported rather
264
+ * than skipped — skipping it means the migration silently never mentions that
265
+ * column. A token matching a model in the schema is a relation, so that
266
+ * comparison waits until the whole schema has been read.
267
+ */
268
+ function parseSchemaText(text, provider = field_types_1.DEFAULT_PROVIDER) {
268
269
  const models = [];
270
+ const typeIssues = [];
271
+ const modelRefs = [];
272
+ /** `@@schema` per model, applied after the model is read rather than mid-loop. */
273
+ const schemas = new Map();
269
274
  const lines = text.split('\n');
270
275
  let current = null;
271
276
  for (let line of lines) {
@@ -292,11 +297,25 @@ function parseSchemaText(text) {
292
297
  if (!current)
293
298
  continue;
294
299
  if (line.startsWith('@@map')) {
300
+ // Just the table name. A schema comes from `@@schema` alone, because a mapped
301
+ // name may legitimately contain a dot: `@@map("reports.daily")` is one table
302
+ // whose name has a dot in it, not a table called `daily` in a schema called
303
+ // `reports`.
295
304
  const m = line.match(/@@map\("(.+)"\)/);
296
305
  if (m?.[1])
297
306
  current.tableName = m[1];
298
307
  continue;
299
308
  }
309
+ if (line.startsWith('@@schema')) {
310
+ // The directive used to be dropped here while `db:push` and the generators
311
+ // honoured it, so a migration created the table in the connection's default
312
+ // schema and the client then read the one the schema file named. Composed in
313
+ // once the whole model has been read, below.
314
+ const m = line.match(/@@schema\("(.*)"\)/);
315
+ if (m !== null)
316
+ schemas.set(current, m[1]?.trim() ?? '');
317
+ continue;
318
+ }
300
319
  if (line.startsWith('@@unique')) {
301
320
  const parsed = parseMappedIndex(line, '@@unique');
302
321
  if (parsed)
@@ -311,25 +330,45 @@ function parseSchemaText(text) {
311
330
  }
312
331
  if (line.startsWith('@@'))
313
332
  continue;
314
- const parts = line.split(/\s+/);
315
- const fieldName = parts[0];
316
- const fieldType = parts[1];
317
- if (!fieldName || !fieldType)
333
+ const head = (0, field_types_1.readFieldLineHead)(line);
334
+ if (!head)
318
335
  continue;
319
- const cleanType = fieldType.replace('[]', '').replace('?', '');
320
- const sqlBase = parseSqlType(cleanType);
321
- if (!AN5_TYPES.has(sqlBase))
336
+ if (!(0, field_types_1.resolveFieldType)(head.type, provider)) {
337
+ modelRefs.push({ model: current.name, field: head.name, type: head.type });
322
338
  continue;
339
+ }
323
340
  current.fields.push({
324
- name: fieldName,
325
- sqlType: cleanType.toUpperCase(),
326
- isOptional: fieldType.endsWith('?'),
341
+ name: head.name,
342
+ sqlType: head.type.toUpperCase(),
343
+ isOptional: head.isOptional,
327
344
  isId: line.includes('@id'),
328
345
  isUnique: line.includes('@unique'),
329
346
  uniqueName: line.match(/@unique\([^)]*\bmap\s*:\s*"([^"]+)"/)?.[1],
330
- defaultValue: line.match(/@default\((.*)\)/)?.[1],
347
+ // `@updatedAt` is `now()` under another name, and db:push treats it as such.
348
+ // Without this here a stamp column looked like it had no default and every
349
+ // diff wanted to add one.
350
+ defaultValue: line.match(/@default\((.*)\)/)?.[1]
351
+ ?? (line.includes('@updatedAt') ? 'now()' : undefined),
331
352
  });
332
353
  }
354
+ // `dbo` stays implicit: an unqualified model produces the SQL it always has, and a
355
+ // named one is qualified with the schema the schema file asked for.
356
+ for (const model of models) {
357
+ const schema = schemas.get(model) ?? '';
358
+ if (schema && schema !== 'dbo')
359
+ model.tableName = `${schema}.${model.tableName}`;
360
+ }
361
+ const modelNames = models.map((model) => model.name);
362
+ for (const ref of modelRefs) {
363
+ if (!modelNames.includes(ref.type)) {
364
+ typeIssues.push({
365
+ path: `${ref.model}.${ref.field}`,
366
+ message: (0, field_types_1.unknownFieldTypeMessage)(ref.type, provider, modelNames),
367
+ });
368
+ }
369
+ }
370
+ if (typeIssues.length > 0)
371
+ throw new field_types_1.FieldTypeError(provider, typeIssues);
333
372
  return models;
334
373
  }
335
374
  function buildCreateTableSql(model) {
package/dist/orm.d.ts ADDED
@@ -0,0 +1,147 @@
1
+ import { An5Adapter, AdapterTableClient } from "@an5/adapters";
2
+ import { An5Metadata } from "./metadata";
3
+ type ExecutorFn = ((queryText: string, params?: Record<string, any>) => Promise<any[]>) & {
4
+ executeRaw?: (queryText: string, params?: Record<string, any>) => Promise<number>;
5
+ transaction?: <R>(fn: (txExecutor: ExecutorFn) => Promise<R>, options?: {
6
+ timeout?: number;
7
+ }) => Promise<R>;
8
+ beginTransaction?: () => Promise<InteractiveTransactionExecutor>;
9
+ };
10
+ type InteractiveTransactionExecutor = {
11
+ executor: ExecutorFn;
12
+ commit: () => Promise<void>;
13
+ rollback: () => Promise<void>;
14
+ };
15
+ export declare class TableClient<T = any> extends AdapterTableClient<T> {
16
+ private executor;
17
+ private orm;
18
+ readonly rawTableName: string;
19
+ constructor(modelName: string, rawTableName: string, executor: ExecutorFn, orm: An5ORM);
20
+ private executeRaw;
21
+ findMany(args?: any): Promise<T[]>;
22
+ findFirst(args?: any): Promise<T | null>;
23
+ findUnique(args?: any): Promise<T | null>;
24
+ count(args?: any): Promise<number>;
25
+ private scopedRelationWhere;
26
+ private handleNestedWrites;
27
+ create(args: any): Promise<T>;
28
+ update(args: any): Promise<T>;
29
+ updateMany(args: any): Promise<{
30
+ count: number;
31
+ }>;
32
+ delete(args: any): Promise<T>;
33
+ deleteMany(args?: any): Promise<{
34
+ count: number;
35
+ }>;
36
+ vectorSearch(args: {
37
+ vector: number[];
38
+ take?: number;
39
+ where?: any;
40
+ include?: any;
41
+ vectorField?: string;
42
+ distanceMetric?: 'cosine' | 'euclidean' | 'dot';
43
+ vectorElementType?: 'float32' | 'float16' | 'uint8';
44
+ }): Promise<(T & {
45
+ distance: number;
46
+ })[]>;
47
+ createMany(args: {
48
+ data: any[];
49
+ skipDuplicates?: boolean;
50
+ }): Promise<{
51
+ count: number;
52
+ }>;
53
+ aggregate(args: any): Promise<any>;
54
+ groupBy(args: any): Promise<any[]>;
55
+ private sequentialUpsert;
56
+ upsert(args: any): Promise<T>;
57
+ }
58
+ export interface MiddlewareParams {
59
+ model?: string;
60
+ action: string;
61
+ args: any;
62
+ runInTransaction?: boolean;
63
+ }
64
+ export type MiddlewareNext = (params: MiddlewareParams) => Promise<any>;
65
+ export type Middleware = (params: MiddlewareParams, next: MiddlewareNext) => Promise<any>;
66
+ export type LogLevel = "query" | "info" | "warn" | "error";
67
+ export interface QueryEvent {
68
+ timestamp: Date;
69
+ query: string;
70
+ params?: Record<string, any>;
71
+ duration: number;
72
+ model?: string;
73
+ action?: string;
74
+ error?: Error;
75
+ }
76
+ export interface LogEvent {
77
+ timestamp: Date;
78
+ message: string;
79
+ level: LogLevel;
80
+ duration?: number;
81
+ query?: string;
82
+ params?: Record<string, any>;
83
+ error?: Error;
84
+ }
85
+ export type EventListener<T = any> = (event: T) => void;
86
+ export declare class ViewClient<T = any> {
87
+ viewName: string;
88
+ rawTableName: string;
89
+ executor: ExecutorFn;
90
+ orm?: An5ORM | undefined;
91
+ private tableClient;
92
+ constructor(viewName: string, rawTableName: string, executor: ExecutorFn, orm?: An5ORM | undefined);
93
+ findMany(args?: any): Promise<T[]>;
94
+ findFirst(args?: any): Promise<T | null>;
95
+ findUnique(args?: any): Promise<T | null>;
96
+ count(args?: any): Promise<number>;
97
+ aggregate(args?: any): Promise<any>;
98
+ groupBy(args?: any): Promise<any[]>;
99
+ vectorSearch(args: any): Promise<T[]>;
100
+ create(): Promise<never>;
101
+ createMany(): Promise<never>;
102
+ update(): Promise<never>;
103
+ updateMany(): Promise<never>;
104
+ delete(): Promise<never>;
105
+ deleteMany(): Promise<never>;
106
+ upsert(): Promise<never>;
107
+ }
108
+ export declare class An5ORM {
109
+ private readonly inTransaction;
110
+ private transactionControl?;
111
+ [key: string]: any;
112
+ private middlewares;
113
+ private eventListeners;
114
+ slowQueryThresholdMs: number;
115
+ readonly metadata: An5Metadata;
116
+ private customExecutor?;
117
+ constructor(customExecutor?: ExecutorFn | An5Adapter | any, metadata?: An5Metadata, inTransaction?: boolean, transactionControl?: Pick<InteractiveTransactionExecutor, "commit" | "rollback"> | undefined);
118
+ $on(event: "query", listener: EventListener<QueryEvent>): void;
119
+ $on(event: "info" | "warn" | "error", listener: EventListener<LogEvent>): void;
120
+ $off(event: string, listener: EventListener): void;
121
+ $emit(event: string, payload: any): void;
122
+ private getExecutor;
123
+ table(name: string): TableClient;
124
+ view(name: string): ViewClient;
125
+ $view(name: string): ViewClient;
126
+ $queryProc<T = any>(procName: string, params?: Record<string, any> | any[]): Promise<T[]>;
127
+ $executeProc(procName: string, params?: Record<string, any> | any[]): Promise<number>;
128
+ $queryFunction<T = any>(fnName: string, params?: Record<string, any> | any[]): Promise<T[]>;
129
+ $use(middleware: Middleware): void;
130
+ parseWhere(modelName: string, where: any, params: Record<string, any>, prefix?: string): string;
131
+ _executeMiddleware(params: MiddlewareParams, finalAction: (params: MiddlewareParams) => Promise<any>): Promise<any>;
132
+ $connect(): Promise<void>;
133
+ $disconnect(): Promise<void>;
134
+ $queryRaw(queryParts: any, ...values: any[]): Promise<any[]>;
135
+ $queryRawUnsafe<R = any>(queryText: string, ...values: any[]): Promise<R>;
136
+ $executeRaw(queryParts: any, ...values: any[]): Promise<number>;
137
+ $executeRawUnsafe(queryText: string, ...values: any[]): Promise<number>;
138
+ $transaction<R>(fn: ((tx: any) => Promise<R>) | Promise<any>[], options?: {
139
+ timeout?: number;
140
+ }): Promise<any>;
141
+ $begin(): Promise<any>;
142
+ $commit(): Promise<void>;
143
+ $rollback(): Promise<void>;
144
+ }
145
+ export declare const an5Orm: An5ORM;
146
+ export default an5Orm;
147
+ //# sourceMappingURL=orm.d.ts.map