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.
@@ -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[key])
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) && !(key in qi.tableMeta.columnMap)) {
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[key] ?? (0, schema_js_1.camelToSnake)(key);
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[col] ?? (0, schema_js_1.camelToSnake)(col);
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[k] ?? (0, schema_js_1.camelToSnake)(k));
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[k] ?? (0, schema_js_1.camelToSnake)(k)));
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) {
@@ -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[key];
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)) {
@@ -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[field] ?? (0, schema_js_1.camelToSnake)(field);
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[field] ?? (0, schema_js_1.camelToSnake)(field);
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[ref.col] ?? (0, schema_js_1.camelToSnake)(ref.col);
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)'}.`);