@almadar/core 7.16.0 → 7.17.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.
@@ -1,6 +1,6 @@
1
- import { d6 as UISlot, Z as Effect, cE as Trait, bN as RenderUIEffect, cQ as TraitEventContract, a2 as Entity, cS as TraitEventListener, cH as TraitConfig, a6 as EntityField, a9 as EntityPersistence, ag as EntityRow, db as UseDeclaration, ab as EntityRef, cV as TraitRef, bs as PageRef, b6 as OrbitalDefinition, bh as OrbitalSchema, br as Page, bt as PageRefObject, cX as TraitReference } from './schema-DJzXvJHi.js';
1
+ import { d7 as UISlot, Z as Effect, cE as Trait, bN as RenderUIEffect, cQ as TraitEventContract, a2 as Entity, cS as TraitEventListener, cH as TraitConfig, a6 as EntityField, a9 as EntityPersistence, ag as EntityRow, dc as UseDeclaration, ab as EntityRef, cV as TraitRef, bs as PageRef, b6 as OrbitalDefinition, bh as OrbitalSchema, br as Page, bt as PageRefObject, cX as TraitReference } from './schema-WX5fN1Ra.js';
2
2
  import { S as SExpr } from './expression-BVRFm0sV.js';
3
- export { C as ComposeBehaviorsInput, a as ComposeBehaviorsResult, E as EventWiringEntry, L as LayoutStrategy, b as applyEventWiring, c as composeBehaviors, d as detectLayoutStrategy } from './compose-behaviors-D2vPjRpX.js';
3
+ export { C as ComposeBehaviorsInput, a as ComposeBehaviorsResult, E as EventWiringEntry, L as LayoutStrategy, b as applyEventWiring, c as composeBehaviors, d as detectLayoutStrategy } from './compose-behaviors-DWMFJEz3.js';
4
4
  import { AnyPatternConfig } from '@almadar/patterns';
5
5
  import 'zod';
6
6
 
@@ -1,4 +1,4 @@
1
- import { b6 as OrbitalDefinition, bh as OrbitalSchema } from './schema-DJzXvJHi.js';
1
+ import { b6 as OrbitalDefinition, bh as OrbitalSchema } from './schema-WX5fN1Ra.js';
2
2
 
3
3
  /**
4
4
  * Event Wiring
@@ -1,8 +1,18 @@
1
+ import { c$ as TraitScope, a9 as EntityPersistence, a2 as Entity, br as Page, bh as OrbitalSchema, cE as Trait } from '../schema-WX5fN1Ra.js';
1
2
  import { S as SExpr } from '../expression-BVRFm0sV.js';
2
- import { a2 as Entity, br as Page, bh as OrbitalSchema, cE as Trait } from '../schema-DJzXvJHi.js';
3
3
  import 'zod';
4
4
  import '@almadar/patterns';
5
5
 
6
+ /**
7
+ * Domain Language Types
8
+ *
9
+ * AST node types for the domain language that maps to KFlow schema.
10
+ * All entity references use explicit names (e.g., Order, Task, CurrentUser)
11
+ * per GAP-002 - no magic variables like `entity.` or `context.`.
12
+ *
13
+ * @packageDocumentation
14
+ */
15
+
6
16
  /**
7
17
  * Effect operator mapping: Both systems use the same operator names
8
18
  */
@@ -31,6 +41,23 @@ interface ASTNode {
31
41
  * Note: These map to OrbitalSchema types via DOMAIN_TO_SCHEMA_FIELD_TYPE
32
42
  */
33
43
  type DomainFieldType = 'text' | 'long text' | 'number' | 'currency' | 'date' | 'timestamp' | 'datetime' | 'yes/no' | 'enum' | 'list' | 'object' | 'relation';
44
+ /**
45
+ * Default value for a `DomainField`. Mirrors the JSON-leaf shape an
46
+ * `EntityField.default` may carry on `OrbitalSchema`: scalar, list, or
47
+ * nested object (when the field type is `'object'` or `'list'`).
48
+ */
49
+ type DomainFieldDefault = string | number | boolean | null | DomainFieldDefault[] | {
50
+ [k: string]: DomainFieldDefault;
51
+ };
52
+ /**
53
+ * For `fieldType: 'list'`, the typed shape of each item. Mirrors
54
+ * `EntityField.items` on `OrbitalSchema`. Currently a single-level
55
+ * type tag (matching how the schema uses it); extend if/when nested
56
+ * list-of-list / list-of-object signatures are required.
57
+ */
58
+ interface DomainFieldItems {
59
+ type: DomainFieldType;
60
+ }
34
61
  interface DomainField extends ASTNode {
35
62
  type: 'field';
36
63
  name: string;
@@ -38,8 +65,10 @@ interface DomainField extends ASTNode {
38
65
  required: boolean;
39
66
  unique: boolean;
40
67
  auto: boolean;
41
- default?: unknown;
68
+ default?: DomainFieldDefault;
42
69
  enumValues?: string[];
70
+ /** List-of-X item type when `fieldType === 'list'`. */
71
+ items?: DomainFieldItems;
43
72
  }
44
73
  type RelationshipType = 'belongs_to' | 'has_many' | 'has_one';
45
74
  interface DomainRelationship extends ASTNode {
@@ -56,6 +85,13 @@ interface DomainEntity extends ASTNode {
56
85
  relationships: DomainRelationship[];
57
86
  states?: string[];
58
87
  initialState?: string;
88
+ /**
89
+ * Storage mode on the resolved schema. Mirrors `EntityPersistence` in
90
+ * `@almadar/core/types/entity.ts`. Omitted ⇒ projector defaults to
91
+ * `'persistent'`. Domain text syntax: `Persistence: <value>` line in
92
+ * the entity section.
93
+ */
94
+ persistence?: EntityPersistence;
59
95
  }
60
96
  interface DomainPageSection extends ASTNode {
61
97
  type: 'page_section';
@@ -146,6 +182,13 @@ interface DomainBehavior extends ASTNode {
146
182
  transitions: DomainTransition[];
147
183
  ticks: DomainTick[];
148
184
  rules: string[];
185
+ /**
186
+ * Instance- vs collection-scoped state machine. Mirrors
187
+ * `Trait.scope: TraitScope` in `@almadar/core/types/trait.ts`.
188
+ * Omitted ⇒ projector defaults to `'instance'`. Domain text syntax:
189
+ * `Scope: instance|collection` line in the behavior section.
190
+ */
191
+ scope?: TraitScope;
149
192
  }
150
193
  interface DomainDocument extends ASTNode {
151
194
  type: 'document';
@@ -926,4 +969,4 @@ declare function getRegistryStats(): {
926
969
  */
927
970
  declare function generateDomainLanguageReference(): string;
928
971
 
929
- export { type ASTNode, type ComparisonCondition, type ComparisonOperator, type DomainBehavior, type DomainChunk, type DomainDocument, type DomainEffect, type DomainEntity, type DomainField, type DomainFieldType, type DomainGuard, type DomainPage, type DomainPageAction, type DomainPageSection, type DomainRelationship, type DomainTick, type DomainToSchemaResult, type DomainTransition, EFFECT_REGISTRY, type EffectMapping, type EffectType, FIELD_TYPE_REGISTRY, type FieldCheckCondition, type FieldReference, type FieldTypeMapping, GUARD_REGISTRY, type GuardCondition, type GuardMapping, KEYWORDS, Lexer, type LogicalCondition, type LogicalOperator, MULTI_WORD_KEYWORDS, type MappingStore, type MergeResult, type ParseError, type ParseResult, type RelationshipType, type SchemaToDomainResult, type SectionMapping, type SourceLocation, type SourceRange, type Token, TokenType, type UserCheckCondition, applySectionUpdate, computeSchemaHash, convertDomainToSchema, convertEntitiesToDomain, convertPagesToDomain, convertSchemaToDomain, convertTraitsToDomain, createMappingStore, deleteSection, detectChanges, domainKeywordToSchemaType, findMapping, findMappingByPath, findMappingsByType, formatBehaviorToDomain, formatBehaviorToSchema, formatDomainGuardToSchema, formatEntityToDomain, formatEntityToSchema, formatGuardConditionToDomain, formatGuardToDomain, formatGuardToSchema, formatMergeSummary, formatPageToDomain, formatPageToSchema, formatSchemaEntityToDomain, formatSchemaGuardToDomain, formatSchemaPageToDomain, formatSchemaTraitToDomain, generateDomainLanguageReference, generateSectionId, getEffectMapping, getFieldTypeMapping, getGuardMapping, getRegisteredEffects, getRegisteredFieldTypes, getRegisteredGuards, getRegistryStats, getSchemaPath, hasSchemaChanged, isEffectRegistered, isFieldTypeRegistered, isGuardRegistered, mergeDomainChunks, parseBehavior, parseDomainEffect, parseDomainEffects, parseDomainGuard, parseEntity, parseGuard, parsePage, parseSectionId, removeMapping, resolveConflict, schemaEntityToDomainEntity, schemaPageToDomainPage, schemaTraitToDomainBehavior, schemaTypeToDomainKeyword, tokenize, updateMappingRange, updateSchemaHash, upsertMapping, validateDomainChunk };
972
+ export { type ASTNode, type ComparisonCondition, type ComparisonOperator, type DomainBehavior, type DomainChunk, type DomainDocument, type DomainEffect, type DomainEntity, type DomainField, type DomainFieldDefault, type DomainFieldItems, type DomainFieldType, type DomainGuard, type DomainPage, type DomainPageAction, type DomainPageSection, type DomainRelationship, type DomainTick, type DomainToSchemaResult, type DomainTransition, EFFECT_REGISTRY, type EffectMapping, type EffectType, EntityPersistence, FIELD_TYPE_REGISTRY, type FieldCheckCondition, type FieldReference, type FieldTypeMapping, GUARD_REGISTRY, type GuardCondition, type GuardMapping, KEYWORDS, Lexer, type LogicalCondition, type LogicalOperator, MULTI_WORD_KEYWORDS, type MappingStore, type MergeResult, type ParseError, type ParseResult, type RelationshipType, type SchemaToDomainResult, type SectionMapping, type SourceLocation, type SourceRange, type Token, TokenType, TraitScope, type UserCheckCondition, applySectionUpdate, computeSchemaHash, convertDomainToSchema, convertEntitiesToDomain, convertPagesToDomain, convertSchemaToDomain, convertTraitsToDomain, createMappingStore, deleteSection, detectChanges, domainKeywordToSchemaType, findMapping, findMappingByPath, findMappingsByType, formatBehaviorToDomain, formatBehaviorToSchema, formatDomainGuardToSchema, formatEntityToDomain, formatEntityToSchema, formatGuardConditionToDomain, formatGuardToDomain, formatGuardToSchema, formatMergeSummary, formatPageToDomain, formatPageToSchema, formatSchemaEntityToDomain, formatSchemaGuardToDomain, formatSchemaPageToDomain, formatSchemaTraitToDomain, generateDomainLanguageReference, generateSectionId, getEffectMapping, getFieldTypeMapping, getGuardMapping, getRegisteredEffects, getRegisteredFieldTypes, getRegisteredGuards, getRegistryStats, getSchemaPath, hasSchemaChanged, isEffectRegistered, isFieldTypeRegistered, isGuardRegistered, mergeDomainChunks, parseBehavior, parseDomainEffect, parseDomainEffects, parseDomainGuard, parseEntity, parseGuard, parsePage, parseSectionId, removeMapping, resolveConflict, schemaEntityToDomainEntity, schemaPageToDomainPage, schemaTraitToDomainBehavior, schemaTypeToDomainKeyword, tokenize, updateMappingRange, updateSchemaHash, upsertMapping, validateDomainChunk };
@@ -445,6 +445,7 @@ function parseEntity(text) {
445
445
  const tokens = lexer.tokenize();
446
446
  let pos = 0;
447
447
  const current = () => tokens[pos] || { type: "EOF" /* EOF */, value: "", line: 0, column: 0, offset: 0 };
448
+ const peek = (offset = 0) => tokens[pos + offset] || { type: "EOF" /* EOF */, value: "", line: 0, column: 0, offset: 0 };
448
449
  const advance = () => tokens[pos++];
449
450
  const isAtEnd = () => current().type === "EOF" /* EOF */;
450
451
  const skip = (type) => {
@@ -526,6 +527,21 @@ function parseEntity(text) {
526
527
  }
527
528
  function parseSection(entity2) {
528
529
  const token = current();
530
+ if (token.type === "IDENTIFIER" /* IDENTIFIER */ && peek(1).type === "COLON" /* COLON */) {
531
+ const propName = token.value.toLowerCase();
532
+ if (propName === "persistence") {
533
+ advance();
534
+ advance();
535
+ if (current().type === "IDENTIFIER" /* IDENTIFIER */) {
536
+ const val = current().value.toLowerCase();
537
+ if (val === "persistent" || val === "runtime" || val === "singleton" || val === "instance" || val === "local") {
538
+ entity2.persistence = val;
539
+ advance();
540
+ }
541
+ }
542
+ return true;
543
+ }
544
+ }
529
545
  if (token.type === "INDENT" /* INDENT */) {
530
546
  advance();
531
547
  parseIndentedFields(entity2);
@@ -759,36 +775,71 @@ function parseEntity(text) {
759
775
  };
760
776
  const parts = [];
761
777
  while (!isAtEnd() && current().type !== "NEWLINE" /* NEWLINE */) {
762
- parts.push(current().value);
778
+ const tok = current();
779
+ parts.push(tok.type === "STRING" /* STRING */ ? `"${tok.value}"` : tok.value);
763
780
  advance();
764
781
  }
765
782
  const content = parts.join(" ").trim();
766
- if (content.includes("|")) {
767
- field.fieldType = "enum";
768
- field.enumValues = content.split("|").map((v) => v.trim()).filter((v) => v);
769
- return field;
770
- }
771
- const segments = content.split(",").map((s) => s.trim().toLowerCase());
772
- for (const segment of segments) {
773
- if (segment === "text") field.fieldType = "text";
774
- else if (segment === "long text") field.fieldType = "long text";
775
- else if (segment === "number") field.fieldType = "number";
776
- else if (segment === "currency") field.fieldType = "currency";
777
- else if (segment === "date") field.fieldType = "date";
778
- else if (segment === "timestamp") field.fieldType = "timestamp";
779
- else if (segment === "datetime") field.fieldType = "datetime";
780
- else if (segment === "yes/no" || segment === "boolean") field.fieldType = "yes/no";
781
- else if (segment === "list") field.fieldType = "list";
782
- else if (segment === "object") field.fieldType = "object";
783
- else if (segment === "required") field.required = true;
784
- else if (segment === "unique") field.unique = true;
785
- else if (segment === "auto") field.auto = true;
786
- else if (segment.startsWith("default ")) {
787
- field.default = parseValue(segment.slice(8));
783
+ const segments = content.split(",").map((s) => s.trim());
784
+ if (segments.length === 0) return field;
785
+ const typeSpec = segments[0];
786
+ if (typeSpec.includes("|")) {
787
+ field.fieldType = "text";
788
+ field.enumValues = typeSpec.split("|").map((v) => v.trim()).filter((v) => v.length > 0);
789
+ } else {
790
+ const lower = typeSpec.toLowerCase();
791
+ if (lower.startsWith("list of ")) {
792
+ field.fieldType = "list";
793
+ field.items = { type: parseDomainFieldType(lower.slice("list of ".length).trim()) };
794
+ } else {
795
+ field.fieldType = parseDomainFieldType(lower);
796
+ }
797
+ }
798
+ for (let i = 1; i < segments.length; i++) {
799
+ const segment = segments[i];
800
+ const lower = segment.toLowerCase();
801
+ if (lower === "required") field.required = true;
802
+ else if (lower === "unique") field.unique = true;
803
+ else if (lower === "auto") field.auto = true;
804
+ else if (lower.startsWith("default ")) {
805
+ field.default = parseDefaultLiteral(segment.slice("default ".length).trim());
788
806
  }
789
807
  }
790
808
  return field;
791
809
  }
810
+ function parseDomainFieldType(keyword) {
811
+ if (keyword === "long text") return "long text";
812
+ if (keyword === "text" || keyword === "string") return "text";
813
+ if (keyword === "number" || keyword === "integer") return "number";
814
+ if (keyword === "currency") return "currency";
815
+ if (keyword === "date") return "date";
816
+ if (keyword === "timestamp") return "timestamp";
817
+ if (keyword === "datetime") return "datetime";
818
+ if (keyword === "yes/no" || keyword === "boolean") return "yes/no";
819
+ if (keyword === "list" || keyword === "array") return "list";
820
+ if (keyword === "object") return "object";
821
+ if (keyword === "enum") return "enum";
822
+ if (keyword === "relation") return "relation";
823
+ return "text";
824
+ }
825
+ function parseDefaultLiteral(text2) {
826
+ const trimmed = text2.trim();
827
+ if (trimmed.startsWith('"') && trimmed.endsWith('"') || trimmed.startsWith("'") && trimmed.endsWith("'")) {
828
+ return trimmed.slice(1, -1);
829
+ }
830
+ if (trimmed.startsWith("[") && trimmed.endsWith("]") || trimmed.startsWith("{") && trimmed.endsWith("}")) {
831
+ try {
832
+ return JSON.parse(trimmed);
833
+ } catch {
834
+ }
835
+ }
836
+ if (trimmed.toLowerCase() === "true") return true;
837
+ if (trimmed.toLowerCase() === "false") return false;
838
+ if (trimmed.toLowerCase() === "null") return null;
839
+ const num = Number(trimmed);
840
+ if (!Number.isNaN(num) && trimmed !== "") return num;
841
+ return trimmed;
842
+ }
792
843
  function parseRelationship(entity2, relType) {
793
844
  if (current().type !== "IDENTIFIER" /* IDENTIFIER */) {
794
845
  return;
@@ -859,27 +910,32 @@ function formatEntityToDomain(entity) {
859
910
  return lines.join("\n");
860
911
  }
861
912
  function formatEntityToSchema(entity) {
862
- const fields = entity.fields.map((field) => ({
863
- name: field.name,
864
- type: mapFieldTypeToSchema(field.fieldType),
865
- required: field.required || void 0,
866
- unique: field.unique || void 0,
867
- auto: field.auto || void 0,
868
- values: field.enumValues,
869
- // OrbitalSchema uses 'values' not 'enumValues'
870
- default: field.default
871
- }));
913
+ const fields = entity.fields.map((field) => {
914
+ const schemaType = field.enumValues && field.enumValues.length > 0 ? mapFieldTypeToSchema(field.fieldType === "enum" ? "text" : field.fieldType) : mapFieldTypeToSchema(field.fieldType);
915
+ const out2 = {
916
+ name: field.name,
917
+ type: schemaType
918
+ };
919
+ if (field.required) out2.required = true;
920
+ if (field.unique) out2.unique = true;
921
+ if (field.auto) out2.auto = true;
922
+ if (field.enumValues && field.enumValues.length > 0) {
923
+ out2.values = field.enumValues;
924
+ }
925
+ if (field.items) {
926
+ out2.items = { type: mapFieldTypeToSchema(field.items.type) };
927
+ }
928
+ if (field.default !== void 0) {
929
+ out2.default = field.default;
930
+ }
931
+ return out2;
932
+ });
872
933
  for (const rel of entity.relationships) {
873
934
  if (rel.relationshipType === "belongs_to") {
874
935
  const fieldName = rel.alias ? toCamelCase(rel.alias) + "Id" : toCamelCase(rel.targetEntity) + "Id";
875
936
  fields.push({
876
937
  name: fieldName,
877
938
  type: "relation",
878
- required: void 0,
879
- unique: void 0,
880
- auto: void 0,
881
- values: void 0,
882
- default: void 0,
883
939
  relation: {
884
940
  entity: rel.targetEntity,
885
941
  type: "many-to-one"
@@ -887,13 +943,15 @@ function formatEntityToSchema(entity) {
887
943
  });
888
944
  }
889
945
  }
890
- return {
946
+ const out = {
891
947
  name: entity.name,
892
948
  collection: toKebabCase(entity.name) + "s",
893
- fields: fields.filter((f) => Object.keys(f).length > 0),
894
- states: entity.states,
895
- initialState: entity.initialState
949
+ fields: fields.filter((f) => Object.keys(f).length > 0)
896
950
  };
951
+ if (entity.states && entity.states.length > 0) out.states = entity.states;
952
+ if (entity.initialState !== void 0) out.initialState = entity.initialState;
953
+ if (entity.persistence !== void 0) out.persistence = entity.persistence;
954
+ return out;
897
955
  }
898
956
  function toCamelCase(text) {
899
957
  return text.toLowerCase().split(/\s+/).map(
@@ -916,14 +974,6 @@ function startsWithVowel(text) {
916
974
  }
917
975
  return /^[aeiou]/i.test(text);
918
976
  }
919
- function parseValue(text) {
920
- text = text.trim();
921
- if (text.toLowerCase() === "true") return true;
922
- if (text.toLowerCase() === "false") return false;
923
- const num = parseFloat(text);
924
- if (!isNaN(num)) return num;
925
- return text.replace(/^["']|["']$/g, "");
926
- }
927
977
  function formatFieldType(field) {
928
978
  const parts = [];
929
979
  if (field.enumValues && field.enumValues.length > 0) {
@@ -1523,7 +1573,7 @@ function parseFieldCheck(text, ctx) {
1523
1573
  type: "field_check",
1524
1574
  field,
1525
1575
  check: "equals",
1526
- value: parseValue2(value)
1576
+ value: parseValue(value)
1527
1577
  };
1528
1578
  }
1529
1579
  }
@@ -1546,7 +1596,7 @@ function parseComparison(text, ctx) {
1546
1596
  type: "comparison",
1547
1597
  field,
1548
1598
  operator: "!=",
1549
- value: parseValue2(isNotMatch[2])
1599
+ value: parseValue(isNotMatch[2])
1550
1600
  };
1551
1601
  }
1552
1602
  }
@@ -1559,7 +1609,7 @@ function parseComparison(text, ctx) {
1559
1609
  type: "comparison",
1560
1610
  field,
1561
1611
  operator,
1562
- value: parseValue2(match[2])
1612
+ value: parseValue(match[2])
1563
1613
  };
1564
1614
  }
1565
1615
  }
@@ -1583,7 +1633,7 @@ function parseFieldReference(text, ctx) {
1583
1633
  fieldName
1584
1634
  };
1585
1635
  }
1586
- function parseValue2(text) {
1636
+ function parseValue(text) {
1587
1637
  text = text.trim();
1588
1638
  if (text.startsWith('"') && text.endsWith('"') || text.startsWith("'") && text.endsWith("'")) {
1589
1639
  return text.slice(1, -1);
@@ -1866,37 +1916,37 @@ function parseComparison2(text, ctx) {
1866
1916
  const atLeastMatch = text.match(/^(.+?)\s+is\s+at\s+least\s+(.+)$/i);
1867
1917
  if (atLeastMatch) {
1868
1918
  const field = parseFieldRef(atLeastMatch[1]);
1869
- const value = parseValue3(atLeastMatch[2]);
1919
+ const value = parseValue2(atLeastMatch[2]);
1870
1920
  return [">=", field, value];
1871
1921
  }
1872
1922
  const atMostMatch = text.match(/^(.+?)\s+is\s+at\s+most\s+(.+)$/i);
1873
1923
  if (atMostMatch) {
1874
1924
  const field = parseFieldRef(atMostMatch[1]);
1875
- const value = parseValue3(atMostMatch[2]);
1925
+ const value = parseValue2(atMostMatch[2]);
1876
1926
  return ["<=", field, value];
1877
1927
  }
1878
1928
  const greaterThanMatch = text.match(/^(.+?)\s+is\s+greater\s+than\s+(.+)$/i);
1879
1929
  if (greaterThanMatch) {
1880
1930
  const field = parseFieldRef(greaterThanMatch[1]);
1881
- const value = parseValue3(greaterThanMatch[2]);
1931
+ const value = parseValue2(greaterThanMatch[2]);
1882
1932
  return [">", field, value];
1883
1933
  }
1884
1934
  const lessThanMatch = text.match(/^(.+?)\s+is\s+less\s+than\s+(.+)$/i);
1885
1935
  if (lessThanMatch) {
1886
1936
  const field = parseFieldRef(lessThanMatch[1]);
1887
- const value = parseValue3(lessThanMatch[2]);
1937
+ const value = parseValue2(lessThanMatch[2]);
1888
1938
  return ["<", field, value];
1889
1939
  }
1890
1940
  const isNotMatch = text.match(/^(.+?)\s+is\s+not\s+(.+)$/i);
1891
1941
  if (isNotMatch) {
1892
1942
  const field = parseFieldRef(isNotMatch[1]);
1893
- const value = parseValue3(isNotMatch[2]);
1943
+ const value = parseValue2(isNotMatch[2]);
1894
1944
  return ["!=", field, value];
1895
1945
  }
1896
1946
  const isMatch = text.match(/^(.+?)\s+is\s+(.+)$/i);
1897
1947
  if (isMatch) {
1898
1948
  const field = parseFieldRef(isMatch[1]);
1899
- const value = parseValue3(isMatch[2]);
1949
+ const value = parseValue2(isMatch[2]);
1900
1950
  return ["=", field, value];
1901
1951
  }
1902
1952
  const opPatterns = [
@@ -1911,7 +1961,7 @@ function parseComparison2(text, ctx) {
1911
1961
  const match = text.match(pattern);
1912
1962
  if (match) {
1913
1963
  const left = parseFieldRef(match[1]);
1914
- const right = parseValue3(match[2]);
1964
+ const right = parseValue2(match[2]);
1915
1965
  return [op, left, right];
1916
1966
  }
1917
1967
  }
@@ -2072,7 +2122,7 @@ function parseFieldRef(text, ctx) {
2072
2122
  const fieldName = toCamelCase3(text);
2073
2123
  return `@entity.${fieldName}`;
2074
2124
  }
2075
- function parseValue3(text) {
2125
+ function parseValue2(text) {
2076
2126
  text = text.trim();
2077
2127
  if (text.startsWith('"') && text.endsWith('"') || text.startsWith("'") && text.endsWith("'")) {
2078
2128
  return text.slice(1, -1);
@@ -2120,7 +2170,7 @@ function parseEffectValue(text, ctx) {
2120
2170
  if (text.includes(".") || text.toLowerCase().startsWith("incoming ") || text.match(/^\w+'s\s+/) || /^[a-z]/.test(text)) {
2121
2171
  return parseFieldRef(text);
2122
2172
  }
2123
- return parseValue3(text);
2173
+ return parseValue2(text);
2124
2174
  }
2125
2175
  function processBindingsInObject(obj) {
2126
2176
  if (obj === null || obj === void 0) {
@@ -2378,6 +2428,18 @@ function parseBehavior(text, entityName) {
2378
2428
  }
2379
2429
  return true;
2380
2430
  }
2431
+ if (token.type === "IDENTIFIER" /* IDENTIFIER */ && token.value.toLowerCase() === "scope") {
2432
+ advance();
2433
+ skip("COLON" /* COLON */);
2434
+ if (current().type === "IDENTIFIER" /* IDENTIFIER */) {
2435
+ const val = current().value.toLowerCase();
2436
+ if (val === "instance" || val === "collection") {
2437
+ behavior2.scope = val;
2438
+ advance();
2439
+ }
2440
+ }
2441
+ return true;
2442
+ }
2381
2443
  if (token.type === "STATES" /* STATES */) {
2382
2444
  advance();
2383
2445
  skip("COLON" /* COLON */);
@@ -2869,6 +2931,9 @@ function formatBehaviorToSchema(behavior) {
2869
2931
  name: behavior.name.replace(/\s+/g, ""),
2870
2932
  description: behavior.name
2871
2933
  };
2934
+ if (behavior.scope !== void 0) {
2935
+ trait.scope = behavior.scope;
2936
+ }
2872
2937
  if (behavior.states.length > 0) {
2873
2938
  trait.stateMachine = {
2874
2939
  states: behavior.states.map((state, index) => ({
@@ -2883,21 +2948,30 @@ function formatBehaviorToSchema(behavior) {
2883
2948
  event: t.event
2884
2949
  };
2885
2950
  if (t.guards.length > 0) {
2886
- const guardExprs = t.guards.map((g) => {
2887
- if (g.raw) {
2888
- return parseDomainGuard(g.raw, behavior.entityName);
2951
+ const guardExprs = [];
2952
+ for (const g of t.guards) {
2953
+ const raw = g.raw ?? formatGuardToCondition(g);
2954
+ try {
2955
+ guardExprs.push(parseDomainGuard(raw, behavior.entityName));
2956
+ } catch {
2889
2957
  }
2890
- return parseDomainGuard(formatGuardToCondition(g), behavior.entityName);
2891
- });
2892
- transition.guard = guardExprs.length === 1 ? guardExprs[0] : ["and", ...guardExprs];
2958
+ }
2959
+ if (guardExprs.length === 1) transition.guard = guardExprs[0];
2960
+ else if (guardExprs.length > 1) transition.guard = ["and", ...guardExprs];
2893
2961
  }
2894
2962
  if (t.effects.length > 0) {
2895
- transition.effects = t.effects.map((e) => {
2963
+ const effectExprs = [];
2964
+ for (const e of t.effects) {
2896
2965
  if (e.config && "_rawSExpr" in e.config && Array.isArray(e.config._rawSExpr)) {
2897
- return e.config._rawSExpr;
2966
+ effectExprs.push(e.config._rawSExpr);
2967
+ continue;
2898
2968
  }
2899
- return parseDomainEffect(e.description, behavior.entityName);
2900
- });
2969
+ try {
2970
+ effectExprs.push(parseDomainEffect(e.description, behavior.entityName));
2971
+ } catch {
2972
+ }
2973
+ }
2974
+ if (effectExprs.length > 0) transition.effects = effectExprs;
2901
2975
  }
2902
2976
  return transition;
2903
2977
  })
@@ -3018,11 +3092,13 @@ function schemaEntityToDomainEntity(entity) {
3018
3092
  unique: field.unique || false,
3019
3093
  auto: field.auto || false
3020
3094
  };
3021
- if (fieldType === "enum") {
3022
- const enumValues = field.enumValues || field.values;
3023
- if (enumValues && enumValues.length > 0) {
3024
- domainField.enumValues = enumValues;
3025
- }
3095
+ const enumValues = field.enumValues ?? field.values;
3096
+ if (enumValues && enumValues.length > 0) {
3097
+ domainField.enumValues = enumValues;
3098
+ }
3099
+ const items = field.items;
3100
+ if (items && typeof items.type === "string") {
3101
+ domainField.items = { type: mapSchemaTypeToDomain(items.type) };
3026
3102
  }
3027
3103
  if (field.default !== void 0) {
3028
3104
  domainField.default = field.default;
@@ -3031,6 +3107,7 @@ function schemaEntityToDomainEntity(entity) {
3031
3107
  }
3032
3108
  const states = entity.states;
3033
3109
  const initialState = entity.initialState;
3110
+ const persistence = entity.persistence;
3034
3111
  return {
3035
3112
  type: "entity",
3036
3113
  name,
@@ -3038,13 +3115,17 @@ function schemaEntityToDomainEntity(entity) {
3038
3115
  fields,
3039
3116
  relationships,
3040
3117
  states,
3041
- initialState
3118
+ initialState,
3119
+ persistence
3042
3120
  };
3043
3121
  }
3044
3122
  function formatEntityText(entity) {
3045
3123
  const lines = [];
3046
3124
  const article = startsWithVowel2(entity.name) ? "An" : "A";
3047
3125
  lines.push(`${article} ${entity.name} is ${entity.description}`);
3126
+ if (entity.persistence !== void 0 && entity.persistence !== "persistent") {
3127
+ lines.push(`Persistence: ${entity.persistence}`);
3128
+ }
3048
3129
  if (entity.fields.length > 0) {
3049
3130
  lines.push("");
3050
3131
  lines.push("It has:");
@@ -3091,6 +3172,8 @@ function formatFieldType2(field) {
3091
3172
  const parts = [];
3092
3173
  if (field.enumValues && field.enumValues.length > 0) {
3093
3174
  parts.push(field.enumValues.join(" | "));
3175
+ } else if (field.fieldType === "list" && field.items) {
3176
+ parts.push(`list of ${field.items.type}`);
3094
3177
  } else {
3095
3178
  parts.push(field.fieldType);
3096
3179
  }
@@ -3098,11 +3181,17 @@ function formatFieldType2(field) {
3098
3181
  if (field.unique) parts.push("unique");
3099
3182
  if (field.auto) parts.push("auto");
3100
3183
  if (field.default !== void 0) {
3101
- const defaultStr = typeof field.default === "string" ? `"${field.default}"` : String(field.default);
3102
- parts.push(`default ${defaultStr}`);
3184
+ parts.push(`default ${formatDefaultValue(field.default)}`);
3103
3185
  }
3104
3186
  return parts.join(", ");
3105
3187
  }
3188
+ function formatDefaultValue(value) {
3189
+ if (typeof value === "string") return `"${value}"`;
3190
+ if (value === null || typeof value === "number" || typeof value === "boolean") {
3191
+ return String(value);
3192
+ }
3193
+ return JSON.stringify(value);
3194
+ }
3106
3195
  function toSpaceSeparated3(text) {
3107
3196
  return text.replace(/([a-z])([A-Z])/g, "$1 $2").toLowerCase();
3108
3197
  }
@@ -5069,6 +5158,7 @@ function schemaTraitToDomainBehavior(trait, entityName) {
5069
5158
  effects
5070
5159
  });
5071
5160
  }
5161
+ const scope = trait.scope;
5072
5162
  return {
5073
5163
  type: "behavior",
5074
5164
  name: formatBehaviorName(name),
@@ -5077,13 +5167,18 @@ function schemaTraitToDomainBehavior(trait, entityName) {
5077
5167
  initialState,
5078
5168
  transitions,
5079
5169
  ticks,
5080
- rules: []
5170
+ rules: [],
5171
+ scope
5081
5172
  };
5082
5173
  }
5083
5174
  function formatBehaviorText(behavior) {
5084
5175
  const lines = [];
5085
5176
  lines.push(behavior.name);
5086
5177
  lines.push("");
5178
+ if (behavior.scope !== void 0 && behavior.scope !== "instance") {
5179
+ lines.push(`Scope: ${behavior.scope}`);
5180
+ lines.push("");
5181
+ }
5087
5182
  if (behavior.states.length > 0) {
5088
5183
  lines.push(`States: ${behavior.states.join(", ")}`);
5089
5184
  lines.push("");
@@ -5394,7 +5489,7 @@ function parseSchemaCondition(condition, defaultEntityName) {
5394
5489
  type: "comparison",
5395
5490
  field: fieldRef,
5396
5491
  operator: comparisonMatch[2],
5397
- value: parseValue4(comparisonMatch[3])
5492
+ value: parseValue3(comparisonMatch[3])
5398
5493
  };
5399
5494
  }
5400
5495
  }
@@ -5422,7 +5517,7 @@ function parseFieldReference2(text, defaultEntityName) {
5422
5517
  }
5423
5518
  return null;
5424
5519
  }
5425
- function parseValue4(text) {
5520
+ function parseValue3(text) {
5426
5521
  text = text.trim();
5427
5522
  if (text.startsWith('"') && text.endsWith('"') || text.startsWith("'") && text.endsWith("'")) {
5428
5523
  return text.slice(1, -1);
@@ -5796,7 +5891,7 @@ function convertDomainToSchema(domainText, baseSchema) {
5796
5891
  const entity = {
5797
5892
  name: entityRecord.name,
5798
5893
  fields: entityRecord.fields || [],
5799
- persistence: "persistent"
5894
+ persistence: entityRecord.persistence ?? "persistent"
5800
5895
  };
5801
5896
  parsedEntities.push({
5802
5897
  name: result.data.name,