turbine-orm 0.74.0 → 0.75.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/powdb.d.ts +17 -2
- package/dist/cjs/powdb.js +6 -4
- package/dist/cjs/powql.js +45 -6
- package/dist/cjs/serverless.d.ts +16 -4
- package/dist/powdb.d.ts +17 -2
- package/dist/powdb.js +6 -4
- package/dist/powql.js +45 -6
- package/dist/serverless.d.ts +16 -4
- package/package.json +1 -1
package/dist/cjs/powdb.d.ts
CHANGED
|
@@ -54,6 +54,7 @@
|
|
|
54
54
|
*/
|
|
55
55
|
import { type PgCompatPool, type PgCompatPoolClient, TurbineClient, type TurbineConfig } from './client.js';
|
|
56
56
|
import { type Dialect } from './dialect.js';
|
|
57
|
+
import type { EngineClientConfig } from './engine-config.js';
|
|
57
58
|
import type { PowdbExec } from './powdb-introspect.js';
|
|
58
59
|
import { type ColumnMetadata, type SchemaMetadata, type TableMetadata } from './schema.js';
|
|
59
60
|
/**
|
|
@@ -932,8 +933,22 @@ export declare class PowdbEmbeddedPool implements PgCompatPool {
|
|
|
932
933
|
}
|
|
933
934
|
export { introspectPowdbDatabase, type PowdbExec, type PowdbIntrospectOptions, } from './powdb-introspect.js';
|
|
934
935
|
export { PowqlInterface } from './powql.js';
|
|
935
|
-
/**
|
|
936
|
-
|
|
936
|
+
/**
|
|
937
|
+
* Options for {@link turbinePowDB}.
|
|
938
|
+
*
|
|
939
|
+
* Extends {@link EngineClientConfig}, the same inverted default the SQL engine
|
|
940
|
+
* factories use: every `TurbineConfig` option reaches the client unless it is one
|
|
941
|
+
* of the transport keys declared below, which {@link turbinePowDB} destructures
|
|
942
|
+
* out before spreading. This used to be a four-key `Pick`, so `globalFilters`,
|
|
943
|
+
* `stableRelationOrder`, `errorMessages` and everything else added after PowDB
|
|
944
|
+
* landed were accepted by the type and then dropped on the floor. A tenant filter
|
|
945
|
+
* that silently does not apply is the worst shape that class of bug can take.
|
|
946
|
+
*
|
|
947
|
+
* Options that PowDB cannot honour by CAPABILITY (rather than by plumbing) still
|
|
948
|
+
* throw a typed `UnsupportedFeatureError` (E017) at the point of use, which is a
|
|
949
|
+
* better answer than an option the type system claims does not exist.
|
|
950
|
+
*/
|
|
951
|
+
export interface TurbinePowdbOptions extends EngineClientConfig {
|
|
937
952
|
/**
|
|
938
953
|
* Client-level default `with`-relation load strategy. On PowDB the default is
|
|
939
954
|
* the batched N+1 loaders; setting `'join'` opts INTO native PowQL server-side
|
package/dist/cjs/powdb.js
CHANGED
|
@@ -2408,14 +2408,16 @@ async function turbinePowDB(target, schema, options = {}) {
|
|
|
2408
2408
|
// The PowQL generator is loaded here to keep client.ts free of any PowDB import.
|
|
2409
2409
|
const { PowqlInterface } = await Promise.resolve().then(() => __importStar(require('./powql.js')));
|
|
2410
2410
|
const queryInterfaceFactory = (p, table, sch, middlewares, opts) => new PowqlInterface(p, table, sch, middlewares, opts);
|
|
2411
|
+
// Forward every client-level option EXCEPT the ones this factory owns (they
|
|
2412
|
+
// configure the pool / transport above, and TurbineClient warns on a config
|
|
2413
|
+
// key it does not recognize). Destructured rather than hand-listed on the way
|
|
2414
|
+
// IN: an allowlist is how `globalFilters` came to be accepted and ignored.
|
|
2415
|
+
const { connectionLimit: _connectionLimit, transactionQueueTimeoutMs: _transactionQueueTimeoutMs, retryStaleReads: _retryStaleReads, assumeEngineVersion: _assumeEngineVersion, readonly: _readonly, powdbClientModule: _powdbClientModule, powdbEmbeddedModule: _powdbEmbeddedModule, ...clientConfig } = options;
|
|
2411
2416
|
const client = new client_js_1.TurbineClient({
|
|
2417
|
+
...clientConfig,
|
|
2412
2418
|
pool,
|
|
2413
2419
|
preparedStatements: false,
|
|
2414
2420
|
dialect: exports.powdbDialect,
|
|
2415
|
-
logging: options.logging,
|
|
2416
|
-
defaultLimit: options.defaultLimit,
|
|
2417
|
-
warnOnUnlimited: options.warnOnUnlimited,
|
|
2418
|
-
relationLoadStrategy: options.relationLoadStrategy,
|
|
2419
2421
|
queryInterfaceFactory,
|
|
2420
2422
|
}, schema);
|
|
2421
2423
|
if (owns) {
|
package/dist/cjs/powql.js
CHANGED
|
@@ -3113,17 +3113,56 @@ class PowqlInterface {
|
|
|
3113
3113
|
const aggOrderExprs = new Map();
|
|
3114
3114
|
const aggInner = new Map();
|
|
3115
3115
|
let aggN = 0;
|
|
3116
|
-
//
|
|
3117
|
-
//
|
|
3118
|
-
//
|
|
3119
|
-
//
|
|
3120
|
-
|
|
3121
|
-
|
|
3116
|
+
// `_count` has the same two shapes as the SQL groupBy (query/aggregates.ts),
|
|
3117
|
+
// and the record form is why this is not a one-liner:
|
|
3118
|
+
// - `true` / omitted → scalar `_count` (row count). Selected by DEFAULT
|
|
3119
|
+
// unless the caller opts out with `_count: false`, so every groupBy row
|
|
3120
|
+
// carries `_count` and `orderBy: { _count }` works without requesting it.
|
|
3121
|
+
// - record form → `_all` is the row count and each field is that column's
|
|
3122
|
+
// NON-NULL count, result `_count: { _all, field }`.
|
|
3123
|
+
// The record form used to fall through both branches here, so PowDB answered
|
|
3124
|
+
// `groupBy({ _count: { id: true } })` with the group keys and NO `_count` key
|
|
3125
|
+
// at all while every SQL engine returned the counts. Accepted and ignored is
|
|
3126
|
+
// the one outcome an aggregate must never have: the caller reads
|
|
3127
|
+
// `g._count.id` off a row that never carried it.
|
|
3128
|
+
const countArg = args._count;
|
|
3129
|
+
const scalarCount = countArg === true || countArg === undefined;
|
|
3130
|
+
const countIsRecord = !scalarCount && countArg !== false && typeof countArg === 'object';
|
|
3131
|
+
if (scalarCount) {
|
|
3122
3132
|
const alias = `agg_${aggN++}`;
|
|
3123
3133
|
proj.push(`${alias}: count(*)`);
|
|
3124
3134
|
aggReaders.push({ alias, outKey: '_count', numeric: true });
|
|
3125
3135
|
aggOrderExprs.set('_count', `.${alias}`);
|
|
3126
3136
|
}
|
|
3137
|
+
else if (countIsRecord) {
|
|
3138
|
+
for (const [field, enabled] of Object.entries(countArg)) {
|
|
3139
|
+
if (!enabled)
|
|
3140
|
+
continue;
|
|
3141
|
+
const alias = `agg_${aggN++}`;
|
|
3142
|
+
if (field === '_all') {
|
|
3143
|
+
// Double underscore in the output key, collision-proof against a
|
|
3144
|
+
// real column named `all`, exactly as the SQL builder aliases it.
|
|
3145
|
+
claim('_count__all', '_count `_all`');
|
|
3146
|
+
proj.push(`${alias}: count(*)`);
|
|
3147
|
+
aggReaders.push({ alias, outKey: '_count:_all', numeric: true });
|
|
3148
|
+
// COUNT(*) is orderable whenever it is selected: the scalar form
|
|
3149
|
+
// above, or the record form containing `_all`. Same rule as SQL.
|
|
3150
|
+
aggOrderExprs.set('_count', `.${alias}`);
|
|
3151
|
+
}
|
|
3152
|
+
else {
|
|
3153
|
+
// Per-field count is the NON-NULL count, which the engine only
|
|
3154
|
+
// honours from the version `projectedCountSupported` gates on; below
|
|
3155
|
+
// it the projection was ignored and the ROW count came back. Same
|
|
3156
|
+
// per-column gate `aggregate()` applies, for the same reason.
|
|
3157
|
+
const col = this.column(field);
|
|
3158
|
+
this.assertProjectedCountSupported(field);
|
|
3159
|
+
claim(`_count_${col.name}`, `_count of column "${col.name}"`);
|
|
3160
|
+
const inner = this.colRefName(col.name);
|
|
3161
|
+
proj.push(`${alias}: count(${inner})`);
|
|
3162
|
+
aggReaders.push({ alias, outKey: `_count:${col.field}`, numeric: true });
|
|
3163
|
+
}
|
|
3164
|
+
}
|
|
3165
|
+
}
|
|
3127
3166
|
for (const fn of ['_sum', '_avg', '_min', '_max']) {
|
|
3128
3167
|
const spec = args[fn];
|
|
3129
3168
|
if (!spec)
|
package/dist/cjs/serverless.d.ts
CHANGED
|
@@ -82,11 +82,23 @@ import { type PgCompatPool, TurbineClient, type TurbineConfig } from './client.j
|
|
|
82
82
|
import type { SchemaMetadata } from './schema.js';
|
|
83
83
|
export type { PgCompatPool, PgCompatPoolClient, PgCompatQueryResult } from './client.js';
|
|
84
84
|
/**
|
|
85
|
-
* Options for `turbineHttp()
|
|
86
|
-
*
|
|
85
|
+
* Options for `turbineHttp()`: everything a `TurbineClient` takes except the
|
|
86
|
+
* pool, which is this function's first argument.
|
|
87
|
+
*
|
|
88
|
+
* This used to be a three-key `Pick` (`logging` / `defaultLimit` /
|
|
89
|
+
* `warnOnUnlimited`), which made the accepted set an ALLOWLIST that stopped
|
|
90
|
+
* being maintained the day it was written. The runtime already spread the whole
|
|
91
|
+
* object through, so the effect was purely at the type level: a serverless
|
|
92
|
+
* caller passing `globalFilters` (or `errorMessages`, or `relationLoadStrategy`)
|
|
93
|
+
* got an excess-property error on an option that would have worked. That is a
|
|
94
|
+
* kinder failure than PowDB's silent drop, but the same allowlist, and it lands
|
|
95
|
+
* on the edge deployments where a tenant filter matters most.
|
|
96
|
+
*
|
|
97
|
+
* Deliberately `Omit<TurbineConfig, 'pool'>` rather than `EngineClientConfig`:
|
|
98
|
+
* unlike the SQL engine factories, this one binds no dialect and pins no
|
|
99
|
+
* prepared-statement mode, so it has no business narrowing those away.
|
|
87
100
|
*/
|
|
88
|
-
export
|
|
89
|
-
}
|
|
101
|
+
export type TurbineHttpOptions = Omit<TurbineConfig, 'pool'>;
|
|
90
102
|
/**
|
|
91
103
|
* Create a TurbineClient bound to an external pg-compatible pool.
|
|
92
104
|
*
|
package/dist/powdb.d.ts
CHANGED
|
@@ -54,6 +54,7 @@
|
|
|
54
54
|
*/
|
|
55
55
|
import { type PgCompatPool, type PgCompatPoolClient, TurbineClient, type TurbineConfig } from './client.js';
|
|
56
56
|
import { type Dialect } from './dialect.js';
|
|
57
|
+
import type { EngineClientConfig } from './engine-config.js';
|
|
57
58
|
import type { PowdbExec } from './powdb-introspect.js';
|
|
58
59
|
import { type ColumnMetadata, type SchemaMetadata, type TableMetadata } from './schema.js';
|
|
59
60
|
/**
|
|
@@ -932,8 +933,22 @@ export declare class PowdbEmbeddedPool implements PgCompatPool {
|
|
|
932
933
|
}
|
|
933
934
|
export { introspectPowdbDatabase, type PowdbExec, type PowdbIntrospectOptions, } from './powdb-introspect.js';
|
|
934
935
|
export { PowqlInterface } from './powql.js';
|
|
935
|
-
/**
|
|
936
|
-
|
|
936
|
+
/**
|
|
937
|
+
* Options for {@link turbinePowDB}.
|
|
938
|
+
*
|
|
939
|
+
* Extends {@link EngineClientConfig}, the same inverted default the SQL engine
|
|
940
|
+
* factories use: every `TurbineConfig` option reaches the client unless it is one
|
|
941
|
+
* of the transport keys declared below, which {@link turbinePowDB} destructures
|
|
942
|
+
* out before spreading. This used to be a four-key `Pick`, so `globalFilters`,
|
|
943
|
+
* `stableRelationOrder`, `errorMessages` and everything else added after PowDB
|
|
944
|
+
* landed were accepted by the type and then dropped on the floor. A tenant filter
|
|
945
|
+
* that silently does not apply is the worst shape that class of bug can take.
|
|
946
|
+
*
|
|
947
|
+
* Options that PowDB cannot honour by CAPABILITY (rather than by plumbing) still
|
|
948
|
+
* throw a typed `UnsupportedFeatureError` (E017) at the point of use, which is a
|
|
949
|
+
* better answer than an option the type system claims does not exist.
|
|
950
|
+
*/
|
|
951
|
+
export interface TurbinePowdbOptions extends EngineClientConfig {
|
|
937
952
|
/**
|
|
938
953
|
* Client-level default `with`-relation load strategy. On PowDB the default is
|
|
939
954
|
* the batched N+1 loaders; setting `'join'` opts INTO native PowQL server-side
|
package/dist/powdb.js
CHANGED
|
@@ -2342,14 +2342,16 @@ export async function turbinePowDB(target, schema, options = {}) {
|
|
|
2342
2342
|
// The PowQL generator is loaded here to keep client.ts free of any PowDB import.
|
|
2343
2343
|
const { PowqlInterface } = await import('./powql.js');
|
|
2344
2344
|
const queryInterfaceFactory = (p, table, sch, middlewares, opts) => new PowqlInterface(p, table, sch, middlewares, opts);
|
|
2345
|
+
// Forward every client-level option EXCEPT the ones this factory owns (they
|
|
2346
|
+
// configure the pool / transport above, and TurbineClient warns on a config
|
|
2347
|
+
// key it does not recognize). Destructured rather than hand-listed on the way
|
|
2348
|
+
// IN: an allowlist is how `globalFilters` came to be accepted and ignored.
|
|
2349
|
+
const { connectionLimit: _connectionLimit, transactionQueueTimeoutMs: _transactionQueueTimeoutMs, retryStaleReads: _retryStaleReads, assumeEngineVersion: _assumeEngineVersion, readonly: _readonly, powdbClientModule: _powdbClientModule, powdbEmbeddedModule: _powdbEmbeddedModule, ...clientConfig } = options;
|
|
2345
2350
|
const client = new TurbineClient({
|
|
2351
|
+
...clientConfig,
|
|
2346
2352
|
pool,
|
|
2347
2353
|
preparedStatements: false,
|
|
2348
2354
|
dialect: powdbDialect,
|
|
2349
|
-
logging: options.logging,
|
|
2350
|
-
defaultLimit: options.defaultLimit,
|
|
2351
|
-
warnOnUnlimited: options.warnOnUnlimited,
|
|
2352
|
-
relationLoadStrategy: options.relationLoadStrategy,
|
|
2353
2355
|
queryInterfaceFactory,
|
|
2354
2356
|
}, schema);
|
|
2355
2357
|
if (owns) {
|
package/dist/powql.js
CHANGED
|
@@ -3077,17 +3077,56 @@ export class PowqlInterface {
|
|
|
3077
3077
|
const aggOrderExprs = new Map();
|
|
3078
3078
|
const aggInner = new Map();
|
|
3079
3079
|
let aggN = 0;
|
|
3080
|
-
//
|
|
3081
|
-
//
|
|
3082
|
-
//
|
|
3083
|
-
//
|
|
3084
|
-
|
|
3085
|
-
|
|
3080
|
+
// `_count` has the same two shapes as the SQL groupBy (query/aggregates.ts),
|
|
3081
|
+
// and the record form is why this is not a one-liner:
|
|
3082
|
+
// - `true` / omitted → scalar `_count` (row count). Selected by DEFAULT
|
|
3083
|
+
// unless the caller opts out with `_count: false`, so every groupBy row
|
|
3084
|
+
// carries `_count` and `orderBy: { _count }` works without requesting it.
|
|
3085
|
+
// - record form → `_all` is the row count and each field is that column's
|
|
3086
|
+
// NON-NULL count, result `_count: { _all, field }`.
|
|
3087
|
+
// The record form used to fall through both branches here, so PowDB answered
|
|
3088
|
+
// `groupBy({ _count: { id: true } })` with the group keys and NO `_count` key
|
|
3089
|
+
// at all while every SQL engine returned the counts. Accepted and ignored is
|
|
3090
|
+
// the one outcome an aggregate must never have: the caller reads
|
|
3091
|
+
// `g._count.id` off a row that never carried it.
|
|
3092
|
+
const countArg = args._count;
|
|
3093
|
+
const scalarCount = countArg === true || countArg === undefined;
|
|
3094
|
+
const countIsRecord = !scalarCount && countArg !== false && typeof countArg === 'object';
|
|
3095
|
+
if (scalarCount) {
|
|
3086
3096
|
const alias = `agg_${aggN++}`;
|
|
3087
3097
|
proj.push(`${alias}: count(*)`);
|
|
3088
3098
|
aggReaders.push({ alias, outKey: '_count', numeric: true });
|
|
3089
3099
|
aggOrderExprs.set('_count', `.${alias}`);
|
|
3090
3100
|
}
|
|
3101
|
+
else if (countIsRecord) {
|
|
3102
|
+
for (const [field, enabled] of Object.entries(countArg)) {
|
|
3103
|
+
if (!enabled)
|
|
3104
|
+
continue;
|
|
3105
|
+
const alias = `agg_${aggN++}`;
|
|
3106
|
+
if (field === '_all') {
|
|
3107
|
+
// Double underscore in the output key, collision-proof against a
|
|
3108
|
+
// real column named `all`, exactly as the SQL builder aliases it.
|
|
3109
|
+
claim('_count__all', '_count `_all`');
|
|
3110
|
+
proj.push(`${alias}: count(*)`);
|
|
3111
|
+
aggReaders.push({ alias, outKey: '_count:_all', numeric: true });
|
|
3112
|
+
// COUNT(*) is orderable whenever it is selected: the scalar form
|
|
3113
|
+
// above, or the record form containing `_all`. Same rule as SQL.
|
|
3114
|
+
aggOrderExprs.set('_count', `.${alias}`);
|
|
3115
|
+
}
|
|
3116
|
+
else {
|
|
3117
|
+
// Per-field count is the NON-NULL count, which the engine only
|
|
3118
|
+
// honours from the version `projectedCountSupported` gates on; below
|
|
3119
|
+
// it the projection was ignored and the ROW count came back. Same
|
|
3120
|
+
// per-column gate `aggregate()` applies, for the same reason.
|
|
3121
|
+
const col = this.column(field);
|
|
3122
|
+
this.assertProjectedCountSupported(field);
|
|
3123
|
+
claim(`_count_${col.name}`, `_count of column "${col.name}"`);
|
|
3124
|
+
const inner = this.colRefName(col.name);
|
|
3125
|
+
proj.push(`${alias}: count(${inner})`);
|
|
3126
|
+
aggReaders.push({ alias, outKey: `_count:${col.field}`, numeric: true });
|
|
3127
|
+
}
|
|
3128
|
+
}
|
|
3129
|
+
}
|
|
3091
3130
|
for (const fn of ['_sum', '_avg', '_min', '_max']) {
|
|
3092
3131
|
const spec = args[fn];
|
|
3093
3132
|
if (!spec)
|
package/dist/serverless.d.ts
CHANGED
|
@@ -82,11 +82,23 @@ import { type PgCompatPool, TurbineClient, type TurbineConfig } from './client.j
|
|
|
82
82
|
import type { SchemaMetadata } from './schema.js';
|
|
83
83
|
export type { PgCompatPool, PgCompatPoolClient, PgCompatQueryResult } from './client.js';
|
|
84
84
|
/**
|
|
85
|
-
* Options for `turbineHttp()
|
|
86
|
-
*
|
|
85
|
+
* Options for `turbineHttp()`: everything a `TurbineClient` takes except the
|
|
86
|
+
* pool, which is this function's first argument.
|
|
87
|
+
*
|
|
88
|
+
* This used to be a three-key `Pick` (`logging` / `defaultLimit` /
|
|
89
|
+
* `warnOnUnlimited`), which made the accepted set an ALLOWLIST that stopped
|
|
90
|
+
* being maintained the day it was written. The runtime already spread the whole
|
|
91
|
+
* object through, so the effect was purely at the type level: a serverless
|
|
92
|
+
* caller passing `globalFilters` (or `errorMessages`, or `relationLoadStrategy`)
|
|
93
|
+
* got an excess-property error on an option that would have worked. That is a
|
|
94
|
+
* kinder failure than PowDB's silent drop, but the same allowlist, and it lands
|
|
95
|
+
* on the edge deployments where a tenant filter matters most.
|
|
96
|
+
*
|
|
97
|
+
* Deliberately `Omit<TurbineConfig, 'pool'>` rather than `EngineClientConfig`:
|
|
98
|
+
* unlike the SQL engine factories, this one binds no dialect and pins no
|
|
99
|
+
* prepared-statement mode, so it has no business narrowing those away.
|
|
87
100
|
*/
|
|
88
|
-
export
|
|
89
|
-
}
|
|
101
|
+
export type TurbineHttpOptions = Omit<TurbineConfig, 'pool'>;
|
|
90
102
|
/**
|
|
91
103
|
* Create a TurbineClient bound to an external pg-compatible pool.
|
|
92
104
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "turbine-orm",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.75.0",
|
|
4
4
|
"description": "Postgres-native TypeScript ORM, runs on Neon, Vercel Postgres, Cloudflare, Supabase. Streaming cursors, typed errors, single-query nested relations. One dependency, no WASM engine",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"//exports": "Each subpath declares its types PER CONDITION. A single shared top-level \"types\" resolves to the ESM declarations for `require` too, which is TS1479 (\"is an ES module ... cannot be require()d\") for any CJS consumer on moduleResolution node16/nodenext. The require condition points at dist/cjs, which ships its own {\"type\":\"commonjs\"} package.json, so those declarations are CJS declarations. Gated in CI by publint + @arethetypeswrong/cli + a real .cts consumer typecheck (see the package-types job in ci.yml).",
|