@almadar/core 10.102.0 → 10.104.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.
Files changed (51) hide show
  1. package/dist/builders.d.ts +5 -5
  2. package/dist/builders.js +5 -2
  3. package/dist/builders.js.map +1 -1
  4. package/dist/{bus-BS4TMOJW.d.ts → bus-DCYJF88B.d.ts} +37 -7
  5. package/dist/{domain-Ds_7wZIL.d.ts → domain-DnEv7amB.d.ts} +17 -5
  6. package/dist/{effect-Hm_QDaSI.d.ts → effect-DhsB3usU.d.ts} +26 -376
  7. package/dist/{entityAccess-D2OpLCyX.d.ts → entityAccess-CLWlioZ0.d.ts} +3 -3
  8. package/dist/{expression-DRwzVnMu.d.ts → expression-CfeHdS-f.d.ts} +10 -1
  9. package/dist/factory/index.d.ts +5 -5
  10. package/dist/factory/index.js +397 -4805
  11. package/dist/factory/index.js.map +1 -1
  12. package/dist/factory-runtime/index.d.ts +5 -5
  13. package/dist/factory-runtime/index.js +34 -9
  14. package/dist/factory-runtime/index.js.map +1 -1
  15. package/dist/fixtures.d.ts +3 -3
  16. package/dist/fixtures.js +20 -3
  17. package/dist/fixtures.js.map +1 -1
  18. package/dist/i18n/index.js +27 -6
  19. package/dist/i18n/index.js.map +1 -1
  20. package/dist/{index-CyfcxG5N.d.ts → index-B1az2E8z.d.ts} +122 -23
  21. package/dist/index.d.ts +85 -17
  22. package/dist/index.js +950 -5291
  23. package/dist/index.js.map +1 -1
  24. package/dist/lolo-document/index.d.ts +166 -0
  25. package/dist/lolo-document/index.js +615 -0
  26. package/dist/lolo-document/index.js.map +1 -0
  27. package/dist/mock/index.d.ts +6 -6
  28. package/dist/mock/index.js +12 -1
  29. package/dist/mock/index.js.map +1 -1
  30. package/dist/patterns/component-mapping.json +17 -192
  31. package/dist/patterns/event-contracts.json +1 -86
  32. package/dist/patterns/index.d.ts +10624 -20955
  33. package/dist/patterns/index.js +663 -5326
  34. package/dist/patterns/index.js.map +1 -1
  35. package/dist/patterns/patterns-registry.json +398 -4806
  36. package/dist/patterns/registry.json +398 -4806
  37. package/dist/{schema-COf9yTjs.d.ts → schema-CjwAD-Y4.d.ts} +1577 -274
  38. package/dist/state-machine/index.d.ts +1 -1
  39. package/dist/themes/index.d.ts +1 -1
  40. package/dist/themes/index.js +2 -1
  41. package/dist/themes/index.js.map +1 -1
  42. package/dist/{trait-Be3xF0px.d.ts → trait-DoFyUfui.d.ts} +75 -11
  43. package/dist/types/index.d.ts +8 -8
  44. package/dist/types/index.js +221 -54
  45. package/dist/types/index.js.map +1 -1
  46. package/dist/{types-CYOzs4As.d.ts → types-DBAbXfRk.d.ts} +2 -2
  47. package/fixtures/events-wire/client-effects-provenance/request.json +5 -0
  48. package/fixtures/events-wire/client-effects-provenance/response.json +57 -0
  49. package/fixtures/events-wire/effect-results-every-kind/response.json +1 -1
  50. package/package.json +7 -2
  51. package/src/types/bindings.ts +28 -3
@@ -0,0 +1,615 @@
1
+ // src/lolo-document/index.ts
2
+ function isIdentStart(ch) {
3
+ return /[A-Za-z_@]/.test(ch);
4
+ }
5
+ function skipString(text, i) {
6
+ let j = i + 1;
7
+ while (j < text.length) {
8
+ const ch = text[j];
9
+ if (ch === "\\") {
10
+ j += 2;
11
+ continue;
12
+ }
13
+ if (ch === '"') return j + 1;
14
+ j++;
15
+ }
16
+ return text.length;
17
+ }
18
+ function skipComment(text, i) {
19
+ if (text.startsWith("#=", i)) {
20
+ const end = text.indexOf("=#", i + 2);
21
+ return end === -1 ? text.length : end + 2;
22
+ }
23
+ const nl = text.indexOf("\n", i);
24
+ return nl === -1 ? text.length : nl;
25
+ }
26
+ function isCommentStart(text, i) {
27
+ return text.startsWith(";;", i) || text[i] === "#";
28
+ }
29
+ function matchDelimiter(text, openIdx) {
30
+ const open = text[openIdx];
31
+ const close = open === "{" ? "}" : open === "[" ? "]" : open === "(" ? ")" : "";
32
+ if (close === "") return -1;
33
+ let depth = 0;
34
+ let i = openIdx;
35
+ while (i < text.length) {
36
+ const ch = text[i];
37
+ if (ch === '"') {
38
+ i = skipString(text, i);
39
+ continue;
40
+ }
41
+ if (isCommentStart(text, i)) {
42
+ i = skipComment(text, i);
43
+ continue;
44
+ }
45
+ if (ch === "{" || ch === "[" || ch === "(") depth++;
46
+ else if (ch === "}" || ch === "]" || ch === ")") {
47
+ depth--;
48
+ if (depth === 0) return i;
49
+ }
50
+ i++;
51
+ }
52
+ return -1;
53
+ }
54
+ function scanValueEnd(text, start, stopBefore) {
55
+ let i = start;
56
+ let depth = 0;
57
+ while (i < stopBefore) {
58
+ const ch = text[i];
59
+ if (ch === '"') {
60
+ i = skipString(text, i);
61
+ continue;
62
+ }
63
+ if (isCommentStart(text, i)) break;
64
+ if (ch === "{" || ch === "[" || ch === "(") depth++;
65
+ else if (ch === "}" || ch === "]" || ch === ")") {
66
+ if (depth === 0) break;
67
+ depth--;
68
+ } else if (ch === "\n" && depth === 0) break;
69
+ i++;
70
+ }
71
+ let end = i;
72
+ while (end > start && /\s/.test(text[end - 1])) end--;
73
+ return end;
74
+ }
75
+ function lineStart(text, idx) {
76
+ const nl = text.lastIndexOf("\n", idx - 1);
77
+ return nl === -1 ? 0 : nl + 1;
78
+ }
79
+ function lineEnd(text, idx) {
80
+ const nl = text.indexOf("\n", idx);
81
+ return nl === -1 ? text.length : nl;
82
+ }
83
+ function indentOf(text, idx) {
84
+ const ls = lineStart(text, idx);
85
+ const m = /^[ \t]*/.exec(text.slice(ls, lineEnd(text, ls)));
86
+ return m ? m[0] : "";
87
+ }
88
+ function* statementStarts(text, bodySpan) {
89
+ let i = bodySpan.start;
90
+ let depth = 0;
91
+ let atLineStart = true;
92
+ while (i < bodySpan.end) {
93
+ const ch = text[i];
94
+ if (ch === '"') {
95
+ i = skipString(text, i);
96
+ atLineStart = false;
97
+ continue;
98
+ }
99
+ if (isCommentStart(text, i)) {
100
+ i = skipComment(text, i);
101
+ continue;
102
+ }
103
+ if (ch === "\n") {
104
+ atLineStart = true;
105
+ i++;
106
+ continue;
107
+ }
108
+ if (/\s/.test(ch)) {
109
+ i++;
110
+ continue;
111
+ }
112
+ if (depth === 0 && atLineStart && isIdentStart(ch)) {
113
+ yield i;
114
+ }
115
+ atLineStart = false;
116
+ if (ch === "{" || ch === "[" || ch === "(") depth++;
117
+ else if (ch === "}" || ch === "]" || ch === ")") depth--;
118
+ i++;
119
+ }
120
+ }
121
+ function aliasForBehavior(behavior) {
122
+ return behavior.split(/[^A-Za-z0-9]+/).filter((part) => part.length > 0).map((part) => part[0].toUpperCase() + part.slice(1)).join("");
123
+ }
124
+ var USES_RE = /^uses\s+([A-Za-z_][\w]*)\s+from\s+"([^"]+)"/;
125
+ var TYPE_RE = /^type\s+([A-Za-z_][\w]*)\s*=/;
126
+ var ENTITY_RE = /^entity\s+([A-Za-z_][\w]*)\s*(\[[^\]]*\])?\s*\{/;
127
+ var TRAIT_REF_RE = /^trait\s+([A-Za-z_][\w]*)\s*(?:::[^=]*)?=\s*([A-Za-z_][\w]*)\.traits\.([A-Za-z_][\w]*)\s*(?:->\s*(@rebindable\s+)?([A-Za-z_][\w]*))?\s*\{/;
128
+ var TRAIT_INLINE_RE = /^trait\s+([A-Za-z_][\w]*)\s*(?:::[^-]*)?->\s*(@rebindable\s+)?([A-Za-z_][\w]*)\s*(\[[^\]]*\])?\s*\{/;
129
+ var PAGE_RE = /^page\s+"([^"]+)"(?:\s+as\s+([A-Za-z_][\w]*))?/;
130
+ var FIELD_RE = /^([A-Za-z_][\w]*)\s*:\s*([^\n=]+?)(?:\s*=\s*|\s*$)/;
131
+ function blockAt(text, openIdx) {
132
+ const close = matchDelimiter(text, openIdx);
133
+ if (close === -1) return null;
134
+ return { span: { start: openIdx, end: close + 1 }, bodySpan: { start: openIdx + 1, end: close } };
135
+ }
136
+ function parseConfigBlock(text, block) {
137
+ const entries = [];
138
+ for (const s of statementStarts(text, block.bodySpan)) {
139
+ if (text[s] === "@") continue;
140
+ const le = lineEnd(text, s);
141
+ const head = text.slice(s, le);
142
+ const m = /^([A-Za-z_][\w]*)\s*:/.exec(head);
143
+ if (!m) continue;
144
+ const key = m[1];
145
+ const afterColon = s + m[0].length;
146
+ const typed = /^(\s*)([^=\n]+?)\s*=\s*/.exec(text.slice(afterColon, le));
147
+ let type;
148
+ let valueStart;
149
+ if (typed && /^\s/.test(text.slice(afterColon, afterColon + 1)) && head.includes(" = ")) {
150
+ type = typed[2].trim();
151
+ valueStart = afterColon + typed[0].length;
152
+ } else {
153
+ valueStart = afterColon;
154
+ while (valueStart < le && /[ \t]/.test(text[valueStart])) valueStart++;
155
+ }
156
+ const valueEnd = scanValueEnd(text, valueStart, block.bodySpan.end);
157
+ entries.push({
158
+ key,
159
+ type,
160
+ valueSpan: { start: valueStart, end: valueEnd },
161
+ rawValue: text.slice(valueStart, valueEnd),
162
+ span: { start: lineStart(text, s), end: valueEnd }
163
+ });
164
+ }
165
+ return { ...block, entries };
166
+ }
167
+ function parseEventsBlock(text, block) {
168
+ const entries = [];
169
+ const body = text.slice(block.bodySpan.start, block.bodySpan.end);
170
+ const re = /([A-Za-z_][\w]*)\s*:\s*([A-Za-z_][\w]*)/g;
171
+ let m;
172
+ while ((m = re.exec(body)) !== null) {
173
+ const start = block.bodySpan.start + m.index;
174
+ entries.push({ upstream: m[1], local: m[2], span: { start, end: start + m[0].length } });
175
+ }
176
+ return { ...block, entries };
177
+ }
178
+ function parseTrait(text, s) {
179
+ const rest = text.slice(s);
180
+ let decl;
181
+ let openOffset;
182
+ const ref = TRAIT_REF_RE.exec(rest);
183
+ if (ref) {
184
+ decl = {
185
+ name: ref[1],
186
+ inline: false,
187
+ alias: ref[2],
188
+ trait: ref[3],
189
+ entity: ref[5],
190
+ rebindable: ref[4] !== void 0
191
+ };
192
+ openOffset = ref[0].length - 1;
193
+ } else {
194
+ const inl = TRAIT_INLINE_RE.exec(rest);
195
+ if (!inl) return null;
196
+ decl = { name: inl[1], inline: true, entity: inl[3], rebindable: inl[2] !== void 0 };
197
+ openOffset = inl[0].length - 1;
198
+ }
199
+ const block = blockAt(text, s + openOffset);
200
+ if (!block) return null;
201
+ const trait = { ...decl, span: { start: s, end: block.span.end }, bodySpan: block.bodySpan, stateBlocks: [] };
202
+ for (const cs of statementStarts(text, block.bodySpan)) {
203
+ const le = lineEnd(text, cs);
204
+ const head = text.slice(cs, le);
205
+ const kw = /^(config|events|listens|state|fields|emits|ticks)\b\s*([A-Za-z_][\w]*)?[^{]*\{/.exec(head);
206
+ if (!kw) continue;
207
+ const open = cs + head.indexOf("{");
208
+ const sub = blockAt(text, open);
209
+ if (!sub) continue;
210
+ if (kw[1] === "config") trait.config = parseConfigBlock(text, sub);
211
+ else if (kw[1] === "events") trait.events = parseEventsBlock(text, sub);
212
+ else if (kw[1] === "listens") trait.listens = sub;
213
+ else if (kw[1] === "state" && kw[2]) trait.stateBlocks.push({ name: kw[2], span: { start: cs, end: sub.span.end } });
214
+ }
215
+ return trait;
216
+ }
217
+ function parseEntity(text, s) {
218
+ const m = ENTITY_RE.exec(text.slice(s));
219
+ if (!m) return null;
220
+ const block = blockAt(text, s + m[0].length - 1);
221
+ if (!block) return null;
222
+ const fields = [];
223
+ for (const fs of statementStarts(text, block.bodySpan)) {
224
+ const le = lineEnd(text, fs);
225
+ const fm = FIELD_RE.exec(text.slice(fs, le));
226
+ if (!fm) continue;
227
+ fields.push({ name: fm[1], type: fm[2].trim(), span: { start: lineStart(text, fs), end: le } });
228
+ }
229
+ return { name: m[1], tags: m[2] ?? "", fields, span: { start: s, end: block.span.end }, bodySpan: block.bodySpan };
230
+ }
231
+ function parsePage(text, s) {
232
+ const le = lineEnd(text, s);
233
+ const head = text.slice(s, le);
234
+ const m = PAGE_RE.exec(head);
235
+ if (!m) return null;
236
+ const bracket = head.indexOf("[", m[0].length);
237
+ const tail = bracket === -1 ? le : s + bracket;
238
+ const arrowIdx = head.indexOf("->", m[0].length);
239
+ const arrowAt = arrowIdx !== -1 && (bracket === -1 || arrowIdx < bracket) ? s + arrowIdx : -1;
240
+ let traitsStart;
241
+ let traitsEnd = tail;
242
+ if (arrowAt === -1) {
243
+ while (traitsEnd > s + m[0].length && /\s/.test(text[traitsEnd - 1])) traitsEnd--;
244
+ traitsStart = traitsEnd;
245
+ } else {
246
+ traitsStart = arrowAt + 2;
247
+ while (traitsStart < tail && /\s/.test(text[traitsStart])) traitsStart++;
248
+ while (traitsEnd > traitsStart && /\s/.test(text[traitsEnd - 1])) traitsEnd--;
249
+ }
250
+ const traits = arrowAt === -1 ? [] : text.slice(traitsStart, traitsEnd).split(",").map((t) => t.trim()).filter((t) => t.length > 0);
251
+ const page = { path: m[1], name: m[2], traits, traitsSpan: { start: traitsStart, end: traitsEnd }, span: { start: s, end: le } };
252
+ if (arrowAt !== -1) {
253
+ let arrowStart = arrowAt;
254
+ while (arrowStart > s && /\s/.test(text[arrowStart - 1])) arrowStart--;
255
+ page.arrowSpan = { start: arrowStart, end: arrowAt + 2 };
256
+ }
257
+ return page;
258
+ }
259
+ function parseLoloDocument(text) {
260
+ const orbitalRe = /^orbital\s+([A-Za-z_][\w]*)\s*\{/m;
261
+ const om = orbitalRe.exec(text);
262
+ if (!om || om.index === void 0) throw new Error("lolo-document: no `orbital Name {` block found");
263
+ const openIdx = om.index + om[0].length - 1;
264
+ const block = blockAt(text, openIdx);
265
+ if (!block) throw new Error("lolo-document: unbalanced orbital block");
266
+ const doc = {
267
+ text,
268
+ orbital: { name: om[1], span: { start: om.index, end: block.span.end }, bodySpan: block.bodySpan },
269
+ uses: [],
270
+ types: [],
271
+ entities: [],
272
+ traits: [],
273
+ pages: [],
274
+ indent: " "
275
+ };
276
+ const appMatch = /^app\s+([\w.-]+)\s+(?:"([^"]+)"|(v[\w.]+))/m.exec(text);
277
+ if (appMatch && appMatch.index !== void 0) {
278
+ doc.app = { name: appMatch[1], version: appMatch[2] ?? appMatch[3] ?? "", span: { start: appMatch.index, end: lineEnd(text, appMatch.index) } };
279
+ }
280
+ let indentSeen = false;
281
+ for (const s of statementStarts(text, block.bodySpan)) {
282
+ if (!indentSeen) {
283
+ doc.indent = indentOf(text, s);
284
+ indentSeen = true;
285
+ }
286
+ const le = lineEnd(text, s);
287
+ const head = text.slice(s, le);
288
+ const u = USES_RE.exec(head);
289
+ if (u) {
290
+ doc.uses.push({ alias: u[1], from: u[2], span: { start: s, end: le } });
291
+ continue;
292
+ }
293
+ const t = TYPE_RE.exec(head);
294
+ if (t) {
295
+ const brace = head.indexOf("{");
296
+ const end = brace !== -1 ? matchDelimiter(text, s + brace) + 1 : le;
297
+ doc.types.push({ name: t[1], span: { start: s, end } });
298
+ continue;
299
+ }
300
+ if (head.startsWith("entity ")) {
301
+ const e = parseEntity(text, s);
302
+ if (e) doc.entities.push(e);
303
+ continue;
304
+ }
305
+ if (head.startsWith("trait ")) {
306
+ const tr = parseTrait(text, s);
307
+ if (tr) doc.traits.push(tr);
308
+ continue;
309
+ }
310
+ if (head.startsWith("page ")) {
311
+ const p = parsePage(text, s);
312
+ if (p) doc.pages.push(p);
313
+ }
314
+ }
315
+ return doc;
316
+ }
317
+ function applyEdits(text, edits) {
318
+ const sorted = [...edits].sort((a, b) => b.start - a.start || b.end - a.end);
319
+ for (let i = 1; i < sorted.length; i++) {
320
+ if (sorted[i].end > sorted[i - 1].start) throw new Error("lolo-document: overlapping edits");
321
+ }
322
+ let out = text;
323
+ for (const e of sorted) out = out.slice(0, e.start) + e.insert + out.slice(e.end);
324
+ return out;
325
+ }
326
+ function quote(s) {
327
+ return '"' + s.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n") + '"';
328
+ }
329
+ function formatLoloValue(value, indent = "") {
330
+ if (value === null) return "null";
331
+ if (typeof value === "string") return quote(value);
332
+ if (typeof value === "number" || typeof value === "boolean") return String(value);
333
+ if (Array.isArray(value)) {
334
+ if (value.length === 0) return "[]";
335
+ const allObjects = value.every((v) => typeof v === "object" && v !== null && !Array.isArray(v));
336
+ if (allObjects) {
337
+ const inner = indent + " ";
338
+ return "[\n" + value.map((v) => inner + formatLoloValue(v, inner)).join("\n") + "\n" + indent + "]";
339
+ }
340
+ return "[" + value.map((v) => formatLoloValue(v, indent)).join(" ") + "]";
341
+ }
342
+ const parts = Object.entries(value).map(([k, v]) => `${k}: ${formatLoloValue(v, indent)}`);
343
+ return parts.length === 0 ? "{}" : "{ " + parts.join(", ") + " }";
344
+ }
345
+ function inferLoloType(value) {
346
+ if (typeof value === "string") return "string";
347
+ if (typeof value === "number") return "number";
348
+ if (typeof value === "boolean") return "boolean";
349
+ if (Array.isArray(value)) return "[object]";
350
+ return "object";
351
+ }
352
+ function findTrait(doc, name) {
353
+ const t = doc.traits.find((x) => x.name === name);
354
+ if (!t) throw new Error(`lolo-document: trait "${name}" not found`);
355
+ return t;
356
+ }
357
+ function insertAtBlockEnd(text, block, line, childIndent) {
358
+ const closeIdx = block.bodySpan.end;
359
+ const ls = lineStart(text, closeIdx);
360
+ const singleLine = !text.slice(block.bodySpan.start, block.bodySpan.end).includes("\n");
361
+ if (singleLine) {
362
+ const parentIndent = indentOf(text, block.span.start);
363
+ let at = block.bodySpan.end;
364
+ while (at > block.bodySpan.start && /\s/.test(text[at - 1])) at--;
365
+ return { start: at, end: block.bodySpan.end, insert: `
366
+ ${childIndent}${line}
367
+ ${parentIndent}` };
368
+ }
369
+ return { start: ls, end: ls, insert: `${childIndent}${line}
370
+ ` };
371
+ }
372
+ function childIndentOf(text, block, fallbackParentIndent) {
373
+ const openerLine = lineStart(text, block.span.start);
374
+ for (const s of statementStarts(text, block.bodySpan)) {
375
+ if (lineStart(text, s) !== openerLine) return indentOf(text, s);
376
+ }
377
+ return fallbackParentIndent + " ";
378
+ }
379
+ function addUses(doc, alias, from) {
380
+ if (doc.uses.some((u) => u.from === from || u.alias === alias)) return [];
381
+ const line = `${doc.indent}uses ${alias} from ${quote(from)}
382
+ `;
383
+ if (doc.uses.length > 0) {
384
+ const last = doc.uses[doc.uses.length - 1];
385
+ const at2 = lineEnd(doc.text, last.span.end) + 1;
386
+ return [{ start: at2, end: at2, insert: line }];
387
+ }
388
+ const at = lineEnd(doc.text, doc.orbital.bodySpan.start) + 1;
389
+ return [{ start: at, end: at, insert: line }];
390
+ }
391
+ function usesAlias(doc, from) {
392
+ return doc.uses.find((u) => u.from === from)?.alias;
393
+ }
394
+ function addTraitRef(doc, input) {
395
+ if (doc.traits.some((t) => t.name === input.name)) throw new Error(`lolo-document: trait "${input.name}" already exists`);
396
+ const ind = doc.indent;
397
+ const inner = ind + " ";
398
+ const lines = [`${ind}trait ${input.name} = ${input.alias}.traits.${input.trait} -> ${input.entity} {`];
399
+ const events = Object.entries(input.events ?? {});
400
+ if (events.length > 0) {
401
+ lines.push(`${inner}events {`);
402
+ for (const [up, local] of events) lines.push(`${inner} ${up}: ${local}`);
403
+ lines.push(`${inner}}`);
404
+ }
405
+ const cfg = Object.entries(input.config);
406
+ if (cfg.length > 0) {
407
+ lines.push(`${inner}config {`);
408
+ for (const [k, v] of cfg) lines.push(`${inner} ${k}: ${formatLoloValue(v, inner + " ")}`);
409
+ lines.push(`${inner}}`);
410
+ }
411
+ lines.push(`${ind}}`);
412
+ const block = lines.join("\n") + "\n\n";
413
+ const anchor = doc.pages.length > 0 ? lineStart(doc.text, doc.pages[0].span.start) : lineStart(doc.text, doc.orbital.bodySpan.end);
414
+ return [{ start: anchor, end: anchor, insert: block }];
415
+ }
416
+ function removeTrait(doc, name) {
417
+ const trait = findTrait(doc, name);
418
+ const edits = [];
419
+ const ls = lineStart(doc.text, trait.span.start);
420
+ let end = lineEnd(doc.text, trait.span.end);
421
+ if (doc.text[end] === "\n") end++;
422
+ if (doc.text[end] === "\n" && doc.text[ls - 1] === "\n" && doc.text[ls - 2] === "\n") end++;
423
+ edits.push({ start: ls, end, insert: "" });
424
+ for (const page of doc.pages) {
425
+ if (page.traits.includes(name)) edits.push(...removePageTrait(doc, page.path, name));
426
+ }
427
+ if (trait.alias !== void 0) {
428
+ const stillUsed = doc.traits.some((t) => t.name !== name && t.alias === trait.alias);
429
+ const pageUses = doc.text.slice(doc.orbital.bodySpan.start, doc.orbital.bodySpan.end).includes(`${trait.alias}.pages.`);
430
+ if (!stillUsed && !pageUses) {
431
+ const u = doc.uses.find((x) => x.alias === trait.alias);
432
+ if (u) {
433
+ const uls = lineStart(doc.text, u.span.start);
434
+ let uend = lineEnd(doc.text, u.span.end);
435
+ if (doc.text[uend] === "\n") uend++;
436
+ edits.push({ start: uls, end: uend, insert: "" });
437
+ }
438
+ }
439
+ }
440
+ return edits;
441
+ }
442
+ function findPage(doc, path) {
443
+ const p = doc.pages.find((x) => x.path === path);
444
+ if (!p) throw new Error(`lolo-document: page "${path}" not found`);
445
+ return p;
446
+ }
447
+ function appendPageTrait(doc, path, traitName) {
448
+ const page = findPage(doc, path);
449
+ if (page.traits.includes(traitName)) return [];
450
+ const insert = page.arrowSpan === void 0 ? ` -> ${traitName}` : page.traits.length === 0 ? traitName : `, ${traitName}`;
451
+ return [{ start: page.traitsSpan.end, end: page.traitsSpan.end, insert }];
452
+ }
453
+ function removePageTrait(doc, path, traitName) {
454
+ const page = findPage(doc, path);
455
+ if (!page.traits.includes(traitName)) return [];
456
+ const remaining = page.traits.filter((t) => t !== traitName);
457
+ if (remaining.length === 0 && page.arrowSpan !== void 0) {
458
+ return [{ start: page.arrowSpan.start, end: page.traitsSpan.end, insert: "" }];
459
+ }
460
+ return [{ start: page.traitsSpan.start, end: page.traitsSpan.end, insert: remaining.join(", ") }];
461
+ }
462
+ function setConfigValue(doc, traitName, key, value) {
463
+ const trait = findTrait(doc, traitName);
464
+ const text = doc.text;
465
+ if (trait.config) {
466
+ const entry = trait.config.entries.find((e) => e.key === key);
467
+ const childIndent = childIndentOf(text, trait.config, indentOf(text, trait.config.span.start));
468
+ if (entry) {
469
+ return [{ start: entry.valueSpan.start, end: entry.valueSpan.end, insert: formatLoloValue(value, childIndent) }];
470
+ }
471
+ const typed = trait.config.entries.some((e) => e.type !== void 0) || trait.inline;
472
+ const line2 = typed ? `${key} : ${inferLoloType(value)} = ${formatLoloValue(value, childIndent)}` : `${key}: ${formatLoloValue(value, childIndent)}`;
473
+ return [insertAtBlockEnd(text, trait.config, line2, childIndent)];
474
+ }
475
+ const inner = indentOf(text, trait.span.start) + " ";
476
+ const line = trait.inline ? `${inner}config {
477
+ ${inner} ${key} : ${inferLoloType(value)} = ${formatLoloValue(value, inner + " ")}
478
+ ${inner}}
479
+ ` : `${inner}config {
480
+ ${inner} ${key}: ${formatLoloValue(value, inner + " ")}
481
+ ${inner}}
482
+ `;
483
+ const ls = lineStart(text, trait.bodySpan.end);
484
+ return [{ start: ls, end: ls, insert: line }];
485
+ }
486
+ function arrayElementSpans(text, arraySpan) {
487
+ const spans = [];
488
+ const open = text.indexOf("[", arraySpan.start);
489
+ if (open === -1 || open >= arraySpan.end) return spans;
490
+ let i = open + 1;
491
+ const close = arraySpan.end - 1;
492
+ while (i < close) {
493
+ const ch = text[i];
494
+ if (/[\s,]/.test(ch)) {
495
+ i++;
496
+ continue;
497
+ }
498
+ if (isCommentStart(text, i)) {
499
+ i = skipComment(text, i);
500
+ continue;
501
+ }
502
+ let end;
503
+ if (ch === "{" || ch === "[" || ch === "(") end = matchDelimiter(text, i) + 1;
504
+ else if (ch === '"') end = skipString(text, i);
505
+ else {
506
+ end = i;
507
+ while (end < close && !/[\s,\]]/.test(text[end])) end++;
508
+ }
509
+ spans.push({ start: i, end });
510
+ i = end;
511
+ }
512
+ return spans;
513
+ }
514
+ function patchObjectLiteral(text, span, patch) {
515
+ const edits = [];
516
+ const body = { start: span.start + 1, end: span.end - 1 };
517
+ const remaining = new Map(Object.entries(patch));
518
+ let i = body.start;
519
+ while (i < body.end) {
520
+ const ch = text[i];
521
+ if (/[\s,]/.test(ch)) {
522
+ i++;
523
+ continue;
524
+ }
525
+ const km = /^([A-Za-z_][\w]*)\s*:\s*/.exec(text.slice(i, body.end));
526
+ if (!km) break;
527
+ const vs = i + km[0].length;
528
+ let ve;
529
+ const vc = text[vs];
530
+ if (vc === "{" || vc === "[" || vc === "(") ve = matchDelimiter(text, vs) + 1;
531
+ else if (vc === '"') ve = skipString(text, vs);
532
+ else {
533
+ ve = vs;
534
+ while (ve < body.end && !/[\s,}]/.test(text[ve])) ve++;
535
+ }
536
+ const key = km[1];
537
+ if (remaining.has(key)) {
538
+ const v = remaining.get(key);
539
+ if (v !== void 0) edits.push({ start: vs, end: ve, insert: formatLoloValue(v) });
540
+ remaining.delete(key);
541
+ }
542
+ i = ve;
543
+ }
544
+ if (remaining.size > 0) {
545
+ const tail = [...remaining.entries()].map(([k, v]) => `${k}: ${formatLoloValue(v)}`).join(", ");
546
+ let insertAt = body.end;
547
+ while (insertAt > body.start && /\s/.test(text[insertAt - 1])) insertAt--;
548
+ const hasFields = text.slice(body.start, body.end).trim().length > 0;
549
+ edits.push({ start: insertAt, end: insertAt, insert: hasFields ? `, ${tail}` : ` ${tail} ` });
550
+ }
551
+ return edits;
552
+ }
553
+ function setConfigArrayRow(doc, traitName, key, index, patch) {
554
+ const trait = findTrait(doc, traitName);
555
+ const entry = trait.config?.entries.find((e) => e.key === key);
556
+ if (!entry) throw new Error(`lolo-document: config "${key}" not found on trait "${traitName}"`);
557
+ const elements = arrayElementSpans(doc.text, entry.valueSpan);
558
+ const el = elements[index];
559
+ if (!el) throw new Error(`lolo-document: "${key}[${index}]" out of range (${elements.length} elements)`);
560
+ if (doc.text[el.start] !== "{") throw new Error(`lolo-document: "${key}[${index}]" is not an object literal`);
561
+ return patchObjectLiteral(doc.text, el, patch);
562
+ }
563
+ function renameEvent(doc, traitName, upstream, local) {
564
+ const trait = findTrait(doc, traitName);
565
+ if (trait.inline) throw new Error("lolo-document: events {} renames apply to referenced traits only");
566
+ const text = doc.text;
567
+ if (trait.events) {
568
+ const existing = trait.events.entries.find((e) => e.upstream === upstream);
569
+ const childIndent = childIndentOf(text, trait.events, indentOf(text, trait.events.span.start));
570
+ if (existing) {
571
+ return [{ start: existing.span.start, end: existing.span.end, insert: `${upstream}: ${local}` }];
572
+ }
573
+ return [insertAtBlockEnd(text, trait.events, `${upstream}: ${local}`, childIndent)];
574
+ }
575
+ const inner = indentOf(text, trait.span.start) + " ";
576
+ const at = lineEnd(text, trait.span.start) + 1;
577
+ return [{ start: at, end: at, insert: `${inner}events {
578
+ ${inner} ${upstream}: ${local}
579
+ ${inner}}
580
+ ` }];
581
+ }
582
+ function addListens(doc, traitName, route, atomRoutes = []) {
583
+ const trait = findTrait(doc, traitName);
584
+ const text = doc.text;
585
+ const line = `${route.source}.${route.event} -> ${route.local}`;
586
+ if (trait.listens) {
587
+ const body = text.slice(trait.listens.bodySpan.start, trait.listens.bodySpan.end);
588
+ if (body.includes(line)) return [];
589
+ const childIndent = childIndentOf(text, trait.listens, indentOf(text, trait.listens.span.start));
590
+ return [insertAtBlockEnd(text, trait.listens, line, childIndent)];
591
+ }
592
+ const inner = indentOf(text, trait.span.start) + " ";
593
+ const routes = trait.inline ? [route] : [...atomRoutes, route];
594
+ const lines = routes.map((r) => `${inner} ${r.source}.${r.event} -> ${r.local}`);
595
+ const ls = lineStart(text, trait.bodySpan.end);
596
+ return [{ start: ls, end: ls, insert: `${inner}listens {
597
+ ${lines.join("\n")}
598
+ ${inner}}
599
+ ` }];
600
+ }
601
+ function addEntityFields(doc, entityName, fields) {
602
+ const entity = doc.entities.find((e) => e.name === entityName);
603
+ if (!entity) throw new Error(`lolo-document: entity "${entityName}" not found`);
604
+ const missing = fields.filter((f) => !entity.fields.some((x) => x.name === f.name));
605
+ if (missing.length === 0) return [];
606
+ const childIndent = childIndentOf(doc.text, entity, indentOf(doc.text, entity.span.start));
607
+ const lines = missing.map((f) => `${childIndent}${f.name} : ${f.type}${f.default !== void 0 ? ` = ${formatLoloValue(f.default, childIndent)}` : ""}
608
+ `);
609
+ const ls = lineStart(doc.text, entity.bodySpan.end);
610
+ return [{ start: ls, end: ls, insert: lines.join("") }];
611
+ }
612
+
613
+ export { addEntityFields, addListens, addTraitRef, addUses, aliasForBehavior, appendPageTrait, applyEdits, arrayElementSpans, findPage, formatLoloValue, lineStart, matchDelimiter, parseLoloDocument, removePageTrait, removeTrait, renameEvent, setConfigArrayRow, setConfigValue, usesAlias };
614
+ //# sourceMappingURL=index.js.map
615
+ //# sourceMappingURL=index.js.map