bonescript-compiler 0.5.2 → 0.5.4

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