bonescript-compiler 0.5.3 → 0.5.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (201) hide show
  1. package/LICENSE +21 -21
  2. package/dist/algorithm_catalog.js +166 -166
  3. package/dist/cli.d.ts +1 -2
  4. package/dist/cli.js +617 -75
  5. package/dist/cli.js.map +1 -1
  6. package/dist/emit_capability.d.ts +0 -13
  7. package/dist/emit_capability.js +134 -296
  8. package/dist/emit_capability.js.map +1 -1
  9. package/dist/emit_composition.js +3 -37
  10. package/dist/emit_composition.js.map +1 -1
  11. package/dist/emit_deploy.js +167 -165
  12. package/dist/emit_deploy.js.map +1 -1
  13. package/dist/emit_events.d.ts +0 -1
  14. package/dist/emit_events.js +275 -325
  15. package/dist/emit_events.js.map +1 -1
  16. package/dist/emit_extras.js +5 -3
  17. package/dist/emit_extras.js.map +1 -1
  18. package/dist/emit_full.js +112 -272
  19. package/dist/emit_full.js.map +1 -1
  20. package/dist/emit_maintenance.js +249 -249
  21. package/dist/emit_nakama.d.ts +23 -0
  22. package/dist/emit_nakama.js +510 -0
  23. package/dist/emit_nakama.js.map +1 -0
  24. package/dist/emit_runtime.d.ts +11 -17
  25. package/dist/emit_runtime.js +688 -29
  26. package/dist/emit_runtime.js.map +1 -1
  27. package/dist/emit_sourcemap.js +66 -66
  28. package/dist/emit_tests.js +12 -47
  29. package/dist/emit_tests.js.map +1 -1
  30. package/dist/emit_websocket.js +3 -0
  31. package/dist/emit_websocket.js.map +1 -1
  32. package/dist/emitter.js +49 -94
  33. package/dist/emitter.js.map +1 -1
  34. package/dist/extension_manager.d.ts +2 -2
  35. package/dist/extension_manager.js +20 -9
  36. package/dist/extension_manager.js.map +1 -1
  37. package/dist/index.d.ts +2 -0
  38. package/dist/index.js +3 -1
  39. package/dist/index.js.map +1 -1
  40. package/dist/ir.d.ts +0 -4
  41. package/dist/lowering.d.ts +14 -5
  42. package/dist/lowering.js +417 -66
  43. package/dist/lowering.js.map +1 -1
  44. package/dist/module_loader.d.ts +2 -2
  45. package/dist/module_loader.js +23 -20
  46. package/dist/module_loader.js.map +1 -1
  47. package/dist/optimizer.js +3 -6
  48. package/dist/optimizer.js.map +1 -1
  49. package/dist/scaffold.d.ts +2 -2
  50. package/dist/scaffold.js +319 -315
  51. package/dist/scaffold.js.map +1 -1
  52. package/dist/solver.js +1 -1
  53. package/dist/solver.js.map +1 -1
  54. package/dist/source_map.js.map +1 -0
  55. package/dist/test.js.map +1 -0
  56. package/dist/test_typechecker.d.ts +5 -0
  57. package/dist/test_typechecker.js +126 -0
  58. package/dist/test_typechecker.js.map +1 -0
  59. package/dist/typechecker.d.ts +0 -7
  60. package/dist/typechecker.js +16 -103
  61. package/dist/typechecker.js.map +1 -1
  62. package/dist/verifier.d.ts +1 -5
  63. package/dist/verifier.js +38 -142
  64. package/dist/verifier.js.map +1 -1
  65. package/package.json +53 -62
  66. package/src/algorithm_catalog.ts +345 -345
  67. package/src/ast.d.ts +244 -0
  68. package/src/ast.ts +334 -334
  69. package/src/cli.ts +704 -98
  70. package/src/emit_batch.ts +140 -140
  71. package/src/emit_capability.ts +436 -613
  72. package/src/emit_composition.ts +196 -229
  73. package/src/emit_deploy.ts +190 -187
  74. package/src/emit_events.ts +307 -362
  75. package/src/emit_extras.ts +240 -237
  76. package/src/emit_full.ts +309 -472
  77. package/src/emit_maintenance.ts +459 -459
  78. package/src/emit_nakama.ts +576 -0
  79. package/src/emit_runtime.ts +730 -17
  80. package/src/emit_sourcemap.ts +140 -140
  81. package/src/emit_tests.ts +205 -243
  82. package/src/emit_websocket.ts +229 -226
  83. package/src/emitter.ts +578 -626
  84. package/src/extension_manager.ts +187 -177
  85. package/src/formatter.ts +297 -297
  86. package/src/index.ts +90 -88
  87. package/src/ir.ts +215 -216
  88. package/src/lexer.d.ts +195 -0
  89. package/src/lexer.ts +630 -630
  90. package/src/lowering.ts +556 -168
  91. package/src/module_loader.ts +114 -112
  92. package/src/optimizer.ts +196 -199
  93. package/src/parse_decls.d.ts +13 -0
  94. package/src/parse_decls.ts +409 -409
  95. package/src/parse_decls2.d.ts +13 -0
  96. package/src/parse_decls2.ts +244 -244
  97. package/src/parse_expr.d.ts +7 -0
  98. package/src/parse_expr.ts +197 -197
  99. package/src/parse_types.d.ts +6 -0
  100. package/src/parse_types.ts +54 -54
  101. package/src/parser.d.ts +10 -0
  102. package/src/parser.ts +1 -1
  103. package/src/parser_base.d.ts +19 -0
  104. package/src/parser_base.ts +57 -57
  105. package/src/parser_recovery.ts +153 -153
  106. package/src/scaffold.ts +375 -371
  107. package/src/solver.ts +330 -330
  108. package/src/typechecker.d.ts +52 -0
  109. package/src/typechecker.ts +591 -700
  110. package/src/types.d.ts +38 -0
  111. package/src/types.ts +122 -122
  112. package/src/verifier.ts +49 -154
  113. package/README.md +0 -382
  114. package/dist/commands/check.d.ts +0 -5
  115. package/dist/commands/check.js +0 -34
  116. package/dist/commands/check.js.map +0 -1
  117. package/dist/commands/compile.d.ts +0 -5
  118. package/dist/commands/compile.js +0 -215
  119. package/dist/commands/compile.js.map +0 -1
  120. package/dist/commands/debug.d.ts +0 -5
  121. package/dist/commands/debug.js +0 -59
  122. package/dist/commands/debug.js.map +0 -1
  123. package/dist/commands/diff.d.ts +0 -5
  124. package/dist/commands/diff.js +0 -123
  125. package/dist/commands/diff.js.map +0 -1
  126. package/dist/commands/fmt.d.ts +0 -5
  127. package/dist/commands/fmt.js +0 -49
  128. package/dist/commands/fmt.js.map +0 -1
  129. package/dist/commands/init.d.ts +0 -5
  130. package/dist/commands/init.js +0 -96
  131. package/dist/commands/init.js.map +0 -1
  132. package/dist/commands/ir.d.ts +0 -5
  133. package/dist/commands/ir.js +0 -27
  134. package/dist/commands/ir.js.map +0 -1
  135. package/dist/commands/lex.d.ts +0 -5
  136. package/dist/commands/lex.js +0 -21
  137. package/dist/commands/lex.js.map +0 -1
  138. package/dist/commands/parse.d.ts +0 -5
  139. package/dist/commands/parse.js +0 -30
  140. package/dist/commands/parse.js.map +0 -1
  141. package/dist/commands/test.d.ts +0 -5
  142. package/dist/commands/test.js +0 -61
  143. package/dist/commands/test.js.map +0 -1
  144. package/dist/commands/verify_determinism.d.ts +0 -5
  145. package/dist/commands/verify_determinism.js +0 -64
  146. package/dist/commands/verify_determinism.js.map +0 -1
  147. package/dist/commands/watch.d.ts +0 -5
  148. package/dist/commands/watch.js +0 -50
  149. package/dist/commands/watch.js.map +0 -1
  150. package/dist/emit_auth.d.ts +0 -18
  151. package/dist/emit_auth.js +0 -507
  152. package/dist/emit_auth.js.map +0 -1
  153. package/dist/emit_database.d.ts +0 -7
  154. package/dist/emit_database.js +0 -72
  155. package/dist/emit_database.js.map +0 -1
  156. package/dist/emit_index.d.ts +0 -6
  157. package/dist/emit_index.js +0 -202
  158. package/dist/emit_index.js.map +0 -1
  159. package/dist/emit_models.d.ts +0 -12
  160. package/dist/emit_models.js +0 -171
  161. package/dist/emit_models.js.map +0 -1
  162. package/dist/emit_openapi.d.ts +0 -9
  163. package/dist/emit_openapi.js +0 -306
  164. package/dist/emit_openapi.js.map +0 -1
  165. package/dist/emit_package.d.ts +0 -7
  166. package/dist/emit_package.js +0 -68
  167. package/dist/emit_package.js.map +0 -1
  168. package/dist/emit_router.d.ts +0 -12
  169. package/dist/emit_router.js +0 -389
  170. package/dist/emit_router.js.map +0 -1
  171. package/dist/lowering_channels.d.ts +0 -11
  172. package/dist/lowering_channels.js +0 -103
  173. package/dist/lowering_channels.js.map +0 -1
  174. package/dist/lowering_entities.d.ts +0 -11
  175. package/dist/lowering_entities.js +0 -232
  176. package/dist/lowering_entities.js.map +0 -1
  177. package/dist/lowering_helpers.d.ts +0 -13
  178. package/dist/lowering_helpers.js +0 -76
  179. package/dist/lowering_helpers.js.map +0 -1
  180. package/src/commands/check.ts +0 -33
  181. package/src/commands/compile.ts +0 -191
  182. package/src/commands/debug.ts +0 -33
  183. package/src/commands/diff.ts +0 -105
  184. package/src/commands/fmt.ts +0 -22
  185. package/src/commands/init.ts +0 -72
  186. package/src/commands/ir.ts +0 -23
  187. package/src/commands/lex.ts +0 -17
  188. package/src/commands/parse.ts +0 -24
  189. package/src/commands/test.ts +0 -36
  190. package/src/commands/verify_determinism.ts +0 -66
  191. package/src/commands/watch.ts +0 -25
  192. package/src/emit_auth.ts +0 -513
  193. package/src/emit_database.ts +0 -72
  194. package/src/emit_index.ts +0 -210
  195. package/src/emit_models.ts +0 -176
  196. package/src/emit_openapi.ts +0 -315
  197. package/src/emit_package.ts +0 -66
  198. package/src/emit_router.ts +0 -408
  199. package/src/lowering_channels.ts +0 -108
  200. package/src/lowering_entities.ts +0 -258
  201. package/src/lowering_helpers.ts +0 -75
package/src/lowering.ts CHANGED
@@ -1,168 +1,556 @@
1
- /**
2
- * BoneScript Lowering Stage 4 of the compilation pipeline.
3
- * Converts typed AST into the Architecture IR (spec/07_IR_SPEC.md).
4
- *
5
- * This file is the orchestrator. The actual lowering logic lives in:
6
- * lowering_helpers.ts — makeId, parseDurationMs, serializeExpr/Type
7
- * lowering_entities.ts — lowerEntity, lowerCapability, lowerField, makeCrudMethod
8
- * lowering_channels.ts — lowerStore, lowerChannel, lowerEvent, lowerFlow
9
- */
10
-
11
- import * as AST from "./ast";
12
- import * as IR from "./ir";
13
- import { makeId, serializeExpr, serializeType } from "./lowering_helpers";
14
- import { lowerEntity, lowerCapability } from "./lowering_entities";
15
- import { lowerStore, lowerChannel, lowerEvent, lowerFlow } from "./lowering_channels";
16
-
17
- export class Lowering {
18
- private systemName: string = "";
19
-
20
- lower(program: AST.ProgramNode, sourceHash: string): IR.IRSystem[] {
21
- return program.systems.map(sys => this.lowerSystem(sys, sourceHash));
22
- }
23
-
24
- private lowerSystem(sys: AST.SystemDeclNode, sourceHash: string): IR.IRSystem {
25
- this.systemName = sys.name;
26
-
27
- const modules: IR.IRModule[] = [];
28
- const events: IR.IREvent[] = [];
29
- const flows: IR.IRFlow[] = [];
30
- const invariants: IR.IRInvariant[] = [];
31
-
32
- // Partition declarations by kind
33
- const entities = sys.declarations.filter((d): d is AST.EntityDeclNode => d.kind === "EntityDecl");
34
- const capabilities = sys.declarations.filter((d): d is AST.CapabilityDeclNode => d.kind === "CapabilityDecl");
35
- const channels = sys.declarations.filter((d): d is AST.ChannelDeclNode => d.kind === "ChannelDecl");
36
- const stores = sys.declarations.filter((d): d is AST.StoreDeclNode => d.kind === "StoreDecl");
37
- const eventDecls = sys.declarations.filter((d): d is AST.EventDeclNode => d.kind === "EventDecl");
38
- const constraints = sys.declarations.filter((d): d is AST.ConstraintDeclNode => d.kind === "ConstraintDecl");
39
- const policies = sys.declarations.filter((d): d is AST.PolicyDeclNode => d.kind === "PolicyDecl");
40
- const flowDecls = sys.declarations.filter((d): d is AST.FlowDeclNode => d.kind === "FlowDecl");
41
- const extensionPoints = sys.declarations.filter((d): d is AST.ExtensionPointDeclNode => d.kind === "ExtensionPointDecl");
42
-
43
- // Stores → data_store modules
44
- for (const store of stores) {
45
- modules.push(lowerStore(this.systemName, store));
46
- }
47
-
48
- // Entities api_service modules (with CRUD + related capabilities)
49
- for (const entity of entities) {
50
- const relatedCaps = capabilities.filter(c =>
51
- c.params.some(p => p.type.kind === "EntityRefType" && p.type.name === entity.name)
52
- );
53
- modules.push(lowerEntity(this.systemName, entity, relatedCaps, stores));
54
- }
55
-
56
- // Standalone capabilities (no entity params) → utility api_service module
57
- // These include algorithm capabilities, pure-function capabilities, etc.
58
- const attachedCaps = new Set(
59
- entities.flatMap(entity =>
60
- capabilities.filter(c =>
61
- c.params.some(p => p.type.kind === "EntityRefType" && p.type.name === entity.name)
62
- )
63
- )
64
- );
65
- const standaloneCaps = capabilities.filter(c => !attachedCaps.has(c));
66
- if (standaloneCaps.length > 0) {
67
- const utilMethods = standaloneCaps.map(cap => lowerCapability(cap));
68
- modules.push({
69
- id: makeId(this.systemName, "api_service", "UtilityService"),
70
- kind: "api_service",
71
- name: "UtilityService",
72
- interfaces: [{ name: "IUtilityService", methods: utilMethods }],
73
- models: [],
74
- events: [],
75
- state_machines: [],
76
- relations: [],
77
- dependencies: [],
78
- config: { authenticated: false, auth_method: "none" },
79
- });
80
- }
81
-
82
- // Channels realtime_service modules
83
- for (const channel of channels) {
84
- modules.push(lowerChannel(this.systemName, channel));
85
- }
86
-
87
- // Build a map from event name → emitting module id by scanning all capabilities
88
- // across all entities. This resolves the event source before lowering events.
89
- const eventSourceMap = new Map<string, string>();
90
- for (const entity of entities) {
91
- const moduleId = makeId(this.systemName, "api_service", `${entity.name}Service`);
92
- const relatedCaps = capabilities.filter(c =>
93
- c.params.some(p => p.type.kind === "EntityRefType" && p.type.name === entity.name)
94
- );
95
- for (const cap of relatedCaps) {
96
- for (const emit of cap.emits) {
97
- if (!eventSourceMap.has(emit.eventName)) {
98
- eventSourceMap.set(emit.eventName, moduleId);
99
- }
100
- }
101
- }
102
- }
103
-
104
- // Events
105
- for (const ev of eventDecls) {
106
- const source = eventSourceMap.get(ev.name) || "unknown";
107
- events.push(lowerEvent(this.systemName, ev, source));
108
- }
109
-
110
- // Flows
111
- for (const flow of flowDecls) {
112
- flows.push(lowerFlow(this.systemName, flow));
113
- }
114
-
115
- // Constraints → invariants
116
- for (const c of constraints) {
117
- invariants.push({
118
- id: makeId(this.systemName, "invariant", c.name),
119
- expression: serializeExpr(c.expr),
120
- scope: "global",
121
- });
122
- }
123
-
124
- // Gateway module (always present per ontology)
125
- const gatewayConfig: Record<string, string | number | boolean> = {
126
- rate_limit: 1000,
127
- cors: true,
128
- tls: true,
129
- };
130
- if (policies.length > 0) {
131
- const mainPolicy = policies[0];
132
- if (mainPolicy.rateLimit) gatewayConfig["rate_limit"] = mainPolicy.rateLimit.count;
133
- if (mainPolicy.encryption) gatewayConfig["encryption"] = mainPolicy.encryption;
134
- }
135
- modules.push({
136
- id: makeId(this.systemName, "gateway", "APIGateway"),
137
- kind: "gateway",
138
- name: "APIGateway",
139
- interfaces: [],
140
- models: [],
141
- events: [],
142
- state_machines: [],
143
- relations: [],
144
- dependencies: modules
145
- .filter(m => m.kind === "api_service" || m.kind === "realtime_service")
146
- .map(m => m.id),
147
- config: gatewayConfig,
148
- });
149
-
150
- return {
151
- name: sys.name,
152
- version: "1.0.0",
153
- source_hash: sourceHash,
154
- domain: sys.domain,
155
- modules,
156
- events,
157
- flows,
158
- invariants,
159
- resolution: {},
160
- extension_points: extensionPoints.map(ep => ({
161
- name: ep.name,
162
- params: ep.params.map(p => ({ name: p.name, type: serializeType(p.type) })),
163
- returns: ep.returns ? serializeType(ep.returns) : null,
164
- stable: ep.stable,
165
- })),
166
- };
167
- }
168
- }
1
+ /**
2
+ * bone ir Lowering — Stage 4 of the compilation pipeline.
3
+ * Converts typed AST into the Architecture IR (spec/07_IR_SPEC.md).
4
+ *
5
+ * Rules:
6
+ * - One module per semantic component
7
+ * - All IDs are deterministic (derived from system name + component name)
8
+ * - All ontology-entailed fields are inserted
9
+ * - Effects are serialized in declaration order
10
+ */
11
+
12
+ import { createHash } from "crypto";
13
+ import * as AST from "./ast";
14
+ import * as IR from "./ir";
15
+
16
+ function toSnakeCase(s: string): string {
17
+ return s.replace(/([a-z])([A-Z])/g, "$1_$2").toLowerCase();
18
+ }
19
+
20
+ // ─── Deterministic ID Generation ─────────────────────────────────────────────
21
+
22
+ function makeId(systemName: string, kind: string, name: string): string {
23
+ const input = `${systemName}.${kind}.${name}`;
24
+ return createHash("sha256").update(input).digest("hex").slice(0, 16);
25
+ }
26
+
27
+ // ─── Duration Parsing ────────────────────────────────────────────────────────
28
+
29
+ function parseDurationMs(dur: string | null): number | null {
30
+ if (!dur) return null;
31
+ const match = dur.match(/^(\d+)(ms|s|m|h|d)$/);
32
+ if (!match) return null;
33
+ const value = parseInt(match[1], 10);
34
+ switch (match[2]) {
35
+ case "ms": return value;
36
+ case "s": return value * 1000;
37
+ case "m": return value * 60_000;
38
+ case "h": return value * 3_600_000;
39
+ case "d": return value * 86_400_000;
40
+ default: return null;
41
+ }
42
+ }
43
+
44
+ // ─── Type Expression Serialization ───────────────────────────────────────────
45
+
46
+ function serializeType(t: AST.TypeExprNode): string {
47
+ switch (t.kind) {
48
+ case "PrimitiveType": return t.name;
49
+ case "GenericType": return `${t.name}<${t.typeArgs.map(serializeType).join(", ")}>`;
50
+ case "EntityRefType": return t.name;
51
+ case "TupleType": return `(${t.elements.map(serializeType).join(", ")})`;
52
+ case "UnionType": return t.members.map(serializeType).join(" | ");
53
+ }
54
+ }
55
+
56
+ // ─── Expression Serialization ────────────────────────────────────────────────
57
+
58
+ function serializeExpr(e: AST.ExprNode): string {
59
+ switch (e.kind) {
60
+ case "Literal":
61
+ if (e.type === "string") return `"${e.value}"`;
62
+ if (e.type === "list") return `[${(e.value as AST.ExprNode[]).map(serializeExpr).join(", ")}]`;
63
+ return String(e.value);
64
+ case "FieldRef":
65
+ return e.path.join(".");
66
+ case "BinaryExpr":
67
+ return `(${serializeExpr(e.left)} ${e.op} ${serializeExpr(e.right)})`;
68
+ case "UnaryExpr":
69
+ return `(${e.op} ${serializeExpr(e.operand)})`;
70
+ case "CallExpr":
71
+ return `${e.name}(${e.args.map(serializeExpr).join(", ")})`;
72
+ case "TernaryExpr":
73
+ return `(${serializeExpr(e.condition)} ? ${serializeExpr(e.consequent)} : ${serializeExpr(e.alternate)})`;
74
+ }
75
+ }
76
+
77
+ // ─── Lowering Engine ─────────────────────────────────────────────────────────
78
+
79
+ export class Lowering {
80
+ private systemName: string = "";
81
+
82
+ lower(program: AST.ProgramNode, sourceHash: string): IR.IRSystem[] {
83
+ const systems: IR.IRSystem[] = [];
84
+
85
+ for (const sys of program.systems) {
86
+ systems.push(this.lowerSystem(sys, sourceHash));
87
+ }
88
+
89
+ return systems;
90
+ }
91
+
92
+ private lowerSystem(sys: AST.SystemDeclNode, sourceHash: string): IR.IRSystem {
93
+ this.systemName = sys.name;
94
+
95
+ const modules: IR.IRModule[] = [];
96
+ const events: IR.IREvent[] = [];
97
+ const flows: IR.IRFlow[] = [];
98
+ const invariants: IR.IRInvariant[] = [];
99
+
100
+ // Collect declarations by type
101
+ const entities = sys.declarations.filter((d): d is AST.EntityDeclNode => d.kind === "EntityDecl");
102
+ const capabilities = sys.declarations.filter((d): d is AST.CapabilityDeclNode => d.kind === "CapabilityDecl");
103
+ const channels = sys.declarations.filter((d): d is AST.ChannelDeclNode => d.kind === "ChannelDecl");
104
+ const stores = sys.declarations.filter((d): d is AST.StoreDeclNode => d.kind === "StoreDecl");
105
+ const eventDecls = sys.declarations.filter((d): d is AST.EventDeclNode => d.kind === "EventDecl");
106
+ const constraints = sys.declarations.filter((d): d is AST.ConstraintDeclNode => d.kind === "ConstraintDecl");
107
+ const policies = sys.declarations.filter((d): d is AST.PolicyDeclNode => d.kind === "PolicyDecl");
108
+ const flowDecls = sys.declarations.filter((d): d is AST.FlowDeclNode => d.kind === "FlowDecl");
109
+ const extensionPoints = sys.declarations.filter((d): d is AST.ExtensionPointDeclNode => d.kind === "ExtensionPointDecl");
110
+
111
+ // Lower stores → data_store modules
112
+ for (const store of stores) {
113
+ modules.push(this.lowerStore(store));
114
+ }
115
+
116
+ // Lower entities → api_service modules (with CRUD + capabilities)
117
+ for (const entity of entities) {
118
+ const relatedCaps = capabilities.filter(c =>
119
+ c.params.some(p => p.type.kind === "EntityRefType" && p.type.name === entity.name)
120
+ );
121
+ modules.push(this.lowerEntity(entity, relatedCaps, stores));
122
+ }
123
+
124
+ // Lower channels → realtime_service modules
125
+ for (const channel of channels) {
126
+ modules.push(this.lowerChannel(channel));
127
+ }
128
+
129
+ // Lower events
130
+ for (const ev of eventDecls) {
131
+ events.push(this.lowerEvent(ev));
132
+ }
133
+
134
+ // Lower flows
135
+ for (const flow of flowDecls) {
136
+ flows.push(this.lowerFlow(flow));
137
+ }
138
+
139
+ // Lower constraints → invariants
140
+ for (const c of constraints) {
141
+ invariants.push({
142
+ id: makeId(this.systemName, "invariant", c.name),
143
+ expression: serializeExpr(c.expr),
144
+ scope: "global",
145
+ });
146
+ }
147
+
148
+ // Add gateway module (ontology: always present)
149
+ const gatewayConfig: Record<string, string | number | boolean> = {
150
+ rate_limit: 1000,
151
+ cors: true,
152
+ tls: true,
153
+ };
154
+ if (policies.length > 0) {
155
+ const mainPolicy = policies[0];
156
+ if (mainPolicy.rateLimit) {
157
+ gatewayConfig["rate_limit"] = mainPolicy.rateLimit.count;
158
+ }
159
+ if (mainPolicy.encryption) {
160
+ gatewayConfig["encryption"] = mainPolicy.encryption;
161
+ }
162
+ }
163
+ modules.push({
164
+ id: makeId(this.systemName, "gateway", "APIGateway"),
165
+ kind: "gateway",
166
+ name: "APIGateway",
167
+ interfaces: [],
168
+ models: [],
169
+ events: [],
170
+ state_machines: [],
171
+ relations: [],
172
+ dependencies: modules.filter(m => m.kind === "api_service" || m.kind === "realtime_service").map(m => m.id),
173
+ config: gatewayConfig,
174
+ });
175
+
176
+ return {
177
+ name: sys.name,
178
+ version: "1.0.0",
179
+ source_hash: sourceHash,
180
+ domain: sys.domain,
181
+ modules,
182
+ events,
183
+ flows,
184
+ invariants,
185
+ resolution: {},
186
+ extension_points: extensionPoints.map(ep => ({
187
+ name: ep.name,
188
+ params: ep.params.map(p => ({ name: p.name, type: serializeType(p.type) })),
189
+ returns: ep.returns ? serializeType(ep.returns) : null,
190
+ stable: ep.stable,
191
+ })),
192
+ };
193
+ }
194
+
195
+ // ─── Store Lowering ────────────────────────────────────────────────────────
196
+
197
+ private lowerStore(store: AST.StoreDeclNode): IR.IRModule {
198
+ // Strip "Store" suffix from model name so SellerStore → Seller → table "sellers"
199
+ const entityName = store.name.replace(/Store$/, "") || store.name;
200
+ const model: IR.IRModel = {
201
+ name: entityName,
202
+ fields: store.schema.map(f => this.lowerField(f)),
203
+ primary_key: "id",
204
+ indexes: [],
205
+ constraints: [],
206
+ };
207
+
208
+ // Add id field if not present
209
+ if (!model.fields.find(f => f.name === "id")) {
210
+ model.fields.unshift({
211
+ name: "id", type: "uuid", nullable: false, unique: true, indexed: true, default_value: "gen_random_uuid()",
212
+ });
213
+ }
214
+
215
+ return {
216
+ id: makeId(this.systemName, "data_store", store.name),
217
+ kind: "data_store",
218
+ name: store.name,
219
+ interfaces: [],
220
+ models: [model],
221
+ events: [],
222
+ state_machines: [],
223
+ relations: [],
224
+ dependencies: [],
225
+ config: {
226
+ engine: store.engine || "postgresql",
227
+ replicas: store.replicas || 1,
228
+ ...(store.retention ? { retention_ms: parseDurationMs(store.retention) || 0 } : {}),
229
+ ...(store.partition ? { partition_key: store.partition } : {}),
230
+ },
231
+ };
232
+ }
233
+
234
+ // ─── Entity Lowering ───────────────────────────────────────────────────────
235
+
236
+ private lowerEntity(entity: AST.EntityDeclNode, capabilities: AST.CapabilityDeclNode[], stores: AST.StoreDeclNode[]): IR.IRModule {
237
+ const moduleId = makeId(this.systemName, "api_service", `${entity.name}Service`);
238
+
239
+ // Build model from entity fields + ontology entailments
240
+ const fields: IR.IRField[] = [
241
+ { name: "id", type: "uuid", nullable: false, unique: true, indexed: true, default_value: "gen_random_uuid()" },
242
+ { name: "created_at", type: "timestamp", nullable: false, unique: false, indexed: true, default_value: "now()" },
243
+ { name: "updated_at", type: "timestamp", nullable: false, unique: false, indexed: false, default_value: "now()" },
244
+ ];
245
+ for (const f of entity.owns) {
246
+ fields.push(this.lowerField(f));
247
+ }
248
+
249
+ // Add derived fields as generated columns (stored: false = virtual)
250
+ const derivedFields: IR.IRField[] = entity.derived.map(d => ({
251
+ name: d.name,
252
+ type: "json", // type inferred at runtime
253
+ nullable: true,
254
+ unique: false,
255
+ indexed: false,
256
+ default_value: `GENERATED ALWAYS AS (${serializeExpr(d.expr)}) STORED`,
257
+ }));
258
+
259
+ // Build indexes from entity index declarations
260
+ const indexes: IR.IRIndex[] = [];
261
+ for (const idx of entity.indexes) {
262
+ indexes.push({ fields: idx, unique: false });
263
+ }
264
+
265
+ // Build constraints from entity constraints
266
+ const modelConstraints: IR.IRModelConstraint[] = [];
267
+ for (const c of entity.constraints) {
268
+ const serialized = serializeExpr(c);
269
+ // Detect unique constraints
270
+ if (c.kind === "FieldRef" && c.path[c.path.length - 1] === "unique") {
271
+ const field = c.path.slice(0, -1).join(".");
272
+ modelConstraints.push({ kind: "unique", target: field, params: {} });
273
+ indexes.push({ fields: [field], unique: true });
274
+ } else {
275
+ modelConstraints.push({ kind: "check", target: entity.name, params: { expression: serialized } });
276
+ }
277
+ }
278
+
279
+ const model: IR.IRModel = {
280
+ name: entity.name,
281
+ fields: [...fields, ...derivedFields],
282
+ primary_key: "id",
283
+ indexes,
284
+ constraints: modelConstraints,
285
+ };
286
+
287
+ // Build state machine if entity has states
288
+ const stateMachines: IR.IRStateMachine[] = [];
289
+ if (entity.states) {
290
+ const states = entity.states.nodes.map(n => n.name);
291
+ const transitions: IR.IRTransition[] = [];
292
+ for (const node of entity.states.nodes) {
293
+ for (const target of node.transitions) {
294
+ transitions.push({ from: node.name, to: target, trigger: `${node.name}_to_${target}`, guard: null });
295
+ }
296
+ for (const target of node.branches) {
297
+ transitions.push({ from: node.name, to: target, trigger: `${node.name}_to_${target}`, guard: null });
298
+ }
299
+ }
300
+ stateMachines.push({
301
+ entity: entity.name,
302
+ states,
303
+ initial: states[0],
304
+ transitions,
305
+ });
306
+ }
307
+
308
+ // Build interface from capabilities
309
+ const methods: IR.IRMethod[] = [];
310
+
311
+ // CRUD methods (always generated for entities)
312
+ methods.push(this.makeCrudMethod("create", entity.name, fields));
313
+ methods.push(this.makeCrudMethod("read", entity.name, fields));
314
+ methods.push(this.makeCrudMethod("update", entity.name, fields));
315
+ methods.push(this.makeCrudMethod("delete", entity.name, fields));
316
+ methods.push(this.makeCrudMethod("list", entity.name, fields));
317
+
318
+ // Capability-derived methods
319
+ for (const cap of capabilities) {
320
+ methods.push(this.lowerCapability(cap));
321
+ }
322
+
323
+ const iface: IR.IRInterface = {
324
+ name: `I${entity.name}Service`,
325
+ methods,
326
+ };
327
+
328
+ // Find related store
329
+ const relatedStore = stores.find(s => s.name.toLowerCase().includes(entity.name.toLowerCase()));
330
+ const deps: string[] = [];
331
+ if (relatedStore) {
332
+ deps.push(makeId(this.systemName, "data_store", relatedStore.name));
333
+ }
334
+
335
+ // Lower relations
336
+ const relations: IR.IRRelation[] = entity.relations.map(rel => {
337
+ const fromTable = toSnakeCase(entity.name) + "s";
338
+ const toTable = toSnakeCase(rel.target) + "s";
339
+ let foreignKey: string;
340
+ let junctionTable: string | undefined;
341
+
342
+ switch (rel.relationType) {
343
+ case "belongs_to":
344
+ foreignKey = toSnakeCase(rel.target) + "_id";
345
+ break;
346
+ case "has_one":
347
+ case "has_many":
348
+ foreignKey = toSnakeCase(entity.name) + "_id";
349
+ break;
350
+ case "many_to_many":
351
+ foreignKey = toSnakeCase(entity.name) + "_id";
352
+ junctionTable = [fromTable, toTable].sort().join("_");
353
+ break;
354
+ default:
355
+ foreignKey = toSnakeCase(rel.target) + "_id";
356
+ }
357
+
358
+ return {
359
+ name: rel.name,
360
+ kind: rel.relationType,
361
+ from_entity: entity.name,
362
+ to_entity: rel.target,
363
+ from_table: fromTable,
364
+ to_table: toTable,
365
+ foreign_key: foreignKey,
366
+ junction_table: junctionTable,
367
+ };
368
+ });
369
+
370
+ return {
371
+ id: moduleId,
372
+ kind: "api_service",
373
+ name: `${entity.name}Service`,
374
+ interfaces: [iface],
375
+ models: [model],
376
+ events: [],
377
+ state_machines: stateMachines,
378
+ relations,
379
+ dependencies: deps,
380
+ config: {
381
+ authenticated: entity.auth !== null && entity.auth !== "none",
382
+ auth_method: entity.auth || "none",
383
+ },
384
+ };
385
+ }
386
+
387
+ private makeCrudMethod(op: string, entityName: string, fields: IR.IRField[]): IR.IRMethod {
388
+ const input: IR.IRField[] = op === "create" || op === "update"
389
+ ? fields.filter(f => f.name !== "id" && f.name !== "created_at" && f.name !== "updated_at")
390
+ : op === "list"
391
+ ? [
392
+ { name: "page", type: "uint", nullable: true, unique: false, indexed: false, default_value: "1" },
393
+ { name: "page_size", type: "uint", nullable: true, unique: false, indexed: false, default_value: "50" },
394
+ ]
395
+ : [{ name: "id", type: "uuid", nullable: false, unique: true, indexed: true, default_value: null }];
396
+
397
+ return {
398
+ name: op,
399
+ input,
400
+ output: op === "list" ? `list<${entityName}>` : op === "delete" ? "bool" : entityName,
401
+ preconditions: [],
402
+ effects: [],
403
+ emissions: [],
404
+ idempotent: op === "read" || op === "list",
405
+ authenticated: true,
406
+ timeout_ms: 30000,
407
+ retry: null,
408
+ pipeline: null,
409
+ algorithm: null,
410
+ sync: null,
411
+ };
412
+ }
413
+
414
+ private lowerCapability(cap: AST.CapabilityDeclNode): IR.IRMethod {
415
+ const input: IR.IRField[] = cap.params.map(p => ({
416
+ name: p.name,
417
+ type: serializeType(p.type),
418
+ nullable: false,
419
+ unique: false,
420
+ indexed: false,
421
+ default_value: null,
422
+ }));
423
+
424
+ const preconditions: IR.IRPrecondition[] = cap.requires.map(r => ({
425
+ expression: serializeExpr(r),
426
+ description: serializeExpr(r),
427
+ }));
428
+
429
+ const effects: IR.IREffect[] = cap.effects.map(e => ({
430
+ target: e.target.path.join("."),
431
+ op: e.op === "=" ? "assign" as const : e.op === "+=" ? "add" as const : "remove" as const,
432
+ value: serializeExpr(e.value),
433
+ }));
434
+
435
+ const emissions = cap.emits.map(e => e.eventName);
436
+
437
+ // Lower pipeline if present
438
+ let pipeline: IR.IRPipeline | null = null;
439
+ if (cap.pipeline) {
440
+ pipeline = {
441
+ parallel: cap.pipeline.parallel,
442
+ steps: cap.pipeline.steps.map(step => ({
443
+ call_name: step.call.name,
444
+ call_args: step.call.args.map(a => serializeExpr(a)),
445
+ bind_as: step.bindAs,
446
+ })),
447
+ on_error: cap.pipeline.onError ? {
448
+ action: cap.pipeline.onError.action,
449
+ call_name: cap.pipeline.onError.call?.name || null,
450
+ call_args: cap.pipeline.onError.call?.args.map(a => serializeExpr(a)) || [],
451
+ } : null,
452
+ };
453
+ }
454
+
455
+ // Lower algorithm if present
456
+ let algorithm: IR.IRAlgorithm | null = null;
457
+ if (cap.algorithm) {
458
+ algorithm = {
459
+ catalog_name: cap.algorithm.name,
460
+ bindings: cap.algorithm.using.map(b => ({
461
+ param: b.param,
462
+ value: serializeExpr(b.value),
463
+ })),
464
+ };
465
+ }
466
+
467
+ return {
468
+ name: cap.name,
469
+ input,
470
+ output: cap.returns ? serializeType(cap.returns) : "result<void, error>",
471
+ preconditions,
472
+ effects,
473
+ emissions,
474
+ idempotent: cap.idempotent || false,
475
+ authenticated: true,
476
+ timeout_ms: parseDurationMs(cap.timeout) || 30000,
477
+ retry: cap.retry ? {
478
+ max_attempts: cap.retry.maxAttempts || 3,
479
+ backoff: (cap.retry.backoff as IR.IRRetryPolicy["backoff"]) || "exponential",
480
+ interval_ms: parseDurationMs(cap.retry.interval) || 1000,
481
+ } : null,
482
+ pipeline,
483
+ algorithm,
484
+ sync: cap.sync,
485
+ };
486
+ }
487
+
488
+ // ─── Channel Lowering ──────────────────────────────────────────────────────
489
+
490
+ private lowerChannel(channel: AST.ChannelDeclNode): IR.IRModule {
491
+ return {
492
+ id: makeId(this.systemName, "realtime_service", channel.name),
493
+ kind: "realtime_service",
494
+ name: channel.name,
495
+ interfaces: [{
496
+ name: `I${channel.name}Channel`,
497
+ methods: [
498
+ { name: "connect", input: [], output: "connection", preconditions: [], effects: [], emissions: [], idempotent: false, authenticated: true, timeout_ms: 5000, retry: null, pipeline: null, algorithm: null, sync: null },
499
+ { name: "subscribe", input: [{ name: "topic", type: "string", nullable: false, unique: false, indexed: false, default_value: null }], output: "subscription", preconditions: [], effects: [], emissions: [], idempotent: true, authenticated: true, timeout_ms: 5000, retry: null, pipeline: null, algorithm: null, sync: null },
500
+ { name: "publish", input: [{ name: "message", type: "json", nullable: false, unique: false, indexed: false, default_value: null }], output: "void", preconditions: [], effects: [], emissions: [], idempotent: false, authenticated: true, timeout_ms: 5000, retry: null, pipeline: null, algorithm: null, sync: null },
501
+ ],
502
+ }],
503
+ models: [],
504
+ events: [],
505
+ state_machines: [],
506
+ relations: [],
507
+ dependencies: [],
508
+ config: {
509
+ transport: channel.transport || "websocket",
510
+ ordering: channel.ordering || "fifo",
511
+ persistence: channel.persistence || "none",
512
+ max_size: channel.maxSize || 10000,
513
+ },
514
+ };
515
+ }
516
+
517
+ // ─── Event Lowering ────────────────────────────────────────────────────────
518
+
519
+ private lowerEvent(ev: AST.EventDeclNode): IR.IREvent {
520
+ return {
521
+ id: makeId(this.systemName, "event", ev.name),
522
+ name: ev.name,
523
+ payload: ev.payload.map(f => this.lowerField(f)),
524
+ source: "unknown", // resolved during dependency resolution
525
+ delivery: (ev.delivery as IR.IRDeliveryMode) || "at_least_once",
526
+ ordering: "fifo",
527
+ ttl_ms: parseDurationMs(ev.ttl),
528
+ };
529
+ }
530
+
531
+ // ─── Flow Lowering ─────────────────────────────────────────────────────────
532
+
533
+ private lowerFlow(flow: AST.FlowDeclNode): IR.IRFlow {
534
+ return {
535
+ name: flow.name,
536
+ steps: flow.steps.map(s => ({
537
+ name: s.name,
538
+ action: `${s.action.name}(${s.action.args.map(serializeExpr).join(", ")})`,
539
+ compensation: s.compensate ? `${s.compensate.name}(${s.compensate.args.map(serializeExpr).join(", ")})` : null,
540
+ })),
541
+ };
542
+ }
543
+
544
+ // ─── Field Lowering ────────────────────────────────────────────────────────
545
+
546
+ private lowerField(f: AST.FieldNode): IR.IRField {
547
+ return {
548
+ name: f.name,
549
+ type: serializeType(f.type),
550
+ nullable: f.type.kind === "GenericType" && f.type.name === "optional",
551
+ unique: false,
552
+ indexed: false,
553
+ default_value: f.defaultValue ? serializeExpr(f.defaultValue) : null,
554
+ };
555
+ }
556
+ }