turbine-orm 0.76.0 → 0.77.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/dist/cjs/cli/index.js +2 -2
- package/dist/cjs/cli/migrate.js +8 -8
- package/dist/cjs/cli/studio.js +6 -3
- package/dist/cjs/client.js +10 -10
- package/dist/cjs/dialect.js +1 -1
- package/dist/cjs/errors.d.ts +2 -2
- package/dist/cjs/errors.js +21 -25
- package/dist/cjs/generate.js +1 -1
- package/dist/cjs/introspect.js +5 -5
- package/dist/cjs/mssql.js +12 -12
- package/dist/cjs/mysql.js +5 -5
- package/dist/cjs/nested-write.js +31 -31
- package/dist/cjs/observe.js +1 -1
- package/dist/cjs/pipeline-submittable.js +5 -1
- package/dist/cjs/pipeline.js +8 -0
- package/dist/cjs/powdb-introspect.d.ts +1 -1
- package/dist/cjs/powdb-introspect.js +4 -4
- package/dist/cjs/powdb-shared.d.ts +348 -0
- package/dist/cjs/powdb-shared.js +587 -0
- package/dist/cjs/powdb.d.ts +12 -299
- package/dist/cjs/powdb.js +106 -567
- package/dist/cjs/powql.d.ts +1 -1
- package/dist/cjs/powql.js +89 -89
- package/dist/cjs/prisma-compat.js +16 -16
- package/dist/cjs/query/aggregates.js +19 -20
- package/dist/cjs/query/batched-loader.js +8 -8
- package/dist/cjs/query/builder.js +8 -8
- package/dist/cjs/query/compound-unique.js +2 -2
- package/dist/cjs/query/filters.js +1 -1
- package/dist/cjs/query/relations.js +60 -48
- package/dist/cjs/query/types.js +4 -4
- package/dist/cjs/query/utils.js +5 -5
- package/dist/cjs/query/warn-registry.d.ts +7 -0
- package/dist/cjs/query/warn-registry.js +7 -0
- package/dist/cjs/query/where-compile.js +1 -1
- package/dist/cjs/query/where.js +30 -31
- package/dist/cjs/query/writes.js +7 -7
- package/dist/cjs/realtime.js +4 -4
- package/dist/cjs/schema-metadata.js +1 -1
- package/dist/cjs/schema-sql.js +3 -3
- package/dist/cjs/seed.js +1 -1
- package/dist/cjs/sqlite.js +7 -7
- package/dist/cjs/typed-sql.js +1 -1
- package/dist/cli/index.js +2 -2
- package/dist/cli/migrate.js +8 -8
- package/dist/cli/studio.js +6 -3
- package/dist/client.js +10 -10
- package/dist/dialect.js +1 -1
- package/dist/errors.d.ts +2 -2
- package/dist/errors.js +21 -25
- package/dist/generate.js +1 -1
- package/dist/introspect.js +5 -5
- package/dist/mssql.js +12 -12
- package/dist/mysql.js +5 -5
- package/dist/nested-write.js +31 -31
- package/dist/observe.js +1 -1
- package/dist/pipeline-submittable.js +6 -2
- package/dist/pipeline.js +9 -1
- package/dist/powdb-introspect.d.ts +1 -1
- package/dist/powdb-introspect.js +2 -2
- package/dist/powdb-shared.d.ts +348 -0
- package/dist/powdb-shared.js +570 -0
- package/dist/powdb.d.ts +12 -299
- package/dist/powdb.js +71 -534
- package/dist/powql.d.ts +1 -1
- package/dist/powql.js +43 -43
- package/dist/prisma-compat.js +16 -16
- package/dist/query/aggregates.js +19 -20
- package/dist/query/batched-loader.js +8 -8
- package/dist/query/builder.js +8 -8
- package/dist/query/compound-unique.js +2 -2
- package/dist/query/filters.js +1 -1
- package/dist/query/relations.js +60 -48
- package/dist/query/types.js +4 -4
- package/dist/query/utils.js +5 -5
- package/dist/query/warn-registry.d.ts +7 -0
- package/dist/query/warn-registry.js +7 -0
- package/dist/query/where-compile.js +1 -1
- package/dist/query/where.js +30 -31
- package/dist/query/writes.js +7 -7
- package/dist/realtime.js +4 -4
- package/dist/schema-metadata.js +1 -1
- package/dist/schema-sql.js +3 -3
- package/dist/seed.js +1 -1
- package/dist/sqlite.js +7 -7
- package/dist/typed-sql.js +1 -1
- package/package.json +15 -7
package/dist/cjs/cli/index.js
CHANGED
|
@@ -2650,7 +2650,7 @@ async function cmdMigrateDeploy(args, config) {
|
|
|
2650
2650
|
(0, ui_js_1.error)('Deploy blocked by migration drift');
|
|
2651
2651
|
(0, ui_js_1.errorLine)();
|
|
2652
2652
|
for (const line of (0, migrate_js_1.formatChecksumMismatchError)(plan.mismatches).split('\n')) {
|
|
2653
|
-
(0, ui_js_1.errorLine)(
|
|
2653
|
+
(0, ui_js_1.errorLine)(line);
|
|
2654
2654
|
}
|
|
2655
2655
|
(0, ui_js_1.errorLine)();
|
|
2656
2656
|
process.exit(1);
|
|
@@ -2724,7 +2724,7 @@ async function confirmDestructive(report) {
|
|
|
2724
2724
|
(0, ui_js_1.error)('DESTRUCTIVE MIGRATION DETECTED');
|
|
2725
2725
|
(0, ui_js_1.newline)();
|
|
2726
2726
|
for (const line of report.split('\n'))
|
|
2727
|
-
console.log(` ${line
|
|
2727
|
+
console.log(` ${line}`);
|
|
2728
2728
|
(0, ui_js_1.newline)();
|
|
2729
2729
|
if (!process.stdin.isTTY) {
|
|
2730
2730
|
console.log(` ${(0, ui_js_1.dim)('Non-interactive shell: rerun with')} ${(0, ui_js_1.cyan)('--allow-destructive')} ${(0, ui_js_1.dim)('to proceed.')}`);
|
package/dist/cjs/cli/migrate.js
CHANGED
|
@@ -85,7 +85,7 @@ function assertNoEmbeddedTransactions(files, section) {
|
|
|
85
85
|
if (offenders.length === 0)
|
|
86
86
|
return;
|
|
87
87
|
const lines = [
|
|
88
|
-
`
|
|
88
|
+
`Refusing to run migrations that manage transactions themselves (${section.toUpperCase()} section):`,
|
|
89
89
|
'',
|
|
90
90
|
];
|
|
91
91
|
for (const o of offenders) {
|
|
@@ -337,7 +337,7 @@ function parseMigrationContent(content, source) {
|
|
|
337
337
|
}
|
|
338
338
|
if (!sawUpMarker) {
|
|
339
339
|
throw new errors_js_1.MigrationError([
|
|
340
|
-
`
|
|
340
|
+
`Migration file has no \`-- UP\` section marker${source ? `: ${source}` : '.'}`,
|
|
341
341
|
'',
|
|
342
342
|
'A migration must contain a line reading `-- UP` (a `-- DOWN` line is optional).',
|
|
343
343
|
'Without it the whole file is a header comment: nothing would run, and the',
|
|
@@ -616,7 +616,7 @@ function createMigration(migrationsDir, name, autoContent, options) {
|
|
|
616
616
|
const recipe = exports.MIGRATION_RECIPES[options.recipe];
|
|
617
617
|
if (!recipe) {
|
|
618
618
|
const known = Object.keys(exports.MIGRATION_RECIPES).join(', ') || '(none)';
|
|
619
|
-
throw new errors_js_1.MigrationError(`
|
|
619
|
+
throw new errors_js_1.MigrationError(`Unknown migration recipe "${options.recipe}". Available recipes: ${known}`);
|
|
620
620
|
}
|
|
621
621
|
// A recipe builds the BODY, below `-- UP`, where a newline is not merely a
|
|
622
622
|
// comment break but directly executable, so it gets the safe name too.
|
|
@@ -848,7 +848,7 @@ function formatChecksumMismatchError(mismatches) {
|
|
|
848
848
|
const modified = mismatches.filter((m) => m.type === 'modified');
|
|
849
849
|
const missing = mismatches.filter((m) => m.type === 'missing');
|
|
850
850
|
const lines = [
|
|
851
|
-
'
|
|
851
|
+
'Migration drift detected, refusing to apply pending migrations.',
|
|
852
852
|
'',
|
|
853
853
|
'Applied migrations should be immutable. The following files no longer match their applied state:',
|
|
854
854
|
'',
|
|
@@ -965,7 +965,7 @@ async function migrateUp(connectionString, migrationsDir, options) {
|
|
|
965
965
|
const adapter = options?.adapter;
|
|
966
966
|
const lock = await acquireMigrationLock(client, lockId, adapter, () => openLockConnection(connectionString));
|
|
967
967
|
if (!lock.acquired) {
|
|
968
|
-
throw new errors_js_1.MigrationError('
|
|
968
|
+
throw new errors_js_1.MigrationError('Could not acquire migration lock, another migration is already running');
|
|
969
969
|
}
|
|
970
970
|
try {
|
|
971
971
|
await ensureTrackingTable(client, dialect);
|
|
@@ -1009,7 +1009,7 @@ async function migrateUp(connectionString, migrationsDir, options) {
|
|
|
1009
1009
|
// pass `allowDestructive: true`. Safe-by-default is the whole point, a
|
|
1010
1010
|
// DROP TABLE should never run just because a file exists.
|
|
1011
1011
|
if (!options?.allowDestructive && destructive.length > 0) {
|
|
1012
|
-
const lines = ['
|
|
1012
|
+
const lines = ['Refusing to apply migrations containing DESTRUCTIVE statements:', ''];
|
|
1013
1013
|
for (const o of destructive) {
|
|
1014
1014
|
lines.push(` ${o.file}`);
|
|
1015
1015
|
for (const h of o.hits) {
|
|
@@ -1189,7 +1189,7 @@ async function migrateDown(connectionString, migrationsDir, options) {
|
|
|
1189
1189
|
const adapter = options?.adapter;
|
|
1190
1190
|
const lock = await acquireMigrationLock(client, lockId, adapter, () => openLockConnection(connectionString));
|
|
1191
1191
|
if (!lock.acquired) {
|
|
1192
|
-
throw new errors_js_1.MigrationError('
|
|
1192
|
+
throw new errors_js_1.MigrationError('Could not acquire migration lock, another migration is already running');
|
|
1193
1193
|
}
|
|
1194
1194
|
try {
|
|
1195
1195
|
await ensureTrackingTable(client, dialect);
|
|
@@ -1221,7 +1221,7 @@ async function migrateDown(connectionString, migrationsDir, options) {
|
|
|
1221
1221
|
offenders.push({ file: file.filename, hits });
|
|
1222
1222
|
}
|
|
1223
1223
|
if (offenders.length > 0) {
|
|
1224
|
-
const lines = ['
|
|
1224
|
+
const lines = ['Refusing to roll back migrations whose DOWN sections are DESTRUCTIVE:', ''];
|
|
1225
1225
|
for (const o of offenders) {
|
|
1226
1226
|
lines.push(` ${o.file}`);
|
|
1227
1227
|
for (const h of o.hits) {
|
package/dist/cjs/cli/studio.js
CHANGED
|
@@ -411,6 +411,9 @@ function constantTimeEqual(a, b) {
|
|
|
411
411
|
function unknownTableMessage(name, ctx) {
|
|
412
412
|
const available = Object.keys(ctx.metadata.tables);
|
|
413
413
|
const list = available.length ? available.join(', ') : '(none)';
|
|
414
|
+
// turbine-prefix-ok: every caller passes this straight to sendJson as an HTTP
|
|
415
|
+
// body, never to a TurbineError, so nothing prepends a `[TURBINE_EXXX]` tag
|
|
416
|
+
// and the prefix is the browser's only signal of who produced the message.
|
|
414
417
|
return `[turbine] Unknown table "${name}" in schema "${ctx.options.schema}". Available: ${list}`;
|
|
415
418
|
}
|
|
416
419
|
// ---------------------------------------------------------------------------
|
|
@@ -836,17 +839,17 @@ function assertNoPiiPredicates(args, tableName, metadata, showPii) {
|
|
|
836
839
|
metadata,
|
|
837
840
|
hiddenReason: (table, column) => (isRedactedColumn(table, column, showPii) ? 'is PII-tagged and redacted' : null),
|
|
838
841
|
refuseColumn: (table, column, reason) => {
|
|
839
|
-
throw new errors_js_1.ValidationError(`
|
|
842
|
+
throw new errors_js_1.ValidationError(`Column "${column}" on "${table.name}" ${reason}, so it cannot be used ` +
|
|
840
843
|
`in a where, orderBy, cursor, or distinct: filtering, sorting, paging, or de-duplicating on a hidden ` +
|
|
841
844
|
`value reveals it. Restart Studio with --show-pii to query it.`);
|
|
842
845
|
},
|
|
843
846
|
refuseDepth: (maxDepth) => {
|
|
844
|
-
throw new errors_js_1.ValidationError(`
|
|
847
|
+
throw new errors_js_1.ValidationError(`Query is nested more than ${maxDepth} levels deep, which is past the point where ` +
|
|
845
848
|
`Studio can prove it does not filter or sort on a PII-tagged and redacted column, so it is refused. ` +
|
|
846
849
|
`Flatten the query, or restart Studio with --show-pii.`);
|
|
847
850
|
},
|
|
848
851
|
refuseShape: (table, key) => {
|
|
849
|
-
throw new errors_js_1.ValidationError(`
|
|
852
|
+
throw new errors_js_1.ValidationError(`Studio does not recognize "${key}" in a query on "${table.name}", so it cannot prove the ` +
|
|
850
853
|
`query does not filter or sort on a PII-tagged and redacted column, and refuses it rather than ` +
|
|
851
854
|
`guessing. Remove it, or restart Studio with --show-pii.`);
|
|
852
855
|
},
|
package/dist/cjs/client.js
CHANGED
|
@@ -295,7 +295,7 @@ function assertUsableConnectionString(value, source) {
|
|
|
295
295
|
// to `%20%20%20`, and resolve it against its placeholder host. Naming it as
|
|
296
296
|
// empty is more useful than reporting the phantom host.
|
|
297
297
|
if (value.trim().length === 0) {
|
|
298
|
-
throw new errors_js_1.ConnectionError(`
|
|
298
|
+
throw new errors_js_1.ConnectionError(`The ${source} is empty. Expected a Postgres connection string such as ` +
|
|
299
299
|
'"postgresql://user:password@host:5432/database".');
|
|
300
300
|
}
|
|
301
301
|
const verdict = classifyConnectionString(value);
|
|
@@ -306,7 +306,7 @@ function assertUsableConnectionString(value, source) {
|
|
|
306
306
|
const tail = '(Check for a missing "//", a stray quote copied out of a .env file, or a shell-truncated value.) ' +
|
|
307
307
|
'The value is not included here because it may contain a password.';
|
|
308
308
|
if (verdict.kind === 'unparseable') {
|
|
309
|
-
throw new errors_js_1.ConnectionError(`
|
|
309
|
+
throw new errors_js_1.ConnectionError(`The ${source} cannot be parsed as a connection string, pg rejects it with "Invalid URL" ` +
|
|
310
310
|
'(most often a non-numeric port). Expected something like ' +
|
|
311
311
|
`"postgresql://user:password@host:5432/database". ${tail}`);
|
|
312
312
|
}
|
|
@@ -317,7 +317,7 @@ function assertUsableConnectionString(value, source) {
|
|
|
317
317
|
const keywordForm = /^[A-Za-z_][A-Za-z0-9_]*\s*=/.test(value.trimStart())
|
|
318
318
|
? 'It looks like a libpq "host=… dbname=…" keyword/value string, which node-postgres does not support. '
|
|
319
319
|
: '';
|
|
320
|
-
throw new errors_js_1.ConnectionError(`
|
|
320
|
+
throw new errors_js_1.ConnectionError(`The ${source} names no host: it has no "postgres://" or "postgresql://" scheme, so pg would ` +
|
|
321
321
|
`resolve it as a relative URL and try to connect to its internal placeholder host. ${keywordForm}` +
|
|
322
322
|
`Expected something like "postgresql://user:password@host:5432/database". ${tail}`);
|
|
323
323
|
}
|
|
@@ -398,7 +398,7 @@ function resolveIsolationLevel(level) {
|
|
|
398
398
|
return undefined;
|
|
399
399
|
const sql = (0, utils_js_1.ownLookup)(ISOLATION_LEVELS, level);
|
|
400
400
|
if (sql === undefined) {
|
|
401
|
-
throw new errors_js_1.ValidationError(
|
|
401
|
+
throw new errors_js_1.ValidationError(`$transaction: unknown isolationLevel ${JSON.stringify(level)}. ` +
|
|
402
402
|
`Expected one of: ${Object.keys(ISOLATION_LEVELS).join(', ')} (case-sensitive).`);
|
|
403
403
|
}
|
|
404
404
|
return sql;
|
|
@@ -748,7 +748,7 @@ class TurbineClient {
|
|
|
748
748
|
// output), not runtime SchemaMetadata, so name the conversion rather than
|
|
749
749
|
// just the requirement.
|
|
750
750
|
const looksLikeSchemaDef = schema !== null && typeof schema === 'object' && Object.hasOwn(schema, 'name') === false;
|
|
751
|
-
throw new errors_js_1.ValidationError('
|
|
751
|
+
throw new errors_js_1.ValidationError('TurbineClient requires schema metadata as its second argument. ' +
|
|
752
752
|
'Run `npx turbine generate` and use the generated client (`turbine()` from your output dir), ' +
|
|
753
753
|
'or pass the generated `schemaMetadata` object: new TurbineClient(config, schemaMetadata).' +
|
|
754
754
|
(looksLikeSchemaDef
|
|
@@ -761,7 +761,7 @@ class TurbineClient {
|
|
|
761
761
|
// the cause. Validate one table's shape here, where the fix is obvious.
|
|
762
762
|
for (const [name, meta] of Object.entries(schema.tables)) {
|
|
763
763
|
if (!meta || typeof meta !== 'object' || !Array.isArray(meta.columns)) {
|
|
764
|
-
throw new errors_js_1.ValidationError(`
|
|
764
|
+
throw new errors_js_1.ValidationError(`Table "${name}" in the schema passed to TurbineClient has no \`columns\` array, so this is ` +
|
|
765
765
|
'not runtime SchemaMetadata. A `defineSchema()` result is a SchemaDef: convert it with ' +
|
|
766
766
|
'`schemaDefToMetadata(def)`, or use the metadata emitted by `npx turbine generate`.');
|
|
767
767
|
}
|
|
@@ -1083,7 +1083,7 @@ class TurbineClient {
|
|
|
1083
1083
|
if (value === undefined)
|
|
1084
1084
|
return undefined;
|
|
1085
1085
|
if (value !== 'null' && value !== 'preserve') {
|
|
1086
|
-
throw new errors_js_1.ValidationError(`
|
|
1086
|
+
throw new errors_js_1.ValidationError(`Invalid temporalInfinity: ${JSON.stringify(value)}. Expected 'preserve' (default: read a Postgres ` +
|
|
1087
1087
|
'temporal `infinity` as the JS number `Infinity` / `-Infinity`, which round-trips through a write ' +
|
|
1088
1088
|
"but breaks the declared `Date` type) or 'null' (read it as null, which serializes cleanly but " +
|
|
1089
1089
|
'makes it indistinguishable from a stored NULL, so a read-modify-write destroys the value).');
|
|
@@ -1095,7 +1095,7 @@ class TurbineClient {
|
|
|
1095
1095
|
return undefined;
|
|
1096
1096
|
const matched = PLAN_CACHE_MODES.find((m) => m === mode);
|
|
1097
1097
|
if (matched === undefined) {
|
|
1098
|
-
throw new errors_js_1.ValidationError(`
|
|
1098
|
+
throw new errors_js_1.ValidationError(`Invalid planCacheMode: ${JSON.stringify(mode)}. Expected one of ${PLAN_CACHE_MODES.map((m) => `'${m}'`).join(', ')}.`);
|
|
1099
1099
|
}
|
|
1100
1100
|
if (dialect.supportsPlanCacheMode !== true) {
|
|
1101
1101
|
throw new errors_js_1.UnsupportedFeatureError(`The planCacheMode option (plan_cache_mode = ${matched})`, dialect.name, '`plan_cache_mode` is a PostgreSQL plan-cache setting with no equivalent on this engine. Remove the option, ' +
|
|
@@ -1194,7 +1194,7 @@ class TurbineClient {
|
|
|
1194
1194
|
const settled = TurbineClient.utcTimestampParserMode;
|
|
1195
1195
|
if (settled === undefined || settled === want)
|
|
1196
1196
|
return;
|
|
1197
|
-
throw new errors_js_1.ValidationError(`
|
|
1197
|
+
throw new errors_js_1.ValidationError(`utcTimestamps: ${want} conflicts with utcTimestamps: ${settled}, which an earlier TurbineClient ` +
|
|
1198
1198
|
'in this process already applied. The zone-less temporal READ parsers (pg OIDs 1114, 1082, 1115, 1182) are ' +
|
|
1199
1199
|
'process-global, so they cannot ' +
|
|
1200
1200
|
'differ per client, while the WRITE side is per client. Serving both values would give this client a ' +
|
|
@@ -1717,7 +1717,7 @@ class TurbineClient {
|
|
|
1717
1717
|
}
|
|
1718
1718
|
for (const [name, value] of Object.entries(options.sessionContext)) {
|
|
1719
1719
|
if (!GUC_NAME_REGEX.test(name)) {
|
|
1720
|
-
throw new errors_js_1.ValidationError(`
|
|
1720
|
+
throw new errors_js_1.ValidationError(`Invalid session-context GUC name "${name}", must match ` +
|
|
1721
1721
|
'/^[A-Za-z_][A-Za-z0-9_]*(\\.[A-Za-z_][A-Za-z0-9_]*)?$/ (optionally namespaced, e.g. "app.current_tenant")');
|
|
1722
1722
|
}
|
|
1723
1723
|
const cfg = this.dialect.buildSetSessionConfig(name, String(value));
|
package/dist/cjs/dialect.js
CHANGED
|
@@ -117,7 +117,7 @@ exports.postgresDialect = {
|
|
|
117
117
|
},
|
|
118
118
|
buildBulkInsertStatement(input) {
|
|
119
119
|
if (!input.columnArrayTypes || input.columnArrayTypes.length !== input.columns.length) {
|
|
120
|
-
throw new errors_js_1.ValidationError(`
|
|
120
|
+
throw new errors_js_1.ValidationError(`createMany bulk insert into "${input.table}": columnArrayTypes must supply one UNNEST cast ` +
|
|
121
121
|
`per column, got ${input.columnArrayTypes?.length ?? 0} for ${input.columns.length} columns ` +
|
|
122
122
|
`(${input.columns.join(', ')}). Schema metadata is missing a pgType for at least one column; ` +
|
|
123
123
|
'regenerate it with `npx turbine generate`.');
|
package/dist/cjs/errors.d.ts
CHANGED
|
@@ -152,11 +152,11 @@ export declare function describeTargetForMessage(target: unknown): string;
|
|
|
152
152
|
* When called with an options object and no explicit `message`, a Prisma-style
|
|
153
153
|
* message is built automatically. By default, only the where-clause keys are
|
|
154
154
|
* shown to avoid leaking PII into logs:
|
|
155
|
-
* `[
|
|
155
|
+
* `[TURBINE_E001] findUniqueOrThrow on "users" found no record matching where: { id }`
|
|
156
156
|
*
|
|
157
157
|
* Set `setErrorMessageMode('verbose')` (or pass `errorMessages: 'verbose'` to
|
|
158
158
|
* the TurbineClient constructor) to include the full where values:
|
|
159
|
-
* `[
|
|
159
|
+
* `[TURBINE_E001] findUniqueOrThrow on "users" found no record matching where: {"id":1}`
|
|
160
160
|
*
|
|
161
161
|
* The full `where` object, `table`, and `operation` are always available as
|
|
162
162
|
* structured properties on the error instance regardless of mode.
|
package/dist/cjs/errors.js
CHANGED
|
@@ -465,11 +465,11 @@ function renderWhereForMessage(where, mode) {
|
|
|
465
465
|
* When called with an options object and no explicit `message`, a Prisma-style
|
|
466
466
|
* message is built automatically. By default, only the where-clause keys are
|
|
467
467
|
* shown to avoid leaking PII into logs:
|
|
468
|
-
* `[
|
|
468
|
+
* `[TURBINE_E001] findUniqueOrThrow on "users" found no record matching where: { id }`
|
|
469
469
|
*
|
|
470
470
|
* Set `setErrorMessageMode('verbose')` (or pass `errorMessages: 'verbose'` to
|
|
471
471
|
* the TurbineClient constructor) to include the full where values:
|
|
472
|
-
* `[
|
|
472
|
+
* `[TURBINE_E001] findUniqueOrThrow on "users" found no record matching where: {"id":1}`
|
|
473
473
|
*
|
|
474
474
|
* The full `where` object, `table`, and `operation` are always available as
|
|
475
475
|
* structured properties on the error instance regardless of mode.
|
|
@@ -490,14 +490,14 @@ class NotFoundError extends TurbineError {
|
|
|
490
490
|
if (!message) {
|
|
491
491
|
if (operation && table) {
|
|
492
492
|
const wherePart = where !== undefined ? ` matching where: ${renderWhereForMessage(where, currentErrorMessageMode())}` : '';
|
|
493
|
-
message =
|
|
493
|
+
message = `${operation} on "${table}" found no record${wherePart}`;
|
|
494
494
|
}
|
|
495
495
|
else if (table) {
|
|
496
496
|
const wherePart = where !== undefined ? ` matching where ${renderWhereForMessage(where, currentErrorMessageMode())}` : '';
|
|
497
|
-
message = `
|
|
497
|
+
message = `No record found in "${table}"${wherePart}`;
|
|
498
498
|
}
|
|
499
499
|
else {
|
|
500
|
-
message = '
|
|
500
|
+
message = 'Record not found';
|
|
501
501
|
}
|
|
502
502
|
}
|
|
503
503
|
super(exports.TurbineErrorCode.NOT_FOUND, message, { cause });
|
|
@@ -520,7 +520,7 @@ class TimeoutError extends TurbineError {
|
|
|
520
520
|
* when wrapping a driver error rather than a client-side timer expiry.
|
|
521
521
|
*/
|
|
522
522
|
constructor(timeoutMs, context = 'Query', options) {
|
|
523
|
-
super(exports.TurbineErrorCode.TIMEOUT, options?.message ??
|
|
523
|
+
super(exports.TurbineErrorCode.TIMEOUT, options?.message ?? `${context} timed out after ${timeoutMs}ms`, options);
|
|
524
524
|
this.name = 'TimeoutError';
|
|
525
525
|
this.timeoutMs = timeoutMs;
|
|
526
526
|
}
|
|
@@ -593,7 +593,7 @@ exports.ConnectionError = ConnectionError;
|
|
|
593
593
|
* placeholder password. Never derived from the caller's value.
|
|
594
594
|
*/
|
|
595
595
|
function malformedConnectionStringMessage(engine, example) {
|
|
596
|
-
return (`
|
|
596
|
+
return (`The ${engine} connection string could not be parsed as a URL. Expected something like "${example}". ` +
|
|
597
597
|
'(Check for a missing "//", a stray quote copied out of a .env file, or a shell-truncated value.) ' +
|
|
598
598
|
'The value is not included here because it may contain a password.');
|
|
599
599
|
}
|
|
@@ -617,7 +617,7 @@ exports.MigrationError = MigrationError;
|
|
|
617
617
|
class CircularRelationError extends TurbineError {
|
|
618
618
|
path;
|
|
619
619
|
constructor(path) {
|
|
620
|
-
super(exports.TurbineErrorCode.CIRCULAR_RELATION, `
|
|
620
|
+
super(exports.TurbineErrorCode.CIRCULAR_RELATION, `Circular or too-deep relation nesting detected: ${path.join(' → ')}. Maximum nesting depth is 10.`);
|
|
621
621
|
this.name = 'CircularRelationError';
|
|
622
622
|
this.path = path;
|
|
623
623
|
}
|
|
@@ -647,7 +647,7 @@ class UniqueConstraintError extends TurbineError {
|
|
|
647
647
|
if (!message) {
|
|
648
648
|
const constraintPart = constraint ? ` on ${constraint}` : '';
|
|
649
649
|
const columnsPart = columns && columns.length > 0 ? ` (${columns.join(', ')})` : '';
|
|
650
|
-
message = `
|
|
650
|
+
message = `Unique constraint violation${constraintPart}${columnsPart}`;
|
|
651
651
|
// PII-safe by default: the raw pg `detail` string contains the
|
|
652
652
|
// conflicting row VALUES (e.g. `Key (email)=(alice@x.com) already
|
|
653
653
|
// exists.`). Only append it in 'verbose' mode. In 'safe' mode the
|
|
@@ -678,7 +678,7 @@ class ForeignKeyError extends TurbineError {
|
|
|
678
678
|
let message = opts.message;
|
|
679
679
|
if (!message) {
|
|
680
680
|
const constraintPart = constraint ? ` on ${constraint}` : '';
|
|
681
|
-
message = `
|
|
681
|
+
message = `Foreign key constraint violation${constraintPart}`;
|
|
682
682
|
// PII-safe by default: the raw pg `detail` string contains the
|
|
683
683
|
// conflicting row VALUES (e.g. `Key (email)=(alice@x.com) already
|
|
684
684
|
// exists.`). Only append it in 'verbose' mode. In 'safe' mode the
|
|
@@ -708,7 +708,7 @@ class NotNullViolationError extends TurbineError {
|
|
|
708
708
|
let message = opts.message;
|
|
709
709
|
if (!message) {
|
|
710
710
|
const columnPart = column ? ` on column "${column}"` : '';
|
|
711
|
-
message = `
|
|
711
|
+
message = `NOT NULL constraint violation${columnPart}`;
|
|
712
712
|
// PII-safe by default: the raw pg `detail` string contains the
|
|
713
713
|
// conflicting row VALUES (e.g. `Key (email)=(alice@x.com) already
|
|
714
714
|
// exists.`). Only append it in 'verbose' mode. In 'safe' mode the
|
|
@@ -754,7 +754,7 @@ class DeadlockError extends TurbineError {
|
|
|
754
754
|
let message = opts.message;
|
|
755
755
|
if (!message) {
|
|
756
756
|
const pgMessage = cause?.message;
|
|
757
|
-
message = pgMessage ? `
|
|
757
|
+
message = pgMessage ? `Deadlock detected: ${pgMessage}` : 'Deadlock detected';
|
|
758
758
|
}
|
|
759
759
|
super(exports.TurbineErrorCode.DEADLOCK_DETECTED, message, { cause });
|
|
760
760
|
this.name = 'DeadlockError';
|
|
@@ -787,9 +787,7 @@ class SerializationFailureError extends TurbineError {
|
|
|
787
787
|
let message = opts.message;
|
|
788
788
|
if (!message) {
|
|
789
789
|
const pgMessage = cause?.message;
|
|
790
|
-
message = pgMessage
|
|
791
|
-
? `[turbine] Serializable transaction conflict: ${pgMessage}`
|
|
792
|
-
: '[turbine] Serializable transaction conflict';
|
|
790
|
+
message = pgMessage ? `Serializable transaction conflict: ${pgMessage}` : 'Serializable transaction conflict';
|
|
793
791
|
}
|
|
794
792
|
super(exports.TurbineErrorCode.SERIALIZATION_FAILURE, message, { cause });
|
|
795
793
|
this.name = 'SerializationFailureError';
|
|
@@ -805,7 +803,7 @@ class CheckConstraintError extends TurbineError {
|
|
|
805
803
|
let message = opts.message;
|
|
806
804
|
if (!message) {
|
|
807
805
|
const constraintPart = constraint ? ` on ${constraint}` : '';
|
|
808
|
-
message = `
|
|
806
|
+
message = `Check constraint violation${constraintPart}`;
|
|
809
807
|
// PII-safe by default: the raw pg `detail` string contains the
|
|
810
808
|
// conflicting row VALUES (e.g. `Key (email)=(alice@x.com) already
|
|
811
809
|
// exists.`). Only append it in 'verbose' mode. In 'safe' mode the
|
|
@@ -834,7 +832,7 @@ class ExclusionConstraintError extends TurbineError {
|
|
|
834
832
|
let message = opts.message;
|
|
835
833
|
if (!message) {
|
|
836
834
|
const constraintPart = constraint ? ` on ${constraint}` : '';
|
|
837
|
-
message = `
|
|
835
|
+
message = `Exclusion constraint violation${constraintPart}`;
|
|
838
836
|
// PII-safe by default: the raw pg `detail` string contains the
|
|
839
837
|
// conflicting row VALUES (e.g. `Key (email)=(alice@x.com) already
|
|
840
838
|
// exists.`). Only append it in 'verbose' mode. In 'safe' mode the
|
|
@@ -886,7 +884,7 @@ class PipelineError extends TurbineError {
|
|
|
886
884
|
const { results, failedIndex, failedTag, cause } = opts;
|
|
887
885
|
const failedCount = results.filter((r) => r.status === 'error').length;
|
|
888
886
|
const message = opts.message ??
|
|
889
|
-
`
|
|
887
|
+
`Pipeline completed with ${failedCount} error(s) out of ${results.length} queries` +
|
|
890
888
|
(failedTag ? ` (first failure: ${failedTag} at index ${failedIndex})` : '');
|
|
891
889
|
super(exports.TurbineErrorCode.PIPELINE, message, { cause });
|
|
892
890
|
this.name = 'PipelineError';
|
|
@@ -901,7 +899,7 @@ class OptimisticLockError extends TurbineError {
|
|
|
901
899
|
versionField;
|
|
902
900
|
expectedVersion;
|
|
903
901
|
constructor(opts) {
|
|
904
|
-
super(exports.TurbineErrorCode.OPTIMISTIC_LOCK, `
|
|
902
|
+
super(exports.TurbineErrorCode.OPTIMISTIC_LOCK, `Optimistic lock failed on "${opts.table}", ` +
|
|
905
903
|
`expected ${opts.versionField} = ${opts.expectedVersion} but row was modified by another transaction`);
|
|
906
904
|
this.name = 'OptimisticLockError';
|
|
907
905
|
this.table = opts.table;
|
|
@@ -920,7 +918,7 @@ class UnsupportedFeatureError extends TurbineError {
|
|
|
920
918
|
feature;
|
|
921
919
|
dialect;
|
|
922
920
|
constructor(feature, dialect, hint) {
|
|
923
|
-
super(exports.TurbineErrorCode.UNSUPPORTED_FEATURE,
|
|
921
|
+
super(exports.TurbineErrorCode.UNSUPPORTED_FEATURE, `${feature} is unsupported on "${dialect}".${hint ? ` ${hint}` : ''}`);
|
|
924
922
|
this.name = 'UnsupportedFeatureError';
|
|
925
923
|
this.feature = feature;
|
|
926
924
|
this.dialect = dialect;
|
|
@@ -958,7 +956,7 @@ class ReadOnlyError extends TurbineError {
|
|
|
958
956
|
* and the refusal `reason` (default `'snapshot'`).
|
|
959
957
|
*/
|
|
960
958
|
constructor(detail, options) {
|
|
961
|
-
super(exports.TurbineErrorCode.READ_ONLY,
|
|
959
|
+
super(exports.TurbineErrorCode.READ_ONLY, `${detail} Route writes to a writable primary.`, {
|
|
962
960
|
cause: options?.cause,
|
|
963
961
|
});
|
|
964
962
|
this.name = 'ReadOnlyError';
|
|
@@ -1132,7 +1130,7 @@ function wrapPgError(err) {
|
|
|
1132
1130
|
// Turbine did not set the deadline (that lives in Postgres config), so
|
|
1133
1131
|
// there is no client-side budget to report → timeoutMs = 0.
|
|
1134
1132
|
return new TimeoutError(0, 'Query', {
|
|
1135
|
-
message: '
|
|
1133
|
+
message: 'Query canceled by server-side statement_timeout',
|
|
1136
1134
|
cause: err,
|
|
1137
1135
|
});
|
|
1138
1136
|
default:
|
|
@@ -1142,9 +1140,7 @@ function wrapPgError(err) {
|
|
|
1142
1140
|
// plain-object map would happily resolve `constructor` or `toString`
|
|
1143
1141
|
// to a function and interpolate it into the message.
|
|
1144
1142
|
const hint = Object.hasOwn(CONNECTION_ERROR_HINTS, e.code) ? CONNECTION_ERROR_HINTS[e.code] : undefined;
|
|
1145
|
-
const head = pgMessage
|
|
1146
|
-
? `[turbine] Database connection error: ${pgMessage}`
|
|
1147
|
-
: `[turbine] Database connection error (${e.code})`;
|
|
1143
|
+
const head = pgMessage ? `Database connection error: ${pgMessage}` : `Database connection error (${e.code})`;
|
|
1148
1144
|
return new ConnectionError(hint ? `${head} (${e.code}) ${hint}` : head, { cause: err, sqlstate: e.code });
|
|
1149
1145
|
}
|
|
1150
1146
|
return err;
|
package/dist/cjs/generate.js
CHANGED
|
@@ -377,7 +377,7 @@ function isEmittableIdentifier(name) {
|
|
|
377
377
|
function requireEmittable(derived, subject, role) {
|
|
378
378
|
if (isEmittableIdentifier(derived))
|
|
379
379
|
return;
|
|
380
|
-
throw new errors_js_1.ValidationError(`
|
|
380
|
+
throw new errors_js_1.ValidationError(`Cannot generate code for ${subject}: it produces ${JSON.stringify(derived)} as ${role}, ` +
|
|
381
381
|
`which is not a valid TypeScript identifier. Rename the database object, or exclude it from generation ` +
|
|
382
382
|
`(introspect \`exclude\`).`);
|
|
383
383
|
}
|
package/dist/cjs/introspect.js
CHANGED
|
@@ -364,7 +364,7 @@ function assertSafeCatalogIdentifier(name, subject) {
|
|
|
364
364
|
if (match === null)
|
|
365
365
|
return;
|
|
366
366
|
const code = match[0].charCodeAt(0).toString(16).padStart(4, '0');
|
|
367
|
-
throw new errors_js_1.ValidationError(`
|
|
367
|
+
throw new errors_js_1.ValidationError(`Refusing to introspect ${subject}: its name contains the control character U+${code.toUpperCase()} ` +
|
|
368
368
|
`at position ${match.index}. Such a name cannot be safely emitted into generated code, SQL comments, or ` +
|
|
369
369
|
`tooling output. Rename the database object, or exclude it from introspection.`);
|
|
370
370
|
}
|
|
@@ -461,22 +461,22 @@ function applyRelationRenames(schema, renames) {
|
|
|
461
461
|
for (const [table, mapping] of Object.entries(renames)) {
|
|
462
462
|
const meta = tables[table];
|
|
463
463
|
if (!meta) {
|
|
464
|
-
throw new errors_js_1.ValidationError(`
|
|
464
|
+
throw new errors_js_1.ValidationError(`relationNames: unknown table "${table}". Known tables: ${Object.keys(tables).join(', ')}.`);
|
|
465
465
|
}
|
|
466
466
|
const relations = { ...meta.relations };
|
|
467
467
|
const columnFields = new Set(Object.values(meta.columnMap));
|
|
468
468
|
for (const [from, to] of Object.entries(mapping)) {
|
|
469
469
|
if (!Object.hasOwn(relations, from)) {
|
|
470
|
-
throw new errors_js_1.ValidationError(`
|
|
470
|
+
throw new errors_js_1.ValidationError(`relationNames: table "${table}" has no relation "${from}". ` +
|
|
471
471
|
`Derived relations: ${Object.keys(relations).join(', ') || '(none)'}.`);
|
|
472
472
|
}
|
|
473
473
|
if (from === to)
|
|
474
474
|
continue;
|
|
475
475
|
if (Object.hasOwn(relations, to)) {
|
|
476
|
-
throw new errors_js_1.ValidationError(`
|
|
476
|
+
throw new errors_js_1.ValidationError(`relationNames: cannot rename "${table}.${from}" to "${to}", that relation already exists.`);
|
|
477
477
|
}
|
|
478
478
|
if (columnFields.has(to)) {
|
|
479
|
-
throw new errors_js_1.ValidationError(`
|
|
479
|
+
throw new errors_js_1.ValidationError(`relationNames: cannot rename "${table}.${from}" to "${to}", a column on "${table}" ` +
|
|
480
480
|
`already uses that field name, and a relation must never shadow a column.`);
|
|
481
481
|
}
|
|
482
482
|
const def = relations[from];
|
package/dist/cjs/mssql.js
CHANGED
|
@@ -663,11 +663,11 @@ exports.mssqlDialect = {
|
|
|
663
663
|
const rowCount = input.rowValues.length;
|
|
664
664
|
const paramCount = input.rowValues.reduce((n, row) => n + row.length, 0);
|
|
665
665
|
if (rowCount > MSSQL_MAX_INSERT_ROWS) {
|
|
666
|
-
throw new errors_js_1.ValidationError(`
|
|
666
|
+
throw new errors_js_1.ValidationError(`SQL Server INSERT … VALUES is limited to ${MSSQL_MAX_INSERT_ROWS} rows per statement (got ${rowCount}). ` +
|
|
667
667
|
'Chunk the data or use individual create() calls.');
|
|
668
668
|
}
|
|
669
669
|
if (paramCount > MSSQL_MAX_PARAMS) {
|
|
670
|
-
throw new errors_js_1.ValidationError(`
|
|
670
|
+
throw new errors_js_1.ValidationError(`SQL Server is limited to ${MSSQL_MAX_PARAMS} bound parameters per statement (got ${paramCount}). ` +
|
|
671
671
|
'Reduce the batch size.');
|
|
672
672
|
}
|
|
673
673
|
let n = 0;
|
|
@@ -915,7 +915,7 @@ function buildForJsonSubquery(dialect, ctx) {
|
|
|
915
915
|
for (const [nestedRelName, nestedSpec] of sortedRelEntries(spec.with)) {
|
|
916
916
|
const nestedRelDef = targetMeta.relations[nestedRelName];
|
|
917
917
|
if (!nestedRelDef) {
|
|
918
|
-
throw new errors_js_1.RelationError(`
|
|
918
|
+
throw new errors_js_1.RelationError(`Unknown relation "${nestedRelName}" on table "${targetTable}". ` +
|
|
919
919
|
(0, utils_js_1.availableClause)(Object.keys(targetMeta.relations), 'It has no relations.'));
|
|
920
920
|
}
|
|
921
921
|
const sub = ctx.recurse(nestedRelDef, nestedSpec, parentAlias, depth + 1, [...path, relDef.name]);
|
|
@@ -937,7 +937,7 @@ function buildForJsonSubquery(dialect, ctx) {
|
|
|
937
937
|
if (typeof dir === 'object' && dir !== null) {
|
|
938
938
|
const sortValue = dir.sort;
|
|
939
939
|
if (typeof sortValue !== 'string') {
|
|
940
|
-
throw new errors_js_1.ValidationError(`
|
|
940
|
+
throw new errors_js_1.ValidationError(`Nested orderBy on "${k}" (table "${targetTable}"): only plain directions and ` +
|
|
941
941
|
`{ sort } specs are supported inside a relation orderBy on SQL Server.`);
|
|
942
942
|
}
|
|
943
943
|
if (dir.nulls !== undefined) {
|
|
@@ -950,7 +950,7 @@ function buildForJsonSubquery(dialect, ctx) {
|
|
|
950
950
|
// resolve on SQL Server too.
|
|
951
951
|
const col = targetMeta.columnMap[k] ?? (0, schema_js_1.camelToSnake)(k);
|
|
952
952
|
if (!targetMeta.allColumns.includes(col)) {
|
|
953
|
-
throw new errors_js_1.ValidationError(`
|
|
953
|
+
throw new errors_js_1.ValidationError(`Unknown field "${k}" in orderBy on table "${targetTable}". ` +
|
|
954
954
|
`Known fields: ${Object.keys(targetMeta.columnMap).join(', ') || '(none)'}.`);
|
|
955
955
|
}
|
|
956
956
|
const safeDir = String(rawDir).toLowerCase() === 'desc' ? 'DESC' : 'ASC';
|
|
@@ -1016,7 +1016,7 @@ function buildForJsonManyToMany(dialect, ctx, h) {
|
|
|
1016
1016
|
const { relDef, spec, params, parentRef, alias, targetTable, targetMeta } = ctx;
|
|
1017
1017
|
const q = (name) => dialect.quoteIdentifier(name);
|
|
1018
1018
|
if (!relDef.through) {
|
|
1019
|
-
throw new errors_js_1.ValidationError(`
|
|
1019
|
+
throw new errors_js_1.ValidationError(`manyToMany relation "${relDef.name}" is missing a \`through\` junction descriptor.`);
|
|
1020
1020
|
}
|
|
1021
1021
|
const qTarget = q(targetTable);
|
|
1022
1022
|
const qJunction = q(relDef.through.table);
|
|
@@ -1025,12 +1025,12 @@ function buildForJsonManyToMany(dialect, ctx, h) {
|
|
|
1025
1025
|
// JOIN: junction.targetKey = target.<PK>.
|
|
1026
1026
|
const targetKeys = (0, schema_js_1.normalizeKeyColumns)(relDef.through.targetKey);
|
|
1027
1027
|
if (targetMeta.primaryKey.length === 0) {
|
|
1028
|
-
throw new errors_js_1.ValidationError(`
|
|
1028
|
+
throw new errors_js_1.ValidationError(`manyToMany relation "${relDef.name}" targets table "${targetTable}" which has no primary key; ` +
|
|
1029
1029
|
'cannot determine the join column.');
|
|
1030
1030
|
}
|
|
1031
1031
|
const targetPk = targetMeta.primaryKey;
|
|
1032
1032
|
if (targetKeys.length !== targetPk.length) {
|
|
1033
|
-
throw new errors_js_1.ValidationError(`
|
|
1033
|
+
throw new errors_js_1.ValidationError(`manyToMany relation "${relDef.name}": through.targetKey has ${targetKeys.length} column(s) ` +
|
|
1034
1034
|
`but target "${targetTable}" primary key has ${targetPk.length}.`);
|
|
1035
1035
|
}
|
|
1036
1036
|
const joinOn = targetKeys.map((jcol, i) => `${jalias}.${q(jcol)} = ${alias}.${q(targetPk[i])}`).join(' AND ');
|
|
@@ -1038,7 +1038,7 @@ function buildForJsonManyToMany(dialect, ctx, h) {
|
|
|
1038
1038
|
const sourceKeys = (0, schema_js_1.normalizeKeyColumns)(relDef.through.sourceKey);
|
|
1039
1039
|
const refKeys = (0, schema_js_1.normalizeKeyColumns)(relDef.referenceKey);
|
|
1040
1040
|
if (sourceKeys.length !== refKeys.length) {
|
|
1041
|
-
throw new errors_js_1.ValidationError(`
|
|
1041
|
+
throw new errors_js_1.ValidationError(`manyToMany relation "${relDef.name}": through.sourceKey has ${sourceKeys.length} column(s) ` +
|
|
1042
1042
|
`but referenceKey has ${refKeys.length}.`);
|
|
1043
1043
|
}
|
|
1044
1044
|
const correlation = sourceKeys.map((jcol, i) => `${jalias}.${q(jcol)} = ${qParent}.${q(refKeys[i])}`).join(' AND ');
|
|
@@ -1320,13 +1320,13 @@ async function loadMssql() {
|
|
|
1320
1320
|
mod = (await (0, optional_peer_import_cjs_1.default)('mssql'));
|
|
1321
1321
|
}
|
|
1322
1322
|
catch (err) {
|
|
1323
|
-
throw new errors_js_1.ConnectionError("
|
|
1323
|
+
throw new errors_js_1.ConnectionError("turbine-orm/mssql requires the optional peer dependency 'mssql'. Install it: npm i mssql. " +
|
|
1324
1324
|
`(${err.message})`);
|
|
1325
1325
|
}
|
|
1326
1326
|
// `mssql` is a CommonJS module; the namespace may be on `default` under ESM interop.
|
|
1327
1327
|
const ns = (mod.default ?? mod);
|
|
1328
1328
|
if (typeof ns.ConnectionPool !== 'function' || typeof ns.Request !== 'function') {
|
|
1329
|
-
throw new errors_js_1.ConnectionError("
|
|
1329
|
+
throw new errors_js_1.ConnectionError("Loaded 'mssql' but it is missing ConnectionPool/Request exports.");
|
|
1330
1330
|
}
|
|
1331
1331
|
return ns;
|
|
1332
1332
|
}
|
|
@@ -1336,7 +1336,7 @@ async function loadMssql() {
|
|
|
1336
1336
|
*/
|
|
1337
1337
|
function assertSupportedVersion(majorVersion) {
|
|
1338
1338
|
if (majorVersion > 0 && majorVersion < 13) {
|
|
1339
|
-
throw new errors_js_1.ConnectionError(`
|
|
1339
|
+
throw new errors_js_1.ConnectionError(`turbine-orm/mssql requires SQL Server 2016+ (FOR JSON / OPENJSON); got major version ${majorVersion}.`);
|
|
1340
1340
|
}
|
|
1341
1341
|
}
|
|
1342
1342
|
function isMssqlPool(x) {
|
package/dist/cjs/mysql.js
CHANGED
|
@@ -1026,7 +1026,7 @@ async function introspectMysql(options) {
|
|
|
1026
1026
|
schemaName = rows[0]?.db ?? '';
|
|
1027
1027
|
}
|
|
1028
1028
|
if (!schemaName) {
|
|
1029
|
-
throw new errors_js_1.ConnectionError('
|
|
1029
|
+
throw new errors_js_1.ConnectionError('Could not determine the MySQL database to introspect, pass a database in the connection string or a `schema` option.');
|
|
1030
1030
|
}
|
|
1031
1031
|
// `await` is LOAD-BEARING, not stylistic: `return somePromise` inside a
|
|
1032
1032
|
// try/finally runs the finally BEFORE the promise settles, so `pool.end()`
|
|
@@ -1101,12 +1101,12 @@ async function loadCreatePool() {
|
|
|
1101
1101
|
mod = (await (0, optional_peer_import_cjs_1.default)('mysql2/promise'));
|
|
1102
1102
|
}
|
|
1103
1103
|
catch (err) {
|
|
1104
|
-
throw new errors_js_1.ConnectionError("
|
|
1104
|
+
throw new errors_js_1.ConnectionError("turbine-orm/mysql requires the optional peer dependency 'mysql2'. Install it: npm i mysql2. " +
|
|
1105
1105
|
`(${err.message})`);
|
|
1106
1106
|
}
|
|
1107
1107
|
const createPool = mod.createPool ?? mod.default?.createPool;
|
|
1108
1108
|
if (typeof createPool !== 'function') {
|
|
1109
|
-
throw new errors_js_1.ConnectionError("
|
|
1109
|
+
throw new errors_js_1.ConnectionError("Loaded 'mysql2/promise' but it has no createPool export.");
|
|
1110
1110
|
}
|
|
1111
1111
|
return createPool;
|
|
1112
1112
|
}
|
|
@@ -1116,12 +1116,12 @@ async function loadCreatePool() {
|
|
|
1116
1116
|
*/
|
|
1117
1117
|
function assertSupportedVersion(version) {
|
|
1118
1118
|
if (/mariadb/i.test(version)) {
|
|
1119
|
-
throw new errors_js_1.ConnectionError(`
|
|
1119
|
+
throw new errors_js_1.ConnectionError(`MariaDB is not supported by turbine-orm/mysql (got "${version}"). Use MySQL 8.0+.`);
|
|
1120
1120
|
}
|
|
1121
1121
|
const m = /^(\d+)\.(\d+)/.exec(version);
|
|
1122
1122
|
const major = m ? Number(m[1]) : 0;
|
|
1123
1123
|
if (major < 8) {
|
|
1124
|
-
throw new errors_js_1.ConnectionError(`
|
|
1124
|
+
throw new errors_js_1.ConnectionError(`turbine-orm/mysql requires MySQL 8.0+ (5.7 lacks JSON_ARRAYAGG); got "${version}".`);
|
|
1125
1125
|
}
|
|
1126
1126
|
}
|
|
1127
1127
|
function isMysql2Pool(x) {
|