@atscript/db 0.1.63 → 0.1.64

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.
@@ -83,6 +83,8 @@ declare class TableMetadata {
83
83
  columnMap: Map<string, string>;
84
84
  dimensions: string[];
85
85
  measures: string[];
86
+ /** path → sibling-ref path for `@db.amount.currency.ref` / `@db.unit.ref`. */
87
+ quantityRefByField: Map<string, string>;
86
88
  pathToPhysical: Map<string, string>;
87
89
  physicalToPath: Map<string, string>;
88
90
  flattenedParents: Set<string>;
@@ -394,6 +396,14 @@ interface TDbFieldMeta {
394
396
  collate?: TDbCollation;
395
397
  /** Whether this field participates in any index (@db.index.plain, @db.index.unique, @db.index.fulltext). */
396
398
  isIndexed?: boolean;
399
+ /** Literal currency code from `@db.amount.currency 'EUR'`. */
400
+ currencyCode?: string;
401
+ /** Sibling field path from `@db.amount.currency.ref 'fieldName'`. */
402
+ currencyRefField?: string;
403
+ /** Literal unit-of-measure from `@db.unit 'kg'`. */
404
+ unitCode?: string;
405
+ /** Sibling field path from `@db.unit.ref 'fieldName'`. */
406
+ unitRefField?: string;
397
407
  /**
398
408
  * For FK fields: the resolved field metadata of the referenced (target) PK column.
399
409
  * Adapters use this in `typeMapper` to produce matching DB types for FK columns
@@ -83,6 +83,8 @@ declare class TableMetadata {
83
83
  columnMap: Map<string, string>;
84
84
  dimensions: string[];
85
85
  measures: string[];
86
+ /** path → sibling-ref path for `@db.amount.currency.ref` / `@db.unit.ref`. */
87
+ quantityRefByField: Map<string, string>;
86
88
  pathToPhysical: Map<string, string>;
87
89
  physicalToPath: Map<string, string>;
88
90
  flattenedParents: Set<string>;
@@ -394,6 +396,14 @@ interface TDbFieldMeta {
394
396
  collate?: TDbCollation;
395
397
  /** Whether this field participates in any index (@db.index.plain, @db.index.unique, @db.index.fulltext). */
396
398
  isIndexed?: boolean;
399
+ /** Literal currency code from `@db.amount.currency 'EUR'`. */
400
+ currencyCode?: string;
401
+ /** Sibling field path from `@db.amount.currency.ref 'fieldName'`. */
402
+ currencyRefField?: string;
403
+ /** Literal unit-of-measure from `@db.unit 'kg'`. */
404
+ unitCode?: string;
405
+ /** Sibling field path from `@db.unit.ref 'fieldName'`. */
406
+ unitRefField?: string;
397
407
  /**
398
408
  * For FK fields: the resolved field metadata of the referenced (target) PK column.
399
409
  * Adapters use this in `typeMapper` to produce matching DB types for FK columns
@@ -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-D3bZPucw.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-CbTGJuRK.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
  /**
@@ -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-Dj9to0bC.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-Cw5EEUl9.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
  /**
@@ -64,6 +64,8 @@ var TableMetadata = class {
64
64
  columnMap = /* @__PURE__ */ new Map();
65
65
  dimensions = [];
66
66
  measures = [];
67
+ /** path → sibling-ref path for `@db.amount.currency.ref` / `@db.unit.ref`. */
68
+ quantityRefByField = /* @__PURE__ */ new Map();
67
69
  pathToPhysical = /* @__PURE__ */ new Map();
68
70
  physicalToPath = /* @__PURE__ */ new Map();
69
71
  flattenedParents = /* @__PURE__ */ new Set();
@@ -361,6 +363,12 @@ var TableMetadata = class {
361
363
  const fromLocal = this._columnFromMap.get(path);
362
364
  let renamedFrom;
363
365
  if (fromLocal) renamedFrom = isFlattened ? this._flattenedPrefix(path) + fromLocal : fromLocal;
366
+ const currencyCode = type.metadata.get("db.amount.currency");
367
+ const currencyRefField = type.metadata.get("db.amount.currency.ref");
368
+ const unitCode = type.metadata.get("db.unit");
369
+ const unitRefField = type.metadata.get("db.unit.ref");
370
+ const quantityRef = currencyRefField ?? unitRefField;
371
+ if (quantityRef) this.quantityRefByField.set(path, quantityRef);
364
372
  descriptors.push({
365
373
  path,
366
374
  type,
@@ -374,7 +382,11 @@ var TableMetadata = class {
374
382
  flattenedFrom: isFlattened ? path : void 0,
375
383
  renamedFrom,
376
384
  collate: this._collateMap.get(path),
377
- isIndexed: indexedFields.has(path) || void 0
385
+ isIndexed: indexedFields.has(path) || void 0,
386
+ currencyCode,
387
+ currencyRefField,
388
+ unitCode,
389
+ unitRefField
378
390
  });
379
391
  }
380
392
  this._resolveFkTargetFields(descriptors);
@@ -1407,6 +1419,19 @@ var AtscriptDbReadable = class {
1407
1419
  }]);
1408
1420
  }
1409
1421
  }
1422
+ const { quantityRefByField } = this._meta;
1423
+ if ($select && quantityRefByField.size > 0) {
1424
+ const groupBySet = new Set($groupBy);
1425
+ for (const item of $select) {
1426
+ if (typeof item === "string") continue;
1427
+ if (item.$field === "*") continue;
1428
+ const refField = quantityRefByField.get(item.$field);
1429
+ if (refField && !groupBySet.has(refField)) throw new require_db_error.DbError("INVALID_QUERY", [{
1430
+ path: "$select",
1431
+ message: `Aggregate "${item.$fn}(${item.$field})" requires "${refField}" in $groupBy — quantity-ref-tagged fields must be grouped by their dimension`
1432
+ }]);
1433
+ }
1434
+ }
1410
1435
  const dbQuery = this._fieldMapper.translateAggregateQuery(query, this._meta);
1411
1436
  return (await this.adapter.aggregate(dbQuery)).map((row) => {
1412
1437
  const mapped = {};
@@ -64,6 +64,8 @@ var TableMetadata = class {
64
64
  columnMap = /* @__PURE__ */ new Map();
65
65
  dimensions = [];
66
66
  measures = [];
67
+ /** path → sibling-ref path for `@db.amount.currency.ref` / `@db.unit.ref`. */
68
+ quantityRefByField = /* @__PURE__ */ new Map();
67
69
  pathToPhysical = /* @__PURE__ */ new Map();
68
70
  physicalToPath = /* @__PURE__ */ new Map();
69
71
  flattenedParents = /* @__PURE__ */ new Set();
@@ -361,6 +363,12 @@ var TableMetadata = class {
361
363
  const fromLocal = this._columnFromMap.get(path);
362
364
  let renamedFrom;
363
365
  if (fromLocal) renamedFrom = isFlattened ? this._flattenedPrefix(path) + fromLocal : fromLocal;
366
+ const currencyCode = type.metadata.get("db.amount.currency");
367
+ const currencyRefField = type.metadata.get("db.amount.currency.ref");
368
+ const unitCode = type.metadata.get("db.unit");
369
+ const unitRefField = type.metadata.get("db.unit.ref");
370
+ const quantityRef = currencyRefField ?? unitRefField;
371
+ if (quantityRef) this.quantityRefByField.set(path, quantityRef);
364
372
  descriptors.push({
365
373
  path,
366
374
  type,
@@ -374,7 +382,11 @@ var TableMetadata = class {
374
382
  flattenedFrom: isFlattened ? path : void 0,
375
383
  renamedFrom,
376
384
  collate: this._collateMap.get(path),
377
- isIndexed: indexedFields.has(path) || void 0
385
+ isIndexed: indexedFields.has(path) || void 0,
386
+ currencyCode,
387
+ currencyRefField,
388
+ unitCode,
389
+ unitRefField
378
390
  });
379
391
  }
380
392
  this._resolveFkTargetFields(descriptors);
@@ -1407,6 +1419,19 @@ var AtscriptDbReadable = class {
1407
1419
  }]);
1408
1420
  }
1409
1421
  }
1422
+ const { quantityRefByField } = this._meta;
1423
+ if ($select && quantityRefByField.size > 0) {
1424
+ const groupBySet = new Set($groupBy);
1425
+ for (const item of $select) {
1426
+ if (typeof item === "string") continue;
1427
+ if (item.$field === "*") continue;
1428
+ const refField = quantityRefByField.get(item.$field);
1429
+ if (refField && !groupBySet.has(refField)) throw new DbError("INVALID_QUERY", [{
1430
+ path: "$select",
1431
+ message: `Aggregate "${item.$fn}(${item.$field})" requires "${refField}" in $groupBy — quantity-ref-tagged fields must be grouped by their dimension`
1432
+ }]);
1433
+ }
1434
+ }
1410
1435
  const dbQuery = this._fieldMapper.translateAggregateQuery(query, this._meta);
1411
1436
  return (await this.adapter.aggregate(dbQuery)).map((row) => {
1412
1437
  const mapped = {};
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-D8tQhNgM.cjs");
3
- const require_db_view = require("./db-view-Jj46sTBk.cjs");
3
+ const require_db_view = require("./db-view-BSwJotLU.cjs");
4
4
  const require_ops = require("./ops.cjs");
5
5
  const require_validator = require("./validator-BIuw_T0k.cjs");
6
6
  require("./nested-writer-v_LPR1yJ.cjs");
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-D3bZPucw.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-Cw5EEUl9.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-CK11BW4j.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-ClTnpAI0.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-Dj9to0bC.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-CbTGJuRK.mjs";
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-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-Dy4GqZrK.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-Cb5gWqj8.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 { 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-T2BrLhwh.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-zOdeCwdz.mjs";
3
3
  import { $dec, $inc, $insert, $mul, $remove, $replace, $update, $upsert, getDbFieldOp, isDbFieldOp, 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-BB5h1Le3.mjs";
5
5
  import "./nested-writer-CT2rLURx.mjs";
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-B9WJv9aH.cjs");
5
+ const require_validation_utils = require("./validation-utils-Bs1yOXNx.cjs");
6
6
  let _atscript_core = require("@atscript/core");
7
7
  //#region src/plugin/annotations/agg.ts
8
8
  const dbAggAnnotations = { agg: {
@@ -63,6 +63,59 @@ const dbAggAnnotations = { agg: {
63
63
  })
64
64
  } };
65
65
  //#endregion
66
+ //#region src/plugin/annotations/amount.ts
67
+ const CURRENCY_CODE_PATTERN = /^[A-Z0-9]{2,10}$/;
68
+ const dbAmountAnnotations = { amount: { currency: {
69
+ $self: new _atscript_core.AnnotationSpec({
70
+ description: "Hard-coded **currency code** for this amount field. Use when a table stores amounts in a single, fixed currency.\n\nFor per-row currency, use `@db.amount.currency.ref 'fieldName'` instead.\n\nOnly valid on `decimal` fields — money in floating-point is wrong.\n\n**Example:**\n```atscript\n@db.amount.currency 'EUR'\namount: decimal\n```\n",
71
+ nodeType: ["prop"],
72
+ multiple: false,
73
+ argument: {
74
+ name: "code",
75
+ type: "string",
76
+ description: "Currency code — uppercase 2–10 alphanumerics (ISO 4217 or custom: 'EUR', 'USD', 'BTC')."
77
+ },
78
+ validate(token, args, doc) {
79
+ const errors = [];
80
+ errors.push(...require_validation_utils.validateFieldBaseType(token, doc, "@db.amount.currency", "decimal"));
81
+ errors.push(...require_validation_utils.validateExclusiveWith(token, "@db.amount.currency", [{
82
+ key: "db.amount.currency.ref",
83
+ displayName: "@db.amount.currency.ref"
84
+ }]));
85
+ const code = args[0]?.text;
86
+ if (code && !CURRENCY_CODE_PATTERN.test(code)) errors.push({
87
+ message: `@db.amount.currency '${code}': invalid currency code (expected 2–10 uppercase letters or digits)`,
88
+ severity: 1,
89
+ range: token.range
90
+ });
91
+ return errors;
92
+ }
93
+ }),
94
+ ref: new _atscript_core.AnnotationSpec({
95
+ description: "Binds this amount to a **sibling field** that holds its currency code. Use when each row may have a different currency.\n\nThe referenced field should be of type `db.currencyCode` (or at least a `string`).\n\nOnly valid on `decimal` fields.\n\n**Example:**\n```atscript\ncurrency: db.currencyCode\n@db.amount.currency.ref 'currency'\namount: decimal\n```\n",
96
+ nodeType: ["prop"],
97
+ multiple: false,
98
+ argument: {
99
+ name: "fieldName",
100
+ type: "string",
101
+ description: "Name of the sibling property holding the currency code."
102
+ },
103
+ validate(token, args, doc) {
104
+ const errors = [];
105
+ errors.push(...require_validation_utils.validateFieldBaseType(token, doc, "@db.amount.currency.ref", "decimal"));
106
+ errors.push(...require_validation_utils.validateExclusiveWith(token, "@db.amount.currency.ref", [{
107
+ key: "db.amount.currency",
108
+ displayName: "@db.amount.currency"
109
+ }, {
110
+ key: "db.unit.ref",
111
+ displayName: "@db.unit.ref"
112
+ }]));
113
+ errors.push(...require_validation_utils.validateSiblingStringField(token, args, doc, "@db.amount.currency.ref"));
114
+ return errors;
115
+ }
116
+ })
117
+ } } };
118
+ //#endregion
66
119
  //#region src/plugin/annotations/column.ts
67
120
  const dbColumnAnnotations = {
68
121
  patch: { strategy: new _atscript_core.AnnotationSpec({
@@ -927,6 +980,53 @@ Writing the annotation explicitly as \`@db.table.${capability} 'auto'\` has the
927
980
  });
928
981
  }
929
982
  //#endregion
983
+ //#region src/plugin/annotations/unit.ts
984
+ const UNIT_HOST_TYPES = ["decimal", "number"];
985
+ const dbUnitAnnotations = { unit: {
986
+ $self: new _atscript_core.AnnotationSpec({
987
+ description: "Hard-coded **unit of measure** for this quantity field. Use when a table stores all values for this field in a single, fixed unit.\n\nFor per-row units, use `@db.unit.ref 'fieldName'` instead.\n\nValid on `decimal` and `number` fields. Unlike `@db.amount.currency`, the unit code is free-form (`'kg'`, `'rpm'`, `'qps'`, …) — no ISO-style validation.\n\nAggregations on a unit-tagged field do not need grouping when the unit is a fixed literal — the constraint is satisfied schema-wide.\n\n**Example:**\n```atscript\n@db.unit 'kg'\nweight: decimal\n```\n",
988
+ nodeType: ["prop"],
989
+ multiple: false,
990
+ argument: {
991
+ name: "code",
992
+ type: "string",
993
+ description: "Unit code (free-form: 'kg', 'g', 'lb', 'm', 'rpm', 'qps', etc.)."
994
+ },
995
+ validate(token, _args, doc) {
996
+ const errors = [];
997
+ errors.push(...require_validation_utils.validateFieldBaseType(token, doc, "@db.unit", UNIT_HOST_TYPES));
998
+ errors.push(...require_validation_utils.validateExclusiveWith(token, "@db.unit", [{
999
+ key: "db.unit.ref",
1000
+ displayName: "@db.unit.ref"
1001
+ }]));
1002
+ return errors;
1003
+ }
1004
+ }),
1005
+ ref: new _atscript_core.AnnotationSpec({
1006
+ description: "Binds this quantity to a **sibling field** that holds its unit. Use when different rows may carry different units (e.g. mixed kg/lb measurements).\n\nThe referenced field must be a `string`.\n\nValid on `decimal` and `number` fields. Aggregating this field forces the referenced unit field into `$groupBy` at runtime — summing rows with different units is rejected.\n\n**Example:**\n```atscript\nunit: string\n@db.unit.ref 'unit'\nweight: decimal\n```\n",
1007
+ nodeType: ["prop"],
1008
+ multiple: false,
1009
+ argument: {
1010
+ name: "fieldName",
1011
+ type: "string",
1012
+ description: "Name of the sibling property holding the unit code."
1013
+ },
1014
+ validate(token, args, doc) {
1015
+ const errors = [];
1016
+ errors.push(...require_validation_utils.validateFieldBaseType(token, doc, "@db.unit.ref", UNIT_HOST_TYPES));
1017
+ errors.push(...require_validation_utils.validateExclusiveWith(token, "@db.unit.ref", [{
1018
+ key: "db.unit",
1019
+ displayName: "@db.unit"
1020
+ }, {
1021
+ key: "db.amount.currency.ref",
1022
+ displayName: "@db.amount.currency.ref"
1023
+ }]));
1024
+ errors.push(...require_validation_utils.validateSiblingStringField(token, args, doc, "@db.unit.ref"));
1025
+ return errors;
1026
+ }
1027
+ })
1028
+ } };
1029
+ //#endregion
930
1030
  //#region src/plugin/annotations/view.ts
931
1031
  const dbViewAnnotations = { view: {
932
1032
  $self: new _atscript_core.AnnotationSpec({
@@ -1121,15 +1221,27 @@ const dbPlugin = () => ({
1121
1221
  rel: dbRelAnnotations.rel,
1122
1222
  view: dbViewAnnotations.view,
1123
1223
  agg: dbAggAnnotations.agg,
1124
- search: dbSearchAnnotations.search
1224
+ search: dbSearchAnnotations.search,
1225
+ amount: dbAmountAnnotations.amount,
1226
+ unit: dbUnitAnnotations.unit
1125
1227
  } },
1126
- primitives: { db: { extensions: { vector: {
1127
- type: {
1128
- kind: "array",
1129
- of: "number"
1228
+ primitives: { db: { extensions: {
1229
+ vector: {
1230
+ type: {
1231
+ kind: "array",
1232
+ of: "number"
1233
+ },
1234
+ documentation: "Represents a **vector embedding** (array of numbers) for **similarity search**.\n\n- Equivalent to `number[]` but explicitly marks the field as a vector embedding.\n- Each adapter maps this to its native vector type:\n - **MongoDB** → BSON array\n - **MySQL 9+** → `VECTOR(N)`\n - **PostgreSQL** → pgvector `vector(N)`\n - **SQLite** → JSON\n\n**Example:**\n```atscript\n@db.search.vector 1536, \"cosine\"\nembedding: db.vector\n```\n"
1130
1235
  },
1131
- documentation: "Represents a **vector embedding** (array of numbers) for **similarity search**.\n\n- Equivalent to `number[]` but explicitly marks the field as a vector embedding.\n- Each adapter maps this to its native vector type:\n - **MongoDB** → BSON array\n - **MySQL 9+** → `VECTOR(N)`\n - **PostgreSQL** → pgvector `vector(N)`\n - **SQLite** → JSON\n\n**Example:**\n```atscript\n@db.search.vector 1536, \"cosine\"\nembedding: db.vector\n```\n"
1132
- } } } }
1236
+ currencyCode: {
1237
+ type: "string",
1238
+ documentation: "Represents a **currency code** — typically ISO 4217 (`'USD'`, `'EUR'`, `'JPY'`) but accepts any uppercase alphanumeric code 2–10 chars long, so crypto and custom codes (`'BTC'`, `'USDC'`, `'POINTS'`) fit too.\n\nPair with `@db.amount.currency.ref 'fieldName'` on a `decimal` field to bind the amount to its row-level currency. The validator checks that the ref target resolves to this type (or a plain `string`).\n\n**Example:**\n```atscript\ncurrency: db.currencyCode\n@db.amount.currency.ref 'currency'\namount: decimal\n```\n",
1239
+ annotations: { "expect.pattern": [{
1240
+ pattern: "^[A-Z0-9]{2,10}$",
1241
+ message: "Invalid currency code (expected 2–10 uppercase letters or digits)"
1242
+ }] }
1243
+ }
1244
+ } } }
1133
1245
  };
1134
1246
  }
1135
1247
  });
package/dist/plugin.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { a as getAnnotationAlias, c as getParentStruct, d as validateFieldBaseType, i as validateRefArgument, l as getParentTypeName, n as hasAnyViewAnnotation, o as getDbTableOwner, r as validateQueryScope, s as getNavTargetTypeName, t as findFKFieldsPointingTo, u as refActionAnnotation } from "./validation-utils-Bh7RVrVl.mjs";
1
+ import { a as getAnnotationAlias, c as getParentStruct, d as validateExclusiveWith, f as validateFieldBaseType, i as validateRefArgument, l as getParentTypeName, n as hasAnyViewAnnotation, o as getDbTableOwner, p as validateSiblingStringField, r as validateQueryScope, s as getNavTargetTypeName, t as findFKFieldsPointingTo, u as refActionAnnotation } from "./validation-utils-eaBuT39W.mjs";
2
2
  import { AnnotationSpec, isArray, isInterface, isPrimitive, isRef, isStructure } from "@atscript/core";
3
3
  //#region src/plugin/annotations/agg.ts
4
4
  const dbAggAnnotations = { agg: {
@@ -59,6 +59,59 @@ const dbAggAnnotations = { agg: {
59
59
  })
60
60
  } };
61
61
  //#endregion
62
+ //#region src/plugin/annotations/amount.ts
63
+ const CURRENCY_CODE_PATTERN = /^[A-Z0-9]{2,10}$/;
64
+ const dbAmountAnnotations = { amount: { currency: {
65
+ $self: new AnnotationSpec({
66
+ description: "Hard-coded **currency code** for this amount field. Use when a table stores amounts in a single, fixed currency.\n\nFor per-row currency, use `@db.amount.currency.ref 'fieldName'` instead.\n\nOnly valid on `decimal` fields — money in floating-point is wrong.\n\n**Example:**\n```atscript\n@db.amount.currency 'EUR'\namount: decimal\n```\n",
67
+ nodeType: ["prop"],
68
+ multiple: false,
69
+ argument: {
70
+ name: "code",
71
+ type: "string",
72
+ description: "Currency code — uppercase 2–10 alphanumerics (ISO 4217 or custom: 'EUR', 'USD', 'BTC')."
73
+ },
74
+ validate(token, args, doc) {
75
+ const errors = [];
76
+ errors.push(...validateFieldBaseType(token, doc, "@db.amount.currency", "decimal"));
77
+ errors.push(...validateExclusiveWith(token, "@db.amount.currency", [{
78
+ key: "db.amount.currency.ref",
79
+ displayName: "@db.amount.currency.ref"
80
+ }]));
81
+ const code = args[0]?.text;
82
+ if (code && !CURRENCY_CODE_PATTERN.test(code)) errors.push({
83
+ message: `@db.amount.currency '${code}': invalid currency code (expected 2–10 uppercase letters or digits)`,
84
+ severity: 1,
85
+ range: token.range
86
+ });
87
+ return errors;
88
+ }
89
+ }),
90
+ ref: new AnnotationSpec({
91
+ description: "Binds this amount to a **sibling field** that holds its currency code. Use when each row may have a different currency.\n\nThe referenced field should be of type `db.currencyCode` (or at least a `string`).\n\nOnly valid on `decimal` fields.\n\n**Example:**\n```atscript\ncurrency: db.currencyCode\n@db.amount.currency.ref 'currency'\namount: decimal\n```\n",
92
+ nodeType: ["prop"],
93
+ multiple: false,
94
+ argument: {
95
+ name: "fieldName",
96
+ type: "string",
97
+ description: "Name of the sibling property holding the currency code."
98
+ },
99
+ validate(token, args, doc) {
100
+ const errors = [];
101
+ errors.push(...validateFieldBaseType(token, doc, "@db.amount.currency.ref", "decimal"));
102
+ errors.push(...validateExclusiveWith(token, "@db.amount.currency.ref", [{
103
+ key: "db.amount.currency",
104
+ displayName: "@db.amount.currency"
105
+ }, {
106
+ key: "db.unit.ref",
107
+ displayName: "@db.unit.ref"
108
+ }]));
109
+ errors.push(...validateSiblingStringField(token, args, doc, "@db.amount.currency.ref"));
110
+ return errors;
111
+ }
112
+ })
113
+ } } };
114
+ //#endregion
62
115
  //#region src/plugin/annotations/column.ts
63
116
  const dbColumnAnnotations = {
64
117
  patch: { strategy: new AnnotationSpec({
@@ -923,6 +976,53 @@ Writing the annotation explicitly as \`@db.table.${capability} 'auto'\` has the
923
976
  });
924
977
  }
925
978
  //#endregion
979
+ //#region src/plugin/annotations/unit.ts
980
+ const UNIT_HOST_TYPES = ["decimal", "number"];
981
+ const dbUnitAnnotations = { unit: {
982
+ $self: new AnnotationSpec({
983
+ description: "Hard-coded **unit of measure** for this quantity field. Use when a table stores all values for this field in a single, fixed unit.\n\nFor per-row units, use `@db.unit.ref 'fieldName'` instead.\n\nValid on `decimal` and `number` fields. Unlike `@db.amount.currency`, the unit code is free-form (`'kg'`, `'rpm'`, `'qps'`, …) — no ISO-style validation.\n\nAggregations on a unit-tagged field do not need grouping when the unit is a fixed literal — the constraint is satisfied schema-wide.\n\n**Example:**\n```atscript\n@db.unit 'kg'\nweight: decimal\n```\n",
984
+ nodeType: ["prop"],
985
+ multiple: false,
986
+ argument: {
987
+ name: "code",
988
+ type: "string",
989
+ description: "Unit code (free-form: 'kg', 'g', 'lb', 'm', 'rpm', 'qps', etc.)."
990
+ },
991
+ validate(token, _args, doc) {
992
+ const errors = [];
993
+ errors.push(...validateFieldBaseType(token, doc, "@db.unit", UNIT_HOST_TYPES));
994
+ errors.push(...validateExclusiveWith(token, "@db.unit", [{
995
+ key: "db.unit.ref",
996
+ displayName: "@db.unit.ref"
997
+ }]));
998
+ return errors;
999
+ }
1000
+ }),
1001
+ ref: new AnnotationSpec({
1002
+ description: "Binds this quantity to a **sibling field** that holds its unit. Use when different rows may carry different units (e.g. mixed kg/lb measurements).\n\nThe referenced field must be a `string`.\n\nValid on `decimal` and `number` fields. Aggregating this field forces the referenced unit field into `$groupBy` at runtime — summing rows with different units is rejected.\n\n**Example:**\n```atscript\nunit: string\n@db.unit.ref 'unit'\nweight: decimal\n```\n",
1003
+ nodeType: ["prop"],
1004
+ multiple: false,
1005
+ argument: {
1006
+ name: "fieldName",
1007
+ type: "string",
1008
+ description: "Name of the sibling property holding the unit code."
1009
+ },
1010
+ validate(token, args, doc) {
1011
+ const errors = [];
1012
+ errors.push(...validateFieldBaseType(token, doc, "@db.unit.ref", UNIT_HOST_TYPES));
1013
+ errors.push(...validateExclusiveWith(token, "@db.unit.ref", [{
1014
+ key: "db.unit",
1015
+ displayName: "@db.unit"
1016
+ }, {
1017
+ key: "db.amount.currency.ref",
1018
+ displayName: "@db.amount.currency.ref"
1019
+ }]));
1020
+ errors.push(...validateSiblingStringField(token, args, doc, "@db.unit.ref"));
1021
+ return errors;
1022
+ }
1023
+ })
1024
+ } };
1025
+ //#endregion
926
1026
  //#region src/plugin/annotations/view.ts
927
1027
  const dbViewAnnotations = { view: {
928
1028
  $self: new AnnotationSpec({
@@ -1117,15 +1217,27 @@ const dbPlugin = () => ({
1117
1217
  rel: dbRelAnnotations.rel,
1118
1218
  view: dbViewAnnotations.view,
1119
1219
  agg: dbAggAnnotations.agg,
1120
- search: dbSearchAnnotations.search
1220
+ search: dbSearchAnnotations.search,
1221
+ amount: dbAmountAnnotations.amount,
1222
+ unit: dbUnitAnnotations.unit
1121
1223
  } },
1122
- primitives: { db: { extensions: { vector: {
1123
- type: {
1124
- kind: "array",
1125
- of: "number"
1224
+ primitives: { db: { extensions: {
1225
+ vector: {
1226
+ type: {
1227
+ kind: "array",
1228
+ of: "number"
1229
+ },
1230
+ documentation: "Represents a **vector embedding** (array of numbers) for **similarity search**.\n\n- Equivalent to `number[]` but explicitly marks the field as a vector embedding.\n- Each adapter maps this to its native vector type:\n - **MongoDB** → BSON array\n - **MySQL 9+** → `VECTOR(N)`\n - **PostgreSQL** → pgvector `vector(N)`\n - **SQLite** → JSON\n\n**Example:**\n```atscript\n@db.search.vector 1536, \"cosine\"\nembedding: db.vector\n```\n"
1126
1231
  },
1127
- documentation: "Represents a **vector embedding** (array of numbers) for **similarity search**.\n\n- Equivalent to `number[]` but explicitly marks the field as a vector embedding.\n- Each adapter maps this to its native vector type:\n - **MongoDB** → BSON array\n - **MySQL 9+** → `VECTOR(N)`\n - **PostgreSQL** → pgvector `vector(N)`\n - **SQLite** → JSON\n\n**Example:**\n```atscript\n@db.search.vector 1536, \"cosine\"\nembedding: db.vector\n```\n"
1128
- } } } }
1232
+ currencyCode: {
1233
+ type: "string",
1234
+ documentation: "Represents a **currency code** — typically ISO 4217 (`'USD'`, `'EUR'`, `'JPY'`) but accepts any uppercase alphanumeric code 2–10 chars long, so crypto and custom codes (`'BTC'`, `'USDC'`, `'POINTS'`) fit too.\n\nPair with `@db.amount.currency.ref 'fieldName'` on a `decimal` field to bind the amount to its row-level currency. The validator checks that the ref target resolves to this type (or a plain `string`).\n\n**Example:**\n```atscript\ncurrency: db.currencyCode\n@db.amount.currency.ref 'currency'\namount: decimal\n```\n",
1235
+ annotations: { "expect.pattern": [{
1236
+ pattern: "^[A-Z0-9]{2,10}$",
1237
+ message: "Invalid currency code (expected 2–10 uppercase letters or digits)"
1238
+ }] }
1239
+ }
1240
+ } } }
1129
1241
  };
1130
1242
  }
1131
1243
  });
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-D3bZPucw.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-Cw5EEUl9.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-Dj9to0bC.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-CbTGJuRK.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/shared.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_validation_utils = require("./validation-utils-B9WJv9aH.cjs");
2
+ const require_validation_utils = require("./validation-utils-Bs1yOXNx.cjs");
3
3
  exports.findFKFieldsPointingTo = require_validation_utils.findFKFieldsPointingTo;
4
4
  exports.getAnnotationAlias = require_validation_utils.getAnnotationAlias;
5
5
  exports.getDbTableOwner = require_validation_utils.getDbTableOwner;
@@ -8,6 +8,8 @@ exports.getParentStruct = require_validation_utils.getParentStruct;
8
8
  exports.getParentTypeName = require_validation_utils.getParentTypeName;
9
9
  exports.hasAnyViewAnnotation = require_validation_utils.hasAnyViewAnnotation;
10
10
  exports.refActionAnnotation = require_validation_utils.refActionAnnotation;
11
+ exports.validateExclusiveWith = require_validation_utils.validateExclusiveWith;
11
12
  exports.validateFieldBaseType = require_validation_utils.validateFieldBaseType;
12
13
  exports.validateQueryScope = require_validation_utils.validateQueryScope;
13
14
  exports.validateRefArgument = require_validation_utils.validateRefArgument;
15
+ exports.validateSiblingStringField = require_validation_utils.validateSiblingStringField;
package/dist/shared.d.cts CHANGED
@@ -1,6 +1,13 @@
1
1
  import { AnnotationSpec, AtscriptDoc, SemanticInterfaceNode, SemanticNode, SemanticPropNode, SemanticStructureNode, TMessages, Token } from "@atscript/core";
2
2
 
3
3
  //#region src/shared/annotation-utils.d.ts
4
+ /** Asserts the field carrying this annotation does not also carry any of `others`. */
5
+ declare function validateExclusiveWith(token: Token, selfName: string, others: Array<{
6
+ key: string;
7
+ displayName?: string;
8
+ }>): TMessages;
9
+ /** Asserts `args[0]` names a sibling property whose primitive base type is `string`. */
10
+ declare function validateSiblingStringField(token: Token, args: Token[], doc: AtscriptDoc, selfName: string): TMessages;
4
11
  /**
5
12
  * Traverse from annotation token → prop → structure → interface
6
13
  * to check if the parent interface has @db.table.
@@ -68,4 +75,4 @@ declare function hasAnyViewAnnotation(node: SemanticNode): boolean;
68
75
  */
69
76
  declare function validateQueryScope(queryToken: Token, allowedTypes: string[], unqualifiedTarget: string | null, doc: AtscriptDoc): TMessages;
70
77
  //#endregion
71
- export { TFKFieldMatch, findFKFieldsPointingTo, getAnnotationAlias, getDbTableOwner, getNavTargetTypeName, getParentStruct, getParentTypeName, hasAnyViewAnnotation, refActionAnnotation, validateFieldBaseType, validateQueryScope, validateRefArgument };
78
+ export { TFKFieldMatch, findFKFieldsPointingTo, getAnnotationAlias, getDbTableOwner, getNavTargetTypeName, getParentStruct, getParentTypeName, hasAnyViewAnnotation, refActionAnnotation, validateExclusiveWith, validateFieldBaseType, validateQueryScope, validateRefArgument, validateSiblingStringField };
package/dist/shared.d.mts CHANGED
@@ -1,6 +1,13 @@
1
1
  import { AnnotationSpec, AtscriptDoc, SemanticInterfaceNode, SemanticNode, SemanticPropNode, SemanticStructureNode, TMessages, Token } from "@atscript/core";
2
2
 
3
3
  //#region src/shared/annotation-utils.d.ts
4
+ /** Asserts the field carrying this annotation does not also carry any of `others`. */
5
+ declare function validateExclusiveWith(token: Token, selfName: string, others: Array<{
6
+ key: string;
7
+ displayName?: string;
8
+ }>): TMessages;
9
+ /** Asserts `args[0]` names a sibling property whose primitive base type is `string`. */
10
+ declare function validateSiblingStringField(token: Token, args: Token[], doc: AtscriptDoc, selfName: string): TMessages;
4
11
  /**
5
12
  * Traverse from annotation token → prop → structure → interface
6
13
  * to check if the parent interface has @db.table.
@@ -68,4 +75,4 @@ declare function hasAnyViewAnnotation(node: SemanticNode): boolean;
68
75
  */
69
76
  declare function validateQueryScope(queryToken: Token, allowedTypes: string[], unqualifiedTarget: string | null, doc: AtscriptDoc): TMessages;
70
77
  //#endregion
71
- export { TFKFieldMatch, findFKFieldsPointingTo, getAnnotationAlias, getDbTableOwner, getNavTargetTypeName, getParentStruct, getParentTypeName, hasAnyViewAnnotation, refActionAnnotation, validateFieldBaseType, validateQueryScope, validateRefArgument };
78
+ export { TFKFieldMatch, findFKFieldsPointingTo, getAnnotationAlias, getDbTableOwner, getNavTargetTypeName, getParentStruct, getParentTypeName, hasAnyViewAnnotation, refActionAnnotation, validateExclusiveWith, validateFieldBaseType, validateQueryScope, validateRefArgument, validateSiblingStringField };
package/dist/shared.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import { a as getAnnotationAlias, c as getParentStruct, d as validateFieldBaseType, i as validateRefArgument, l as getParentTypeName, n as hasAnyViewAnnotation, o as getDbTableOwner, r as validateQueryScope, s as getNavTargetTypeName, t as findFKFieldsPointingTo, u as refActionAnnotation } from "./validation-utils-Bh7RVrVl.mjs";
2
- export { findFKFieldsPointingTo, getAnnotationAlias, getDbTableOwner, getNavTargetTypeName, getParentStruct, getParentTypeName, hasAnyViewAnnotation, refActionAnnotation, validateFieldBaseType, validateQueryScope, validateRefArgument };
1
+ import { a as getAnnotationAlias, c as getParentStruct, d as validateExclusiveWith, f as validateFieldBaseType, i as validateRefArgument, l as getParentTypeName, n as hasAnyViewAnnotation, o as getDbTableOwner, p as validateSiblingStringField, r as validateQueryScope, s as getNavTargetTypeName, t as findFKFieldsPointingTo, u as refActionAnnotation } from "./validation-utils-eaBuT39W.mjs";
2
+ export { findFKFieldsPointingTo, getAnnotationAlias, getDbTableOwner, getNavTargetTypeName, getParentStruct, getParentTypeName, hasAnyViewAnnotation, refActionAnnotation, validateExclusiveWith, validateFieldBaseType, validateQueryScope, validateRefArgument, validateSiblingStringField };
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-Jj46sTBk.cjs");
2
+ const require_db_view = require("./db-view-BSwJotLU.cjs");
3
3
  require("./validator-BIuw_T0k.cjs");
4
4
  require("./nested-writer-v_LPR1yJ.cjs");
5
5
  //#region src/schema/schema-hash.ts
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-D3bZPucw.cjs";
2
- import { r as AtscriptDbView, t as DbSpace } from "./db-space-CK11BW4j.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-Cw5EEUl9.cjs";
2
+ import { r as AtscriptDbView, t as DbSpace } from "./db-space-ClTnpAI0.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-Dj9to0bC.mjs";
2
- import { r as AtscriptDbView, t as DbSpace } from "./db-space-Dy4GqZrK.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-CbTGJuRK.mjs";
2
+ import { r as AtscriptDbView, t as DbSpace } from "./db-space-Cb5gWqj8.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 { h as NoopLogger } from "./db-view-T2BrLhwh.mjs";
1
+ import { h as NoopLogger } from "./db-view-zOdeCwdz.mjs";
2
2
  import "./validator-BB5h1Le3.mjs";
3
3
  import "./nested-writer-CT2rLURx.mjs";
4
4
  //#region src/schema/schema-hash.ts
@@ -1,5 +1,49 @@
1
1
  let _atscript_core = require("@atscript/core");
2
2
  //#region src/shared/annotation-utils.ts
3
+ /** Asserts the field carrying this annotation does not also carry any of `others`. */
4
+ function validateExclusiveWith(token, selfName, others) {
5
+ const errors = [];
6
+ const field = token.parentNode;
7
+ for (const { key, displayName } of others) if (field.countAnnotations(key) > 0) errors.push({
8
+ message: `${selfName} cannot coexist with ${displayName ?? `@${key}`} on the same field — pick one form, not both`,
9
+ severity: 1,
10
+ range: token.range
11
+ });
12
+ return errors;
13
+ }
14
+ /** Resolves a field/prop's primitive base type, or `undefined` if it isn't a ref-to-primitive. */
15
+ function getPrimitiveBaseType(node, doc) {
16
+ const def = node.getDefinition();
17
+ if (!def || !(0, _atscript_core.isRef)(def)) return void 0;
18
+ const unwound = doc.unwindType(def.id, def.chain);
19
+ if (!unwound || !(0, _atscript_core.isPrimitive)(unwound.def)) return void 0;
20
+ const ct = unwound.def.config.type;
21
+ return typeof ct === "object" ? ct.kind === "final" ? ct.value : ct.kind : ct;
22
+ }
23
+ /** Asserts `args[0]` names a sibling property whose primitive base type is `string`. */
24
+ function validateSiblingStringField(token, args, doc, selfName) {
25
+ const errors = [];
26
+ const fieldName = args[0]?.text;
27
+ if (!fieldName) return errors;
28
+ const struct = getParentStruct(token);
29
+ if (!struct) return errors;
30
+ const sibling = struct.props.get(fieldName);
31
+ if (!sibling) {
32
+ errors.push({
33
+ message: `${selfName} '${fieldName}': no sibling field named '${fieldName}' on this type`,
34
+ severity: 1,
35
+ range: token.range
36
+ });
37
+ return errors;
38
+ }
39
+ const baseType = getPrimitiveBaseType(sibling, doc);
40
+ if (baseType !== void 0 && baseType !== "string") errors.push({
41
+ message: `${selfName} '${fieldName}': sibling field must be a string, got '${baseType}'`,
42
+ severity: 1,
43
+ range: token.range
44
+ });
45
+ return errors;
46
+ }
3
47
  /**
4
48
  * Traverse from annotation token → prop → structure → interface
5
49
  * to check if the parent interface has @db.table.
@@ -31,12 +75,9 @@ function getParentTypeName(token) {
31
75
  */
32
76
  function validateFieldBaseType(token, doc, annotationName, expectedType) {
33
77
  const errors = [];
34
- const definition = token.parentNode.getDefinition();
35
- if (!definition || !(0, _atscript_core.isRef)(definition)) return errors;
36
- const unwound = doc.unwindType(definition.id, definition.chain);
37
- if (!unwound || !(0, _atscript_core.isPrimitive)(unwound.def)) return errors;
38
- const ct = unwound.def.config.type;
39
- const baseType = typeof ct === "object" ? ct.kind === "final" ? ct.value : ct.kind : ct;
78
+ const field = token.parentNode;
79
+ const baseType = getPrimitiveBaseType(field, doc);
80
+ if (baseType === void 0) return errors;
40
81
  const allowed = Array.isArray(expectedType) ? expectedType : [expectedType];
41
82
  if (!allowed.includes(baseType)) errors.push({
42
83
  message: `${annotationName} is not compatible with type "${baseType}" — requires ${allowed.join(" or ")}`,
@@ -316,6 +357,12 @@ Object.defineProperty(exports, "refActionAnnotation", {
316
357
  return refActionAnnotation;
317
358
  }
318
359
  });
360
+ Object.defineProperty(exports, "validateExclusiveWith", {
361
+ enumerable: true,
362
+ get: function() {
363
+ return validateExclusiveWith;
364
+ }
365
+ });
319
366
  Object.defineProperty(exports, "validateFieldBaseType", {
320
367
  enumerable: true,
321
368
  get: function() {
@@ -334,3 +381,9 @@ Object.defineProperty(exports, "validateRefArgument", {
334
381
  return validateRefArgument;
335
382
  }
336
383
  });
384
+ Object.defineProperty(exports, "validateSiblingStringField", {
385
+ enumerable: true,
386
+ get: function() {
387
+ return validateSiblingStringField;
388
+ }
389
+ });
@@ -1,5 +1,49 @@
1
1
  import { AnnotationSpec, isArray, isInterface, isPrimitive, isQueryComparison, isQueryLogical, isRef, isStructure } from "@atscript/core";
2
2
  //#region src/shared/annotation-utils.ts
3
+ /** Asserts the field carrying this annotation does not also carry any of `others`. */
4
+ function validateExclusiveWith(token, selfName, others) {
5
+ const errors = [];
6
+ const field = token.parentNode;
7
+ for (const { key, displayName } of others) if (field.countAnnotations(key) > 0) errors.push({
8
+ message: `${selfName} cannot coexist with ${displayName ?? `@${key}`} on the same field — pick one form, not both`,
9
+ severity: 1,
10
+ range: token.range
11
+ });
12
+ return errors;
13
+ }
14
+ /** Resolves a field/prop's primitive base type, or `undefined` if it isn't a ref-to-primitive. */
15
+ function getPrimitiveBaseType(node, doc) {
16
+ const def = node.getDefinition();
17
+ if (!def || !isRef(def)) return void 0;
18
+ const unwound = doc.unwindType(def.id, def.chain);
19
+ if (!unwound || !isPrimitive(unwound.def)) return void 0;
20
+ const ct = unwound.def.config.type;
21
+ return typeof ct === "object" ? ct.kind === "final" ? ct.value : ct.kind : ct;
22
+ }
23
+ /** Asserts `args[0]` names a sibling property whose primitive base type is `string`. */
24
+ function validateSiblingStringField(token, args, doc, selfName) {
25
+ const errors = [];
26
+ const fieldName = args[0]?.text;
27
+ if (!fieldName) return errors;
28
+ const struct = getParentStruct(token);
29
+ if (!struct) return errors;
30
+ const sibling = struct.props.get(fieldName);
31
+ if (!sibling) {
32
+ errors.push({
33
+ message: `${selfName} '${fieldName}': no sibling field named '${fieldName}' on this type`,
34
+ severity: 1,
35
+ range: token.range
36
+ });
37
+ return errors;
38
+ }
39
+ const baseType = getPrimitiveBaseType(sibling, doc);
40
+ if (baseType !== void 0 && baseType !== "string") errors.push({
41
+ message: `${selfName} '${fieldName}': sibling field must be a string, got '${baseType}'`,
42
+ severity: 1,
43
+ range: token.range
44
+ });
45
+ return errors;
46
+ }
3
47
  /**
4
48
  * Traverse from annotation token → prop → structure → interface
5
49
  * to check if the parent interface has @db.table.
@@ -31,12 +75,9 @@ function getParentTypeName(token) {
31
75
  */
32
76
  function validateFieldBaseType(token, doc, annotationName, expectedType) {
33
77
  const errors = [];
34
- const definition = token.parentNode.getDefinition();
35
- if (!definition || !isRef(definition)) return errors;
36
- const unwound = doc.unwindType(definition.id, definition.chain);
37
- if (!unwound || !isPrimitive(unwound.def)) return errors;
38
- const ct = unwound.def.config.type;
39
- const baseType = typeof ct === "object" ? ct.kind === "final" ? ct.value : ct.kind : ct;
78
+ const field = token.parentNode;
79
+ const baseType = getPrimitiveBaseType(field, doc);
80
+ if (baseType === void 0) return errors;
40
81
  const allowed = Array.isArray(expectedType) ? expectedType : [expectedType];
41
82
  if (!allowed.includes(baseType)) errors.push({
42
83
  message: `${annotationName} is not compatible with type "${baseType}" — requires ${allowed.join(" or ")}`,
@@ -268,4 +309,4 @@ function validateQueryScope(queryToken, allowedTypes, unqualifiedTarget, doc) {
268
309
  return errors;
269
310
  }
270
311
  //#endregion
271
- export { getAnnotationAlias as a, getParentStruct as c, validateFieldBaseType as d, validateRefArgument as i, getParentTypeName as l, hasAnyViewAnnotation as n, getDbTableOwner as o, validateQueryScope as r, getNavTargetTypeName as s, findFKFieldsPointingTo as t, refActionAnnotation as u };
312
+ export { getAnnotationAlias as a, getParentStruct as c, validateExclusiveWith as d, validateFieldBaseType as f, validateRefArgument as i, getParentTypeName as l, hasAnyViewAnnotation as n, getDbTableOwner as o, validateSiblingStringField as p, validateQueryScope as r, getNavTargetTypeName as s, findFKFieldsPointingTo as t, refActionAnnotation as u };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atscript/db",
3
- "version": "0.1.63",
3
+ "version": "0.1.64",
4
4
  "description": "Database adapter utilities for atscript.",
5
5
  "keywords": [
6
6
  "atscript",