turbine-orm 0.79.0 → 0.79.1-next.e9dcb00

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.
@@ -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 pg_1 = __importDefault(require("pg"));
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
- pg_1.default.types.setTypeParser(20, (0, utils_js_1.markTurbineParser)((val) => {
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 pg_1.default.Pool(TurbineClient.withPlanCacheMode(poolConfig, this.planCacheMode));
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 pg_1.default.Pool(TurbineClient.withPlanCacheMode({
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,
@@ -35,7 +35,7 @@ exports.buildRelationsFromForeignKeys = buildRelationsFromForeignKeys;
35
35
  exports.addAutoManyToManyRelations = addAutoManyToManyRelations;
36
36
  exports.deriveCatalogRelations = deriveCatalogRelations;
37
37
  exports.deriveEngineRelations = deriveEngineRelations;
38
- const pg_1 = __importDefault(require("pg"));
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 pg_1.default.Pool({
544
+ const pool = new _pg_1.default.Pool({
545
545
  connectionString: options.connectionString,
546
546
  max: 1,
547
547
  connectionTimeoutMillis: 10_000,
@@ -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 pg_1 = __importDefault(require("pg"));
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 pg_1.default.Pool({ connectionString: options.connectionString, max: 1 });
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 result_1 = __importDefault(require("pg/lib/result"));
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 result_1.default(undefined, client._types));
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: utils_1.prepareValue });
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: utils_1.prepareValue });
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: utils_1.prepareValue,
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: utils_1.prepareValue });
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: utils_1.prepareValue,
467
+ valueMapper: prepareValue,
464
468
  });
465
469
  connection.describe({ type: 'P', name: '' });
466
470
  connection.execute({ portal: '', rows: 0 });
@@ -51,7 +51,7 @@ exports.warnRedundantSortTerm = warnRedundantSortTerm;
51
51
  exports.selectNamesNothingMessage = selectNamesNothingMessage;
52
52
  exports.selectOmitExclusiveMessage = selectOmitExclusiveMessage;
53
53
  exports.normalizePagination = normalizePagination;
54
- const pg_1 = __importDefault(require("pg"));
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 = pg_1.default.types.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 = pg_1.default.types.getTypeParser;
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 = pg_1.default.types.getTypeParser;
1197
- const setParser = pg_1.default.types.setTypeParser;
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 = pg_1.default.types.getTypeParser;
1255
- const setParser = pg_1.default.types.setTypeParser;
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 pg_1.default.types.getTypeParser(oid, 'text')(value);
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;
@@ -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
- finally {
120
- if (sink) {
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';
@@ -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;
@@ -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 Result from 'pg/lib/result';
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
@@ -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';
@@ -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;
@@ -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
- finally {
111
- if (sink) {
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.0",
3
+ "version": "0.79.1-next.e9dcb00",
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 && echo '{\"type\":\"commonjs\"}' > dist/cjs/package.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
- "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 size && npm run check:release-tests",
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",