@almadar/ui 4.15.3 → 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,
@@ -52117,8 +52131,15 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
52117
52131
  payload: payload || {},
52118
52132
  state: result.previousState
52119
52133
  };
52120
- if (binding.config) {
52121
- sharedBindings.config = binding.config;
52134
+ const sharedDeclared = runtime.collectDeclaredConfigDefaults(
52135
+ binding.trait
52136
+ );
52137
+ const sharedCallSite = binding.config;
52138
+ if (sharedDeclared || sharedCallSite) {
52139
+ sharedBindings.config = {
52140
+ ...sharedDeclared ?? {},
52141
+ ...sharedCallSite ?? {}
52142
+ };
52122
52143
  }
52123
52144
  const serverHandlers = runtime.createServerEffectHandlers({
52124
52145
  persistence,
@@ -52152,8 +52173,15 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
52152
52173
  payload: payload || {},
52153
52174
  state: result.previousState
52154
52175
  };
52155
- if (binding.config) {
52156
- bindingCtx.config = binding.config;
52176
+ const declaredDefaults = runtime.collectDeclaredConfigDefaults(
52177
+ binding.trait
52178
+ );
52179
+ const callSiteConfig = binding.config;
52180
+ if (declaredDefaults || callSiteConfig) {
52181
+ bindingCtx.config = {
52182
+ ...declaredDefaults ?? {},
52183
+ ...callSiteConfig ?? {}
52184
+ };
52157
52185
  }
52158
52186
  const effectContext = {
52159
52187
  traitName: binding.trait.name,
@@ -52175,21 +52203,29 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
52175
52203
  const executor = new runtime.EffectExecutor({ handlers: trackingHandlers, bindings: bindingCtx, context: effectContext });
52176
52204
  try {
52177
52205
  await executor.executeAll(result.effects);
52178
- console.log(
52179
- "[TraitStateMachine] After executeAll, pendingSlots:",
52180
- Object.fromEntries(pendingSlots.entries())
52181
- );
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
+ });
52182
52216
  void slotSource;
52183
52217
  for (const [slot, patterns] of pendingSlots) {
52184
52218
  flushSlot(traitName, slot, patterns);
52185
52219
  }
52186
52220
  } catch (error) {
52187
- console.error(
52188
- "[TraitStateMachine] Effect execution error:",
52189
- error,
52190
- "| effects:",
52191
- JSON.stringify(result.effects)
52192
- );
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
+ });
52193
52229
  }
52194
52230
  } else if (!result.executed) {
52195
52231
  if (result.guardResult === false) {
@@ -52335,13 +52371,20 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
52335
52371
  if (eventKey === "INIT" || eventKey === "LOAD" || eventKey === "$MOUNT") {
52336
52372
  continue;
52337
52373
  }
52338
- 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) => {
52339
52377
  if (event.source && event.source.fromBridge) {
52378
+ crossTraitLog.debug("self:fire-skipped-bridge-echo", { traitName, busKey: selfBusKey, eventKey });
52340
52379
  return;
52341
52380
  }
52381
+ crossTraitLog.info("self:fire", { traitName, busKey: selfBusKey, eventKey });
52342
52382
  enqueueAndDrain(eventKey, event.payload);
52343
52383
  });
52344
- unsubscribes.push(unsub);
52384
+ unsubscribes.push(() => {
52385
+ crossTraitLog.debug("self:unsubscribe", { traitName, busKey: selfBusKey, eventKey });
52386
+ unsub();
52387
+ });
52345
52388
  }
52346
52389
  }
52347
52390
  for (const binding of traitBindings) {
@@ -52359,18 +52402,23 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
52359
52402
  const sourceOrbital = listen.source?.orbital ?? ownOrbital;
52360
52403
  if (!sourceOrbital) continue;
52361
52404
  const busKey = `UI:${sourceOrbital}.${sourceTrait}.${listen.event}`;
52362
- 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 });
52363
52406
  const unsub = eventBus.on(busKey, (event) => {
52364
52407
  crossTraitLog.info("listen:fired", { busKey, targetTrait: binding.trait.name, triggers: listen.triggers });
52365
52408
  enqueueAndDrain(listen.triggers, event.payload);
52366
52409
  });
52367
- unsubscribes.push(unsub);
52410
+ unsubscribes.push(() => {
52411
+ crossTraitLog.debug("listen:unsubscribe", { busKey, targetTrait: binding.trait.name, triggers: listen.triggers });
52412
+ unsub();
52413
+ });
52368
52414
  }
52369
52415
  }
52370
52416
  return () => {
52417
+ crossTraitLog.debug("cleanup:start", { unsubscribeCount: unsubscribes.length });
52371
52418
  for (const unsub of unsubscribes) {
52372
52419
  unsub();
52373
52420
  }
52421
+ crossTraitLog.debug("cleanup:done", {});
52374
52422
  };
52375
52423
  }, [traitBindings, eventBus, enqueueAndDrain]);
52376
52424
  return {
package/dist/avl/index.js CHANGED
@@ -43,7 +43,7 @@ import { EffectComposer, Bloom, DepthOfField, Vignette } from '@react-three/post
43
43
  import ELK from 'elkjs/lib/elk.bundled.js';
44
44
  import { MarkerType, Handle, Position, getBezierPath, EdgeLabelRenderer, BaseEdge, ReactFlowProvider, useNodesState, useEdgesState, useReactFlow, ReactFlow, Controls, Background, BackgroundVariant } from '@xyflow/react';
45
45
  import '@tanstack/react-query';
46
- import { InMemoryPersistence, StateMachineManager, createContextFromBindings, interpolateValue, createServerEffectHandlers, EffectExecutor } from '@almadar/runtime';
46
+ import { InMemoryPersistence, StateMachineManager, createContextFromBindings, interpolateValue, collectDeclaredConfigDefaults, createServerEffectHandlers, EffectExecutor } from '@almadar/runtime';
47
47
 
48
48
  var __defProp = Object.defineProperty;
49
49
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -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,
@@ -52071,8 +52085,15 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
52071
52085
  payload: payload || {},
52072
52086
  state: result.previousState
52073
52087
  };
52074
- if (binding.config) {
52075
- sharedBindings.config = binding.config;
52088
+ const sharedDeclared = collectDeclaredConfigDefaults(
52089
+ binding.trait
52090
+ );
52091
+ const sharedCallSite = binding.config;
52092
+ if (sharedDeclared || sharedCallSite) {
52093
+ sharedBindings.config = {
52094
+ ...sharedDeclared ?? {},
52095
+ ...sharedCallSite ?? {}
52096
+ };
52076
52097
  }
52077
52098
  const serverHandlers = createServerEffectHandlers({
52078
52099
  persistence,
@@ -52106,8 +52127,15 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
52106
52127
  payload: payload || {},
52107
52128
  state: result.previousState
52108
52129
  };
52109
- if (binding.config) {
52110
- bindingCtx.config = binding.config;
52130
+ const declaredDefaults = collectDeclaredConfigDefaults(
52131
+ binding.trait
52132
+ );
52133
+ const callSiteConfig = binding.config;
52134
+ if (declaredDefaults || callSiteConfig) {
52135
+ bindingCtx.config = {
52136
+ ...declaredDefaults ?? {},
52137
+ ...callSiteConfig ?? {}
52138
+ };
52111
52139
  }
52112
52140
  const effectContext = {
52113
52141
  traitName: binding.trait.name,
@@ -52129,21 +52157,29 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
52129
52157
  const executor = new EffectExecutor({ handlers: trackingHandlers, bindings: bindingCtx, context: effectContext });
52130
52158
  try {
52131
52159
  await executor.executeAll(result.effects);
52132
- console.log(
52133
- "[TraitStateMachine] After executeAll, pendingSlots:",
52134
- Object.fromEntries(pendingSlots.entries())
52135
- );
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
+ });
52136
52170
  void slotSource;
52137
52171
  for (const [slot, patterns] of pendingSlots) {
52138
52172
  flushSlot(traitName, slot, patterns);
52139
52173
  }
52140
52174
  } catch (error) {
52141
- console.error(
52142
- "[TraitStateMachine] Effect execution error:",
52143
- error,
52144
- "| effects:",
52145
- JSON.stringify(result.effects)
52146
- );
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
+ });
52147
52183
  }
52148
52184
  } else if (!result.executed) {
52149
52185
  if (result.guardResult === false) {
@@ -52289,13 +52325,20 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
52289
52325
  if (eventKey === "INIT" || eventKey === "LOAD" || eventKey === "$MOUNT") {
52290
52326
  continue;
52291
52327
  }
52292
- 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) => {
52293
52331
  if (event.source && event.source.fromBridge) {
52332
+ crossTraitLog.debug("self:fire-skipped-bridge-echo", { traitName, busKey: selfBusKey, eventKey });
52294
52333
  return;
52295
52334
  }
52335
+ crossTraitLog.info("self:fire", { traitName, busKey: selfBusKey, eventKey });
52296
52336
  enqueueAndDrain(eventKey, event.payload);
52297
52337
  });
52298
- unsubscribes.push(unsub);
52338
+ unsubscribes.push(() => {
52339
+ crossTraitLog.debug("self:unsubscribe", { traitName, busKey: selfBusKey, eventKey });
52340
+ unsub();
52341
+ });
52299
52342
  }
52300
52343
  }
52301
52344
  for (const binding of traitBindings) {
@@ -52313,18 +52356,23 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
52313
52356
  const sourceOrbital = listen.source?.orbital ?? ownOrbital;
52314
52357
  if (!sourceOrbital) continue;
52315
52358
  const busKey = `UI:${sourceOrbital}.${sourceTrait}.${listen.event}`;
52316
- 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 });
52317
52360
  const unsub = eventBus.on(busKey, (event) => {
52318
52361
  crossTraitLog.info("listen:fired", { busKey, targetTrait: binding.trait.name, triggers: listen.triggers });
52319
52362
  enqueueAndDrain(listen.triggers, event.payload);
52320
52363
  });
52321
- unsubscribes.push(unsub);
52364
+ unsubscribes.push(() => {
52365
+ crossTraitLog.debug("listen:unsubscribe", { busKey, targetTrait: binding.trait.name, triggers: listen.triggers });
52366
+ unsub();
52367
+ });
52322
52368
  }
52323
52369
  }
52324
52370
  return () => {
52371
+ crossTraitLog.debug("cleanup:start", { unsubscribeCount: unsubscribes.length });
52325
52372
  for (const unsub of unsubscribes) {
52326
52373
  unsub();
52327
52374
  }
52375
+ crossTraitLog.debug("cleanup:done", {});
52328
52376
  };
52329
52377
  }, [traitBindings, eventBus, enqueueAndDrain]);
52330
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,
@@ -38653,8 +38658,15 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
38653
38658
  payload: payload || {},
38654
38659
  state: result.previousState
38655
38660
  };
38656
- if (binding.config) {
38657
- sharedBindings.config = binding.config;
38661
+ const sharedDeclared = runtime.collectDeclaredConfigDefaults(
38662
+ binding.trait
38663
+ );
38664
+ const sharedCallSite = binding.config;
38665
+ if (sharedDeclared || sharedCallSite) {
38666
+ sharedBindings.config = {
38667
+ ...sharedDeclared ?? {},
38668
+ ...sharedCallSite ?? {}
38669
+ };
38658
38670
  }
38659
38671
  const serverHandlers = runtime.createServerEffectHandlers({
38660
38672
  persistence,
@@ -38688,8 +38700,15 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
38688
38700
  payload: payload || {},
38689
38701
  state: result.previousState
38690
38702
  };
38691
- if (binding.config) {
38692
- bindingCtx.config = binding.config;
38703
+ const declaredDefaults = runtime.collectDeclaredConfigDefaults(
38704
+ binding.trait
38705
+ );
38706
+ const callSiteConfig = binding.config;
38707
+ if (declaredDefaults || callSiteConfig) {
38708
+ bindingCtx.config = {
38709
+ ...declaredDefaults ?? {},
38710
+ ...callSiteConfig ?? {}
38711
+ };
38693
38712
  }
38694
38713
  const effectContext = {
38695
38714
  traitName: binding.trait.name,
@@ -38711,21 +38730,29 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
38711
38730
  const executor = new runtime.EffectExecutor({ handlers: trackingHandlers, bindings: bindingCtx, context: effectContext });
38712
38731
  try {
38713
38732
  await executor.executeAll(result.effects);
38714
- console.log(
38715
- "[TraitStateMachine] After executeAll, pendingSlots:",
38716
- Object.fromEntries(pendingSlots.entries())
38717
- );
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
+ });
38718
38743
  void slotSource;
38719
38744
  for (const [slot, patterns] of pendingSlots) {
38720
38745
  flushSlot(traitName, slot, patterns);
38721
38746
  }
38722
38747
  } catch (error) {
38723
- console.error(
38724
- "[TraitStateMachine] Effect execution error:",
38725
- error,
38726
- "| effects:",
38727
- JSON.stringify(result.effects)
38728
- );
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
+ });
38729
38756
  }
38730
38757
  } else if (!result.executed) {
38731
38758
  if (result.guardResult === false) {
@@ -38871,13 +38898,20 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
38871
38898
  if (eventKey === "INIT" || eventKey === "LOAD" || eventKey === "$MOUNT") {
38872
38899
  continue;
38873
38900
  }
38874
- 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) => {
38875
38904
  if (event.source && event.source.fromBridge) {
38905
+ crossTraitLog.debug("self:fire-skipped-bridge-echo", { traitName, busKey: selfBusKey, eventKey });
38876
38906
  return;
38877
38907
  }
38908
+ crossTraitLog.info("self:fire", { traitName, busKey: selfBusKey, eventKey });
38878
38909
  enqueueAndDrain(eventKey, event.payload);
38879
38910
  });
38880
- unsubscribes.push(unsub);
38911
+ unsubscribes.push(() => {
38912
+ crossTraitLog.debug("self:unsubscribe", { traitName, busKey: selfBusKey, eventKey });
38913
+ unsub();
38914
+ });
38881
38915
  }
38882
38916
  }
38883
38917
  for (const binding of traitBindings) {
@@ -38895,18 +38929,23 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
38895
38929
  const sourceOrbital = listen.source?.orbital ?? ownOrbital;
38896
38930
  if (!sourceOrbital) continue;
38897
38931
  const busKey = `UI:${sourceOrbital}.${sourceTrait}.${listen.event}`;
38898
- 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 });
38899
38933
  const unsub = eventBus.on(busKey, (event) => {
38900
38934
  crossTraitLog.info("listen:fired", { busKey, targetTrait: binding.trait.name, triggers: listen.triggers });
38901
38935
  enqueueAndDrain(listen.triggers, event.payload);
38902
38936
  });
38903
- unsubscribes.push(unsub);
38937
+ unsubscribes.push(() => {
38938
+ crossTraitLog.debug("listen:unsubscribe", { busKey, targetTrait: binding.trait.name, triggers: listen.triggers });
38939
+ unsub();
38940
+ });
38904
38941
  }
38905
38942
  }
38906
38943
  return () => {
38944
+ crossTraitLog.debug("cleanup:start", { unsubscribeCount: unsubscribes.length });
38907
38945
  for (const unsub of unsubscribes) {
38908
38946
  unsub();
38909
38947
  }
38948
+ crossTraitLog.debug("cleanup:done", {});
38910
38949
  };
38911
38950
  }, [traitBindings, eventBus, enqueueAndDrain]);
38912
38951
  return {
@@ -36,7 +36,7 @@ import langGo from 'react-syntax-highlighter/dist/esm/languages/prism/go.js';
36
36
  import langGraphql from 'react-syntax-highlighter/dist/esm/languages/prism/graphql.js';
37
37
  import { isCircuitEvent, schemaToIR, getPage, clearSchemaCache as clearSchemaCache$1, isEntityCall, isInlineTrait } from '@almadar/core';
38
38
  import '@tanstack/react-query';
39
- import { StateMachineManager, createContextFromBindings, interpolateValue, createServerEffectHandlers, EffectExecutor, InMemoryPersistence } from '@almadar/runtime';
39
+ import { StateMachineManager, createContextFromBindings, interpolateValue, collectDeclaredConfigDefaults, createServerEffectHandlers, EffectExecutor, InMemoryPersistence } from '@almadar/runtime';
40
40
  import { OrbitalServerRuntime } from '@almadar/runtime/OrbitalServerRuntime';
41
41
 
42
42
  var __defProp = Object.defineProperty;
@@ -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,
@@ -38608,8 +38613,15 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
38608
38613
  payload: payload || {},
38609
38614
  state: result.previousState
38610
38615
  };
38611
- if (binding.config) {
38612
- sharedBindings.config = binding.config;
38616
+ const sharedDeclared = collectDeclaredConfigDefaults(
38617
+ binding.trait
38618
+ );
38619
+ const sharedCallSite = binding.config;
38620
+ if (sharedDeclared || sharedCallSite) {
38621
+ sharedBindings.config = {
38622
+ ...sharedDeclared ?? {},
38623
+ ...sharedCallSite ?? {}
38624
+ };
38613
38625
  }
38614
38626
  const serverHandlers = createServerEffectHandlers({
38615
38627
  persistence,
@@ -38643,8 +38655,15 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
38643
38655
  payload: payload || {},
38644
38656
  state: result.previousState
38645
38657
  };
38646
- if (binding.config) {
38647
- bindingCtx.config = binding.config;
38658
+ const declaredDefaults = collectDeclaredConfigDefaults(
38659
+ binding.trait
38660
+ );
38661
+ const callSiteConfig = binding.config;
38662
+ if (declaredDefaults || callSiteConfig) {
38663
+ bindingCtx.config = {
38664
+ ...declaredDefaults ?? {},
38665
+ ...callSiteConfig ?? {}
38666
+ };
38648
38667
  }
38649
38668
  const effectContext = {
38650
38669
  traitName: binding.trait.name,
@@ -38666,21 +38685,29 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
38666
38685
  const executor = new EffectExecutor({ handlers: trackingHandlers, bindings: bindingCtx, context: effectContext });
38667
38686
  try {
38668
38687
  await executor.executeAll(result.effects);
38669
- console.log(
38670
- "[TraitStateMachine] After executeAll, pendingSlots:",
38671
- Object.fromEntries(pendingSlots.entries())
38672
- );
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
+ });
38673
38698
  void slotSource;
38674
38699
  for (const [slot, patterns] of pendingSlots) {
38675
38700
  flushSlot(traitName, slot, patterns);
38676
38701
  }
38677
38702
  } catch (error) {
38678
- console.error(
38679
- "[TraitStateMachine] Effect execution error:",
38680
- error,
38681
- "| effects:",
38682
- JSON.stringify(result.effects)
38683
- );
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
+ });
38684
38711
  }
38685
38712
  } else if (!result.executed) {
38686
38713
  if (result.guardResult === false) {
@@ -38826,13 +38853,20 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
38826
38853
  if (eventKey === "INIT" || eventKey === "LOAD" || eventKey === "$MOUNT") {
38827
38854
  continue;
38828
38855
  }
38829
- 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) => {
38830
38859
  if (event.source && event.source.fromBridge) {
38860
+ crossTraitLog.debug("self:fire-skipped-bridge-echo", { traitName, busKey: selfBusKey, eventKey });
38831
38861
  return;
38832
38862
  }
38863
+ crossTraitLog.info("self:fire", { traitName, busKey: selfBusKey, eventKey });
38833
38864
  enqueueAndDrain(eventKey, event.payload);
38834
38865
  });
38835
- unsubscribes.push(unsub);
38866
+ unsubscribes.push(() => {
38867
+ crossTraitLog.debug("self:unsubscribe", { traitName, busKey: selfBusKey, eventKey });
38868
+ unsub();
38869
+ });
38836
38870
  }
38837
38871
  }
38838
38872
  for (const binding of traitBindings) {
@@ -38850,18 +38884,23 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
38850
38884
  const sourceOrbital = listen.source?.orbital ?? ownOrbital;
38851
38885
  if (!sourceOrbital) continue;
38852
38886
  const busKey = `UI:${sourceOrbital}.${sourceTrait}.${listen.event}`;
38853
- 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 });
38854
38888
  const unsub = eventBus.on(busKey, (event) => {
38855
38889
  crossTraitLog.info("listen:fired", { busKey, targetTrait: binding.trait.name, triggers: listen.triggers });
38856
38890
  enqueueAndDrain(listen.triggers, event.payload);
38857
38891
  });
38858
- unsubscribes.push(unsub);
38892
+ unsubscribes.push(() => {
38893
+ crossTraitLog.debug("listen:unsubscribe", { busKey, targetTrait: binding.trait.name, triggers: listen.triggers });
38894
+ unsub();
38895
+ });
38859
38896
  }
38860
38897
  }
38861
38898
  return () => {
38899
+ crossTraitLog.debug("cleanup:start", { unsubscribeCount: unsubscribes.length });
38862
38900
  for (const unsub of unsubscribes) {
38863
38901
  unsub();
38864
38902
  }
38903
+ crossTraitLog.debug("cleanup:done", {});
38865
38904
  };
38866
38905
  }, [traitBindings, eventBus, enqueueAndDrain]);
38867
38906
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@almadar/ui",
3
- "version": "4.15.3",
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",
@@ -118,10 +118,10 @@
118
118
  "access": "public"
119
119
  },
120
120
  "dependencies": {
121
- "@almadar/core": "^7.0.0",
121
+ "@almadar/core": "^7.5.1",
122
122
  "@almadar/evaluator": ">=2.9.2",
123
123
  "@almadar/patterns": ">=2.17.1",
124
- "@almadar/runtime": "^5.7.0",
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",