@almadar/runtime 1.0.10 → 1.0.13

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.
@@ -0,0 +1,1870 @@
1
+ import { resolveBinding, evaluate, createMinimalContext, evaluateGuard } from '@almadar/evaluator';
2
+ export { createMinimalContext } from '@almadar/evaluator';
3
+ import { OrbitalSchemaSchema, isEntityReference, parseEntityRef, parseImportedTraitRef, isPageReference, isPageReferenceString, isPageReferenceObject, parsePageRef } from '@almadar/core';
4
+
5
+ // src/EventBus.ts
6
+ var EventBus = class {
7
+ listeners = /* @__PURE__ */ new Map();
8
+ debug;
9
+ constructor(options = {}) {
10
+ this.debug = options.debug ?? false;
11
+ }
12
+ /**
13
+ * Emit an event to all registered listeners
14
+ */
15
+ emit(type, payload, source) {
16
+ const event = {
17
+ type,
18
+ payload,
19
+ timestamp: Date.now(),
20
+ source
21
+ };
22
+ const listeners = this.listeners.get(type);
23
+ const listenerCount = listeners?.size ?? 0;
24
+ if (this.debug) {
25
+ if (listenerCount > 0) {
26
+ console.log(`[EventBus] Emit: ${type} \u2192 ${listenerCount} listener(s)`, payload);
27
+ } else {
28
+ console.warn(`[EventBus] Emit: ${type} (NO LISTENERS)`, payload);
29
+ }
30
+ }
31
+ if (listeners) {
32
+ const listenersCopy = Array.from(listeners);
33
+ for (const listener of listenersCopy) {
34
+ try {
35
+ listener(event);
36
+ } catch (error) {
37
+ console.error(`[EventBus] Error in listener for '${type}':`, error);
38
+ }
39
+ }
40
+ }
41
+ if (type !== "*") {
42
+ const wildcardListeners = this.listeners.get("*");
43
+ if (wildcardListeners) {
44
+ for (const listener of Array.from(wildcardListeners)) {
45
+ try {
46
+ listener(event);
47
+ } catch (error) {
48
+ console.error(`[EventBus] Error in wildcard listener:`, error);
49
+ }
50
+ }
51
+ }
52
+ }
53
+ }
54
+ /**
55
+ * Subscribe to an event type
56
+ */
57
+ on(type, listener) {
58
+ if (!this.listeners.has(type)) {
59
+ this.listeners.set(type, /* @__PURE__ */ new Set());
60
+ }
61
+ const listeners = this.listeners.get(type);
62
+ listeners.add(listener);
63
+ if (this.debug) {
64
+ console.log(`[EventBus] Subscribed to '${type}', total: ${listeners.size}`);
65
+ }
66
+ return () => {
67
+ listeners.delete(listener);
68
+ if (this.debug) {
69
+ console.log(`[EventBus] Unsubscribed from '${type}', remaining: ${listeners.size}`);
70
+ }
71
+ if (listeners.size === 0) {
72
+ this.listeners.delete(type);
73
+ }
74
+ };
75
+ }
76
+ /**
77
+ * Subscribe to ALL events (wildcard listener)
78
+ * Useful for event tracking, logging, debugging
79
+ */
80
+ onAny(listener) {
81
+ return this.on("*", listener);
82
+ }
83
+ /**
84
+ * Check if there are listeners for an event type
85
+ */
86
+ hasListeners(type) {
87
+ const listeners = this.listeners.get(type);
88
+ return listeners !== void 0 && listeners.size > 0;
89
+ }
90
+ /**
91
+ * Get all registered event types
92
+ */
93
+ getRegisteredEvents() {
94
+ return Array.from(this.listeners.keys());
95
+ }
96
+ /**
97
+ * Clear all listeners
98
+ */
99
+ clear() {
100
+ if (this.debug) {
101
+ console.log(`[EventBus] Clearing all listeners (${this.listeners.size} event types)`);
102
+ }
103
+ this.listeners.clear();
104
+ }
105
+ /**
106
+ * Get listener count for an event type (for testing)
107
+ */
108
+ getListenerCount(type) {
109
+ return this.listeners.get(type)?.size ?? 0;
110
+ }
111
+ };
112
+ var OPERATORS = /* @__PURE__ */ new Set([
113
+ // Comparison
114
+ "=",
115
+ "!=",
116
+ "<",
117
+ ">",
118
+ "<=",
119
+ ">=",
120
+ // Logic
121
+ "and",
122
+ "or",
123
+ "not",
124
+ "if",
125
+ // Math
126
+ "+",
127
+ "-",
128
+ "*",
129
+ "/",
130
+ "%",
131
+ // Array
132
+ "count",
133
+ "first",
134
+ "last",
135
+ "map",
136
+ "filter",
137
+ "find",
138
+ "some",
139
+ "every",
140
+ "reduce",
141
+ // String
142
+ "concat",
143
+ "upper",
144
+ "lower",
145
+ "trim",
146
+ "substring",
147
+ "split",
148
+ "join",
149
+ "str",
150
+ // Effects
151
+ "set",
152
+ "emit",
153
+ "navigate",
154
+ "persist",
155
+ "notify",
156
+ "render-ui",
157
+ "render",
158
+ "spawn",
159
+ "despawn",
160
+ "call-service",
161
+ "do",
162
+ "when",
163
+ "increment",
164
+ "decrement",
165
+ "log"
166
+ ]);
167
+ function interpolateProps(props, ctx) {
168
+ const result = {};
169
+ for (const [key, value] of Object.entries(props)) {
170
+ result[key] = interpolateValue(value, ctx);
171
+ }
172
+ return result;
173
+ }
174
+ function interpolateValue(value, ctx) {
175
+ if (value === null || value === void 0) {
176
+ return value;
177
+ }
178
+ if (typeof value === "string") {
179
+ return interpolateString(value, ctx);
180
+ }
181
+ if (Array.isArray(value)) {
182
+ return interpolateArray(value, ctx);
183
+ }
184
+ if (typeof value === "object") {
185
+ return interpolateProps(value, ctx);
186
+ }
187
+ return value;
188
+ }
189
+ function interpolateString(value, ctx) {
190
+ if (value.startsWith("@") && isPureBinding(value)) {
191
+ return resolveBinding(value, ctx);
192
+ }
193
+ if (value.includes("@")) {
194
+ return interpolateEmbeddedBindings(value, ctx);
195
+ }
196
+ return value;
197
+ }
198
+ function isPureBinding(value) {
199
+ return /^@[\w]+(?:\.[\w]+)*$/.test(value);
200
+ }
201
+ function interpolateEmbeddedBindings(value, ctx) {
202
+ return value.replace(/@[\w]+(?:\.[\w]+)*/g, (match) => {
203
+ const resolved = resolveBinding(match, ctx);
204
+ return resolved !== void 0 ? String(resolved) : match;
205
+ });
206
+ }
207
+ function interpolateArray(value, ctx) {
208
+ if (value.length === 0) {
209
+ return [];
210
+ }
211
+ if (isSExpression(value)) {
212
+ return evaluate(value, ctx);
213
+ }
214
+ return value.map((item) => interpolateValue(item, ctx));
215
+ }
216
+ function isSExpression(value) {
217
+ if (value.length === 0) return false;
218
+ const first = value[0];
219
+ if (typeof first !== "string") return false;
220
+ if (OPERATORS.has(first)) return true;
221
+ if (first.includes("/")) return true;
222
+ if (first === "lambda" || first === "let") return true;
223
+ return false;
224
+ }
225
+ function containsBindings(value) {
226
+ if (typeof value === "string") {
227
+ return value.includes("@");
228
+ }
229
+ if (Array.isArray(value)) {
230
+ return value.some(containsBindings);
231
+ }
232
+ if (value !== null && typeof value === "object") {
233
+ return Object.values(value).some(containsBindings);
234
+ }
235
+ return false;
236
+ }
237
+ function extractBindings(value) {
238
+ const bindings = [];
239
+ function collect(v) {
240
+ if (typeof v === "string") {
241
+ const matches = v.match(/@[\w]+(?:\.[\w]+)*/g);
242
+ if (matches) {
243
+ bindings.push(...matches);
244
+ }
245
+ } else if (Array.isArray(v)) {
246
+ v.forEach(collect);
247
+ } else if (v !== null && typeof v === "object") {
248
+ Object.values(v).forEach(collect);
249
+ }
250
+ }
251
+ collect(value);
252
+ return [...new Set(bindings)];
253
+ }
254
+ function createContextFromBindings(bindings) {
255
+ return createMinimalContext(
256
+ bindings.entity || {},
257
+ bindings.payload || {},
258
+ bindings.state || "idle"
259
+ );
260
+ }
261
+ function findInitialState(trait) {
262
+ if (!trait.states || trait.states.length === 0) {
263
+ console.warn(`[StateMachine] Trait "${trait.name}" has no states defined, using "unknown"`);
264
+ return "unknown";
265
+ }
266
+ const markedInitial = trait.states.find((s) => s.isInitial)?.name;
267
+ const firstState = trait.states[0]?.name;
268
+ return markedInitial || firstState || "unknown";
269
+ }
270
+ function createInitialTraitState(trait) {
271
+ return {
272
+ traitName: trait.name,
273
+ currentState: findInitialState(trait),
274
+ previousState: null,
275
+ lastEvent: null,
276
+ context: {}
277
+ };
278
+ }
279
+ function findTransition(trait, currentState, eventKey) {
280
+ if (!trait.transitions || trait.transitions.length === 0) {
281
+ return void 0;
282
+ }
283
+ return trait.transitions.find((t) => {
284
+ if (Array.isArray(t.from)) {
285
+ return t.from.includes(currentState) && t.event === eventKey;
286
+ }
287
+ return t.from === currentState && t.event === eventKey;
288
+ });
289
+ }
290
+ function normalizeEventKey(eventKey) {
291
+ if (!eventKey) return "";
292
+ return eventKey.startsWith("UI:") ? eventKey.slice(3) : eventKey;
293
+ }
294
+ function processEvent(options) {
295
+ const { traitState, trait, eventKey, payload, entityData } = options;
296
+ const normalizedEvent = normalizeEventKey(eventKey);
297
+ const transition = findTransition(trait, traitState.currentState, normalizedEvent);
298
+ if (!transition) {
299
+ return {
300
+ executed: false,
301
+ newState: traitState.currentState,
302
+ previousState: traitState.currentState,
303
+ effects: []
304
+ };
305
+ }
306
+ if (transition.guard) {
307
+ const ctx = createContextFromBindings({
308
+ entity: entityData,
309
+ payload,
310
+ state: traitState.currentState
311
+ });
312
+ try {
313
+ const guardPasses = evaluateGuard(
314
+ transition.guard,
315
+ ctx
316
+ );
317
+ if (!guardPasses) {
318
+ return {
319
+ executed: false,
320
+ newState: traitState.currentState,
321
+ previousState: traitState.currentState,
322
+ effects: [],
323
+ transition: {
324
+ from: traitState.currentState,
325
+ to: transition.to,
326
+ event: normalizedEvent
327
+ },
328
+ guardResult: false
329
+ };
330
+ }
331
+ } catch (error) {
332
+ console.error("[StateMachineCore] Guard evaluation error:", error);
333
+ }
334
+ }
335
+ return {
336
+ executed: true,
337
+ newState: transition.to,
338
+ previousState: traitState.currentState,
339
+ effects: transition.effects || [],
340
+ transition: {
341
+ from: traitState.currentState,
342
+ to: transition.to,
343
+ event: normalizedEvent
344
+ },
345
+ guardResult: transition.guard ? true : void 0
346
+ };
347
+ }
348
+ var StateMachineManager = class {
349
+ traits = /* @__PURE__ */ new Map();
350
+ states = /* @__PURE__ */ new Map();
351
+ constructor(traits = []) {
352
+ for (const trait of traits) {
353
+ this.addTrait(trait);
354
+ }
355
+ }
356
+ /**
357
+ * Add a trait to the manager.
358
+ */
359
+ addTrait(trait) {
360
+ this.traits.set(trait.name, trait);
361
+ this.states.set(trait.name, createInitialTraitState(trait));
362
+ }
363
+ /**
364
+ * Remove a trait from the manager.
365
+ */
366
+ removeTrait(traitName) {
367
+ this.traits.delete(traitName);
368
+ this.states.delete(traitName);
369
+ }
370
+ /**
371
+ * Get current state for a trait.
372
+ */
373
+ getState(traitName) {
374
+ return this.states.get(traitName);
375
+ }
376
+ /**
377
+ * Get all current states.
378
+ */
379
+ getAllStates() {
380
+ return new Map(this.states);
381
+ }
382
+ /**
383
+ * Check if a trait can handle an event from its current state.
384
+ */
385
+ canHandleEvent(traitName, eventKey) {
386
+ const trait = this.traits.get(traitName);
387
+ const state = this.states.get(traitName);
388
+ if (!trait || !state) return false;
389
+ return !!findTransition(trait, state.currentState, normalizeEventKey(eventKey));
390
+ }
391
+ /**
392
+ * Send an event to all traits.
393
+ *
394
+ * @returns Array of transition results (one per trait that had a matching transition)
395
+ */
396
+ sendEvent(eventKey, payload, entityData) {
397
+ const results = [];
398
+ for (const [traitName, trait] of this.traits) {
399
+ const traitState = this.states.get(traitName);
400
+ if (!traitState) continue;
401
+ const result = processEvent({
402
+ traitState,
403
+ trait,
404
+ eventKey,
405
+ payload,
406
+ entityData
407
+ });
408
+ if (result.executed) {
409
+ this.states.set(traitName, {
410
+ ...traitState,
411
+ currentState: result.newState,
412
+ previousState: result.previousState,
413
+ lastEvent: normalizeEventKey(eventKey),
414
+ context: { ...traitState.context, ...payload }
415
+ });
416
+ results.push({ traitName, result });
417
+ }
418
+ }
419
+ return results;
420
+ }
421
+ /**
422
+ * Reset a trait to its initial state.
423
+ */
424
+ resetTrait(traitName) {
425
+ const trait = this.traits.get(traitName);
426
+ if (trait) {
427
+ this.states.set(traitName, createInitialTraitState(trait));
428
+ }
429
+ }
430
+ /**
431
+ * Reset all traits to initial states.
432
+ */
433
+ resetAll() {
434
+ for (const [traitName, trait] of this.traits) {
435
+ this.states.set(traitName, createInitialTraitState(trait));
436
+ }
437
+ }
438
+ };
439
+
440
+ // src/EffectExecutor.ts
441
+ function parseEffect(effect) {
442
+ if (!Array.isArray(effect) || effect.length === 0) {
443
+ return null;
444
+ }
445
+ const [operator, ...args] = effect;
446
+ if (typeof operator !== "string") {
447
+ return null;
448
+ }
449
+ return { operator, args };
450
+ }
451
+ function resolveArgs(args, bindings) {
452
+ const ctx = createContextFromBindings(bindings);
453
+ return args.map((arg) => interpolateValue(arg, ctx));
454
+ }
455
+ var EffectExecutor = class {
456
+ handlers;
457
+ bindings;
458
+ context;
459
+ debug;
460
+ constructor(options) {
461
+ this.handlers = options.handlers;
462
+ this.bindings = options.bindings;
463
+ this.context = options.context;
464
+ this.debug = options.debug ?? false;
465
+ }
466
+ /**
467
+ * Execute a single effect.
468
+ */
469
+ async execute(effect) {
470
+ const parsed = parseEffect(effect);
471
+ if (!parsed) {
472
+ if (this.debug) {
473
+ console.warn("[EffectExecutor] Invalid effect format:", effect);
474
+ }
475
+ return;
476
+ }
477
+ const { operator, args } = parsed;
478
+ const resolvedArgs = resolveArgs(args, this.bindings);
479
+ if (this.debug) {
480
+ console.log("[EffectExecutor] Executing:", operator, resolvedArgs);
481
+ }
482
+ try {
483
+ await this.dispatch(operator, resolvedArgs);
484
+ } catch (error) {
485
+ console.error("[EffectExecutor] Error executing effect:", operator, error);
486
+ throw error;
487
+ }
488
+ }
489
+ /**
490
+ * Execute multiple effects in sequence.
491
+ */
492
+ async executeAll(effects) {
493
+ for (const effect of effects) {
494
+ await this.execute(effect);
495
+ }
496
+ }
497
+ /**
498
+ * Execute multiple effects in parallel.
499
+ */
500
+ async executeParallel(effects) {
501
+ await Promise.all(effects.map((effect) => this.execute(effect)));
502
+ }
503
+ // ==========================================================================
504
+ // Effect Dispatch
505
+ // ==========================================================================
506
+ async dispatch(operator, args) {
507
+ switch (operator) {
508
+ // === Universal Effects ===
509
+ case "emit": {
510
+ const event = args[0];
511
+ const payload = args[1];
512
+ this.handlers.emit(event, payload);
513
+ break;
514
+ }
515
+ case "set": {
516
+ const [entityId, field, value] = args;
517
+ this.handlers.set(entityId, field, value);
518
+ break;
519
+ }
520
+ case "persist": {
521
+ const action = args[0];
522
+ const entityType = args[1];
523
+ const data = args[2];
524
+ await this.handlers.persist(action, entityType, data);
525
+ break;
526
+ }
527
+ case "call-service": {
528
+ const service = args[0];
529
+ const action = args[1];
530
+ const params = args[2];
531
+ await this.handlers.callService(service, action, params);
532
+ break;
533
+ }
534
+ case "fetch": {
535
+ if (this.handlers.fetch) {
536
+ const entityType = args[0];
537
+ const options = args[1];
538
+ await this.handlers.fetch(entityType, options);
539
+ } else {
540
+ this.logUnsupported("fetch");
541
+ }
542
+ break;
543
+ }
544
+ case "spawn": {
545
+ if (this.handlers.spawn) {
546
+ const entityType = args[0];
547
+ const props = args[1];
548
+ this.handlers.spawn(entityType, props);
549
+ } else {
550
+ this.logUnsupported("spawn");
551
+ }
552
+ break;
553
+ }
554
+ case "despawn": {
555
+ if (this.handlers.despawn) {
556
+ const entityId = args[0];
557
+ this.handlers.despawn(entityId);
558
+ } else {
559
+ this.logUnsupported("despawn");
560
+ }
561
+ break;
562
+ }
563
+ case "log": {
564
+ if (this.handlers.log) {
565
+ const message = args[0];
566
+ const level = args[1];
567
+ const data = args[2];
568
+ this.handlers.log(message, level, data);
569
+ } else {
570
+ console.log(args[0], args.slice(1));
571
+ }
572
+ break;
573
+ }
574
+ // === Client-Only Effects ===
575
+ case "render-ui":
576
+ case "render": {
577
+ if (this.handlers.renderUI) {
578
+ const slot = args[0];
579
+ const pattern = args[1];
580
+ const props = args[2];
581
+ const priority = args[3];
582
+ this.handlers.renderUI(slot, pattern, props, priority);
583
+ } else {
584
+ this.logUnsupported("render-ui");
585
+ }
586
+ break;
587
+ }
588
+ case "navigate": {
589
+ if (this.handlers.navigate) {
590
+ const path = args[0];
591
+ const params = args[1];
592
+ this.handlers.navigate(path, params);
593
+ } else {
594
+ this.logUnsupported("navigate");
595
+ }
596
+ break;
597
+ }
598
+ case "notify": {
599
+ if (this.handlers.notify) {
600
+ const message = args[0];
601
+ const type = args[1] || "info";
602
+ this.handlers.notify(message, type);
603
+ } else {
604
+ console.log(`[Notify:${args[1] || "info"}] ${args[0]}`);
605
+ }
606
+ break;
607
+ }
608
+ // === Compound Effects ===
609
+ case "do": {
610
+ const nestedEffects = args;
611
+ for (const nested of nestedEffects) {
612
+ await this.execute(nested);
613
+ }
614
+ break;
615
+ }
616
+ case "when": {
617
+ const condition = args[0];
618
+ const thenEffect = args[1];
619
+ const elseEffect = args[2];
620
+ if (condition) {
621
+ await this.execute(thenEffect);
622
+ } else if (elseEffect) {
623
+ await this.execute(elseEffect);
624
+ }
625
+ break;
626
+ }
627
+ default: {
628
+ if (this.debug) {
629
+ console.warn("[EffectExecutor] Unknown operator:", operator);
630
+ }
631
+ }
632
+ }
633
+ }
634
+ logUnsupported(operator) {
635
+ if (this.debug) {
636
+ console.warn(
637
+ `[EffectExecutor] Effect "${operator}" not supported on this platform`
638
+ );
639
+ }
640
+ }
641
+ };
642
+ function createTestExecutor(overrides = {}) {
643
+ const noopAsync = async () => {
644
+ };
645
+ const noop = () => {
646
+ };
647
+ return new EffectExecutor({
648
+ handlers: {
649
+ emit: overrides.emit ?? noop,
650
+ persist: overrides.persist ?? noopAsync,
651
+ set: overrides.set ?? noop,
652
+ callService: overrides.callService ?? (async () => ({})),
653
+ ...overrides
654
+ },
655
+ bindings: {},
656
+ context: {
657
+ traitName: "TestTrait",
658
+ state: "test",
659
+ transition: "test->test"
660
+ },
661
+ debug: true
662
+ });
663
+ }
664
+
665
+ // src/loader/schema-loader.ts
666
+ function isElectron() {
667
+ return typeof process !== "undefined" && !!process.versions?.electron;
668
+ }
669
+ function isBrowser() {
670
+ return typeof window !== "undefined" && !isElectron();
671
+ }
672
+ function isNode() {
673
+ return typeof process !== "undefined" && !isBrowser();
674
+ }
675
+ var HttpImportChain = class _HttpImportChain {
676
+ chain = [];
677
+ /**
678
+ * Try to add a path to the chain.
679
+ * @returns Error message if circular, null if OK
680
+ */
681
+ push(absolutePath) {
682
+ if (this.chain.includes(absolutePath)) {
683
+ const cycle = [
684
+ ...this.chain.slice(this.chain.indexOf(absolutePath)),
685
+ absolutePath
686
+ ];
687
+ return `Circular import detected: ${cycle.join(" -> ")}`;
688
+ }
689
+ this.chain.push(absolutePath);
690
+ return null;
691
+ }
692
+ /**
693
+ * Remove the last path from the chain.
694
+ */
695
+ pop() {
696
+ this.chain.pop();
697
+ }
698
+ /**
699
+ * Clone the chain for nested loading.
700
+ */
701
+ clone() {
702
+ const newChain = new _HttpImportChain();
703
+ newChain.chain = [...this.chain];
704
+ return newChain;
705
+ }
706
+ };
707
+ var HttpLoaderCache = class {
708
+ cache = /* @__PURE__ */ new Map();
709
+ get(url) {
710
+ return this.cache.get(url);
711
+ }
712
+ set(url, schema) {
713
+ this.cache.set(url, schema);
714
+ }
715
+ has(url) {
716
+ return this.cache.has(url);
717
+ }
718
+ clear() {
719
+ this.cache.clear();
720
+ }
721
+ get size() {
722
+ return this.cache.size;
723
+ }
724
+ };
725
+ var HttpLoader = class {
726
+ options;
727
+ cache;
728
+ constructor(options) {
729
+ this.options = {
730
+ basePath: options.basePath,
731
+ stdLibPath: options.stdLibPath ?? "",
732
+ scopedPaths: options.scopedPaths ?? {},
733
+ fetchOptions: options.fetchOptions,
734
+ timeout: options.timeout ?? 3e4,
735
+ credentials: options.credentials ?? "same-origin"
736
+ };
737
+ this.cache = new HttpLoaderCache();
738
+ }
739
+ /**
740
+ * Load a schema from an import path.
741
+ */
742
+ async load(importPath, fromPath, chain) {
743
+ const importChain = chain ?? new HttpImportChain();
744
+ const resolveResult = this.resolvePath(importPath, fromPath);
745
+ if (!resolveResult.success) {
746
+ return resolveResult;
747
+ }
748
+ const absoluteUrl = resolveResult.data;
749
+ const circularError = importChain.push(absoluteUrl);
750
+ if (circularError) {
751
+ return { success: false, error: circularError };
752
+ }
753
+ try {
754
+ const cached = this.cache.get(absoluteUrl);
755
+ if (cached) {
756
+ return { success: true, data: cached };
757
+ }
758
+ const loadResult = await this.fetchSchema(absoluteUrl);
759
+ if (!loadResult.success) {
760
+ return loadResult;
761
+ }
762
+ const loaded = {
763
+ schema: loadResult.data,
764
+ sourcePath: absoluteUrl,
765
+ importPath
766
+ };
767
+ this.cache.set(absoluteUrl, loaded);
768
+ return { success: true, data: loaded };
769
+ } finally {
770
+ importChain.pop();
771
+ }
772
+ }
773
+ /**
774
+ * Load a specific orbital from a schema by name.
775
+ */
776
+ async loadOrbital(importPath, orbitalName, fromPath, chain) {
777
+ const schemaResult = await this.load(importPath, fromPath, chain);
778
+ if (!schemaResult.success) {
779
+ return schemaResult;
780
+ }
781
+ const schema = schemaResult.data.schema;
782
+ let orbital;
783
+ if (orbitalName) {
784
+ const found = schema.orbitals.find(
785
+ (o) => o.name === orbitalName
786
+ );
787
+ if (!found) {
788
+ return {
789
+ success: false,
790
+ error: `Orbital "${orbitalName}" not found in ${importPath}. Available: ${schema.orbitals.map((o) => o.name).join(", ")}`
791
+ };
792
+ }
793
+ orbital = found;
794
+ } else {
795
+ if (schema.orbitals.length === 0) {
796
+ return {
797
+ success: false,
798
+ error: `No orbitals found in ${importPath}`
799
+ };
800
+ }
801
+ orbital = schema.orbitals[0];
802
+ }
803
+ return {
804
+ success: true,
805
+ data: {
806
+ orbital,
807
+ sourcePath: schemaResult.data.sourcePath,
808
+ importPath
809
+ }
810
+ };
811
+ }
812
+ /**
813
+ * Resolve an import path to an absolute URL.
814
+ */
815
+ resolvePath(importPath, fromPath) {
816
+ if (importPath.startsWith("http://") || importPath.startsWith("https://")) {
817
+ return { success: true, data: importPath };
818
+ }
819
+ if (importPath.startsWith("std/")) {
820
+ return this.resolveStdPath(importPath);
821
+ }
822
+ if (importPath.startsWith("@")) {
823
+ return this.resolveScopedPath(importPath);
824
+ }
825
+ if (importPath.startsWith("./") || importPath.startsWith("../")) {
826
+ return this.resolveRelativePath(importPath, fromPath);
827
+ }
828
+ return this.resolveRelativePath(`./${importPath}`, fromPath);
829
+ }
830
+ /**
831
+ * Resolve a standard library path.
832
+ */
833
+ resolveStdPath(importPath) {
834
+ if (!this.options.stdLibPath) {
835
+ return {
836
+ success: false,
837
+ error: `Standard library URL not configured. Cannot load: ${importPath}`
838
+ };
839
+ }
840
+ const relativePath = importPath.slice(4);
841
+ let absoluteUrl = this.joinUrl(this.options.stdLibPath, relativePath);
842
+ if (!absoluteUrl.endsWith(".orb")) {
843
+ absoluteUrl += ".orb";
844
+ }
845
+ return { success: true, data: absoluteUrl };
846
+ }
847
+ /**
848
+ * Resolve a scoped package path.
849
+ */
850
+ resolveScopedPath(importPath) {
851
+ const match = importPath.match(/^(@[^/]+)/);
852
+ if (!match) {
853
+ return {
854
+ success: false,
855
+ error: `Invalid scoped package path: ${importPath}`
856
+ };
857
+ }
858
+ const scope = match[1];
859
+ const scopeRoot = this.options.scopedPaths[scope];
860
+ if (!scopeRoot) {
861
+ return {
862
+ success: false,
863
+ error: `Scoped package "${scope}" not configured. Available: ${Object.keys(this.options.scopedPaths).join(", ") || "none"}`
864
+ };
865
+ }
866
+ const relativePath = importPath.slice(scope.length + 1);
867
+ let absoluteUrl = this.joinUrl(scopeRoot, relativePath);
868
+ if (!absoluteUrl.endsWith(".orb")) {
869
+ absoluteUrl += ".orb";
870
+ }
871
+ return { success: true, data: absoluteUrl };
872
+ }
873
+ /**
874
+ * Resolve a relative path.
875
+ */
876
+ resolveRelativePath(importPath, fromPath) {
877
+ const baseUrl = fromPath ? this.getParentUrl(fromPath) : this.options.basePath;
878
+ let absoluteUrl = this.joinUrl(baseUrl, importPath);
879
+ if (!absoluteUrl.endsWith(".orb")) {
880
+ absoluteUrl += ".orb";
881
+ }
882
+ return { success: true, data: absoluteUrl };
883
+ }
884
+ /**
885
+ * Fetch and parse an OrbitalSchema from a URL.
886
+ */
887
+ async fetchSchema(url) {
888
+ try {
889
+ const controller = new AbortController();
890
+ const timeoutId = setTimeout(
891
+ () => controller.abort(),
892
+ this.options.timeout
893
+ );
894
+ try {
895
+ const response = await fetch(url, {
896
+ ...this.options.fetchOptions,
897
+ credentials: this.options.credentials,
898
+ signal: controller.signal,
899
+ headers: {
900
+ Accept: "application/json",
901
+ ...this.options.fetchOptions?.headers
902
+ }
903
+ });
904
+ if (!response.ok) {
905
+ return {
906
+ success: false,
907
+ error: `HTTP ${response.status}: ${response.statusText} for ${url}`
908
+ };
909
+ }
910
+ const text = await response.text();
911
+ let data;
912
+ try {
913
+ data = JSON.parse(text);
914
+ } catch (e) {
915
+ return {
916
+ success: false,
917
+ error: `Invalid JSON in ${url}: ${e instanceof Error ? e.message : String(e)}`
918
+ };
919
+ }
920
+ const parseResult = OrbitalSchemaSchema.safeParse(data);
921
+ if (!parseResult.success) {
922
+ const errors = parseResult.error.errors.map((e) => ` - ${e.path.join(".")}: ${e.message}`).join("\n");
923
+ return {
924
+ success: false,
925
+ error: `Invalid schema in ${url}:
926
+ ${errors}`
927
+ };
928
+ }
929
+ return { success: true, data: parseResult.data };
930
+ } finally {
931
+ clearTimeout(timeoutId);
932
+ }
933
+ } catch (e) {
934
+ if (e instanceof Error && e.name === "AbortError") {
935
+ return {
936
+ success: false,
937
+ error: `Request timeout for ${url} (${this.options.timeout}ms)`
938
+ };
939
+ }
940
+ return {
941
+ success: false,
942
+ error: `Failed to fetch ${url}: ${e instanceof Error ? e.message : String(e)}`
943
+ };
944
+ }
945
+ }
946
+ /**
947
+ * Join URL parts, handling relative paths and trailing slashes.
948
+ */
949
+ joinUrl(base, path) {
950
+ if (path.startsWith("http://") || path.startsWith("https://")) {
951
+ return path;
952
+ }
953
+ if (typeof URL !== "undefined") {
954
+ try {
955
+ const baseUrl = base.endsWith("/") ? base : base + "/";
956
+ return new URL(path, baseUrl).href;
957
+ } catch {
958
+ }
959
+ }
960
+ const normalizedBase = base.endsWith("/") ? base.slice(0, -1) : base;
961
+ const normalizedPath = path.startsWith("/") ? path : "/" + path;
962
+ if (normalizedPath.startsWith("/./")) {
963
+ return normalizedBase + normalizedPath.slice(2);
964
+ }
965
+ if (normalizedPath.startsWith("/../")) {
966
+ const baseParts = normalizedBase.split("/");
967
+ baseParts.pop();
968
+ return baseParts.join("/") + normalizedPath.slice(3);
969
+ }
970
+ return normalizedBase + normalizedPath;
971
+ }
972
+ /**
973
+ * Get parent URL (directory) from a URL.
974
+ */
975
+ getParentUrl(url) {
976
+ if (typeof URL !== "undefined") {
977
+ try {
978
+ const urlObj = new URL(url);
979
+ const pathParts = urlObj.pathname.split("/");
980
+ pathParts.pop();
981
+ urlObj.pathname = pathParts.join("/");
982
+ return urlObj.href;
983
+ } catch {
984
+ }
985
+ }
986
+ const lastSlash = url.lastIndexOf("/");
987
+ if (lastSlash !== -1) {
988
+ return url.slice(0, lastSlash);
989
+ }
990
+ return url;
991
+ }
992
+ /**
993
+ * Clear the cache.
994
+ */
995
+ clearCache() {
996
+ this.cache.clear();
997
+ }
998
+ /**
999
+ * Get cache statistics.
1000
+ */
1001
+ getCacheStats() {
1002
+ return { size: this.cache.size };
1003
+ }
1004
+ };
1005
+
1006
+ // src/loader/unified-loader.ts
1007
+ var externalLoaderModule = null;
1008
+ async function getExternalLoaderModule() {
1009
+ if (externalLoaderModule) {
1010
+ return externalLoaderModule;
1011
+ }
1012
+ if (isBrowser()) {
1013
+ return null;
1014
+ }
1015
+ try {
1016
+ externalLoaderModule = await import('./external-loader-FJVQACFN.js');
1017
+ return externalLoaderModule;
1018
+ } catch {
1019
+ return null;
1020
+ }
1021
+ }
1022
+ var UnifiedImportChain = class _UnifiedImportChain {
1023
+ chain = [];
1024
+ push(path) {
1025
+ const normalized = this.normalizePath(path);
1026
+ if (this.chain.includes(normalized)) {
1027
+ const cycle = [
1028
+ ...this.chain.slice(this.chain.indexOf(normalized)),
1029
+ normalized
1030
+ ];
1031
+ return `Circular import detected: ${cycle.join(" -> ")}`;
1032
+ }
1033
+ this.chain.push(normalized);
1034
+ return null;
1035
+ }
1036
+ pop() {
1037
+ this.chain.pop();
1038
+ }
1039
+ clone() {
1040
+ const newChain = new _UnifiedImportChain();
1041
+ newChain.chain = [...this.chain];
1042
+ return newChain;
1043
+ }
1044
+ normalizePath(path) {
1045
+ if (path.startsWith("http://") || path.startsWith("https://")) {
1046
+ return path;
1047
+ }
1048
+ return path.replace(/\\/g, "/");
1049
+ }
1050
+ };
1051
+ var UnifiedLoader = class {
1052
+ options;
1053
+ httpLoader = null;
1054
+ fsLoader = null;
1055
+ fsLoaderInitialized = false;
1056
+ cache = /* @__PURE__ */ new Map();
1057
+ constructor(options) {
1058
+ this.options = options;
1059
+ this.httpLoader = new HttpLoader({
1060
+ basePath: options.basePath,
1061
+ stdLibPath: options.stdLibPath,
1062
+ scopedPaths: options.scopedPaths,
1063
+ ...options.http
1064
+ });
1065
+ }
1066
+ /**
1067
+ * Initialize the filesystem loader if available.
1068
+ */
1069
+ async initFsLoader() {
1070
+ if (this.fsLoaderInitialized) {
1071
+ return;
1072
+ }
1073
+ this.fsLoaderInitialized = true;
1074
+ if (this.options.forceLoader === "http") {
1075
+ return;
1076
+ }
1077
+ const module = await getExternalLoaderModule();
1078
+ if (module) {
1079
+ this.fsLoader = new module.ExternalOrbitalLoader({
1080
+ basePath: this.options.basePath,
1081
+ stdLibPath: this.options.stdLibPath,
1082
+ scopedPaths: this.options.scopedPaths,
1083
+ ...this.options.fileSystem
1084
+ });
1085
+ }
1086
+ }
1087
+ /**
1088
+ * Determine which loader to use for an import path.
1089
+ */
1090
+ getLoaderForPath(importPath) {
1091
+ if (this.options.forceLoader) {
1092
+ return this.options.forceLoader;
1093
+ }
1094
+ if (importPath.startsWith("http://") || importPath.startsWith("https://")) {
1095
+ return "http";
1096
+ }
1097
+ if (importPath.startsWith("std/") && this.options.stdLibPath) {
1098
+ if (this.options.stdLibPath.startsWith("http://") || this.options.stdLibPath.startsWith("https://")) {
1099
+ return "http";
1100
+ }
1101
+ }
1102
+ if (importPath.startsWith("@") && this.options.scopedPaths) {
1103
+ const match = importPath.match(/^(@[^/]+)/);
1104
+ if (match) {
1105
+ const scopePath = this.options.scopedPaths[match[1]];
1106
+ if (scopePath && (scopePath.startsWith("http://") || scopePath.startsWith("https://"))) {
1107
+ return "http";
1108
+ }
1109
+ }
1110
+ }
1111
+ if (isBrowser()) {
1112
+ return "http";
1113
+ }
1114
+ return "filesystem";
1115
+ }
1116
+ /**
1117
+ * Load a schema from an import path.
1118
+ *
1119
+ * Note: We delegate chain management to the inner loader (HttpLoader or FsLoader).
1120
+ * The inner loader handles circular import detection, so we don't push/pop here.
1121
+ * We only use the unified cache to avoid duplicate loads across loaders.
1122
+ */
1123
+ async load(importPath, fromPath, chain) {
1124
+ await this.initFsLoader();
1125
+ const importChain = chain ?? new UnifiedImportChain();
1126
+ const loaderType = this.getLoaderForPath(importPath);
1127
+ const resolveResult = this.resolvePath(importPath, fromPath);
1128
+ if (!resolveResult.success) {
1129
+ return resolveResult;
1130
+ }
1131
+ const absolutePath = resolveResult.data;
1132
+ const cached = this.cache.get(absolutePath);
1133
+ if (cached) {
1134
+ return { success: true, data: cached };
1135
+ }
1136
+ let result;
1137
+ if (loaderType === "http") {
1138
+ if (!this.httpLoader) {
1139
+ return {
1140
+ success: false,
1141
+ error: "HTTP loader not available"
1142
+ };
1143
+ }
1144
+ result = await this.httpLoader.load(importPath, fromPath, importChain);
1145
+ } else {
1146
+ if (!this.fsLoader) {
1147
+ if (this.httpLoader) {
1148
+ result = await this.httpLoader.load(
1149
+ importPath,
1150
+ fromPath,
1151
+ importChain
1152
+ );
1153
+ } else {
1154
+ return {
1155
+ success: false,
1156
+ error: `Filesystem loader not available and import "${importPath}" cannot be loaded via HTTP. This typically happens when loading local files in a browser environment.`
1157
+ };
1158
+ }
1159
+ } else {
1160
+ result = await this.fsLoader.load(importPath, fromPath, importChain);
1161
+ }
1162
+ }
1163
+ if (result.success) {
1164
+ this.cache.set(absolutePath, result.data);
1165
+ }
1166
+ return result;
1167
+ }
1168
+ /**
1169
+ * Load a specific orbital from a schema by name.
1170
+ */
1171
+ async loadOrbital(importPath, orbitalName, fromPath, chain) {
1172
+ const schemaResult = await this.load(importPath, fromPath, chain);
1173
+ if (!schemaResult.success) {
1174
+ return schemaResult;
1175
+ }
1176
+ const schema = schemaResult.data.schema;
1177
+ if (orbitalName) {
1178
+ const found = schema.orbitals.find((o) => o.name === orbitalName);
1179
+ if (!found) {
1180
+ return {
1181
+ success: false,
1182
+ error: `Orbital "${orbitalName}" not found in ${importPath}. Available: ${schema.orbitals.map((o) => o.name).join(", ")}`
1183
+ };
1184
+ }
1185
+ return {
1186
+ success: true,
1187
+ data: {
1188
+ orbital: found,
1189
+ sourcePath: schemaResult.data.sourcePath,
1190
+ importPath
1191
+ }
1192
+ };
1193
+ }
1194
+ if (schema.orbitals.length === 0) {
1195
+ return {
1196
+ success: false,
1197
+ error: `No orbitals found in ${importPath}`
1198
+ };
1199
+ }
1200
+ return {
1201
+ success: true,
1202
+ data: {
1203
+ orbital: schema.orbitals[0],
1204
+ sourcePath: schemaResult.data.sourcePath,
1205
+ importPath
1206
+ }
1207
+ };
1208
+ }
1209
+ /**
1210
+ * Resolve an import path to an absolute path/URL.
1211
+ */
1212
+ resolvePath(importPath, fromPath) {
1213
+ const loaderType = this.getLoaderForPath(importPath);
1214
+ if (loaderType === "http" && this.httpLoader) {
1215
+ return this.httpLoader.resolvePath(importPath, fromPath);
1216
+ }
1217
+ if (this.fsLoader) {
1218
+ return this.fsLoader.resolvePath(importPath, fromPath);
1219
+ }
1220
+ if (this.httpLoader) {
1221
+ return this.httpLoader.resolvePath(importPath, fromPath);
1222
+ }
1223
+ return {
1224
+ success: false,
1225
+ error: "No loader available for path resolution"
1226
+ };
1227
+ }
1228
+ /**
1229
+ * Clear all caches.
1230
+ */
1231
+ clearCache() {
1232
+ this.cache.clear();
1233
+ this.httpLoader?.clearCache();
1234
+ this.fsLoader?.clearCache();
1235
+ }
1236
+ /**
1237
+ * Get combined cache statistics.
1238
+ */
1239
+ getCacheStats() {
1240
+ let size = this.cache.size;
1241
+ if (this.httpLoader) {
1242
+ size += this.httpLoader.getCacheStats().size;
1243
+ }
1244
+ if (this.fsLoader) {
1245
+ size += this.fsLoader.getCacheStats().size;
1246
+ }
1247
+ return { size };
1248
+ }
1249
+ /**
1250
+ * Check if filesystem loading is available.
1251
+ */
1252
+ async hasFilesystemAccess() {
1253
+ await this.initFsLoader();
1254
+ return this.fsLoader !== null;
1255
+ }
1256
+ /**
1257
+ * Get current environment info.
1258
+ */
1259
+ getEnvironmentInfo() {
1260
+ return {
1261
+ isElectron: isElectron(),
1262
+ isBrowser: isBrowser(),
1263
+ isNode: isNode(),
1264
+ hasFilesystem: this.fsLoader !== null
1265
+ };
1266
+ }
1267
+ };
1268
+ function createUnifiedLoader(options) {
1269
+ return new UnifiedLoader(options);
1270
+ }
1271
+ var ReferenceResolver = class {
1272
+ loader;
1273
+ options;
1274
+ localTraits;
1275
+ loaderInitialized = false;
1276
+ constructor(options) {
1277
+ this.options = options;
1278
+ this.loader = options.loader;
1279
+ this.localTraits = options.localTraits ?? /* @__PURE__ */ new Map();
1280
+ }
1281
+ async ensureLoader() {
1282
+ if (this.loader || this.loaderInitialized) return;
1283
+ this.loaderInitialized = true;
1284
+ try {
1285
+ const { ExternalOrbitalLoader } = await import('./external-loader-FJVQACFN.js');
1286
+ this.loader = new ExternalOrbitalLoader(this.options);
1287
+ } catch {
1288
+ }
1289
+ }
1290
+ /**
1291
+ * Resolve all references in an orbital.
1292
+ */
1293
+ async resolve(orbital, sourcePath, chain) {
1294
+ const errors = [];
1295
+ const warnings = [];
1296
+ const importChain = chain ?? { push: () => null, pop: () => {
1297
+ }, clone() {
1298
+ return this;
1299
+ } };
1300
+ const importsResult = await this.resolveImports(
1301
+ orbital.uses ?? [],
1302
+ sourcePath,
1303
+ importChain
1304
+ );
1305
+ if (!importsResult.success) {
1306
+ return { success: false, errors: importsResult.errors };
1307
+ }
1308
+ const imports = importsResult.data;
1309
+ const entityResult = this.resolveEntity(orbital.entity, imports);
1310
+ if (!entityResult.success) {
1311
+ errors.push(...entityResult.errors);
1312
+ }
1313
+ const traitsResult = this.resolveTraits(orbital.traits, imports);
1314
+ if (!traitsResult.success) {
1315
+ errors.push(...traitsResult.errors);
1316
+ }
1317
+ const pagesResult = this.resolvePages(orbital.pages, imports);
1318
+ if (!pagesResult.success) {
1319
+ errors.push(...pagesResult.errors);
1320
+ }
1321
+ if (errors.length > 0) {
1322
+ return { success: false, errors };
1323
+ }
1324
+ if (!entityResult.success || !traitsResult.success || !pagesResult.success) {
1325
+ return { success: false, errors: ["Internal error: unexpected failure state"] };
1326
+ }
1327
+ return {
1328
+ success: true,
1329
+ data: {
1330
+ name: orbital.name,
1331
+ entity: entityResult.data.entity,
1332
+ entitySource: entityResult.data.source,
1333
+ traits: traitsResult.data,
1334
+ pages: pagesResult.data,
1335
+ imports,
1336
+ original: orbital
1337
+ },
1338
+ warnings
1339
+ };
1340
+ }
1341
+ /**
1342
+ * Resolve `uses` declarations to loaded orbitals.
1343
+ */
1344
+ async resolveImports(uses, sourcePath, chain) {
1345
+ const errors = [];
1346
+ const orbitals = /* @__PURE__ */ new Map();
1347
+ if (this.options.skipExternalLoading) {
1348
+ return {
1349
+ success: true,
1350
+ data: { orbitals },
1351
+ warnings: ["External loading skipped"]
1352
+ };
1353
+ }
1354
+ for (const use of uses) {
1355
+ if (orbitals.has(use.as)) {
1356
+ errors.push(`Duplicate import alias: ${use.as}`);
1357
+ continue;
1358
+ }
1359
+ await this.ensureLoader();
1360
+ if (!this.loader) {
1361
+ errors.push(`No loader available to resolve import: ${use.from}`);
1362
+ continue;
1363
+ }
1364
+ const loadResult = await this.loader.loadOrbital(
1365
+ use.from,
1366
+ void 0,
1367
+ sourcePath,
1368
+ chain
1369
+ );
1370
+ if (!loadResult.success) {
1371
+ errors.push(`Failed to load "${use.from}" as "${use.as}": ${loadResult.error}`);
1372
+ continue;
1373
+ }
1374
+ orbitals.set(use.as, {
1375
+ alias: use.as,
1376
+ from: use.from,
1377
+ orbital: loadResult.data.orbital,
1378
+ sourcePath: loadResult.data.sourcePath
1379
+ });
1380
+ }
1381
+ if (errors.length > 0) {
1382
+ return { success: false, errors };
1383
+ }
1384
+ return { success: true, data: { orbitals }, warnings: [] };
1385
+ }
1386
+ /**
1387
+ * Resolve entity reference.
1388
+ */
1389
+ resolveEntity(entityRef, imports) {
1390
+ if (!isEntityReference(entityRef)) {
1391
+ return {
1392
+ success: true,
1393
+ data: { entity: entityRef },
1394
+ warnings: []
1395
+ };
1396
+ }
1397
+ const parsed = parseEntityRef(entityRef);
1398
+ if (!parsed) {
1399
+ return {
1400
+ success: false,
1401
+ errors: [`Invalid entity reference format: ${entityRef}. Expected "Alias.entity"`]
1402
+ };
1403
+ }
1404
+ const imported = imports.orbitals.get(parsed.alias);
1405
+ if (!imported) {
1406
+ return {
1407
+ success: false,
1408
+ errors: [
1409
+ `Unknown import alias in entity reference: ${parsed.alias}. Available aliases: ${Array.from(imports.orbitals.keys()).join(", ") || "none"}`
1410
+ ]
1411
+ };
1412
+ }
1413
+ const importedEntity = this.getEntityFromOrbital(imported.orbital);
1414
+ if (!importedEntity) {
1415
+ return {
1416
+ success: false,
1417
+ errors: [
1418
+ `Imported orbital "${parsed.alias}" does not have an inline entity. Entity references cannot be chained.`
1419
+ ]
1420
+ };
1421
+ }
1422
+ const persistence = importedEntity.persistence ?? "persistent";
1423
+ return {
1424
+ success: true,
1425
+ data: {
1426
+ entity: importedEntity,
1427
+ source: {
1428
+ alias: parsed.alias,
1429
+ persistence
1430
+ }
1431
+ },
1432
+ warnings: []
1433
+ };
1434
+ }
1435
+ /**
1436
+ * Get the entity from an orbital (handling EntityRef).
1437
+ */
1438
+ getEntityFromOrbital(orbital) {
1439
+ const entityRef = orbital.entity;
1440
+ if (typeof entityRef === "string") {
1441
+ return null;
1442
+ }
1443
+ return entityRef;
1444
+ }
1445
+ /**
1446
+ * Resolve trait references.
1447
+ */
1448
+ resolveTraits(traitRefs, imports) {
1449
+ const errors = [];
1450
+ const resolved = [];
1451
+ for (const traitRef of traitRefs) {
1452
+ const result = this.resolveTraitRef(traitRef, imports);
1453
+ if (!result.success) {
1454
+ errors.push(...result.errors);
1455
+ } else {
1456
+ resolved.push(result.data);
1457
+ }
1458
+ }
1459
+ if (errors.length > 0) {
1460
+ return { success: false, errors };
1461
+ }
1462
+ return { success: true, data: resolved, warnings: [] };
1463
+ }
1464
+ /**
1465
+ * Resolve a single trait reference.
1466
+ */
1467
+ resolveTraitRef(traitRef, imports) {
1468
+ if (typeof traitRef !== "string" && "stateMachine" in traitRef) {
1469
+ return {
1470
+ success: true,
1471
+ data: {
1472
+ trait: traitRef,
1473
+ source: { type: "inline" }
1474
+ },
1475
+ warnings: []
1476
+ };
1477
+ }
1478
+ if (typeof traitRef !== "string" && "ref" in traitRef) {
1479
+ const refObj = traitRef;
1480
+ return this.resolveTraitRefString(refObj.ref, imports, refObj.config, refObj.linkedEntity);
1481
+ }
1482
+ if (typeof traitRef === "string") {
1483
+ return this.resolveTraitRefString(traitRef, imports);
1484
+ }
1485
+ return {
1486
+ success: false,
1487
+ errors: [`Unknown trait reference format: ${JSON.stringify(traitRef)}`]
1488
+ };
1489
+ }
1490
+ /**
1491
+ * Resolve a trait reference string.
1492
+ */
1493
+ resolveTraitRefString(ref, imports, config, linkedEntity) {
1494
+ const parsed = parseImportedTraitRef(ref);
1495
+ if (parsed) {
1496
+ const imported = imports.orbitals.get(parsed.alias);
1497
+ if (!imported) {
1498
+ return {
1499
+ success: false,
1500
+ errors: [
1501
+ `Unknown import alias in trait reference: ${parsed.alias}. Available aliases: ${Array.from(imports.orbitals.keys()).join(", ") || "none"}`
1502
+ ]
1503
+ };
1504
+ }
1505
+ const trait = this.findTraitInOrbital(imported.orbital, parsed.traitName);
1506
+ if (!trait) {
1507
+ return {
1508
+ success: false,
1509
+ errors: [
1510
+ `Trait "${parsed.traitName}" not found in imported orbital "${parsed.alias}". Available traits: ${this.listTraitsInOrbital(imported.orbital).join(", ") || "none"}`
1511
+ ]
1512
+ };
1513
+ }
1514
+ return {
1515
+ success: true,
1516
+ data: {
1517
+ trait,
1518
+ source: { type: "imported", alias: parsed.alias, traitName: parsed.traitName },
1519
+ config,
1520
+ linkedEntity
1521
+ },
1522
+ warnings: []
1523
+ };
1524
+ }
1525
+ const localTrait = this.localTraits.get(ref);
1526
+ if (localTrait) {
1527
+ return {
1528
+ success: true,
1529
+ data: {
1530
+ trait: localTrait,
1531
+ source: { type: "local", name: ref },
1532
+ config,
1533
+ linkedEntity
1534
+ },
1535
+ warnings: []
1536
+ };
1537
+ }
1538
+ return {
1539
+ success: false,
1540
+ errors: [
1541
+ `Trait "${ref}" not found. For imported traits, use format "Alias.traits.TraitName". Local traits available: ${Array.from(this.localTraits.keys()).join(", ") || "none"}`
1542
+ ]
1543
+ };
1544
+ }
1545
+ /**
1546
+ * Find a trait in an orbital by name.
1547
+ */
1548
+ findTraitInOrbital(orbital, traitName) {
1549
+ for (const traitRef of orbital.traits) {
1550
+ if (typeof traitRef !== "string" && "stateMachine" in traitRef) {
1551
+ if (traitRef.name === traitName) {
1552
+ return traitRef;
1553
+ }
1554
+ }
1555
+ if (typeof traitRef !== "string" && "ref" in traitRef) {
1556
+ const refObj = traitRef;
1557
+ if (refObj.ref === traitName || refObj.name === traitName) ;
1558
+ }
1559
+ }
1560
+ return null;
1561
+ }
1562
+ /**
1563
+ * List trait names in an orbital.
1564
+ */
1565
+ listTraitsInOrbital(orbital) {
1566
+ const names = [];
1567
+ for (const traitRef of orbital.traits) {
1568
+ if (typeof traitRef !== "string" && "stateMachine" in traitRef) {
1569
+ names.push(traitRef.name);
1570
+ }
1571
+ }
1572
+ return names;
1573
+ }
1574
+ /**
1575
+ * Resolve page references.
1576
+ */
1577
+ resolvePages(pageRefs, imports) {
1578
+ const errors = [];
1579
+ const resolved = [];
1580
+ for (const pageRef of pageRefs) {
1581
+ const result = this.resolvePageRef(pageRef, imports);
1582
+ if (!result.success) {
1583
+ errors.push(...result.errors);
1584
+ } else {
1585
+ resolved.push(result.data);
1586
+ }
1587
+ }
1588
+ if (errors.length > 0) {
1589
+ return { success: false, errors };
1590
+ }
1591
+ return { success: true, data: resolved, warnings: [] };
1592
+ }
1593
+ /**
1594
+ * Resolve a single page reference.
1595
+ */
1596
+ resolvePageRef(pageRef, imports) {
1597
+ if (!isPageReference(pageRef)) {
1598
+ return {
1599
+ success: true,
1600
+ data: {
1601
+ page: pageRef,
1602
+ source: { type: "inline" },
1603
+ pathOverridden: false
1604
+ },
1605
+ warnings: []
1606
+ };
1607
+ }
1608
+ if (isPageReferenceString(pageRef)) {
1609
+ return this.resolvePageRefString(pageRef, imports);
1610
+ }
1611
+ if (isPageReferenceObject(pageRef)) {
1612
+ return this.resolvePageRefObject(pageRef, imports);
1613
+ }
1614
+ return {
1615
+ success: false,
1616
+ errors: [`Unknown page reference format: ${JSON.stringify(pageRef)}`]
1617
+ };
1618
+ }
1619
+ /**
1620
+ * Resolve a page reference string.
1621
+ */
1622
+ resolvePageRefString(ref, imports) {
1623
+ const parsed = parsePageRef(ref);
1624
+ if (!parsed) {
1625
+ return {
1626
+ success: false,
1627
+ errors: [`Invalid page reference format: ${ref}. Expected "Alias.pages.PageName"`]
1628
+ };
1629
+ }
1630
+ const imported = imports.orbitals.get(parsed.alias);
1631
+ if (!imported) {
1632
+ return {
1633
+ success: false,
1634
+ errors: [
1635
+ `Unknown import alias in page reference: ${parsed.alias}. Available aliases: ${Array.from(imports.orbitals.keys()).join(", ") || "none"}`
1636
+ ]
1637
+ };
1638
+ }
1639
+ const page = this.findPageInOrbital(imported.orbital, parsed.pageName);
1640
+ if (!page) {
1641
+ return {
1642
+ success: false,
1643
+ errors: [
1644
+ `Page "${parsed.pageName}" not found in imported orbital "${parsed.alias}". Available pages: ${this.listPagesInOrbital(imported.orbital).join(", ") || "none"}`
1645
+ ]
1646
+ };
1647
+ }
1648
+ return {
1649
+ success: true,
1650
+ data: {
1651
+ page,
1652
+ source: { type: "imported", alias: parsed.alias, pageName: parsed.pageName },
1653
+ pathOverridden: false
1654
+ },
1655
+ warnings: []
1656
+ };
1657
+ }
1658
+ /**
1659
+ * Resolve a page reference object with optional path override.
1660
+ */
1661
+ resolvePageRefObject(refObj, imports) {
1662
+ const baseResult = this.resolvePageRefString(refObj.ref, imports);
1663
+ if (!baseResult.success) {
1664
+ return baseResult;
1665
+ }
1666
+ const resolved = baseResult.data;
1667
+ if (refObj.path) {
1668
+ const originalPath = resolved.page.path;
1669
+ resolved.page = {
1670
+ ...resolved.page,
1671
+ path: refObj.path
1672
+ };
1673
+ resolved.pathOverridden = true;
1674
+ resolved.originalPath = originalPath;
1675
+ }
1676
+ return {
1677
+ success: true,
1678
+ data: resolved,
1679
+ warnings: baseResult.warnings
1680
+ };
1681
+ }
1682
+ /**
1683
+ * Find a page in an orbital by name.
1684
+ */
1685
+ findPageInOrbital(orbital, pageName) {
1686
+ const pages = orbital.pages;
1687
+ if (!pages) return null;
1688
+ for (const pageRef of pages) {
1689
+ if (typeof pageRef !== "string" && !("ref" in pageRef)) {
1690
+ const page = pageRef;
1691
+ if (page.name === pageName) {
1692
+ return { ...page };
1693
+ }
1694
+ }
1695
+ }
1696
+ return null;
1697
+ }
1698
+ /**
1699
+ * List page names in an orbital.
1700
+ */
1701
+ listPagesInOrbital(orbital) {
1702
+ const pages = orbital.pages;
1703
+ if (!pages) return [];
1704
+ const names = [];
1705
+ for (const pageRef of pages) {
1706
+ if (typeof pageRef !== "string" && !("ref" in pageRef)) {
1707
+ names.push(pageRef.name);
1708
+ }
1709
+ }
1710
+ return names;
1711
+ }
1712
+ /**
1713
+ * Add local traits for resolution.
1714
+ */
1715
+ addLocalTraits(traits) {
1716
+ for (const trait of traits) {
1717
+ this.localTraits.set(trait.name, trait);
1718
+ }
1719
+ }
1720
+ /**
1721
+ * Clear loader cache.
1722
+ */
1723
+ clearCache() {
1724
+ this.loader?.clearCache();
1725
+ }
1726
+ };
1727
+ async function resolveSchema(schema, options) {
1728
+ const resolver = new ReferenceResolver(options);
1729
+ const errors = [];
1730
+ const warnings = [];
1731
+ const resolved = [];
1732
+ for (const orbital of schema.orbitals) {
1733
+ const inlineTraits = orbital.traits.filter(
1734
+ (t) => typeof t !== "string" && "stateMachine" in t
1735
+ );
1736
+ resolver.addLocalTraits(inlineTraits);
1737
+ }
1738
+ for (const orbital of schema.orbitals) {
1739
+ const result = await resolver.resolve(orbital);
1740
+ if (!result.success) {
1741
+ errors.push(`Orbital "${orbital.name}": ${result.errors.join(", ")}`);
1742
+ } else {
1743
+ resolved.push(result.data);
1744
+ warnings.push(...result.warnings.map((w) => `Orbital "${orbital.name}": ${w}`));
1745
+ }
1746
+ }
1747
+ if (errors.length > 0) {
1748
+ return { success: false, errors };
1749
+ }
1750
+ return { success: true, data: resolved, warnings };
1751
+ }
1752
+
1753
+ // src/UsesIntegration.ts
1754
+ async function preprocessSchema(schema, options) {
1755
+ const namespaceEvents = options.namespaceEvents ?? true;
1756
+ const resolveResult = await resolveSchema(schema, options);
1757
+ if (!resolveResult.success) {
1758
+ return { success: false, errors: resolveResult.errors };
1759
+ }
1760
+ const resolved = resolveResult.data;
1761
+ const warnings = resolveResult.warnings;
1762
+ const preprocessedOrbitals = [];
1763
+ const entitySharing = {};
1764
+ const eventNamespaces = {};
1765
+ for (const resolvedOrbital of resolved) {
1766
+ const orbitalName = resolvedOrbital.name;
1767
+ const persistence = resolvedOrbital.entitySource?.persistence ?? resolvedOrbital.entity.persistence ?? "persistent";
1768
+ entitySharing[orbitalName] = {
1769
+ entityName: resolvedOrbital.entity.name,
1770
+ persistence,
1771
+ isShared: persistence !== "runtime",
1772
+ sourceAlias: resolvedOrbital.entitySource?.alias,
1773
+ collectionName: resolvedOrbital.entity.collection
1774
+ };
1775
+ eventNamespaces[orbitalName] = {};
1776
+ for (const resolvedTrait of resolvedOrbital.traits) {
1777
+ const traitName = resolvedTrait.trait.name;
1778
+ const namespace = {
1779
+ emits: {},
1780
+ listens: {}
1781
+ };
1782
+ if (namespaceEvents && resolvedTrait.source.type === "imported") {
1783
+ const emits = resolvedTrait.trait.emits ?? [];
1784
+ for (const emit of emits) {
1785
+ const eventName = typeof emit === "string" ? emit : emit.event;
1786
+ namespace.emits[eventName] = `${orbitalName}.${traitName}.${eventName}`;
1787
+ }
1788
+ const listens = resolvedTrait.trait.listens ?? [];
1789
+ for (const listen of listens) {
1790
+ namespace.listens[listen.event] = listen.event;
1791
+ }
1792
+ }
1793
+ eventNamespaces[orbitalName][traitName] = namespace;
1794
+ }
1795
+ const preprocessedOrbital = {
1796
+ name: orbitalName,
1797
+ description: resolvedOrbital.original.description,
1798
+ visual_prompt: resolvedOrbital.original.visual_prompt,
1799
+ // Resolved entity (always inline now)
1800
+ entity: resolvedOrbital.entity,
1801
+ // Resolved traits (inline definitions)
1802
+ traits: resolvedOrbital.traits.map((rt) => {
1803
+ if (rt.config || rt.linkedEntity) {
1804
+ return {
1805
+ ref: rt.trait.name,
1806
+ config: rt.config,
1807
+ linkedEntity: rt.linkedEntity,
1808
+ // Include the resolved trait definition for runtime
1809
+ _resolved: rt.trait
1810
+ };
1811
+ }
1812
+ return rt.trait;
1813
+ }),
1814
+ // Resolved pages (inline definitions with path overrides applied)
1815
+ pages: resolvedOrbital.pages.map((rp) => rp.page),
1816
+ // Preserve other fields
1817
+ exposes: resolvedOrbital.original.exposes,
1818
+ domainContext: resolvedOrbital.original.domainContext,
1819
+ design: resolvedOrbital.original.design
1820
+ };
1821
+ preprocessedOrbitals.push(preprocessedOrbital);
1822
+ }
1823
+ const preprocessedSchema = {
1824
+ ...schema,
1825
+ orbitals: preprocessedOrbitals
1826
+ };
1827
+ return {
1828
+ success: true,
1829
+ data: {
1830
+ schema: preprocessedSchema,
1831
+ entitySharing,
1832
+ eventNamespaces,
1833
+ warnings
1834
+ }
1835
+ };
1836
+ }
1837
+ function getIsolatedCollectionName(orbitalName, entitySharing) {
1838
+ const info = entitySharing[orbitalName];
1839
+ if (!info) {
1840
+ throw new Error(`Unknown orbital: ${orbitalName}`);
1841
+ }
1842
+ if (info.persistence === "runtime") {
1843
+ return `${orbitalName}_${info.entityName}`;
1844
+ }
1845
+ return info.collectionName || info.entityName.toLowerCase() + "s";
1846
+ }
1847
+ function getNamespacedEvent(orbitalName, traitName, eventName, eventNamespaces) {
1848
+ const orbitalNs = eventNamespaces[orbitalName];
1849
+ if (!orbitalNs) return eventName;
1850
+ const traitNs = orbitalNs[traitName];
1851
+ if (!traitNs) return eventName;
1852
+ return traitNs.emits[eventName] || eventName;
1853
+ }
1854
+ function isNamespacedEvent(eventName) {
1855
+ return eventName.includes(".");
1856
+ }
1857
+ function parseNamespacedEvent(eventName) {
1858
+ const parts = eventName.split(".");
1859
+ if (parts.length === 3) {
1860
+ return { orbital: parts[0], trait: parts[1], event: parts[2] };
1861
+ }
1862
+ if (parts.length === 2) {
1863
+ return { trait: parts[0], event: parts[1] };
1864
+ }
1865
+ return { event: eventName };
1866
+ }
1867
+
1868
+ export { EffectExecutor, EventBus, StateMachineManager, containsBindings, createContextFromBindings, createInitialTraitState, createTestExecutor, createUnifiedLoader, extractBindings, findInitialState, findTransition, getIsolatedCollectionName, getNamespacedEvent, interpolateProps, interpolateValue, isNamespacedEvent, normalizeEventKey, parseNamespacedEvent, preprocessSchema, processEvent };
1869
+ //# sourceMappingURL=chunk-VW6RWQA5.js.map
1870
+ //# sourceMappingURL=chunk-VW6RWQA5.js.map