turbine-orm 0.79.0 → 0.79.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/client.js +4 -4
- package/dist/cjs/introspect.js +2 -2
- package/dist/cjs/observe.js +2 -2
- package/dist/cjs/pg-edge.d.ts +42 -0
- package/dist/cjs/pg-edge.js +47 -0
- package/dist/cjs/pipeline-submittable.js +12 -8
- package/dist/cjs/query/utils.js +8 -8
- package/dist/cjs/query-events.d.ts +7 -0
- package/dist/cjs/query-events.js +23 -15
- package/dist/client.js +1 -1
- package/dist/introspect.js +1 -1
- package/dist/observe.js +1 -1
- package/dist/pg-edge.d.ts +42 -0
- package/dist/pg-edge.js +45 -0
- package/dist/pipeline-submittable.js +6 -2
- package/dist/query/utils.js +1 -1
- package/dist/query-events.d.ts +7 -0
- package/dist/query-events.js +23 -15
- package/package.json +16 -3
package/dist/cjs/client.js
CHANGED
|
@@ -28,7 +28,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
28
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
29
|
exports.TurbineClient = exports.TransactionClient = exports.READ_OPERATIONS = void 0;
|
|
30
30
|
exports.withRetry = withRetry;
|
|
31
|
-
const
|
|
31
|
+
const _pg_1 = __importDefault(require("pg"));
|
|
32
32
|
const connection_url_js_1 = require("./connection-url.js");
|
|
33
33
|
const dialect_js_1 = require("./dialect.js");
|
|
34
34
|
const errors_js_1 = require("./errors.js");
|
|
@@ -828,7 +828,7 @@ class TurbineClient {
|
|
|
828
828
|
const ownsAnyPool = !config.pool;
|
|
829
829
|
if (ownsAnyPool && !TurbineClient.int8ParserRegistered) {
|
|
830
830
|
(0, utils_js_1.warnParserOverwrite)(20, 'int8');
|
|
831
|
-
|
|
831
|
+
_pg_1.default.types.setTypeParser(20, (0, utils_js_1.markTurbineParser)((val) => {
|
|
832
832
|
const n = Number(val);
|
|
833
833
|
return Number.isSafeInteger(n) ? n : val;
|
|
834
834
|
}));
|
|
@@ -978,7 +978,7 @@ class TurbineClient {
|
|
|
978
978
|
// Held as a `pg.Pool` for the length of this block: `PgCompatPool.on` is
|
|
979
979
|
// optional (HTTP drivers have no pool events), and here we know we own a
|
|
980
980
|
// real pg pool that has one. Same shape as the replica loop below.
|
|
981
|
-
const ownPool = new
|
|
981
|
+
const ownPool = new _pg_1.default.Pool(TurbineClient.withPlanCacheMode(poolConfig, this.planCacheMode));
|
|
982
982
|
ownPool.on('error', (err) => {
|
|
983
983
|
console.error('[turbine] Unexpected pool error:', err.message);
|
|
984
984
|
});
|
|
@@ -995,7 +995,7 @@ class TurbineClient {
|
|
|
995
995
|
this.ownedReplicaPools = [];
|
|
996
996
|
for (const replica of config.replicas ?? []) {
|
|
997
997
|
if (typeof replica === 'string') {
|
|
998
|
-
const replicaPool = new
|
|
998
|
+
const replicaPool = new _pg_1.default.Pool(TurbineClient.withPlanCacheMode({
|
|
999
999
|
connectionString: replica,
|
|
1000
1000
|
max: config.poolSize ?? config.max ?? 10,
|
|
1001
1001
|
idleTimeoutMillis: config.idleTimeoutMs ?? config.idleTimeoutMillis ?? 30_000,
|
package/dist/cjs/introspect.js
CHANGED
|
@@ -35,7 +35,7 @@ exports.buildRelationsFromForeignKeys = buildRelationsFromForeignKeys;
|
|
|
35
35
|
exports.addAutoManyToManyRelations = addAutoManyToManyRelations;
|
|
36
36
|
exports.deriveCatalogRelations = deriveCatalogRelations;
|
|
37
37
|
exports.deriveEngineRelations = deriveEngineRelations;
|
|
38
|
-
const
|
|
38
|
+
const _pg_1 = __importDefault(require("pg"));
|
|
39
39
|
const dialect_js_1 = require("./dialect.js");
|
|
40
40
|
const errors_js_1 = require("./errors.js");
|
|
41
41
|
const schema_js_1 = require("./schema.js");
|
|
@@ -541,7 +541,7 @@ function assertDistinctColumnFields(tableName, columns) {
|
|
|
541
541
|
async function introspectPostgresCatalog(options) {
|
|
542
542
|
const schema = options.schema ?? 'public';
|
|
543
543
|
const dialect = dialect_js_1.postgresDialect;
|
|
544
|
-
const pool = new
|
|
544
|
+
const pool = new _pg_1.default.Pool({
|
|
545
545
|
connectionString: options.connectionString,
|
|
546
546
|
max: 1,
|
|
547
547
|
connectionTimeoutMillis: 10_000,
|
package/dist/cjs/observe.js
CHANGED
|
@@ -20,7 +20,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
20
20
|
exports.ObserveEngine = exports.HttpJsonSink = exports.PgMetricsSink = void 0;
|
|
21
21
|
exports.floorToMinute = floorToMinute;
|
|
22
22
|
exports.percentile = percentile;
|
|
23
|
-
const
|
|
23
|
+
const _pg_1 = __importDefault(require("pg"));
|
|
24
24
|
const errors_js_1 = require("./errors.js");
|
|
25
25
|
/**
|
|
26
26
|
* Buffer key: minute bucket + model + action. Joined on a NUL, which cannot
|
|
@@ -82,7 +82,7 @@ class PgMetricsSink {
|
|
|
82
82
|
pool;
|
|
83
83
|
retentionDays;
|
|
84
84
|
constructor(options) {
|
|
85
|
-
this.pool = new
|
|
85
|
+
this.pool = new _pg_1.default.Pool({ connectionString: options.connectionString, max: 1 });
|
|
86
86
|
this.retentionDays = options.retentionDays ?? 30;
|
|
87
87
|
}
|
|
88
88
|
async init() {
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* turbine-orm, the stand-in for `pg` on runtimes that cannot run it.
|
|
3
|
+
*
|
|
4
|
+
* The modules the serverless entry shares with the main entry import the
|
|
5
|
+
* driver as `#pg`, a package.json `imports` alias. It resolves to the real `pg`
|
|
6
|
+
* everywhere except under the `edge-light` and `browser` conditions (Vercel
|
|
7
|
+
* Edge, Next.js edge routes, browser bundles), where it resolves to this file.
|
|
8
|
+
* Those runtimes have no TCP sockets and no `fs`, `net` or `tls`, so bundling
|
|
9
|
+
* `pg` there fails the build outright, although `turbineHttp()` never opens a
|
|
10
|
+
* pg connection. `workerd` keeps the real driver: `pg` supports Cloudflare
|
|
11
|
+
* Workers, and a Hyperdrive user may let Turbine open the pool.
|
|
12
|
+
*
|
|
13
|
+
* What the shared code touches on a caller-supplied pool:
|
|
14
|
+
* - `types.getTypeParser`, when a relation's JSON column is decoded with the
|
|
15
|
+
* driver's text parser. The identity parser here leaves the JSON wire text
|
|
16
|
+
* as it arrived (a `date` inside a relation stays a string), which is also
|
|
17
|
+
* what an HTTP driver that ships its own type parsers would disagree with
|
|
18
|
+
* least.
|
|
19
|
+
* - nothing else: parser registration, `new pg.Pool` and the pipeline wire
|
|
20
|
+
* protocol only run when Turbine owns a TCP pool, and on this runtime that
|
|
21
|
+
* is the error below instead of an unresolvable import.
|
|
22
|
+
*/
|
|
23
|
+
declare function unavailable(): never;
|
|
24
|
+
declare class Unavailable {
|
|
25
|
+
constructor();
|
|
26
|
+
}
|
|
27
|
+
declare const pg: {
|
|
28
|
+
Pool: typeof Unavailable;
|
|
29
|
+
Client: typeof Unavailable;
|
|
30
|
+
Result: typeof Unavailable;
|
|
31
|
+
utils: {
|
|
32
|
+
prepareValue: typeof unavailable;
|
|
33
|
+
};
|
|
34
|
+
types: {
|
|
35
|
+
getTypeParser: () => (value: string) => string;
|
|
36
|
+
setTypeParser: () => void;
|
|
37
|
+
arrayParser: {
|
|
38
|
+
create: typeof unavailable;
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
export default pg;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* turbine-orm, the stand-in for `pg` on runtimes that cannot run it.
|
|
4
|
+
*
|
|
5
|
+
* The modules the serverless entry shares with the main entry import the
|
|
6
|
+
* driver as `#pg`, a package.json `imports` alias. It resolves to the real `pg`
|
|
7
|
+
* everywhere except under the `edge-light` and `browser` conditions (Vercel
|
|
8
|
+
* Edge, Next.js edge routes, browser bundles), where it resolves to this file.
|
|
9
|
+
* Those runtimes have no TCP sockets and no `fs`, `net` or `tls`, so bundling
|
|
10
|
+
* `pg` there fails the build outright, although `turbineHttp()` never opens a
|
|
11
|
+
* pg connection. `workerd` keeps the real driver: `pg` supports Cloudflare
|
|
12
|
+
* Workers, and a Hyperdrive user may let Turbine open the pool.
|
|
13
|
+
*
|
|
14
|
+
* What the shared code touches on a caller-supplied pool:
|
|
15
|
+
* - `types.getTypeParser`, when a relation's JSON column is decoded with the
|
|
16
|
+
* driver's text parser. The identity parser here leaves the JSON wire text
|
|
17
|
+
* as it arrived (a `date` inside a relation stays a string), which is also
|
|
18
|
+
* what an HTTP driver that ships its own type parsers would disagree with
|
|
19
|
+
* least.
|
|
20
|
+
* - nothing else: parser registration, `new pg.Pool` and the pipeline wire
|
|
21
|
+
* protocol only run when Turbine owns a TCP pool, and on this runtime that
|
|
22
|
+
* is the error below instead of an unresolvable import.
|
|
23
|
+
*/
|
|
24
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
+
const errors_js_1 = require("./errors.js");
|
|
26
|
+
function unavailable() {
|
|
27
|
+
throw new errors_js_1.ConnectionError('turbine-orm cannot open its own Postgres connection in this runtime: it has no TCP driver. ' +
|
|
28
|
+
'Pass a pg-compatible pool instead of a connection string, e.g. turbineHttp(pool, schema) from turbine-orm/serverless.');
|
|
29
|
+
}
|
|
30
|
+
class Unavailable {
|
|
31
|
+
constructor() {
|
|
32
|
+
unavailable();
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const identity = (value) => value;
|
|
36
|
+
const pg = {
|
|
37
|
+
Pool: Unavailable,
|
|
38
|
+
Client: Unavailable,
|
|
39
|
+
Result: Unavailable,
|
|
40
|
+
utils: { prepareValue: unavailable },
|
|
41
|
+
types: {
|
|
42
|
+
getTypeParser: () => identity,
|
|
43
|
+
setTypeParser: () => { },
|
|
44
|
+
arrayParser: { create: unavailable },
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
exports.default = pg;
|
|
@@ -24,9 +24,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
24
24
|
exports.pipelineClientNeedsDiscard = pipelineClientNeedsDiscard;
|
|
25
25
|
exports.runPipelined = runPipelined;
|
|
26
26
|
exports.supportsExtendedPipeline = supportsExtendedPipeline;
|
|
27
|
-
const
|
|
28
|
-
const utils_1 = require("pg/lib/utils");
|
|
27
|
+
const _pg_1 = __importDefault(require("pg"));
|
|
29
28
|
const errors_js_1 = require("./errors.js");
|
|
29
|
+
// Read off pg's own module object (`pg.Result` IS `pg/lib/result`, `pg.utils`
|
|
30
|
+
// IS `pg/lib/utils`) rather than imported by subpath, so the whole driver comes
|
|
31
|
+
// through the one `#pg` alias that edge bundles swap out (see src/pg-edge.ts).
|
|
32
|
+
const Result = _pg_1.default.Result;
|
|
33
|
+
const { prepareValue } = _pg_1.default.utils;
|
|
30
34
|
/**
|
|
31
35
|
* Marks a client this module could not hand back in a reusable state, so the
|
|
32
36
|
* caller releases it WITH an error and the pool drops it instead of lending it
|
|
@@ -130,7 +134,7 @@ async function runPipelined(client, queries, options = {}) {
|
|
|
130
134
|
// Results array: one Result per query (not counting BEGIN/COMMIT)
|
|
131
135
|
const results = [];
|
|
132
136
|
for (let i = 0; i < queries.length; i++) {
|
|
133
|
-
results.push(new
|
|
137
|
+
results.push(new Result(undefined, client._types));
|
|
134
138
|
}
|
|
135
139
|
// commandComplete counter, tracks position across all commands
|
|
136
140
|
let commandIndex = 0;
|
|
@@ -230,7 +234,7 @@ async function runPipelined(client, queries, options = {}) {
|
|
|
230
234
|
rollbackSent = true;
|
|
231
235
|
try {
|
|
232
236
|
connection.parse({ text: 'ROLLBACK', name: '' });
|
|
233
|
-
connection.bind({ portal: '', statement: '', values: [], valueMapper:
|
|
237
|
+
connection.bind({ portal: '', statement: '', values: [], valueMapper: prepareValue });
|
|
234
238
|
connection.execute({ portal: '', rows: 0 });
|
|
235
239
|
connection.sync();
|
|
236
240
|
return false;
|
|
@@ -428,7 +432,7 @@ async function runPipelined(client, queries, options = {}) {
|
|
|
428
432
|
// One sync = one ReadyForQuery
|
|
429
433
|
// BEGIN
|
|
430
434
|
connection.parse({ text: 'BEGIN', name: '' });
|
|
431
|
-
connection.bind({ portal: '', statement: '', values: [], valueMapper:
|
|
435
|
+
connection.bind({ portal: '', statement: '', values: [], valueMapper: prepareValue });
|
|
432
436
|
connection.execute({ portal: '', rows: 0 });
|
|
433
437
|
// Each query
|
|
434
438
|
for (const q of queries) {
|
|
@@ -437,14 +441,14 @@ async function runPipelined(client, queries, options = {}) {
|
|
|
437
441
|
portal: '',
|
|
438
442
|
statement: '',
|
|
439
443
|
values: q.params,
|
|
440
|
-
valueMapper:
|
|
444
|
+
valueMapper: prepareValue,
|
|
441
445
|
});
|
|
442
446
|
connection.describe({ type: 'P', name: '' });
|
|
443
447
|
connection.execute({ portal: '', rows: 0 });
|
|
444
448
|
}
|
|
445
449
|
// COMMIT
|
|
446
450
|
connection.parse({ text: 'COMMIT', name: '' });
|
|
447
|
-
connection.bind({ portal: '', statement: '', values: [], valueMapper:
|
|
451
|
+
connection.bind({ portal: '', statement: '', values: [], valueMapper: prepareValue });
|
|
448
452
|
connection.execute({ portal: '', rows: 0 });
|
|
449
453
|
// Single Sync
|
|
450
454
|
connection.sync();
|
|
@@ -460,7 +464,7 @@ async function runPipelined(client, queries, options = {}) {
|
|
|
460
464
|
portal: '',
|
|
461
465
|
statement: '',
|
|
462
466
|
values: q.params,
|
|
463
|
-
valueMapper:
|
|
467
|
+
valueMapper: prepareValue,
|
|
464
468
|
});
|
|
465
469
|
connection.describe({ type: 'P', name: '' });
|
|
466
470
|
connection.execute({ portal: '', rows: 0 });
|
package/dist/cjs/query/utils.js
CHANGED
|
@@ -51,7 +51,7 @@ exports.warnRedundantSortTerm = warnRedundantSortTerm;
|
|
|
51
51
|
exports.selectNamesNothingMessage = selectNamesNothingMessage;
|
|
52
52
|
exports.selectOmitExclusiveMessage = selectOmitExclusiveMessage;
|
|
53
53
|
exports.normalizePagination = normalizePagination;
|
|
54
|
-
const
|
|
54
|
+
const _pg_1 = __importDefault(require("pg"));
|
|
55
55
|
const errors_js_1 = require("../errors.js");
|
|
56
56
|
const schema_js_1 = require("../schema.js");
|
|
57
57
|
const warn_registry_js_1 = require("./warn-registry.js");
|
|
@@ -999,7 +999,7 @@ function parseUtcTimestampText(text) {
|
|
|
999
999
|
* the hard way.
|
|
1000
1000
|
*/
|
|
1001
1001
|
function createPgArrayParser(element) {
|
|
1002
|
-
const arrayParser =
|
|
1002
|
+
const arrayParser = _pg_1.default.types.arrayParser;
|
|
1003
1003
|
return (text) => text
|
|
1004
1004
|
? arrayParser.create(text, (entry) => (entry === null || entry === undefined ? null : element(entry))).parse()
|
|
1005
1005
|
: null;
|
|
@@ -1129,7 +1129,7 @@ function isDefaultTextParser(oid, parser) {
|
|
|
1129
1129
|
* being replaced.
|
|
1130
1130
|
*/
|
|
1131
1131
|
function warnParserOverwrite(oid, typeName) {
|
|
1132
|
-
const getParser =
|
|
1132
|
+
const getParser = _pg_1.default.types.getTypeParser;
|
|
1133
1133
|
const current = getParser(oid, 'text');
|
|
1134
1134
|
// Turbine's own earlier registration is not a third party's expectation.
|
|
1135
1135
|
if (current[TURBINE_PARSER])
|
|
@@ -1193,8 +1193,8 @@ function registerUtcTemporalParsers() {
|
|
|
1193
1193
|
// pg-types declares get/setTypeParser over its own OID enum, which lists the
|
|
1194
1194
|
// scalar types only. The array OIDs are just as real, so both calls are
|
|
1195
1195
|
// retyped over a plain number rather than the incomplete enum.
|
|
1196
|
-
const getParser =
|
|
1197
|
-
const setParser =
|
|
1196
|
+
const getParser = _pg_1.default.types.getTypeParser;
|
|
1197
|
+
const setParser = _pg_1.default.types.setTypeParser;
|
|
1198
1198
|
warnParserOverwrite(1114, 'timestamp');
|
|
1199
1199
|
warnParserOverwrite(1082, 'date');
|
|
1200
1200
|
warnParserOverwrite(1115, 'timestamp[]');
|
|
@@ -1251,8 +1251,8 @@ function registerUtcTemporalParsers() {
|
|
|
1251
1251
|
* what makes it so, and is not an optimisation of it.
|
|
1252
1252
|
*/
|
|
1253
1253
|
function registerFastTemporalParserIfDefault(oid, build) {
|
|
1254
|
-
const getParser =
|
|
1255
|
-
const setParser =
|
|
1254
|
+
const getParser = _pg_1.default.types.getTypeParser;
|
|
1255
|
+
const setParser = _pg_1.default.types.setTypeParser;
|
|
1256
1256
|
const current = getParser(oid, 'text');
|
|
1257
1257
|
const isTurbines = current[TURBINE_PARSER] === true;
|
|
1258
1258
|
if (!isTurbines && !isDefaultTextParser(oid, current))
|
|
@@ -1347,7 +1347,7 @@ function jsonWireCoercionOid(pgType) {
|
|
|
1347
1347
|
function coerceJsonWireValue(oid, value) {
|
|
1348
1348
|
if (typeof value !== 'string')
|
|
1349
1349
|
return value;
|
|
1350
|
-
return
|
|
1350
|
+
return _pg_1.default.types.getTypeParser(oid, 'text')(value);
|
|
1351
1351
|
}
|
|
1352
1352
|
// ---------------------------------------------------------------------------
|
|
1353
1353
|
// Unknown-field diagnostics
|
|
@@ -72,6 +72,13 @@ export declare function deferredIdentity(tag: string): {
|
|
|
72
72
|
* {@link RAW_QUERY_MODEL} and the action names the entry point (`'raw'`,
|
|
73
73
|
* `'sql'`, `'rawQuery'`, `'$queryRaw'` ...). `wrap` turns a driver error into
|
|
74
74
|
* what the caller sees, and the event carries that same error.
|
|
75
|
+
*
|
|
76
|
+
* The failure is reported from inside the `catch`, never handed to a `finally`
|
|
77
|
+
* through a local. SWC's minifier (the one Next.js runs over server bundles)
|
|
78
|
+
* treats `error = wrap(err); throw error;` as a single-use temporary and
|
|
79
|
+
* rewrites it to `throw wrap(err)`, dropping the assignment the `finally` then
|
|
80
|
+
* reads. Every failed statement in a minified bundle was reported as a
|
|
81
|
+
* success. `minified-events.test.ts` runs this module through that minifier.
|
|
75
82
|
*/
|
|
76
83
|
export declare function runReportedStatement<R extends {
|
|
77
84
|
rowCount?: number | null;
|
package/dist/cjs/query-events.js
CHANGED
|
@@ -103,31 +103,39 @@ function deferredIdentity(tag) {
|
|
|
103
103
|
* {@link RAW_QUERY_MODEL} and the action names the entry point (`'raw'`,
|
|
104
104
|
* `'sql'`, `'rawQuery'`, `'$queryRaw'` ...). `wrap` turns a driver error into
|
|
105
105
|
* what the caller sees, and the event carries that same error.
|
|
106
|
+
*
|
|
107
|
+
* The failure is reported from inside the `catch`, never handed to a `finally`
|
|
108
|
+
* through a local. SWC's minifier (the one Next.js runs over server bundles)
|
|
109
|
+
* treats `error = wrap(err); throw error;` as a single-use temporary and
|
|
110
|
+
* rewrites it to `throw wrap(err)`, dropping the assignment the `finally` then
|
|
111
|
+
* reads. Every failed statement in a minified bundle was reported as a
|
|
112
|
+
* success. `minified-events.test.ts` runs this module through that minifier.
|
|
106
113
|
*/
|
|
107
114
|
async function runReportedStatement(sink, identity, sql, params, run, wrap) {
|
|
108
115
|
const start = performance.now();
|
|
116
|
+
const report = (rows, error) => {
|
|
117
|
+
if (!sink)
|
|
118
|
+
return;
|
|
119
|
+
emitStatementEvent(sink, {
|
|
120
|
+
...identity,
|
|
121
|
+
sql,
|
|
122
|
+
params,
|
|
123
|
+
duration: performance.now() - start,
|
|
124
|
+
rows,
|
|
125
|
+
...(error === undefined ? {} : { error }),
|
|
126
|
+
});
|
|
127
|
+
};
|
|
109
128
|
let result;
|
|
110
|
-
let error;
|
|
111
129
|
try {
|
|
112
130
|
result = await run();
|
|
113
|
-
return result;
|
|
114
131
|
}
|
|
115
132
|
catch (err) {
|
|
116
|
-
error = wrap(err);
|
|
133
|
+
const error = wrap(err);
|
|
134
|
+
report(0, error instanceof Error ? error : new Error(String(error)));
|
|
117
135
|
throw error;
|
|
118
136
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
emitStatementEvent(sink, {
|
|
122
|
-
...identity,
|
|
123
|
-
sql,
|
|
124
|
-
params,
|
|
125
|
-
duration: performance.now() - start,
|
|
126
|
-
rows: resultRowCount(result),
|
|
127
|
-
...(error === undefined ? {} : { error: error instanceof Error ? error : new Error(String(error)) }),
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
}
|
|
137
|
+
report(resultRowCount(result));
|
|
138
|
+
return result;
|
|
131
139
|
}
|
|
132
140
|
/** The identity of a raw statement run through `action`. */
|
|
133
141
|
const rawIdentity = (action) => ({
|
package/dist/client.js
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* const users = db.table<User>('users');
|
|
22
22
|
* ```
|
|
23
23
|
*/
|
|
24
|
-
import pg from 'pg';
|
|
24
|
+
import pg from '#pg';
|
|
25
25
|
import { mergeConnectionStringOptions } from './connection-url.js';
|
|
26
26
|
import { postgresDialect } from './dialect.js';
|
|
27
27
|
import { ConnectionError, errorMessageModesDiverged, PipelineError, registerClientErrorMessageMode, runWithErrorMessageMode, setErrorMessageMode, TimeoutError, UnsupportedFeatureError, ValidationError, wrapPgError, } from './errors.js';
|
package/dist/introspect.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*
|
|
8
8
|
* This is the foundation of `npx turbine generate`.
|
|
9
9
|
*/
|
|
10
|
-
import pg from 'pg';
|
|
10
|
+
import pg from '#pg';
|
|
11
11
|
import { postgresDialect } from './dialect.js';
|
|
12
12
|
import { ValidationError } from './errors.js';
|
|
13
13
|
import { isDateType, pgTypeToTs, singularize, snakeToCamel, } from './schema.js';
|
package/dist/observe.js
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* model/action identity, the counts, and the latency percentiles. It never
|
|
13
13
|
* carries SQL text or bound parameter values.
|
|
14
14
|
*/
|
|
15
|
-
import pg from 'pg';
|
|
15
|
+
import pg from '#pg';
|
|
16
16
|
import { ValidationError } from './errors.js';
|
|
17
17
|
/**
|
|
18
18
|
* Buffer key: minute bucket + model + action. Joined on a NUL, which cannot
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* turbine-orm, the stand-in for `pg` on runtimes that cannot run it.
|
|
3
|
+
*
|
|
4
|
+
* The modules the serverless entry shares with the main entry import the
|
|
5
|
+
* driver as `#pg`, a package.json `imports` alias. It resolves to the real `pg`
|
|
6
|
+
* everywhere except under the `edge-light` and `browser` conditions (Vercel
|
|
7
|
+
* Edge, Next.js edge routes, browser bundles), where it resolves to this file.
|
|
8
|
+
* Those runtimes have no TCP sockets and no `fs`, `net` or `tls`, so bundling
|
|
9
|
+
* `pg` there fails the build outright, although `turbineHttp()` never opens a
|
|
10
|
+
* pg connection. `workerd` keeps the real driver: `pg` supports Cloudflare
|
|
11
|
+
* Workers, and a Hyperdrive user may let Turbine open the pool.
|
|
12
|
+
*
|
|
13
|
+
* What the shared code touches on a caller-supplied pool:
|
|
14
|
+
* - `types.getTypeParser`, when a relation's JSON column is decoded with the
|
|
15
|
+
* driver's text parser. The identity parser here leaves the JSON wire text
|
|
16
|
+
* as it arrived (a `date` inside a relation stays a string), which is also
|
|
17
|
+
* what an HTTP driver that ships its own type parsers would disagree with
|
|
18
|
+
* least.
|
|
19
|
+
* - nothing else: parser registration, `new pg.Pool` and the pipeline wire
|
|
20
|
+
* protocol only run when Turbine owns a TCP pool, and on this runtime that
|
|
21
|
+
* is the error below instead of an unresolvable import.
|
|
22
|
+
*/
|
|
23
|
+
declare function unavailable(): never;
|
|
24
|
+
declare class Unavailable {
|
|
25
|
+
constructor();
|
|
26
|
+
}
|
|
27
|
+
declare const pg: {
|
|
28
|
+
Pool: typeof Unavailable;
|
|
29
|
+
Client: typeof Unavailable;
|
|
30
|
+
Result: typeof Unavailable;
|
|
31
|
+
utils: {
|
|
32
|
+
prepareValue: typeof unavailable;
|
|
33
|
+
};
|
|
34
|
+
types: {
|
|
35
|
+
getTypeParser: () => (value: string) => string;
|
|
36
|
+
setTypeParser: () => void;
|
|
37
|
+
arrayParser: {
|
|
38
|
+
create: typeof unavailable;
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
export default pg;
|
package/dist/pg-edge.js
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* turbine-orm, the stand-in for `pg` on runtimes that cannot run it.
|
|
3
|
+
*
|
|
4
|
+
* The modules the serverless entry shares with the main entry import the
|
|
5
|
+
* driver as `#pg`, a package.json `imports` alias. It resolves to the real `pg`
|
|
6
|
+
* everywhere except under the `edge-light` and `browser` conditions (Vercel
|
|
7
|
+
* Edge, Next.js edge routes, browser bundles), where it resolves to this file.
|
|
8
|
+
* Those runtimes have no TCP sockets and no `fs`, `net` or `tls`, so bundling
|
|
9
|
+
* `pg` there fails the build outright, although `turbineHttp()` never opens a
|
|
10
|
+
* pg connection. `workerd` keeps the real driver: `pg` supports Cloudflare
|
|
11
|
+
* Workers, and a Hyperdrive user may let Turbine open the pool.
|
|
12
|
+
*
|
|
13
|
+
* What the shared code touches on a caller-supplied pool:
|
|
14
|
+
* - `types.getTypeParser`, when a relation's JSON column is decoded with the
|
|
15
|
+
* driver's text parser. The identity parser here leaves the JSON wire text
|
|
16
|
+
* as it arrived (a `date` inside a relation stays a string), which is also
|
|
17
|
+
* what an HTTP driver that ships its own type parsers would disagree with
|
|
18
|
+
* least.
|
|
19
|
+
* - nothing else: parser registration, `new pg.Pool` and the pipeline wire
|
|
20
|
+
* protocol only run when Turbine owns a TCP pool, and on this runtime that
|
|
21
|
+
* is the error below instead of an unresolvable import.
|
|
22
|
+
*/
|
|
23
|
+
import { ConnectionError } from './errors.js';
|
|
24
|
+
function unavailable() {
|
|
25
|
+
throw new ConnectionError('turbine-orm cannot open its own Postgres connection in this runtime: it has no TCP driver. ' +
|
|
26
|
+
'Pass a pg-compatible pool instead of a connection string, e.g. turbineHttp(pool, schema) from turbine-orm/serverless.');
|
|
27
|
+
}
|
|
28
|
+
class Unavailable {
|
|
29
|
+
constructor() {
|
|
30
|
+
unavailable();
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
const identity = (value) => value;
|
|
34
|
+
const pg = {
|
|
35
|
+
Pool: Unavailable,
|
|
36
|
+
Client: Unavailable,
|
|
37
|
+
Result: Unavailable,
|
|
38
|
+
utils: { prepareValue: unavailable },
|
|
39
|
+
types: {
|
|
40
|
+
getTypeParser: () => identity,
|
|
41
|
+
setTypeParser: () => { },
|
|
42
|
+
arrayParser: { create: unavailable },
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
export default pg;
|
|
@@ -16,9 +16,13 @@
|
|
|
16
16
|
* This is the same pattern used by pg-cursor and pg-query-stream, but extended
|
|
17
17
|
* to handle N queries in a single pipeline.
|
|
18
18
|
*/
|
|
19
|
-
import
|
|
20
|
-
import { prepareValue } from 'pg/lib/utils';
|
|
19
|
+
import pg from '#pg';
|
|
21
20
|
import { PipelineError, TimeoutError, wrapPgError } from './errors.js';
|
|
21
|
+
// Read off pg's own module object (`pg.Result` IS `pg/lib/result`, `pg.utils`
|
|
22
|
+
// IS `pg/lib/utils`) rather than imported by subpath, so the whole driver comes
|
|
23
|
+
// through the one `#pg` alias that edge bundles swap out (see src/pg-edge.ts).
|
|
24
|
+
const Result = pg.Result;
|
|
25
|
+
const { prepareValue } = pg.utils;
|
|
22
26
|
/**
|
|
23
27
|
* Marks a client this module could not hand back in a reusable state, so the
|
|
24
28
|
* caller releases it WITH an error and the pool drops it instead of lending it
|
package/dist/query/utils.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Standalone utility functions and classes used by the query builder.
|
|
5
5
|
*/
|
|
6
|
-
import pg from 'pg';
|
|
6
|
+
import pg from '#pg';
|
|
7
7
|
import { ValidationError } from '../errors.js';
|
|
8
8
|
import { camelToSnake, localDateTimeKind, snakeToCamel, timeOfDayKind } from '../schema.js';
|
|
9
9
|
import { shouldWarnOnce, WARN_NS } from './warn-registry.js';
|
package/dist/query-events.d.ts
CHANGED
|
@@ -72,6 +72,13 @@ export declare function deferredIdentity(tag: string): {
|
|
|
72
72
|
* {@link RAW_QUERY_MODEL} and the action names the entry point (`'raw'`,
|
|
73
73
|
* `'sql'`, `'rawQuery'`, `'$queryRaw'` ...). `wrap` turns a driver error into
|
|
74
74
|
* what the caller sees, and the event carries that same error.
|
|
75
|
+
*
|
|
76
|
+
* The failure is reported from inside the `catch`, never handed to a `finally`
|
|
77
|
+
* through a local. SWC's minifier (the one Next.js runs over server bundles)
|
|
78
|
+
* treats `error = wrap(err); throw error;` as a single-use temporary and
|
|
79
|
+
* rewrites it to `throw wrap(err)`, dropping the assignment the `finally` then
|
|
80
|
+
* reads. Every failed statement in a minified bundle was reported as a
|
|
81
|
+
* success. `minified-events.test.ts` runs this module through that minifier.
|
|
75
82
|
*/
|
|
76
83
|
export declare function runReportedStatement<R extends {
|
|
77
84
|
rowCount?: number | null;
|
package/dist/query-events.js
CHANGED
|
@@ -94,31 +94,39 @@ export function deferredIdentity(tag) {
|
|
|
94
94
|
* {@link RAW_QUERY_MODEL} and the action names the entry point (`'raw'`,
|
|
95
95
|
* `'sql'`, `'rawQuery'`, `'$queryRaw'` ...). `wrap` turns a driver error into
|
|
96
96
|
* what the caller sees, and the event carries that same error.
|
|
97
|
+
*
|
|
98
|
+
* The failure is reported from inside the `catch`, never handed to a `finally`
|
|
99
|
+
* through a local. SWC's minifier (the one Next.js runs over server bundles)
|
|
100
|
+
* treats `error = wrap(err); throw error;` as a single-use temporary and
|
|
101
|
+
* rewrites it to `throw wrap(err)`, dropping the assignment the `finally` then
|
|
102
|
+
* reads. Every failed statement in a minified bundle was reported as a
|
|
103
|
+
* success. `minified-events.test.ts` runs this module through that minifier.
|
|
97
104
|
*/
|
|
98
105
|
export async function runReportedStatement(sink, identity, sql, params, run, wrap) {
|
|
99
106
|
const start = performance.now();
|
|
107
|
+
const report = (rows, error) => {
|
|
108
|
+
if (!sink)
|
|
109
|
+
return;
|
|
110
|
+
emitStatementEvent(sink, {
|
|
111
|
+
...identity,
|
|
112
|
+
sql,
|
|
113
|
+
params,
|
|
114
|
+
duration: performance.now() - start,
|
|
115
|
+
rows,
|
|
116
|
+
...(error === undefined ? {} : { error }),
|
|
117
|
+
});
|
|
118
|
+
};
|
|
100
119
|
let result;
|
|
101
|
-
let error;
|
|
102
120
|
try {
|
|
103
121
|
result = await run();
|
|
104
|
-
return result;
|
|
105
122
|
}
|
|
106
123
|
catch (err) {
|
|
107
|
-
error = wrap(err);
|
|
124
|
+
const error = wrap(err);
|
|
125
|
+
report(0, error instanceof Error ? error : new Error(String(error)));
|
|
108
126
|
throw error;
|
|
109
127
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
emitStatementEvent(sink, {
|
|
113
|
-
...identity,
|
|
114
|
-
sql,
|
|
115
|
-
params,
|
|
116
|
-
duration: performance.now() - start,
|
|
117
|
-
rows: resultRowCount(result),
|
|
118
|
-
...(error === undefined ? {} : { error: error instanceof Error ? error : new Error(String(error)) }),
|
|
119
|
-
});
|
|
120
|
-
}
|
|
121
|
-
}
|
|
128
|
+
report(resultRowCount(result));
|
|
129
|
+
return result;
|
|
122
130
|
}
|
|
123
131
|
/** The identity of a raw statement run through `action`. */
|
|
124
132
|
export const rawIdentity = (action) => ({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "turbine-orm",
|
|
3
|
-
"version": "0.79.
|
|
3
|
+
"version": "0.79.1",
|
|
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).",
|
|
@@ -96,6 +96,15 @@
|
|
|
96
96
|
}
|
|
97
97
|
}
|
|
98
98
|
},
|
|
99
|
+
"//imports": "`#pg` is how the modules shared with the serverless entry reach node-postgres. It is `pg` itself everywhere except edge-light and browser, which get dist/pg-edge.js (see src/pg-edge.ts): those runtimes cannot bundle pg at all, and turbineHttp() never uses it. workerd is listed first so Cloudflare keeps the real driver (pg supports Workers). An imports target may be a bare package name, which is what keeps src/ runnable under tsx with no dist/ present. The CJS build (dist/cjs) is Node-only: scripts/finish-cjs-build.mjs rewrites its require(\"#pg\") to require(\"pg\"), because a require() resolves #imports against the NEAREST package.json (dist/cjs/package.json) and publint rejects a nested imports field. Edge bundlers resolve the import condition, i.e. the ESM files. `npm run check:edge` bundles the serverless entry for the edge conditions and fails on any pg or Node-builtin import.",
|
|
100
|
+
"imports": {
|
|
101
|
+
"#pg": {
|
|
102
|
+
"workerd": "pg",
|
|
103
|
+
"edge-light": "./dist/pg-edge.js",
|
|
104
|
+
"browser": "./dist/pg-edge.js",
|
|
105
|
+
"default": "pg"
|
|
106
|
+
}
|
|
107
|
+
},
|
|
99
108
|
"main": "./dist/cjs/index.js",
|
|
100
109
|
"types": "./dist/index.d.ts",
|
|
101
110
|
"bin": {
|
|
@@ -115,7 +124,7 @@
|
|
|
115
124
|
"//prepublishOnly": "The gate for a MANUAL LOCAL `npm publish`, which is the normal release flow here, so it is the ONLY gate on that path (release.yml runs on a pushed tag, and a local publish never triggers it). `check:package` joined it 2026-07-28: publint --strict + attw --pack, the two checks that catch a broken exports map or CJS declarations that resolve to ESM, which is a class of bug that shipped once already and is invisible to every other script here. `test:coverage:cli` joined at the same time (~3s) so a local publish cannot ship a Studio/migrate coverage regression. Still deliberately absent, because they need containers or minutes of packing: the non-Postgres engine suites and the tarball install smoke, which release.yml + ci.yml run. check:cycles joined 2026-08-02 (the query/->client.ts acyclicity rule as a build failure instead of CLAUDE.md prose). check:release-tests joined at the same time and runs LAST because it is the expensive one: outside CI it runs the full DB-backed `npm test` (refusing to publish when no DATABASE_URL is reachable, escape hatch TURBINE_PUBLISH_WITHOUT_DB=1), which closes the old gap where the release path never executed a query against a real Postgres; in CI it self-skips since release.yml/nightly.yml run that suite as dedicated jobs. Known double-coverage: `npm test` re-runs the unit-lane files serially, so a local publish executes them twice; accepted, the second pass is the one with a real database behind it.",
|
|
116
125
|
"scripts": {
|
|
117
126
|
"prebuild": "npm run gen:studio",
|
|
118
|
-
"build": "rm -rf dist && tsc && tsc --project tsconfig.cjs.json &&
|
|
127
|
+
"build": "rm -rf dist && tsc && tsc --project tsconfig.cjs.json && node scripts/finish-cjs-build.mjs",
|
|
119
128
|
"dev": "tsc --watch",
|
|
120
129
|
"typecheck": "tsc --noEmit --project tsconfig.test.json",
|
|
121
130
|
"generate": "tsx src/cli/index.ts generate",
|
|
@@ -165,7 +174,9 @@
|
|
|
165
174
|
"check:package": "publint --strict && attw --pack . --profile node16",
|
|
166
175
|
"//check:package-types": "The @types/pg tripwire, and the permanent guard on the v0.28.1 regression: no reference to the pg module family may reach a published .d.ts, or a types-only package becomes a hard requirement for every consumer compiling under strict. It lived as an inline grep in ONE ci.yml job, which left the gate proving the change safe off the publish path entirely (a tag push and a local `npm publish` both reached npm without asking). The logic is scripts/check-no-pg-types.mjs now and this script is its single entry point, called by prepublishOnly and by the `consumer-types` job in BOTH ci.yml and release.yml. Reads dist/, so it must run after `npm run build`.",
|
|
167
176
|
"check:package-types": "node scripts/check-no-pg-types.mjs",
|
|
168
|
-
"
|
|
177
|
+
"//check:edge": "The edge-runtime gate for turbine-orm/serverless: bundles dist/serverless.js (the ESM build, which is what edge bundlers resolve) with esbuild under the edge-light/worker/browser conditions and fails on any pg-family import, any Node builtin but node:async_hooks, or any other package. Every other gate here runs on Node, which is how a static `import pg from 'pg'` in a shared module broke every edge build while CI stayed green. A CONTROL bundle under Node conditions must reach pg first, or the scan refuses to pass. Reads dist/, so it runs after `npm run build`: in prepublishOnly, and in the build job of ci.yml and the test job of release.yml.",
|
|
178
|
+
"check:edge": "node scripts/check-edge-bundle.mjs",
|
|
179
|
+
"prepublishOnly": "npm run build && npm run typecheck && npm run lint && npm run test:unit && npm run test:coverage:cli && npm run check:error-codes && npm run check:error-prefix && npm run check:changelog && npm run check:cycles && npm run check:skip-gates && npm run check:private-terms && npm run check:package && npm run check:package-types && npm run check:edge && npm run size && npm run check:release-tests",
|
|
169
180
|
"prepack": "node scripts/strip-prepare.mjs",
|
|
170
181
|
"postpack": "node scripts/restore-prepare.mjs",
|
|
171
182
|
"size": "size-limit",
|
|
@@ -195,11 +206,13 @@
|
|
|
195
206
|
"@biomejs/biome": "^2.4.10",
|
|
196
207
|
"@size-limit/esbuild": "^12.1.0",
|
|
197
208
|
"@size-limit/file": "^12.1.0",
|
|
209
|
+
"@swc/core": "1.16.2",
|
|
198
210
|
"@types/node": "^26.1.0",
|
|
199
211
|
"@types/pg": "^8.11.11",
|
|
200
212
|
"@zvndev/powdb-client": "^0.20.0",
|
|
201
213
|
"@zvndev/powdb-embedded": "^0.20.0",
|
|
202
214
|
"c8": "^11.0.0",
|
|
215
|
+
"esbuild": "^0.28.2",
|
|
203
216
|
"husky": "^9.1.7",
|
|
204
217
|
"lint-staged": "^17.0.8",
|
|
205
218
|
"mssql": "^12.7.0",
|