@metaobjectsdev/metadata 0.15.0 → 0.15.1
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.
- package/dist/core/identity/identity-constants.d.ts +0 -2
- package/dist/core/identity/identity-constants.d.ts.map +1 -1
- package/dist/core/identity/identity-constants.js +0 -2
- package/dist/core/identity/identity-constants.js.map +1 -1
- package/dist/core/identity/identity-definition.embedded.d.ts.map +1 -1
- package/dist/core/identity/identity-definition.embedded.js +3 -11
- package/dist/core/identity/identity-definition.embedded.js.map +1 -1
- package/dist/core/identity/meta-identity.d.ts +2 -7
- package/dist/core/identity/meta-identity.d.ts.map +1 -1
- package/dist/core/identity/meta-identity.js +3 -10
- package/dist/core/identity/meta-identity.js.map +1 -1
- package/dist/core/index/index-constants.d.ts +11 -0
- package/dist/core/index/index-constants.d.ts.map +1 -0
- package/dist/core/index/index-constants.js +23 -0
- package/dist/core/index/index-constants.js.map +1 -0
- package/dist/core/index/index-definition.embedded.d.ts +3 -0
- package/dist/core/index/index-definition.embedded.d.ts.map +1 -0
- package/dist/core/index/index-definition.embedded.js +23 -0
- package/dist/core/index/index-definition.embedded.js.map +1 -0
- package/dist/core/index/meta-index.d.ts +7 -0
- package/dist/core/index/meta-index.d.ts.map +1 -0
- package/dist/core/index/meta-index.js +17 -0
- package/dist/core/index/meta-index.js.map +1 -0
- package/dist/core/object/meta-object.d.ts +3 -0
- package/dist/core/object/meta-object.d.ts.map +1 -1
- package/dist/core/object/meta-object.js +5 -1
- package/dist/core/object/meta-object.js.map +1 -1
- package/dist/core/object/object-definition.embedded.d.ts.map +1 -1
- package/dist/core/object/object-definition.embedded.js +7 -0
- package/dist/core/object/object-definition.embedded.js.map +1 -1
- package/dist/core-types.d.ts.map +1 -1
- package/dist/core-types.js +15 -1
- package/dist/core-types.js.map +1 -1
- package/dist/errors.d.ts +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +3 -0
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/loader/meta-data-loader.d.ts.map +1 -1
- package/dist/loader/meta-data-loader.js +5 -1
- package/dist/loader/meta-data-loader.js.map +1 -1
- package/dist/loader/validation-passes.d.ts +1 -0
- package/dist/loader/validation-passes.d.ts.map +1 -1
- package/dist/loader/validation-passes.js +39 -1
- package/dist/loader/validation-passes.js.map +1 -1
- package/dist/persistence/db/db-definition.embedded.d.ts.map +1 -1
- package/dist/persistence/db/db-definition.embedded.js +43 -0
- package/dist/persistence/db/db-definition.embedded.js.map +1 -1
- package/dist/shared/base-types.d.ts +3 -1
- package/dist/shared/base-types.d.ts.map +1 -1
- package/dist/shared/base-types.js +3 -0
- package/dist/shared/base-types.js.map +1 -1
- package/package.json +1 -1
- package/src/core/identity/identity-constants.ts +0 -2
- package/src/core/identity/identity-definition.embedded.ts +3 -11
- package/src/core/identity/meta-identity.ts +2 -11
- package/src/core/index/index-constants.ts +31 -0
- package/src/core/index/index-definition.embedded.ts +25 -0
- package/src/core/index/meta-index.ts +22 -0
- package/src/core/object/meta-object.ts +9 -0
- package/src/core/object/object-definition.embedded.ts +7 -0
- package/src/core-types.ts +18 -0
- package/src/errors.ts +3 -0
- package/src/index.ts +5 -0
- package/src/loader/meta-data-loader.ts +6 -1
- package/src/loader/validation-passes.ts +60 -0
- package/src/persistence/db/db-definition.embedded.ts +43 -0
- package/src/shared/base-types.ts +3 -0
|
@@ -10,7 +10,6 @@ import {
|
|
|
10
10
|
IDENTITY_SUBTYPE_REFERENCE,
|
|
11
11
|
IDENTITY_ATTR_FIELDS,
|
|
12
12
|
IDENTITY_ATTR_GENERATION,
|
|
13
|
-
IDENTITY_ATTR_UNIQUE,
|
|
14
13
|
IDENTITY_REFERENCE_ATTR_REFERENCES,
|
|
15
14
|
IDENTITY_REFERENCE_ATTR_ENFORCE,
|
|
16
15
|
IDENTITY_REFERENCE_ATTR_ON_DELETE,
|
|
@@ -28,15 +27,6 @@ export class MetaIdentity extends MetaData {
|
|
|
28
27
|
return Array.isArray(f) ? (f as string[]) : [];
|
|
29
28
|
}
|
|
30
29
|
|
|
31
|
-
/**
|
|
32
|
-
* Whether the identity enforces uniqueness.
|
|
33
|
-
* Defaults to true; explicit `@unique: false` makes it a non-unique index.
|
|
34
|
-
* ADR-0039: resolving.
|
|
35
|
-
*/
|
|
36
|
-
get unique(): boolean {
|
|
37
|
-
return this.attr(IDENTITY_ATTR_UNIQUE) !== false;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
30
|
isPrimary(): boolean {
|
|
41
31
|
return this.subType === IDENTITY_SUBTYPE_PRIMARY;
|
|
42
32
|
}
|
|
@@ -67,7 +57,8 @@ export class MetaPrimaryIdentity extends MetaIdentity {
|
|
|
67
57
|
}
|
|
68
58
|
|
|
69
59
|
/**
|
|
70
|
-
* Secondary identity — a unique
|
|
60
|
+
* Secondary identity — always a unique index on one or more fields.
|
|
61
|
+
* Uniqueness is in the type; use `index.lookup` for non-unique indexes.
|
|
71
62
|
* `@generation` does not apply here.
|
|
72
63
|
*/
|
|
73
64
|
export class MetaSecondaryIdentity extends MetaIdentity {}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// Index concern constants — type name, subtypes, and attr keys.
|
|
2
|
+
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
// Index type name (re-exported from shared/base-types.ts as TYPE_INDEX)
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Index type name as the brief-specified interface name.
|
|
9
|
+
* Downstream tasks import `INDEX` from here; `TYPE_INDEX` comes from base-types.
|
|
10
|
+
*/
|
|
11
|
+
export const INDEX = "index";
|
|
12
|
+
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
// Index subtypes (1: lookup)
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
|
|
17
|
+
export const INDEX_SUBTYPE_LOOKUP = "lookup";
|
|
18
|
+
|
|
19
|
+
export const INDEX_SUBTYPES = [INDEX_SUBTYPE_LOOKUP] as const;
|
|
20
|
+
export type IndexSubType = (typeof INDEX_SUBTYPES)[number];
|
|
21
|
+
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
// Index attrs (logical — core)
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
|
|
26
|
+
/** The field name(s) composing this index. */
|
|
27
|
+
export const INDEX_ATTR_FIELDS = "fields";
|
|
28
|
+
|
|
29
|
+
// NOTE: physical RDB-only index attrs (@using / @expr / @where / @orders) are
|
|
30
|
+
// NOT core — they are contributed by the db provider, reusing the existing
|
|
31
|
+
// IDENTITY_ATTR_* constants in persistence/db/db-constants.ts.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// Embedded definition for the index type (core vocab — physical attrs come from db provider).
|
|
2
|
+
import type { ProviderDefinition } from "../../provider-data.js";
|
|
3
|
+
|
|
4
|
+
export const INDEX_DEFINITION: ProviderDefinition = {
|
|
5
|
+
"provider": "metaobjects-core-types",
|
|
6
|
+
"types": [
|
|
7
|
+
{
|
|
8
|
+
"type": "index",
|
|
9
|
+
"subType": "lookup",
|
|
10
|
+
"description": "A non-unique lookup index on one or more fields. Use for query-performance indexes that do NOT enforce uniqueness — declare identity.secondary for unique constraints instead.",
|
|
11
|
+
"whenToUse": "You need a DB index for query performance (fast lookups/sorts) but NOT uniqueness enforcement. @fields names the indexed columns; the db provider adds @orders / @expr / @where / @using for physical tuning.",
|
|
12
|
+
"children": [
|
|
13
|
+
{
|
|
14
|
+
"type": "attr",
|
|
15
|
+
"subType": "string",
|
|
16
|
+
"name": "fields",
|
|
17
|
+
"isArray": true,
|
|
18
|
+
"min": 1,
|
|
19
|
+
"max": 1,
|
|
20
|
+
"description": "The field name(s) this index covers (at least one). When @expr is present, it is the key expression derived from these fields."
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
]
|
|
25
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// MetaIndex — concrete node class for type=index nodes.
|
|
2
|
+
// MetaLookupIndex is the single concrete subtype class.
|
|
3
|
+
//
|
|
4
|
+
// Extends MetaData directly: no model wrapper, no metaOf() indirection.
|
|
5
|
+
|
|
6
|
+
import { MetaData } from "../../shared/meta-data.js";
|
|
7
|
+
import {
|
|
8
|
+
INDEX_SUBTYPE_LOOKUP,
|
|
9
|
+
INDEX_ATTR_FIELDS,
|
|
10
|
+
} from "./index-constants.js";
|
|
11
|
+
|
|
12
|
+
export class MetaIndex extends MetaData {
|
|
13
|
+
/** ADR-0039: resolving — @fields may be inherited via extends. */
|
|
14
|
+
fields(): string[] {
|
|
15
|
+
const f = this.attr(INDEX_ATTR_FIELDS);
|
|
16
|
+
return Array.isArray(f) ? (f as string[]) : [];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
isLookup(): boolean {
|
|
20
|
+
return this.subType === INDEX_SUBTYPE_LOOKUP;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -7,6 +7,7 @@ import { MetaData } from "../../shared/meta-data.js";
|
|
|
7
7
|
import {
|
|
8
8
|
TYPE_FIELD,
|
|
9
9
|
TYPE_IDENTITY,
|
|
10
|
+
TYPE_INDEX,
|
|
10
11
|
TYPE_RELATIONSHIP,
|
|
11
12
|
TYPE_VALIDATOR,
|
|
12
13
|
TYPE_LAYOUT,
|
|
@@ -32,6 +33,7 @@ import {
|
|
|
32
33
|
} from "../identity/identity-constants.js";
|
|
33
34
|
import type { MetaField } from "../field/meta-field.js";
|
|
34
35
|
import type { MetaIdentity, MetaReferenceIdentity } from "../identity/meta-identity.js";
|
|
36
|
+
import type { MetaIndex } from "../index/meta-index.js";
|
|
35
37
|
import type { MetaLayout } from "../../presentation/layout/meta-layout.js";
|
|
36
38
|
import type { MetaRelationship } from "../relationship/meta-relationship.js";
|
|
37
39
|
import type { MetaValidator } from "../validator/meta-validator.js";
|
|
@@ -129,6 +131,13 @@ export class MetaObject extends MetaData {
|
|
|
129
131
|
);
|
|
130
132
|
}
|
|
131
133
|
|
|
134
|
+
/** All effective lookup indexes (own + inherited via extends). ADR-0039: resolving. */
|
|
135
|
+
lookupIndexes(): MetaIndex[] {
|
|
136
|
+
return this.cached("lookupIndexes", () =>
|
|
137
|
+
this.children().filter((c): c is MetaIndex => c.type === TYPE_INDEX),
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
132
141
|
/** All effective validators (own + inherited via extends). */
|
|
133
142
|
validators(): MetaValidator[] {
|
|
134
143
|
return this.cached("validators", () =>
|
package/src/core-types.ts
CHANGED
|
@@ -51,6 +51,9 @@ import { LAYOUT_DEFINITION } from "./presentation/layout/layout-definition.embed
|
|
|
51
51
|
import { MetaTemplate } from "./template/meta-template.js";
|
|
52
52
|
import { TEMPLATE_DEFINITION } from "./template/template-definition.embedded.js";
|
|
53
53
|
import { TEMPLATE_SUBTYPES } from "./template/template-constants.js";
|
|
54
|
+
import { MetaIndex } from "./core/index/meta-index.js";
|
|
55
|
+
import { INDEX_DEFINITION } from "./core/index/index-definition.embedded.js";
|
|
56
|
+
import { INDEX_SUBTYPES } from "./core/index/index-constants.js";
|
|
54
57
|
import {
|
|
55
58
|
TYPE_METADATA,
|
|
56
59
|
TYPE_OBJECT,
|
|
@@ -64,6 +67,7 @@ import {
|
|
|
64
67
|
TYPE_SOURCE,
|
|
65
68
|
TYPE_ORIGIN,
|
|
66
69
|
TYPE_TEMPLATE,
|
|
70
|
+
TYPE_INDEX,
|
|
67
71
|
SUBTYPE_ROOT,
|
|
68
72
|
} from "./shared/base-types.js";
|
|
69
73
|
import { CHILD_RULE_WILDCARD } from "./shared/structural.js";
|
|
@@ -463,6 +467,20 @@ function registerCoreTypeDefs(registry: TypeRegistry): void {
|
|
|
463
467
|
registry.register(relationshipDef);
|
|
464
468
|
}
|
|
465
469
|
|
|
470
|
+
// index — 1 subtype (lookup). Non-unique lookup indexes for query-performance.
|
|
471
|
+
// Distinct from identity.secondary (which enforces uniqueness). A single
|
|
472
|
+
// MetaIndex class backs the lookup subtype; physical attrs (@orders/@expr/
|
|
473
|
+
// @where/@using) are contributed by the db provider via registry.extend.
|
|
474
|
+
const INDEX_FACTORIES: FactoryMap = Object.fromEntries(
|
|
475
|
+
INDEX_SUBTYPES.map((subType) => [
|
|
476
|
+
`${TYPE_INDEX}.${subType}`,
|
|
477
|
+
(typeId: TypeId, name: string) => new MetaIndex(typeId, name),
|
|
478
|
+
]),
|
|
479
|
+
);
|
|
480
|
+
for (const indexDef of defineProviderFromData(INDEX_DEFINITION, INDEX_FACTORIES)) {
|
|
481
|
+
registry.register(indexDef);
|
|
482
|
+
}
|
|
483
|
+
|
|
466
484
|
// Declare the core cross-references ON their TypeDefinitions, so the loader's
|
|
467
485
|
// registry-derived validation resolves them generically (a dangling target fails the
|
|
468
486
|
// load). Set on the concrete subtypes the parser produces. (Production moves these into
|
package/src/errors.ts
CHANGED
|
@@ -155,6 +155,9 @@ export const ERROR_CODES = [
|
|
|
155
155
|
// clash / required-child cycle / conflicting attr redefinition. The detail names
|
|
156
156
|
// which of the six checks fired and the offending type(s).
|
|
157
157
|
"ERR_INVALID_METAMODEL_CONSTRAINT",
|
|
158
|
+
// index.lookup field-resolution: @fields is empty or names a field that does
|
|
159
|
+
// not exist on the owning entity's effective (resolved via extends) field set.
|
|
160
|
+
"ERR_INVALID_INDEX",
|
|
158
161
|
"ERR_UNKNOWN",
|
|
159
162
|
] as const;
|
|
160
163
|
|
package/src/index.ts
CHANGED
|
@@ -13,6 +13,7 @@ import type { MetaRoot } from "./shared/meta-root.js";
|
|
|
13
13
|
import type { MetaObject } from "./core/object/meta-object.js";
|
|
14
14
|
import type { MetaField } from "./core/field/meta-field.js";
|
|
15
15
|
import type { MetaIdentity } from "./core/identity/meta-identity.js";
|
|
16
|
+
import type { MetaIndex } from "./core/index/meta-index.js";
|
|
16
17
|
import type { MetaRelationship } from "./core/relationship/meta-relationship.js";
|
|
17
18
|
import type { MetaValidator } from "./core/validator/meta-validator.js";
|
|
18
19
|
import type { MetaView } from "./presentation/view/meta-view.js";
|
|
@@ -30,6 +31,7 @@ export * from "./core/attr/attr-constants.js";
|
|
|
30
31
|
export * from "./core/documentation/doc-constants.js";
|
|
31
32
|
export * from "./core/validator/validator-constants.js";
|
|
32
33
|
export * from "./core/identity/identity-constants.js";
|
|
34
|
+
export * from "./core/index/index-constants.js";
|
|
33
35
|
export * from "./core/relationship/relationship-constants.js";
|
|
34
36
|
export * from "./core/query/query-constants.js";
|
|
35
37
|
export * from "./persistence/source/source-constants.js";
|
|
@@ -67,6 +69,8 @@ export {
|
|
|
67
69
|
MetaSecondaryIdentity,
|
|
68
70
|
MetaReferenceIdentity,
|
|
69
71
|
} from "./core/identity/meta-identity.js";
|
|
72
|
+
// Index
|
|
73
|
+
export { MetaIndex } from "./core/index/meta-index.js";
|
|
70
74
|
export type { IdentityGeneration } from "./core/identity/meta-identity.js";
|
|
71
75
|
// FR-024 — projection identity pass-through derivation (computed local key;
|
|
72
76
|
// pure tree read, codegen-facing).
|
|
@@ -122,6 +126,7 @@ export type AnyMeta =
|
|
|
122
126
|
| MetaObject
|
|
123
127
|
| MetaField
|
|
124
128
|
| MetaIdentity
|
|
129
|
+
| MetaIndex
|
|
125
130
|
| MetaRelationship
|
|
126
131
|
| MetaValidator
|
|
127
132
|
| MetaView
|
|
@@ -17,7 +17,7 @@ import { ParseError } from "../errors.js";
|
|
|
17
17
|
import type { LoaderWarning } from "../source.js";
|
|
18
18
|
import { codeSource, resolvedSource } from "../source.js";
|
|
19
19
|
import { parseJson } from "../parser-json.js";
|
|
20
|
-
import { validateDataGridSortFields, validateFilterableHasIndex, validateFilterableHasSupportedOps, validateOriginPaths, validateDerivedFieldProvidability, validateDataGridFilterValues, validateFieldObjectStorage, validateFieldMap, validateTemplatePayloadRefs, validateFieldDefaults, validateRelationships } from "./validation-passes.js";
|
|
20
|
+
import { validateDataGridSortFields, validateFilterableHasIndex, validateFilterableHasSupportedOps, validateOriginPaths, validateDerivedFieldProvidability, validateDataGridFilterValues, validateFieldObjectStorage, validateFieldMap, validateTemplatePayloadRefs, validateFieldDefaults, validateRelationships, validateIndexLookupFields } from "./validation-passes.js";
|
|
21
21
|
import { runRegisteredValidation } from "./validation-registry.js";
|
|
22
22
|
import { validateSourceRoles } from "../persistence/source/validate-source-roles.js";
|
|
23
23
|
import { validateSourcePhysicalNames } from "../persistence/source/validate-source-physical-names.js";
|
|
@@ -495,6 +495,11 @@ export class MetaDataLoader {
|
|
|
495
495
|
// are invalid on a 1:N relationship.
|
|
496
496
|
errors.push(...validateRelationships(root));
|
|
497
497
|
|
|
498
|
+
// index.lookup @fields resolution — each index.lookup must name ≥1 field,
|
|
499
|
+
// and every field must exist in the entity's effective (resolved) field set
|
|
500
|
+
// (ADR-0039: resolving accessor, so inherited fields via extends are visible).
|
|
501
|
+
errors.push(...validateIndexLookupFields(root));
|
|
502
|
+
|
|
498
503
|
// Phase 2 — validation DERIVED FROM THE TYPE REGISTRY: each node's TypeDefinition
|
|
499
504
|
// carries its reference descriptors + imperative validator, run as one recursive walk
|
|
500
505
|
// over a built-once symbol table. A downstream provider's custom type validates itself
|
|
@@ -25,7 +25,13 @@ import {
|
|
|
25
25
|
TYPE_RELATIONSHIP,
|
|
26
26
|
TYPE_SOURCE,
|
|
27
27
|
TYPE_TEMPLATE,
|
|
28
|
+
TYPE_INDEX,
|
|
28
29
|
} from "../shared/base-types.js";
|
|
30
|
+
import {
|
|
31
|
+
INDEX_SUBTYPE_LOOKUP,
|
|
32
|
+
INDEX_ATTR_FIELDS,
|
|
33
|
+
} from "../core/index/index-constants.js";
|
|
34
|
+
import { MetaIndex } from "../core/index/meta-index.js";
|
|
29
35
|
import {
|
|
30
36
|
TEMPLATE_ATTR_PAYLOAD_REF,
|
|
31
37
|
TEMPLATE_ATTR_RESPONSE_REF,
|
|
@@ -1428,6 +1434,60 @@ export function validateRelationships(root: MetaData): ParseError[] {
|
|
|
1428
1434
|
// NOTE: identity.reference @references resolution moved to the validation registry
|
|
1429
1435
|
// (defaultValidationRegistry → a declarative reference descriptor with dottedFieldPath).
|
|
1430
1436
|
|
|
1437
|
+
// ---------------------------------------------------------------------------
|
|
1438
|
+
// index.lookup @fields resolution (Task 3)
|
|
1439
|
+
//
|
|
1440
|
+
// Every index.lookup on an entity must name at least one field, and every
|
|
1441
|
+
// named field must exist in the entity's EFFECTIVE (resolved) field set.
|
|
1442
|
+
// ADR-0039: use children() / MetaIndex.fields() — never own* — so that a
|
|
1443
|
+
// field inherited via extends still resolves correctly.
|
|
1444
|
+
// ---------------------------------------------------------------------------
|
|
1445
|
+
|
|
1446
|
+
export function validateIndexLookupFields(root: MetaData): ParseError[] {
|
|
1447
|
+
const errors: ParseError[] = [];
|
|
1448
|
+
// ADR-0039: root has no super; children()==ownChildren() but resolving is the default.
|
|
1449
|
+
for (const obj of root.children().filter((c) => c.type === TYPE_OBJECT)) {
|
|
1450
|
+
// Effective (resolved) field names — includes inherited fields via extends.
|
|
1451
|
+
const effectiveFieldNames = new Set(
|
|
1452
|
+
obj.children().filter((c) => c.type === TYPE_FIELD).map((f) => f.name),
|
|
1453
|
+
);
|
|
1454
|
+
for (const node of obj.children().filter(
|
|
1455
|
+
(c) => c.type === TYPE_INDEX && c.subType === INDEX_SUBTYPE_LOOKUP,
|
|
1456
|
+
)) {
|
|
1457
|
+
// MetaIndex.fields() uses the resolving attr() accessor per ADR-0039.
|
|
1458
|
+
const idx = node as MetaIndex;
|
|
1459
|
+
const fields = idx.fields();
|
|
1460
|
+
|
|
1461
|
+
// Rule 1: must have at least one field.
|
|
1462
|
+
if (fields.length === 0) {
|
|
1463
|
+
errors.push(
|
|
1464
|
+
new ParseError(
|
|
1465
|
+
`index.lookup "${idx.name}" on "${obj.name}" has no @${INDEX_ATTR_FIELDS}; ` +
|
|
1466
|
+
`at least one field is required`,
|
|
1467
|
+
{ code: "ERR_INVALID_INDEX", source: idx.source },
|
|
1468
|
+
),
|
|
1469
|
+
);
|
|
1470
|
+
continue;
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
// Rule 2: every named field must resolve against the entity's effective field set.
|
|
1474
|
+
for (const fieldName of fields) {
|
|
1475
|
+
if (!effectiveFieldNames.has(fieldName)) {
|
|
1476
|
+
errors.push(
|
|
1477
|
+
new ParseError(
|
|
1478
|
+
`index.lookup "${idx.name}" on "${obj.name}" references field "${fieldName}" ` +
|
|
1479
|
+
`which does not exist on "${obj.name}". ` +
|
|
1480
|
+
`Available fields: ${[...effectiveFieldNames].join(", ") || "(none)"}`,
|
|
1481
|
+
{ code: "ERR_INVALID_INDEX", source: idx.source },
|
|
1482
|
+
),
|
|
1483
|
+
);
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
return errors;
|
|
1489
|
+
}
|
|
1490
|
+
|
|
1431
1491
|
function checkFilterClauses(
|
|
1432
1492
|
filter: Record<string, unknown>,
|
|
1433
1493
|
allow: Map<string, readonly string[]>,
|
|
@@ -247,6 +247,49 @@ export const DB_DEFINITION: ProviderDefinition = {
|
|
|
247
247
|
"description": "Physical foreign-key constraint name override. Absent → the backend's auto-derived default (e.g. `<table>_<firstFkColumn>_fk`). Lets a model adopt an existing database whose FK constraints follow a different naming convention without a destructive rename. RDB-physical — contributed by the db provider."
|
|
248
248
|
}
|
|
249
249
|
]
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
"type": "index",
|
|
253
|
+
"subType": "lookup",
|
|
254
|
+
"children": [
|
|
255
|
+
{
|
|
256
|
+
"type": "attr",
|
|
257
|
+
"subType": "string",
|
|
258
|
+
"name": "orders",
|
|
259
|
+
"isArray": true,
|
|
260
|
+
"min": 0,
|
|
261
|
+
"max": 1,
|
|
262
|
+
"allowedValues": [
|
|
263
|
+
"asc",
|
|
264
|
+
"desc"
|
|
265
|
+
],
|
|
266
|
+
"description": "Physical index-key sort direction, positional to @fields ('asc' | 'desc'). Omit for all-ascending (the default); a shorter array leaves trailing keys ascending. Drives DESC-ordered index keys (e.g. a recency index on a timestamp). RDB-physical — contributed by the db provider."
|
|
267
|
+
},
|
|
268
|
+
{
|
|
269
|
+
"type": "attr",
|
|
270
|
+
"subType": "string",
|
|
271
|
+
"name": "where",
|
|
272
|
+
"min": 0,
|
|
273
|
+
"max": 1,
|
|
274
|
+
"description": "Partial-index predicate (raw SQL, e.g. \"delivered_at IS NULL\"). When set, the index covers only rows matching the predicate — smaller and cheaper for queries that always filter on it. Absent = a full index over every row. RDB-physical — contributed by the db provider."
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
"type": "attr",
|
|
278
|
+
"subType": "string",
|
|
279
|
+
"name": "expr",
|
|
280
|
+
"min": 0,
|
|
281
|
+
"max": 1,
|
|
282
|
+
"description": "Raw key EXPRESSION for a functional/expression index (e.g. \"lower(email)\"). Used INSTEAD of @fields — the index key is the expression rather than plain columns. RDB-physical — contributed by the db provider."
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
"type": "attr",
|
|
286
|
+
"subType": "string",
|
|
287
|
+
"name": "using",
|
|
288
|
+
"min": 0,
|
|
289
|
+
"max": 1,
|
|
290
|
+
"description": "Index access method (e.g. \"gin\", \"gist\", \"hash\"); default \"btree\" (not rendered). Pair with @expr for e.g. a GIN index over an array/jsonb expression. RDB-physical — contributed by the db provider."
|
|
291
|
+
}
|
|
292
|
+
]
|
|
250
293
|
}
|
|
251
294
|
]
|
|
252
295
|
};
|
package/src/shared/base-types.ts
CHANGED
|
@@ -17,6 +17,8 @@ export const TYPE_LAYOUT = "layout";
|
|
|
17
17
|
export const TYPE_SOURCE = "source";
|
|
18
18
|
export const TYPE_ORIGIN = "origin";
|
|
19
19
|
export const TYPE_TEMPLATE = "template";
|
|
20
|
+
/** Non-unique lookup indexes — `index.lookup` is the only registered subtype. */
|
|
21
|
+
export const TYPE_INDEX = "index";
|
|
20
22
|
|
|
21
23
|
export const BASE_TYPES = [
|
|
22
24
|
TYPE_METADATA,
|
|
@@ -31,6 +33,7 @@ export const BASE_TYPES = [
|
|
|
31
33
|
TYPE_SOURCE,
|
|
32
34
|
TYPE_ORIGIN,
|
|
33
35
|
TYPE_TEMPLATE,
|
|
36
|
+
TYPE_INDEX,
|
|
34
37
|
] as const;
|
|
35
38
|
export type BaseType = (typeof BASE_TYPES)[number];
|
|
36
39
|
|