turbine-orm 0.37.0 → 0.38.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 +3 -3
- package/dist/cjs/cli/studio-ui.generated.js +1 -1
- package/dist/cjs/cli/studio.js +262 -60
- package/dist/cjs/errors.js +27 -0
- package/dist/cjs/nested-write.js +7 -7
- package/dist/cjs/query/batched-loader.js +4 -3
- package/dist/cjs/query/builder.js +9 -5
- package/dist/cjs/query/relations.js +7 -6
- package/dist/cjs/query/utils.js +13 -0
- package/dist/cjs/query/where-compile.js +2 -1
- package/dist/cjs/query/where.js +3 -3
- package/dist/cli/studio-ui.generated.js +1 -1
- package/dist/cli/studio.d.ts +27 -5
- package/dist/cli/studio.js +262 -60
- package/dist/errors.d.ts +11 -0
- package/dist/errors.js +26 -0
- package/dist/nested-write.js +8 -8
- package/dist/query/batched-loader.js +4 -3
- package/dist/query/builder.js +10 -6
- package/dist/query/relations.js +7 -6
- package/dist/query/utils.d.ts +10 -0
- package/dist/query/utils.js +12 -0
- package/dist/query/where-compile.js +2 -1
- package/dist/query/where.js +4 -4
- package/package.json +1 -1
|
@@ -88,6 +88,7 @@ const index_advisor_js_1 = require("../index-advisor.js");
|
|
|
88
88
|
const schema_js_1 = require("../schema.js");
|
|
89
89
|
const batched_loader_js_1 = require("./batched-loader.js");
|
|
90
90
|
const filters_js_1 = require("./filters.js");
|
|
91
|
+
const utils_js_1 = require("./utils.js");
|
|
91
92
|
const whereMod = __importStar(require("./where.js"));
|
|
92
93
|
const writesMod = __importStar(require("./writes.js"));
|
|
93
94
|
/** Relations already warned about missing FK indexes (once per process, dev only). */
|
|
@@ -388,10 +389,10 @@ function buildOrderBy(qi, orderBy, params, lateralSink) {
|
|
|
388
389
|
// are validated in the relation branch below, so skip them here.
|
|
389
390
|
if (process.env.NODE_ENV !== 'production') {
|
|
390
391
|
for (const [key, value] of Object.entries(orderBy)) {
|
|
391
|
-
if (isRelationOrderByValue(qi, value) && qi.tableMeta.relations
|
|
392
|
+
if (isRelationOrderByValue(qi, value) && (0, utils_js_1.ownLookup)(qi.tableMeta.relations, key))
|
|
392
393
|
continue;
|
|
393
394
|
const snakeKey = (0, schema_js_1.camelToSnake)(key);
|
|
394
|
-
if (!qi.tableMeta.columns.some((c) => c.name === snakeKey) && !(
|
|
395
|
+
if (!qi.tableMeta.columns.some((c) => c.name === snakeKey) && !Object.hasOwn(qi.tableMeta.columnMap, key)) {
|
|
395
396
|
console.warn(`[turbine] Unknown orderBy field "${key}" for table "${qi.tableMeta.name}". ` +
|
|
396
397
|
'This will cause a runtime error.');
|
|
397
398
|
}
|
|
@@ -473,7 +474,7 @@ function nullsSuffix(qi, nulls) {
|
|
|
473
474
|
* camelCase-named DB columns like "sortOrder").
|
|
474
475
|
*/
|
|
475
476
|
function resolveOrderByColumn(_qi, table, meta, key) {
|
|
476
|
-
const col = meta.columnMap
|
|
477
|
+
const col = (0, utils_js_1.ownLookup)(meta.columnMap, key) ?? (0, schema_js_1.camelToSnake)(key);
|
|
477
478
|
if (!meta.allColumns.includes(col)) {
|
|
478
479
|
throw new errors_js_1.ValidationError(`[turbine] Unknown field "${key}" in orderBy on table "${table}". ` +
|
|
479
480
|
`Known fields: ${Object.keys(meta.columnMap).join(', ') || '(none)'}.`);
|
|
@@ -591,7 +592,7 @@ function buildRelationOrderBy(qi, relName, value, alias, params, ctx, lateralSin
|
|
|
591
592
|
.map(([col, dirValue]) => {
|
|
592
593
|
// columnMap-first resolution (camelToSnake fallback): mirrors the
|
|
593
594
|
// scalar orderBy path so camelCase-named DB columns resolve here too.
|
|
594
|
-
const snakeCol = targetMeta.columnMap
|
|
595
|
+
const snakeCol = (0, utils_js_1.ownLookup)(targetMeta.columnMap, col) ?? (0, schema_js_1.camelToSnake)(col);
|
|
595
596
|
if (!targetMeta.allColumns.includes(snakeCol)) {
|
|
596
597
|
throw new errors_js_1.ValidationError(`[turbine] Unknown column "${col}" in orderBy on relation "${relName}" (table "${relDef.to}").`);
|
|
597
598
|
}
|
|
@@ -1059,7 +1060,7 @@ function resolveTargetColumns(qi, spec, targetMeta, includePii) {
|
|
|
1059
1060
|
// opt-in and comes back regardless of the query's `includePii`.
|
|
1060
1061
|
const selectedFields = Object.entries(spec.select)
|
|
1061
1062
|
.filter(([, v]) => v)
|
|
1062
|
-
.map(([k]) => targetMeta.columnMap
|
|
1063
|
+
.map(([k]) => (0, utils_js_1.ownLookup)(targetMeta.columnMap, k) ?? (0, schema_js_1.camelToSnake)(k));
|
|
1063
1064
|
return selectedFields.filter((col) => targetMeta.allColumns.includes(col));
|
|
1064
1065
|
}
|
|
1065
1066
|
// Default / omit-only relation projection: PII columns are excluded unless
|
|
@@ -1069,7 +1070,7 @@ function resolveTargetColumns(qi, spec, targetMeta, includePii) {
|
|
|
1069
1070
|
if (spec !== true && spec.omit) {
|
|
1070
1071
|
const omittedFields = new Set(Object.entries(spec.omit)
|
|
1071
1072
|
.filter(([, v]) => v)
|
|
1072
|
-
.map(([k]) => targetMeta.columnMap
|
|
1073
|
+
.map(([k]) => (0, utils_js_1.ownLookup)(targetMeta.columnMap, k) ?? (0, schema_js_1.camelToSnake)(k)));
|
|
1073
1074
|
return targetMeta.allColumns.filter((col) => !omittedFields.has(col) && !(hasPii && piiCols.has(col)));
|
|
1074
1075
|
}
|
|
1075
1076
|
if (hasPii) {
|
package/dist/cjs/query/utils.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
8
|
exports.OPERATOR_KEYS = exports.LRUCache = void 0;
|
|
9
9
|
exports.quoteIdent = quoteIdent;
|
|
10
|
+
exports.ownLookup = ownLookup;
|
|
10
11
|
exports.escSingleQuote = escSingleQuote;
|
|
11
12
|
exports.escapeLike = escapeLike;
|
|
12
13
|
exports.fnv1a64Hex = fnv1a64Hex;
|
|
@@ -28,6 +29,18 @@ exports.parseDbDate = parseDbDate;
|
|
|
28
29
|
function quoteIdent(name) {
|
|
29
30
|
return `"${name.replace(/"/g, '""')}"`;
|
|
30
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* Prototype-safe own-property read for the plain metadata maps (columnMap,
|
|
34
|
+
* relations, reverseColumnMap). These are constructed as plain objects, so a
|
|
35
|
+
* bare `map[key]` for a user-supplied field name like "constructor",
|
|
36
|
+
* "toString", or "__proto__" returns an inherited member from
|
|
37
|
+
* `Object.prototype` — a truthy value that slips past validation and produces a
|
|
38
|
+
* cryptic `TypeError` instead of a clean `ValidationError`. Returns `undefined`
|
|
39
|
+
* unless `key` is an OWN enumerable/non-enumerable property.
|
|
40
|
+
*/
|
|
41
|
+
function ownLookup(map, key) {
|
|
42
|
+
return Object.hasOwn(map, key) ? map[key] : undefined;
|
|
43
|
+
}
|
|
31
44
|
/**
|
|
32
45
|
* Escape single quotes for use as string keys in json_build_object().
|
|
33
46
|
* Doubles single quotes per SQL quoting rules.
|
|
@@ -39,6 +39,7 @@ exports.walkWhere = walkWhere;
|
|
|
39
39
|
exports.classifyScalarForSql = classifyScalarForSql;
|
|
40
40
|
exports.fingerprintScalarToken = fingerprintScalarToken;
|
|
41
41
|
const filters_js_1 = require("./filters.js");
|
|
42
|
+
const utils_js_1 = require("./utils.js");
|
|
42
43
|
/** True when a normalized relation filter carries at least one cardinality key. */
|
|
43
44
|
function isRelationFilterObj(filterObj) {
|
|
44
45
|
return ('some' in filterObj || 'every' in filterObj || 'none' in filterObj || 'is' in filterObj || 'isNot' in filterObj);
|
|
@@ -78,7 +79,7 @@ function walkWhere(host, where) {
|
|
|
78
79
|
events.push({ kind: 'not', condition: value });
|
|
79
80
|
continue;
|
|
80
81
|
}
|
|
81
|
-
const relDef = host.tableMeta.relations
|
|
82
|
+
const relDef = (0, utils_js_1.ownLookup)(host.tableMeta.relations, key);
|
|
82
83
|
if (relDef && typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
|
83
84
|
const filterObj = host.normalizeRelationFilter(relDef, value);
|
|
84
85
|
if (isRelationFilterObj(filterObj)) {
|
package/dist/cjs/query/where.js
CHANGED
|
@@ -723,7 +723,7 @@ function buildScopedWhere(qi, scope, where, params) {
|
|
|
723
723
|
*/
|
|
724
724
|
function buildScopedScalarClause(qi, scope, field, value, params, clauses) {
|
|
725
725
|
const meta = scope.meta;
|
|
726
|
-
const col = meta.columnMap
|
|
726
|
+
const col = (0, utils_js_1.ownLookup)(meta.columnMap, field) ?? (0, schema_js_1.camelToSnake)(field);
|
|
727
727
|
if (!meta.allColumns.includes(col))
|
|
728
728
|
throw scope.unknownColumn(field);
|
|
729
729
|
const qCol = `${scope.qualifier}${qi.q(col)}`;
|
|
@@ -800,7 +800,7 @@ function collectScopedScalarParams(qi, scope, field, value, params) {
|
|
|
800
800
|
if (value === null)
|
|
801
801
|
return;
|
|
802
802
|
const meta = scope.meta;
|
|
803
|
-
const col = meta.columnMap
|
|
803
|
+
const col = (0, utils_js_1.ownLookup)(meta.columnMap, field) ?? (0, schema_js_1.camelToSnake)(field);
|
|
804
804
|
if (typeof value === 'object' && !Array.isArray(value) && (0, filters_js_1.isJsonFilter)(value)) {
|
|
805
805
|
const colType = pgTypeForColumn(qi, meta, col);
|
|
806
806
|
if (isJsonColumnType(qi, colType)) {
|
|
@@ -1071,7 +1071,7 @@ function resolveColumnRef(_qi, ref, ctx, mode) {
|
|
|
1071
1071
|
`Case-insensitive column-to-column comparison is not supported: use client.sql\`...\` ` +
|
|
1072
1072
|
`for lower(a) = lower(b).`);
|
|
1073
1073
|
}
|
|
1074
|
-
const col = ctx.meta.columnMap
|
|
1074
|
+
const col = (0, utils_js_1.ownLookup)(ctx.meta.columnMap, ref.col) ?? (0, schema_js_1.camelToSnake)(ref.col);
|
|
1075
1075
|
if (!ctx.meta.allColumns.includes(col)) {
|
|
1076
1076
|
throw new errors_js_1.ValidationError(`[turbine] Unknown field "${ref.col}" referenced by { col } in where on table "${ctx.table}". ` +
|
|
1077
1077
|
`Known fields: ${Object.keys(ctx.meta.columnMap).join(', ') || '(none)'}.`);
|