@almadar/ui 4.15.4 → 4.15.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4348,6 +4348,13 @@ function useUISlotManager() {
4348
4348
  indexTraitRender(content.sourceTrait, content);
4349
4349
  notifyTraitSubscribers(content.sourceTrait, content);
4350
4350
  }
4351
+ slotLog.info("slot:written", {
4352
+ slot: config.target,
4353
+ sourceKey,
4354
+ sourceTrait: content.sourceTrait,
4355
+ patternType: content.pattern,
4356
+ priority: content.priority
4357
+ });
4351
4358
  notifySubscribers(config.target, aggregateSlot(nextSources));
4352
4359
  return nextAll;
4353
4360
  });
@@ -4385,7 +4392,10 @@ function useUISlotManager() {
4385
4392
  const sourceKey = sourceTrait;
4386
4393
  setSources((prev) => {
4387
4394
  const slotSources = prev[slot];
4388
- if (!slotSources || !(sourceKey in slotSources)) return prev;
4395
+ if (!slotSources || !(sourceKey in slotSources)) {
4396
+ slotLog.debug("slot:clear-noop", { slot, sourceTrait, reason: !slotSources ? "no-slot" : "no-source" });
4397
+ return prev;
4398
+ }
4389
4399
  const content = slotSources[sourceKey];
4390
4400
  const timer = timersRef.current.get(content.id);
4391
4401
  if (timer) {
@@ -4399,6 +4409,7 @@ function useUISlotManager() {
4399
4409
  }
4400
4410
  const nextSources = { ...slotSources };
4401
4411
  delete nextSources[sourceKey];
4412
+ slotLog.info("slot:cleared", { slot, sourceTrait, lastPatternType: content.pattern });
4402
4413
  notifySubscribers(slot, aggregateSlot(nextSources));
4403
4414
  return { ...prev, [slot]: nextSources };
4404
4415
  });
@@ -4517,10 +4528,12 @@ function useUISlotManager() {
4517
4528
  updateTraitContent
4518
4529
  };
4519
4530
  }
4520
- var DEFAULT_SOURCE_KEY, MULTI_SOURCE_STACK_TRAIT, ALL_SLOTS, DEFAULT_SLOTS, DEFAULT_SOURCES, idCounter;
4531
+ var slotLog, DEFAULT_SOURCE_KEY, MULTI_SOURCE_STACK_TRAIT, ALL_SLOTS, DEFAULT_SLOTS, DEFAULT_SOURCES, idCounter;
4521
4532
  var init_useUISlots = __esm({
4522
4533
  "hooks/useUISlots.ts"() {
4523
4534
  "use client";
4535
+ init_logger();
4536
+ slotLog = createLogger("almadar:ui:useUISlots");
4524
4537
  DEFAULT_SOURCE_KEY = "__default__";
4525
4538
  MULTI_SOURCE_STACK_TRAIT = "__multi_source_stack__";
4526
4539
  ALL_SLOTS = [
@@ -5426,11 +5439,11 @@ function refId(obj) {
5426
5439
  refIds.set(obj, id);
5427
5440
  return id;
5428
5441
  }
5429
- var slotLog, refIds, nextRefId;
5442
+ var slotLog2, refIds, nextRefId;
5430
5443
  var init_slot_types = __esm({
5431
5444
  "runtime/ui/slot-types.ts"() {
5432
5445
  init_logger();
5433
- slotLog = createLogger("almadar:ui:slot-render");
5446
+ slotLog2 = createLogger("almadar:ui:slot-render");
5434
5447
  refIds = /* @__PURE__ */ new WeakMap();
5435
5448
  nextRefId = 1;
5436
5449
  }
@@ -47008,7 +47021,7 @@ function SlotContentRenderer({
47008
47021
  }) {
47009
47022
  const entityProp = content.props.entity;
47010
47023
  if (content.pattern === "form-section") {
47011
- slotLog.debug("SlotContentRenderer:form-section-render", {
47024
+ slotLog2.debug("SlotContentRenderer:form-section-render", {
47012
47025
  contentId: content.id,
47013
47026
  sourceTrait: content.sourceTrait,
47014
47027
  entityRefId: refId(entityProp),
@@ -51757,6 +51770,7 @@ init_traitRegistry();
51757
51770
  init_verificationRegistry();
51758
51771
  var crossTraitLog = createLogger("almadar:ui:cross-trait");
51759
51772
  var flushLog = createLogger("almadar:ui:slot-flush");
51773
+ var stateLog = createLogger("almadar:ui:state-transitions");
51760
51774
  function toTraitDefinition(binding) {
51761
51775
  return {
51762
51776
  name: binding.trait.name,
@@ -52189,21 +52203,29 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
52189
52203
  const executor = new runtime.EffectExecutor({ handlers: trackingHandlers, bindings: bindingCtx, context: effectContext });
52190
52204
  try {
52191
52205
  await executor.executeAll(result.effects);
52192
- console.log(
52193
- "[TraitStateMachine] After executeAll, pendingSlots:",
52194
- Object.fromEntries(pendingSlots.entries())
52195
- );
52206
+ stateLog.info("transition:render-ui-dispatched", {
52207
+ traitName,
52208
+ fromState: result.previousState,
52209
+ toState: result.newState,
52210
+ event: eventKey,
52211
+ slotsTouched: Array.from(pendingSlots.keys()).join(","),
52212
+ patternTypes: Array.from(pendingSlots.entries()).map(
52213
+ ([slot, patterns]) => `${slot}:[${patterns.map((p2) => p2.pattern?.type ?? "null").join(",")}]`
52214
+ ).join(";")
52215
+ });
52196
52216
  void slotSource;
52197
52217
  for (const [slot, patterns] of pendingSlots) {
52198
52218
  flushSlot(traitName, slot, patterns);
52199
52219
  }
52200
52220
  } catch (error) {
52201
- console.error(
52202
- "[TraitStateMachine] Effect execution error:",
52203
- error,
52204
- "| effects:",
52205
- JSON.stringify(result.effects)
52206
- );
52221
+ stateLog.error("transition:effect-error", {
52222
+ traitName,
52223
+ fromState: result.previousState,
52224
+ toState: result.newState,
52225
+ event: eventKey,
52226
+ error: error instanceof Error ? error.message : String(error),
52227
+ effectCount: result.effects.length
52228
+ });
52207
52229
  }
52208
52230
  } else if (!result.executed) {
52209
52231
  if (result.guardResult === false) {
@@ -52349,13 +52371,20 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
52349
52371
  if (eventKey === "INIT" || eventKey === "LOAD" || eventKey === "$MOUNT") {
52350
52372
  continue;
52351
52373
  }
52352
- const unsub = eventBus.on(`UI:${orbitalName}.${traitName}.${eventKey}`, (event) => {
52374
+ const selfBusKey = `UI:${orbitalName}.${traitName}.${eventKey}`;
52375
+ crossTraitLog.debug("self:subscribe", { traitName, busKey: selfBusKey, eventKey });
52376
+ const unsub = eventBus.on(selfBusKey, (event) => {
52353
52377
  if (event.source && event.source.fromBridge) {
52378
+ crossTraitLog.debug("self:fire-skipped-bridge-echo", { traitName, busKey: selfBusKey, eventKey });
52354
52379
  return;
52355
52380
  }
52381
+ crossTraitLog.info("self:fire", { traitName, busKey: selfBusKey, eventKey });
52356
52382
  enqueueAndDrain(eventKey, event.payload);
52357
52383
  });
52358
- unsubscribes.push(unsub);
52384
+ unsubscribes.push(() => {
52385
+ crossTraitLog.debug("self:unsubscribe", { traitName, busKey: selfBusKey, eventKey });
52386
+ unsub();
52387
+ });
52359
52388
  }
52360
52389
  }
52361
52390
  for (const binding of traitBindings) {
@@ -52373,18 +52402,23 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
52373
52402
  const sourceOrbital = listen.source?.orbital ?? ownOrbital;
52374
52403
  if (!sourceOrbital) continue;
52375
52404
  const busKey = `UI:${sourceOrbital}.${sourceTrait}.${listen.event}`;
52376
- crossTraitLog.debug("listen:subscribed", { busKey, targetTrait: binding.trait.name, triggers: listen.triggers });
52405
+ crossTraitLog.debug("listen:subscribed", { busKey, targetTrait: binding.trait.name, sourceOrbital, sourceTrait, listenEvent: listen.event, triggers: listen.triggers });
52377
52406
  const unsub = eventBus.on(busKey, (event) => {
52378
52407
  crossTraitLog.info("listen:fired", { busKey, targetTrait: binding.trait.name, triggers: listen.triggers });
52379
52408
  enqueueAndDrain(listen.triggers, event.payload);
52380
52409
  });
52381
- unsubscribes.push(unsub);
52410
+ unsubscribes.push(() => {
52411
+ crossTraitLog.debug("listen:unsubscribe", { busKey, targetTrait: binding.trait.name, triggers: listen.triggers });
52412
+ unsub();
52413
+ });
52382
52414
  }
52383
52415
  }
52384
52416
  return () => {
52417
+ crossTraitLog.debug("cleanup:start", { unsubscribeCount: unsubscribes.length });
52385
52418
  for (const unsub of unsubscribes) {
52386
52419
  unsub();
52387
52420
  }
52421
+ crossTraitLog.debug("cleanup:done", {});
52388
52422
  };
52389
52423
  }, [traitBindings, eventBus, enqueueAndDrain]);
52390
52424
  return {
package/dist/avl/index.js CHANGED
@@ -4302,6 +4302,13 @@ function useUISlotManager() {
4302
4302
  indexTraitRender(content.sourceTrait, content);
4303
4303
  notifyTraitSubscribers(content.sourceTrait, content);
4304
4304
  }
4305
+ slotLog.info("slot:written", {
4306
+ slot: config.target,
4307
+ sourceKey,
4308
+ sourceTrait: content.sourceTrait,
4309
+ patternType: content.pattern,
4310
+ priority: content.priority
4311
+ });
4305
4312
  notifySubscribers(config.target, aggregateSlot(nextSources));
4306
4313
  return nextAll;
4307
4314
  });
@@ -4339,7 +4346,10 @@ function useUISlotManager() {
4339
4346
  const sourceKey = sourceTrait;
4340
4347
  setSources((prev) => {
4341
4348
  const slotSources = prev[slot];
4342
- if (!slotSources || !(sourceKey in slotSources)) return prev;
4349
+ if (!slotSources || !(sourceKey in slotSources)) {
4350
+ slotLog.debug("slot:clear-noop", { slot, sourceTrait, reason: !slotSources ? "no-slot" : "no-source" });
4351
+ return prev;
4352
+ }
4343
4353
  const content = slotSources[sourceKey];
4344
4354
  const timer = timersRef.current.get(content.id);
4345
4355
  if (timer) {
@@ -4353,6 +4363,7 @@ function useUISlotManager() {
4353
4363
  }
4354
4364
  const nextSources = { ...slotSources };
4355
4365
  delete nextSources[sourceKey];
4366
+ slotLog.info("slot:cleared", { slot, sourceTrait, lastPatternType: content.pattern });
4356
4367
  notifySubscribers(slot, aggregateSlot(nextSources));
4357
4368
  return { ...prev, [slot]: nextSources };
4358
4369
  });
@@ -4471,10 +4482,12 @@ function useUISlotManager() {
4471
4482
  updateTraitContent
4472
4483
  };
4473
4484
  }
4474
- var DEFAULT_SOURCE_KEY, MULTI_SOURCE_STACK_TRAIT, ALL_SLOTS, DEFAULT_SLOTS, DEFAULT_SOURCES, idCounter;
4485
+ var slotLog, DEFAULT_SOURCE_KEY, MULTI_SOURCE_STACK_TRAIT, ALL_SLOTS, DEFAULT_SLOTS, DEFAULT_SOURCES, idCounter;
4475
4486
  var init_useUISlots = __esm({
4476
4487
  "hooks/useUISlots.ts"() {
4477
4488
  "use client";
4489
+ init_logger();
4490
+ slotLog = createLogger("almadar:ui:useUISlots");
4478
4491
  DEFAULT_SOURCE_KEY = "__default__";
4479
4492
  MULTI_SOURCE_STACK_TRAIT = "__multi_source_stack__";
4480
4493
  ALL_SLOTS = [
@@ -5380,11 +5393,11 @@ function refId(obj) {
5380
5393
  refIds.set(obj, id);
5381
5394
  return id;
5382
5395
  }
5383
- var slotLog, refIds, nextRefId;
5396
+ var slotLog2, refIds, nextRefId;
5384
5397
  var init_slot_types = __esm({
5385
5398
  "runtime/ui/slot-types.ts"() {
5386
5399
  init_logger();
5387
- slotLog = createLogger("almadar:ui:slot-render");
5400
+ slotLog2 = createLogger("almadar:ui:slot-render");
5388
5401
  refIds = /* @__PURE__ */ new WeakMap();
5389
5402
  nextRefId = 1;
5390
5403
  }
@@ -46962,7 +46975,7 @@ function SlotContentRenderer({
46962
46975
  }) {
46963
46976
  const entityProp = content.props.entity;
46964
46977
  if (content.pattern === "form-section") {
46965
- slotLog.debug("SlotContentRenderer:form-section-render", {
46978
+ slotLog2.debug("SlotContentRenderer:form-section-render", {
46966
46979
  contentId: content.id,
46967
46980
  sourceTrait: content.sourceTrait,
46968
46981
  entityRefId: refId(entityProp),
@@ -51711,6 +51724,7 @@ init_traitRegistry();
51711
51724
  init_verificationRegistry();
51712
51725
  var crossTraitLog = createLogger("almadar:ui:cross-trait");
51713
51726
  var flushLog = createLogger("almadar:ui:slot-flush");
51727
+ var stateLog = createLogger("almadar:ui:state-transitions");
51714
51728
  function toTraitDefinition(binding) {
51715
51729
  return {
51716
51730
  name: binding.trait.name,
@@ -52143,21 +52157,29 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
52143
52157
  const executor = new EffectExecutor({ handlers: trackingHandlers, bindings: bindingCtx, context: effectContext });
52144
52158
  try {
52145
52159
  await executor.executeAll(result.effects);
52146
- console.log(
52147
- "[TraitStateMachine] After executeAll, pendingSlots:",
52148
- Object.fromEntries(pendingSlots.entries())
52149
- );
52160
+ stateLog.info("transition:render-ui-dispatched", {
52161
+ traitName,
52162
+ fromState: result.previousState,
52163
+ toState: result.newState,
52164
+ event: eventKey,
52165
+ slotsTouched: Array.from(pendingSlots.keys()).join(","),
52166
+ patternTypes: Array.from(pendingSlots.entries()).map(
52167
+ ([slot, patterns]) => `${slot}:[${patterns.map((p2) => p2.pattern?.type ?? "null").join(",")}]`
52168
+ ).join(";")
52169
+ });
52150
52170
  void slotSource;
52151
52171
  for (const [slot, patterns] of pendingSlots) {
52152
52172
  flushSlot(traitName, slot, patterns);
52153
52173
  }
52154
52174
  } catch (error) {
52155
- console.error(
52156
- "[TraitStateMachine] Effect execution error:",
52157
- error,
52158
- "| effects:",
52159
- JSON.stringify(result.effects)
52160
- );
52175
+ stateLog.error("transition:effect-error", {
52176
+ traitName,
52177
+ fromState: result.previousState,
52178
+ toState: result.newState,
52179
+ event: eventKey,
52180
+ error: error instanceof Error ? error.message : String(error),
52181
+ effectCount: result.effects.length
52182
+ });
52161
52183
  }
52162
52184
  } else if (!result.executed) {
52163
52185
  if (result.guardResult === false) {
@@ -52303,13 +52325,20 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
52303
52325
  if (eventKey === "INIT" || eventKey === "LOAD" || eventKey === "$MOUNT") {
52304
52326
  continue;
52305
52327
  }
52306
- const unsub = eventBus.on(`UI:${orbitalName}.${traitName}.${eventKey}`, (event) => {
52328
+ const selfBusKey = `UI:${orbitalName}.${traitName}.${eventKey}`;
52329
+ crossTraitLog.debug("self:subscribe", { traitName, busKey: selfBusKey, eventKey });
52330
+ const unsub = eventBus.on(selfBusKey, (event) => {
52307
52331
  if (event.source && event.source.fromBridge) {
52332
+ crossTraitLog.debug("self:fire-skipped-bridge-echo", { traitName, busKey: selfBusKey, eventKey });
52308
52333
  return;
52309
52334
  }
52335
+ crossTraitLog.info("self:fire", { traitName, busKey: selfBusKey, eventKey });
52310
52336
  enqueueAndDrain(eventKey, event.payload);
52311
52337
  });
52312
- unsubscribes.push(unsub);
52338
+ unsubscribes.push(() => {
52339
+ crossTraitLog.debug("self:unsubscribe", { traitName, busKey: selfBusKey, eventKey });
52340
+ unsub();
52341
+ });
52313
52342
  }
52314
52343
  }
52315
52344
  for (const binding of traitBindings) {
@@ -52327,18 +52356,23 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
52327
52356
  const sourceOrbital = listen.source?.orbital ?? ownOrbital;
52328
52357
  if (!sourceOrbital) continue;
52329
52358
  const busKey = `UI:${sourceOrbital}.${sourceTrait}.${listen.event}`;
52330
- crossTraitLog.debug("listen:subscribed", { busKey, targetTrait: binding.trait.name, triggers: listen.triggers });
52359
+ crossTraitLog.debug("listen:subscribed", { busKey, targetTrait: binding.trait.name, sourceOrbital, sourceTrait, listenEvent: listen.event, triggers: listen.triggers });
52331
52360
  const unsub = eventBus.on(busKey, (event) => {
52332
52361
  crossTraitLog.info("listen:fired", { busKey, targetTrait: binding.trait.name, triggers: listen.triggers });
52333
52362
  enqueueAndDrain(listen.triggers, event.payload);
52334
52363
  });
52335
- unsubscribes.push(unsub);
52364
+ unsubscribes.push(() => {
52365
+ crossTraitLog.debug("listen:unsubscribe", { busKey, targetTrait: binding.trait.name, triggers: listen.triggers });
52366
+ unsub();
52367
+ });
52336
52368
  }
52337
52369
  }
52338
52370
  return () => {
52371
+ crossTraitLog.debug("cleanup:start", { unsubscribeCount: unsubscribes.length });
52339
52372
  for (const unsub of unsubscribes) {
52340
52373
  unsub();
52341
52374
  }
52375
+ crossTraitLog.debug("cleanup:done", {});
52342
52376
  };
52343
52377
  }, [traitBindings, eventBus, enqueueAndDrain]);
52344
52378
  return {
@@ -40897,6 +40897,10 @@ function useDeepAgentGeneration() {
40897
40897
 
40898
40898
  // hooks/index.ts
40899
40899
  init_useEventBus();
40900
+
40901
+ // hooks/useUISlots.ts
40902
+ init_logger();
40903
+ var slotLog2 = createLogger("almadar:ui:useUISlots");
40900
40904
  var DEFAULT_SOURCE_KEY = "__default__";
40901
40905
  var MULTI_SOURCE_STACK_TRAIT = "__multi_source_stack__";
40902
40906
  var ALL_SLOTS2 = [
@@ -41055,6 +41059,13 @@ function useUISlotManager() {
41055
41059
  indexTraitRender(content.sourceTrait, content);
41056
41060
  notifyTraitSubscribers(content.sourceTrait, content);
41057
41061
  }
41062
+ slotLog2.info("slot:written", {
41063
+ slot: config.target,
41064
+ sourceKey,
41065
+ sourceTrait: content.sourceTrait,
41066
+ patternType: content.pattern,
41067
+ priority: content.priority
41068
+ });
41058
41069
  notifySubscribers(config.target, aggregateSlot(nextSources));
41059
41070
  return nextAll;
41060
41071
  });
@@ -41092,7 +41103,10 @@ function useUISlotManager() {
41092
41103
  const sourceKey = sourceTrait;
41093
41104
  setSources((prev) => {
41094
41105
  const slotSources = prev[slot];
41095
- if (!slotSources || !(sourceKey in slotSources)) return prev;
41106
+ if (!slotSources || !(sourceKey in slotSources)) {
41107
+ slotLog2.debug("slot:clear-noop", { slot, sourceTrait, reason: !slotSources ? "no-slot" : "no-source" });
41108
+ return prev;
41109
+ }
41096
41110
  const content = slotSources[sourceKey];
41097
41111
  const timer = timersRef.current.get(content.id);
41098
41112
  if (timer) {
@@ -41106,6 +41120,7 @@ function useUISlotManager() {
41106
41120
  }
41107
41121
  const nextSources = { ...slotSources };
41108
41122
  delete nextSources[sourceKey];
41123
+ slotLog2.info("slot:cleared", { slot, sourceTrait, lastPatternType: content.pattern });
41109
41124
  notifySubscribers(slot, aggregateSlot(nextSources));
41110
41125
  return { ...prev, [slot]: nextSources };
41111
41126
  });
@@ -40852,6 +40852,10 @@ function useDeepAgentGeneration() {
40852
40852
 
40853
40853
  // hooks/index.ts
40854
40854
  init_useEventBus();
40855
+
40856
+ // hooks/useUISlots.ts
40857
+ init_logger();
40858
+ var slotLog2 = createLogger("almadar:ui:useUISlots");
40855
40859
  var DEFAULT_SOURCE_KEY = "__default__";
40856
40860
  var MULTI_SOURCE_STACK_TRAIT = "__multi_source_stack__";
40857
40861
  var ALL_SLOTS2 = [
@@ -41010,6 +41014,13 @@ function useUISlotManager() {
41010
41014
  indexTraitRender(content.sourceTrait, content);
41011
41015
  notifyTraitSubscribers(content.sourceTrait, content);
41012
41016
  }
41017
+ slotLog2.info("slot:written", {
41018
+ slot: config.target,
41019
+ sourceKey,
41020
+ sourceTrait: content.sourceTrait,
41021
+ patternType: content.pattern,
41022
+ priority: content.priority
41023
+ });
41013
41024
  notifySubscribers(config.target, aggregateSlot(nextSources));
41014
41025
  return nextAll;
41015
41026
  });
@@ -41047,7 +41058,10 @@ function useUISlotManager() {
41047
41058
  const sourceKey = sourceTrait;
41048
41059
  setSources((prev) => {
41049
41060
  const slotSources = prev[slot];
41050
- if (!slotSources || !(sourceKey in slotSources)) return prev;
41061
+ if (!slotSources || !(sourceKey in slotSources)) {
41062
+ slotLog2.debug("slot:clear-noop", { slot, sourceTrait, reason: !slotSources ? "no-slot" : "no-source" });
41063
+ return prev;
41064
+ }
41051
41065
  const content = slotSources[sourceKey];
41052
41066
  const timer = timersRef.current.get(content.id);
41053
41067
  if (timer) {
@@ -41061,6 +41075,7 @@ function useUISlotManager() {
41061
41075
  }
41062
41076
  const nextSources = { ...slotSources };
41063
41077
  delete nextSources[sourceKey];
41078
+ slotLog2.info("slot:cleared", { slot, sourceTrait, lastPatternType: content.pattern });
41064
41079
  notifySubscribers(slot, aggregateSlot(nextSources));
41065
41080
  return { ...prev, [slot]: nextSources };
41066
41081
  });
@@ -3,6 +3,56 @@
3
3
  var react = require('react');
4
4
  var jsxRuntime = require('react/jsx-runtime');
5
5
 
6
+ // lib/logger.ts
7
+ var LEVEL_PRIORITY = { DEBUG: 0, INFO: 1, WARN: 2, ERROR: 3 };
8
+ var ENV = typeof process !== "undefined" && process.env ? process.env : {};
9
+ function envGet(key) {
10
+ return ENV[key] ?? ENV[`VITE_${key}`];
11
+ }
12
+ var NODE_ENV = envGet("NODE_ENV") ?? "development";
13
+ var CONFIGURED_LEVEL = (envGet("LOG_LEVEL") ?? (NODE_ENV === "production" ? "info" : "debug")).toUpperCase();
14
+ var MIN_PRIORITY = LEVEL_PRIORITY[CONFIGURED_LEVEL] ?? 0;
15
+ var DEBUG_FILTER = (envGet("ALMADAR_DEBUG") ?? "").split(",").map((s) => s.trim()).filter(Boolean);
16
+ function matchesNamespace(namespace) {
17
+ if (DEBUG_FILTER.length === 0) return true;
18
+ return DEBUG_FILTER.some((pattern) => {
19
+ if (pattern === "*" || pattern === "almadar:*") return true;
20
+ if (pattern.endsWith(":*")) return namespace.startsWith(pattern.slice(0, -1));
21
+ return namespace === pattern;
22
+ });
23
+ }
24
+ function createLogger(namespace) {
25
+ const nsAllowed = matchesNamespace(namespace);
26
+ const log = (level, message, data, correlationId) => {
27
+ if (LEVEL_PRIORITY[level] < MIN_PRIORITY) return;
28
+ if (level === "DEBUG" && !nsAllowed) return;
29
+ const prefix = `[${namespace}]`;
30
+ const logData = correlationId ? { ...data, cid: correlationId } : data;
31
+ switch (level) {
32
+ case "DEBUG":
33
+ console.debug(prefix, message, logData ?? "");
34
+ break;
35
+ case "INFO":
36
+ console.info(prefix, message, logData ?? "");
37
+ break;
38
+ case "WARN":
39
+ console.warn(prefix, message, logData ?? "");
40
+ break;
41
+ case "ERROR":
42
+ console.error(prefix, message, logData ?? "");
43
+ break;
44
+ }
45
+ };
46
+ return {
47
+ debug: (msg, data, cid) => log("DEBUG", msg, data, cid),
48
+ info: (msg, data, cid) => log("INFO", msg, data, cid),
49
+ warn: (msg, data, cid) => log("WARN", msg, data, cid),
50
+ error: (msg, data, cid) => log("ERROR", msg, data, cid)
51
+ };
52
+ }
53
+
54
+ // hooks/useUISlots.ts
55
+ var slotLog = createLogger("almadar:ui:useUISlots");
6
56
  var DEFAULT_SOURCE_KEY = "__default__";
7
57
  var MULTI_SOURCE_STACK_TRAIT = "__multi_source_stack__";
8
58
  var ALL_SLOTS = [
@@ -161,6 +211,13 @@ function useUISlotManager() {
161
211
  indexTraitRender(content.sourceTrait, content);
162
212
  notifyTraitSubscribers(content.sourceTrait, content);
163
213
  }
214
+ slotLog.info("slot:written", {
215
+ slot: config.target,
216
+ sourceKey,
217
+ sourceTrait: content.sourceTrait,
218
+ patternType: content.pattern,
219
+ priority: content.priority
220
+ });
164
221
  notifySubscribers(config.target, aggregateSlot(nextSources));
165
222
  return nextAll;
166
223
  });
@@ -198,7 +255,10 @@ function useUISlotManager() {
198
255
  const sourceKey = sourceTrait;
199
256
  setSources((prev) => {
200
257
  const slotSources = prev[slot];
201
- if (!slotSources || !(sourceKey in slotSources)) return prev;
258
+ if (!slotSources || !(sourceKey in slotSources)) {
259
+ slotLog.debug("slot:clear-noop", { slot, sourceTrait, reason: !slotSources ? "no-slot" : "no-source" });
260
+ return prev;
261
+ }
202
262
  const content = slotSources[sourceKey];
203
263
  const timer = timersRef.current.get(content.id);
204
264
  if (timer) {
@@ -212,6 +272,7 @@ function useUISlotManager() {
212
272
  }
213
273
  const nextSources = { ...slotSources };
214
274
  delete nextSources[sourceKey];
275
+ slotLog.info("slot:cleared", { slot, sourceTrait, lastPatternType: content.pattern });
215
276
  notifySubscribers(slot, aggregateSlot(nextSources));
216
277
  return { ...prev, [slot]: nextSources };
217
278
  });
@@ -1,6 +1,56 @@
1
1
  import { createContext, useMemo, useContext, useState, useEffect, useCallback, useRef } from 'react';
2
2
  import { jsx } from 'react/jsx-runtime';
3
3
 
4
+ // lib/logger.ts
5
+ var LEVEL_PRIORITY = { DEBUG: 0, INFO: 1, WARN: 2, ERROR: 3 };
6
+ var ENV = typeof process !== "undefined" && process.env ? process.env : {};
7
+ function envGet(key) {
8
+ return ENV[key] ?? ENV[`VITE_${key}`];
9
+ }
10
+ var NODE_ENV = envGet("NODE_ENV") ?? "development";
11
+ var CONFIGURED_LEVEL = (envGet("LOG_LEVEL") ?? (NODE_ENV === "production" ? "info" : "debug")).toUpperCase();
12
+ var MIN_PRIORITY = LEVEL_PRIORITY[CONFIGURED_LEVEL] ?? 0;
13
+ var DEBUG_FILTER = (envGet("ALMADAR_DEBUG") ?? "").split(",").map((s) => s.trim()).filter(Boolean);
14
+ function matchesNamespace(namespace) {
15
+ if (DEBUG_FILTER.length === 0) return true;
16
+ return DEBUG_FILTER.some((pattern) => {
17
+ if (pattern === "*" || pattern === "almadar:*") return true;
18
+ if (pattern.endsWith(":*")) return namespace.startsWith(pattern.slice(0, -1));
19
+ return namespace === pattern;
20
+ });
21
+ }
22
+ function createLogger(namespace) {
23
+ const nsAllowed = matchesNamespace(namespace);
24
+ const log = (level, message, data, correlationId) => {
25
+ if (LEVEL_PRIORITY[level] < MIN_PRIORITY) return;
26
+ if (level === "DEBUG" && !nsAllowed) return;
27
+ const prefix = `[${namespace}]`;
28
+ const logData = correlationId ? { ...data, cid: correlationId } : data;
29
+ switch (level) {
30
+ case "DEBUG":
31
+ console.debug(prefix, message, logData ?? "");
32
+ break;
33
+ case "INFO":
34
+ console.info(prefix, message, logData ?? "");
35
+ break;
36
+ case "WARN":
37
+ console.warn(prefix, message, logData ?? "");
38
+ break;
39
+ case "ERROR":
40
+ console.error(prefix, message, logData ?? "");
41
+ break;
42
+ }
43
+ };
44
+ return {
45
+ debug: (msg, data, cid) => log("DEBUG", msg, data, cid),
46
+ info: (msg, data, cid) => log("INFO", msg, data, cid),
47
+ warn: (msg, data, cid) => log("WARN", msg, data, cid),
48
+ error: (msg, data, cid) => log("ERROR", msg, data, cid)
49
+ };
50
+ }
51
+
52
+ // hooks/useUISlots.ts
53
+ var slotLog = createLogger("almadar:ui:useUISlots");
4
54
  var DEFAULT_SOURCE_KEY = "__default__";
5
55
  var MULTI_SOURCE_STACK_TRAIT = "__multi_source_stack__";
6
56
  var ALL_SLOTS = [
@@ -159,6 +209,13 @@ function useUISlotManager() {
159
209
  indexTraitRender(content.sourceTrait, content);
160
210
  notifyTraitSubscribers(content.sourceTrait, content);
161
211
  }
212
+ slotLog.info("slot:written", {
213
+ slot: config.target,
214
+ sourceKey,
215
+ sourceTrait: content.sourceTrait,
216
+ patternType: content.pattern,
217
+ priority: content.priority
218
+ });
162
219
  notifySubscribers(config.target, aggregateSlot(nextSources));
163
220
  return nextAll;
164
221
  });
@@ -196,7 +253,10 @@ function useUISlotManager() {
196
253
  const sourceKey = sourceTrait;
197
254
  setSources((prev) => {
198
255
  const slotSources = prev[slot];
199
- if (!slotSources || !(sourceKey in slotSources)) return prev;
256
+ if (!slotSources || !(sourceKey in slotSources)) {
257
+ slotLog.debug("slot:clear-noop", { slot, sourceTrait, reason: !slotSources ? "no-slot" : "no-source" });
258
+ return prev;
259
+ }
200
260
  const content = slotSources[sourceKey];
201
261
  const timer = timersRef.current.get(content.id);
202
262
  if (timer) {
@@ -210,6 +270,7 @@ function useUISlotManager() {
210
270
  }
211
271
  const nextSources = { ...slotSources };
212
272
  delete nextSources[sourceKey];
273
+ slotLog.info("slot:cleared", { slot, sourceTrait, lastPatternType: content.pattern });
213
274
  notifySubscribers(slot, aggregateSlot(nextSources));
214
275
  return { ...prev, [slot]: nextSources };
215
276
  });
@@ -1046,6 +1046,7 @@ function useEmitEvent() {
1046
1046
  [eventBus]
1047
1047
  );
1048
1048
  }
1049
+ var slotLog = createLogger("almadar:ui:useUISlots");
1049
1050
  var DEFAULT_SOURCE_KEY = "__default__";
1050
1051
  var MULTI_SOURCE_STACK_TRAIT = "__multi_source_stack__";
1051
1052
  var ALL_SLOTS = [
@@ -1204,6 +1205,13 @@ function useUISlotManager() {
1204
1205
  indexTraitRender(content.sourceTrait, content);
1205
1206
  notifyTraitSubscribers(content.sourceTrait, content);
1206
1207
  }
1208
+ slotLog.info("slot:written", {
1209
+ slot: config.target,
1210
+ sourceKey,
1211
+ sourceTrait: content.sourceTrait,
1212
+ patternType: content.pattern,
1213
+ priority: content.priority
1214
+ });
1207
1215
  notifySubscribers(config.target, aggregateSlot(nextSources));
1208
1216
  return nextAll;
1209
1217
  });
@@ -1241,7 +1249,10 @@ function useUISlotManager() {
1241
1249
  const sourceKey = sourceTrait;
1242
1250
  setSources((prev) => {
1243
1251
  const slotSources = prev[slot];
1244
- if (!slotSources || !(sourceKey in slotSources)) return prev;
1252
+ if (!slotSources || !(sourceKey in slotSources)) {
1253
+ slotLog.debug("slot:clear-noop", { slot, sourceTrait, reason: !slotSources ? "no-slot" : "no-source" });
1254
+ return prev;
1255
+ }
1245
1256
  const content = slotSources[sourceKey];
1246
1257
  const timer = timersRef.current.get(content.id);
1247
1258
  if (timer) {
@@ -1255,6 +1266,7 @@ function useUISlotManager() {
1255
1266
  }
1256
1267
  const nextSources = { ...slotSources };
1257
1268
  delete nextSources[sourceKey];
1269
+ slotLog.info("slot:cleared", { slot, sourceTrait, lastPatternType: content.pattern });
1258
1270
  notifySubscribers(slot, aggregateSlot(nextSources));
1259
1271
  return { ...prev, [slot]: nextSources };
1260
1272
  });
@@ -1044,6 +1044,7 @@ function useEmitEvent() {
1044
1044
  [eventBus]
1045
1045
  );
1046
1046
  }
1047
+ var slotLog = createLogger("almadar:ui:useUISlots");
1047
1048
  var DEFAULT_SOURCE_KEY = "__default__";
1048
1049
  var MULTI_SOURCE_STACK_TRAIT = "__multi_source_stack__";
1049
1050
  var ALL_SLOTS = [
@@ -1202,6 +1203,13 @@ function useUISlotManager() {
1202
1203
  indexTraitRender(content.sourceTrait, content);
1203
1204
  notifyTraitSubscribers(content.sourceTrait, content);
1204
1205
  }
1206
+ slotLog.info("slot:written", {
1207
+ slot: config.target,
1208
+ sourceKey,
1209
+ sourceTrait: content.sourceTrait,
1210
+ patternType: content.pattern,
1211
+ priority: content.priority
1212
+ });
1205
1213
  notifySubscribers(config.target, aggregateSlot(nextSources));
1206
1214
  return nextAll;
1207
1215
  });
@@ -1239,7 +1247,10 @@ function useUISlotManager() {
1239
1247
  const sourceKey = sourceTrait;
1240
1248
  setSources((prev) => {
1241
1249
  const slotSources = prev[slot];
1242
- if (!slotSources || !(sourceKey in slotSources)) return prev;
1250
+ if (!slotSources || !(sourceKey in slotSources)) {
1251
+ slotLog.debug("slot:clear-noop", { slot, sourceTrait, reason: !slotSources ? "no-slot" : "no-source" });
1252
+ return prev;
1253
+ }
1243
1254
  const content = slotSources[sourceKey];
1244
1255
  const timer = timersRef.current.get(content.id);
1245
1256
  if (timer) {
@@ -1253,6 +1264,7 @@ function useUISlotManager() {
1253
1264
  }
1254
1265
  const nextSources = { ...slotSources };
1255
1266
  delete nextSources[sourceKey];
1267
+ slotLog.info("slot:cleared", { slot, sourceTrait, lastPatternType: content.pattern });
1256
1268
  notifySubscribers(slot, aggregateSlot(nextSources));
1257
1269
  return { ...prev, [slot]: nextSources };
1258
1270
  });
@@ -2086,11 +2086,11 @@ function refId(obj) {
2086
2086
  refIds.set(obj, id);
2087
2087
  return id;
2088
2088
  }
2089
- var slotLog, refIds, nextRefId;
2089
+ var slotLog2, refIds, nextRefId;
2090
2090
  var init_slot_types = __esm({
2091
2091
  "runtime/ui/slot-types.ts"() {
2092
2092
  init_logger();
2093
- slotLog = createLogger("almadar:ui:slot-render");
2093
+ slotLog2 = createLogger("almadar:ui:slot-render");
2094
2094
  refIds = /* @__PURE__ */ new WeakMap();
2095
2095
  nextRefId = 1;
2096
2096
  }
@@ -37880,7 +37880,7 @@ function SlotContentRenderer({
37880
37880
  }) {
37881
37881
  const entityProp = content.props.entity;
37882
37882
  if (content.pattern === "form-section") {
37883
- slotLog.debug("SlotContentRenderer:form-section-render", {
37883
+ slotLog2.debug("SlotContentRenderer:form-section-render", {
37884
37884
  contentId: content.id,
37885
37885
  sourceTrait: content.sourceTrait,
37886
37886
  entityRefId: refId(entityProp),
@@ -38091,6 +38091,10 @@ var init_UISlotRenderer = __esm({
38091
38091
 
38092
38092
  // hooks/index.ts
38093
38093
  init_useEventBus();
38094
+
38095
+ // hooks/useUISlots.ts
38096
+ init_logger();
38097
+ createLogger("almadar:ui:useUISlots");
38094
38098
  var ALL_SLOTS = [
38095
38099
  "main",
38096
38100
  "sidebar",
@@ -38293,6 +38297,7 @@ init_traitRegistry();
38293
38297
  init_verificationRegistry();
38294
38298
  var crossTraitLog = createLogger("almadar:ui:cross-trait");
38295
38299
  var flushLog = createLogger("almadar:ui:slot-flush");
38300
+ var stateLog = createLogger("almadar:ui:state-transitions");
38296
38301
  function toTraitDefinition(binding) {
38297
38302
  return {
38298
38303
  name: binding.trait.name,
@@ -38725,21 +38730,29 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
38725
38730
  const executor = new runtime.EffectExecutor({ handlers: trackingHandlers, bindings: bindingCtx, context: effectContext });
38726
38731
  try {
38727
38732
  await executor.executeAll(result.effects);
38728
- console.log(
38729
- "[TraitStateMachine] After executeAll, pendingSlots:",
38730
- Object.fromEntries(pendingSlots.entries())
38731
- );
38733
+ stateLog.info("transition:render-ui-dispatched", {
38734
+ traitName,
38735
+ fromState: result.previousState,
38736
+ toState: result.newState,
38737
+ event: eventKey,
38738
+ slotsTouched: Array.from(pendingSlots.keys()).join(","),
38739
+ patternTypes: Array.from(pendingSlots.entries()).map(
38740
+ ([slot, patterns]) => `${slot}:[${patterns.map((p2) => p2.pattern?.type ?? "null").join(",")}]`
38741
+ ).join(";")
38742
+ });
38732
38743
  void slotSource;
38733
38744
  for (const [slot, patterns] of pendingSlots) {
38734
38745
  flushSlot(traitName, slot, patterns);
38735
38746
  }
38736
38747
  } catch (error) {
38737
- console.error(
38738
- "[TraitStateMachine] Effect execution error:",
38739
- error,
38740
- "| effects:",
38741
- JSON.stringify(result.effects)
38742
- );
38748
+ stateLog.error("transition:effect-error", {
38749
+ traitName,
38750
+ fromState: result.previousState,
38751
+ toState: result.newState,
38752
+ event: eventKey,
38753
+ error: error instanceof Error ? error.message : String(error),
38754
+ effectCount: result.effects.length
38755
+ });
38743
38756
  }
38744
38757
  } else if (!result.executed) {
38745
38758
  if (result.guardResult === false) {
@@ -38885,13 +38898,20 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
38885
38898
  if (eventKey === "INIT" || eventKey === "LOAD" || eventKey === "$MOUNT") {
38886
38899
  continue;
38887
38900
  }
38888
- const unsub = eventBus.on(`UI:${orbitalName}.${traitName}.${eventKey}`, (event) => {
38901
+ const selfBusKey = `UI:${orbitalName}.${traitName}.${eventKey}`;
38902
+ crossTraitLog.debug("self:subscribe", { traitName, busKey: selfBusKey, eventKey });
38903
+ const unsub = eventBus.on(selfBusKey, (event) => {
38889
38904
  if (event.source && event.source.fromBridge) {
38905
+ crossTraitLog.debug("self:fire-skipped-bridge-echo", { traitName, busKey: selfBusKey, eventKey });
38890
38906
  return;
38891
38907
  }
38908
+ crossTraitLog.info("self:fire", { traitName, busKey: selfBusKey, eventKey });
38892
38909
  enqueueAndDrain(eventKey, event.payload);
38893
38910
  });
38894
- unsubscribes.push(unsub);
38911
+ unsubscribes.push(() => {
38912
+ crossTraitLog.debug("self:unsubscribe", { traitName, busKey: selfBusKey, eventKey });
38913
+ unsub();
38914
+ });
38895
38915
  }
38896
38916
  }
38897
38917
  for (const binding of traitBindings) {
@@ -38909,18 +38929,23 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
38909
38929
  const sourceOrbital = listen.source?.orbital ?? ownOrbital;
38910
38930
  if (!sourceOrbital) continue;
38911
38931
  const busKey = `UI:${sourceOrbital}.${sourceTrait}.${listen.event}`;
38912
- crossTraitLog.debug("listen:subscribed", { busKey, targetTrait: binding.trait.name, triggers: listen.triggers });
38932
+ crossTraitLog.debug("listen:subscribed", { busKey, targetTrait: binding.trait.name, sourceOrbital, sourceTrait, listenEvent: listen.event, triggers: listen.triggers });
38913
38933
  const unsub = eventBus.on(busKey, (event) => {
38914
38934
  crossTraitLog.info("listen:fired", { busKey, targetTrait: binding.trait.name, triggers: listen.triggers });
38915
38935
  enqueueAndDrain(listen.triggers, event.payload);
38916
38936
  });
38917
- unsubscribes.push(unsub);
38937
+ unsubscribes.push(() => {
38938
+ crossTraitLog.debug("listen:unsubscribe", { busKey, targetTrait: binding.trait.name, triggers: listen.triggers });
38939
+ unsub();
38940
+ });
38918
38941
  }
38919
38942
  }
38920
38943
  return () => {
38944
+ crossTraitLog.debug("cleanup:start", { unsubscribeCount: unsubscribes.length });
38921
38945
  for (const unsub of unsubscribes) {
38922
38946
  unsub();
38923
38947
  }
38948
+ crossTraitLog.debug("cleanup:done", {});
38924
38949
  };
38925
38950
  }, [traitBindings, eventBus, enqueueAndDrain]);
38926
38951
  return {
@@ -2041,11 +2041,11 @@ function refId(obj) {
2041
2041
  refIds.set(obj, id);
2042
2042
  return id;
2043
2043
  }
2044
- var slotLog, refIds, nextRefId;
2044
+ var slotLog2, refIds, nextRefId;
2045
2045
  var init_slot_types = __esm({
2046
2046
  "runtime/ui/slot-types.ts"() {
2047
2047
  init_logger();
2048
- slotLog = createLogger("almadar:ui:slot-render");
2048
+ slotLog2 = createLogger("almadar:ui:slot-render");
2049
2049
  refIds = /* @__PURE__ */ new WeakMap();
2050
2050
  nextRefId = 1;
2051
2051
  }
@@ -37835,7 +37835,7 @@ function SlotContentRenderer({
37835
37835
  }) {
37836
37836
  const entityProp = content.props.entity;
37837
37837
  if (content.pattern === "form-section") {
37838
- slotLog.debug("SlotContentRenderer:form-section-render", {
37838
+ slotLog2.debug("SlotContentRenderer:form-section-render", {
37839
37839
  contentId: content.id,
37840
37840
  sourceTrait: content.sourceTrait,
37841
37841
  entityRefId: refId(entityProp),
@@ -38046,6 +38046,10 @@ var init_UISlotRenderer = __esm({
38046
38046
 
38047
38047
  // hooks/index.ts
38048
38048
  init_useEventBus();
38049
+
38050
+ // hooks/useUISlots.ts
38051
+ init_logger();
38052
+ createLogger("almadar:ui:useUISlots");
38049
38053
  var ALL_SLOTS = [
38050
38054
  "main",
38051
38055
  "sidebar",
@@ -38248,6 +38252,7 @@ init_traitRegistry();
38248
38252
  init_verificationRegistry();
38249
38253
  var crossTraitLog = createLogger("almadar:ui:cross-trait");
38250
38254
  var flushLog = createLogger("almadar:ui:slot-flush");
38255
+ var stateLog = createLogger("almadar:ui:state-transitions");
38251
38256
  function toTraitDefinition(binding) {
38252
38257
  return {
38253
38258
  name: binding.trait.name,
@@ -38680,21 +38685,29 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
38680
38685
  const executor = new EffectExecutor({ handlers: trackingHandlers, bindings: bindingCtx, context: effectContext });
38681
38686
  try {
38682
38687
  await executor.executeAll(result.effects);
38683
- console.log(
38684
- "[TraitStateMachine] After executeAll, pendingSlots:",
38685
- Object.fromEntries(pendingSlots.entries())
38686
- );
38688
+ stateLog.info("transition:render-ui-dispatched", {
38689
+ traitName,
38690
+ fromState: result.previousState,
38691
+ toState: result.newState,
38692
+ event: eventKey,
38693
+ slotsTouched: Array.from(pendingSlots.keys()).join(","),
38694
+ patternTypes: Array.from(pendingSlots.entries()).map(
38695
+ ([slot, patterns]) => `${slot}:[${patterns.map((p2) => p2.pattern?.type ?? "null").join(",")}]`
38696
+ ).join(";")
38697
+ });
38687
38698
  void slotSource;
38688
38699
  for (const [slot, patterns] of pendingSlots) {
38689
38700
  flushSlot(traitName, slot, patterns);
38690
38701
  }
38691
38702
  } catch (error) {
38692
- console.error(
38693
- "[TraitStateMachine] Effect execution error:",
38694
- error,
38695
- "| effects:",
38696
- JSON.stringify(result.effects)
38697
- );
38703
+ stateLog.error("transition:effect-error", {
38704
+ traitName,
38705
+ fromState: result.previousState,
38706
+ toState: result.newState,
38707
+ event: eventKey,
38708
+ error: error instanceof Error ? error.message : String(error),
38709
+ effectCount: result.effects.length
38710
+ });
38698
38711
  }
38699
38712
  } else if (!result.executed) {
38700
38713
  if (result.guardResult === false) {
@@ -38840,13 +38853,20 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
38840
38853
  if (eventKey === "INIT" || eventKey === "LOAD" || eventKey === "$MOUNT") {
38841
38854
  continue;
38842
38855
  }
38843
- const unsub = eventBus.on(`UI:${orbitalName}.${traitName}.${eventKey}`, (event) => {
38856
+ const selfBusKey = `UI:${orbitalName}.${traitName}.${eventKey}`;
38857
+ crossTraitLog.debug("self:subscribe", { traitName, busKey: selfBusKey, eventKey });
38858
+ const unsub = eventBus.on(selfBusKey, (event) => {
38844
38859
  if (event.source && event.source.fromBridge) {
38860
+ crossTraitLog.debug("self:fire-skipped-bridge-echo", { traitName, busKey: selfBusKey, eventKey });
38845
38861
  return;
38846
38862
  }
38863
+ crossTraitLog.info("self:fire", { traitName, busKey: selfBusKey, eventKey });
38847
38864
  enqueueAndDrain(eventKey, event.payload);
38848
38865
  });
38849
- unsubscribes.push(unsub);
38866
+ unsubscribes.push(() => {
38867
+ crossTraitLog.debug("self:unsubscribe", { traitName, busKey: selfBusKey, eventKey });
38868
+ unsub();
38869
+ });
38850
38870
  }
38851
38871
  }
38852
38872
  for (const binding of traitBindings) {
@@ -38864,18 +38884,23 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
38864
38884
  const sourceOrbital = listen.source?.orbital ?? ownOrbital;
38865
38885
  if (!sourceOrbital) continue;
38866
38886
  const busKey = `UI:${sourceOrbital}.${sourceTrait}.${listen.event}`;
38867
- crossTraitLog.debug("listen:subscribed", { busKey, targetTrait: binding.trait.name, triggers: listen.triggers });
38887
+ crossTraitLog.debug("listen:subscribed", { busKey, targetTrait: binding.trait.name, sourceOrbital, sourceTrait, listenEvent: listen.event, triggers: listen.triggers });
38868
38888
  const unsub = eventBus.on(busKey, (event) => {
38869
38889
  crossTraitLog.info("listen:fired", { busKey, targetTrait: binding.trait.name, triggers: listen.triggers });
38870
38890
  enqueueAndDrain(listen.triggers, event.payload);
38871
38891
  });
38872
- unsubscribes.push(unsub);
38892
+ unsubscribes.push(() => {
38893
+ crossTraitLog.debug("listen:unsubscribe", { busKey, targetTrait: binding.trait.name, triggers: listen.triggers });
38894
+ unsub();
38895
+ });
38873
38896
  }
38874
38897
  }
38875
38898
  return () => {
38899
+ crossTraitLog.debug("cleanup:start", { unsubscribeCount: unsubscribes.length });
38876
38900
  for (const unsub of unsubscribes) {
38877
38901
  unsub();
38878
38902
  }
38903
+ crossTraitLog.debug("cleanup:done", {});
38879
38904
  };
38880
38905
  }, [traitBindings, eventBus, enqueueAndDrain]);
38881
38906
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@almadar/ui",
3
- "version": "4.15.4",
3
+ "version": "4.15.5",
4
4
  "description": "React UI components, hooks, and providers for Almadar",
5
5
  "type": "module",
6
6
  "main": "./dist/components/index.js",
@@ -121,7 +121,7 @@
121
121
  "@almadar/core": "^7.5.1",
122
122
  "@almadar/evaluator": ">=2.9.2",
123
123
  "@almadar/patterns": ">=2.17.1",
124
- "@almadar/runtime": "^5.8.3",
124
+ "@almadar/runtime": "^5.8.4",
125
125
  "@almadar/std": ">=6.4.1",
126
126
  "@almadar/syntax": ">=1.3.1",
127
127
  "@xyflow/react": "12.10.1",