@atscript/db 0.1.54 → 0.1.56

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.
Files changed (42) hide show
  1. package/dist/db-error-Cepx-RsQ.mjs +29 -0
  2. package/dist/db-error-D8tQhNgM.cjs +40 -0
  3. package/dist/{db-readable-GBjlsJXR.d.mts → db-readable-B9Irll4q.d.mts} +151 -116
  4. package/dist/{db-readable-ktHqF277.d.cts → db-readable-Cwjbk6o_.d.cts} +150 -115
  5. package/dist/{db-space-DB0MT6B3.d.cts → db-space-CAcEB09Y.d.mts} +15 -2
  6. package/dist/{db-space-DpaXOdRp.d.mts → db-space-CQg17vv7.d.cts} +15 -2
  7. package/dist/{db-validator-plugin-Cz4QoDWg.d.cts → db-validator-plugin-DDvYyv5t.d.mts} +10 -0
  8. package/dist/{db-validator-plugin-KC4aNIQq.d.mts → db-validator-plugin-DRGMCEn3.d.cts} +10 -0
  9. package/dist/{db-view-DjDKgytJ.cjs → db-view-DIGN4079.cjs} +81 -19
  10. package/dist/{db-view-B1j_IKSf.mjs → db-view-DNwX6_4x.mjs} +72 -10
  11. package/dist/index.cjs +5 -4
  12. package/dist/index.d.cts +5 -5
  13. package/dist/index.d.mts +7 -7
  14. package/dist/index.mjs +4 -3
  15. package/dist/{nested-writer-CNDyhg2L.mjs → nested-writer-CT2rLURx.mjs} +9 -16
  16. package/dist/{nested-writer-BIQ6EfaR.cjs → nested-writer-v_LPR1yJ.cjs} +14 -27
  17. package/dist/ops.d.mts +1 -1
  18. package/dist/plugin.cjs +99 -9
  19. package/dist/plugin.mjs +99 -9
  20. package/dist/rel.cjs +1 -1
  21. package/dist/rel.d.cts +2 -2
  22. package/dist/rel.d.mts +2 -2
  23. package/dist/rel.mjs +1 -1
  24. package/dist/shared.cjs +1 -1
  25. package/dist/shared.mjs +1 -1
  26. package/dist/sync.cjs +5 -5
  27. package/dist/sync.d.cts +2 -2
  28. package/dist/sync.d.mts +2 -2
  29. package/dist/sync.mjs +5 -5
  30. package/dist/{validator-D_7Fqzs4.mjs → validator-BB5h1Le3.mjs} +42 -0
  31. package/dist/{validator-0iGuvGOD.cjs → validator-BIuw_T0k.cjs} +42 -0
  32. package/dist/validator.cjs +1 -1
  33. package/dist/validator.d.cts +1 -1
  34. package/dist/validator.d.mts +3 -3
  35. package/dist/validator.mjs +1 -1
  36. package/package.json +6 -6
  37. /package/dist/{control-D1QdBO21.cjs → control-CDnwVj4q.cjs} +0 -0
  38. /package/dist/{control-DBd_ff5-.mjs → control-ExEKWYBE.mjs} +0 -0
  39. /package/dist/{ops-DcHDxrjX.d.mts → ops-C61kelof.d.mts} +0 -0
  40. /package/dist/{validation-utils-BiG3pLP0.cjs → validation-utils-B9WJv9aH.cjs} +0 -0
  41. /package/dist/{validation-utils-aNrgK-cj.mjs → validation-utils-Bh7RVrVl.mjs} +0 -0
  42. /package/dist/{validator-BeXlQISk.d.mts → validator-DttN2e5_.d.mts} +0 -0
@@ -1,8 +1,9 @@
1
- import { a as batchPatchNestedTo, c as batchReplaceNestedTo, d as preValidateNestedFrom, f as validateBatch, h as DbError, i as batchPatchNestedFrom, l as batchReplaceNestedVia, m as remapDeleteFkViolation, n as batchInsertNestedTo, o as batchPatchNestedVia, p as enrichFkViolation, r as batchInsertNestedVia, s as batchReplaceNestedFrom, t as batchInsertNestedFrom, u as checkDepthOverflow } from "./nested-writer-CNDyhg2L.mjs";
1
+ import { t as DbError } from "./db-error-Cepx-RsQ.mjs";
2
2
  import { resolveAlias } from "./agg.mjs";
3
3
  import { n as findRemoteFK, t as findFKForRelation } from "./relation-helpers-CLasawQq.mjs";
4
4
  import { separateFieldOps } from "./ops.mjs";
5
- import { i as forceNavNonOptional, r as dbPlugin, s as getKeyProps, t as buildDbValidator } from "./validator-D_7Fqzs4.mjs";
5
+ import { i as forceNavNonOptional, r as dbPlugin, s as getKeyProps, t as buildDbValidator } from "./validator-BB5h1Le3.mjs";
6
+ import { a as batchPatchNestedTo, c as batchReplaceNestedTo, d as preValidateNestedFrom, f as validateBatch, i as batchPatchNestedFrom, l as batchReplaceNestedVia, m as remapDeleteFkViolation, n as batchInsertNestedTo, o as batchPatchNestedVia, p as enrichFkViolation, r as batchInsertNestedVia, s as batchReplaceNestedFrom, t as batchInsertNestedFrom, u as checkDepthOverflow } from "./nested-writer-CT2rLURx.mjs";
6
7
  import { flattenAnnotatedType, isAnnotatedType } from "@atscript/typescript/utils";
7
8
  import { AsyncLocalStorage } from "node:async_hooks";
8
9
  //#region src/logger.ts
@@ -1096,6 +1097,15 @@ var AtscriptDbReadable = class {
1096
1097
  _ensureBuilt() {
1097
1098
  if (!this._meta.isBuilt) this._meta.build(this.type, this.adapter, this.logger);
1098
1099
  }
1100
+ /**
1101
+ * Built table metadata. Triggers a lazy build on first access — safe to call
1102
+ * from peer tables that need this one's relations / nav fields before any
1103
+ * operation has run against it directly.
1104
+ */
1105
+ getMetadata() {
1106
+ this._ensureBuilt();
1107
+ return this._meta;
1108
+ }
1099
1109
  _ensureSearchable() {
1100
1110
  if (!this.adapter.isSearchable()) throw new DbError("INVALID_QUERY", [{
1101
1111
  path: "$search",
@@ -2358,6 +2368,7 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
2358
2368
  _fkLookupResolver;
2359
2369
  _integrity;
2360
2370
  validators = /* @__PURE__ */ new Map();
2371
+ _fromDepthMap;
2361
2372
  constructor(_type, adapter, logger, _tableResolver, _writeTableResolver) {
2362
2373
  super(_type, adapter, logger, _tableResolver);
2363
2374
  if (_writeTableResolver) this._writeTableResolver = _writeTableResolver;
@@ -2411,16 +2422,20 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
2411
2422
  */
2412
2423
  async insertMany(payloads, opts) {
2413
2424
  this._ensureBuilt();
2414
- const maxDepth = opts?.maxDepth ?? 3;
2415
- const depth = opts?._depth ?? 0;
2425
+ const { _depth, maxDepth: userMax } = opts ?? {};
2426
+ const maxDepth = userMax ?? 3;
2427
+ const depth = _depth ?? 0;
2416
2428
  const canNest = depth < maxDepth && this._writeTableResolver && this._meta.navFields.size > 0;
2417
2429
  if (!canNest && this._meta.navFields.size > 0) checkDepthOverflow(payloads, maxDepth, this._meta);
2418
2430
  return enrichFkViolation(this._meta, () => this.adapter.withTransaction(async () => {
2419
2431
  const items = payloads.map((p) => this._applyDefaults({ ...p }));
2420
- validateBatch(this.getValidator("insert"), items, {
2432
+ const validator = this.getValidator("insert");
2433
+ const ctx = {
2421
2434
  mode: "insert",
2422
2435
  navFields: this._meta.navFields
2423
- });
2436
+ };
2437
+ this._applyDepthCtx(ctx, depth);
2438
+ validateBatch(validator, items, ctx);
2424
2439
  const host = this;
2425
2440
  if (canNest) await batchInsertNestedTo(host, items, maxDepth, depth);
2426
2441
  const prepared = [];
@@ -2460,10 +2475,13 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
2460
2475
  return enrichFkViolation(this._meta, () => this.adapter.withTransaction(async () => {
2461
2476
  const items = payloads.map((p) => this._applyDefaults({ ...p }));
2462
2477
  const originals = canNest ? payloads.map((p) => ({ ...p })) : [];
2463
- validateBatch(this.getValidator("bulkReplace"), items, {
2478
+ const validator = this.getValidator("bulkReplace");
2479
+ const ctx = {
2464
2480
  mode: "replace",
2465
2481
  navFields: this._meta.navFields
2466
- });
2482
+ };
2483
+ this._applyDepthCtx(ctx, depth);
2484
+ validateBatch(validator, items, ctx);
2467
2485
  const host = this;
2468
2486
  if (canNest) await batchReplaceNestedTo(host, items, maxDepth, depth);
2469
2487
  await this._integrity.validateForeignKeys(items, this._meta, this._fkLookupResolver, this._writeTableResolver);
@@ -2507,11 +2525,14 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
2507
2525
  const canNest = depth < maxDepth && this._writeTableResolver && this._meta.navFields.size > 0;
2508
2526
  if (!canNest && this._meta.navFields.size > 0) checkDepthOverflow(payloads, maxDepth, this._meta);
2509
2527
  return enrichFkViolation(this._meta, () => this.adapter.withTransaction(async () => {
2510
- validateBatch(this.getValidator("bulkUpdate"), payloads, {
2528
+ const validator = this.getValidator("bulkUpdate");
2529
+ const ctx = {
2511
2530
  mode: "patch",
2512
2531
  flatMap: this.flatMap,
2513
2532
  navFields: this._meta.navFields
2514
- });
2533
+ };
2534
+ this._applyDepthCtx(ctx, depth);
2535
+ validateBatch(validator, payloads, ctx);
2515
2536
  const originals = canNest ? payloads.map((p) => ({ ...p })) : [];
2516
2537
  const host = this;
2517
2538
  if (canNest) await batchPatchNestedTo(host, payloads, maxDepth, depth);
@@ -2687,6 +2708,47 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
2687
2708
  return fieldType ? this.adapter.prepareId(value, fieldType) : value;
2688
2709
  }
2689
2710
  /**
2711
+ * Lazy — builds a `normalized-path → from-depth` map from `this._meta.flatMap`
2712
+ * on first use. Only paths reachable through an unbroken chain of `db.rel.from`
2713
+ * nav fields from the root are included (chains crossing `to`/`via` are excluded).
2714
+ */
2715
+ _getFromDepthMap() {
2716
+ if (!this._fromDepthMap) {
2717
+ const out = /* @__PURE__ */ new Map();
2718
+ for (const [path, def] of this._meta.flatMap) {
2719
+ if (!def.metadata?.has("db.rel.from")) continue;
2720
+ const segments = path.split(".");
2721
+ let prefix = "";
2722
+ let depth = 0;
2723
+ let valid = true;
2724
+ for (let i = 0; i < segments.length; i++) {
2725
+ prefix = prefix ? `${prefix}.${segments[i]}` : segments[i];
2726
+ const pmd = this._meta.flatMap.get(prefix)?.metadata;
2727
+ if (pmd?.has("db.rel.to") || pmd?.has("db.rel.via")) {
2728
+ valid = false;
2729
+ break;
2730
+ }
2731
+ if (pmd?.has("db.rel.from")) depth++;
2732
+ }
2733
+ if (valid) out.set(path, depth);
2734
+ }
2735
+ this._fromDepthMap = out;
2736
+ }
2737
+ return this._fromDepthMap;
2738
+ }
2739
+ /**
2740
+ * Populate the depth-limit bundle on a `DbValidationContext`. Only the root
2741
+ * write call (`depth === 0`) enforces — nested re-entries leave `depthCheck`
2742
+ * unset so the full tree is validated once at the root.
2743
+ */
2744
+ _applyDepthCtx(ctx, depth) {
2745
+ if (depth !== 0 || this._meta.navFields.size === 0) return;
2746
+ ctx.depthCheck = {
2747
+ limit: this.type.metadata.get("db.depth.limit") ?? 0,
2748
+ fromDepthMap: this._getFromDepthMap()
2749
+ };
2750
+ }
2751
+ /**
2690
2752
  * Pre-validate items (type validation + FK constraints) without inserting them.
2691
2753
  * Used by parent tables to validate FROM children before the main insert,
2692
2754
  * ensuring errors are caught before the parent is committed.
package/dist/index.cjs CHANGED
@@ -1,8 +1,9 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_nested_writer = require("./nested-writer-BIQ6EfaR.cjs");
3
- const require_db_view = require("./db-view-DjDKgytJ.cjs");
2
+ const require_db_error = require("./db-error-D8tQhNgM.cjs");
3
+ const require_db_view = require("./db-view-DIGN4079.cjs");
4
4
  const require_ops = require("./ops.cjs");
5
- const require_validator = require("./validator-0iGuvGOD.cjs");
5
+ const require_validator = require("./validator-BIuw_T0k.cjs");
6
+ require("./nested-writer-v_LPR1yJ.cjs");
6
7
  let _uniqu_core = require("@uniqu/core");
7
8
  //#region src/table/db-space.ts
8
9
  /**
@@ -152,7 +153,7 @@ exports.AtscriptDbReadable = require_db_view.AtscriptDbReadable;
152
153
  exports.AtscriptDbTable = require_db_view.AtscriptDbTable;
153
154
  exports.AtscriptDbView = require_db_view.AtscriptDbView;
154
155
  exports.BaseDbAdapter = require_db_view.BaseDbAdapter;
155
- exports.DbError = require_nested_writer.DbError;
156
+ exports.DbError = require_db_error.DbError;
156
157
  exports.DbSpace = DbSpace;
157
158
  exports.DocumentFieldMapper = require_db_view.DocumentFieldMapper;
158
159
  exports.FieldMappingStrategy = require_db_view.FieldMappingStrategy;
package/dist/index.d.cts CHANGED
@@ -1,7 +1,7 @@
1
- import { $ as TValueFormatterPair, A as TDbForeignKey, B as TExistingColumn, C as TCascadeTarget, D as TDbDefaultValue, E as TDbDefaultFn, F as TDbInsertResult, G as TIdDescriptor, H as TFieldMeta, I as TDbReferentialAction, J as TRelationInfo, K as TMetaResponse, L as TDbRelation, M as TDbIndexField, N as TDbIndexType, O as TDbDeleteResult, P as TDbInsertManyResult, Q as TTableResolver, R as TDbStorageType, S as TCascadeResolver, T as TDbCollation, U as TFkLookupResolver, V as TExistingTableOption, W as TFkLookupTarget, X as TSyncColumnResult, Y as TSearchIndexInfo, Z as TTableOptionDiff, _ as DbQuery, a as FieldMappingStrategy, at as UniquSelect, b as NavPropsOf, c as NoopLogger, d as AggregateExpr, et as TWriteTableResolver, f as AggregateFn, g as DbControls, h as AtscriptDbWritable, i as DocumentFieldMapper, it as WithRelation, j as TDbIndex, k as TDbFieldMeta, l as TGenericLogger, m as AggregateResult, n as DbResponse, nt as Uniquery, o as BaseDbAdapter, p as AggregateQuery, q as TMetadataOverrides, r as resolveDesignType, rt as UniqueryControls, s as TableMetadata, t as AtscriptDbReadable, tt as TypedWithRelation, u as AggregateControls, v as FieldOpsFor, w as TColumnDiff, x as OwnPropsOf, y as FilterExpr, z as TDbUpdateResult } from "./db-readable-ktHqF277.cjs";
1
+ import { $ as TTableResolver, A as TDbFieldMeta, B as TDbUpdateResult, C as TDbActionIntent, D as TDbDefaultFn, E as TDbCollation, F as TDbInsertManyResult, G as TFkLookupTarget, H as TExistingTableOption, I as TDbInsertResult, J as TMetadataOverrides, K as TIdDescriptor, L as TDbReferentialAction, M as TDbIndex, N as TDbIndexField, O as TDbDefaultValue, P as TDbIndexType, Q as TTableOptionDiff, R as TDbRelation, S as TDbActionInfo, T as TDbActionProcessor, U as TFieldMeta, V as TExistingColumn, W as TFkLookupResolver, X as TSearchIndexInfo, Y as TRelationInfo, Z as TSyncColumnResult, _ as NavPropsOf, a as FieldMappingStrategy, at as WithRelation, b as TCascadeTarget, c as AggregateExpr, ct as TGenericLogger, d as AggregateResult, et as TValueFormatterPair, f as AtscriptDbWritable, g as FilterExpr, h as FieldOpsFor, i as DocumentFieldMapper, it as UniqueryControls, j as TDbForeignKey, k as TDbDeleteResult, l as AggregateFn, lt as UniquSelect, m as DbQuery, n as DbResponse, nt as TypedWithRelation, o as BaseDbAdapter, ot as TableMetadata, p as DbControls, q as TMetaResponse, r as resolveDesignType, rt as Uniquery, s as AggregateControls, st as NoopLogger, t as AtscriptDbReadable, tt as TWriteTableResolver, u as AggregateQuery, v as OwnPropsOf, w as TDbActionLevel, x as TColumnDiff, y as TCascadeResolver, z as TDbStorageType } from "./db-readable-Cwjbk6o_.cjs";
2
2
  import { a as $remove, c as $upsert, d as getDbFieldOp, f as isDbFieldOp, i as $mul, l as TDbFieldOp, n as $inc, o as $replace, p as separateFieldOps, r as $insert, s as $update, t as $dec, u as TFieldOps } from "./ops-DXJ4Zw0P.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-DB0MT6B3.cjs";
4
- import { n as createDbValidatorPlugin, t as DbValidationContext } from "./db-validator-plugin-Cz4QoDWg.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-CQg17vv7.cjs";
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";
7
7
 
@@ -81,7 +81,7 @@ declare class ApplicationIntegrity extends IntegrityStrategy {
81
81
  }
82
82
  //#endregion
83
83
  //#region src/db-error.d.ts
84
- type DbErrorCode = "CONFLICT" | "FK_VIOLATION" | "NOT_FOUND" | "CASCADE_CYCLE" | "INVALID_QUERY";
84
+ type DbErrorCode = "CONFLICT" | "FK_VIOLATION" | "NOT_FOUND" | "CASCADE_CYCLE" | "INVALID_QUERY" | "DEPTH_EXCEEDED";
85
85
  declare class DbError extends Error {
86
86
  readonly code: DbErrorCode;
87
87
  readonly errors: Array<{
@@ -114,4 +114,4 @@ declare class DbError extends Error {
114
114
  */
115
115
  declare function decomposePatch(payload: Record<string, unknown>, table: AtscriptDbTable): Record<string, unknown>;
116
116
  //#endregion
117
- export { $dec, $inc, $insert, $mul, $remove, $replace, $update, $upsert, type AggregateControls, type AggregateExpr, type AggregateFn, type AggregateQuery, type AggregateResult, ApplicationIntegrity, AtscriptDbReadable, AtscriptDbTable, AtscriptDbView, type AtscriptDbWritable, type AtscriptQueryComparison, type AtscriptQueryFieldRef, type AtscriptQueryNode, type AtscriptRef, BaseDbAdapter, type DbControls, DbError, type DbErrorCode, type DbQuery, type DbResponse, DbSpace, type DbValidationContext, DocumentFieldMapper, FieldMappingStrategy, type FieldOpsFor, type FilterExpr, type FilterVisitor, IntegrityStrategy, NativeIntegrity, type NavPropsOf, NoopLogger, type OwnPropsOf, RelationalFieldMapper, type TAdapterFactory, type TArrayPatch, type TCascadeResolver, type TCascadeTarget, type TColumnDiff, type TDbCollation, type TDbDefaultFn, type TDbDefaultValue, type TDbDeleteResult, type TDbFieldMeta, type TDbFieldOp, type TDbForeignKey, type TDbIndex, type TDbIndexField, type TDbIndexType, type TDbInsertManyResult, type TDbInsertResult, type TDbPatch, type TDbReferentialAction, type TDbRelation, type TDbStorageType, type TDbUpdateResult, type TExistingColumn, type TExistingTableOption, type TFieldMeta, type TFieldOps, type TFkLookupResolver, type TFkLookupTarget, type TGenericLogger, type TIdDescriptor, type TMetaResponse, type TMetadataOverrides, type TRelationInfo, type TSearchIndexInfo, type TSyncColumnResult, type TTableOptionDiff, type TTableResolver, type TValueFormatterPair, type TViewColumnMapping, type TViewJoin, type TViewPlan, type TWriteTableResolver, TableMetadata, type TypedWithRelation, UniquSelect, type Uniquery, type UniqueryControls, type ValidationContext, type ValidatorMode, type WithRelation, buildDbValidator, buildValidationContext, computeInsights, createDbValidatorPlugin, decomposePatch, forceNavNonOptional, getDbFieldOp, getKeyProps, isDbFieldOp, isNavRelation, isPrimitive, resolveDesignType, separateFieldOps, translateQueryTree, walkFilter };
117
+ export { $dec, $inc, $insert, $mul, $remove, $replace, $update, $upsert, type AggregateControls, type AggregateExpr, type AggregateFn, type AggregateQuery, type AggregateResult, ApplicationIntegrity, AtscriptDbReadable, AtscriptDbTable, AtscriptDbView, type AtscriptDbWritable, type AtscriptQueryComparison, type AtscriptQueryFieldRef, type AtscriptQueryNode, type AtscriptRef, BaseDbAdapter, type DbControls, DbError, type DbErrorCode, type DbQuery, type DbResponse, DbSpace, type DbValidationContext, DocumentFieldMapper, FieldMappingStrategy, type FieldOpsFor, type FilterExpr, type FilterVisitor, IntegrityStrategy, NativeIntegrity, type NavPropsOf, NoopLogger, type OwnPropsOf, RelationalFieldMapper, type TAdapterFactory, type TArrayPatch, type TCascadeResolver, type TCascadeTarget, type TColumnDiff, type TDbActionInfo, type TDbActionIntent, type TDbActionLevel, type TDbActionProcessor, type TDbCollation, type TDbDefaultFn, type TDbDefaultValue, type TDbDeleteResult, type TDbFieldMeta, type TDbFieldOp, type TDbForeignKey, type TDbIndex, type TDbIndexField, type TDbIndexType, type TDbInsertManyResult, type TDbInsertResult, type TDbPatch, type TDbReferentialAction, type TDbRelation, type TDbStorageType, type TDbUpdateResult, type TExistingColumn, type TExistingTableOption, type TFieldMeta, type TFieldOps, type TFkLookupResolver, type TFkLookupTarget, type TGenericLogger, type TIdDescriptor, type TMetaResponse, type TMetadataOverrides, type TRelationInfo, type TSearchIndexInfo, type TSyncColumnResult, type TTableOptionDiff, type TTableResolver, type TValueFormatterPair, type TViewColumnMapping, type TViewJoin, type TViewPlan, type TWriteTableResolver, TableMetadata, type TypedWithRelation, UniquSelect, type Uniquery, type UniqueryControls, type ValidationContext, type ValidatorMode, type WithRelation, buildDbValidator, buildValidationContext, computeInsights, createDbValidatorPlugin, decomposePatch, forceNavNonOptional, getDbFieldOp, getKeyProps, isDbFieldOp, isNavRelation, isPrimitive, resolveDesignType, separateFieldOps, translateQueryTree, walkFilter };
package/dist/index.d.mts CHANGED
@@ -1,8 +1,8 @@
1
- import { $ as TValueFormatterPair, A as TDbForeignKey, B as TExistingColumn, C as TCascadeTarget, D as TDbDefaultValue, E as TDbDefaultFn, F as TDbInsertResult, G as TIdDescriptor, H as TFieldMeta, I as TDbReferentialAction, J as TRelationInfo, K as TMetaResponse, L as TDbRelation, M as TDbIndexField, N as TDbIndexType, O as TDbDeleteResult, P as TDbInsertManyResult, Q as TTableResolver, R as TDbStorageType, S as TCascadeResolver, T as TDbCollation, U as TFkLookupResolver, V as TExistingTableOption, W as TFkLookupTarget, X as TSyncColumnResult, Y as TSearchIndexInfo, Z as TTableOptionDiff, _ as DbQuery, a as FieldMappingStrategy, at as UniquSelect, b as NavPropsOf, c as NoopLogger, d as AggregateExpr, et as TWriteTableResolver, f as AggregateFn, g as DbControls, h as AtscriptDbWritable, i as DocumentFieldMapper, it as WithRelation, j as TDbIndex, k as TDbFieldMeta, l as TGenericLogger, m as AggregateResult, n as DbResponse, nt as Uniquery, o as BaseDbAdapter, p as AggregateQuery, q as TMetadataOverrides, r as resolveDesignType, rt as UniqueryControls, s as TableMetadata, t as AtscriptDbReadable, tt as TypedWithRelation, u as AggregateControls, v as FieldOpsFor, w as TColumnDiff, x as OwnPropsOf, y as FilterExpr, z as TDbUpdateResult } from "./db-readable-GBjlsJXR.mjs";
2
- import { a as $remove, c as $upsert, d as getDbFieldOp, f as isDbFieldOp, i as $mul, l as TDbFieldOp, n as $inc, o as $replace, p as separateFieldOps, r as $insert, s as $update, t as $dec, u as TFieldOps } from "./ops-DcHDxrjX.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-DpaXOdRp.mjs";
4
- import { n as createDbValidatorPlugin, t as DbValidationContext } from "./db-validator-plugin-KC4aNIQq.mjs";
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-BeXlQISk.mjs";
1
+ import { $ as TTableResolver, A as TDbFieldMeta, B as TDbUpdateResult, C as TDbActionIntent, D as TDbDefaultFn, E as TDbCollation, F as TDbInsertManyResult, G as TFkLookupTarget, H as TExistingTableOption, I as TDbInsertResult, J as TMetadataOverrides, K as TIdDescriptor, L as TDbReferentialAction, M as TDbIndex, N as TDbIndexField, O as TDbDefaultValue, P as TDbIndexType, Q as TTableOptionDiff, R as TDbRelation, S as TDbActionInfo, T as TDbActionProcessor, U as TFieldMeta, V as TExistingColumn, W as TFkLookupResolver, X as TSearchIndexInfo, Y as TRelationInfo, Z as TSyncColumnResult, _ as NavPropsOf, a as FieldMappingStrategy, at as WithRelation, b as TCascadeTarget, c as AggregateExpr, ct as TGenericLogger, d as AggregateResult, et as TValueFormatterPair, f as AtscriptDbWritable, g as FilterExpr, h as FieldOpsFor, i as DocumentFieldMapper, it as UniqueryControls, j as TDbForeignKey, k as TDbDeleteResult, l as AggregateFn, lt as UniquSelect, m as DbQuery, n as DbResponse, nt as TypedWithRelation, o as BaseDbAdapter, ot as TableMetadata, p as DbControls, q as TMetaResponse, r as resolveDesignType, rt as Uniquery, s as AggregateControls, st as NoopLogger, t as AtscriptDbReadable, tt as TWriteTableResolver, u as AggregateQuery, v as OwnPropsOf, w as TDbActionLevel, x as TColumnDiff, y as TCascadeResolver, z as TDbStorageType } from "./db-readable-B9Irll4q.mjs";
2
+ import { a as $remove, c as $upsert, d as getDbFieldOp, f as isDbFieldOp, i as $mul, l as TDbFieldOp, n as $inc, o as $replace, p as separateFieldOps, r as $insert, s as $update, t as $dec, u as TFieldOps } from "./ops-C61kelof.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-CAcEB09Y.mjs";
4
+ import { n as createDbValidatorPlugin, t as DbValidationContext } from "./db-validator-plugin-DDvYyv5t.mjs";
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";
7
7
 
8
8
  //#region src/strategies/relational-field-mapper.d.ts
@@ -81,7 +81,7 @@ declare class ApplicationIntegrity extends IntegrityStrategy {
81
81
  }
82
82
  //#endregion
83
83
  //#region src/db-error.d.ts
84
- type DbErrorCode = "CONFLICT" | "FK_VIOLATION" | "NOT_FOUND" | "CASCADE_CYCLE" | "INVALID_QUERY";
84
+ type DbErrorCode = "CONFLICT" | "FK_VIOLATION" | "NOT_FOUND" | "CASCADE_CYCLE" | "INVALID_QUERY" | "DEPTH_EXCEEDED";
85
85
  declare class DbError extends Error {
86
86
  readonly code: DbErrorCode;
87
87
  readonly errors: Array<{
@@ -114,4 +114,4 @@ declare class DbError extends Error {
114
114
  */
115
115
  declare function decomposePatch(payload: Record<string, unknown>, table: AtscriptDbTable): Record<string, unknown>;
116
116
  //#endregion
117
- export { $dec, $inc, $insert, $mul, $remove, $replace, $update, $upsert, type AggregateControls, type AggregateExpr, type AggregateFn, type AggregateQuery, type AggregateResult, ApplicationIntegrity, AtscriptDbReadable, AtscriptDbTable, AtscriptDbView, type AtscriptDbWritable, type AtscriptQueryComparison, type AtscriptQueryFieldRef, type AtscriptQueryNode, type AtscriptRef, BaseDbAdapter, type DbControls, DbError, type DbErrorCode, type DbQuery, type DbResponse, DbSpace, type DbValidationContext, DocumentFieldMapper, FieldMappingStrategy, type FieldOpsFor, type FilterExpr, type FilterVisitor, IntegrityStrategy, NativeIntegrity, type NavPropsOf, NoopLogger, type OwnPropsOf, RelationalFieldMapper, type TAdapterFactory, type TArrayPatch, type TCascadeResolver, type TCascadeTarget, type TColumnDiff, type TDbCollation, type TDbDefaultFn, type TDbDefaultValue, type TDbDeleteResult, type TDbFieldMeta, type TDbFieldOp, type TDbForeignKey, type TDbIndex, type TDbIndexField, type TDbIndexType, type TDbInsertManyResult, type TDbInsertResult, type TDbPatch, type TDbReferentialAction, type TDbRelation, type TDbStorageType, type TDbUpdateResult, type TExistingColumn, type TExistingTableOption, type TFieldMeta, type TFieldOps, type TFkLookupResolver, type TFkLookupTarget, type TGenericLogger, type TIdDescriptor, type TMetaResponse, type TMetadataOverrides, type TRelationInfo, type TSearchIndexInfo, type TSyncColumnResult, type TTableOptionDiff, type TTableResolver, type TValueFormatterPair, type TViewColumnMapping, type TViewJoin, type TViewPlan, type TWriteTableResolver, TableMetadata, type TypedWithRelation, UniquSelect, type Uniquery, type UniqueryControls, type ValidationContext, type ValidatorMode, type WithRelation, buildDbValidator, buildValidationContext, computeInsights, createDbValidatorPlugin, decomposePatch, forceNavNonOptional, getDbFieldOp, getKeyProps, isDbFieldOp, isNavRelation, isPrimitive, resolveDesignType, separateFieldOps, translateQueryTree, walkFilter };
117
+ export { $dec, $inc, $insert, $mul, $remove, $replace, $update, $upsert, type AggregateControls, type AggregateExpr, type AggregateFn, type AggregateQuery, type AggregateResult, ApplicationIntegrity, AtscriptDbReadable, AtscriptDbTable, AtscriptDbView, type AtscriptDbWritable, type AtscriptQueryComparison, type AtscriptQueryFieldRef, type AtscriptQueryNode, type AtscriptRef, BaseDbAdapter, type DbControls, DbError, type DbErrorCode, type DbQuery, type DbResponse, DbSpace, type DbValidationContext, DocumentFieldMapper, FieldMappingStrategy, type FieldOpsFor, type FilterExpr, type FilterVisitor, IntegrityStrategy, NativeIntegrity, type NavPropsOf, NoopLogger, type OwnPropsOf, RelationalFieldMapper, type TAdapterFactory, type TArrayPatch, type TCascadeResolver, type TCascadeTarget, type TColumnDiff, type TDbActionInfo, type TDbActionIntent, type TDbActionLevel, type TDbActionProcessor, type TDbCollation, type TDbDefaultFn, type TDbDefaultValue, type TDbDeleteResult, type TDbFieldMeta, type TDbFieldOp, type TDbForeignKey, type TDbIndex, type TDbIndexField, type TDbIndexType, type TDbInsertManyResult, type TDbInsertResult, type TDbPatch, type TDbReferentialAction, type TDbRelation, type TDbStorageType, type TDbUpdateResult, type TExistingColumn, type TExistingTableOption, type TFieldMeta, type TFieldOps, type TFkLookupResolver, type TFkLookupTarget, type TGenericLogger, type TIdDescriptor, type TMetaResponse, type TMetadataOverrides, type TRelationInfo, type TSearchIndexInfo, type TSyncColumnResult, type TTableOptionDiff, type TTableResolver, type TValueFormatterPair, type TViewColumnMapping, type TViewJoin, type TViewPlan, type TWriteTableResolver, TableMetadata, type TypedWithRelation, UniquSelect, type Uniquery, type UniqueryControls, type ValidationContext, type ValidatorMode, type WithRelation, buildDbValidator, buildValidationContext, computeInsights, createDbValidatorPlugin, decomposePatch, forceNavNonOptional, getDbFieldOp, getKeyProps, isDbFieldOp, isNavRelation, isPrimitive, resolveDesignType, separateFieldOps, translateQueryTree, walkFilter };
package/dist/index.mjs CHANGED
@@ -1,7 +1,8 @@
1
- import { h as DbError } from "./nested-writer-CNDyhg2L.mjs";
2
- import { a as BaseDbAdapter, c as AtscriptDbReadable, d as DocumentFieldMapper, f as FieldMappingStrategy, h as NoopLogger, i as ApplicationIntegrity, l as resolveDesignType, m as TableMetadata, n as AtscriptDbTable, o as IntegrityStrategy, p as UniquSelect, r as decomposePatch, s as NativeIntegrity, t as AtscriptDbView, u as RelationalFieldMapper } from "./db-view-B1j_IKSf.mjs";
1
+ import { t as DbError } from "./db-error-Cepx-RsQ.mjs";
2
+ import { a as BaseDbAdapter, c as AtscriptDbReadable, d as DocumentFieldMapper, f as FieldMappingStrategy, h as NoopLogger, i as ApplicationIntegrity, l as resolveDesignType, m as TableMetadata, n as AtscriptDbTable, o as IntegrityStrategy, p as UniquSelect, r as decomposePatch, s as NativeIntegrity, t as AtscriptDbView, u as RelationalFieldMapper } from "./db-view-DNwX6_4x.mjs";
3
3
  import { $dec, $inc, $insert, $mul, $remove, $replace, $update, $upsert, getDbFieldOp, isDbFieldOp, separateFieldOps } from "./ops.mjs";
4
- import { a as isNavRelation, i as forceNavNonOptional, n as buildValidationContext, o as createDbValidatorPlugin, s as getKeyProps, t as buildDbValidator } from "./validator-D_7Fqzs4.mjs";
4
+ import { a as isNavRelation, i as forceNavNonOptional, n as buildValidationContext, o as createDbValidatorPlugin, s as getKeyProps, t as buildDbValidator } from "./validator-BB5h1Le3.mjs";
5
+ import "./nested-writer-CT2rLURx.mjs";
5
6
  import { computeInsights, isPrimitive, walkFilter } from "@uniqu/core";
6
7
  //#region src/table/db-space.ts
7
8
  /**
@@ -1,16 +1,6 @@
1
+ import { n as DepthLimitExceededError, t as DbError } from "./db-error-Cepx-RsQ.mjs";
1
2
  import { r as resolveRelationTargetTable } from "./relation-helpers-CLasawQq.mjs";
2
3
  import { ValidatorError } from "@atscript/typescript/utils";
3
- //#region src/db-error.ts
4
- var DbError = class extends Error {
5
- name = "DbError";
6
- constructor(code, errors, message) {
7
- super(message ?? errors[0]?.message ?? "Database error");
8
- this.code = code;
9
- this.errors = errors;
10
- this.stack = void 0;
11
- }
12
- };
13
- //#endregion
14
4
  //#region src/table/error-utils.ts
15
5
  /**
16
6
  * Prefixes error paths with a nav field context.
@@ -89,10 +79,13 @@ function validateBatch(validator, items, ctx) {
89
79
  for (let i = 0; i < items.length; i++) try {
90
80
  validator.validate(items[i], false, ctx);
91
81
  } catch (error) {
92
- if (error instanceof ValidatorError && items.length > 1) throw new ValidatorError(error.errors.map((err) => ({
93
- ...err,
94
- path: `[${i}].${err.path}`
95
- })));
82
+ if (items.length > 1) {
83
+ if (error instanceof ValidatorError) throw new ValidatorError(error.errors.map((err) => ({
84
+ ...err,
85
+ path: `[${i}].${err.path}`
86
+ })));
87
+ if (error instanceof DepthLimitExceededError) throw new DepthLimitExceededError(`[${i}].${error.field}`, error.declared, error.actual);
88
+ }
96
89
  throw error;
97
90
  }
98
91
  }
@@ -621,4 +614,4 @@ async function viaReplace(targetTable, junctionTable, targets, parentPK, targetP
621
614
  }
622
615
  }
623
616
  //#endregion
624
- export { batchPatchNestedTo as a, batchReplaceNestedTo as c, preValidateNestedFrom as d, validateBatch as f, DbError as h, batchPatchNestedFrom as i, batchReplaceNestedVia as l, remapDeleteFkViolation as m, batchInsertNestedTo as n, batchPatchNestedVia as o, enrichFkViolation as p, batchInsertNestedVia as r, batchReplaceNestedFrom as s, batchInsertNestedFrom as t, checkDepthOverflow as u };
617
+ export { batchPatchNestedTo as a, batchReplaceNestedTo as c, preValidateNestedFrom as d, validateBatch as f, batchPatchNestedFrom as i, batchReplaceNestedVia as l, remapDeleteFkViolation as m, batchInsertNestedTo as n, batchPatchNestedVia as o, enrichFkViolation as p, batchInsertNestedVia as r, batchReplaceNestedFrom as s, batchInsertNestedFrom as t, checkDepthOverflow as u };
@@ -1,16 +1,6 @@
1
+ const require_db_error = require("./db-error-D8tQhNgM.cjs");
1
2
  const require_relation_helpers = require("./relation-helpers-BYvsE1tR.cjs");
2
3
  let _atscript_typescript_utils = require("@atscript/typescript/utils");
3
- //#region src/db-error.ts
4
- var DbError = class extends Error {
5
- name = "DbError";
6
- constructor(code, errors, message) {
7
- super(message ?? errors[0]?.message ?? "Database error");
8
- this.code = code;
9
- this.errors = errors;
10
- this.stack = void 0;
11
- }
12
- };
13
- //#endregion
14
4
  //#region src/table/error-utils.ts
15
5
  /**
16
6
  * Prefixes error paths with a nav field context.
@@ -31,7 +21,7 @@ async function wrapNestedError(navField, fn) {
31
21
  return await fn();
32
22
  } catch (error) {
33
23
  if (error instanceof _atscript_typescript_utils.ValidatorError) throw new _atscript_typescript_utils.ValidatorError(prefixErrorPaths(error.errors, navField));
34
- if (error instanceof DbError) throw new DbError(error.code, prefixErrorPaths(error.errors, navField));
24
+ if (error instanceof require_db_error.DbError) throw new require_db_error.DbError(error.code, prefixErrorPaths(error.errors, navField));
35
25
  throw error;
36
26
  }
37
27
  }
@@ -44,14 +34,14 @@ async function enrichFkViolation(meta, fn) {
44
34
  try {
45
35
  return await fn();
46
36
  } catch (error) {
47
- if (error instanceof DbError && error.code === "FK_VIOLATION" && error.errors.every((err) => !err.path)) {
37
+ if (error instanceof require_db_error.DbError && error.code === "FK_VIOLATION" && error.errors.every((err) => !err.path)) {
48
38
  const msg = error.errors[0]?.message ?? error.message;
49
39
  const errors = [];
50
40
  for (const [, fk] of meta.foreignKeys) for (const field of fk.fields) errors.push({
51
41
  path: field,
52
42
  message: msg
53
43
  });
54
- throw new DbError("FK_VIOLATION", errors.length > 0 ? errors : error.errors);
44
+ throw new require_db_error.DbError("FK_VIOLATION", errors.length > 0 ? errors : error.errors);
55
45
  }
56
46
  throw error;
57
47
  }
@@ -64,7 +54,7 @@ async function remapDeleteFkViolation(tableName, fn) {
64
54
  try {
65
55
  return await fn();
66
56
  } catch (error) {
67
- if (error instanceof DbError && error.code === "FK_VIOLATION") throw new DbError("CONFLICT", [{
57
+ if (error instanceof require_db_error.DbError && error.code === "FK_VIOLATION") throw new require_db_error.DbError("CONFLICT", [{
68
58
  path: tableName,
69
59
  message: `Cannot delete from "${tableName}": referenced by child records (RESTRICT)`
70
60
  }]);
@@ -89,10 +79,13 @@ function validateBatch(validator, items, ctx) {
89
79
  for (let i = 0; i < items.length; i++) try {
90
80
  validator.validate(items[i], false, ctx);
91
81
  } catch (error) {
92
- if (error instanceof _atscript_typescript_utils.ValidatorError && items.length > 1) throw new _atscript_typescript_utils.ValidatorError(error.errors.map((err) => ({
93
- ...err,
94
- path: `[${i}].${err.path}`
95
- })));
82
+ if (items.length > 1) {
83
+ if (error instanceof _atscript_typescript_utils.ValidatorError) throw new _atscript_typescript_utils.ValidatorError(error.errors.map((err) => ({
84
+ ...err,
85
+ path: `[${i}].${err.path}`
86
+ })));
87
+ if (error instanceof require_db_error.DepthLimitExceededError) throw new require_db_error.DepthLimitExceededError(`[${i}].${error.field}`, error.declared, error.actual);
88
+ }
96
89
  throw error;
97
90
  }
98
91
  }
@@ -321,13 +314,13 @@ async function batchPatchNestedTo(host, items, maxDepth, depth) {
321
314
  filter: pkFilter,
322
315
  controls: {}
323
316
  });
324
- if (!current) throw new DbError("NOT_FOUND", [{
317
+ if (!current) throw new require_db_error.DbError("NOT_FOUND", [{
325
318
  path: navField,
326
319
  message: `Cannot patch relation '${navField}' — source record not found`
327
320
  }]);
328
321
  fkValue = fk.localFields.length === 1 ? current[fk.localFields[0]] : void 0;
329
322
  }
330
- if (fkValue === null || fkValue === void 0) throw new DbError("FK_VIOLATION", [{
323
+ if (fkValue === null || fkValue === void 0) throw new require_db_error.DbError("FK_VIOLATION", [{
331
324
  path: fk.localFields[0],
332
325
  message: `Cannot patch relation '${navField}' — foreign key '${fk.localFields[0]}' is null`
333
326
  }]);
@@ -621,12 +614,6 @@ async function viaReplace(targetTable, junctionTable, targets, parentPK, targetP
621
614
  }
622
615
  }
623
616
  //#endregion
624
- Object.defineProperty(exports, "DbError", {
625
- enumerable: true,
626
- get: function() {
627
- return DbError;
628
- }
629
- });
630
617
  Object.defineProperty(exports, "batchInsertNestedFrom", {
631
618
  enumerable: true,
632
619
  get: function() {
package/dist/ops.d.mts CHANGED
@@ -1,2 +1,2 @@
1
- import { a as $remove, c as $upsert, d as getDbFieldOp, f as isDbFieldOp, i as $mul, l as TDbFieldOp, n as $inc, o as $replace, p as separateFieldOps, r as $insert, s as $update, t as $dec, u as TFieldOps } from "./ops-DcHDxrjX.mjs";
1
+ import { a as $remove, c as $upsert, d as getDbFieldOp, f as isDbFieldOp, i as $mul, l as TDbFieldOp, n as $inc, o as $replace, p as separateFieldOps, r as $insert, s as $update, t as $dec, u as TFieldOps } from "./ops-C61kelof.mjs";
2
2
  export { $dec, $inc, $insert, $mul, $remove, $replace, $update, $upsert, TDbFieldOp, TFieldOps, getDbFieldOp, isDbFieldOp, separateFieldOps };
package/dist/plugin.cjs CHANGED
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_validation_utils = require("./validation-utils-BiG3pLP0.cjs");
5
+ const require_validation_utils = require("./validation-utils-B9WJv9aH.cjs");
6
6
  let _atscript_core = require("@atscript/core");
7
7
  //#region src/plugin/annotations/agg.ts
8
8
  const dbAggAnnotations = { agg: {
@@ -155,7 +155,9 @@ const dbColumnAnnotations = {
155
155
  validate(token, _args, doc) {
156
156
  return require_validation_utils.validateFieldBaseType(token, doc, "@db.column.measure", ["number", "decimal"]);
157
157
  }
158
- })
158
+ }),
159
+ filterable: columnCapability("filterable", "filtering"),
160
+ sortable: columnCapability("sortable", "sorting")
159
161
  },
160
162
  default: {
161
163
  $self: new _atscript_core.AnnotationSpec({
@@ -226,6 +228,30 @@ const dbColumnAnnotations = {
226
228
  }
227
229
  })
228
230
  };
231
+ function columnCapability(capability, verb) {
232
+ const example = capability === "filterable" ? " @db.column.filterable\n email: string\n" : " @db.column.sortable\n createdAt: number.timestamp\n";
233
+ return new _atscript_core.AnnotationSpec({
234
+ description: `Marks a column as ${capability} in the readable controller's query/pages endpoints. Relevant only when the host \`@db.table\` interface opts into strict mode with \`@db.table.${capability} 'manual'\`; otherwise ${verb} is open on all columns (default-open, back-compat).
235
+
236
+ **Example:**
237
+ \`\`\`atscript
238
+ @db.table "users"
239
+ @db.table.${capability} "manual"\nexport interface User {
240
+ ` + example + "}\n```\n",
241
+ nodeType: ["prop"],
242
+ multiple: false,
243
+ validate(token, _args, _doc) {
244
+ const errors = [];
245
+ const owner = require_validation_utils.getDbTableOwner(token);
246
+ if (!owner || owner.countAnnotations("db.table") === 0) errors.push({
247
+ message: `@db.column.${capability} is only valid on fields of a @db.table interface`,
248
+ severity: 1,
249
+ range: token.range
250
+ });
251
+ return errors;
252
+ }
253
+ });
254
+ }
229
255
  //#endregion
230
256
  //#region src/plugin/annotations/index-ann.ts
231
257
  const dbIndexAnnotations = { index: {
@@ -281,7 +307,7 @@ const dbIndexAnnotations = { index: {
281
307
  //#region src/plugin/annotations/rel.ts
282
308
  const dbRelAnnotations = { rel: {
283
309
  FK: new _atscript_core.AnnotationSpec({
284
- description: "Declares a foreign key constraint on this field. The field must use a chain reference type (e.g., `User.id`) to specify the FK target.\n\n**Example:**\n```atscript\n@db.rel.FK\nauthorId: User.id\n\n// With alias (required when multiple FKs point to the same type)\n@db.rel.FK \"author\"\nauthorId: User.id\n```\n",
310
+ description: "Declares a foreign key reference on this field. The field must use a chain reference type (e.g., `User.id`) whose target is a primary key (`@meta.id`) or unique (`@db.index.unique`) field.\n\n**Dual role:**\n- On a `@db.table` interface, `@db.rel.FK` additionally drives DB-relation semantics — relation loading with `@db.rel.to` / `@db.rel.from`, junction pairing with `@db.rel.via`, etc.\n- On any other interface (value-help sources, WF forms, plain interfaces), `@db.rel.FK` acts purely as the value-help indicator: the client-side picker resolver uses it to decide which fields render a value-help picker. The target's `@db.http.path` (stamped by its readable controller) supplies the picker URL.\n\n**Example:**\n```atscript\n@db.rel.FK\nauthorId: User.id\n\n// With alias (required when multiple FKs point to the same type)\n@db.rel.FK \"author\"\nauthorId: User.id\n```\n",
285
311
  nodeType: ["prop"],
286
312
  argument: {
287
313
  optional: true,
@@ -293,12 +319,6 @@ const dbRelAnnotations = { rel: {
293
319
  const errors = [];
294
320
  const field = token.parentNode;
295
321
  const alias = args[0]?.text;
296
- const owner = require_validation_utils.getDbTableOwner(token);
297
- if (!owner || owner.countAnnotations("db.table") === 0) errors.push({
298
- message: "@db.rel.FK is only valid on fields of a @db.table interface",
299
- severity: 1,
300
- range: token.range
301
- });
302
322
  if (field.countAnnotations("db.rel.to") > 0 || field.countAnnotations("db.rel.from") > 0) errors.push({
303
323
  message: "A field cannot be both a foreign key and a navigational property",
304
324
  severity: 1,
@@ -705,6 +725,8 @@ const dbSearchAnnotations = { search: {
705
725
  //#region src/plugin/annotations/table.ts
706
726
  const dbTableAnnotations = {
707
727
  table: {
728
+ filterable: tableCapability("filterable"),
729
+ sortable: tableCapability("sortable"),
708
730
  $self: new _atscript_core.AnnotationSpec({
709
731
  description: "Marks an interface as a database-persisted entity (table in SQL, collection in MongoDB). If the name argument is omitted, the adapter derives the table name from the interface name.\n\n**Example:**\n```atscript\n@db.table \"users\"\nexport interface User { ... }\n```\n",
710
732
  nodeType: ["interface"],
@@ -771,8 +793,75 @@ const dbTableAnnotations = {
771
793
  description: "Sync method: \"drop\" (lossy) or \"recreate\" (lossless with data copy).",
772
794
  values: ["drop", "recreate"]
773
795
  }
796
+ }) },
797
+ depth: { limit: new _atscript_core.AnnotationSpec({
798
+ description: "Security guard on nested-write payloads. `N` is a non-negative integer declaring the maximum depth a client may nest `@db.rel.from` children in insert, replace, or patch payloads. Writes deeper than `N` are rejected at the server boundary with HTTP 400 before any DB access.\n\n**Default when absent:** `0` — any nested-write payload is rejected. Authors opt in explicitly to `N >= 1` when they want the server to accept deep writes. This is a security / blast-radius control, not a performance knob.\n\n**Scope:** affects only write acceptance. Has no effect on `/meta` serialization, read/query paths, or wire shape — the meta endpoint always ships FK refs as the shallow `{ id, metadata }` shape regardless of this annotation.\n\n**Example:**\n```atscript\n@db.table \"authors\"\n@db.depth.limit 2\nexport interface Author { ... }\n```\n",
799
+ nodeType: ["interface"],
800
+ multiple: false,
801
+ argument: {
802
+ name: "depth",
803
+ type: "number",
804
+ description: "Non-negative integer: maximum nesting depth accepted for nested writes."
805
+ },
806
+ validate(token, args, _doc) {
807
+ const errors = [];
808
+ const owner = token.parentNode;
809
+ if (owner.countAnnotations("db.table") === 0) errors.push({
810
+ message: "@db.depth.limit is only valid on @db.table interfaces",
811
+ severity: 1,
812
+ range: token.range
813
+ });
814
+ if (owner.countAnnotations("db.depth.limit") > 1) errors.push({
815
+ message: "Multiple @db.depth.limit annotations on the same interface",
816
+ severity: 1,
817
+ range: token.range
818
+ });
819
+ const raw = args[0]?.text;
820
+ if (raw !== void 0) {
821
+ const num = Number(raw);
822
+ if (!Number.isFinite(num) || !Number.isInteger(num) || num < 0) errors.push({
823
+ message: `@db.depth.limit depth must be a non-negative integer, got '${raw}'`,
824
+ severity: 1,
825
+ range: args[0].range
826
+ });
827
+ }
828
+ return errors;
829
+ }
774
830
  }) }
775
831
  };
832
+ function tableCapability(capability) {
833
+ const example = capability === "filterable" ? " @db.column.filterable\n email: string\n // other fields not filterable via the controller\n" : " @db.column.sortable\n createdAt: number.timestamp\n // other fields not sortable via the controller\n";
834
+ const verb = capability === "filterable" ? "filter" : "sort";
835
+ return new _atscript_core.AnnotationSpec({
836
+ description: `Controls ${verb}-gating on the readable controller's \`/query\` and \`/pages\` endpoints.\n\n- **\`'auto'\`** (default when the annotation is absent) — every column is ${capability}.\n- **\`'manual'\`** — only fields annotated \`@db.column.${capability}\` are ${capability}; all others are rejected with HTTP 400.
837
+
838
+ Writing the annotation explicitly as \`@db.table.${capability} 'auto'\` has the same runtime effect as omitting it; use it to document intent.
839
+
840
+ **Example:**
841
+ \`\`\`atscript
842
+ @db.table "users"
843
+ @db.table.${capability} "manual"\nexport interface User {
844
+ ` + example + "}\n```\n",
845
+ nodeType: ["interface"],
846
+ multiple: false,
847
+ argument: {
848
+ optional: true,
849
+ name: "mode",
850
+ type: "string",
851
+ description: `${verb[0].toUpperCase()}${verb.slice(1)}-gating mode: 'auto' (default) or 'manual'.`,
852
+ values: ["auto", "manual"]
853
+ },
854
+ validate(token, _args, _doc) {
855
+ const errors = [];
856
+ if (token.parentNode.countAnnotations("db.table") === 0) errors.push({
857
+ message: `@db.table.${capability} requires @db.table on the same interface`,
858
+ severity: 1,
859
+ range: token.range
860
+ });
861
+ return errors;
862
+ }
863
+ });
864
+ }
776
865
  //#endregion
777
866
  //#region src/plugin/annotations/view.ts
778
867
  const dbViewAnnotations = { view: {
@@ -964,6 +1053,7 @@ const dbPlugin = () => ({
964
1053
  ignore: dbColumnAnnotations.ignore,
965
1054
  http: dbTableAnnotations.http,
966
1055
  sync: dbTableAnnotations.sync,
1056
+ depth: dbTableAnnotations.depth,
967
1057
  rel: dbRelAnnotations.rel,
968
1058
  view: dbViewAnnotations.view,
969
1059
  agg: dbAggAnnotations.agg,