@prisma/orm-framework 8.0.0-rc.17-dev.29 → 8.0.0-rc.17-dev.31

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.
@@ -5,10 +5,11 @@ import { r as createDataTypeLookup } from "./data-type-4xQlxYB5-DD158cOq.mjs";
5
5
  import { S as isAuthoringPslBlockDescriptor, f as describeTaggedLiteralFailure, x as isAuthoringModelAttributeDescriptor } from "./temporal-presets-DEyPX8qQ-Bz4DdZ8v.mjs";
6
6
  import { _ as readWrittenValue, a as describeExpected, d as exactRewrite, i as describeAdmittedForms, n as admittedTags, o as describeRefusal, r as castTypedValue, t as admittedForms, v as tagForm } from "./authoring-Bv-ExMUC.mjs";
7
7
  import { a as flatPslModels, c as namespacePslExtensionBlocks } from "./psl-ast-CyvE6CR-.mjs";
8
+ import { l as printSyntax } from "./ast-helpers-CzydMPdh-Mi0SrZRa.mjs";
8
9
  import { n as isPslIdentifier } from "./tokenizer-C-GTKKjR-B1vOzJy0.mjs";
9
- import { D as TypesBlockAst, S as PathExprAst, T as TaggedLiteralExprAst, a as CompositeTypeDeclarationAst, d as IdentifierAst, g as ModelDeclarationAst, i as BooleanLiteralExprAst, l as FunctionCallAst, p as MixinDeclarationAst, t as ArrayLiteralAst, u as GenericBlockDeclarationAst, v as NamespaceDeclarationAst, w as StringLiteralExprAst, x as ObjectLiteralExprAst, y as NumberLiteralExprAst } from "./declarations-CTFzoo9j-BaCuW9E2.mjs";
10
+ import { D as TypesBlockAst, S as PathExprAst, T as TaggedLiteralExprAst, a as CompositeTypeDeclarationAst, c as FieldDeclarationAst, d as IdentifierAst, f as KeyValuePairAst, g as ModelDeclarationAst, h as ModelAttributeAst, i as BooleanLiteralExprAst, l as FunctionCallAst, p as MixinDeclarationAst, t as ArrayLiteralAst, u as GenericBlockDeclarationAst, v as NamespaceDeclarationAst, w as StringLiteralExprAst, x as ObjectLiteralExprAst, y as NumberLiteralExprAst } from "./declarations-DuSbyF44-Bee8mK-C.mjs";
10
11
  import { a as mapPslDiagnostics, c as readResolvedAttributes, i as keywordPslSpan, l as readResolvedConstructorCall, n as diagnosticFromSpan, o as nodePslSpan, r as diagnosticSource, s as readResolvedAttribute, t as createPslDiagnosticCollector } from "./resolve-C8PUf8zv-QAvnPhCt.mjs";
11
- //#region ../../../1-framework/2-authoring/psl-parser/dist/symbol-table-DzoniUhK.mjs
12
+ //#region ../../../1-framework/2-authoring/psl-parser/dist/binder-DNpvbbBT.mjs
12
13
  function* modelAttributeDescriptors(namespace) {
13
14
  for (const value of Object.values(namespace)) {
14
15
  if (isAuthoringModelAttributeDescriptor(value)) {
@@ -265,6 +266,12 @@ function namespaceMember(namespace, name) {
265
266
  symbol: block,
266
267
  namespace
267
268
  };
269
+ const mixin = namespace.mixins[name];
270
+ if (mixin !== void 0) return {
271
+ kind: "mixin",
272
+ symbol: mixin,
273
+ namespace
274
+ };
268
275
  }
269
276
  var ContributedScope = class extends Scope {
270
277
  #registry;
@@ -308,6 +315,11 @@ var DocumentScope = class extends Scope {
308
315
  kind: "block",
309
316
  symbol: block
310
317
  };
318
+ const mixin = records.mixins[name];
319
+ if (mixin !== void 0) return {
320
+ kind: "mixin",
321
+ symbol: mixin
322
+ };
311
323
  const namespace = records.namespaces[name];
312
324
  if (namespace !== void 0) return {
313
325
  kind: "namespace",
@@ -317,7 +329,7 @@ var DocumentScope = class extends Scope {
317
329
  }
318
330
  ownNames() {
319
331
  const records = this.#records;
320
- return recordNames(records.models, records.compositeTypes, records.namedTypes, records.blocks, records.namespaces);
332
+ return recordNames(records.models, records.compositeTypes, records.namedTypes, records.blocks, records.mixins, records.namespaces);
321
333
  }
322
334
  };
323
335
  var NamespaceScope = class extends Scope {
@@ -331,7 +343,7 @@ var NamespaceScope = class extends Scope {
331
343
  }
332
344
  ownNames() {
333
345
  const namespace = this.#namespace;
334
- return recordNames(namespace.models, namespace.compositeTypes, namespace.blocks);
346
+ return recordNames(namespace.models, namespace.compositeTypes, namespace.blocks, namespace.mixins);
335
347
  }
336
348
  };
337
349
  function contributedScope(registry) {
@@ -366,6 +378,355 @@ function lookupMember(qualifier, name) {
366
378
  }
367
379
  return namespaceMember(qualifier.symbol, name);
368
380
  }
381
+ /**
382
+ * Owns duplicate-declaration detection for all PSL scopes; downstream consumers
383
+ * should consume first-wins symbols rather than re-emitting duplicate diagnostics.
384
+ */
385
+ function buildSymbolTable(options) {
386
+ const { documents, sources } = options;
387
+ const diagnostics = [];
388
+ const namespaces = Object.create(null);
389
+ const namedTypes = Object.create(null);
390
+ const blocks = Object.create(null);
391
+ const models = Object.create(null);
392
+ const compositeTypes = Object.create(null);
393
+ const mixins = Object.create(null);
394
+ const topLevelNames = /* @__PURE__ */ new Set();
395
+ for (const document of documents) {
396
+ const sourceFile = sources.sourceFileFor(document.syntax);
397
+ const claim = (taken, name) => {
398
+ const text = name?.name();
399
+ if (text === void 0) return void 0;
400
+ if (taken.has(text)) {
401
+ const range = nameRange(name, sources);
402
+ if (range) diagnostics.push({
403
+ code: "PSL_DUPLICATE_DECLARATION",
404
+ message: `Duplicate declaration of "${text}"`,
405
+ filename: sourceFile.filename,
406
+ range
407
+ });
408
+ return;
409
+ }
410
+ taken.add(text);
411
+ return text;
412
+ };
413
+ for (const declaration of document.declarations()) if (declaration instanceof ModelDeclarationAst) {
414
+ const name = claim(topLevelNames, declaration.name());
415
+ if (name !== void 0) models[name] = buildModel(name, declaration, sources, diagnostics);
416
+ } else if (declaration instanceof CompositeTypeDeclarationAst) {
417
+ const name = claim(topLevelNames, declaration.name());
418
+ if (name !== void 0) compositeTypes[name] = buildCompositeType(name, declaration, sources, diagnostics);
419
+ } else if (declaration instanceof GenericBlockDeclarationAst) {
420
+ const name = claim(topLevelNames, declaration.name());
421
+ if (name !== void 0) blocks[name] = buildBlock(name, declaration, sources);
422
+ } else if (declaration instanceof MixinDeclarationAst) {
423
+ const name = claim(topLevelNames, declaration.name());
424
+ if (name !== void 0) mixins[name] = buildMixin(name, declaration, sources, diagnostics);
425
+ } else if (declaration instanceof NamespaceDeclarationAst) {
426
+ const declaredName = declaration.name()?.name();
427
+ if (declaredName === void 0) continue;
428
+ let namespace = namespaces[declaredName];
429
+ if (namespace === void 0) {
430
+ const name = claim(topLevelNames, declaration.name());
431
+ if (name === void 0) continue;
432
+ namespace = {
433
+ kind: "namespace",
434
+ name,
435
+ declarations: [{
436
+ node: declaration,
437
+ span: nodePslSpan(declaration.syntax, sources)
438
+ }],
439
+ models: Object.create(null),
440
+ compositeTypes: Object.create(null),
441
+ blocks: Object.create(null),
442
+ mixins: Object.create(null)
443
+ };
444
+ namespaces[name] = namespace;
445
+ } else namespace.declarations.push({
446
+ node: declaration,
447
+ span: nodePslSpan(declaration.syntax, sources)
448
+ });
449
+ extendNamespace(namespace, declaration, diagnostics, sources);
450
+ } else if (declaration instanceof TypesBlockAst) for (const binding of declaration.declarations()) {
451
+ const name = claim(topLevelNames, binding.name());
452
+ if (name === void 0) continue;
453
+ const resolved = resolveNamedTypeBinding(binding, sources);
454
+ namedTypes[name] = {
455
+ kind: "namedType",
456
+ name,
457
+ node: binding,
458
+ span: nodePslSpan(binding.syntax, sources),
459
+ ...resolved
460
+ };
461
+ }
462
+ }
463
+ return {
464
+ symbolTable: { topLevel: {
465
+ namespaces,
466
+ namedTypes,
467
+ blocks,
468
+ models,
469
+ compositeTypes,
470
+ mixins
471
+ } },
472
+ diagnostics
473
+ };
474
+ }
475
+ function buildModel(name, node, sources, diagnostics) {
476
+ return {
477
+ kind: "model",
478
+ name,
479
+ node,
480
+ span: nodePslSpan(node.syntax, sources),
481
+ fields: buildFields(name, node.fields(), sources, diagnostics),
482
+ attributes: readResolvedAttributes(node.attributes(), sources)
483
+ };
484
+ }
485
+ function buildCompositeType(name, node, sources, diagnostics) {
486
+ return {
487
+ kind: "compositeType",
488
+ name,
489
+ node,
490
+ span: nodePslSpan(node.syntax, sources),
491
+ fields: buildFields(name, node.fields(), sources, diagnostics),
492
+ attributes: readResolvedAttributes(node.attributes(), sources)
493
+ };
494
+ }
495
+ function buildBlock(name, node, sources) {
496
+ return {
497
+ kind: "block",
498
+ name,
499
+ keyword: node.keyword()?.text ?? "",
500
+ node,
501
+ span: nodePslSpan(node.syntax, sources),
502
+ entries: Array.from(node.entries()),
503
+ attributes: readResolvedAttributes(node.attributes(), sources)
504
+ };
505
+ }
506
+ const FIELD_BLOCK_KEYWORDS = /* @__PURE__ */ new Set(["model", "type"]);
507
+ function buildMixin(name, node, sources, diagnostics) {
508
+ const keyword = node.keyword()?.text ?? "";
509
+ const hasFields = FIELD_BLOCK_KEYWORDS.has(keyword);
510
+ return {
511
+ kind: "mixin",
512
+ name,
513
+ keyword,
514
+ node,
515
+ span: nodePslSpan(node.syntax, sources),
516
+ fields: hasFields ? buildFields(name, node.fields(), sources, diagnostics) : Object.create(null),
517
+ entries: hasFields ? [] : Array.from(node.entries()),
518
+ attributes: readResolvedAttributes(node.attributes(), sources)
519
+ };
520
+ }
521
+ function extendNamespace(namespace, node, diagnostics, sources) {
522
+ const { models, compositeTypes, blocks, mixins } = namespace;
523
+ for (const member of node.declarations()) {
524
+ const memberName = member.name()?.name();
525
+ if (memberName === void 0) continue;
526
+ if (Object.hasOwn(models, memberName) || Object.hasOwn(compositeTypes, memberName) || Object.hasOwn(blocks, memberName) || Object.hasOwn(mixins, memberName)) {
527
+ const range = nameRange(member.name(), sources);
528
+ if (range) diagnostics.push({
529
+ code: "PSL_DUPLICATE_DECLARATION",
530
+ message: `Duplicate declaration of "${memberName}"`,
531
+ filename: sources.sourceFileFor(member.syntax).filename,
532
+ range
533
+ });
534
+ continue;
535
+ }
536
+ if (member instanceof ModelDeclarationAst) models[memberName] = buildModel(memberName, member, sources, diagnostics);
537
+ else if (member instanceof CompositeTypeDeclarationAst) compositeTypes[memberName] = buildCompositeType(memberName, member, sources, diagnostics);
538
+ else if (member instanceof GenericBlockDeclarationAst) blocks[memberName] = buildBlock(memberName, member, sources);
539
+ else if (member instanceof MixinDeclarationAst) mixins[memberName] = buildMixin(memberName, member, sources, diagnostics);
540
+ }
541
+ }
542
+ function placeMixinMembers(input) {
543
+ const { topLevel } = input.symbolTable;
544
+ const diagnostics = [];
545
+ for (const records of [topLevel, ...Object.values(topLevel.namespaces)]) {
546
+ for (const symbol of Object.values(records.models)) placeFields(symbol, input, diagnostics);
547
+ for (const symbol of Object.values(records.compositeTypes)) placeFields(symbol, input, diagnostics);
548
+ for (const symbol of Object.values(records.blocks)) placeEntries(symbol, input, diagnostics);
549
+ }
550
+ return diagnostics;
551
+ }
552
+ function includesMixin(symbol, input) {
553
+ for (const inclusion of symbol.node.inclusions()) if (input.mixinOf(inclusion) !== void 0) return true;
554
+ return false;
555
+ }
556
+ function inclusionDiagnostic(code, message, inclusion, sources) {
557
+ return {
558
+ code,
559
+ message,
560
+ filename: sources.sourceFileFor(inclusion.syntax).filename,
561
+ range: nodeRange(inclusion.syntax, sources)
562
+ };
563
+ }
564
+ function mixinToPlace(inclusion, symbol, placed, input, diagnostics) {
565
+ const mixin = input.mixinOf(inclusion);
566
+ if (mixin === void 0) return void 0;
567
+ if (placed.has(mixin)) {
568
+ diagnostics.push(inclusionDiagnostic(symbol.kind === "block" ? "PSL_INVALID_EXTENSION_BLOCK_MEMBER" : "PSL_INVALID_MODEL_MEMBER", `Mixin "${mixin.name}" is already included in this block`, inclusion, input.sources));
569
+ return;
570
+ }
571
+ placed.add(mixin);
572
+ return mixin;
573
+ }
574
+ function alreadyProvided(mixin, member, symbol, inclusion, sources) {
575
+ return inclusionDiagnostic("PSL_DUPLICATE_DECLARATION", `Mixin "${mixin.name}" provides "${member}", which "${symbol.name}" already has`, inclusion, sources);
576
+ }
577
+ function writtenAttributes(symbol) {
578
+ return new Map(symbol.attributes.map((attribute) => [attribute.node.syntax, attribute]));
579
+ }
580
+ function placeFields(symbol, input, diagnostics) {
581
+ if (!includesMixin(symbol, input)) return;
582
+ const { sources } = input;
583
+ const ownFields = new Map(Object.values(symbol.fields).map((field) => [field.node.syntax, field]));
584
+ const ownAttributes = writtenAttributes(symbol);
585
+ const fields = Object.create(null);
586
+ const attributes = [];
587
+ const placed = /* @__PURE__ */ new Set();
588
+ for (const member of symbol.node.members()) if (member instanceof FieldDeclarationAst) {
589
+ const field = ownFields.get(member.syntax);
590
+ if (field === void 0) continue;
591
+ if (Object.hasOwn(fields, field.name)) {
592
+ const diagnostic = duplicateDeclaration(field.name, member.name(), sources);
593
+ if (diagnostic !== void 0) diagnostics.push(diagnostic);
594
+ } else fields[field.name] = field;
595
+ } else if (member instanceof ModelAttributeAst) {
596
+ const attribute = ownAttributes.get(member.syntax);
597
+ if (attribute !== void 0) attributes.push(attribute);
598
+ } else {
599
+ const mixin = mixinToPlace(member, symbol, placed, input, diagnostics);
600
+ if (mixin === void 0) continue;
601
+ for (const field of Object.values(mixin.fields)) if (Object.hasOwn(fields, field.name)) diagnostics.push(alreadyProvided(mixin, field.name, symbol, member, sources));
602
+ else fields[field.name] = field;
603
+ attributes.push(...mixin.attributes);
604
+ }
605
+ symbol.fields = fields;
606
+ symbol.attributes = attributes;
607
+ }
608
+ function placeEntries(symbol, input, diagnostics) {
609
+ if (!includesMixin(symbol, input)) return;
610
+ const ownAttributes = writtenAttributes(symbol);
611
+ const entries = [];
612
+ const attributes = [];
613
+ const placed = /* @__PURE__ */ new Set();
614
+ for (const member of symbol.node.members()) if (member instanceof KeyValuePairAst) entries.push(member);
615
+ else if (member instanceof ModelAttributeAst) {
616
+ const attribute = ownAttributes.get(member.syntax);
617
+ if (attribute !== void 0) attributes.push(attribute);
618
+ } else {
619
+ const mixin = mixinToPlace(member, symbol, placed, input, diagnostics);
620
+ if (mixin === void 0) continue;
621
+ const present = new Set(entries.map((entry) => entry.key()?.name()));
622
+ for (const entry of mixin.entries) {
623
+ const key = entry.key()?.name();
624
+ if (key !== void 0 && present.has(key)) diagnostics.push(alreadyProvided(mixin, key, symbol, member, input.sources));
625
+ else entries.push(entry);
626
+ }
627
+ attributes.push(...mixin.attributes);
628
+ }
629
+ symbol.entries = entries;
630
+ symbol.attributes = attributes;
631
+ }
632
+ function duplicateDeclaration(name, nameNode, sources) {
633
+ const range = nameRange(nameNode, sources);
634
+ if (nameNode === void 0 || range === void 0) return void 0;
635
+ return {
636
+ code: "PSL_DUPLICATE_DECLARATION",
637
+ message: `Duplicate declaration of "${name}"`,
638
+ filename: sources.sourceFileFor(nameNode.syntax).filename,
639
+ range
640
+ };
641
+ }
642
+ function buildFields(ownerName, fields, sources, diagnostics) {
643
+ const result = Object.create(null);
644
+ for (const field of fields) {
645
+ const nameNode = field.name();
646
+ const name = nameNode?.name();
647
+ if (name === void 0) continue;
648
+ if (Object.hasOwn(result, name)) {
649
+ const diagnostic = duplicateDeclaration(name, nameNode, sources);
650
+ if (diagnostic !== void 0) diagnostics.push(diagnostic);
651
+ continue;
652
+ }
653
+ result[name] = buildField(ownerName, name, field, sources, diagnostics);
654
+ }
655
+ return result;
656
+ }
657
+ function buildField(ownerName, name, node, sources, diagnostics) {
658
+ const attributes = readResolvedAttributes(node.attributes(), sources);
659
+ const span = nodePslSpan(node.syntax, sources);
660
+ const annotation = node.typeAnnotation();
661
+ const typeName = annotation?.name();
662
+ if (typeName?.isOverQualified()) {
663
+ const path = typeName.path();
664
+ diagnostics.push({
665
+ code: "PSL_INVALID_QUALIFIED_TYPE",
666
+ message: `Field "${ownerName}.${name}" has an invalid qualified type "${path.join(".")}"; use at most one namespace qualifier (e.g. "ns.TypeName")`,
667
+ filename: sources.sourceFileFor(typeName.syntax).filename,
668
+ range: nodeRange(typeName.syntax, sources)
669
+ });
670
+ return {
671
+ kind: "field",
672
+ name,
673
+ node,
674
+ span,
675
+ typeName: path[path.length - 1] ?? "",
676
+ optional: false,
677
+ list: false,
678
+ elementOptional: false,
679
+ malformedType: true,
680
+ attributes
681
+ };
682
+ }
683
+ const typeConstructor = annotation?.isConstructor() ? readResolvedConstructorCall(annotation, sources) : void 0;
684
+ const typeNamespaceId = typeName?.namespace()?.name();
685
+ const typeContractSpaceId = typeName?.space()?.name();
686
+ return {
687
+ kind: "field",
688
+ name,
689
+ node,
690
+ span,
691
+ typeName: typeName?.identifier()?.name() ?? "",
692
+ ...typeNamespaceId !== void 0 ? { typeNamespaceId } : {},
693
+ ...typeContractSpaceId !== void 0 ? { typeContractSpaceId } : {},
694
+ optional: annotation?.isOptional() ?? false,
695
+ list: annotation?.isList() ?? false,
696
+ elementOptional: annotation?.isElementOptional() ?? false,
697
+ ...typeConstructor !== void 0 ? { typeConstructor } : {},
698
+ attributes
699
+ };
700
+ }
701
+ function resolveNamedTypeBinding(node, sources) {
702
+ const annotation = node.typeAnnotation();
703
+ const isConstructor = annotation?.isConstructor() ?? false;
704
+ const baseType = annotation?.name()?.identifier()?.name();
705
+ const typeConstructor = readResolvedConstructorCall(annotation, sources);
706
+ return {
707
+ isConstructor,
708
+ ...!isConstructor && baseType !== void 0 ? { baseType } : {},
709
+ ...typeConstructor !== void 0 ? { typeConstructor } : {},
710
+ attributes: readResolvedAttributes(node.attributes(), sources)
711
+ };
712
+ }
713
+ function nameRange(name, sources) {
714
+ if (name === void 0) return void 0;
715
+ const sourceFile = sources.sourceFileFor(name.syntax);
716
+ for (const token of name.syntax.tokens()) if (token.kind === "Ident") return {
717
+ start: sourceFile.positionAt(token.offset),
718
+ end: sourceFile.positionAt(token.offset + token.text.length)
719
+ };
720
+ }
721
+ function nodeRange(node, sources) {
722
+ const sourceFile = sources.sourceFileFor(node);
723
+ const start = node.offset;
724
+ const end = start + node.green.textLength;
725
+ return {
726
+ start: sourceFile.positionAt(start),
727
+ end: sourceFile.positionAt(end)
728
+ };
729
+ }
369
730
  const PSL_UNRESOLVED_REFERENCE = "PSL_UNRESOLVED_REFERENCE";
370
731
  function typeReferenceNode(symbol) {
371
732
  return symbol.node.typeAnnotation()?.name()?.syntax;
@@ -413,17 +774,33 @@ var ScopeStack = class {
413
774
  this.#scopes.pop();
414
775
  }
415
776
  };
416
- function walkEntities(symbolTable, stack, binder, visit) {
777
+ const FIELD_MIXIN_KEYWORDS = /* @__PURE__ */ new Set(["model", "type"]);
778
+ function hasFields(mixin) {
779
+ return FIELD_MIXIN_KEYWORDS.has(mixin.keyword);
780
+ }
781
+ function walkFieldOwners(symbolTable, stack, binder, visit) {
417
782
  const { topLevel } = symbolTable;
418
- for (const entity of Object.values(topLevel.models)) visit(entity);
419
- for (const entity of Object.values(topLevel.compositeTypes)) visit(entity);
783
+ const visitAll = (records, namespace) => {
784
+ for (const entity of Object.values(records.models)) visit(entity, namespace);
785
+ for (const entity of Object.values(records.compositeTypes)) visit(entity, namespace);
786
+ for (const mixin of Object.values(records.mixins)) if (hasFields(mixin)) visit(mixin, namespace);
787
+ };
788
+ visitAll(topLevel);
420
789
  for (const namespace of Object.values(topLevel.namespaces)) {
421
790
  stack.push(binder.scopeAt(namespace.declarations[0].node.syntax));
422
- for (const entity of Object.values(namespace.models)) visit(entity, namespace);
423
- for (const entity of Object.values(namespace.compositeTypes)) visit(entity, namespace);
791
+ visitAll(namespace, namespace);
424
792
  stack.pop();
425
793
  }
426
794
  }
795
+ function isWrittenIn(member, holder) {
796
+ return member.parent === holder.node.syntax;
797
+ }
798
+ function ownFields(owner) {
799
+ return Object.values(owner.fields).filter((field) => isWrittenIn(field.node.syntax, owner));
800
+ }
801
+ function ownAttributes(holder) {
802
+ return holder.attributes.filter((attribute) => isWrittenIn(attribute.node.syntax, holder));
803
+ }
427
804
  function declarationResolution(entity, namespace) {
428
805
  return entity.kind === "model" ? {
429
806
  kind: "model",
@@ -490,6 +867,14 @@ function bind(options) {
490
867
  symbol
491
868
  });
492
869
  }
870
+ for (const symbol of Object.values(symbolTable.topLevel.mixins)) {
871
+ declarations.set(symbol.node.syntax, symbol);
872
+ const declaredName = symbol.node.name()?.syntax;
873
+ if (declaredName !== void 0) references.set(declaredName, {
874
+ kind: "mixin",
875
+ symbol
876
+ });
877
+ }
493
878
  for (const namespace of Object.values(symbolTable.topLevel.namespaces)) {
494
879
  const scope = namespaceScope(namespace, document);
495
880
  for (const declaration of namespace.declarations) {
@@ -510,12 +895,37 @@ function bind(options) {
510
895
  namespace
511
896
  });
512
897
  }
513
- }
514
- walkEntities(symbolTable, stack, binder, (entity, namespace) => {
515
- declarations.set(entity.node.syntax, entity);
516
- const declaredName = entity.node.name()?.syntax;
517
- if (declaredName !== void 0) references.set(declaredName, declarationResolution(entity, namespace));
518
- for (const field of Object.values(entity.fields)) {
898
+ for (const symbol of Object.values(namespace.mixins)) {
899
+ declarations.set(symbol.node.syntax, symbol);
900
+ const declaredName = symbol.node.name()?.syntax;
901
+ if (declaredName !== void 0) references.set(declaredName, {
902
+ kind: "mixin",
903
+ symbol,
904
+ namespace
905
+ });
906
+ }
907
+ }
908
+ const included = /* @__PURE__ */ new Map();
909
+ const inclusionContext = {
910
+ sources,
911
+ references,
912
+ diagnostics,
913
+ included
914
+ };
915
+ bindInclusions(symbolTable.topLevel, document, inclusionContext);
916
+ for (const namespace of Object.values(symbolTable.topLevel.namespaces)) bindInclusions(namespace, binder.scopeAt(namespace.declarations[0].node.syntax), inclusionContext);
917
+ diagnostics.push(...placeMixinMembers({
918
+ symbolTable,
919
+ sources,
920
+ mixinOf: (inclusion) => included.get(inclusion.syntax)
921
+ }));
922
+ walkFieldOwners(symbolTable, stack, binder, (entity, namespace) => {
923
+ if (entity.kind !== "mixin") {
924
+ declarations.set(entity.node.syntax, entity);
925
+ const declaredName = entity.node.name()?.syntax;
926
+ if (declaredName !== void 0) references.set(declaredName, declarationResolution(entity, namespace));
927
+ }
928
+ for (const field of ownFields(entity)) {
519
929
  declarations.set(field.node.syntax, field);
520
930
  const fieldName = field.node.name()?.syntax;
521
931
  if (fieldName !== void 0) references.set(fieldName, {
@@ -550,7 +960,7 @@ function bind(options) {
550
960
  });
551
961
  }
552
962
  });
553
- walkEntities(symbolTable, stack, binder, (entity) => {
963
+ walkFieldOwners(symbolTable, stack, binder, (entity) => {
554
964
  const context = {
555
965
  owner: entity,
556
966
  scope: stack.current(),
@@ -560,17 +970,17 @@ function bind(options) {
560
970
  sources,
561
971
  describeUnsupportedAttribute
562
972
  };
563
- const specContext = entity.kind === "model" ? {
973
+ const specContext = entity.kind === "model" || entity.kind === "mixin" && entity.keyword === "model" ? {
564
974
  symbols: symbolTable,
565
975
  model: entity,
566
976
  defaultFunctionRegistry,
567
977
  dataTypes
568
978
  } : void 0;
569
- bindAttributes(entity, entity.attributes, attributeSpecs.model, (factory) => specContext === void 0 ? void 0 : factory(specContext), {
979
+ bindAttributes(entity, ownAttributes(entity), attributeSpecs.model, (factory) => specContext === void 0 ? void 0 : factory(specContext), {
570
980
  ...context,
571
981
  field: void 0
572
982
  });
573
- for (const field of Object.values(entity.fields)) bindAttributes(field, field.attributes, attributeSpecs.field, (factory) => {
983
+ for (const field of ownFields(entity)) bindAttributes(field, field.attributes, attributeSpecs.field, (factory) => {
574
984
  if (specContext === void 0) return void 0;
575
985
  const node = typeReferenceNode(field);
576
986
  const typeResolution = node === void 0 ? void 0 : references.get(node);
@@ -592,17 +1002,22 @@ function bind(options) {
592
1002
  references,
593
1003
  diagnostics
594
1004
  };
595
- const bindBlocks = (blocks) => {
1005
+ const bindBlocks = (records) => {
596
1006
  const context = {
597
1007
  ...blockContext,
598
1008
  scope: stack.current()
599
1009
  };
600
- for (const block of Object.values(blocks)) bindBlock(block, context);
601
- };
602
- bindBlocks(symbolTable.topLevel.blocks);
1010
+ for (const block of Object.values(records.blocks)) bindBlock({
1011
+ keyword: block.keyword,
1012
+ entries: block.entries.filter((entry) => isWrittenIn(entry.syntax, block)),
1013
+ attributes: ownAttributes(block)
1014
+ }, context);
1015
+ for (const mixin of Object.values(records.mixins)) if (!hasFields(mixin)) bindBlock(mixin, context);
1016
+ };
1017
+ bindBlocks(symbolTable.topLevel);
603
1018
  for (const namespace of Object.values(symbolTable.topLevel.namespaces)) {
604
1019
  stack.push(binder.scopeAt(namespace.declarations[0].node.syntax));
605
- bindBlocks(namespace.blocks);
1020
+ bindBlocks(namespace);
606
1021
  stack.pop();
607
1022
  }
608
1023
  return {
@@ -610,6 +1025,49 @@ function bind(options) {
610
1025
  diagnostics
611
1026
  };
612
1027
  }
1028
+ function bindInclusions(records, scope, ctx) {
1029
+ const report = (code, message, inclusion) => {
1030
+ ctx.diagnostics.push({
1031
+ code,
1032
+ message,
1033
+ ...diagnosticSource(ctx.sources, inclusion.syntax).at()
1034
+ });
1035
+ };
1036
+ for (const mixin of Object.values(records.mixins)) for (const inclusion of mixin.node.inclusions()) {
1037
+ if (inclusion.name() === void 0) continue;
1038
+ report(hasFields(mixin) ? "PSL_INVALID_MODEL_MEMBER" : "PSL_INVALID_EXTENSION_BLOCK_MEMBER", "A mixin cannot include another mixin", inclusion);
1039
+ }
1040
+ const holders = [
1041
+ ...Object.values(records.models),
1042
+ ...Object.values(records.compositeTypes),
1043
+ ...Object.values(records.blocks)
1044
+ ];
1045
+ for (const holder of holders) {
1046
+ const keyword = holder.kind === "model" ? "model" : holder.kind === "compositeType" ? "type" : holder.keyword;
1047
+ for (const inclusion of holder.node.inclusions()) {
1048
+ const reference = inclusion.name();
1049
+ const outcome = resolveTypeReference(reference, scope, ctx.references);
1050
+ if (reference === void 0 || outcome === void 0) continue;
1051
+ const { resolution } = outcome;
1052
+ if (resolution.kind !== "mixin") {
1053
+ const name = printSyntax(reference.syntax);
1054
+ ctx.references.set(reference.syntax, {
1055
+ kind: "unresolved",
1056
+ name
1057
+ });
1058
+ report(PSL_UNRESOLVED_REFERENCE, `Cannot find mixin "${name}"`, inclusion);
1059
+ continue;
1060
+ }
1061
+ ctx.references.set(reference.syntax, resolution);
1062
+ const mixin = resolution.symbol;
1063
+ if (mixin.keyword === keyword) {
1064
+ ctx.included.set(inclusion.syntax, mixin);
1065
+ continue;
1066
+ }
1067
+ report(holder.kind === "block" ? "PSL_INVALID_EXTENSION_BLOCK_MEMBER" : "PSL_INVALID_MODEL_MEMBER", `Mixin "${mixin.name}" is for "${mixin.keyword}" blocks, not "${keyword}" blocks`, inclusion);
1068
+ }
1069
+ }
1070
+ }
613
1071
  function bindBlock(block, ctx) {
614
1072
  const descriptor = findBlockDescriptor(ctx.pslBlockDescriptors, block.keyword);
615
1073
  if (descriptor === void 0) return;
@@ -1062,11 +1520,11 @@ function resolveTypeReference(reference, scope, references, describeUnresolved)
1062
1520
  message: found.badQualifier,
1063
1521
  name: found.qualifier
1064
1522
  };
1065
- if (found.kind === "namespace" || found.kind === "contributedNamespace") {
1523
+ if (found.kind === "namespace" || found.kind === "contributedNamespace" || found.kind === "mixin") {
1066
1524
  const written = namespaceId === void 0 ? name : `${namespaceId}.${name}`;
1067
1525
  return {
1068
1526
  resolution: found,
1069
- message: `"${written}" is a namespace; a type reference must name a model, composite type, enum, or named type`,
1527
+ message: `"${written}" is a ${found.kind === "mixin" ? "mixin" : "namespace"}; a type reference must name a model, composite type, enum, or named type`,
1070
1528
  name: written
1071
1529
  };
1072
1530
  }
@@ -1092,278 +1550,6 @@ function describeQualifier(resolution) {
1092
1550
  function own(record, name) {
1093
1551
  return Object.hasOwn(record, name) ? record[name] : void 0;
1094
1552
  }
1095
- /**
1096
- * Owns duplicate-declaration detection for all PSL scopes; downstream consumers
1097
- * should consume first-wins symbols rather than re-emitting duplicate diagnostics.
1098
- */
1099
- function buildSymbolTable(options) {
1100
- const { documents, sources } = options;
1101
- const diagnostics = [];
1102
- const namespaces = Object.create(null);
1103
- const namedTypes = Object.create(null);
1104
- const blocks = Object.create(null);
1105
- const models = Object.create(null);
1106
- const compositeTypes = Object.create(null);
1107
- const topLevelNames = /* @__PURE__ */ new Set();
1108
- for (const document of documents) {
1109
- const sourceFile = sources.sourceFileFor(document.syntax);
1110
- const claim = (taken, name) => {
1111
- const text = name?.name();
1112
- if (text === void 0) return void 0;
1113
- if (taken.has(text)) {
1114
- const range = nameRange(name, sources);
1115
- if (range) diagnostics.push({
1116
- code: "PSL_DUPLICATE_DECLARATION",
1117
- message: `Duplicate declaration of "${text}"`,
1118
- filename: sourceFile.filename,
1119
- range
1120
- });
1121
- return;
1122
- }
1123
- taken.add(text);
1124
- return text;
1125
- };
1126
- for (const declaration of document.declarations()) if (declaration instanceof ModelDeclarationAst) {
1127
- const name = claim(topLevelNames, declaration.name());
1128
- if (name !== void 0) models[name] = buildModel(name, declaration, sources, diagnostics);
1129
- } else if (declaration instanceof CompositeTypeDeclarationAst) {
1130
- const name = claim(topLevelNames, declaration.name());
1131
- if (name !== void 0) compositeTypes[name] = buildCompositeType(name, declaration, sources, diagnostics);
1132
- } else if (declaration instanceof GenericBlockDeclarationAst) {
1133
- const name = claim(topLevelNames, declaration.name());
1134
- if (name !== void 0) blocks[name] = buildBlock(name, declaration, sources);
1135
- } else if (declaration instanceof NamespaceDeclarationAst) {
1136
- const declaredName = declaration.name()?.name();
1137
- if (declaredName === void 0) continue;
1138
- let namespace = namespaces[declaredName];
1139
- if (namespace === void 0) {
1140
- const name = claim(topLevelNames, declaration.name());
1141
- if (name === void 0) continue;
1142
- namespace = {
1143
- kind: "namespace",
1144
- name,
1145
- declarations: [{
1146
- node: declaration,
1147
- span: nodePslSpan(declaration.syntax, sources)
1148
- }],
1149
- models: Object.create(null),
1150
- compositeTypes: Object.create(null),
1151
- blocks: Object.create(null)
1152
- };
1153
- namespaces[name] = namespace;
1154
- } else namespace.declarations.push({
1155
- node: declaration,
1156
- span: nodePslSpan(declaration.syntax, sources)
1157
- });
1158
- extendNamespace(namespace, declaration, diagnostics, sources);
1159
- } else if (declaration instanceof TypesBlockAst) for (const binding of declaration.declarations()) {
1160
- const name = claim(topLevelNames, binding.name());
1161
- if (name === void 0) continue;
1162
- const resolved = resolveNamedTypeBinding(binding, sources);
1163
- namedTypes[name] = {
1164
- kind: "namedType",
1165
- name,
1166
- node: binding,
1167
- span: nodePslSpan(binding.syntax, sources),
1168
- ...resolved
1169
- };
1170
- }
1171
- reportMixins(document.declarations(), sources, diagnostics);
1172
- }
1173
- return {
1174
- symbolTable: { topLevel: {
1175
- namespaces,
1176
- namedTypes,
1177
- blocks,
1178
- models,
1179
- compositeTypes
1180
- } },
1181
- diagnostics
1182
- };
1183
- }
1184
- function buildModel(name, node, sources, diagnostics) {
1185
- return {
1186
- kind: "model",
1187
- name,
1188
- node,
1189
- span: nodePslSpan(node.syntax, sources),
1190
- fields: buildFields(name, node.fields(), sources, diagnostics),
1191
- attributes: readResolvedAttributes(node.attributes(), sources)
1192
- };
1193
- }
1194
- function buildCompositeType(name, node, sources, diagnostics) {
1195
- return {
1196
- kind: "compositeType",
1197
- name,
1198
- node,
1199
- span: nodePslSpan(node.syntax, sources),
1200
- fields: buildFields(name, node.fields(), sources, diagnostics),
1201
- attributes: readResolvedAttributes(node.attributes(), sources)
1202
- };
1203
- }
1204
- function buildBlock(name, node, sources) {
1205
- return {
1206
- kind: "block",
1207
- name,
1208
- keyword: node.keyword()?.text ?? "",
1209
- node,
1210
- span: nodePslSpan(node.syntax, sources),
1211
- entries: Array.from(node.entries()),
1212
- attributes: readResolvedAttributes(node.attributes(), sources)
1213
- };
1214
- }
1215
- function extendNamespace(namespace, node, diagnostics, sources) {
1216
- const { models, compositeTypes, blocks } = namespace;
1217
- for (const member of node.declarations()) {
1218
- if (member instanceof MixinDeclarationAst) continue;
1219
- const memberName = member.name()?.name();
1220
- if (memberName === void 0) continue;
1221
- if (Object.hasOwn(models, memberName) || Object.hasOwn(compositeTypes, memberName) || Object.hasOwn(blocks, memberName)) {
1222
- const range = nameRange(member.name(), sources);
1223
- if (range) diagnostics.push({
1224
- code: "PSL_DUPLICATE_DECLARATION",
1225
- message: `Duplicate declaration of "${memberName}"`,
1226
- filename: sources.sourceFileFor(member.syntax).filename,
1227
- range
1228
- });
1229
- continue;
1230
- }
1231
- if (member instanceof ModelDeclarationAst) models[memberName] = buildModel(memberName, member, sources, diagnostics);
1232
- else if (member instanceof CompositeTypeDeclarationAst) compositeTypes[memberName] = buildCompositeType(memberName, member, sources, diagnostics);
1233
- else if (member instanceof GenericBlockDeclarationAst) blocks[memberName] = buildBlock(memberName, member, sources);
1234
- }
1235
- }
1236
- const MIXINS_NOT_SUPPORTED = "Mixins are not supported yet";
1237
- function reportMixins(declarations, sources, diagnostics) {
1238
- for (const declaration of declarations) {
1239
- if (declaration instanceof NamespaceDeclarationAst) {
1240
- reportMixins(declaration.declarations(), sources, diagnostics);
1241
- continue;
1242
- }
1243
- if (declaration instanceof TypesBlockAst) continue;
1244
- if (declaration instanceof MixinDeclarationAst) {
1245
- const range = nameRange(declaration.name(), sources) ?? mixinKeywordRange(declaration, sources);
1246
- if (range !== void 0) diagnostics.push({
1247
- code: "PSL_INVALID_DECLARATION",
1248
- message: MIXINS_NOT_SUPPORTED,
1249
- filename: sources.sourceFileFor(declaration.syntax).filename,
1250
- range
1251
- });
1252
- }
1253
- for (const inclusion of declaration.inclusions()) reportInclusion(inclusion, sources, diagnostics);
1254
- }
1255
- }
1256
- function reportInclusion(inclusion, sources, diagnostics) {
1257
- if (inclusion.name() === void 0) return;
1258
- diagnostics.push({
1259
- code: "PSL_INVALID_DECLARATION",
1260
- message: MIXINS_NOT_SUPPORTED,
1261
- filename: sources.sourceFileFor(inclusion.syntax).filename,
1262
- range: nodeRange(inclusion.syntax, sources)
1263
- });
1264
- }
1265
- function mixinKeywordRange(declaration, sources) {
1266
- const token = declaration.mixinKeyword();
1267
- if (token === void 0) return void 0;
1268
- const sourceFile = sources.sourceFileFor(declaration.syntax);
1269
- return {
1270
- start: sourceFile.positionAt(token.offset),
1271
- end: sourceFile.positionAt(token.offset + token.text.length)
1272
- };
1273
- }
1274
- function buildFields(ownerName, fields, sources, diagnostics) {
1275
- const result = Object.create(null);
1276
- for (const field of fields) {
1277
- const nameNode = field.name();
1278
- const name = nameNode?.name();
1279
- if (name === void 0) continue;
1280
- if (Object.hasOwn(result, name)) {
1281
- const range = nameRange(nameNode, sources);
1282
- if (range) diagnostics.push({
1283
- code: "PSL_DUPLICATE_DECLARATION",
1284
- message: `Duplicate declaration of "${name}"`,
1285
- filename: sources.sourceFileFor(field.syntax).filename,
1286
- range
1287
- });
1288
- continue;
1289
- }
1290
- result[name] = buildField(ownerName, name, field, sources, diagnostics);
1291
- }
1292
- return result;
1293
- }
1294
- function buildField(ownerName, name, node, sources, diagnostics) {
1295
- const attributes = readResolvedAttributes(node.attributes(), sources);
1296
- const span = nodePslSpan(node.syntax, sources);
1297
- const annotation = node.typeAnnotation();
1298
- const typeName = annotation?.name();
1299
- if (typeName?.isOverQualified()) {
1300
- const path = typeName.path();
1301
- diagnostics.push({
1302
- code: "PSL_INVALID_QUALIFIED_TYPE",
1303
- message: `Field "${ownerName}.${name}" has an invalid qualified type "${path.join(".")}"; use at most one namespace qualifier (e.g. "ns.TypeName")`,
1304
- filename: sources.sourceFileFor(typeName.syntax).filename,
1305
- range: nodeRange(typeName.syntax, sources)
1306
- });
1307
- return {
1308
- kind: "field",
1309
- name,
1310
- node,
1311
- span,
1312
- typeName: path[path.length - 1] ?? "",
1313
- optional: false,
1314
- list: false,
1315
- elementOptional: false,
1316
- malformedType: true,
1317
- attributes
1318
- };
1319
- }
1320
- const typeConstructor = annotation?.isConstructor() ? readResolvedConstructorCall(annotation, sources) : void 0;
1321
- const typeNamespaceId = typeName?.namespace()?.name();
1322
- const typeContractSpaceId = typeName?.space()?.name();
1323
- return {
1324
- kind: "field",
1325
- name,
1326
- node,
1327
- span,
1328
- typeName: typeName?.identifier()?.name() ?? "",
1329
- ...typeNamespaceId !== void 0 ? { typeNamespaceId } : {},
1330
- ...typeContractSpaceId !== void 0 ? { typeContractSpaceId } : {},
1331
- optional: annotation?.isOptional() ?? false,
1332
- list: annotation?.isList() ?? false,
1333
- elementOptional: annotation?.isElementOptional() ?? false,
1334
- ...typeConstructor !== void 0 ? { typeConstructor } : {},
1335
- attributes
1336
- };
1337
- }
1338
- function resolveNamedTypeBinding(node, sources) {
1339
- const annotation = node.typeAnnotation();
1340
- const isConstructor = annotation?.isConstructor() ?? false;
1341
- const baseType = annotation?.name()?.identifier()?.name();
1342
- const typeConstructor = readResolvedConstructorCall(annotation, sources);
1343
- return {
1344
- isConstructor,
1345
- ...!isConstructor && baseType !== void 0 ? { baseType } : {},
1346
- ...typeConstructor !== void 0 ? { typeConstructor } : {},
1347
- attributes: readResolvedAttributes(node.attributes(), sources)
1348
- };
1349
- }
1350
- function nameRange(name, sources) {
1351
- if (name === void 0) return void 0;
1352
- const sourceFile = sources.sourceFileFor(name.syntax);
1353
- for (const token of name.syntax.tokens()) if (token.kind === "Ident") return {
1354
- start: sourceFile.positionAt(token.offset),
1355
- end: sourceFile.positionAt(token.offset + token.text.length)
1356
- };
1357
- }
1358
- function nodeRange(node, sources) {
1359
- const sourceFile = sources.sourceFileFor(node);
1360
- const start = node.offset;
1361
- const end = start + node.green.textLength;
1362
- return {
1363
- start: sourceFile.positionAt(start),
1364
- end: sourceFile.positionAt(end)
1365
- };
1366
- }
1367
1553
  //#endregion
1368
1554
  //#region ../../../1-framework/2-authoring/psl-parser/dist/index.mjs
1369
1555
  function getPositionalArgument(attribute, index = 0) {