@atscript/db 0.1.148 → 0.1.150

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 (35) hide show
  1. package/dist/agg.d.cts +1 -1
  2. package/dist/agg.d.mts +1 -1
  3. package/dist/{buckets-DJiYlMXc.d.cts → buckets-24Ac4sIS.d.cts} +80 -5
  4. package/dist/{buckets-CNdTOnei.d.mts → buckets-XOz0Bw5x.d.mts} +80 -5
  5. package/dist/{column-diff-CUU4GvYg.cjs → column-diff-CKKBVDvx.cjs} +646 -341
  6. package/dist/{column-diff-Cp6ZoyRE.mjs → column-diff-CKLDWBbo.mjs} +522 -253
  7. package/dist/{fk-diff-BQ4krij8.d.cts → fk-diff-CCLiJ9sJ.d.cts} +2 -1
  8. package/dist/{fk-diff-DsaIijVX.d.mts → fk-diff-R8eU7gq-.d.mts} +2 -1
  9. package/dist/index.cjs +10 -3
  10. package/dist/index.d.cts +66 -4
  11. package/dist/index.d.mts +66 -4
  12. package/dist/index.mjs +3 -3
  13. package/dist/plugin.cjs +143 -36
  14. package/dist/plugin.mjs +138 -31
  15. package/dist/rel.d.cts +2 -2
  16. package/dist/rel.d.mts +2 -2
  17. package/dist/{relation-helpers-kX7jjgME.d.cts → relation-helpers-DjVJN2ng.d.cts} +1 -1
  18. package/dist/{relation-helpers-Ba0v49sn.d.mts → relation-helpers-H7UEVkHs.d.mts} +1 -1
  19. package/dist/search-fields-DBIZKwyb.mjs +117 -0
  20. package/dist/search-fields-DwrsJUC1.cjs +158 -0
  21. package/dist/shared.cjs +3 -1
  22. package/dist/shared.d.cts +19 -1
  23. package/dist/shared.d.mts +19 -1
  24. package/dist/shared.mjs +2 -2
  25. package/dist/sync.cjs +1 -1
  26. package/dist/sync.d.cts +2 -2
  27. package/dist/sync.d.mts +2 -2
  28. package/dist/sync.mjs +1 -1
  29. package/dist/{validation-utils-Dq0uZ7ef.mjs → validation-utils-DqZAH6aT.mjs} +44 -2
  30. package/dist/{validation-utils-Da2GjobR.cjs → validation-utils-debIYpq0.cjs} +56 -2
  31. package/package.json +6 -6
  32. package/dist/derived-rules-0sKn4f5C.mjs +0 -44
  33. package/dist/derived-rules-YstgIxG-.cjs +0 -67
  34. package/dist/numeric-operand-B1jKH7x5.mjs +0 -21
  35. package/dist/numeric-operand-DKfiRLYp.cjs +0 -26
@@ -0,0 +1,117 @@
1
+ //#region src/shared/derived-rules.ts
2
+ /**
3
+ * The rules the compiler plugin and the runtime share about entities and
4
+ * `@db.column.derived` fields — one dependency-free list each, so a
5
+ * compile-time diagnostic and its runtime mirror can never disagree.
6
+ * @since 0.1.141
7
+ */
8
+ /** The annotations that make a declaration a DB entity: a table, or a managed / external view. */
9
+ const DB_ENTITY_ANNOTATIONS = [
10
+ "db.table",
11
+ "db.view",
12
+ "db.view.for"
13
+ ];
14
+ /** Primitive leaf types a JSON-stored path may end at (view JSON leaves, derived columns). */
15
+ const JSON_LEAF_TYPES = new Set([
16
+ "string",
17
+ "number",
18
+ "boolean"
19
+ ]);
20
+ /** Whether `type` (a resolved design type) is one of {@link JSON_LEAF_TYPES}. */
21
+ function isJsonLeafType(type) {
22
+ return JSON_LEAF_TYPES.has(type);
23
+ }
24
+ /** Annotations a `@db.column.derived` field cannot carry (D8), with the reason. */
25
+ const DERIVED_INCOMPATIBLE = [
26
+ ["meta.id", "a computed column cannot identify the row"],
27
+ ["db.rel.FK", "a foreign key needs a stored, writable column"],
28
+ ["db.default", "the value is computed, never defaulted"],
29
+ ["db.default.increment", "the value is computed, never defaulted"],
30
+ ["db.default.uuid", "the value is computed, never defaulted"],
31
+ ["db.default.now", "the value is computed, never defaulted"],
32
+ ["db.column.version", "the version column is adapter-managed"],
33
+ ["db.column.version.exempt", "a derived column is never written"],
34
+ ["db.encrypted", "the value is a cleartext extraction of its source"],
35
+ ["db.json", "a derived column is a primitive leaf, not a JSON value"],
36
+ ["db.ignore", "an ignored field has no column to derive"],
37
+ ["db.writeOnly", "a derived column is never written"],
38
+ ["db.index.fulltext", "fulltext indexes need a stored text column"],
39
+ ["db.index.geo", "a geo index needs a db.geoPoint column"],
40
+ ["db.search.vector", "a vector index needs a stored embedding column"],
41
+ ["db.mongo.search.text", "Atlas Search indexes the stored document, which holds no derived field"],
42
+ ["db.mongo.search.autocomplete", "Atlas Search indexes the stored document, which holds no derived field"]
43
+ ];
44
+ //#endregion
45
+ //#region src/shared/numeric-operand.ts
46
+ /** Primitive tags of timestamps (MySQL may store them as native `TIMESTAMP`). */
47
+ const TIMESTAMP_TAGS = [
48
+ "timestamp",
49
+ "created",
50
+ "updated"
51
+ ];
52
+ /**
53
+ * Why a field cannot be an arithmetic operand, or `undefined` when it can: it
54
+ * must be a `number` — not a decimal (exact; an expression is IEEE double) and
55
+ * not a timestamp-tagged number. The one rule behind `@db.compute` (declared,
56
+ * from the `.as` source) and query-time arithmetic (from the runtime
57
+ * descriptor), so the two cannot diverge. Storage rules (`@db.ignore`,
58
+ * encrypted, JSON) stay with the callers.
59
+ */
60
+ function numericTypeProblem({ base, tags }) {
61
+ if (base !== "number" && base !== "integer") return base === "decimal" ? "is a decimal" : `is a ${base}`;
62
+ if (tags && TIMESTAMP_TAGS.some((t) => tags.has(t))) return "is a timestamp";
63
+ }
64
+ //#endregion
65
+ //#region src/shared/search-fields.ts
66
+ /**
67
+ * Problem suffixes, shared by the plugin (compile time) and the runtime
68
+ * metadata build so both word a refusal identically.
69
+ */
70
+ const SEARCH_PROBLEM = {
71
+ timestamp: "is a timestamp — filter it by range instead",
72
+ now: "is a timestamp (@db.default.now) — filter it by range instead",
73
+ precision: "has @db.column.precision (stored as a decimal)",
74
+ float: "is a floating-point number — declare it number.int (or a sized integer), add @expect.int, or use @db.default.increment",
75
+ decimal: "is a decimal"
76
+ };
77
+ /** Problem for a primitive of another base type (`boolean`, …). */
78
+ function otherBaseProblem(base) {
79
+ return `is a ${base}`;
80
+ }
81
+ /**
82
+ * The one rule behind `@db.column.searchable` / `@db.index.fulltext`:
83
+ * a string (or a non-primitive) is `"text"`; an integer is `"integer"`;
84
+ * floats, decimals, timestamps and other primitives are a `{ problem }`.
85
+ */
86
+ function searchKindOf(d) {
87
+ const { base } = d;
88
+ if (base === void 0 || base === "string") return "text";
89
+ if (base === "decimal") return { problem: SEARCH_PROBLEM.decimal };
90
+ if (base !== "number" && base !== "integer") return { problem: otherBaseProblem(base) };
91
+ if (d.tags && TIMESTAMP_TAGS.some((t) => d.tags.has(t))) return { problem: SEARCH_PROBLEM.timestamp };
92
+ if (d.now) return { problem: SEARCH_PROBLEM.now };
93
+ if (d.precision) return { problem: SEARCH_PROBLEM.precision };
94
+ if (base === "integer" || d.tags?.has("int") || d.expectInt || d.increment) return "integer";
95
+ return { problem: SEARCH_PROBLEM.float };
96
+ }
97
+ /**
98
+ * Runtime mirror of the plugin's verdict: how the annotated type takes part
99
+ * in `$search`. Structures, arrays, unions and unresolved types keep their
100
+ * legacy `"text"` treatment.
101
+ */
102
+ function searchMemberKind(type) {
103
+ const def = type?.type;
104
+ if (!def || def.kind !== "") return "text";
105
+ const metadata = type.metadata;
106
+ const has = (key) => metadata?.has?.(key) === true;
107
+ return searchKindOf({
108
+ base: def.designType,
109
+ tags: def.tags,
110
+ expectInt: has("expect.int"),
111
+ increment: has("db.default.increment"),
112
+ now: has("db.default.now"),
113
+ precision: has("db.column.precision")
114
+ });
115
+ }
116
+ //#endregion
117
+ export { DERIVED_INCOMPATIBLE as a, DB_ENTITY_ANNOTATIONS as i, searchMemberKind as n, JSON_LEAF_TYPES as o, numericTypeProblem as r, isJsonLeafType as s, searchKindOf as t };
@@ -0,0 +1,158 @@
1
+ //#region src/shared/derived-rules.ts
2
+ /**
3
+ * The rules the compiler plugin and the runtime share about entities and
4
+ * `@db.column.derived` fields — one dependency-free list each, so a
5
+ * compile-time diagnostic and its runtime mirror can never disagree.
6
+ * @since 0.1.141
7
+ */
8
+ /** The annotations that make a declaration a DB entity: a table, or a managed / external view. */
9
+ const DB_ENTITY_ANNOTATIONS = [
10
+ "db.table",
11
+ "db.view",
12
+ "db.view.for"
13
+ ];
14
+ /** Primitive leaf types a JSON-stored path may end at (view JSON leaves, derived columns). */
15
+ const JSON_LEAF_TYPES = new Set([
16
+ "string",
17
+ "number",
18
+ "boolean"
19
+ ]);
20
+ /** Whether `type` (a resolved design type) is one of {@link JSON_LEAF_TYPES}. */
21
+ function isJsonLeafType(type) {
22
+ return JSON_LEAF_TYPES.has(type);
23
+ }
24
+ /** Annotations a `@db.column.derived` field cannot carry (D8), with the reason. */
25
+ const DERIVED_INCOMPATIBLE = [
26
+ ["meta.id", "a computed column cannot identify the row"],
27
+ ["db.rel.FK", "a foreign key needs a stored, writable column"],
28
+ ["db.default", "the value is computed, never defaulted"],
29
+ ["db.default.increment", "the value is computed, never defaulted"],
30
+ ["db.default.uuid", "the value is computed, never defaulted"],
31
+ ["db.default.now", "the value is computed, never defaulted"],
32
+ ["db.column.version", "the version column is adapter-managed"],
33
+ ["db.column.version.exempt", "a derived column is never written"],
34
+ ["db.encrypted", "the value is a cleartext extraction of its source"],
35
+ ["db.json", "a derived column is a primitive leaf, not a JSON value"],
36
+ ["db.ignore", "an ignored field has no column to derive"],
37
+ ["db.writeOnly", "a derived column is never written"],
38
+ ["db.index.fulltext", "fulltext indexes need a stored text column"],
39
+ ["db.index.geo", "a geo index needs a db.geoPoint column"],
40
+ ["db.search.vector", "a vector index needs a stored embedding column"],
41
+ ["db.mongo.search.text", "Atlas Search indexes the stored document, which holds no derived field"],
42
+ ["db.mongo.search.autocomplete", "Atlas Search indexes the stored document, which holds no derived field"]
43
+ ];
44
+ //#endregion
45
+ //#region src/shared/numeric-operand.ts
46
+ /** Primitive tags of timestamps (MySQL may store them as native `TIMESTAMP`). */
47
+ const TIMESTAMP_TAGS = [
48
+ "timestamp",
49
+ "created",
50
+ "updated"
51
+ ];
52
+ /**
53
+ * Why a field cannot be an arithmetic operand, or `undefined` when it can: it
54
+ * must be a `number` — not a decimal (exact; an expression is IEEE double) and
55
+ * not a timestamp-tagged number. The one rule behind `@db.compute` (declared,
56
+ * from the `.as` source) and query-time arithmetic (from the runtime
57
+ * descriptor), so the two cannot diverge. Storage rules (`@db.ignore`,
58
+ * encrypted, JSON) stay with the callers.
59
+ */
60
+ function numericTypeProblem({ base, tags }) {
61
+ if (base !== "number" && base !== "integer") return base === "decimal" ? "is a decimal" : `is a ${base}`;
62
+ if (tags && TIMESTAMP_TAGS.some((t) => tags.has(t))) return "is a timestamp";
63
+ }
64
+ //#endregion
65
+ //#region src/shared/search-fields.ts
66
+ /**
67
+ * Problem suffixes, shared by the plugin (compile time) and the runtime
68
+ * metadata build so both word a refusal identically.
69
+ */
70
+ const SEARCH_PROBLEM = {
71
+ timestamp: "is a timestamp — filter it by range instead",
72
+ now: "is a timestamp (@db.default.now) — filter it by range instead",
73
+ precision: "has @db.column.precision (stored as a decimal)",
74
+ float: "is a floating-point number — declare it number.int (or a sized integer), add @expect.int, or use @db.default.increment",
75
+ decimal: "is a decimal"
76
+ };
77
+ /** Problem for a primitive of another base type (`boolean`, …). */
78
+ function otherBaseProblem(base) {
79
+ return `is a ${base}`;
80
+ }
81
+ /**
82
+ * The one rule behind `@db.column.searchable` / `@db.index.fulltext`:
83
+ * a string (or a non-primitive) is `"text"`; an integer is `"integer"`;
84
+ * floats, decimals, timestamps and other primitives are a `{ problem }`.
85
+ */
86
+ function searchKindOf(d) {
87
+ const { base } = d;
88
+ if (base === void 0 || base === "string") return "text";
89
+ if (base === "decimal") return { problem: SEARCH_PROBLEM.decimal };
90
+ if (base !== "number" && base !== "integer") return { problem: otherBaseProblem(base) };
91
+ if (d.tags && TIMESTAMP_TAGS.some((t) => d.tags.has(t))) return { problem: SEARCH_PROBLEM.timestamp };
92
+ if (d.now) return { problem: SEARCH_PROBLEM.now };
93
+ if (d.precision) return { problem: SEARCH_PROBLEM.precision };
94
+ if (base === "integer" || d.tags?.has("int") || d.expectInt || d.increment) return "integer";
95
+ return { problem: SEARCH_PROBLEM.float };
96
+ }
97
+ /**
98
+ * Runtime mirror of the plugin's verdict: how the annotated type takes part
99
+ * in `$search`. Structures, arrays, unions and unresolved types keep their
100
+ * legacy `"text"` treatment.
101
+ */
102
+ function searchMemberKind(type) {
103
+ const def = type?.type;
104
+ if (!def || def.kind !== "") return "text";
105
+ const metadata = type.metadata;
106
+ const has = (key) => metadata?.has?.(key) === true;
107
+ return searchKindOf({
108
+ base: def.designType,
109
+ tags: def.tags,
110
+ expectInt: has("expect.int"),
111
+ increment: has("db.default.increment"),
112
+ now: has("db.default.now"),
113
+ precision: has("db.column.precision")
114
+ });
115
+ }
116
+ //#endregion
117
+ Object.defineProperty(exports, "DB_ENTITY_ANNOTATIONS", {
118
+ enumerable: true,
119
+ get: function() {
120
+ return DB_ENTITY_ANNOTATIONS;
121
+ }
122
+ });
123
+ Object.defineProperty(exports, "DERIVED_INCOMPATIBLE", {
124
+ enumerable: true,
125
+ get: function() {
126
+ return DERIVED_INCOMPATIBLE;
127
+ }
128
+ });
129
+ Object.defineProperty(exports, "JSON_LEAF_TYPES", {
130
+ enumerable: true,
131
+ get: function() {
132
+ return JSON_LEAF_TYPES;
133
+ }
134
+ });
135
+ Object.defineProperty(exports, "isJsonLeafType", {
136
+ enumerable: true,
137
+ get: function() {
138
+ return isJsonLeafType;
139
+ }
140
+ });
141
+ Object.defineProperty(exports, "numericTypeProblem", {
142
+ enumerable: true,
143
+ get: function() {
144
+ return numericTypeProblem;
145
+ }
146
+ });
147
+ Object.defineProperty(exports, "searchKindOf", {
148
+ enumerable: true,
149
+ get: function() {
150
+ return searchKindOf;
151
+ }
152
+ });
153
+ Object.defineProperty(exports, "searchMemberKind", {
154
+ enumerable: true,
155
+ get: function() {
156
+ return searchMemberKind;
157
+ }
158
+ });
package/dist/shared.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_aggregate_rules = require("./aggregate-rules-D_bsCpUI.cjs");
3
- const require_validation_utils = require("./validation-utils-Da2GjobR.cjs");
3
+ const require_validation_utils = require("./validation-utils-debIYpq0.cjs");
4
4
  exports.NOT_DIMENSION_REASON = require_aggregate_rules.NOT_DIMENSION_REASON;
5
5
  exports.earlierJoinTargets = require_validation_utils.earlierJoinTargets;
6
6
  exports.findFKFieldsPointingTo = require_validation_utils.findFKFieldsPointingTo;
@@ -12,12 +12,14 @@ exports.getNavTargetTypeName = require_validation_utils.getNavTargetTypeName;
12
12
  exports.getParentStruct = require_validation_utils.getParentStruct;
13
13
  exports.getParentTypeName = require_validation_utils.getParentTypeName;
14
14
  exports.hasAnyViewAnnotation = require_validation_utils.hasAnyViewAnnotation;
15
+ exports.integerMemberIndexProblem = require_validation_utils.integerMemberIndexProblem;
15
16
  exports.isAliasDecl = require_validation_utils.isAliasDecl;
16
17
  exports.isDbSourceDecl = require_validation_utils.isDbSourceDecl;
17
18
  exports.isStrictAggregateStruct = require_aggregate_rules.isStrictAggregateStruct;
18
19
  exports.joinTargets = require_validation_utils.joinTargets;
19
20
  exports.primitiveBaseType = require_validation_utils.primitiveBaseType;
20
21
  exports.refActionAnnotation = require_validation_utils.refActionAnnotation;
22
+ exports.searchFieldVerdict = require_validation_utils.searchFieldVerdict;
21
23
  exports.validateExclusiveWith = require_validation_utils.validateExclusiveWith;
22
24
  exports.validateFieldBaseType = require_validation_utils.validateFieldBaseType;
23
25
  exports.validateQueryScope = require_validation_utils.validateQueryScope;
package/dist/shared.d.cts CHANGED
@@ -8,6 +8,24 @@ declare function validateExclusiveWith(token: Token, selfName: string, others: A
8
8
  }>): TMessages;
9
9
  /** Primitive base type (`string`, `number`, `decimal`, …) of a resolved definition, if it is a primitive. */
10
10
  declare function primitiveBaseType(def: SemanticNode | undefined): string | undefined;
11
+ /**
12
+ * Whether a prop takes part in `$search` as text or as an integer, or why it
13
+ * cannot (compile-time mirror of the runtime `searchMemberKind`). Anything
14
+ * that is not a ref to a primitive (structures, arrays, unions, unresolved
15
+ * refs) is left alone.
16
+ */
17
+ declare function searchFieldVerdict(field: SemanticNode, doc: AtscriptDoc): {
18
+ kind: "string" | "integer" | "other";
19
+ } | {
20
+ problem: string;
21
+ };
22
+ /**
23
+ * Why an integer fulltext member is not index-backed (its exact-number match
24
+ * would scan — or fail on MongoDB next to `$text`), or `undefined`. Backed:
25
+ * `_id`, the first `@meta.id` of the type, or the first field (in declaration
26
+ * order) of a `@db.index.plain` / `@db.index.unique` group.
27
+ */
28
+ declare function integerMemberIndexProblem(token: Token): string | undefined;
11
29
  /** Asserts `args[0]` names a sibling property whose primitive base type is `string`. */
12
30
  declare function validateSiblingStringField(token: Token, args: Token[], doc: AtscriptDoc, selfName: string): TMessages;
13
31
  /**
@@ -139,4 +157,4 @@ declare const NOT_DIMENSION_REASON = "not a dimension";
139
157
  */
140
158
  declare function isStrictAggregateStruct(struct: SemanticStructureNode): boolean;
141
159
  //#endregion
142
- export { NOT_DIMENSION_REASON, TFKFieldMatch, TRefArgumentOptions, earlierJoinTargets, findFKFieldsPointingTo, findViewCycle, forEachFieldRef, getAnnotationAlias, getDbTableOwner, getNavTargetTypeName, getParentStruct, getParentTypeName, hasAnyViewAnnotation, isAliasDecl, isDbSourceDecl, isStrictAggregateStruct, joinTargets, primitiveBaseType, refActionAnnotation, validateExclusiveWith, validateFieldBaseType, validateQueryScope, validateRefArgument, validateSiblingStringField, viewJoins, viewScopeTypes };
160
+ export { NOT_DIMENSION_REASON, TFKFieldMatch, TRefArgumentOptions, earlierJoinTargets, findFKFieldsPointingTo, findViewCycle, forEachFieldRef, getAnnotationAlias, getDbTableOwner, getNavTargetTypeName, getParentStruct, getParentTypeName, hasAnyViewAnnotation, integerMemberIndexProblem, isAliasDecl, isDbSourceDecl, isStrictAggregateStruct, joinTargets, primitiveBaseType, refActionAnnotation, searchFieldVerdict, validateExclusiveWith, validateFieldBaseType, validateQueryScope, validateRefArgument, validateSiblingStringField, viewJoins, viewScopeTypes };
package/dist/shared.d.mts CHANGED
@@ -8,6 +8,24 @@ declare function validateExclusiveWith(token: Token, selfName: string, others: A
8
8
  }>): TMessages;
9
9
  /** Primitive base type (`string`, `number`, `decimal`, …) of a resolved definition, if it is a primitive. */
10
10
  declare function primitiveBaseType(def: SemanticNode | undefined): string | undefined;
11
+ /**
12
+ * Whether a prop takes part in `$search` as text or as an integer, or why it
13
+ * cannot (compile-time mirror of the runtime `searchMemberKind`). Anything
14
+ * that is not a ref to a primitive (structures, arrays, unions, unresolved
15
+ * refs) is left alone.
16
+ */
17
+ declare function searchFieldVerdict(field: SemanticNode, doc: AtscriptDoc): {
18
+ kind: "string" | "integer" | "other";
19
+ } | {
20
+ problem: string;
21
+ };
22
+ /**
23
+ * Why an integer fulltext member is not index-backed (its exact-number match
24
+ * would scan — or fail on MongoDB next to `$text`), or `undefined`. Backed:
25
+ * `_id`, the first `@meta.id` of the type, or the first field (in declaration
26
+ * order) of a `@db.index.plain` / `@db.index.unique` group.
27
+ */
28
+ declare function integerMemberIndexProblem(token: Token): string | undefined;
11
29
  /** Asserts `args[0]` names a sibling property whose primitive base type is `string`. */
12
30
  declare function validateSiblingStringField(token: Token, args: Token[], doc: AtscriptDoc, selfName: string): TMessages;
13
31
  /**
@@ -139,4 +157,4 @@ declare const NOT_DIMENSION_REASON = "not a dimension";
139
157
  */
140
158
  declare function isStrictAggregateStruct(struct: SemanticStructureNode): boolean;
141
159
  //#endregion
142
- export { NOT_DIMENSION_REASON, TFKFieldMatch, TRefArgumentOptions, earlierJoinTargets, findFKFieldsPointingTo, findViewCycle, forEachFieldRef, getAnnotationAlias, getDbTableOwner, getNavTargetTypeName, getParentStruct, getParentTypeName, hasAnyViewAnnotation, isAliasDecl, isDbSourceDecl, isStrictAggregateStruct, joinTargets, primitiveBaseType, refActionAnnotation, validateExclusiveWith, validateFieldBaseType, validateQueryScope, validateRefArgument, validateSiblingStringField, viewJoins, viewScopeTypes };
160
+ export { NOT_DIMENSION_REASON, TFKFieldMatch, TRefArgumentOptions, earlierJoinTargets, findFKFieldsPointingTo, findViewCycle, forEachFieldRef, getAnnotationAlias, getDbTableOwner, getNavTargetTypeName, getParentStruct, getParentTypeName, hasAnyViewAnnotation, integerMemberIndexProblem, isAliasDecl, isDbSourceDecl, isStrictAggregateStruct, joinTargets, primitiveBaseType, refActionAnnotation, searchFieldVerdict, validateExclusiveWith, validateFieldBaseType, validateQueryScope, validateRefArgument, validateSiblingStringField, viewJoins, viewScopeTypes };
package/dist/shared.mjs CHANGED
@@ -1,3 +1,3 @@
1
1
  import { n as isStrictAggregateStruct, t as NOT_DIMENSION_REASON } from "./aggregate-rules-jdPrxqWa.mjs";
2
- import { S as validateSiblingStringField, _ as getParentTypeName, a as hasAnyViewAnnotation, b as validateExclusiveWith, c as joinTargets, d as viewJoins, f as viewScopeTypes, g as getParentStruct, h as getNavTargetTypeName, i as forEachFieldRef, l as validateQueryScope, m as getDbTableOwner, n as findFKFieldsPointingTo, o as isAliasDecl, p as getAnnotationAlias, r as findViewCycle, s as isDbSourceDecl, t as earlierJoinTargets, u as validateRefArgument, v as primitiveBaseType, x as validateFieldBaseType, y as refActionAnnotation } from "./validation-utils-Dq0uZ7ef.mjs";
3
- export { NOT_DIMENSION_REASON, earlierJoinTargets, findFKFieldsPointingTo, findViewCycle, forEachFieldRef, getAnnotationAlias, getDbTableOwner, getNavTargetTypeName, getParentStruct, getParentTypeName, hasAnyViewAnnotation, isAliasDecl, isDbSourceDecl, isStrictAggregateStruct, joinTargets, primitiveBaseType, refActionAnnotation, validateExclusiveWith, validateFieldBaseType, validateQueryScope, validateRefArgument, validateSiblingStringField, viewJoins, viewScopeTypes };
2
+ import { C as validateFieldBaseType, S as validateExclusiveWith, _ as getParentTypeName, a as hasAnyViewAnnotation, b as refActionAnnotation, c as joinTargets, d as viewJoins, f as viewScopeTypes, g as getParentStruct, h as getNavTargetTypeName, i as forEachFieldRef, l as validateQueryScope, m as getDbTableOwner, n as findFKFieldsPointingTo, o as isAliasDecl, p as getAnnotationAlias, r as findViewCycle, s as isDbSourceDecl, t as earlierJoinTargets, u as validateRefArgument, v as integerMemberIndexProblem, w as validateSiblingStringField, x as searchFieldVerdict, y as primitiveBaseType } from "./validation-utils-DqZAH6aT.mjs";
3
+ export { NOT_DIMENSION_REASON, earlierJoinTargets, findFKFieldsPointingTo, findViewCycle, forEachFieldRef, getAnnotationAlias, getDbTableOwner, getNavTargetTypeName, getParentStruct, getParentTypeName, hasAnyViewAnnotation, integerMemberIndexProblem, isAliasDecl, isDbSourceDecl, isStrictAggregateStruct, joinTargets, primitiveBaseType, refActionAnnotation, searchFieldVerdict, validateExclusiveWith, validateFieldBaseType, validateQueryScope, validateRefArgument, validateSiblingStringField, viewJoins, viewScopeTypes };
package/dist/sync.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_db_error = require("./db-error-DRQH4sLY.cjs");
3
- const require_column_diff = require("./column-diff-CUU4GvYg.cjs");
3
+ const require_column_diff = require("./column-diff-CKKBVDvx.cjs");
4
4
  //#region src/schema/table-option-diff.ts
5
5
  /**
6
6
  * Computes the difference between desired and existing table options.
package/dist/sync.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { Dt as TReferencingForeignKey, Et as TPrimaryKeyChange, H as TDbFieldMeta, Mt as TTableOptionDiff, Q as TDbObjectKind, Vn as TGenericLogger, Xt as DbSpace, dt as TDerivedChangeReason, gt as TExistingTableOption, ht as TExistingForeignKey, pt as TEnsureTableOptions } from "./buckets-DJiYlMXc.cjs";
2
- import { _ as computeTableSnapshot, a as hasForeignKeyChanges, b as snapshotToExistingTableOptions, c as TFieldSnapshot, d as TViewColumnSnapshot, f as TViewJoinSnapshot, g as computeTableHash, h as computeSchemaHash, i as fkKey, l as TForeignKeySnapshot, m as canonicalizeQueryNode, n as computeForeignKeyDiff, o as computeColumnDiff, p as TViewSnapshot, t as TForeignKeyDiff, u as TTableSnapshot, v as computeViewSnapshot, y as snapshotToExistingColumns } from "./fk-diff-BQ4krij8.cjs";
1
+ import { Dt as TPrimaryKeyChange, H as TDbFieldMeta, Hn as TGenericLogger, Nt as TTableOptionDiff, Ot as TReferencingForeignKey, Q as TDbObjectKind, Zt as DbSpace, _t as TExistingTableOption, ft as TDerivedChangeReason, gt as TExistingForeignKey, mt as TEnsureTableOptions } from "./buckets-24Ac4sIS.cjs";
2
+ import { _ as computeTableSnapshot, a as hasForeignKeyChanges, b as snapshotToExistingTableOptions, c as TFieldSnapshot, d as TViewColumnSnapshot, f as TViewJoinSnapshot, g as computeTableHash, h as computeSchemaHash, i as fkKey, l as TForeignKeySnapshot, m as canonicalizeQueryNode, n as computeForeignKeyDiff, o as computeColumnDiff, p as TViewSnapshot, t as TForeignKeyDiff, u as TTableSnapshot, v as computeViewSnapshot, y as snapshotToExistingColumns } from "./fk-diff-CCLiJ9sJ.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 { Dt as TReferencingForeignKey, Et as TPrimaryKeyChange, H as TDbFieldMeta, Mt as TTableOptionDiff, Q as TDbObjectKind, Vn as TGenericLogger, Xt as DbSpace, dt as TDerivedChangeReason, gt as TExistingTableOption, ht as TExistingForeignKey, pt as TEnsureTableOptions } from "./buckets-CNdTOnei.mjs";
2
- import { _ as computeTableSnapshot, a as hasForeignKeyChanges, b as snapshotToExistingTableOptions, c as TFieldSnapshot, d as TViewColumnSnapshot, f as TViewJoinSnapshot, g as computeTableHash, h as computeSchemaHash, i as fkKey, l as TForeignKeySnapshot, m as canonicalizeQueryNode, n as computeForeignKeyDiff, o as computeColumnDiff, p as TViewSnapshot, t as TForeignKeyDiff, u as TTableSnapshot, v as computeViewSnapshot, y as snapshotToExistingColumns } from "./fk-diff-DsaIijVX.mjs";
1
+ import { Dt as TPrimaryKeyChange, H as TDbFieldMeta, Hn as TGenericLogger, Nt as TTableOptionDiff, Ot as TReferencingForeignKey, Q as TDbObjectKind, Zt as DbSpace, _t as TExistingTableOption, ft as TDerivedChangeReason, gt as TExistingForeignKey, mt as TEnsureTableOptions } from "./buckets-XOz0Bw5x.mjs";
2
+ import { _ as computeTableSnapshot, a as hasForeignKeyChanges, b as snapshotToExistingTableOptions, c as TFieldSnapshot, d as TViewColumnSnapshot, f as TViewJoinSnapshot, g as computeTableHash, h as computeSchemaHash, i as fkKey, l as TForeignKeySnapshot, m as canonicalizeQueryNode, n as computeForeignKeyDiff, o as computeColumnDiff, p as TViewSnapshot, t as TForeignKeyDiff, u as TTableSnapshot, v as computeViewSnapshot, y as snapshotToExistingColumns } from "./fk-diff-R8eU7gq-.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,5 +1,5 @@
1
1
  import { r as DbError } from "./db-error-Az85UhTX.mjs";
2
- import { a as computeTableHash, at as aliasTargetOf, c as snapshotToExistingColumns, d as computeForeignKeyDiff, dt as NoopLogger, f as fkColumns, i as computeSchemaHash, l as snapshotToExistingTableOptions, m as hasForeignKeyChanges, o as computeTableSnapshot, p as fkKey, r as canonicalizeQueryNode, s as computeViewSnapshot, t as computeColumnDiff, u as viewSnapshotSources } from "./column-diff-Cp6ZoyRE.mjs";
2
+ import { _t as NoopLogger, a as computeTableHash, c as snapshotToExistingColumns, d as computeForeignKeyDiff, dt as aliasTargetOf, f as fkColumns, i as computeSchemaHash, l as snapshotToExistingTableOptions, m as hasForeignKeyChanges, o as computeTableSnapshot, p as fkKey, r as canonicalizeQueryNode, s as computeViewSnapshot, t as computeColumnDiff, u as viewSnapshotSources } from "./column-diff-CKLDWBbo.mjs";
3
3
  //#region src/schema/table-option-diff.ts
4
4
  /**
5
5
  * Computes the difference between desired and existing table options.
@@ -1,4 +1,4 @@
1
- import { t as DB_ENTITY_ANNOTATIONS } from "./derived-rules-0sKn4f5C.mjs";
1
+ import { i as DB_ENTITY_ANNOTATIONS, t as searchKindOf } from "./search-fields-DBIZKwyb.mjs";
2
2
  import { AnnotationSpec, isArray, isInterface, isPrimitive, isQueryComparison, isQueryLogical, isRef, isStructure } from "@atscript/core";
3
3
  //#region src/shared/annotation-utils.ts
4
4
  /** Asserts the field carrying this annotation does not also carry any of `others`. */
@@ -24,6 +24,48 @@ function getPrimitiveBaseType(node, doc) {
24
24
  if (!def || !isRef(def)) return void 0;
25
25
  return primitiveBaseType(doc.unwindType(def.id, def.chain)?.def);
26
26
  }
27
+ /**
28
+ * Whether a prop takes part in `$search` as text or as an integer, or why it
29
+ * cannot (compile-time mirror of the runtime `searchMemberKind`). Anything
30
+ * that is not a ref to a primitive (structures, arrays, unions, unresolved
31
+ * refs) is left alone.
32
+ */
33
+ function searchFieldVerdict(field, doc) {
34
+ const def = field.getDefinition();
35
+ if (!def || !isRef(def)) return { kind: "other" };
36
+ const leaf = doc.unwindType(def.id, def.chain)?.def;
37
+ const base = primitiveBaseType(leaf);
38
+ if (base === void 0) return { kind: "other" };
39
+ const kind = searchKindOf({
40
+ base,
41
+ tags: isPrimitive(leaf) ? leaf.tags : void 0,
42
+ expectInt: field.countAnnotations("expect.int") > 0,
43
+ increment: field.countAnnotations("db.default.increment") > 0,
44
+ now: field.countAnnotations("db.default.now") > 0,
45
+ precision: field.countAnnotations("db.column.precision") > 0
46
+ });
47
+ if (typeof kind === "object") return kind;
48
+ return { kind: kind === "integer" ? "integer" : base === "string" ? "string" : "other" };
49
+ }
50
+ /**
51
+ * Why an integer fulltext member is not index-backed (its exact-number match
52
+ * would scan — or fail on MongoDB next to `$text`), or `undefined`. Backed:
53
+ * `_id`, the first `@meta.id` of the type, or the first field (in declaration
54
+ * order) of a `@db.index.plain` / `@db.index.unique` group.
55
+ */
56
+ function integerMemberIndexProblem(token) {
57
+ const field = token.parentNode;
58
+ const struct = getParentStruct(token);
59
+ if (!struct) return void 0;
60
+ if (field.id === "_id") return void 0;
61
+ const props = [...struct.props.values()];
62
+ if (field.countAnnotations("meta.id") > 0 && props.find((p) => p.countAnnotations("meta.id") > 0) === field) return;
63
+ for (const ann of ["db.index.plain", "db.index.unique"]) for (const own of field.annotations?.filter((a) => a.name === ann) ?? []) {
64
+ const group = own.args[0]?.text ?? field.id;
65
+ if (props.find((p) => p.annotations?.some((a) => a.name === ann && (a.args[0]?.text ?? p.id) === group)) === field) return void 0;
66
+ }
67
+ return `@db.index.fulltext on the integer field "${field.id}" needs an index for its exact-number match — make it the primary key (first @meta.id) or the first field of a @db.index.plain / @db.index.unique`;
68
+ }
27
69
  /** Asserts `args[0]` names a sibling property whose primitive base type is `string`. */
28
70
  function validateSiblingStringField(token, args, doc, selfName) {
29
71
  const errors = [];
@@ -408,4 +450,4 @@ function forEachFieldRef(expr, fn) {
408
450
  }
409
451
  }
410
452
  //#endregion
411
- export { validateSiblingStringField as S, getParentTypeName as _, hasAnyViewAnnotation as a, validateExclusiveWith as b, joinTargets as c, viewJoins as d, viewScopeTypes as f, getParentStruct as g, getNavTargetTypeName as h, forEachFieldRef as i, validateQueryScope as l, getDbTableOwner as m, findFKFieldsPointingTo as n, isAliasDecl as o, getAnnotationAlias as p, findViewCycle as r, isDbSourceDecl as s, earlierJoinTargets as t, validateRefArgument as u, primitiveBaseType as v, validateFieldBaseType as x, refActionAnnotation as y };
453
+ export { validateFieldBaseType as C, validateExclusiveWith as S, getParentTypeName as _, hasAnyViewAnnotation as a, refActionAnnotation as b, joinTargets as c, viewJoins as d, viewScopeTypes as f, getParentStruct as g, getNavTargetTypeName as h, forEachFieldRef as i, validateQueryScope as l, getDbTableOwner as m, findFKFieldsPointingTo as n, isAliasDecl as o, getAnnotationAlias as p, findViewCycle as r, isDbSourceDecl as s, earlierJoinTargets as t, validateRefArgument as u, integerMemberIndexProblem as v, validateSiblingStringField as w, searchFieldVerdict as x, primitiveBaseType as y };
@@ -1,4 +1,4 @@
1
- const require_derived_rules = require("./derived-rules-YstgIxG-.cjs");
1
+ const require_search_fields = require("./search-fields-DwrsJUC1.cjs");
2
2
  let _atscript_core = require("@atscript/core");
3
3
  //#region src/shared/annotation-utils.ts
4
4
  /** Asserts the field carrying this annotation does not also carry any of `others`. */
@@ -24,6 +24,48 @@ function getPrimitiveBaseType(node, doc) {
24
24
  if (!def || !(0, _atscript_core.isRef)(def)) return void 0;
25
25
  return primitiveBaseType(doc.unwindType(def.id, def.chain)?.def);
26
26
  }
27
+ /**
28
+ * Whether a prop takes part in `$search` as text or as an integer, or why it
29
+ * cannot (compile-time mirror of the runtime `searchMemberKind`). Anything
30
+ * that is not a ref to a primitive (structures, arrays, unions, unresolved
31
+ * refs) is left alone.
32
+ */
33
+ function searchFieldVerdict(field, doc) {
34
+ const def = field.getDefinition();
35
+ if (!def || !(0, _atscript_core.isRef)(def)) return { kind: "other" };
36
+ const leaf = doc.unwindType(def.id, def.chain)?.def;
37
+ const base = primitiveBaseType(leaf);
38
+ if (base === void 0) return { kind: "other" };
39
+ const kind = require_search_fields.searchKindOf({
40
+ base,
41
+ tags: (0, _atscript_core.isPrimitive)(leaf) ? leaf.tags : void 0,
42
+ expectInt: field.countAnnotations("expect.int") > 0,
43
+ increment: field.countAnnotations("db.default.increment") > 0,
44
+ now: field.countAnnotations("db.default.now") > 0,
45
+ precision: field.countAnnotations("db.column.precision") > 0
46
+ });
47
+ if (typeof kind === "object") return kind;
48
+ return { kind: kind === "integer" ? "integer" : base === "string" ? "string" : "other" };
49
+ }
50
+ /**
51
+ * Why an integer fulltext member is not index-backed (its exact-number match
52
+ * would scan — or fail on MongoDB next to `$text`), or `undefined`. Backed:
53
+ * `_id`, the first `@meta.id` of the type, or the first field (in declaration
54
+ * order) of a `@db.index.plain` / `@db.index.unique` group.
55
+ */
56
+ function integerMemberIndexProblem(token) {
57
+ const field = token.parentNode;
58
+ const struct = getParentStruct(token);
59
+ if (!struct) return void 0;
60
+ if (field.id === "_id") return void 0;
61
+ const props = [...struct.props.values()];
62
+ if (field.countAnnotations("meta.id") > 0 && props.find((p) => p.countAnnotations("meta.id") > 0) === field) return;
63
+ for (const ann of ["db.index.plain", "db.index.unique"]) for (const own of field.annotations?.filter((a) => a.name === ann) ?? []) {
64
+ const group = own.args[0]?.text ?? field.id;
65
+ if (props.find((p) => p.annotations?.some((a) => a.name === ann && (a.args[0]?.text ?? p.id) === group)) === field) return void 0;
66
+ }
67
+ return `@db.index.fulltext on the integer field "${field.id}" needs an index for its exact-number match — make it the primary key (first @meta.id) or the first field of a @db.index.plain / @db.index.unique`;
68
+ }
27
69
  /** Asserts `args[0]` names a sibling property whose primitive base type is `string`. */
28
70
  function validateSiblingStringField(token, args, doc, selfName) {
29
71
  const errors = [];
@@ -180,7 +222,7 @@ function refActionAnnotation(name) {
180
222
  //#region src/shared/validation-utils.ts
181
223
  /** Whether a declaration node is a `@db.table` or a `@db.view` (managed or external). */
182
224
  function isDbSourceDecl(node) {
183
- return require_derived_rules.DB_ENTITY_ANNOTATIONS.some((name) => node.countAnnotations(name) > 0);
225
+ return require_search_fields.DB_ENTITY_ANNOTATIONS.some((name) => node.countAnnotations(name) > 0);
184
226
  }
185
227
  /** Whether a declaration node is a `@db.alias` type. @since 0.1.141 */
186
228
  function isAliasDecl(node) {
@@ -468,6 +510,12 @@ Object.defineProperty(exports, "hasAnyViewAnnotation", {
468
510
  return hasAnyViewAnnotation;
469
511
  }
470
512
  });
513
+ Object.defineProperty(exports, "integerMemberIndexProblem", {
514
+ enumerable: true,
515
+ get: function() {
516
+ return integerMemberIndexProblem;
517
+ }
518
+ });
471
519
  Object.defineProperty(exports, "isAliasDecl", {
472
520
  enumerable: true,
473
521
  get: function() {
@@ -498,6 +546,12 @@ Object.defineProperty(exports, "refActionAnnotation", {
498
546
  return refActionAnnotation;
499
547
  }
500
548
  });
549
+ Object.defineProperty(exports, "searchFieldVerdict", {
550
+ enumerable: true,
551
+ get: function() {
552
+ return searchFieldVerdict;
553
+ }
554
+ });
501
555
  Object.defineProperty(exports, "validateExclusiveWith", {
502
556
  enumerable: true,
503
557
  get: function() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atscript/db",
3
- "version": "0.1.148",
3
+ "version": "0.1.150",
4
4
  "description": "Database adapter utilities for atscript.",
5
5
  "keywords": [
6
6
  "atscript",
@@ -71,14 +71,14 @@
71
71
  "access": "public"
72
72
  },
73
73
  "devDependencies": {
74
- "@atscript/core": "^0.1.100",
75
- "@atscript/typescript": "^0.1.100",
74
+ "@atscript/core": "^0.1.101",
75
+ "@atscript/typescript": "^0.1.101",
76
76
  "@uniqu/core": "^0.1.13",
77
- "unplugin-atscript": "^0.1.100"
77
+ "unplugin-atscript": "^0.1.101"
78
78
  },
79
79
  "peerDependencies": {
80
- "@atscript/core": "^0.1.100",
81
- "@atscript/typescript": "^0.1.100",
80
+ "@atscript/core": "^0.1.101",
81
+ "@atscript/typescript": "^0.1.101",
82
82
  "@uniqu/core": "^0.1.13"
83
83
  },
84
84
  "scripts": {
@@ -1,44 +0,0 @@
1
- //#region src/shared/derived-rules.ts
2
- /**
3
- * The rules the compiler plugin and the runtime share about entities and
4
- * `@db.column.derived` fields — one dependency-free list each, so a
5
- * compile-time diagnostic and its runtime mirror can never disagree.
6
- * @since 0.1.141
7
- */
8
- /** The annotations that make a declaration a DB entity: a table, or a managed / external view. */
9
- const DB_ENTITY_ANNOTATIONS = [
10
- "db.table",
11
- "db.view",
12
- "db.view.for"
13
- ];
14
- /** Primitive leaf types a JSON-stored path may end at (view JSON leaves, derived columns). */
15
- const JSON_LEAF_TYPES = new Set([
16
- "string",
17
- "number",
18
- "boolean"
19
- ]);
20
- /** Whether `type` (a resolved design type) is one of {@link JSON_LEAF_TYPES}. */
21
- function isJsonLeafType(type) {
22
- return JSON_LEAF_TYPES.has(type);
23
- }
24
- /** Annotations a `@db.column.derived` field cannot carry (D8), with the reason. */
25
- const DERIVED_INCOMPATIBLE = [
26
- ["meta.id", "a computed column cannot identify the row"],
27
- ["db.rel.FK", "a foreign key needs a stored, writable column"],
28
- ["db.default", "the value is computed, never defaulted"],
29
- ["db.default.increment", "the value is computed, never defaulted"],
30
- ["db.default.uuid", "the value is computed, never defaulted"],
31
- ["db.default.now", "the value is computed, never defaulted"],
32
- ["db.column.version", "the version column is adapter-managed"],
33
- ["db.encrypted", "the value is a cleartext extraction of its source"],
34
- ["db.json", "a derived column is a primitive leaf, not a JSON value"],
35
- ["db.ignore", "an ignored field has no column to derive"],
36
- ["db.writeOnly", "a derived column is never written"],
37
- ["db.index.fulltext", "fulltext indexes need a stored text column"],
38
- ["db.index.geo", "a geo index needs a db.geoPoint column"],
39
- ["db.search.vector", "a vector index needs a stored embedding column"],
40
- ["db.mongo.search.text", "Atlas Search indexes the stored document, which holds no derived field"],
41
- ["db.mongo.search.autocomplete", "Atlas Search indexes the stored document, which holds no derived field"]
42
- ];
43
- //#endregion
44
- export { isJsonLeafType as i, DERIVED_INCOMPATIBLE as n, JSON_LEAF_TYPES as r, DB_ENTITY_ANNOTATIONS as t };