turbine-orm 0.81.0 → 0.90.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.
- package/README.md +26 -25
- package/dist/checkout.d.ts +27 -2
- package/dist/checkout.js +75 -4
- package/dist/cjs/checkout.d.ts +27 -2
- package/dist/cjs/checkout.js +76 -3
- package/dist/cjs/cli/code-first-tags.d.ts +62 -0
- package/dist/cjs/cli/code-first-tags.js +124 -0
- package/dist/cjs/cli/compile-query.js +9 -0
- package/dist/cjs/cli/config.d.ts +35 -6
- package/dist/cjs/cli/config.js +17 -1
- package/dist/cjs/cli/destructive.d.ts +7 -4
- package/dist/cjs/cli/destructive.js +224 -20
- package/dist/cjs/cli/error-catalog.js +36 -0
- package/dist/cjs/cli/error-text.d.ts +15 -0
- package/dist/cjs/cli/error-text.js +40 -0
- package/dist/cjs/cli/index.d.ts +62 -4
- package/dist/cjs/cli/index.js +568 -108
- package/dist/cjs/cli/local-server-guard.d.ts +53 -0
- package/dist/cjs/cli/local-server-guard.js +119 -0
- package/dist/cjs/cli/local-server-output.d.ts +41 -0
- package/dist/cjs/cli/local-server-output.js +103 -0
- package/dist/cjs/cli/mcp.d.ts +8 -0
- package/dist/cjs/cli/mcp.js +121 -32
- package/dist/cjs/cli/migrate.d.ts +113 -5
- package/dist/cjs/cli/migrate.js +270 -21
- package/dist/cjs/cli/observe.d.ts +6 -1
- package/dist/cjs/cli/observe.js +85 -55
- package/dist/cjs/cli/pii-predicate-guard.d.ts +43 -23
- package/dist/cjs/cli/pii-predicate-guard.js +98 -37
- package/dist/cjs/cli/prisma-resolve.js +17 -13
- package/dist/cjs/cli/prisma-schema.js +12 -4
- package/dist/cjs/cli/studio-ui.generated.js +1 -1
- package/dist/cjs/cli/studio.d.ts +14 -2
- package/dist/cjs/cli/studio.js +244 -69
- package/dist/cjs/cli/ui.d.ts +1 -1
- package/dist/cjs/client.d.ts +558 -85
- package/dist/cjs/client.js +1270 -395
- package/dist/cjs/connection-guard.d.ts +13 -0
- package/dist/cjs/connection-guard.js +28 -0
- package/dist/cjs/connection-url.d.ts +51 -16
- package/dist/cjs/connection-url.js +67 -34
- package/dist/cjs/diagnostics.d.ts +81 -0
- package/dist/cjs/diagnostics.js +147 -0
- package/dist/cjs/dialect.d.ts +449 -20
- package/dist/cjs/dialect.js +117 -76
- package/dist/cjs/engine-config.d.ts +7 -6
- package/dist/cjs/errors.d.ts +126 -6
- package/dist/cjs/errors.js +256 -13
- package/dist/cjs/generate.d.ts +57 -7
- package/dist/cjs/generate.js +635 -279
- package/dist/cjs/global-filters-config.d.ts +59 -0
- package/dist/cjs/global-filters-config.js +367 -0
- package/dist/cjs/index.d.ts +9 -4
- package/dist/cjs/index.js +10 -2
- package/dist/cjs/introspect.d.ts +31 -6
- package/dist/cjs/introspect.js +108 -50
- package/dist/cjs/log-codes.d.ts +12 -0
- package/dist/cjs/log-codes.js +77 -0
- package/dist/cjs/logger.d.ts +87 -0
- package/dist/cjs/logger.js +98 -0
- package/dist/cjs/mssql.d.ts +8 -5
- package/dist/cjs/mssql.js +352 -65
- package/dist/cjs/mysql.d.ts +3 -5
- package/dist/cjs/mysql.js +420 -70
- package/dist/cjs/nested-write.d.ts +3 -0
- package/dist/cjs/nested-write.js +179 -114
- package/dist/cjs/pipeline-submittable.d.ts +12 -0
- package/dist/cjs/pipeline-submittable.js +118 -44
- package/dist/cjs/pipeline.js +21 -26
- package/dist/cjs/powdb-introspect.d.ts +5 -2
- package/dist/cjs/powdb-introspect.js +20 -15
- package/dist/cjs/powdb-shared.d.ts +36 -5
- package/dist/cjs/powdb-shared.js +67 -8
- package/dist/cjs/powdb.d.ts +4 -0
- package/dist/cjs/powdb.js +183 -51
- package/dist/cjs/powql.d.ts +394 -51
- package/dist/cjs/powql.js +1829 -284
- package/dist/cjs/prisma-compat.d.ts +23 -15
- package/dist/cjs/prisma-compat.js +507 -372
- package/dist/cjs/query/aggregates.d.ts +17 -1
- package/dist/cjs/query/aggregates.js +261 -29
- package/dist/cjs/query/batched-loader.d.ts +25 -6
- package/dist/cjs/query/batched-loader.js +103 -20
- package/dist/cjs/query/builder.d.ts +325 -184
- package/dist/cjs/query/builder.js +1452 -490
- package/dist/cjs/query/column-ref.d.ts +61 -0
- package/dist/cjs/query/column-ref.js +73 -0
- package/dist/cjs/query/compound-unique.d.ts +21 -0
- package/dist/cjs/query/compound-unique.js +98 -18
- package/dist/cjs/query/deferred.d.ts +53 -16
- package/dist/cjs/query/expr.d.ts +217 -0
- package/dist/cjs/query/expr.js +859 -0
- package/dist/cjs/query/filters.d.ts +155 -53
- package/dist/cjs/query/filters.js +613 -76
- package/dist/cjs/query/index.d.ts +2 -0
- package/dist/cjs/query/index.js +7 -1
- package/dist/cjs/query/json-value.d.ts +43 -0
- package/dist/cjs/query/json-value.js +59 -0
- package/dist/cjs/query/normalize-args.d.ts +87 -0
- package/dist/cjs/query/normalize-args.js +527 -0
- package/dist/cjs/query/option-surface.d.ts +16 -4
- package/dist/cjs/query/option-surface.js +57 -14
- package/dist/cjs/query/relation-names.d.ts +14 -0
- package/dist/cjs/query/relation-names.js +77 -18
- package/dist/cjs/query/relations.d.ts +30 -3
- package/dist/cjs/query/relations.js +219 -63
- package/dist/cjs/query/row-lock.d.ts +49 -0
- package/dist/cjs/query/row-lock.js +138 -0
- package/dist/cjs/query/sql-comment.d.ts +161 -0
- package/dist/cjs/query/sql-comment.js +381 -0
- package/dist/cjs/query/stream-idle.d.ts +132 -0
- package/dist/cjs/query/stream-idle.js +212 -0
- package/dist/cjs/query/table-client.d.ts +101 -0
- package/dist/cjs/query/table-client.js +74 -0
- package/dist/cjs/query/types.d.ts +677 -298
- package/dist/cjs/query/utils.d.ts +105 -6
- package/dist/cjs/query/utils.js +426 -19
- package/dist/cjs/query/warn-registry.d.ts +18 -0
- package/dist/cjs/query/warn-registry.js +18 -0
- package/dist/cjs/query/where-compile.d.ts +138 -14
- package/dist/cjs/query/where-compile.js +513 -29
- package/dist/cjs/query/where.d.ts +159 -59
- package/dist/cjs/query/where.js +716 -246
- package/dist/cjs/query/writes.d.ts +112 -50
- package/dist/cjs/query/writes.js +1498 -200
- package/dist/cjs/query-events.d.ts +4 -2
- package/dist/cjs/query-events.js +3 -1
- package/dist/cjs/realtime.d.ts +7 -3
- package/dist/cjs/realtime.js +13 -2
- package/dist/cjs/schema-builder.d.ts +55 -4
- package/dist/cjs/schema-builder.js +382 -18
- package/dist/cjs/schema-metadata.d.ts +21 -1
- package/dist/cjs/schema-metadata.js +29 -7
- package/dist/cjs/schema-sql.d.ts +75 -5
- package/dist/cjs/schema-sql.js +497 -45
- package/dist/cjs/schema.d.ts +26 -5
- package/dist/cjs/schema.js +110 -13
- package/dist/cjs/sql-placeholders.d.ts +56 -0
- package/dist/cjs/sql-placeholders.js +204 -0
- package/dist/cjs/sqlite.d.ts +75 -20
- package/dist/cjs/sqlite.js +593 -95
- package/dist/cjs/supplied-pool-types.d.ts +48 -0
- package/dist/cjs/supplied-pool-types.js +256 -0
- package/dist/cjs/transaction-control.d.ts +51 -0
- package/dist/cjs/transaction-control.js +316 -0
- package/dist/cjs/transaction-state.d.ts +342 -0
- package/dist/cjs/transaction-state.js +707 -0
- package/dist/cjs/typed-sql.d.ts +37 -3
- package/dist/cjs/typed-sql.js +102 -8
- package/dist/cli/code-first-tags.d.ts +62 -0
- package/dist/cli/code-first-tags.js +120 -0
- package/dist/cli/compile-query.js +9 -0
- package/dist/cli/config.d.ts +35 -6
- package/dist/cli/config.js +16 -1
- package/dist/cli/destructive.d.ts +7 -4
- package/dist/cli/destructive.js +224 -20
- package/dist/cli/error-catalog.js +36 -0
- package/dist/cli/error-text.d.ts +15 -0
- package/dist/cli/error-text.js +37 -0
- package/dist/cli/index.d.ts +62 -4
- package/dist/cli/index.js +569 -112
- package/dist/cli/local-server-guard.d.ts +53 -0
- package/dist/cli/local-server-guard.js +113 -0
- package/dist/cli/local-server-output.d.ts +41 -0
- package/dist/cli/local-server-output.js +98 -0
- package/dist/cli/mcp.d.ts +8 -0
- package/dist/cli/mcp.js +121 -34
- package/dist/cli/migrate.d.ts +113 -5
- package/dist/cli/migrate.js +261 -21
- package/dist/cli/observe.d.ts +6 -1
- package/dist/cli/observe.js +84 -54
- package/dist/cli/pii-predicate-guard.d.ts +43 -23
- package/dist/cli/pii-predicate-guard.js +100 -35
- package/dist/cli/prisma-resolve.js +15 -11
- package/dist/cli/prisma-schema.js +12 -4
- package/dist/cli/studio-ui.generated.js +1 -1
- package/dist/cli/studio.d.ts +14 -2
- package/dist/cli/studio.js +246 -71
- package/dist/cli/ui.d.ts +1 -1
- package/dist/client.d.ts +558 -85
- package/dist/client.js +1275 -400
- package/dist/connection-guard.d.ts +13 -0
- package/dist/connection-guard.js +27 -0
- package/dist/connection-url.d.ts +51 -16
- package/dist/connection-url.js +63 -34
- package/dist/diagnostics.d.ts +81 -0
- package/dist/diagnostics.js +144 -0
- package/dist/dialect.d.ts +449 -20
- package/dist/dialect.js +115 -43
- package/dist/engine-config.d.ts +7 -6
- package/dist/errors.d.ts +126 -6
- package/dist/errors.js +252 -12
- package/dist/generate.d.ts +57 -7
- package/dist/generate.js +635 -281
- package/dist/global-filters-config.d.ts +59 -0
- package/dist/global-filters-config.js +361 -0
- package/dist/index.d.ts +9 -4
- package/dist/index.js +4 -2
- package/dist/introspect.d.ts +31 -6
- package/dist/introspect.js +106 -50
- package/dist/log-codes.d.ts +12 -0
- package/dist/log-codes.js +74 -0
- package/dist/logger.d.ts +87 -0
- package/dist/logger.js +93 -0
- package/dist/mssql.d.ts +8 -5
- package/dist/mssql.js +354 -67
- package/dist/mysql.d.ts +3 -5
- package/dist/mysql.js +423 -73
- package/dist/nested-write.d.ts +3 -0
- package/dist/nested-write.js +177 -112
- package/dist/pipeline-submittable.d.ts +12 -0
- package/dist/pipeline-submittable.js +117 -44
- package/dist/pipeline.js +21 -26
- package/dist/powdb-introspect.d.ts +5 -2
- package/dist/powdb-introspect.js +21 -16
- package/dist/powdb-shared.d.ts +36 -5
- package/dist/powdb-shared.js +65 -8
- package/dist/powdb.d.ts +4 -0
- package/dist/powdb.js +183 -51
- package/dist/powql.d.ts +394 -51
- package/dist/powql.js +1836 -291
- package/dist/prisma-compat.d.ts +23 -15
- package/dist/prisma-compat.js +508 -373
- package/dist/query/aggregates.d.ts +17 -1
- package/dist/query/aggregates.js +263 -32
- package/dist/query/batched-loader.d.ts +25 -6
- package/dist/query/batched-loader.js +101 -21
- package/dist/query/builder.d.ts +325 -184
- package/dist/query/builder.js +1458 -495
- package/dist/query/column-ref.d.ts +61 -0
- package/dist/query/column-ref.js +66 -0
- package/dist/query/compound-unique.d.ts +21 -0
- package/dist/query/compound-unique.js +97 -19
- package/dist/query/deferred.d.ts +53 -16
- package/dist/query/expr.d.ts +217 -0
- package/dist/query/expr.js +848 -0
- package/dist/query/filters.d.ts +155 -53
- package/dist/query/filters.js +597 -72
- package/dist/query/index.d.ts +2 -0
- package/dist/query/index.js +4 -0
- package/dist/query/json-value.d.ts +43 -0
- package/dist/query/json-value.js +54 -0
- package/dist/query/normalize-args.d.ts +87 -0
- package/dist/query/normalize-args.js +517 -0
- package/dist/query/option-surface.d.ts +16 -4
- package/dist/query/option-surface.js +56 -13
- package/dist/query/relation-names.d.ts +14 -0
- package/dist/query/relation-names.js +77 -19
- package/dist/query/relations.d.ts +30 -3
- package/dist/query/relations.js +220 -67
- package/dist/query/row-lock.d.ts +49 -0
- package/dist/query/row-lock.js +131 -0
- package/dist/query/sql-comment.d.ts +161 -0
- package/dist/query/sql-comment.js +374 -0
- package/dist/query/stream-idle.d.ts +132 -0
- package/dist/query/stream-idle.js +203 -0
- package/dist/query/table-client.d.ts +101 -0
- package/dist/query/table-client.js +70 -0
- package/dist/query/types.d.ts +677 -298
- package/dist/query/utils.d.ts +105 -6
- package/dist/query/utils.js +414 -18
- package/dist/query/warn-registry.d.ts +18 -0
- package/dist/query/warn-registry.js +18 -0
- package/dist/query/where-compile.d.ts +138 -14
- package/dist/query/where-compile.js +508 -31
- package/dist/query/where.d.ts +159 -59
- package/dist/query/where.js +711 -248
- package/dist/query/writes.d.ts +112 -50
- package/dist/query/writes.js +1496 -202
- package/dist/query-events.d.ts +4 -2
- package/dist/query-events.js +3 -1
- package/dist/realtime.d.ts +7 -3
- package/dist/realtime.js +13 -2
- package/dist/schema-builder.d.ts +55 -4
- package/dist/schema-builder.js +381 -18
- package/dist/schema-metadata.d.ts +21 -1
- package/dist/schema-metadata.js +28 -7
- package/dist/schema-sql.d.ts +75 -5
- package/dist/schema-sql.js +494 -44
- package/dist/schema.d.ts +26 -5
- package/dist/schema.js +109 -13
- package/dist/sql-placeholders.d.ts +56 -0
- package/dist/sql-placeholders.js +200 -0
- package/dist/sqlite.d.ts +75 -20
- package/dist/sqlite.js +597 -99
- package/dist/supplied-pool-types.d.ts +48 -0
- package/dist/supplied-pool-types.js +247 -0
- package/dist/transaction-control.d.ts +51 -0
- package/dist/transaction-control.js +311 -0
- package/dist/transaction-state.d.ts +342 -0
- package/dist/transaction-state.js +696 -0
- package/dist/typed-sql.d.ts +37 -3
- package/dist/typed-sql.js +100 -8
- package/package.json +7 -4
- package/skills/turbine-orm/SKILL.md +56 -6
package/dist/introspect.js
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import pg from '#pg';
|
|
11
11
|
import { postgresDialect } from './dialect.js';
|
|
12
12
|
import { ValidationError } from './errors.js';
|
|
13
|
+
import { consoleLogger, logTo } from './logger.js';
|
|
13
14
|
import { isDateType, pgTypeToTs, singularize, snakeToCamel, } from './schema.js';
|
|
14
15
|
/**
|
|
15
16
|
* Map a `pg_constraint.confdeltype` / `confupdtype` character to a
|
|
@@ -328,6 +329,58 @@ export function defaultExcludedTablesPresent(names, options = {}) {
|
|
|
328
329
|
*/
|
|
329
330
|
// biome-ignore lint/suspicious/noControlCharactersInRegex: detecting control characters is this pattern's purpose
|
|
330
331
|
const UNSAFE_CATALOG_CHARS = /[\u0000-\u001f\u007f-\u009f\u2028\u2029]/;
|
|
332
|
+
/**
|
|
333
|
+
* Characters that must not reach a terminal or a log verbatim: everything in
|
|
334
|
+
* {@link UNSAFE_CATALOG_CHARS} (an ESC starts an ANSI sequence, a carriage
|
|
335
|
+
* return overwrites the line), plus the bidirectional controls that reorder how
|
|
336
|
+
* a name is displayed without changing what it is.
|
|
337
|
+
*/
|
|
338
|
+
// biome-ignore lint/suspicious/noControlCharactersInRegex: neutralizing control characters is this pattern's purpose
|
|
339
|
+
const TERMINAL_UNSAFE_CHARS = /[\u0000-\u001f\u007f-\u009f\u2028\u2029\u200e\u200f\u202a-\u202e\u2066-\u2069]/g;
|
|
340
|
+
/**
|
|
341
|
+
* A catalog name made safe to PRINT. Every diagnostic that quotes a table,
|
|
342
|
+
* column, enum, relation or constraint name goes through this, because Postgres
|
|
343
|
+
* lets anyone with CREATE privilege pick any characters for those, and an
|
|
344
|
+
* unescaped name in an error message is a way to write escape sequences to the
|
|
345
|
+
* developer's terminal. Control and bidi characters become visible escapes
|
|
346
|
+
* (`\n`, `\u001b`); everything else, including non-ASCII letters, is untouched.
|
|
347
|
+
*/
|
|
348
|
+
export function printableCatalogName(name) {
|
|
349
|
+
return name.replace(TERMINAL_UNSAFE_CHARS, (ch) => {
|
|
350
|
+
if (ch === '\n')
|
|
351
|
+
return '\\n';
|
|
352
|
+
if (ch === '\r')
|
|
353
|
+
return '\\r';
|
|
354
|
+
if (ch === '\t')
|
|
355
|
+
return '\\t';
|
|
356
|
+
return `\\u${ch.charCodeAt(0).toString(16).padStart(4, '0')}`;
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* An empty record for something keyed by catalog names. Prototype-less on
|
|
361
|
+
* purpose: a plain `{}` turns a table named `__proto__` into a prototype
|
|
362
|
+
* assignment (the table silently disappears), and makes `enums['constructor']`
|
|
363
|
+
* truthy before any enum was ever added.
|
|
364
|
+
*/
|
|
365
|
+
export function nullRecord() {
|
|
366
|
+
return Object.create(null);
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* The TypeScript type for a catalog type name. A user-defined type can be named
|
|
370
|
+
* after an `Object.prototype` member (`toString`, `constructor`, `valueOf`),
|
|
371
|
+
* which a plain-object type table answers with a function instead of nothing, so
|
|
372
|
+
* that case is pinned to `unknown` here, before any type table is consulted,
|
|
373
|
+
* rather than emitted as function source. `lookup` is the dialect's own mapping
|
|
374
|
+
* for every other name.
|
|
375
|
+
*/
|
|
376
|
+
function tsTypeForCatalogType(typeName, nullable, lookup = pgTypeToTs) {
|
|
377
|
+
const isArray = typeName.startsWith('_');
|
|
378
|
+
if ((isArray ? typeName.slice(1) : typeName) in Object.prototype) {
|
|
379
|
+
const unknownType = isArray ? 'unknown[]' : 'unknown';
|
|
380
|
+
return nullable ? `${unknownType} | null` : unknownType;
|
|
381
|
+
}
|
|
382
|
+
return lookup(typeName, nullable);
|
|
383
|
+
}
|
|
331
384
|
/**
|
|
332
385
|
* Refuse one catalog identifier carrying a character from
|
|
333
386
|
* {@link UNSAFE_CATALOG_CHARS}. `subject` names the object in the error.
|
|
@@ -337,7 +390,7 @@ export function assertSafeCatalogIdentifier(name, subject) {
|
|
|
337
390
|
if (match === null)
|
|
338
391
|
return;
|
|
339
392
|
const code = match[0].charCodeAt(0).toString(16).padStart(4, '0');
|
|
340
|
-
throw new ValidationError(`Refusing to introspect ${subject}: its name contains the control character U+${code.toUpperCase()} ` +
|
|
393
|
+
throw new ValidationError(`Refusing to introspect ${printableCatalogName(subject)}: its name contains the control character U+${code.toUpperCase()} ` +
|
|
341
394
|
`at position ${match.index}. Such a name cannot be safely emitted into generated code, SQL comments, or ` +
|
|
342
395
|
`tooling output. Rename the database object, or exclude it from introspection.`);
|
|
343
396
|
}
|
|
@@ -397,14 +450,15 @@ export function assertSafeCatalogSchema(schema) {
|
|
|
397
450
|
/**
|
|
398
451
|
* Introspect a database into {@link SchemaMetadata}, routing through the active
|
|
399
452
|
* dialect's {@link Dialect.introspector} so each engine can override the catalog
|
|
400
|
-
* SQL. PostgreSQL is
|
|
453
|
+
* SQL. A dialect with none, PostgreSQL included, is read by
|
|
454
|
+
* {@link introspectPostgresCatalog}.
|
|
401
455
|
*/
|
|
402
456
|
export async function introspect(options) {
|
|
403
457
|
const dialect = options.dialect ?? postgresDialect;
|
|
404
458
|
const introspector = dialect.introspector;
|
|
405
459
|
const schema = introspector
|
|
406
460
|
? await introspector.introspect(options)
|
|
407
|
-
: //
|
|
461
|
+
: // PostgreSQL, and any dialect without an introspector, reads the Postgres catalog.
|
|
408
462
|
await introspectPostgresCatalog(options);
|
|
409
463
|
// Applied here rather than inside each introspector so every engine gets it.
|
|
410
464
|
const renamed = options.relationNames ? applyRelationRenames(schema, options.relationNames) : schema;
|
|
@@ -430,18 +484,18 @@ export async function introspect(options) {
|
|
|
430
484
|
* means the call sites it was supposed to fix break at runtime instead.
|
|
431
485
|
*/
|
|
432
486
|
export function applyRelationRenames(schema, renames) {
|
|
433
|
-
const tables =
|
|
487
|
+
const tables = Object.assign(nullRecord(), schema.tables);
|
|
434
488
|
for (const [table, mapping] of Object.entries(renames)) {
|
|
435
|
-
const meta = tables[table];
|
|
489
|
+
const meta = Object.hasOwn(tables, table) ? tables[table] : undefined;
|
|
436
490
|
if (!meta) {
|
|
437
|
-
throw new ValidationError(`relationNames: unknown table "${table}". Known tables: ${Object.keys(tables).join(', ')}.`);
|
|
491
|
+
throw new ValidationError(`relationNames: unknown table "${printableCatalogName(table)}". Known tables: ${Object.keys(tables).map(printableCatalogName).join(', ')}.`);
|
|
438
492
|
}
|
|
439
|
-
const relations =
|
|
493
|
+
const relations = Object.assign(nullRecord(), meta.relations);
|
|
440
494
|
const columnFields = new Set(Object.values(meta.columnMap));
|
|
441
495
|
for (const [from, to] of Object.entries(mapping)) {
|
|
442
496
|
if (!Object.hasOwn(relations, from)) {
|
|
443
|
-
throw new ValidationError(`relationNames: table "${table}" has no relation "${from}". ` +
|
|
444
|
-
`Derived relations: ${Object.keys(relations).join(', ') || '(none)'}.`);
|
|
497
|
+
throw new ValidationError(`relationNames: table "${printableCatalogName(table)}" has no relation "${printableCatalogName(from)}". ` +
|
|
498
|
+
`Derived relations: ${Object.keys(relations).map(printableCatalogName).join(', ') || '(none)'}.`);
|
|
445
499
|
}
|
|
446
500
|
if (from === to)
|
|
447
501
|
continue;
|
|
@@ -494,21 +548,21 @@ export function assertDistinctColumnFields(tableName, columns) {
|
|
|
494
548
|
for (const [field, names] of columnsByField) {
|
|
495
549
|
if (names.length < 2)
|
|
496
550
|
continue;
|
|
497
|
-
const quoted = names.map((n) => `"${n}"`);
|
|
551
|
+
const quoted = names.map((n) => `"${printableCatalogName(n)}"`);
|
|
498
552
|
const list = `${quoted.slice(0, -1).join(', ')} and ${quoted[quoted.length - 1]}`;
|
|
499
|
-
collisions.push(`columns ${list} ${names.length === 2 ? 'both' : 'all'} resolve to the field "${field}"`);
|
|
553
|
+
collisions.push(`columns ${list} ${names.length === 2 ? 'both' : 'all'} resolve to the field "${printableCatalogName(field)}"`);
|
|
500
554
|
}
|
|
501
555
|
if (collisions.length === 0)
|
|
502
556
|
return;
|
|
503
|
-
throw new ValidationError(`Field collision on table "${tableName}": ${collisions.join('; ')}. A client addresses one column per field, ` +
|
|
557
|
+
throw new ValidationError(`Field collision on table "${printableCatalogName(tableName)}": ${collisions.join('; ')}. A client addresses one column per field, ` +
|
|
504
558
|
'so reads would fold the colliding columns into one property and a write to that field would reach only ' +
|
|
505
559
|
'one of them. Rename one of the columns, or set `keepColumnNames: true` in turbine.config.ts ' +
|
|
506
560
|
'(`turbine generate --keep-column-names`) so every field is its raw column name.');
|
|
507
561
|
}
|
|
508
562
|
/**
|
|
509
563
|
* PostgreSQL catalog introspector: reads information_schema + pg_catalog and
|
|
510
|
-
* produces {@link SchemaMetadata}.
|
|
511
|
-
*
|
|
564
|
+
* produces {@link SchemaMetadata}. {@link introspect} calls it for PostgreSQL;
|
|
565
|
+
* call {@link introspect} for dialect routing.
|
|
512
566
|
*/
|
|
513
567
|
export async function introspectPostgresCatalog(options) {
|
|
514
568
|
const schema = options.schema ?? 'public';
|
|
@@ -581,8 +635,7 @@ export async function introspectPostgresCatalog(options) {
|
|
|
581
635
|
field: options.keepColumnNames ? row.column_name : snakeToCamel(row.column_name),
|
|
582
636
|
dialectType,
|
|
583
637
|
pgType: dialectType,
|
|
584
|
-
tsType:
|
|
585
|
-
pgTypeToTs(isArray ? dialectType : baseType, isNullable),
|
|
638
|
+
tsType: tsTypeForCatalogType(isArray ? dialectType : baseType, isNullable, dialect.typeToTypeScript?.bind(dialect) ?? pgTypeToTs),
|
|
586
639
|
nullable: isNullable,
|
|
587
640
|
hasDefault: row.column_default !== null,
|
|
588
641
|
// Server-generated = a sequence default (serial/BIGSERIAL → nextval(…))
|
|
@@ -678,9 +731,9 @@ export async function introspectPostgresCatalog(options) {
|
|
|
678
731
|
});
|
|
679
732
|
}
|
|
680
733
|
// ----- Collect enums -----
|
|
681
|
-
const enums =
|
|
734
|
+
const enums = nullRecord();
|
|
682
735
|
for (const row of enumResult.rows) {
|
|
683
|
-
if (!enums
|
|
736
|
+
if (!Object.hasOwn(enums, row.typname))
|
|
684
737
|
enums[row.typname] = [];
|
|
685
738
|
enums[row.typname].push(row.enumlabel);
|
|
686
739
|
}
|
|
@@ -726,9 +779,9 @@ export async function introspectPostgresCatalog(options) {
|
|
|
726
779
|
}
|
|
727
780
|
const foreignKeys = Array.from(fkGroups.values());
|
|
728
781
|
if (crossSchemaRefs.length > 0) {
|
|
729
|
-
|
|
730
|
-
`${crossSchemaRefs.join(', ')}. Generated clients address tables by bare name within one schema, so no ` +
|
|
731
|
-
`relation is emitted for these. Introspect the other schema separately, or add the target table to this one
|
|
782
|
+
logTo(options.logger ?? consoleLogger, 'warn', `Skipped ${crossSchemaRefs.length} foreign key(s) referencing a table outside schema "${printableCatalogName(schema)}": ` +
|
|
783
|
+
`${crossSchemaRefs.map(printableCatalogName).join(', ')}. Generated clients address tables by bare name within one schema, so no ` +
|
|
784
|
+
`relation is emitted for these. Introspect the other schema separately, or add the target table to this one.`, { code: 'introspect-cross-schema-fk', schema, references: crossSchemaRefs.length });
|
|
732
785
|
}
|
|
733
786
|
// Referential actions (ON DELETE / ON UPDATE) per constraint. Keyed
|
|
734
787
|
// "<table>::<constraint>" because the NAME alone is not unique (the same
|
|
@@ -761,16 +814,17 @@ export async function introspectPostgresCatalog(options) {
|
|
|
761
814
|
enums,
|
|
762
815
|
fkActions,
|
|
763
816
|
legacyToManyUniques: options.legacyToManyUniques,
|
|
817
|
+
...(options.logger !== undefined ? { logger: options.logger } : {}),
|
|
764
818
|
});
|
|
765
819
|
// ----- Assemble TableMetadata for each table -----
|
|
766
|
-
const tables =
|
|
820
|
+
const tables = nullRecord();
|
|
767
821
|
for (const tableName of tableNames) {
|
|
768
822
|
const columns = columnsByTable.get(tableName) ?? [];
|
|
769
|
-
const columnMap =
|
|
770
|
-
const reverseColumnMap =
|
|
823
|
+
const columnMap = nullRecord();
|
|
824
|
+
const reverseColumnMap = nullRecord();
|
|
771
825
|
const dateColumns = new Set();
|
|
772
|
-
const dialectTypes =
|
|
773
|
-
const pgTypes =
|
|
826
|
+
const dialectTypes = nullRecord();
|
|
827
|
+
const pgTypes = nullRecord();
|
|
774
828
|
const allColumns = [];
|
|
775
829
|
for (const col of columns) {
|
|
776
830
|
columnMap[col.field] = col.name;
|
|
@@ -794,7 +848,7 @@ export async function introspectPostgresCatalog(options) {
|
|
|
794
848
|
allColumns,
|
|
795
849
|
primaryKey: pkByTable.get(tableName) ?? [],
|
|
796
850
|
uniqueColumns: uniqueByTable.get(tableName) ?? [],
|
|
797
|
-
relations: relationsByTable.get(tableName) ??
|
|
851
|
+
relations: relationsByTable.get(tableName) ?? nullRecord(),
|
|
798
852
|
indexes: indexesByTable.get(tableName) ?? [],
|
|
799
853
|
checks: checksByTable.get(tableName) ?? [],
|
|
800
854
|
...(viewNameSet.has(tableName) ? { isView: true } : {}),
|
|
@@ -1181,13 +1235,13 @@ export function detectUniqueForeignKeySets(pkByTable, uniqueByTable, indexesByTa
|
|
|
1181
1235
|
* colliding name would otherwise shadow a column field and generate types
|
|
1182
1236
|
* that fail `tsc --strict` (TS2430/TS2322).
|
|
1183
1237
|
*/
|
|
1184
|
-
function resolveRelationNameCollision(candidate, taken, table, source) {
|
|
1238
|
+
function resolveRelationNameCollision(candidate, taken, table, source, logger) {
|
|
1185
1239
|
if (!taken.has(candidate))
|
|
1186
1240
|
return candidate;
|
|
1187
1241
|
let name = `${candidate}Rel`;
|
|
1188
1242
|
for (let i = 2; taken.has(name); i++)
|
|
1189
1243
|
name = `${candidate}Rel${i}`;
|
|
1190
|
-
|
|
1244
|
+
logTo(logger, 'warn', printableCatalogName(`Relation name "${candidate}" on table "${table}" (from ${source}) collides with an existing column or relation, using "${name}" instead.`), { code: 'introspect-relation-renamed', table, relation: name, wanted: candidate });
|
|
1191
1245
|
return name;
|
|
1192
1246
|
}
|
|
1193
1247
|
/**
|
|
@@ -1229,7 +1283,7 @@ function resolveRelationNameCollision(candidate, taken, table, source) {
|
|
|
1229
1283
|
* engine introspectors and `defineSchema` path do) to keep every reverse
|
|
1230
1284
|
* relation `hasMany`.
|
|
1231
1285
|
*/
|
|
1232
|
-
export function buildRelationsFromForeignKeys(foreignKeys, columnFieldsByTable, fkActions, unknownTypedFieldsByTable, uniqueSetsByTable) {
|
|
1286
|
+
export function buildRelationsFromForeignKeys(foreignKeys, columnFieldsByTable, fkActions, unknownTypedFieldsByTable, uniqueSetsByTable, logger = consoleLogger) {
|
|
1233
1287
|
// Count FKs per (source, target) pair for disambiguation.
|
|
1234
1288
|
const fkCounts = new Map();
|
|
1235
1289
|
for (const fk of foreignKeys) {
|
|
@@ -1265,18 +1319,19 @@ export function buildRelationsFromForeignKeys(foreignKeys, columnFieldsByTable,
|
|
|
1265
1319
|
const taken = takenFor(table);
|
|
1266
1320
|
if (!taken.has(legacy))
|
|
1267
1321
|
return legacy;
|
|
1268
|
-
//
|
|
1269
|
-
// (
|
|
1270
|
-
// typeSafeRelations() keeps the generated
|
|
1322
|
+
// A relation named like a json/jsonb column keeps that name: it works at
|
|
1323
|
+
// runtime and compiles (the column's `unknown` type absorbs the relation
|
|
1324
|
+
// payload). Keep it and warn; typeSafeRelations() keeps the generated types
|
|
1325
|
+
// sound.
|
|
1271
1326
|
if (!assignedFor(table).has(legacy) && unknownTypedFieldsByTable?.get(table)?.has(legacy)) {
|
|
1272
|
-
|
|
1327
|
+
logTo(logger, 'warn', printableCatalogName(`Relation "${legacy}" on table "${table}" (from ${source}) shadows the json/jsonb column ` +
|
|
1273
1328
|
`"${legacy}", keeping the historical name for runtime compatibility; the relation is omitted from ` +
|
|
1274
|
-
`the generated types. Rename the column to expose it.`);
|
|
1329
|
+
`the generated types. Rename the column to expose it.`), { code: 'introspect-relation-shadows-json', table, relation: legacy });
|
|
1275
1330
|
return legacy;
|
|
1276
1331
|
}
|
|
1277
1332
|
if (modern !== null && modern !== legacy && !taken.has(modern))
|
|
1278
1333
|
return modern;
|
|
1279
|
-
return resolveRelationNameCollision(modern ?? legacy, taken, table, source);
|
|
1334
|
+
return resolveRelationNameCollision(modern ?? legacy, taken, table, source, logger);
|
|
1280
1335
|
};
|
|
1281
1336
|
for (const fk of foreignKeys) {
|
|
1282
1337
|
const pairKey = `${fk.sourceTable}→${fk.targetTable}`;
|
|
@@ -1325,7 +1380,7 @@ export function buildRelationsFromForeignKeys(foreignKeys, columnFieldsByTable,
|
|
|
1325
1380
|
if (actions?.onUpdate && actions.onUpdate !== 'no action')
|
|
1326
1381
|
actionFields.onUpdate = actions.onUpdate;
|
|
1327
1382
|
if (!relationsByTable.has(fk.sourceTable))
|
|
1328
|
-
relationsByTable.set(fk.sourceTable,
|
|
1383
|
+
relationsByTable.set(fk.sourceTable, nullRecord());
|
|
1329
1384
|
relationsByTable.get(fk.sourceTable)[belongsToName] = {
|
|
1330
1385
|
type: 'belongsTo',
|
|
1331
1386
|
name: belongsToName,
|
|
@@ -1366,7 +1421,7 @@ export function buildRelationsFromForeignKeys(foreignKeys, columnFieldsByTable,
|
|
|
1366
1421
|
takenFor(fk.targetTable).add(reverseName);
|
|
1367
1422
|
assignedFor(fk.targetTable).add(reverseName);
|
|
1368
1423
|
if (!relationsByTable.has(fk.targetTable))
|
|
1369
|
-
relationsByTable.set(fk.targetTable,
|
|
1424
|
+
relationsByTable.set(fk.targetTable, nullRecord());
|
|
1370
1425
|
relationsByTable.get(fk.targetTable)[reverseName] = {
|
|
1371
1426
|
type: reverseType,
|
|
1372
1427
|
name: reverseName,
|
|
@@ -1405,7 +1460,7 @@ export function buildRelationsFromForeignKeys(foreignKeys, columnFieldsByTable,
|
|
|
1405
1460
|
* - a shadowed concrete-typed column → deterministic `Rel` suffix + warn
|
|
1406
1461
|
* instead of silently dropping the relation.
|
|
1407
1462
|
*/
|
|
1408
|
-
export function addAutoManyToManyRelations(tableNames, foreignKeys, pkByTable, columnNamesByTable, relationsByTable, columnFieldsByTable, unknownTypedFieldsByTable, uniqueIndexColsByTable) {
|
|
1463
|
+
export function addAutoManyToManyRelations(tableNames, foreignKeys, pkByTable, columnNamesByTable, relationsByTable, columnFieldsByTable, unknownTypedFieldsByTable, uniqueIndexColsByTable, logger = consoleLogger) {
|
|
1409
1464
|
for (const tableName of tableNames) {
|
|
1410
1465
|
// FKs whose source is this table, both must be single-column.
|
|
1411
1466
|
const tableFks = foreignKeys.filter((fk) => fk.sourceTable === tableName);
|
|
@@ -1451,22 +1506,22 @@ export function addAutoManyToManyRelations(tableNames, foreignKeys, pkByTable, c
|
|
|
1451
1506
|
const targetTbl = other.targetTable; // B
|
|
1452
1507
|
let relName = snakeToCamel(targetTbl); // plural table name → e.g. "tags"
|
|
1453
1508
|
if (!relationsByTable.has(sourceTbl))
|
|
1454
|
-
relationsByTable.set(sourceTbl,
|
|
1509
|
+
relationsByTable.set(sourceTbl, nullRecord());
|
|
1455
1510
|
const existing = relationsByTable.get(sourceTbl);
|
|
1456
1511
|
// Additive-only: never clobber an existing relation name.
|
|
1457
|
-
if (existing
|
|
1512
|
+
if (Object.hasOwn(existing, relName))
|
|
1458
1513
|
return;
|
|
1459
1514
|
const columnFields = columnFieldsByTable?.get(sourceTbl);
|
|
1460
1515
|
if (columnFields?.has(relName)) {
|
|
1461
1516
|
if (unknownTypedFieldsByTable?.get(sourceTbl)?.has(relName)) {
|
|
1462
|
-
//
|
|
1463
|
-
|
|
1517
|
+
// Same json/jsonb shadow as above: keep the name and warn.
|
|
1518
|
+
logTo(logger, 'warn', printableCatalogName(`Relation "${relName}" on table "${sourceTbl}" (junction ${tableName}) shadows the ` +
|
|
1464
1519
|
`json/jsonb column "${relName}", keeping the historical name for runtime compatibility; ` +
|
|
1465
|
-
`the relation is omitted from the generated types.`);
|
|
1520
|
+
`the relation is omitted from the generated types.`), { code: 'introspect-relation-shadows-json', table: sourceTbl, relation: relName });
|
|
1466
1521
|
}
|
|
1467
1522
|
else {
|
|
1468
1523
|
const taken = new Set([...columnFields, ...Object.keys(existing)]);
|
|
1469
|
-
relName = resolveRelationNameCollision(relName, taken, sourceTbl, `junction ${tableName}
|
|
1524
|
+
relName = resolveRelationNameCollision(relName, taken, sourceTbl, `junction ${tableName}`, logger);
|
|
1470
1525
|
}
|
|
1471
1526
|
}
|
|
1472
1527
|
existing[relName] = {
|
|
@@ -1530,7 +1585,8 @@ export function deriveCatalogRelations(inputs) {
|
|
|
1530
1585
|
const uniqueSetsByTable = inputs.legacyToManyUniques
|
|
1531
1586
|
? undefined
|
|
1532
1587
|
: detectUniqueForeignKeySets(pkByTable, uniqueByTable, indexesByTable);
|
|
1533
|
-
const
|
|
1588
|
+
const logger = inputs.logger ?? consoleLogger;
|
|
1589
|
+
const relationsByTable = buildRelationsFromForeignKeys(foreignKeys, columnFieldsByTable, inputs.fkActions, unknownTypedFieldsByTable, uniqueSetsByTable, logger);
|
|
1534
1590
|
// Prisma's implicit m2m junctions have no primary key (just a two-column
|
|
1535
1591
|
// UNIQUE index over the FK columns), so pass the introspected two-column
|
|
1536
1592
|
// unique indexes as the fallback junction-key source.
|
|
@@ -1559,7 +1615,7 @@ export function deriveCatalogRelations(inputs) {
|
|
|
1559
1615
|
if (twoColUniques.length > 0)
|
|
1560
1616
|
uniqueIndexColsByTable.set(tbl, twoColUniques);
|
|
1561
1617
|
}
|
|
1562
|
-
addAutoManyToManyRelations(tableNames, foreignKeys, pkByTable, new Map(Array.from(columnsByTable, ([tbl, cols]) => [tbl, cols.map((c) => c.name)])), relationsByTable, columnFieldsByTable, unknownTypedFieldsByTable, uniqueIndexColsByTable);
|
|
1618
|
+
addAutoManyToManyRelations(tableNames, foreignKeys, pkByTable, new Map(Array.from(columnsByTable, ([tbl, cols]) => [tbl, cols.map((c) => c.name)])), relationsByTable, columnFieldsByTable, unknownTypedFieldsByTable, uniqueIndexColsByTable, logger);
|
|
1563
1619
|
return relationsByTable;
|
|
1564
1620
|
}
|
|
1565
1621
|
/**
|
|
@@ -1571,7 +1627,7 @@ export function deriveCatalogRelations(inputs) {
|
|
|
1571
1627
|
* relation names for the same logical schema (the engines previously carried
|
|
1572
1628
|
* stale copies of a retired naming scheme).
|
|
1573
1629
|
*/
|
|
1574
|
-
export function deriveEngineRelations(tableNames, foreignKeys, pkByTable, columnsByTable) {
|
|
1630
|
+
export function deriveEngineRelations(tableNames, foreignKeys, pkByTable, columnsByTable, logger = consoleLogger) {
|
|
1575
1631
|
const tableSet = new Set(tableNames);
|
|
1576
1632
|
const fks = foreignKeys.filter((fk) => tableSet.has(fk.sourceTable) && tableSet.has(fk.targetTable));
|
|
1577
1633
|
const columnFieldsByTable = new Map();
|
|
@@ -1580,7 +1636,7 @@ export function deriveEngineRelations(tableNames, foreignKeys, pkByTable, column
|
|
|
1580
1636
|
columnFieldsByTable.set(tbl, new Set(cols.map((c) => c.field)));
|
|
1581
1637
|
unknownTypedFieldsByTable.set(tbl, new Set(cols.filter((c) => isUnknownTsType(c.tsType)).map((c) => c.field)));
|
|
1582
1638
|
}
|
|
1583
|
-
const relationsByTable = buildRelationsFromForeignKeys(fks, columnFieldsByTable, undefined, unknownTypedFieldsByTable);
|
|
1584
|
-
addAutoManyToManyRelations(tableNames, fks, pkByTable, new Map(Array.from(columnsByTable, ([tbl, cols]) => [tbl, cols.map((c) => c.name)])), relationsByTable, columnFieldsByTable, unknownTypedFieldsByTable);
|
|
1639
|
+
const relationsByTable = buildRelationsFromForeignKeys(fks, columnFieldsByTable, undefined, unknownTypedFieldsByTable, undefined, logger);
|
|
1640
|
+
addAutoManyToManyRelations(tableNames, fks, pkByTable, new Map(Array.from(columnsByTable, ([tbl, cols]) => [tbl, cols.map((c) => c.name)])), relationsByTable, columnFieldsByTable, unknownTypedFieldsByTable, undefined, logger);
|
|
1585
1641
|
return relationsByTable;
|
|
1586
1642
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* turbine-orm, every `code` a Turbine log line can carry.
|
|
3
|
+
*
|
|
4
|
+
* A log line's TEXT is not a stable contract; its `code` is. This list is the
|
|
5
|
+
* whole set. Library code imports it as a TYPE only (see `TurbineLogCode` in
|
|
6
|
+
* logger.ts), so an emitter cannot use a code that is not here and the list
|
|
7
|
+
* itself never reaches a bundle. The tests import the runtime array: one test
|
|
8
|
+
* per code, and a source scan that fails on a code nothing emits.
|
|
9
|
+
*/
|
|
10
|
+
export declare const LOG_CODES: readonly ["unknown-config-key", "plan-cache-mode-ignored", "prepared-statements-on-pooler", "connection-option-ignored", "pool-error", "query-listener-error", "pool-listener-error", "replica-close-error", "external-pool", "pool-created", "replicas-configured", "pipeline", "raw-sql", "transaction-committed", "transaction-rolled-back", "listen", "notify", "connection-verified", "disconnect-skipped", "pool-disconnected", "typed-sql", "listen-reconnecting", "listen-lost", "parser-overwrite", "untyped-columns", "unordered-page", "auto-strategy", "deep-with", "flatten-blocked", "redundant-sort-term", "unknown-query-option", "unlimited-find-many", "temporal-infinity", "cache-mismatch", "stream-idle-timeout", "sql-comment-dropped", "relation-payload-json", "flatten-fallback", "unindexed-relation", "prisma-compat-unknown-option", "prisma-compat-native-spelling", "prisma-map-stale", "prisma-compat-junction-collision", "powdb-link-collision", "powdb-link-drift", "powdb-introspect-no-primary-key", "introspect-cross-schema-fk", "introspect-relation-renamed", "introspect-relation-shadows-json", "generate-relation-shadows-column"];
|
|
11
|
+
/** A stable, machine-readable log line id. */
|
|
12
|
+
export type TurbineLogCode = (typeof LOG_CODES)[number];
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* turbine-orm, every `code` a Turbine log line can carry.
|
|
3
|
+
*
|
|
4
|
+
* A log line's TEXT is not a stable contract; its `code` is. This list is the
|
|
5
|
+
* whole set. Library code imports it as a TYPE only (see `TurbineLogCode` in
|
|
6
|
+
* logger.ts), so an emitter cannot use a code that is not here and the list
|
|
7
|
+
* itself never reaches a bundle. The tests import the runtime array: one test
|
|
8
|
+
* per code, and a source scan that fails on a code nothing emits.
|
|
9
|
+
*/
|
|
10
|
+
export const LOG_CODES = [
|
|
11
|
+
// client.ts, construction (always on)
|
|
12
|
+
'unknown-config-key',
|
|
13
|
+
'plan-cache-mode-ignored',
|
|
14
|
+
'prepared-statements-on-pooler',
|
|
15
|
+
'connection-option-ignored',
|
|
16
|
+
// client.ts, runtime errors
|
|
17
|
+
'pool-error',
|
|
18
|
+
'query-listener-error',
|
|
19
|
+
'pool-listener-error',
|
|
20
|
+
'replica-close-error',
|
|
21
|
+
// client.ts, `logging: true` lifecycle lines
|
|
22
|
+
'external-pool',
|
|
23
|
+
'pool-created',
|
|
24
|
+
'replicas-configured',
|
|
25
|
+
'pipeline',
|
|
26
|
+
'raw-sql',
|
|
27
|
+
'transaction-committed',
|
|
28
|
+
'transaction-rolled-back',
|
|
29
|
+
'listen',
|
|
30
|
+
'notify',
|
|
31
|
+
'connection-verified',
|
|
32
|
+
'disconnect-skipped',
|
|
33
|
+
'pool-disconnected',
|
|
34
|
+
// typed-sql.ts
|
|
35
|
+
'typed-sql',
|
|
36
|
+
// realtime.ts
|
|
37
|
+
'listen-reconnecting',
|
|
38
|
+
'listen-lost',
|
|
39
|
+
// query/utils.ts, a pg type parser replaced (client construction, always on)
|
|
40
|
+
'parser-overwrite',
|
|
41
|
+
// query/builder.ts, query/utils.ts, query/option-surface.ts: per-query advice
|
|
42
|
+
'untyped-columns',
|
|
43
|
+
'unordered-page',
|
|
44
|
+
'auto-strategy',
|
|
45
|
+
'deep-with',
|
|
46
|
+
'flatten-blocked',
|
|
47
|
+
'redundant-sort-term',
|
|
48
|
+
'unknown-query-option',
|
|
49
|
+
// query/builder.ts, always on
|
|
50
|
+
'unlimited-find-many',
|
|
51
|
+
'temporal-infinity',
|
|
52
|
+
'cache-mismatch',
|
|
53
|
+
'stream-idle-timeout',
|
|
54
|
+
// query/sql-comment.ts, always on (a sqlComment.dynamic value left out)
|
|
55
|
+
'sql-comment-dropped',
|
|
56
|
+
// query/relations.ts, per-query advice
|
|
57
|
+
'relation-payload-json',
|
|
58
|
+
'flatten-fallback',
|
|
59
|
+
'unindexed-relation',
|
|
60
|
+
// prisma-compat.ts
|
|
61
|
+
'prisma-compat-unknown-option',
|
|
62
|
+
'prisma-compat-native-spelling',
|
|
63
|
+
'prisma-map-stale',
|
|
64
|
+
'prisma-compat-junction-collision',
|
|
65
|
+
// powdb.ts, powdb-introspect.ts
|
|
66
|
+
'powdb-link-collision',
|
|
67
|
+
'powdb-link-drift',
|
|
68
|
+
'powdb-introspect-no-primary-key',
|
|
69
|
+
// introspect.ts, generate.ts
|
|
70
|
+
'introspect-cross-schema-fk',
|
|
71
|
+
'introspect-relation-renamed',
|
|
72
|
+
'introspect-relation-shadows-json',
|
|
73
|
+
'generate-relation-shadows-column',
|
|
74
|
+
];
|
package/dist/logger.d.ts
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* turbine-orm, the pluggable logger.
|
|
3
|
+
*
|
|
4
|
+
* Every line Turbine writes goes through a {@link TurbineLogger}: the client's
|
|
5
|
+
* `logger` option, or {@link consoleLogger} when none is set. Each line is a
|
|
6
|
+
* human message plus a {@link TurbineLogEntry} whose `code` is stable and
|
|
7
|
+
* machine-readable; the message text is not a stable contract.
|
|
8
|
+
*
|
|
9
|
+
* No log line carries a bound parameter value or a row value, whatever
|
|
10
|
+
* `logQueryParams` says: that option governs `$on('query')` events only. An
|
|
11
|
+
* entry about a where clause names its keys, never its values.
|
|
12
|
+
*
|
|
13
|
+
* This module and `seed.ts` (a CLI-driven runner) are the only library modules
|
|
14
|
+
* that call `console` directly; a source scan fails the build on another.
|
|
15
|
+
*/
|
|
16
|
+
import type { ResolvedDiagnostics } from './diagnostics.js';
|
|
17
|
+
import type { TurbineLogCode } from './log-codes.js';
|
|
18
|
+
export type { TurbineLogCode } from './log-codes.js';
|
|
19
|
+
/** The structured half of a log line. */
|
|
20
|
+
export interface TurbineLogEntry {
|
|
21
|
+
/** Stable, machine-readable id of the line, e.g. `'pool-error'`, `'unknown-config-key'`. */
|
|
22
|
+
code: string;
|
|
23
|
+
/** The table the line is about, when there is one. */
|
|
24
|
+
table?: string;
|
|
25
|
+
/** The operation the line is about (`'findMany'`, `'$transaction'`), when there is one. */
|
|
26
|
+
operation?: string;
|
|
27
|
+
/** The error the line reports, when it reports one. */
|
|
28
|
+
error?: unknown;
|
|
29
|
+
[field: string]: unknown;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Where Turbine's log lines go. Every method is optional, and a missing method
|
|
33
|
+
* drops that level, so `{}` silences Turbine entirely. The argument order,
|
|
34
|
+
* `(message, entry)`, matches `console` and winston; pino and bunyan take
|
|
35
|
+
* `(object, message)` and need a two-line adapter.
|
|
36
|
+
*
|
|
37
|
+
* A method that throws is ignored: logging never fails the operation that
|
|
38
|
+
* logged.
|
|
39
|
+
*/
|
|
40
|
+
export interface TurbineLogger {
|
|
41
|
+
debug?(message: string, entry: TurbineLogEntry): void;
|
|
42
|
+
info?(message: string, entry: TurbineLogEntry): void;
|
|
43
|
+
warn?(message: string, entry: TurbineLogEntry): void;
|
|
44
|
+
error?(message: string, entry: TurbineLogEntry): void;
|
|
45
|
+
}
|
|
46
|
+
/** One of the four {@link TurbineLogger} levels. */
|
|
47
|
+
export type TurbineLogLevel = 'debug' | 'info' | 'warn' | 'error';
|
|
48
|
+
/**
|
|
49
|
+
* The logger a client uses when `logger` is not set. `console` is looked up
|
|
50
|
+
* on every call, not captured, so anything that replaces `console.warn` (a
|
|
51
|
+
* test, an APM agent) sees Turbine's lines.
|
|
52
|
+
*/
|
|
53
|
+
export declare const consoleLogger: TurbineLogger;
|
|
54
|
+
/**
|
|
55
|
+
* Validate the `logger` option: unset means {@link consoleLogger}; otherwise an
|
|
56
|
+
* object whose `debug` / `info` / `warn` / `error` members, where present, are
|
|
57
|
+
* functions. Other members are ignored, so a pino, winston or console object is
|
|
58
|
+
* accepted as it is. Throws `ValidationError` (E003) otherwise.
|
|
59
|
+
*/
|
|
60
|
+
export declare function resolveLogger(logger: unknown): TurbineLogger;
|
|
61
|
+
/** An entry as Turbine's own emitters build it: the code is one of {@link TurbineLogCode}. */
|
|
62
|
+
export interface InternalLogEntry extends TurbineLogEntry {
|
|
63
|
+
code: TurbineLogCode;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Send one line to `logger` at `level`. A logger without that level drops it;
|
|
67
|
+
* a logger that throws is ignored. Called as a method, so a class-based
|
|
68
|
+
* logger keeps its `this`.
|
|
69
|
+
*
|
|
70
|
+
* Callers build `message` only once they know the line is wanted (behind the
|
|
71
|
+
* `logging` flag, the advice gate, or the warn-once registry), so a silenced
|
|
72
|
+
* line costs nothing.
|
|
73
|
+
*/
|
|
74
|
+
export declare function logTo(logger: TurbineLogger, level: TurbineLogLevel, message: string, entry: InternalLogEntry): void;
|
|
75
|
+
/**
|
|
76
|
+
* What a client hands to code that wraps it (prisma-compat) or runs on its
|
|
77
|
+
* behalf: its logger and its resolved diagnostics.
|
|
78
|
+
*/
|
|
79
|
+
export interface ClientLogContext {
|
|
80
|
+
readonly logger: TurbineLogger;
|
|
81
|
+
readonly diagnostics: ResolvedDiagnostics;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* The key a `TurbineClient` exposes its {@link ClientLogContext} under.
|
|
85
|
+
* `Symbol.for`, so an ESM and a CJS copy of the package agree on it.
|
|
86
|
+
*/
|
|
87
|
+
export declare const CLIENT_LOG_CONTEXT: unique symbol;
|
package/dist/logger.js
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* turbine-orm, the pluggable logger.
|
|
3
|
+
*
|
|
4
|
+
* Every line Turbine writes goes through a {@link TurbineLogger}: the client's
|
|
5
|
+
* `logger` option, or {@link consoleLogger} when none is set. Each line is a
|
|
6
|
+
* human message plus a {@link TurbineLogEntry} whose `code` is stable and
|
|
7
|
+
* machine-readable; the message text is not a stable contract.
|
|
8
|
+
*
|
|
9
|
+
* No log line carries a bound parameter value or a row value, whatever
|
|
10
|
+
* `logQueryParams` says: that option governs `$on('query')` events only. An
|
|
11
|
+
* entry about a where clause names its keys, never its values.
|
|
12
|
+
*
|
|
13
|
+
* This module and `seed.ts` (a CLI-driven runner) are the only library modules
|
|
14
|
+
* that call `console` directly; a source scan fails the build on another.
|
|
15
|
+
*/
|
|
16
|
+
import { ValidationError } from './errors.js';
|
|
17
|
+
const LEVELS = ['debug', 'info', 'warn', 'error'];
|
|
18
|
+
/**
|
|
19
|
+
* What the default logger prints for one line: `[turbine] <message>`, plus the
|
|
20
|
+
* entry's error as a second argument when the message ends with a colon. Such
|
|
21
|
+
* a message names a failure and the error completes it (`Query listener error:`
|
|
22
|
+
* followed by the error and its stack); every other message is complete on its
|
|
23
|
+
* own and already carries what it needs from the error.
|
|
24
|
+
*/
|
|
25
|
+
function consoleArgs(message, entry) {
|
|
26
|
+
// turbine-prefix-ok: this line only ever reaches the console.* calls in consoleLogger, never an Error.
|
|
27
|
+
const line = `[turbine] ${message}`;
|
|
28
|
+
return entry.error !== undefined && message.endsWith(':') ? [line, entry.error] : [line];
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* The logger a client uses when `logger` is not set. `console` is looked up
|
|
32
|
+
* on every call, not captured, so anything that replaces `console.warn` (a
|
|
33
|
+
* test, an APM agent) sees Turbine's lines.
|
|
34
|
+
*/
|
|
35
|
+
export const consoleLogger = Object.freeze({
|
|
36
|
+
debug(message, entry) {
|
|
37
|
+
console.log(...consoleArgs(message, entry));
|
|
38
|
+
},
|
|
39
|
+
info(message, entry) {
|
|
40
|
+
console.info(...consoleArgs(message, entry));
|
|
41
|
+
},
|
|
42
|
+
warn(message, entry) {
|
|
43
|
+
console.warn(...consoleArgs(message, entry));
|
|
44
|
+
},
|
|
45
|
+
error(message, entry) {
|
|
46
|
+
console.error(...consoleArgs(message, entry));
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
/**
|
|
50
|
+
* Validate the `logger` option: unset means {@link consoleLogger}; otherwise an
|
|
51
|
+
* object whose `debug` / `info` / `warn` / `error` members, where present, are
|
|
52
|
+
* functions. Other members are ignored, so a pino, winston or console object is
|
|
53
|
+
* accepted as it is. Throws `ValidationError` (E003) otherwise.
|
|
54
|
+
*/
|
|
55
|
+
export function resolveLogger(logger) {
|
|
56
|
+
if (logger === undefined)
|
|
57
|
+
return consoleLogger;
|
|
58
|
+
if ((typeof logger !== 'object' && typeof logger !== 'function') || logger === null || Array.isArray(logger)) {
|
|
59
|
+
throw new ValidationError(`Invalid logger option: expected an object with optional debug, info, warn and error methods, got ${logger === null ? 'null' : Array.isArray(logger) ? 'an array' : typeof logger}. Pass {} to silence Turbine, or leave the option unset for console.`);
|
|
60
|
+
}
|
|
61
|
+
for (const level of LEVELS) {
|
|
62
|
+
const member = logger[level];
|
|
63
|
+
if (member !== undefined && typeof member !== 'function') {
|
|
64
|
+
throw new ValidationError(`Invalid logger option: logger.${level} must be a function (message, entry) or absent, got ${typeof member}.`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return logger;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Send one line to `logger` at `level`. A logger without that level drops it;
|
|
71
|
+
* a logger that throws is ignored. Called as a method, so a class-based
|
|
72
|
+
* logger keeps its `this`.
|
|
73
|
+
*
|
|
74
|
+
* Callers build `message` only once they know the line is wanted (behind the
|
|
75
|
+
* `logging` flag, the advice gate, or the warn-once registry), so a silenced
|
|
76
|
+
* line costs nothing.
|
|
77
|
+
*/
|
|
78
|
+
export function logTo(logger, level, message, entry) {
|
|
79
|
+
const method = logger[level];
|
|
80
|
+
if (typeof method !== 'function')
|
|
81
|
+
return;
|
|
82
|
+
try {
|
|
83
|
+
method.call(logger, message, entry);
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
// A logging failure must never become the caller's failure.
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* The key a `TurbineClient` exposes its {@link ClientLogContext} under.
|
|
91
|
+
* `Symbol.for`, so an ESM and a CJS copy of the package agree on it.
|
|
92
|
+
*/
|
|
93
|
+
export const CLIENT_LOG_CONTEXT = Symbol.for('turbine-orm.clientLogContext');
|