@atscript/db 0.1.84 → 0.1.86

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.
@@ -526,7 +526,7 @@ interface TMetadataOverrides {
526
526
  *
527
527
  * Typically provided by the driver/registry (e.g. `DbSpace.getTable`).
528
528
  */
529
- type TTableResolver = (type: TAtscriptAnnotatedType) => Pick<AtscriptDbTableLike, "findMany" | "loadRelations" | "primaryKeys" | "preferredId" | "relations" | "foreignKeys"> | undefined;
529
+ type TTableResolver = (type: TAtscriptAnnotatedType) => Pick<AtscriptDbTableLike, "findMany" | "loadRelations" | "primaryKeys" | "preferredId" | "relations" | "foreignKeys" | "isValidFieldPath"> | undefined;
530
530
  /** Minimal table interface used by the table resolver. Avoids circular dependency with AtscriptDbTable. */
531
531
  interface AtscriptDbTableLike {
532
532
  findMany(query: unknown): Promise<Array<Record<string, unknown>>>;
@@ -536,6 +536,7 @@ interface AtscriptDbTableLike {
536
536
  relations: ReadonlyMap<string, TDbRelation>;
537
537
  foreignKeys: ReadonlyMap<string, TDbForeignKey>;
538
538
  getMetadata(): TableMetadata;
539
+ isValidFieldPath(path: string, visited?: Set<string>): boolean;
539
540
  }
540
541
  /** Minimal writable table interface for nested creation/update. */
541
542
  interface AtscriptDbWritable {
@@ -1330,6 +1331,17 @@ declare class AtscriptDbReadable<T extends TAtscriptAnnotatedType = TAtscriptAnn
1330
1331
  * Pre-computed field metadata for adapter use.
1331
1332
  */
1332
1333
  get fieldDescriptors(): readonly TDbFieldMeta[];
1334
+ /**
1335
+ * Resolves whether `path` references a real field — directly via `flatMap`
1336
+ * or transitively through a nav relation by recursing into the target
1337
+ * table. Defense-in-depth for query-path validation: `flattenAnnotatedType`
1338
+ * still truncates real self-referential cycles, so paths like
1339
+ * `parent.parent.name` on a self-ref schema would miss `flatMap.has` but
1340
+ * remain valid field references on the target.
1341
+ *
1342
+ * Cycle-safe via a visited set keyed on `<tableName>:<navField>`.
1343
+ */
1344
+ isValidFieldPath(path: string, _visited?: Set<string>): boolean;
1333
1345
  /**
1334
1346
  * Creates a new validator with custom options.
1335
1347
  */
@@ -526,7 +526,7 @@ interface TMetadataOverrides {
526
526
  *
527
527
  * Typically provided by the driver/registry (e.g. `DbSpace.getTable`).
528
528
  */
529
- type TTableResolver = (type: TAtscriptAnnotatedType) => Pick<AtscriptDbTableLike, "findMany" | "loadRelations" | "primaryKeys" | "preferredId" | "relations" | "foreignKeys"> | undefined;
529
+ type TTableResolver = (type: TAtscriptAnnotatedType) => Pick<AtscriptDbTableLike, "findMany" | "loadRelations" | "primaryKeys" | "preferredId" | "relations" | "foreignKeys" | "isValidFieldPath"> | undefined;
530
530
  /** Minimal table interface used by the table resolver. Avoids circular dependency with AtscriptDbTable. */
531
531
  interface AtscriptDbTableLike {
532
532
  findMany(query: unknown): Promise<Array<Record<string, unknown>>>;
@@ -536,6 +536,7 @@ interface AtscriptDbTableLike {
536
536
  relations: ReadonlyMap<string, TDbRelation>;
537
537
  foreignKeys: ReadonlyMap<string, TDbForeignKey>;
538
538
  getMetadata(): TableMetadata;
539
+ isValidFieldPath(path: string, visited?: Set<string>): boolean;
539
540
  }
540
541
  /** Minimal writable table interface for nested creation/update. */
541
542
  interface AtscriptDbWritable {
@@ -1330,6 +1331,17 @@ declare class AtscriptDbReadable<T extends TAtscriptAnnotatedType = TAtscriptAnn
1330
1331
  * Pre-computed field metadata for adapter use.
1331
1332
  */
1332
1333
  get fieldDescriptors(): readonly TDbFieldMeta[];
1334
+ /**
1335
+ * Resolves whether `path` references a real field — directly via `flatMap`
1336
+ * or transitively through a nav relation by recursing into the target
1337
+ * table. Defense-in-depth for query-path validation: `flattenAnnotatedType`
1338
+ * still truncates real self-referential cycles, so paths like
1339
+ * `parent.parent.name` on a self-ref schema would miss `flatMap.has` but
1340
+ * remain valid field references on the target.
1341
+ *
1342
+ * Cycle-safe via a visited set keyed on `<tableName>:<navField>`.
1343
+ */
1344
+ isValidFieldPath(path: string, _visited?: Set<string>): boolean;
1333
1345
  /**
1334
1346
  * Creates a new validator with custom options.
1335
1347
  */
@@ -1,6 +1,6 @@
1
- import { B as TDbInsertResult, J as TFkLookupResolver, N as TDbDeleteResult, W as TDbUpdateResult, dt as TableMetadata, it as TTableResolver, o as BaseDbAdapter, ot as TWriteTableResolver, pt as TGenericLogger, t as AtscriptDbReadable, x as TCascadeResolver, z as TDbInsertManyResult } from "./db-readable-BUUVDkLf.mjs";
2
- import { AtscriptQueryComparison, AtscriptQueryFieldRef, AtscriptQueryFieldRef as AtscriptQueryFieldRef$1, AtscriptQueryNode, AtscriptQueryNode as AtscriptQueryNode$1, AtscriptRef, FlatOf, NavPropsOf, OwnPropsOf, PrimaryKeyOf, TAtscriptAnnotatedType, TAtscriptDataType, Validator } from "@atscript/typescript/utils";
1
+ import { B as TDbInsertResult, J as TFkLookupResolver, N as TDbDeleteResult, W as TDbUpdateResult, dt as TableMetadata, it as TTableResolver, o as BaseDbAdapter, ot as TWriteTableResolver, pt as TGenericLogger, t as AtscriptDbReadable, x as TCascadeResolver, z as TDbInsertManyResult } from "./db-readable-BtuzT9hI.cjs";
3
2
  import { FilterExpr } from "@uniqu/core";
3
+ import { AtscriptQueryComparison, AtscriptQueryFieldRef, AtscriptQueryFieldRef as AtscriptQueryFieldRef$1, AtscriptQueryNode, AtscriptQueryNode as AtscriptQueryNode$1, AtscriptRef, FlatOf, NavPropsOf, OwnPropsOf, PrimaryKeyOf, TAtscriptAnnotatedType, TAtscriptDataType, Validator } from "@atscript/typescript/utils";
4
4
 
5
5
  //#region src/strategies/integrity.d.ts
6
6
  /**
@@ -1,6 +1,6 @@
1
- import { B as TDbInsertResult, J as TFkLookupResolver, N as TDbDeleteResult, W as TDbUpdateResult, dt as TableMetadata, it as TTableResolver, o as BaseDbAdapter, ot as TWriteTableResolver, pt as TGenericLogger, t as AtscriptDbReadable, x as TCascadeResolver, z as TDbInsertManyResult } from "./db-readable-DgIxrEVQ.cjs";
2
- import { FilterExpr } from "@uniqu/core";
1
+ import { B as TDbInsertResult, J as TFkLookupResolver, N as TDbDeleteResult, W as TDbUpdateResult, dt as TableMetadata, it as TTableResolver, o as BaseDbAdapter, ot as TWriteTableResolver, pt as TGenericLogger, t as AtscriptDbReadable, x as TCascadeResolver, z as TDbInsertManyResult } from "./db-readable-DSFedi60.mjs";
3
2
  import { AtscriptQueryComparison, AtscriptQueryFieldRef, AtscriptQueryFieldRef as AtscriptQueryFieldRef$1, AtscriptQueryNode, AtscriptQueryNode as AtscriptQueryNode$1, AtscriptRef, FlatOf, NavPropsOf, OwnPropsOf, PrimaryKeyOf, TAtscriptAnnotatedType, TAtscriptDataType, Validator } from "@atscript/typescript/utils";
3
+ import { FilterExpr } from "@uniqu/core";
4
4
 
5
5
  //#region src/strategies/integrity.d.ts
6
6
  /**
@@ -1361,6 +1361,35 @@ var AtscriptDbReadable = class {
1361
1361
  return this._meta.fieldDescriptors;
1362
1362
  }
1363
1363
  /**
1364
+ * Resolves whether `path` references a real field — directly via `flatMap`
1365
+ * or transitively through a nav relation by recursing into the target
1366
+ * table. Defense-in-depth for query-path validation: `flattenAnnotatedType`
1367
+ * still truncates real self-referential cycles, so paths like
1368
+ * `parent.parent.name` on a self-ref schema would miss `flatMap.has` but
1369
+ * remain valid field references on the target.
1370
+ *
1371
+ * Cycle-safe via a visited set keyed on `<tableName>:<navField>`.
1372
+ */
1373
+ isValidFieldPath(path, _visited) {
1374
+ if (this.flatMap.has(path)) return true;
1375
+ const dotIdx = path.indexOf(".");
1376
+ if (dotIdx === -1) return false;
1377
+ const head = path.slice(0, dotIdx);
1378
+ const tail = path.slice(dotIdx + 1);
1379
+ if (!this.navFields.has(head)) return false;
1380
+ const relation = this._meta.relations.get(head);
1381
+ if (!relation) return false;
1382
+ const targetType = relation.targetType();
1383
+ if (!targetType || !this._tableResolver) return false;
1384
+ const targetTable = this._tableResolver(targetType);
1385
+ if (!targetTable || typeof targetTable.isValidFieldPath !== "function") return false;
1386
+ const visited = _visited ?? /* @__PURE__ */ new Set();
1387
+ const cycleKey = `${this.tableName}:${head}`;
1388
+ if (visited.has(cycleKey)) return false;
1389
+ visited.add(cycleKey);
1390
+ return targetTable.isValidFieldPath(tail, visited);
1391
+ }
1392
+ /**
1364
1393
  * Creates a new validator with custom options.
1365
1394
  */
1366
1395
  createValidator(opts) {
@@ -1676,7 +1705,7 @@ var AtscriptDbReadable = class {
1676
1705
  * Public entry point for relation loading. Used by adapters for nested $with delegation.
1677
1706
  */
1678
1707
  async loadRelations(rows, withRelations) {
1679
- const { loadRelationsImpl } = await Promise.resolve().then(() => require("./relation-loader-CRC5LcqM.cjs")).then((n) => n.relation_loader_exports);
1708
+ const { loadRelationsImpl } = await Promise.resolve().then(() => require("./relation-loader-B8CmCJOs.cjs")).then((n) => n.relation_loader_exports);
1680
1709
  return loadRelationsImpl(rows, withRelations, this);
1681
1710
  }
1682
1711
  /**
@@ -1361,6 +1361,35 @@ var AtscriptDbReadable = class {
1361
1361
  return this._meta.fieldDescriptors;
1362
1362
  }
1363
1363
  /**
1364
+ * Resolves whether `path` references a real field — directly via `flatMap`
1365
+ * or transitively through a nav relation by recursing into the target
1366
+ * table. Defense-in-depth for query-path validation: `flattenAnnotatedType`
1367
+ * still truncates real self-referential cycles, so paths like
1368
+ * `parent.parent.name` on a self-ref schema would miss `flatMap.has` but
1369
+ * remain valid field references on the target.
1370
+ *
1371
+ * Cycle-safe via a visited set keyed on `<tableName>:<navField>`.
1372
+ */
1373
+ isValidFieldPath(path, _visited) {
1374
+ if (this.flatMap.has(path)) return true;
1375
+ const dotIdx = path.indexOf(".");
1376
+ if (dotIdx === -1) return false;
1377
+ const head = path.slice(0, dotIdx);
1378
+ const tail = path.slice(dotIdx + 1);
1379
+ if (!this.navFields.has(head)) return false;
1380
+ const relation = this._meta.relations.get(head);
1381
+ if (!relation) return false;
1382
+ const targetType = relation.targetType();
1383
+ if (!targetType || !this._tableResolver) return false;
1384
+ const targetTable = this._tableResolver(targetType);
1385
+ if (!targetTable || typeof targetTable.isValidFieldPath !== "function") return false;
1386
+ const visited = _visited ?? /* @__PURE__ */ new Set();
1387
+ const cycleKey = `${this.tableName}:${head}`;
1388
+ if (visited.has(cycleKey)) return false;
1389
+ visited.add(cycleKey);
1390
+ return targetTable.isValidFieldPath(tail, visited);
1391
+ }
1392
+ /**
1364
1393
  * Creates a new validator with custom options.
1365
1394
  */
1366
1395
  createValidator(opts) {
@@ -1676,7 +1705,7 @@ var AtscriptDbReadable = class {
1676
1705
  * Public entry point for relation loading. Used by adapters for nested $with delegation.
1677
1706
  */
1678
1707
  async loadRelations(rows, withRelations) {
1679
- const { loadRelationsImpl } = await import("./relation-loader-BEOTXNcq.mjs").then((n) => n.n);
1708
+ const { loadRelationsImpl } = await import("./relation-loader-BH3AkSnY.mjs").then((n) => n.n);
1680
1709
  return loadRelationsImpl(rows, withRelations, this);
1681
1710
  }
1682
1711
  /**
package/dist/index.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_db_error = require("./db-error-CrtPzaZ-.cjs");
3
- const require_db_view = require("./db-view-da5OTSRH.cjs");
3
+ const require_db_view = require("./db-view-BAUO_elV.cjs");
4
4
  const require_ops = require("./ops.cjs");
5
5
  const require_validator = require("./validator-DrmUaZA3.cjs");
6
6
  let _uniqu_core = require("@uniqu/core");
package/dist/index.d.cts CHANGED
@@ -1,6 +1,6 @@
1
- import { $ as TMetadataOverrides, A as TDbCollation, B as TDbInsertResult, C as TColumnDiff, D as TDbActionIntent, E as TDbActionInfo, F as TDbForeignKey, G as TExistingColumn, H as TDbRelation, I as TDbIndex, J as TFkLookupResolver, K as TExistingTableOption, L as TDbIndexField, M as TDbDefaultValue, N as TDbDeleteResult, O as TDbActionLevel, P as TDbFieldMeta, Q as TMetaResponse, R as TDbIndexType, S as TCascadeTarget, T as TCrudPermissions, U as TDbStorageType, V as TDbReferentialAction, W as TDbUpdateResult, X as TIdDescriptor, Y as TFkLookupTarget, Z as TIdentification, _ as FlatOf, a as FieldMappingStrategy, at as TValueFormatterPair, b as PrimaryKeyOf, c as AggregateExpr, ct as Uniquery, d as AggregateResult, dt as TableMetadata, et as TRelationInfo, f as AtscriptDbWritable, ft as NoopLogger, g as FilterExpr, h as FieldOpsFor, i as DocumentFieldMapper, it as TTableResolver, j as TDbDefaultFn, k as TDbActionProcessor, l as AggregateFn, lt as UniqueryControls, m as DbQuery, mt as UniquSelect, n as DbResponse, nt as TSyncColumnResult, o as BaseDbAdapter, ot as TWriteTableResolver, p as DbControls, pt as TGenericLogger, q as TFieldMeta, r as resolveDesignType, rt as TTableOptionDiff, s as AggregateControls, st as TypedWithRelation, t as AtscriptDbReadable, tt as TSearchIndexInfo, u as AggregateQuery, ut as WithRelation, v as NavPropsOf, w as TCrudOp, x as TCascadeResolver, y as OwnPropsOf, z as TDbInsertManyResult } from "./db-readable-DgIxrEVQ.cjs";
1
+ import { $ as TMetadataOverrides, A as TDbCollation, B as TDbInsertResult, C as TColumnDiff, D as TDbActionIntent, E as TDbActionInfo, F as TDbForeignKey, G as TExistingColumn, H as TDbRelation, I as TDbIndex, J as TFkLookupResolver, K as TExistingTableOption, L as TDbIndexField, M as TDbDefaultValue, N as TDbDeleteResult, O as TDbActionLevel, P as TDbFieldMeta, Q as TMetaResponse, R as TDbIndexType, S as TCascadeTarget, T as TCrudPermissions, U as TDbStorageType, V as TDbReferentialAction, W as TDbUpdateResult, X as TIdDescriptor, Y as TFkLookupTarget, Z as TIdentification, _ as FlatOf, a as FieldMappingStrategy, at as TValueFormatterPair, b as PrimaryKeyOf, c as AggregateExpr, ct as Uniquery, d as AggregateResult, dt as TableMetadata, et as TRelationInfo, f as AtscriptDbWritable, ft as NoopLogger, g as FilterExpr, h as FieldOpsFor, i as DocumentFieldMapper, it as TTableResolver, j as TDbDefaultFn, k as TDbActionProcessor, l as AggregateFn, lt as UniqueryControls, m as DbQuery, mt as UniquSelect, n as DbResponse, nt as TSyncColumnResult, o as BaseDbAdapter, ot as TWriteTableResolver, p as DbControls, pt as TGenericLogger, q as TFieldMeta, r as resolveDesignType, rt as TTableOptionDiff, s as AggregateControls, st as TypedWithRelation, t as AtscriptDbReadable, tt as TSearchIndexInfo, u as AggregateQuery, ut as WithRelation, v as NavPropsOf, w as TCrudOp, x as TCascadeResolver, y as OwnPropsOf, z as TDbInsertManyResult } from "./db-readable-BtuzT9hI.cjs";
2
2
  import { a as $mul, c as $update, d as TDbFieldOp, f as TFieldOps, g as separateFieldOps, h as separateCas, i as $insert, l as $upsert, m as isDbFieldOp, n as $dec, o as $remove, p as getDbFieldOp, r as $inc, s as $replace, t as $cas, u as TDbCas } from "./ops-lzmfzuY9.cjs";
3
- import { a as AtscriptQueryComparison, c as AtscriptRef, d as translateQueryTree, f as AtscriptDbTable, i as TViewColumnMapping, l as TViewJoin, m as NativeIntegrity, n as TAdapterFactory, o as AtscriptQueryFieldRef, p as IntegrityStrategy, r as AtscriptDbView, s as AtscriptQueryNode, t as DbSpace, u as TViewPlan } from "./db-space-DiErv7Q1.cjs";
3
+ import { a as AtscriptQueryComparison, c as AtscriptRef, d as translateQueryTree, f as AtscriptDbTable, i as TViewColumnMapping, l as TViewJoin, m as NativeIntegrity, n as TAdapterFactory, o as AtscriptQueryFieldRef, p as IntegrityStrategy, r as AtscriptDbView, s as AtscriptQueryNode, t as DbSpace, u as TViewPlan } from "./db-space-Bf6lqUee.cjs";
4
4
  import { n as createDbValidatorPlugin, t as DbValidationContext } from "./db-validator-plugin-DRGMCEn3.cjs";
5
5
  import { c as TArrayPatch, i as buildValidationContext, l as TDbPatch, n as ValidatorMode, o as forceNavNonOptional, r as buildDbValidator, s as isNavRelation, t as ValidationContext, u as getKeyProps } from "./validator-_z_A3cKa.cjs";
6
6
  import { AggregateQuery as AggregateQuery$1, FilterExpr as FilterExpr$1, FilterVisitor, Uniquery as Uniquery$1, computeInsights, isPrimitive, walkFilter } from "@uniqu/core";
package/dist/index.d.mts CHANGED
@@ -1,6 +1,6 @@
1
- import { $ as TMetadataOverrides, A as TDbCollation, B as TDbInsertResult, C as TColumnDiff, D as TDbActionIntent, E as TDbActionInfo, F as TDbForeignKey, G as TExistingColumn, H as TDbRelation, I as TDbIndex, J as TFkLookupResolver, K as TExistingTableOption, L as TDbIndexField, M as TDbDefaultValue, N as TDbDeleteResult, O as TDbActionLevel, P as TDbFieldMeta, Q as TMetaResponse, R as TDbIndexType, S as TCascadeTarget, T as TCrudPermissions, U as TDbStorageType, V as TDbReferentialAction, W as TDbUpdateResult, X as TIdDescriptor, Y as TFkLookupTarget, Z as TIdentification, _ as FlatOf, a as FieldMappingStrategy, at as TValueFormatterPair, b as PrimaryKeyOf, c as AggregateExpr, ct as Uniquery, d as AggregateResult, dt as TableMetadata, et as TRelationInfo, f as AtscriptDbWritable, ft as NoopLogger, g as FilterExpr, h as FieldOpsFor, i as DocumentFieldMapper, it as TTableResolver, j as TDbDefaultFn, k as TDbActionProcessor, l as AggregateFn, lt as UniqueryControls, m as DbQuery, mt as UniquSelect, n as DbResponse, nt as TSyncColumnResult, o as BaseDbAdapter, ot as TWriteTableResolver, p as DbControls, pt as TGenericLogger, q as TFieldMeta, r as resolveDesignType, rt as TTableOptionDiff, s as AggregateControls, st as TypedWithRelation, t as AtscriptDbReadable, tt as TSearchIndexInfo, u as AggregateQuery, ut as WithRelation, v as NavPropsOf, w as TCrudOp, x as TCascadeResolver, y as OwnPropsOf, z as TDbInsertManyResult } from "./db-readable-BUUVDkLf.mjs";
1
+ import { $ as TMetadataOverrides, A as TDbCollation, B as TDbInsertResult, C as TColumnDiff, D as TDbActionIntent, E as TDbActionInfo, F as TDbForeignKey, G as TExistingColumn, H as TDbRelation, I as TDbIndex, J as TFkLookupResolver, K as TExistingTableOption, L as TDbIndexField, M as TDbDefaultValue, N as TDbDeleteResult, O as TDbActionLevel, P as TDbFieldMeta, Q as TMetaResponse, R as TDbIndexType, S as TCascadeTarget, T as TCrudPermissions, U as TDbStorageType, V as TDbReferentialAction, W as TDbUpdateResult, X as TIdDescriptor, Y as TFkLookupTarget, Z as TIdentification, _ as FlatOf, a as FieldMappingStrategy, at as TValueFormatterPair, b as PrimaryKeyOf, c as AggregateExpr, ct as Uniquery, d as AggregateResult, dt as TableMetadata, et as TRelationInfo, f as AtscriptDbWritable, ft as NoopLogger, g as FilterExpr, h as FieldOpsFor, i as DocumentFieldMapper, it as TTableResolver, j as TDbDefaultFn, k as TDbActionProcessor, l as AggregateFn, lt as UniqueryControls, m as DbQuery, mt as UniquSelect, n as DbResponse, nt as TSyncColumnResult, o as BaseDbAdapter, ot as TWriteTableResolver, p as DbControls, pt as TGenericLogger, q as TFieldMeta, r as resolveDesignType, rt as TTableOptionDiff, s as AggregateControls, st as TypedWithRelation, t as AtscriptDbReadable, tt as TSearchIndexInfo, u as AggregateQuery, ut as WithRelation, v as NavPropsOf, w as TCrudOp, x as TCascadeResolver, y as OwnPropsOf, z as TDbInsertManyResult } from "./db-readable-DSFedi60.mjs";
2
2
  import { a as $mul, c as $update, d as TDbFieldOp, f as TFieldOps, g as separateFieldOps, h as separateCas, i as $insert, l as $upsert, m as isDbFieldOp, n as $dec, o as $remove, p as getDbFieldOp, r as $inc, s as $replace, t as $cas, u as TDbCas } from "./ops-Blqr0ipy.mjs";
3
- import { a as AtscriptQueryComparison, c as AtscriptRef, d as translateQueryTree, f as AtscriptDbTable, i as TViewColumnMapping, l as TViewJoin, m as NativeIntegrity, n as TAdapterFactory, o as AtscriptQueryFieldRef, p as IntegrityStrategy, r as AtscriptDbView, s as AtscriptQueryNode, t as DbSpace, u as TViewPlan } from "./db-space-CZooARt_.mjs";
3
+ import { a as AtscriptQueryComparison, c as AtscriptRef, d as translateQueryTree, f as AtscriptDbTable, i as TViewColumnMapping, l as TViewJoin, m as NativeIntegrity, n as TAdapterFactory, o as AtscriptQueryFieldRef, p as IntegrityStrategy, r as AtscriptDbView, s as AtscriptQueryNode, t as DbSpace, u as TViewPlan } from "./db-space-FHPios8Z.mjs";
4
4
  import { n as createDbValidatorPlugin, t as DbValidationContext } from "./db-validator-plugin-DDvYyv5t.mjs";
5
5
  import { c as TArrayPatch, i as buildValidationContext, l as TDbPatch, n as ValidatorMode, o as forceNavNonOptional, r as buildDbValidator, s as isNavRelation, t as ValidationContext, u as getKeyProps } from "./validator-DttN2e5_.mjs";
6
6
  import { AggregateQuery as AggregateQuery$1, FilterExpr as FilterExpr$1, FilterVisitor, Uniquery as Uniquery$1, computeInsights, isPrimitive, walkFilter } from "@uniqu/core";
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { n as DbError, t as CasExhaustedError } from "./db-error-yx2jdg8B.mjs";
2
- import { a as ApplicationIntegrity, c as NativeIntegrity, d as RelationalFieldMapper, f as DocumentFieldMapper, g as NoopLogger, h as TableMetadata, i as decomposePatch, l as AtscriptDbReadable, m as UniquSelect, n as AtscriptDbTable, o as BaseDbAdapter, p as FieldMappingStrategy, r as assertNoVersionWrites, s as IntegrityStrategy, t as AtscriptDbView, u as resolveDesignType } from "./db-view-Dfw-FP5o.mjs";
2
+ import { a as ApplicationIntegrity, c as NativeIntegrity, d as RelationalFieldMapper, f as DocumentFieldMapper, g as NoopLogger, h as TableMetadata, i as decomposePatch, l as AtscriptDbReadable, m as UniquSelect, n as AtscriptDbTable, o as BaseDbAdapter, p as FieldMappingStrategy, r as assertNoVersionWrites, s as IntegrityStrategy, t as AtscriptDbView, u as resolveDesignType } from "./db-view-TUR1vy7k.mjs";
3
3
  import { $cas, $dec, $inc, $insert, $mul, $remove, $replace, $update, $upsert, getDbFieldOp, isDbFieldOp, separateCas, separateFieldOps } from "./ops.mjs";
4
4
  import { a as isNavRelation, i as forceNavNonOptional, n as buildValidationContext, o as createDbValidatorPlugin, s as getKeyProps, t as buildDbValidator } from "./validator-DTDf9yWe.mjs";
5
5
  import { computeInsights, isPrimitive, walkFilter } from "@uniqu/core";
package/dist/rel.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_relation_loader = require("./relation-loader-CRC5LcqM.cjs");
2
+ const require_relation_loader = require("./relation-loader-B8CmCJOs.cjs");
3
3
  const require_relation_helpers = require("./relation-helpers-BYvsE1tR.cjs");
4
4
  const require_nested_writer = require("./nested-writer-Chl_zySG.cjs");
5
5
  exports.batchInsertNestedFrom = require_nested_writer.batchInsertNestedFrom;
package/dist/rel.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { F as TDbForeignKey, H as TDbRelation, dt as TableMetadata, it as TTableResolver, o as BaseDbAdapter, ot as TWriteTableResolver, pt as TGenericLogger } from "./db-readable-DgIxrEVQ.cjs";
1
+ import { F as TDbForeignKey, H as TDbRelation, dt as TableMetadata, it as TTableResolver, o as BaseDbAdapter, ot as TWriteTableResolver, pt as TGenericLogger } from "./db-readable-BtuzT9hI.cjs";
2
2
  import { t as DbValidationContext } from "./db-validator-plugin-DRGMCEn3.cjs";
3
3
  import { FilterExpr, WithRelation } from "@uniqu/core";
4
4
  import { Validator } from "@atscript/typescript/utils";
package/dist/rel.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { F as TDbForeignKey, H as TDbRelation, dt as TableMetadata, it as TTableResolver, o as BaseDbAdapter, ot as TWriteTableResolver, pt as TGenericLogger } from "./db-readable-BUUVDkLf.mjs";
1
+ import { F as TDbForeignKey, H as TDbRelation, dt as TableMetadata, it as TTableResolver, o as BaseDbAdapter, ot as TWriteTableResolver, pt as TGenericLogger } from "./db-readable-DSFedi60.mjs";
2
2
  import { t as DbValidationContext } from "./db-validator-plugin-DDvYyv5t.mjs";
3
3
  import { Validator } from "@atscript/typescript/utils";
4
4
  import { FilterExpr, WithRelation } from "@uniqu/core";
package/dist/rel.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { t as loadRelationsImpl } from "./relation-loader-BEOTXNcq.mjs";
1
+ import { t as loadRelationsImpl } from "./relation-loader-BH3AkSnY.mjs";
2
2
  import { n as findRemoteFK, r as resolveRelationTargetTable, t as findFKForRelation } from "./relation-helpers-CLasawQq.mjs";
3
3
  import { a as batchPatchNestedTo, c as batchReplaceNestedTo, d as preValidateNestedFrom, f as validateBatch, i as batchPatchNestedFrom, l as batchReplaceNestedVia, n as batchInsertNestedTo, o as batchPatchNestedVia, r as batchInsertNestedVia, s as batchReplaceNestedFrom, t as batchInsertNestedFrom, u as checkDepthOverflow } from "./nested-writer-CVMRAPoF.mjs";
4
4
  export { batchInsertNestedFrom, batchInsertNestedTo, batchInsertNestedVia, batchPatchNestedFrom, batchPatchNestedTo, batchPatchNestedVia, batchReplaceNestedFrom, batchReplaceNestedTo, batchReplaceNestedVia, checkDepthOverflow, findFKForRelation, findRemoteFK, loadRelationsImpl, preValidateNestedFrom, resolveRelationTargetTable, validateBatch };
@@ -12,7 +12,10 @@ var __exportAll = (all, no_symbols) => {
12
12
  //#endregion
13
13
  const require_relation_helpers = require("./relation-helpers-BYvsE1tR.cjs");
14
14
  //#region src/rel/relation-loader.ts
15
- var relation_loader_exports = /* @__PURE__ */ __exportAll({ loadRelationsImpl: () => loadRelationsImpl });
15
+ var relation_loader_exports = /* @__PURE__ */ __exportAll({
16
+ ensureSelectIncludesFields: () => ensureSelectIncludesFields,
17
+ loadRelationsImpl: () => loadRelationsImpl
18
+ });
16
19
  /**
17
20
  * Loads related data for `$with` relations and attaches them to result rows.
18
21
  */
@@ -314,9 +317,10 @@ async function loadViaCompositeKey(rows, opts) {
314
317
  }
315
318
  const targetBaseFilter = targetOrFilters.length === 1 ? targetOrFilters[0] : { $or: targetOrFilters };
316
319
  const finalFilter = relQuery.filter ? { $and: [targetBaseFilter, relQuery.filter] } : targetBaseFilter;
320
+ const controls = ensureSelectIncludesFields(relQuery.controls, targetPKFields);
317
321
  const targetRows = await targetTable.findMany({
318
322
  filter: finalFilter,
319
- controls: relQuery.controls
323
+ controls
320
324
  });
321
325
  const targetIndex = /* @__PURE__ */ new Map();
322
326
  for (const item of targetRows) targetIndex.set(compositeKey(targetPKFields, item), item);
@@ -339,19 +343,47 @@ async function loadViaCompositeKey(rows, opts) {
339
343
  }
340
344
  }
341
345
  /**
342
- * If controls include an array-style $select, ensure the given join fields
343
- * are present so that FK matching works after the query returns.
346
+ * Ensure the given join fields survive the user's $select on a $with relation
347
+ * read. Without this, the JS-side join would key off undefined and silently
348
+ * return empty relations.
349
+ *
350
+ * - Array $select: append missing FK fields.
351
+ * - Include-mode object $select (any value is 1/true, or empty object): set
352
+ * missing FK fields to 1.
353
+ * - Exclude-mode object $select: leave alone — FK survives by default. If the
354
+ * FK is explicitly excluded, throw rather than silently override user intent.
344
355
  */
345
356
  function ensureSelectIncludesFields(controls, fields) {
346
357
  if (!controls) return controls;
347
358
  const sel = controls.$select;
348
- if (!Array.isArray(sel)) return controls;
349
- const augmented = [...sel];
350
- for (const f of fields) if (!augmented.includes(f)) augmented.push(f);
351
- return {
352
- ...controls,
353
- $select: augmented
354
- };
359
+ if (sel === void 0 || sel === null) return controls;
360
+ if (Array.isArray(sel)) {
361
+ const augmented = [...sel];
362
+ for (const f of fields) if (!augmented.includes(f)) augmented.push(f);
363
+ return {
364
+ ...controls,
365
+ $select: augmented
366
+ };
367
+ }
368
+ if (typeof sel === "object") {
369
+ const selObj = sel;
370
+ const isInclude = Object.keys(selObj).length === 0 || Object.values(selObj).some((v) => v === 1 || v === true);
371
+ const augmented = { ...selObj };
372
+ let mutated = false;
373
+ for (const f of fields) {
374
+ const v = augmented[f];
375
+ if (v === 0 || v === false) throw new Error(`Cannot exclude join column "${f}" from $select on $with relation`);
376
+ if (v === void 0 && isInclude) {
377
+ augmented[f] = 1;
378
+ mutated = true;
379
+ }
380
+ }
381
+ return mutated ? {
382
+ ...controls,
383
+ $select: augmented
384
+ } : controls;
385
+ }
386
+ return controls;
355
387
  }
356
388
  function compositeKey(fields, obj) {
357
389
  let key = "";
@@ -420,9 +452,10 @@ function queryCompositeFK(rows, opts) {
420
452
  if (orFilters.length === 0) return Promise.resolve([]);
421
453
  const baseFilter = orFilters.length === 1 ? orFilters[0] : { $or: orFilters };
422
454
  const targetFilter = relQuery.filter ? { $and: [baseFilter, relQuery.filter] } : baseFilter;
455
+ const controls = ensureSelectIncludesFields(relQuery.controls, targetFields);
423
456
  return targetTable.findMany({
424
457
  filter: targetFilter,
425
- controls: relQuery.controls
458
+ controls
426
459
  });
427
460
  }
428
461
  //#endregion
@@ -12,7 +12,10 @@ var __exportAll = (all, no_symbols) => {
12
12
  };
13
13
  //#endregion
14
14
  //#region src/rel/relation-loader.ts
15
- var relation_loader_exports = /* @__PURE__ */ __exportAll({ loadRelationsImpl: () => loadRelationsImpl });
15
+ var relation_loader_exports = /* @__PURE__ */ __exportAll({
16
+ ensureSelectIncludesFields: () => ensureSelectIncludesFields,
17
+ loadRelationsImpl: () => loadRelationsImpl
18
+ });
16
19
  /**
17
20
  * Loads related data for `$with` relations and attaches them to result rows.
18
21
  */
@@ -314,9 +317,10 @@ async function loadViaCompositeKey(rows, opts) {
314
317
  }
315
318
  const targetBaseFilter = targetOrFilters.length === 1 ? targetOrFilters[0] : { $or: targetOrFilters };
316
319
  const finalFilter = relQuery.filter ? { $and: [targetBaseFilter, relQuery.filter] } : targetBaseFilter;
320
+ const controls = ensureSelectIncludesFields(relQuery.controls, targetPKFields);
317
321
  const targetRows = await targetTable.findMany({
318
322
  filter: finalFilter,
319
- controls: relQuery.controls
323
+ controls
320
324
  });
321
325
  const targetIndex = /* @__PURE__ */ new Map();
322
326
  for (const item of targetRows) targetIndex.set(compositeKey(targetPKFields, item), item);
@@ -339,19 +343,47 @@ async function loadViaCompositeKey(rows, opts) {
339
343
  }
340
344
  }
341
345
  /**
342
- * If controls include an array-style $select, ensure the given join fields
343
- * are present so that FK matching works after the query returns.
346
+ * Ensure the given join fields survive the user's $select on a $with relation
347
+ * read. Without this, the JS-side join would key off undefined and silently
348
+ * return empty relations.
349
+ *
350
+ * - Array $select: append missing FK fields.
351
+ * - Include-mode object $select (any value is 1/true, or empty object): set
352
+ * missing FK fields to 1.
353
+ * - Exclude-mode object $select: leave alone — FK survives by default. If the
354
+ * FK is explicitly excluded, throw rather than silently override user intent.
344
355
  */
345
356
  function ensureSelectIncludesFields(controls, fields) {
346
357
  if (!controls) return controls;
347
358
  const sel = controls.$select;
348
- if (!Array.isArray(sel)) return controls;
349
- const augmented = [...sel];
350
- for (const f of fields) if (!augmented.includes(f)) augmented.push(f);
351
- return {
352
- ...controls,
353
- $select: augmented
354
- };
359
+ if (sel === void 0 || sel === null) return controls;
360
+ if (Array.isArray(sel)) {
361
+ const augmented = [...sel];
362
+ for (const f of fields) if (!augmented.includes(f)) augmented.push(f);
363
+ return {
364
+ ...controls,
365
+ $select: augmented
366
+ };
367
+ }
368
+ if (typeof sel === "object") {
369
+ const selObj = sel;
370
+ const isInclude = Object.keys(selObj).length === 0 || Object.values(selObj).some((v) => v === 1 || v === true);
371
+ const augmented = { ...selObj };
372
+ let mutated = false;
373
+ for (const f of fields) {
374
+ const v = augmented[f];
375
+ if (v === 0 || v === false) throw new Error(`Cannot exclude join column "${f}" from $select on $with relation`);
376
+ if (v === void 0 && isInclude) {
377
+ augmented[f] = 1;
378
+ mutated = true;
379
+ }
380
+ }
381
+ return mutated ? {
382
+ ...controls,
383
+ $select: augmented
384
+ } : controls;
385
+ }
386
+ return controls;
355
387
  }
356
388
  function compositeKey(fields, obj) {
357
389
  let key = "";
@@ -420,9 +452,10 @@ function queryCompositeFK(rows, opts) {
420
452
  if (orFilters.length === 0) return Promise.resolve([]);
421
453
  const baseFilter = orFilters.length === 1 ? orFilters[0] : { $or: orFilters };
422
454
  const targetFilter = relQuery.filter ? { $and: [baseFilter, relQuery.filter] } : baseFilter;
455
+ const controls = ensureSelectIncludesFields(relQuery.controls, targetFields);
423
456
  return targetTable.findMany({
424
457
  filter: targetFilter,
425
- controls: relQuery.controls
458
+ controls
426
459
  });
427
460
  }
428
461
  //#endregion
package/dist/sync.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_db_view = require("./db-view-da5OTSRH.cjs");
2
+ const require_db_view = require("./db-view-BAUO_elV.cjs");
3
3
  //#region src/schema/schema-hash.ts
4
4
  /** Extracts sorted field snapshots from a readable's field descriptors. */
5
5
  function extractFieldSnapshots(fields, typeMapper) {
package/dist/sync.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { C as TColumnDiff, F as TDbForeignKey, G as TExistingColumn, K as TExistingTableOption, M as TDbDefaultValue, P as TDbFieldMeta, U as TDbStorageType, pt as TGenericLogger, rt as TTableOptionDiff, t as AtscriptDbReadable } from "./db-readable-DgIxrEVQ.cjs";
2
- import { r as AtscriptDbView, t as DbSpace } from "./db-space-DiErv7Q1.cjs";
1
+ import { C as TColumnDiff, F as TDbForeignKey, G as TExistingColumn, K as TExistingTableOption, M as TDbDefaultValue, P as TDbFieldMeta, U as TDbStorageType, pt as TGenericLogger, rt as TTableOptionDiff, t as AtscriptDbReadable } from "./db-readable-BtuzT9hI.cjs";
2
+ import { r as AtscriptDbView, t as DbSpace } from "./db-space-Bf6lqUee.cjs";
3
3
  import { TAtscriptAnnotatedType } from "@atscript/typescript/utils";
4
4
 
5
5
  //#region src/schema/sync-entry.d.ts
package/dist/sync.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { C as TColumnDiff, F as TDbForeignKey, G as TExistingColumn, K as TExistingTableOption, M as TDbDefaultValue, P as TDbFieldMeta, U as TDbStorageType, pt as TGenericLogger, rt as TTableOptionDiff, t as AtscriptDbReadable } from "./db-readable-BUUVDkLf.mjs";
2
- import { r as AtscriptDbView, t as DbSpace } from "./db-space-CZooARt_.mjs";
1
+ import { C as TColumnDiff, F as TDbForeignKey, G as TExistingColumn, K as TExistingTableOption, M as TDbDefaultValue, P as TDbFieldMeta, U as TDbStorageType, pt as TGenericLogger, rt as TTableOptionDiff, t as AtscriptDbReadable } from "./db-readable-DSFedi60.mjs";
2
+ import { r as AtscriptDbView, t as DbSpace } from "./db-space-FHPios8Z.mjs";
3
3
  import { TAtscriptAnnotatedType } from "@atscript/typescript/utils";
4
4
 
5
5
  //#region src/schema/sync-entry.d.ts
package/dist/sync.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { g as NoopLogger } from "./db-view-Dfw-FP5o.mjs";
1
+ import { g as NoopLogger } from "./db-view-TUR1vy7k.mjs";
2
2
  //#region src/schema/schema-hash.ts
3
3
  /** Extracts sorted field snapshots from a readable's field descriptors. */
4
4
  function extractFieldSnapshots(fields, typeMapper) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atscript/db",
3
- "version": "0.1.84",
3
+ "version": "0.1.86",
4
4
  "description": "Database adapter utilities for atscript.",
5
5
  "keywords": [
6
6
  "atscript",
@@ -71,14 +71,14 @@
71
71
  "access": "public"
72
72
  },
73
73
  "devDependencies": {
74
- "@atscript/core": "^0.1.59",
75
- "@atscript/typescript": "^0.1.59",
74
+ "@atscript/core": "^0.1.60",
75
+ "@atscript/typescript": "^0.1.60",
76
76
  "@uniqu/core": "^0.1.6",
77
- "unplugin-atscript": "^0.1.59"
77
+ "unplugin-atscript": "^0.1.60"
78
78
  },
79
79
  "peerDependencies": {
80
- "@atscript/core": "^0.1.59",
81
- "@atscript/typescript": "^0.1.59",
80
+ "@atscript/core": "^0.1.60",
81
+ "@atscript/typescript": "^0.1.60",
82
82
  "@uniqu/core": "^0.1.6"
83
83
  },
84
84
  "scripts": {