turbine-orm 0.53.0 → 0.54.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -2
- package/dist/cjs/cli/mcp.js +6 -0
- package/dist/cjs/cli/studio.js +10 -0
- package/dist/cjs/client.d.ts +182 -25
- package/dist/cjs/client.js +210 -29
- package/dist/cjs/dialect.d.ts +24 -0
- package/dist/cjs/dialect.js +1 -0
- package/dist/cjs/index.d.ts +1 -1
- package/dist/cjs/mssql.js +1 -0
- package/dist/cjs/mysql.js +1 -0
- package/dist/cjs/powdb.js +1 -0
- package/dist/cjs/query/builder.js +11 -2
- package/dist/cjs/query/utils.d.ts +85 -1
- package/dist/cjs/query/utils.js +151 -1
- package/dist/cjs/query/warn-registry.d.ts +6 -0
- package/dist/cjs/query/warn-registry.js +6 -0
- package/dist/cjs/sqlite.js +1 -0
- package/dist/cli/mcp.js +6 -0
- package/dist/cli/studio.js +11 -1
- package/dist/client.d.ts +182 -25
- package/dist/client.js +211 -30
- package/dist/dialect.d.ts +24 -0
- package/dist/dialect.js +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/mssql.js +1 -0
- package/dist/mysql.js +1 -0
- package/dist/powdb.js +1 -0
- package/dist/query/builder.js +11 -2
- package/dist/query/utils.d.ts +85 -1
- package/dist/query/utils.js +146 -1
- package/dist/query/warn-registry.d.ts +6 -0
- package/dist/query/warn-registry.js +6 -0
- package/dist/sqlite.js +1 -0
- package/package.json +1 -1
package/dist/query/utils.js
CHANGED
|
@@ -321,6 +321,151 @@ export function parseDbDate(value) {
|
|
|
321
321
|
return new Date(`${value.replace(' ', 'T')}Z`);
|
|
322
322
|
}
|
|
323
323
|
// ---------------------------------------------------------------------------
|
|
324
|
+
// Driver type parsers for the zone-less temporal OIDs
|
|
325
|
+
// ---------------------------------------------------------------------------
|
|
326
|
+
/**
|
|
327
|
+
* A Postgres `date` wire value: `YYYY-MM-DD`, optionally with more than four
|
|
328
|
+
* year digits, optionally suffixed ` BC`. Anything else (`infinity`,
|
|
329
|
+
* `-infinity`, and any shape a future server adds) is deliberately NOT matched
|
|
330
|
+
* so it falls through to the driver's own parser untouched.
|
|
331
|
+
*/
|
|
332
|
+
const PG_DATE_TEXT_RE = /^(\d{4,})-(\d{2})-(\d{2})( BC)?$/;
|
|
333
|
+
/**
|
|
334
|
+
* Build the driver parser for Postgres `date` (OID 1082) that reads a
|
|
335
|
+
* zone-less calendar day as **UTC midnight**.
|
|
336
|
+
*
|
|
337
|
+
* The pg default builds the Date from the process's LOCAL zone, so the stored
|
|
338
|
+
* calendar day `2026-07-21` comes back as `2026-07-20T22:00:00Z` in
|
|
339
|
+
* `Europe/Berlin` and `2026-07-20T15:00:00Z` in `Asia/Tokyo`: the wrong
|
|
340
|
+
* calendar day everywhere east of UTC, and the wrong instant everywhere except
|
|
341
|
+
* UTC itself. It is also the exact mirror-image of the WRITE side, which
|
|
342
|
+
* already renders a bound `Date` from its UTC components
|
|
343
|
+
* ({@link toLocalDateTimeLiteral}), so today a read-modify-write cycle on a
|
|
344
|
+
* `date` column east of UTC walks the stored day one day earlier per cycle.
|
|
345
|
+
* This is the missing read half of `utcTimestamps`, matching what
|
|
346
|
+
* {@link parseDbDate} already does for the JSON path and what the OID 1114
|
|
347
|
+
* parser already does for `timestamp`.
|
|
348
|
+
*
|
|
349
|
+
* `fallback` is the parser this one REPLACES, and it must be captured with
|
|
350
|
+
* `pg.types.getTypeParser(1082, 'text')` BEFORE registration (reading it after
|
|
351
|
+
* would hand back this function and recurse forever). It keeps `infinity` /
|
|
352
|
+
* `-infinity` on the driver's `Infinity` / `-Infinity`.
|
|
353
|
+
*
|
|
354
|
+
* `setUTCFullYear` rather than the `Date` constructor, so a two-or-three-digit
|
|
355
|
+
* year is not silently mapped into the 1900s, and ` BC` maps to the
|
|
356
|
+
* astronomical year (`0044 BC` → -43) the way the driver's own parser does.
|
|
357
|
+
*/
|
|
358
|
+
export function createUtcDateParser(fallback) {
|
|
359
|
+
return (text) => {
|
|
360
|
+
const m = PG_DATE_TEXT_RE.exec(text);
|
|
361
|
+
if (!m)
|
|
362
|
+
return fallback(text);
|
|
363
|
+
const year = m[4] ? -(Number(m[1]) - 1) : Number(m[1]);
|
|
364
|
+
const date = new Date(0);
|
|
365
|
+
date.setUTCFullYear(year, Number(m[2]) - 1, Number(m[3]));
|
|
366
|
+
date.setUTCHours(0, 0, 0, 0);
|
|
367
|
+
return date;
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* A Postgres `timestamp` (without time zone) wire value:
|
|
372
|
+
* `YYYY-MM-DD HH:MM:SS`, optionally with more than four year digits, optional
|
|
373
|
+
* fractional seconds, optional ` BC`. As with {@link PG_DATE_TEXT_RE},
|
|
374
|
+
* `infinity` / `-infinity` and any shape a future server adds deliberately do
|
|
375
|
+
* NOT match, so they fall through to the driver's own parser untouched.
|
|
376
|
+
*/
|
|
377
|
+
const PG_TIMESTAMP_TEXT_RE = /^(\d{4,})-(\d{2})-(\d{2})[ T](\d{2}):(\d{2}):(\d{2})(?:\.(\d+))?( BC)?$/;
|
|
378
|
+
/**
|
|
379
|
+
* Build the driver parser for Postgres `timestamp` (OID 1114) that reads an
|
|
380
|
+
* offset-less date-time as UTC. Also lifted to the `_timestamp` array OID
|
|
381
|
+
* (1115), so the scalar and the array can never settle on different
|
|
382
|
+
* interpretations.
|
|
383
|
+
*
|
|
384
|
+
* `fallback` is the parser this one REPLACES and must be captured with
|
|
385
|
+
* `pg.types.getTypeParser(1114, 'text')` BEFORE registration (see
|
|
386
|
+
* {@link createUtcDateParser}). It is what keeps `infinity` / `-infinity` on
|
|
387
|
+
* the driver's `Infinity` / `-Infinity`: the earlier
|
|
388
|
+
* `new Date(text.replace(' ', 'T') + 'Z')` form turned `'infinity'` into
|
|
389
|
+
* `'infinityZ'` and so into an `Invalid Date` that flowed on silently.
|
|
390
|
+
*
|
|
391
|
+
* Component assembly rather than `Date` string parsing, for the same reason as
|
|
392
|
+
* the `date` parser: a year outside four digits and a ` BC` suffix are not
|
|
393
|
+
* parseable as ISO-8601 and would otherwise also become `Invalid Date`.
|
|
394
|
+
* Fractional seconds are truncated to milliseconds, which is what
|
|
395
|
+
* `Date`-string parsing did too.
|
|
396
|
+
*/
|
|
397
|
+
export function createUtcTimestampParser(fallback) {
|
|
398
|
+
return (text) => {
|
|
399
|
+
const m = PG_TIMESTAMP_TEXT_RE.exec(text);
|
|
400
|
+
if (!m)
|
|
401
|
+
return fallback(text);
|
|
402
|
+
const year = m[8] ? -(Number(m[1]) - 1) : Number(m[1]);
|
|
403
|
+
const ms = m[7] ? Number(m[7].slice(0, 3).padEnd(3, '0')) : 0;
|
|
404
|
+
const date = new Date(0);
|
|
405
|
+
date.setUTCFullYear(year, Number(m[2]) - 1, Number(m[3]));
|
|
406
|
+
date.setUTCHours(Number(m[4]), Number(m[5]), Number(m[6]), ms);
|
|
407
|
+
return date;
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* The offset-less-timestamp-as-UTC reading, with no fallback: `text` must be a
|
|
412
|
+
* plain `YYYY-MM-DD HH:MM:SS[.ffffff]`. Used where the input shape is already
|
|
413
|
+
* known (tests, JSON-wire coercion); the DRIVER parser is
|
|
414
|
+
* {@link createUtcTimestampParser}, which delegates everything else.
|
|
415
|
+
*/
|
|
416
|
+
export function parseUtcTimestampText(text) {
|
|
417
|
+
return new Date(`${text.replace(' ', 'T')}Z`);
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* Lift an element parser to the matching Postgres array OID.
|
|
421
|
+
*
|
|
422
|
+
* Array OIDs do NOT inherit their element type's parser: registering a parser
|
|
423
|
+
* for `date` (1082) leaves `date[]` (1182) on the driver's default, so the same
|
|
424
|
+
* value read from a scalar column and from an array column would disagree by
|
|
425
|
+
* the process offset. Every scalar temporal parser Turbine registers is
|
|
426
|
+
* therefore registered in its array form too.
|
|
427
|
+
*
|
|
428
|
+
* `pg.types.arrayParser` is a public member of the `pg` module (it is what the
|
|
429
|
+
* driver's own `_text` / `_date` parsers are built from), so this adds no
|
|
430
|
+
* dependency. NULL elements stay `null` and are never handed to `element`.
|
|
431
|
+
*/
|
|
432
|
+
export function createPgArrayParser(element) {
|
|
433
|
+
const arrayParser = pg.types.arrayParser;
|
|
434
|
+
return (text) => arrayParser.create(text, (entry) => (entry === null || entry === undefined ? null : element(entry))).parse();
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* Register the UTC readings of the four zone-less temporal OIDs on the pg
|
|
438
|
+
* module: `timestamp` (1114), `date` (1082) and their array forms (1115, 1182).
|
|
439
|
+
*
|
|
440
|
+
* ONE place, because `pg.types.setTypeParser` is process-global and the pairing
|
|
441
|
+
* matters: registering a scalar without its array form, or a `date` without the
|
|
442
|
+
* `timestamp` beside it, produces two columns of the same row disagreeing about
|
|
443
|
+
* what the same wire text means. Both callers are processes Turbine owns the
|
|
444
|
+
* pg module in: `TurbineClient` on a pool it created (never on an external
|
|
445
|
+
* pool, whose parser configuration belongs to the caller), and `turbine studio`,
|
|
446
|
+
* which builds a raw pool of its own and must render what the application sees.
|
|
447
|
+
*
|
|
448
|
+
* Each fallback is read BEFORE its parser is installed, so an unrecognised wire
|
|
449
|
+
* value (`infinity`, and whatever a future server adds) still reaches the
|
|
450
|
+
* driver's own parser.
|
|
451
|
+
*/
|
|
452
|
+
export function registerUtcTemporalParsers() {
|
|
453
|
+
// pg-types declares get/setTypeParser over its own OID enum, which lists the
|
|
454
|
+
// scalar types only. The array OIDs are just as real, so both calls are
|
|
455
|
+
// retyped over a plain number rather than the incomplete enum.
|
|
456
|
+
const getParser = pg.types.getTypeParser;
|
|
457
|
+
const setParser = pg.types.setTypeParser;
|
|
458
|
+
const parseDate = createUtcDateParser(getParser(1082, 'text'));
|
|
459
|
+
const parseTimestamp = createUtcTimestampParser(getParser(1114, 'text'));
|
|
460
|
+
setParser(1114, parseTimestamp);
|
|
461
|
+
setParser(1082, parseDate);
|
|
462
|
+
// Array OIDs do not inherit their element parser, so `date[]` / `timestamp[]`
|
|
463
|
+
// would otherwise keep returning local-zone Dates while the scalar columns
|
|
464
|
+
// beside them returned UTC ones.
|
|
465
|
+
setParser(1182, createPgArrayParser(parseDate));
|
|
466
|
+
setParser(1115, createPgArrayParser(parseTimestamp));
|
|
467
|
+
}
|
|
468
|
+
// ---------------------------------------------------------------------------
|
|
324
469
|
// JSON-wire value coercion (relationLoadStrategy: 'join')
|
|
325
470
|
// ---------------------------------------------------------------------------
|
|
326
471
|
/**
|
|
@@ -343,7 +488,7 @@ export function parseDbDate(value) {
|
|
|
343
488
|
* numeric '1000.50' (string) 1000.5 (number, LOSSY)
|
|
344
489
|
* int8 '9007199254740993' 9007199254740992 (LOSSY)
|
|
345
490
|
* bytea Buffer '\xdeadbeef' (string)
|
|
346
|
-
* date Date (
|
|
491
|
+
* date Date (UTC midnight) Date (UTC midnight, by coincidence)
|
|
347
492
|
* interval { days, hours, … } '1 day 02:03:04' (string)
|
|
348
493
|
* point { x, y } '(1,2)' (string)
|
|
349
494
|
* circle { x, y, radius } '<(1,2),3>' (string)
|
|
@@ -82,4 +82,10 @@ export declare const WARN_NS: {
|
|
|
82
82
|
* the offenders, not one per column. The namespace name is historical.
|
|
83
83
|
*/
|
|
84
84
|
readonly untypedDateColumn: "untypedDateColumn";
|
|
85
|
+
/**
|
|
86
|
+
* `planCacheMode` was set on a client given an EXTERNAL pool, where Turbine
|
|
87
|
+
* runs no connection setup, so the option is a no-op (client.ts constructor).
|
|
88
|
+
* Keyed on the requested mode.
|
|
89
|
+
*/
|
|
90
|
+
readonly planCacheModeIgnored: "planCacheModeIgnored";
|
|
85
91
|
};
|
|
@@ -117,4 +117,10 @@ export const WARN_NS = {
|
|
|
117
117
|
* the offenders, not one per column. The namespace name is historical.
|
|
118
118
|
*/
|
|
119
119
|
untypedDateColumn: 'untypedDateColumn',
|
|
120
|
+
/**
|
|
121
|
+
* `planCacheMode` was set on a client given an EXTERNAL pool, where Turbine
|
|
122
|
+
* runs no connection setup, so the option is a no-op (client.ts constructor).
|
|
123
|
+
* Keyed on the requested mode.
|
|
124
|
+
*/
|
|
125
|
+
planCacheModeIgnored: 'planCacheModeIgnored',
|
|
120
126
|
};
|
package/dist/sqlite.js
CHANGED
|
@@ -377,6 +377,7 @@ export const sqliteDialect = {
|
|
|
377
377
|
supportsAdvisoryLock: false,
|
|
378
378
|
// No FROM-clause LATERAL: the opt-in lateral pick plan is Postgres-only.
|
|
379
379
|
supportsLateralJoin: false,
|
|
380
|
+
supportsPlanCacheMode: false,
|
|
380
381
|
// SQLite explains a compiled query with `EXPLAIN QUERY PLAN` (four columns:
|
|
381
382
|
// id, parent, notused, detail), overriding the inherited Postgres `EXPLAIN`.
|
|
382
383
|
explainQuery: { prefix: 'EXPLAIN QUERY PLAN' },
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "turbine-orm",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.54.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).",
|