@xdbml/parse 0.3.2 → 0.5.0
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/ast.d.ts +24 -8
- package/dist/index.d.ts +5 -1
- package/dist/index.js +3 -1
- package/dist/keywords.d.ts +3 -3
- package/dist/keywords.js +14 -0
- package/dist/module-resolver.d.ts +3 -3
- package/dist/module-resolver.js +21 -11
- package/dist/name-resolver.d.ts +2 -2
- package/dist/name-resolver.js +43 -3
- package/dist/parser.d.ts +31 -7
- package/dist/parser.js +116 -18
- package/dist/relationships.d.ts +99 -0
- package/dist/relationships.js +436 -0
- package/dist/supertypes.d.ts +110 -0
- package/dist/supertypes.js +526 -0
- package/package.json +2 -1
package/dist/ast.d.ts
CHANGED
|
@@ -38,7 +38,7 @@ export interface ExperimentalDeclaration {
|
|
|
38
38
|
features: string[];
|
|
39
39
|
span: Span;
|
|
40
40
|
}
|
|
41
|
-
export type TopLevelStatement = ProjectDeclaration | ContainerDeclaration | EntityDeclaration | TypeDeclaration | EdgeDeclaration | ViewDeclaration | EnumDeclaration | RefDeclaration | TablePartialDeclaration | TableGroupDeclaration | NoteDeclaration | TopLevelRecordsDeclaration | ModuleImportDirective;
|
|
41
|
+
export type TopLevelStatement = ProjectDeclaration | ContainerDeclaration | EntityDeclaration | TypeDeclaration | EdgeDeclaration | ViewDeclaration | EnumDeclaration | RefDeclaration | TablePartialDeclaration | TableGroupDeclaration | SupertypeGroupDeclaration | NoteDeclaration | TopLevelRecordsDeclaration | ModuleImportDirective;
|
|
42
42
|
export interface ProjectDeclaration {
|
|
43
43
|
kind: 'ProjectDeclaration';
|
|
44
44
|
name: string;
|
|
@@ -182,7 +182,7 @@ export interface TypeDeclaration {
|
|
|
182
182
|
kind: 'TypeDeclaration';
|
|
183
183
|
name: string;
|
|
184
184
|
/**
|
|
185
|
-
* v0.2 scalar form (spec §
|
|
185
|
+
* v0.2 scalar form (spec §15.7): when present, this Type is an alias
|
|
186
186
|
* for the given type expression rather than an object-shaped record.
|
|
187
187
|
* Examples:
|
|
188
188
|
*
|
|
@@ -339,6 +339,21 @@ export interface TableGroupDeclaration {
|
|
|
339
339
|
members: string[];
|
|
340
340
|
span: Span;
|
|
341
341
|
}
|
|
342
|
+
export interface SupertypeGroupDeclaration {
|
|
343
|
+
kind: 'SupertypeGroupDeclaration';
|
|
344
|
+
name: string;
|
|
345
|
+
settings: Setting[];
|
|
346
|
+
members: SupertypeGroupMember[];
|
|
347
|
+
span: Span;
|
|
348
|
+
}
|
|
349
|
+
export interface SupertypeGroupMember {
|
|
350
|
+
kind: 'SupertypeGroupMember';
|
|
351
|
+
/** Entity path as written: `Person` or `crm.Person`. */
|
|
352
|
+
name: string;
|
|
353
|
+
/** Member settings; `strategy` is the only recognized one (spec §12.7.4). */
|
|
354
|
+
settings: Setting[];
|
|
355
|
+
span: Span;
|
|
356
|
+
}
|
|
342
357
|
export interface PartialInjection {
|
|
343
358
|
kind: 'PartialInjection';
|
|
344
359
|
/** Identifier after the `~`. */
|
|
@@ -385,7 +400,7 @@ export interface ModuleImportDirective {
|
|
|
385
400
|
/**
|
|
386
401
|
* Directive mode:
|
|
387
402
|
* - `'reuse'`: transitive (visible to files that further import this file).
|
|
388
|
-
* The recommended default per spec §
|
|
403
|
+
* The recommended default per spec §27.4.
|
|
389
404
|
* - `'use'`: non-transitive (private to this file).
|
|
390
405
|
*/
|
|
391
406
|
mode: 'use' | 'reuse';
|
|
@@ -424,9 +439,10 @@ export type ImportSpec = {
|
|
|
424
439
|
* type Email as PII_Email
|
|
425
440
|
* field core.dim_customer.email
|
|
426
441
|
*
|
|
427
|
-
* The element type is one of the keywords from §
|
|
442
|
+
* The element type is one of the keywords from §27.3 (table, entity,
|
|
428
443
|
* collection, record, enum, tablepartial, note, schema, container,
|
|
429
|
-
* tablegroup, type, edge, view, diagramview, field
|
|
444
|
+
* tablegroup, type, edge, view, diagramview, field, and from v0.5
|
|
445
|
+
* supertypegroup). Stored lowercased.
|
|
430
446
|
*
|
|
431
447
|
* The source path follows xDBML's standard dotted form. Container.Entity
|
|
432
448
|
* for an entity inside a Container; Container.Entity.Field for a field.
|
|
@@ -447,7 +463,7 @@ export interface ImportItem {
|
|
|
447
463
|
* an entity, type, container, etc. clone). Field imports are not supported
|
|
448
464
|
* in P4 -- when they land, this type may grow to a wider union.
|
|
449
465
|
*
|
|
450
|
-
* Per spec §
|
|
466
|
+
* Per spec §27.6, the clone contains exactly the imported declaration(s),
|
|
451
467
|
* without surrounding wrappers from the source file. For an entity clone
|
|
452
468
|
* the content is the EntityDeclaration alone (no container wrapper); for a
|
|
453
469
|
* container clone the content is the ContainerDeclaration including its
|
|
@@ -459,7 +475,7 @@ export interface CloneBlock {
|
|
|
459
475
|
* Statements that the importing file pulls in from the source. Most are
|
|
460
476
|
* top-level shapes (Entity, Type, Enum, Container, etc.). The one
|
|
461
477
|
* exception is `FieldDeclaration`: when the parent directive imports
|
|
462
|
-
* one or more fields via `field <path>` items (spec §
|
|
478
|
+
* one or more fields via `field <path>` items (spec §27.8), each field
|
|
463
479
|
* appears here as a bare FieldDeclaration with no entity wrapper.
|
|
464
480
|
* The `flatten()` pass lifts each bare field into a synthetic
|
|
465
481
|
* TypeDeclaration at file scope so downstream consumers see a normal
|
|
@@ -545,7 +561,7 @@ export interface ParseOptions {
|
|
|
545
561
|
* Because this reader is synchronous, a host that supports remote sources
|
|
546
562
|
* must return the fetched text from a cache it populated beforehand. The
|
|
547
563
|
* network fetch, and its SSRF / redirect / size / timeout obligations
|
|
548
|
-
* (spec §
|
|
564
|
+
* (spec §27.14.5), live in the host's resolver, not in the parser.
|
|
549
565
|
*
|
|
550
566
|
* If absent, reference-only directives fall back to the P4 rejection
|
|
551
567
|
* with a clear "no resolver available" message. Clone-block-bearing
|
package/dist/index.d.ts
CHANGED
|
@@ -27,11 +27,15 @@
|
|
|
27
27
|
export * from './ast.ts';
|
|
28
28
|
export { tokenize, TokenKind, LexError } from './lexer.ts';
|
|
29
29
|
export type { Token } from './lexer.ts';
|
|
30
|
-
export { parse, Parser, ParseError } from './parser.ts';
|
|
30
|
+
export { parse, Parser, ParseError, SUPPORTED_XDBML_VERSION, compareVersions } from './parser.ts';
|
|
31
31
|
export { flatten } from './module-resolver.ts';
|
|
32
32
|
export { classifyModuleSource, isUrlKey, ModuleSourceError, } from './module-resolver.ts';
|
|
33
33
|
export type { ModuleSource } from './module-resolver.ts';
|
|
34
34
|
export { resolveNames, SymbolTable } from './name-resolver.ts';
|
|
35
|
+
export { FOREIGN_MASTER_FLAG, checkRelationships, hasForeignMasterFlag, isForeignMaster, pathToString, refChildEndpoint, refParentEndpoint, relationshipType, versionAtLeast, CONSTRAINT_TYPES, V04_RELATIONSHIP_SETTINGS, constraintType, entityNames, isEntityLevelEndpoint, isUndirected, } from './relationships.ts';
|
|
36
|
+
export type { RelationshipType, ConstraintType } from './relationships.ts';
|
|
37
|
+
export { SUPERTYPE_GROUP_VALUES, canonicalSupertypeGroupValue, checkSupertypeGroups, resolveSupertypeGroups, subtypeStrategy, supertypeChains, supertypeGroupSettings, } from './supertypes.ts';
|
|
38
|
+
export type { Completeness, Exclusivity, MaterializationStrategy, MergeOption, ResolvedSupertypeGroup, SupertypeGroupSettings, SupertypeGroupValueSetting, } from './supertypes.ts';
|
|
35
39
|
export type { Diagnostic, DiagnosticCode, ResolutionResult, SymbolEntry, SymbolKind, } from './name-resolver.ts';
|
|
36
40
|
export { xdbmlLanguageConfig, xdbmlMonarchTokensProvider, } from './monarch.ts';
|
|
37
41
|
export type { XDbmlLanguageConfiguration, XDbmlMonarchLanguage, } from './monarch.ts';
|
package/dist/index.js
CHANGED
|
@@ -26,8 +26,10 @@
|
|
|
26
26
|
*/
|
|
27
27
|
export * from "./ast.js";
|
|
28
28
|
export { tokenize, TokenKind, LexError } from "./lexer.js";
|
|
29
|
-
export { parse, Parser, ParseError } from "./parser.js";
|
|
29
|
+
export { parse, Parser, ParseError, SUPPORTED_XDBML_VERSION, compareVersions } from "./parser.js";
|
|
30
30
|
export { flatten } from "./module-resolver.js";
|
|
31
31
|
export { classifyModuleSource, isUrlKey, ModuleSourceError, } from "./module-resolver.js";
|
|
32
32
|
export { resolveNames, SymbolTable } from "./name-resolver.js";
|
|
33
|
+
export { FOREIGN_MASTER_FLAG, checkRelationships, hasForeignMasterFlag, isForeignMaster, pathToString, refChildEndpoint, refParentEndpoint, relationshipType, versionAtLeast, CONSTRAINT_TYPES, V04_RELATIONSHIP_SETTINGS, constraintType, entityNames, isEntityLevelEndpoint, isUndirected, } from "./relationships.js";
|
|
34
|
+
export { SUPERTYPE_GROUP_VALUES, canonicalSupertypeGroupValue, checkSupertypeGroups, resolveSupertypeGroups, subtypeStrategy, supertypeChains, supertypeGroupSettings, } from "./supertypes.js";
|
|
33
35
|
export { xdbmlLanguageConfig, xdbmlMonarchTokensProvider, } from "./monarch.js";
|
package/dist/keywords.d.ts
CHANGED
|
@@ -33,13 +33,13 @@ export declare const ENTITY_KEYWORDS: readonly ["table", "entity", "collection",
|
|
|
33
33
|
* The full set of declaration keywords. Includes containers,
|
|
34
34
|
* entities, and other top-level constructs.
|
|
35
35
|
*/
|
|
36
|
-
export declare const DECLARATION_KEYWORDS: readonly ["project", "container", "schema", "database", "keyspace", "namespace", "dataset", "bucket", "table", "entity", "collection", "record", "type", "edge", "view", "enum", "ref", "note", "tablepartial", "tablegroup", "diagramview"];
|
|
36
|
+
export declare const DECLARATION_KEYWORDS: readonly ["project", "container", "schema", "database", "keyspace", "namespace", "dataset", "bucket", "table", "entity", "collection", "record", "type", "edge", "view", "enum", "ref", "note", "tablepartial", "tablegroup", "supertypegroup", "diagramview"];
|
|
37
37
|
export declare const STRUCTURAL_TYPE_KEYWORDS: readonly ["object", "struct", "array", "list", "map", "dict", "dictionary", "set", "json", "jsonb", "variant"];
|
|
38
38
|
export declare const POLYMORPHISM_KEYWORDS: readonly ["union", "oneof", "anyof", "allof"];
|
|
39
39
|
export declare const SCALAR_TYPES: readonly ["tinyint", "smallint", "mediumint", "int", "integer", "bigint", "int32", "int64", "float", "double", "decimal", "dec", "numeric", "real", "bit", "bool", "boolean", "char", "varchar", "varchar2", "nvarchar", "nvarchar2", "nchar", "text", "mediumtext", "longtext", "string", "ntext", "binary", "varbinary", "blob", "mediumblob", "longblob", "tinyblob", "tinytext", "json", "jsonb", "variant", "xml", "date", "time", "datetime", "datetime2", "timestamp", "timestamptz", "year", "uuid", "inet6", "money", "smallmoney", "enum"];
|
|
40
40
|
export declare const BSON_TYPES: readonly ["objectid", "decimal128", "bindata", "minkey", "maxkey", "symbol", "regex", "long", "double"];
|
|
41
|
-
export declare const SETTING_FLAGS: readonly ["pk", "primary", "key", "unique", "null", "not", "required", "increment", "inactive"];
|
|
42
|
-
export declare const SETTING_KEYS: readonly ["note", "default", "ref", "name", "color", "headercolor", "as", "check", "type", "target", "targets", "database_type", "source", "source_cardinality", "target_cardinality", "min_source", "max_source", "min_target", "max_target", "undirected", "discriminator", "source_query", "materialized", "refresh_schedule", "refresh_on", "source_database", "storage_options", "pattern", "format", "minlength", "maxlength", "minimum", "maximum", "exclusiveminimum", "exclusivemaximum", "multipleof", "minitems", "maxitems", "uniqueitems", "minproperties", "maxproperties", "synonyms", "business_term", "granularity", "tags", "delete", "update", "indexes", "checks", "replication", "location", "default_charset", "cloned_at"];
|
|
41
|
+
export declare const SETTING_FLAGS: readonly ["pk", "primary", "key", "unique", "null", "not", "required", "increment", "inactive", "foreign_master"];
|
|
42
|
+
export declare const SETTING_KEYS: readonly ["note", "default", "ref", "name", "color", "headercolor", "as", "check", "type", "target", "targets", "database_type", "source", "source_cardinality", "target_cardinality", "min_source", "max_source", "min_target", "max_target", "undirected", "source_role", "target_role", "source_verb", "target_verb", "constraint_type", "discriminator", "supertype", "completeness", "exclusivity", "strategy", "merge", "source_query", "materialized", "refresh_schedule", "refresh_on", "source_database", "storage_options", "pattern", "format", "minlength", "maxlength", "minimum", "maximum", "exclusiveminimum", "exclusivemaximum", "multipleof", "minitems", "maxitems", "uniqueitems", "minproperties", "maxproperties", "synonyms", "business_term", "granularity", "tags", "delete", "update", "indexes", "checks", "replication", "location", "default_charset", "cloned_at"];
|
|
43
43
|
export declare const GRANULARITY_VALUES: readonly ["year", "quarter", "month", "week", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"];
|
|
44
44
|
export declare const DIRECTIVE_KEYWORDS: readonly ["xdbml", "experimental"];
|
|
45
45
|
export declare const MODULE_KEYWORDS: readonly ["use", "reuse", "from", "as"];
|
package/dist/keywords.js
CHANGED
|
@@ -66,6 +66,7 @@ export const DECLARATION_KEYWORDS = [
|
|
|
66
66
|
'note',
|
|
67
67
|
'tablepartial',
|
|
68
68
|
'tablegroup',
|
|
69
|
+
'supertypegroup',
|
|
69
70
|
'diagramview',
|
|
70
71
|
];
|
|
71
72
|
/* -------------------------------------------------------------------------
|
|
@@ -164,6 +165,7 @@ export const SETTING_FLAGS = [
|
|
|
164
165
|
'required',
|
|
165
166
|
'increment',
|
|
166
167
|
'inactive', // v0.2 §11.9: Ref flag for visualization-only deactivation
|
|
168
|
+
'foreign_master', // v0.4 §11.10: Ref flag for denormalized replication
|
|
167
169
|
];
|
|
168
170
|
export const SETTING_KEYS = [
|
|
169
171
|
// General
|
|
@@ -188,7 +190,19 @@ export const SETTING_KEYS = [
|
|
|
188
190
|
'min_target',
|
|
189
191
|
'max_target',
|
|
190
192
|
'undirected',
|
|
193
|
+
// v0.4 relationship documentation (spec 11.14, 11.15)
|
|
194
|
+
'source_role',
|
|
195
|
+
'target_role',
|
|
196
|
+
'source_verb',
|
|
197
|
+
'target_verb',
|
|
198
|
+
'constraint_type',
|
|
191
199
|
'discriminator',
|
|
200
|
+
// v0.5 supertype groups (spec 12.2); `discriminator` is listed above
|
|
201
|
+
'supertype',
|
|
202
|
+
'completeness',
|
|
203
|
+
'exclusivity',
|
|
204
|
+
'strategy',
|
|
205
|
+
'merge',
|
|
192
206
|
'source_query',
|
|
193
207
|
'materialized',
|
|
194
208
|
'refresh_schedule',
|
|
@@ -30,13 +30,13 @@ import type { CloneBlock, ModuleImportDirective, ParseOptions, XDbmlDocument } f
|
|
|
30
30
|
*
|
|
31
31
|
* Inside a Container body, each `ModuleImportDirective` is replaced by its
|
|
32
32
|
* `clone.statements` as `ContainerBodyItem`s. Note that the spec table in
|
|
33
|
-
* §
|
|
33
|
+
* §27.6 guarantees that clone-block content for entity/edge/view/enum
|
|
34
34
|
* imports is shape-compatible with `ContainerBodyItem`; other shapes
|
|
35
35
|
* (e.g., a TablePartial clone inside a Container directive) would be
|
|
36
36
|
* semantically invalid per the spec and would surface as a downstream
|
|
37
37
|
* type error rather than being caught here.
|
|
38
38
|
*
|
|
39
|
-
* Field-level imports (spec §
|
|
39
|
+
* Field-level imports (spec §27.8) get a special transform: the clone
|
|
40
40
|
* block holds a bare `FieldDeclaration`, which `flatten()` lifts into a
|
|
41
41
|
* synthetic `TypeDeclaration` at file scope. Downstream consumers see
|
|
42
42
|
* a normal Named Type and can use it as a field type without learning
|
|
@@ -59,7 +59,7 @@ export type ParseFn = (source: string, options: ParseOptions, resolutionStack: R
|
|
|
59
59
|
* - `kind: 'resolved'` -- the file was opened, parsed, and a clone
|
|
60
60
|
* block was synthesized
|
|
61
61
|
* - `kind: 'cycle'` -- the resolution chain already contains this
|
|
62
|
-
* file; per spec §
|
|
62
|
+
* file; per spec §27.15 cycles are allowed, so we return an empty
|
|
63
63
|
* clone block and let name resolution (P6+) handle the actual
|
|
64
64
|
* cross-file linking
|
|
65
65
|
* - `kind: 'no-resolver'` -- no `readFile` was supplied; caller should
|
package/dist/module-resolver.js
CHANGED
|
@@ -30,13 +30,13 @@ import { SCALAR_TYPES, BSON_TYPES } from "./keywords.js";
|
|
|
30
30
|
*
|
|
31
31
|
* Inside a Container body, each `ModuleImportDirective` is replaced by its
|
|
32
32
|
* `clone.statements` as `ContainerBodyItem`s. Note that the spec table in
|
|
33
|
-
* §
|
|
33
|
+
* §27.6 guarantees that clone-block content for entity/edge/view/enum
|
|
34
34
|
* imports is shape-compatible with `ContainerBodyItem`; other shapes
|
|
35
35
|
* (e.g., a TablePartial clone inside a Container directive) would be
|
|
36
36
|
* semantically invalid per the spec and would surface as a downstream
|
|
37
37
|
* type error rather than being caught here.
|
|
38
38
|
*
|
|
39
|
-
* Field-level imports (spec §
|
|
39
|
+
* Field-level imports (spec §27.8) get a special transform: the clone
|
|
40
40
|
* block holds a bare `FieldDeclaration`, which `flatten()` lifts into a
|
|
41
41
|
* synthetic `TypeDeclaration` at file scope. Downstream consumers see
|
|
42
42
|
* a normal Named Type and can use it as a field type without learning
|
|
@@ -92,11 +92,11 @@ function flattenTopLevel(stmt, out) {
|
|
|
92
92
|
*
|
|
93
93
|
* - For SCALAR fields (any TypeExpression that isn't ObjectType): the
|
|
94
94
|
* Type is in scalar form with `scalarBase = field.type`. Equivalent
|
|
95
|
-
* to the v0.2 scalar-Named-Type form (spec §
|
|
95
|
+
* to the v0.2 scalar-Named-Type form (spec §15.7).
|
|
96
96
|
*
|
|
97
97
|
* - For OBJECT-typed fields (`field foo object { ... }`): the Type is
|
|
98
98
|
* in object form with `body = field.type.fields`. Equivalent to the
|
|
99
|
-
* v0.1 object-Named-Type form (spec §
|
|
99
|
+
* v0.1 object-Named-Type form (spec §15).
|
|
100
100
|
*
|
|
101
101
|
* Other type shapes (Array, Map, Set, Tuple, Union, polymorphic types)
|
|
102
102
|
* are valid as `scalarBase` of a Type and pass through unchanged. The
|
|
@@ -225,7 +225,7 @@ export function resolveImport(directive, options, resolutionStack, depth, parseF
|
|
|
225
225
|
* import spec. Applies aliases by renaming the extracted declaration.
|
|
226
226
|
*
|
|
227
227
|
* For `ImportAll`: every TopLevelStatement except ProjectDeclaration
|
|
228
|
-
* (per spec §
|
|
228
|
+
* (per spec §27.4, Project declarations cannot be imported).
|
|
229
229
|
*
|
|
230
230
|
* For `ImportList`: each item is matched by element-type + source path.
|
|
231
231
|
* Items not found in the source produce nothing (silent for P5; a
|
|
@@ -234,7 +234,7 @@ export function resolveImport(directive, options, resolutionStack, depth, parseF
|
|
|
234
234
|
function extractImports(spec, sourceDoc) {
|
|
235
235
|
if (spec.kind === 'ImportAll') {
|
|
236
236
|
// ImportAll never matches field imports (the `*` form is for top-level
|
|
237
|
-
// declarations only per spec §
|
|
237
|
+
// declarations only per spec §27.2). Return the source's top-level
|
|
238
238
|
// statements minus Project.
|
|
239
239
|
return sourceDoc.statements
|
|
240
240
|
.filter((s) => s.kind !== 'ProjectDeclaration')
|
|
@@ -248,7 +248,7 @@ function extractImports(spec, sourceDoc) {
|
|
|
248
248
|
out.push(applyAlias(found, item));
|
|
249
249
|
}
|
|
250
250
|
// Silent skip on not-found. The spec leaves diagnostics to name
|
|
251
|
-
// resolution (§
|
|
251
|
+
// resolution (§27.13). In practice, users notice missing entities
|
|
252
252
|
// because their references downstream become unresolved.
|
|
253
253
|
}
|
|
254
254
|
return out;
|
|
@@ -276,6 +276,8 @@ function extractImports(spec, sourceDoc) {
|
|
|
276
276
|
* ViewDeclaration with matching name; supports dotted path.
|
|
277
277
|
* - 'tablegroup':
|
|
278
278
|
* TableGroupDeclaration with matching name (top-level only).
|
|
279
|
+
* - 'supertypegroup':
|
|
280
|
+
* SupertypeGroupDeclaration with matching name (top-level only, v0.5).
|
|
279
281
|
* - 'tablepartial':
|
|
280
282
|
* TablePartialDeclaration with matching name (top-level only).
|
|
281
283
|
* - 'note':
|
|
@@ -344,6 +346,12 @@ function findImportTarget(item, doc) {
|
|
|
344
346
|
if (item.elementType === 'tablegroup') {
|
|
345
347
|
return doc.statements.find((s) => s.kind === 'TableGroupDeclaration' && s.name === path);
|
|
346
348
|
}
|
|
349
|
+
// SupertypeGroup (spec §12, v0.5): top-level only, bare name. The group
|
|
350
|
+
// comes with its membership; its entities resolve in the importing
|
|
351
|
+
// document, by import or declaration, like TableGroup members.
|
|
352
|
+
if (item.elementType === 'supertypegroup') {
|
|
353
|
+
return doc.statements.find((s) => s.kind === 'SupertypeGroupDeclaration' && s.name === path);
|
|
354
|
+
}
|
|
347
355
|
if (item.elementType === 'tablepartial') {
|
|
348
356
|
return doc.statements.find((s) => s.kind === 'TablePartialDeclaration' && s.name === path);
|
|
349
357
|
}
|
|
@@ -403,7 +411,7 @@ function findImportTarget(item, doc) {
|
|
|
403
411
|
* object types to find a FieldDeclaration in a referenced source document.
|
|
404
412
|
* Returns undefined when any segment fails to resolve.
|
|
405
413
|
*
|
|
406
|
-
* Path shapes accepted (per spec §
|
|
414
|
+
* Path shapes accepted (per spec §27.8):
|
|
407
415
|
* - `entity.field` -- top-level entity
|
|
408
416
|
* - `container.entity.field` -- container-qualified entity
|
|
409
417
|
* - `entity.field.sub` -- nested via ObjectType
|
|
@@ -594,6 +602,8 @@ function applyAlias(stmt, item) {
|
|
|
594
602
|
return { ...stmt, name: item.alias };
|
|
595
603
|
case 'TableGroupDeclaration':
|
|
596
604
|
return { ...stmt, name: item.alias };
|
|
605
|
+
case 'SupertypeGroupDeclaration':
|
|
606
|
+
return { ...stmt, name: item.alias };
|
|
597
607
|
case 'TablePartialDeclaration':
|
|
598
608
|
return { ...stmt, name: item.alias };
|
|
599
609
|
case 'NoteDeclaration':
|
|
@@ -605,7 +615,7 @@ function applyAlias(stmt, item) {
|
|
|
605
615
|
}
|
|
606
616
|
}
|
|
607
617
|
/* -------------------------------------------------------------------------
|
|
608
|
-
* Module source classification (spec §
|
|
618
|
+
* Module source classification (spec §27.14, "Remote module sources")
|
|
609
619
|
*
|
|
610
620
|
* A `from` source is recognized purely by its scheme. A source beginning
|
|
611
621
|
* with 'https://' is a remote (URL) source; anything else is a relative
|
|
@@ -617,7 +627,7 @@ function applyAlias(stmt, item) {
|
|
|
617
627
|
* This is pure, synchronous classification. The actual network fetch is
|
|
618
628
|
* delegated to ParseOptions.readFile (the host's resolver). The obligations
|
|
619
629
|
* on a fetcher (SSRF defenses, https-only redirects, size and time limits)
|
|
620
|
-
* live with that resolver, not here; see spec §
|
|
630
|
+
* live with that resolver, not here; see spec §27.14.5.
|
|
621
631
|
* ----------------------------------------------------------------------- */
|
|
622
632
|
/** Raised when a `from` source string is structurally disallowed. */
|
|
623
633
|
export class ModuleSourceError extends Error {
|
|
@@ -687,7 +697,7 @@ export function classifyModuleSource(from) {
|
|
|
687
697
|
* - A remote (https) source resolves to its normalized href. No '.xdbml'
|
|
688
698
|
* is appended (a raw-content URL may carry a query string).
|
|
689
699
|
* - A relative source whose importer is itself a remote module resolves
|
|
690
|
-
* against the importer's base URL per RFC 3986 (spec §
|
|
700
|
+
* against the importer's base URL per RFC 3986 (spec §27.14.1). A remote
|
|
691
701
|
* module therefore can never reach the local filesystem.
|
|
692
702
|
* - A relative source with a local importer resolves on the filesystem,
|
|
693
703
|
* exactly as in v0.2.
|
package/dist/name-resolver.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Name resolution pass (spec §
|
|
2
|
+
* Name resolution pass (spec §27.10 / §27.15, parser batch P6).
|
|
3
3
|
*
|
|
4
4
|
* `resolveNames(doc)` walks an xDBML document (the flattened view; clone
|
|
5
5
|
* blocks have been merged) and produces:
|
|
@@ -67,7 +67,7 @@ export interface SymbolEntry {
|
|
|
67
67
|
* Stable diagnostic code. Tooling can match on these to filter or style
|
|
68
68
|
* messages without parsing the human-readable text.
|
|
69
69
|
*/
|
|
70
|
-
export type DiagnosticCode = 'duplicate-declaration' | 'unresolved-type' | 'unresolved-entity' | 'unresolved-field' | 'unresolved-partial' | 'unresolved-tablegroup-member' | 'unresolved-records-entity' | 'unresolved-records-column' | 'empty-import' | 'invalid-nested-path';
|
|
70
|
+
export type DiagnosticCode = 'duplicate-declaration' | 'unresolved-type' | 'unresolved-entity' | 'unresolved-field' | 'unresolved-partial' | 'unresolved-tablegroup-member' | 'unresolved-records-entity' | 'unresolved-records-column' | 'empty-import' | 'invalid-nested-path' | 'foreign-master-composite' | 'foreign-master-duplicate-child' | 'foreign-master-without-ref' | 'construct-requires-version' | 'ambiguous-ref-endpoint' | 'invalid-constraint-type' | 'constraint-type-on-foreign-master' | 'invalid-undirected' | 'entity-level-many-to-many' | 'missing-supertype' | 'unresolved-supertype-group-member' | 'invalid-supertype-group-value' | 'duplicate-subtype' | 'supertype-is-subtype' | 'subtype-in-multiple-groups' | 'supertype-cycle' | 'supertype-attribute-redeclared' | 'discriminator-on-overlapping-group' | 'merge-without-roll-up' | 'empty-supertype-group';
|
|
71
71
|
/**
|
|
72
72
|
* A single resolution diagnostic. Severity is currently always `error`,
|
|
73
73
|
* but the field is included to leave room for future warnings (e.g.,
|
package/dist/name-resolver.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Name resolution pass (spec §
|
|
2
|
+
* Name resolution pass (spec §27.10 / §27.15, parser batch P6).
|
|
3
3
|
*
|
|
4
4
|
* `resolveNames(doc)` walks an xDBML document (the flattened view; clone
|
|
5
5
|
* blocks have been merged) and produces:
|
|
@@ -40,6 +40,8 @@
|
|
|
40
40
|
*/
|
|
41
41
|
import { SCALAR_TYPES, BSON_TYPES } from "./keywords.js";
|
|
42
42
|
import { flatten } from "./module-resolver.js";
|
|
43
|
+
import { checkRelationships } from "./relationships.js";
|
|
44
|
+
import { checkSupertypeGroups } from "./supertypes.js";
|
|
43
45
|
/**
|
|
44
46
|
* Read-only handle on the collected symbol table.
|
|
45
47
|
*
|
|
@@ -134,6 +136,10 @@ export function resolveNames(doc) {
|
|
|
134
136
|
const symbols = new SymbolTable(entries);
|
|
135
137
|
// Pass 2: resolve references.
|
|
136
138
|
resolveReferences(flat, symbols, diagnostics);
|
|
139
|
+
// Pass 3: relationship rules that the grammar cannot express (spec 11.11).
|
|
140
|
+
diagnostics.push(...checkRelationships(flat));
|
|
141
|
+
// Pass 4: supertype group rules (spec 12.8).
|
|
142
|
+
diagnostics.push(...checkSupertypeGroups(flat));
|
|
137
143
|
return { diagnostics, symbols };
|
|
138
144
|
}
|
|
139
145
|
/* -------------------------------------------------------------------------
|
|
@@ -507,8 +513,28 @@ function resolveRefSpec(endpoint, symbols, diagnostics) {
|
|
|
507
513
|
else {
|
|
508
514
|
maxEntityLen = leadingFields.length - 1;
|
|
509
515
|
}
|
|
510
|
-
|
|
516
|
+
// An entity-level endpoint (spec 11.16) names an entity and stops there:
|
|
517
|
+
// `Customer`, or `shop.orders` for an entity inside a container. It is
|
|
518
|
+
// checked as a fallback rather than first, so an endpoint that already
|
|
519
|
+
// resolved as entity-plus-attribute keeps that reading and every document
|
|
520
|
+
// that parsed before this means what it meant before.
|
|
521
|
+
const wholePath = leadingFields.join('.');
|
|
522
|
+
const entityLevelMatch = (!hasComposite && !hasNonFieldTail)
|
|
523
|
+
? resolveEntityRef(wholePath, symbols)
|
|
524
|
+
: undefined;
|
|
525
|
+
if (maxEntityLen < 1) {
|
|
526
|
+
// A single segment cannot be entity-plus-attribute, so it is either an
|
|
527
|
+
// entity-level endpoint or a reference to something undeclared.
|
|
528
|
+
if (!entityLevelMatch) {
|
|
529
|
+
diagnostics.push({
|
|
530
|
+
severity: 'error',
|
|
531
|
+
code: 'unresolved-entity',
|
|
532
|
+
message: `Relationship endpoint references unknown entity '${wholePath}'.`,
|
|
533
|
+
span: endpoint.span,
|
|
534
|
+
});
|
|
535
|
+
}
|
|
511
536
|
return;
|
|
537
|
+
}
|
|
512
538
|
// PHASE 1 (cont'd): longest-prefix entity match.
|
|
513
539
|
let entity;
|
|
514
540
|
let entityPrefixLen = 0;
|
|
@@ -522,15 +548,29 @@ function resolveRefSpec(endpoint, symbols, diagnostics) {
|
|
|
522
548
|
}
|
|
523
549
|
}
|
|
524
550
|
if (!entity) {
|
|
551
|
+
if (entityLevelMatch)
|
|
552
|
+
return; // entity-level endpoint; nothing further to check
|
|
525
553
|
const guess = leadingFields.slice(0, maxEntityLen).join('.');
|
|
526
554
|
diagnostics.push({
|
|
527
555
|
severity: 'error',
|
|
528
556
|
code: 'unresolved-entity',
|
|
529
|
-
message: `
|
|
557
|
+
message: `Relationship endpoint references unknown entity '${guess}'.`,
|
|
530
558
|
span: endpoint.span,
|
|
531
559
|
});
|
|
532
560
|
return;
|
|
533
561
|
}
|
|
562
|
+
if (entityLevelMatch) {
|
|
563
|
+
// Both readings exist: `a.b` names an entity, and `a` names an entity
|
|
564
|
+
// with a field `b`. The attribute reading wins, and the collision is
|
|
565
|
+
// reported so the author can qualify the path differently.
|
|
566
|
+
diagnostics.push({
|
|
567
|
+
severity: 'warning',
|
|
568
|
+
code: 'ambiguous-ref-endpoint',
|
|
569
|
+
message: `Endpoint '${wholePath}' names both an entity and a field of entity '${entity.qualifiedName}'. ` +
|
|
570
|
+
'Reading it as the field; rename one of them or qualify the path to remove the ambiguity.',
|
|
571
|
+
span: endpoint.span,
|
|
572
|
+
});
|
|
573
|
+
}
|
|
534
574
|
if (entity.declaration.kind !== 'EntityDeclaration')
|
|
535
575
|
return;
|
|
536
576
|
// PHASE 2: compute the post-entity portion of endpoint.path.
|
package/dist/parser.d.ts
CHANGED
|
@@ -13,8 +13,22 @@ import type { ParseOptions, Position, XDbmlDocument } from './ast.ts';
|
|
|
13
13
|
import type { Token } from './lexer.ts';
|
|
14
14
|
export declare class ParseError extends Error {
|
|
15
15
|
position: Position;
|
|
16
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Optional machine-readable reason. Set for the errors a caller may want
|
|
18
|
+
* to tell apart from a plain syntax error: `unsupported-version` when a
|
|
19
|
+
* document declares a newer version than this parser supports (spec 4.1).
|
|
20
|
+
*/
|
|
21
|
+
code?: string;
|
|
22
|
+
constructor(message: string, position: Position, code?: string);
|
|
17
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* The newest specification version this parser implements. A document
|
|
26
|
+
* declaring a later version is refused (spec 4.1) rather than parsed with
|
|
27
|
+
* semantics it does not have.
|
|
28
|
+
*/
|
|
29
|
+
export declare const SUPPORTED_XDBML_VERSION = "0.5";
|
|
30
|
+
/** Compare dotted version strings numerically: -1, 0 or 1. */
|
|
31
|
+
export declare function compareVersions(a: string, b: string): number;
|
|
18
32
|
export declare class Parser {
|
|
19
33
|
private tokens;
|
|
20
34
|
private idx;
|
|
@@ -30,7 +44,7 @@ export declare class Parser {
|
|
|
30
44
|
* The set of file paths currently being parsed in the resolution chain.
|
|
31
45
|
* Used for cycle detection: when resolving a directive whose `from` path
|
|
32
46
|
* is already in this set, the parser produces an empty clone for that
|
|
33
|
-
* directive rather than recursing (matching spec §
|
|
47
|
+
* directive rather than recursing (matching spec §27.15: cycles are
|
|
34
48
|
* allowed; name resolution handles them). The set is passed by reference
|
|
35
49
|
* across recursive parse() calls so all transitive levels see it.
|
|
36
50
|
*
|
|
@@ -83,13 +97,13 @@ export declare class Parser {
|
|
|
83
97
|
private parseEntityBody;
|
|
84
98
|
private parsePartialInjection;
|
|
85
99
|
/**
|
|
86
|
-
* Parse a `records { ... }` block inside an entity body (§
|
|
100
|
+
* Parse a `records { ... }` block inside an entity body (§26.1, implicit
|
|
87
101
|
* column list). Values are stored as SettingValue cells; row boundaries
|
|
88
102
|
* are determined by source line (see `parseRecordRow`).
|
|
89
103
|
*/
|
|
90
104
|
private parseRecordsBlock;
|
|
91
105
|
/**
|
|
92
|
-
* Top-level records declaration (§
|
|
106
|
+
* Top-level records declaration (§26.2, new in v0.2):
|
|
93
107
|
*
|
|
94
108
|
* records users (id, name, email) { ... }
|
|
95
109
|
* records core.users (id, name, email) { ... }
|
|
@@ -151,12 +165,12 @@ export declare class Parser {
|
|
|
151
165
|
* that match the import items by name and element type (matching is
|
|
152
166
|
* downstream-consumer's job; the parser is permissive).
|
|
153
167
|
*
|
|
154
|
-
* Per spec §
|
|
168
|
+
* Per spec §27.6, clone content uses the importing file's vocabulary
|
|
155
169
|
* (aliases already applied) and is parsed under the importing file's
|
|
156
170
|
* xdbml version directive.
|
|
157
171
|
*
|
|
158
172
|
* Most clone-block content uses TopLevelStatement shapes (Entity, Type,
|
|
159
|
-
* Container, etc.). The exception is field imports (§
|
|
173
|
+
* Container, etc.). The exception is field imports (§27.8): when the
|
|
160
174
|
* directive imports one or more fields via `field <path>` items, the
|
|
161
175
|
* clone block holds each field as a bare FieldDeclaration with no entity
|
|
162
176
|
* wrapper. The dispatch below checks whether the next token starts a
|
|
@@ -278,7 +292,7 @@ export declare class Parser {
|
|
|
278
292
|
private parseCardinalityOperator;
|
|
279
293
|
private parseRefEndpoint;
|
|
280
294
|
/**
|
|
281
|
-
* Parse a dotted path with the §
|
|
295
|
+
* Parse a dotted path with the §20 segment vocabulary:
|
|
282
296
|
*
|
|
283
297
|
* IDENTIFIER -- a field segment
|
|
284
298
|
* .IDENTIFIER -- field
|
|
@@ -297,6 +311,16 @@ export declare class Parser {
|
|
|
297
311
|
private parsePathSegments;
|
|
298
312
|
private parseTablePartial;
|
|
299
313
|
private parseTableGroup;
|
|
314
|
+
/**
|
|
315
|
+
* `SupertypeGroup <name> [supertype: X, ...] { Sub1 Sub2 [strategy: y] }`.
|
|
316
|
+
* Members are separated like TableGroup members: newline, comma or
|
|
317
|
+
* semicolon (spec §3.9). A name is required (§12.1); a writer exporting a
|
|
318
|
+
* group that has none emits `undefinedGroup1`, `undefinedGroup2`, ... so
|
|
319
|
+
* the parser never supplies one. Values are validated after parsing, by
|
|
320
|
+
* `checkSupertypeGroups()`, so an unknown value gets a located diagnostic
|
|
321
|
+
* rather than stopping the parse.
|
|
322
|
+
*/
|
|
323
|
+
private parseSupertypeGroup;
|
|
300
324
|
private parseIndexes;
|
|
301
325
|
private parseIndexEntry;
|
|
302
326
|
private parseIndexComponent;
|