@almadar/runtime 6.33.0 → 6.35.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  import { Router } from 'express';
2
- import { I as IEventBus, R as RuntimeEvent, a as EventListener, U as Unsubscribe, T as TraitDefinition, b as RuntimeConfig, c as TransitionObserver, C as ConfigContext, d as TraitState, e as TransitionResult, f as EvaluationContextExtensions, E as EffectHandlers } from './types-ConZnrpe.js';
3
- import { EventPayload, EntityRow, Orbital, ServiceCallResult, TraitConfig, DeclaredTraitConfig, Entity, OrbitalSchema, Trait, PatternConfig, ResolvedPatternProps, SExpr, BusEventSource, OrbitalDefinition, TraitTick } from '@almadar/core';
2
+ import { I as IEventBus, R as RuntimeEvent, a as EventListener, U as Unsubscribe, T as TraitDefinition, b as RuntimeConfig, c as TransitionObserver, d as TraitState, C as ConfigContext, e as TransitionResult, f as EvaluationContextExtensions, E as EffectHandlers } from './types-C8RsO0xa.js';
3
+ import { EventPayload, EventId, EntityRow, Orbital, ServiceCallResult, TraitConfig, DeclaredTraitConfig, Entity, OrbitalSchema, Trait, PatternConfig, ResolvedPatternProps, SExpr, BusEventSource, OrbitalDefinition, TraitTick } from '@almadar/core';
4
4
 
5
5
  /**
6
6
  * EventBus - Platform-Agnostic Pub/Sub Implementation
@@ -47,7 +47,7 @@ declare class EventBus implements IEventBus {
47
47
  * beyond `maxDepth`, the event is dropped and an error is logged.
48
48
  * This prevents infinite loops from circular emit/listen chains.
49
49
  */
50
- emit(type: string, payload?: EventPayload, source?: RuntimeEvent['source']): void;
50
+ emit(type: string, payload?: EventPayload, source?: RuntimeEvent['source'], routingKey?: string): void;
51
51
  /**
52
52
  * Subscribe to an event type
53
53
  */
@@ -101,7 +101,7 @@ declare function createInitialTraitState(trait: TraitDefinition): TraitState;
101
101
  * when multiple sibling transitions share the same `from`/`event` and
102
102
  * disambiguate by guard.
103
103
  */
104
- declare function findTransition(trait: TraitDefinition, currentState: string, eventKey: string): TraitDefinition['transitions'][0] | undefined;
104
+ declare function findTransition(trait: TraitDefinition, currentState: string, eventKey: string, eventId?: EventId): TraitDefinition['transitions'][0] | undefined;
105
105
  /**
106
106
  * Normalize event key - strip UI: prefix if present.
107
107
  */
@@ -116,6 +116,12 @@ interface ProcessEventOptions {
116
116
  trait: TraitDefinition;
117
117
  /** Event key to process */
118
118
  eventKey: string;
119
+ /**
120
+ * V4 dual-carry id sibling of `eventKey` — the id of the fired event,
121
+ * when known. Threaded to `findMatchingTransitions` for id-primary
122
+ * matching; absent means name-only dispatch (legacy).
123
+ */
124
+ eventId?: EventId;
119
125
  /** Event payload */
120
126
  payload?: EventPayload;
121
127
  /** Entity data for binding resolution */
@@ -179,6 +185,15 @@ interface ProcessEventOptions {
179
185
  declare function processEvent(options: ProcessEventOptions): TransitionResult;
180
186
  declare class StateMachineManager {
181
187
  private traits;
188
+ /**
189
+ * V4 identity index: trait id → trait name. Populated for traits that
190
+ * carry an `id` (ledger-backed schemas). Lets callers resolve a trait's
191
+ * state by its stable id, which survives a mid-session rename — the
192
+ * name-keyed maps are re-pointed on rename via {@link renameTrait}, but
193
+ * an id holder never has to observe the rename at all. Empty for legacy
194
+ * id-free schemas, where every lookup stays name-keyed (unchanged).
195
+ */
196
+ private traitIdToName;
182
197
  /**
183
198
  * Per-trait call-site config, surfaced to guard expressions so
184
199
  * `@config.X` resolves at runtime. Populated by the orbital's
@@ -217,6 +232,26 @@ declare class StateMachineManager {
217
232
  * Add a trait to the manager.
218
233
  */
219
234
  addTrait(trait: TraitDefinition): void;
235
+ /**
236
+ * Resolve a trait by its V4 id. Returns undefined when no trait carries
237
+ * that id (legacy schema, or unknown id). Exact-match only — no name
238
+ * similarity.
239
+ */
240
+ getTraitById(traitId: string): TraitDefinition | undefined;
241
+ /**
242
+ * Get a trait's current state by its V4 id (id-first lookup). Falls back
243
+ * to `undefined` when the id is unknown. The id survives a rename, so a
244
+ * holder of the id reads the right state without ever seeing the new name.
245
+ */
246
+ getStateById(traitId: string, entityId?: string): TraitState | undefined;
247
+ /**
248
+ * Apply a mid-session trait rename: re-point the name-keyed maps from
249
+ * `oldName` to `newName`, preserving live state, queues, and the id
250
+ * index. A no-op when the trait isn't registered under `oldName`. This
251
+ * is the interpreter-side of a ledger `curName` edit for the trait's own
252
+ * name-keyed storage; id-keyed references need no update.
253
+ */
254
+ renameTrait(oldName: string, newName: string): void;
220
255
  /**
221
256
  * Bind the call-site config for a trait so guard `@config.X`
222
257
  * resolves at runtime. Typically called by the orbital
@@ -256,7 +291,7 @@ declare class StateMachineManager {
256
291
  /**
257
292
  * Check if a trait can handle an event from its current state.
258
293
  */
259
- canHandleEvent(traitName: string, eventKey: string, entityId?: string): boolean;
294
+ canHandleEvent(traitName: string, eventKey: string, entityId?: string, eventId?: EventId): boolean;
260
295
  /**
261
296
  * Send an event to all traits.
262
297
  *
@@ -267,9 +302,13 @@ declare class StateMachineManager {
267
302
  * `@entity.X` see prior step writes — required for [runtime] entities
268
303
  * that have no persistence row to reload.
269
304
  *
305
+ * `eventId` is the V4 dual-carry id sibling of `eventKey` (see
306
+ * {@link ProcessEventOptions.eventId}) — additive and optional, threaded
307
+ * through to `processEvent` for id-primary transition matching.
308
+ *
270
309
  * @returns Array of transition results (one per trait that had a matching transition)
271
310
  */
272
- sendEvent(eventKey: string, payload?: EventPayload, entityData?: EntityRow, entityByTrait?: Record<string, EntityRow>): Array<{
311
+ sendEvent(eventKey: string, payload?: EventPayload, entityData?: EntityRow, entityByTrait?: Record<string, EntityRow>, eventId?: EventId, targetTrait?: string): Array<{
273
312
  traitName: string;
274
313
  result: TransitionResult;
275
314
  }>;
@@ -280,7 +319,7 @@ declare class StateMachineManager {
280
319
  * trait to process them sequentially (actor-model guarantee: one event
281
320
  * at a time per trait, effects fully awaited before the next event).
282
321
  */
283
- enqueueEvent(eventKey: string, payload?: EventPayload, entityData?: EntityRow, entityByTrait?: Record<string, EntityRow>): void;
322
+ enqueueEvent(eventKey: string, payload?: EventPayload, entityData?: EntityRow, entityByTrait?: Record<string, EntityRow>, eventId?: EventId): void;
284
323
  /**
285
324
  * Drain a single (trait, entity) pair's event queue, processing
286
325
  * events sequentially. Pass `entityId` to drain a specific entity
@@ -898,8 +937,21 @@ interface RegisteredOrbital {
898
937
  */
899
938
  interface OrbitalEventRequest {
900
939
  event: string;
940
+ /**
941
+ * V4 dual-carry id sibling of `event` — the fired event's id, when known
942
+ * (e.g. threaded from a `listens[].triggersId`). Optional; absent means
943
+ * the state machine dispatches by name only (legacy).
944
+ */
945
+ eventId?: EventId;
901
946
  payload?: EventPayload;
902
947
  entityId?: string;
948
+ /**
949
+ * Scoped-listen delivery: dispatch to THIS trait only. A listens-matched
950
+ * trigger is addressed to the listening trait; without this, a trigger
951
+ * renamed to INIT broadcast orbital-wide and re-ran every trait's
952
+ * initializer (R-SCOPED-LISTEN-INIT-RENAME-FREEZE re-fire cascade).
953
+ */
954
+ targetTrait?: string;
903
955
  /** User context for @user bindings (from Firebase auth) */
904
956
  user?: {
905
957
  uid: string;
@@ -1,4 +1,4 @@
1
1
  import 'express';
2
- export { F as ClientEffectTuple, G as ClientNavigateTuple, H as ClientNotifyTuple, J as ClientRenderUITuple, K as EffectResult, e as InMemoryPersistence, M as LoaderConfig, O as OrbitalEventRequest, f as OrbitalEventResponse, N as OrbitalServerRuntime, g as OrbitalServerRuntimeConfig, P as PersistenceAdapter, R as RegisteredOrbital, l as RuntimeOrbital, m as RuntimeOrbitalSchema, n as RuntimeTrait, Q as RuntimeTraitTick, p as collectDeclaredConfigDefaults, T as createOrbitalServerRuntime } from './OrbitalServerRuntime-Cep380it.js';
3
- import './types-ConZnrpe.js';
2
+ export { F as ClientEffectTuple, G as ClientNavigateTuple, H as ClientNotifyTuple, J as ClientRenderUITuple, K as EffectResult, e as InMemoryPersistence, M as LoaderConfig, O as OrbitalEventRequest, f as OrbitalEventResponse, N as OrbitalServerRuntime, g as OrbitalServerRuntimeConfig, P as PersistenceAdapter, R as RegisteredOrbital, l as RuntimeOrbital, m as RuntimeOrbitalSchema, n as RuntimeTrait, Q as RuntimeTraitTick, p as collectDeclaredConfigDefaults, T as createOrbitalServerRuntime } from './OrbitalServerRuntime-BY-qzEQ_.js';
3
+ import './types-C8RsO0xa.js';
4
4
  import '@almadar/core';
@@ -1,5 +1,5 @@
1
- import { createTickScheduler, EventBus, createUnifiedLoader, MockPersistenceAdapter, InMemoryPersistence, preprocessSchema, normalizeCallSiteConfigToValues, StateMachineManager, parseDurationString, createContextFromBindings, validateEventPayload, formatPayloadValidationError, collectDeclaredConfigDefaults, resolveCallSitePayloadCaptures, collectDeclaredEntityDefaults, EffectExecutor } from './chunk-JZDLN2FS.js';
2
- export { InMemoryPersistence, collectDeclaredConfigDefaults } from './chunk-JZDLN2FS.js';
1
+ import { createTickScheduler, EventBus, createUnifiedLoader, MockPersistenceAdapter, InMemoryPersistence, preprocessSchema, normalizeCallSiteConfigToValues, StateMachineManager, parseDurationString, createContextFromBindings, validateEventPayload, formatPayloadValidationError, collectDeclaredConfigDefaults, resolveCallSitePayloadCaptures, collectDeclaredEntityDefaults, EffectExecutor } from './chunk-VCEF5JOX.js';
2
+ export { InMemoryPersistence, collectDeclaredConfigDefaults } from './chunk-VCEF5JOX.js';
3
3
  import { isValidCronExpression } from './chunk-OU3ITB5S.js';
4
4
  import './chunk-OQJIK6PZ.js';
5
5
  import './chunk-SCRAHWOC.js';
@@ -9,6 +9,29 @@ import * as nodeModule from 'module';
9
9
  import { evaluateGuard, evaluate } from '@almadar/evaluator';
10
10
  import { buildResolvedTraitConfigs, isInlineTrait, isEntityCall } from '@almadar/core';
11
11
 
12
+ // src/identity/routing.ts
13
+ function eventRouteKey(eventName, eventId) {
14
+ return eventId ? `@evt:${eventId}` : eventName;
15
+ }
16
+ function buildSourceMatcher(src, listenerOrbital) {
17
+ if (src.kind === "any") return () => true;
18
+ if (src.kind === "trait") {
19
+ if (src.traitId !== void 0) {
20
+ const wantedTraitId = src.traitId;
21
+ return (source) => !!source && source.traitId === wantedTraitId;
22
+ }
23
+ const wantedTrait2 = src.trait;
24
+ return (source) => !!source && source.orbital === listenerOrbital && source.trait === wantedTrait2;
25
+ }
26
+ if (src.orbitalId !== void 0 && src.traitId !== void 0) {
27
+ const wantedOrbitalId = src.orbitalId;
28
+ const wantedTraitId = src.traitId;
29
+ return (source) => !!source && source.orbitalId === wantedOrbitalId && source.traitId === wantedTraitId;
30
+ }
31
+ const wantedOrbital = src.orbital;
32
+ const wantedTrait = src.trait;
33
+ return (source) => !!source && source.orbital === wantedOrbital && source.trait === wantedTrait;
34
+ }
12
35
  var _resolvedNodeRequire = null;
13
36
  function nodeRequire(modulePath) {
14
37
  if (!_resolvedNodeRequire) {
@@ -454,6 +477,9 @@ var OrbitalServerRuntime = class {
454
477
  const states = sm?.states || [];
455
478
  const transitions = sm?.transitions || [];
456
479
  return {
480
+ // V4 dual-carry: thread the trait id so the manager's id index is
481
+ // populated for ledger-backed schemas (undefined → name-keyed only).
482
+ ...t.id !== void 0 ? { id: t.id } : {},
457
483
  name: t.name,
458
484
  states,
459
485
  transitions,
@@ -521,7 +547,7 @@ var OrbitalServerRuntime = class {
521
547
  const fields = entity.fields.filter(
522
548
  (f) => typeof f.name === "string" && f.name.length > 0
523
549
  );
524
- this.persistence.registerEntity({ name: entity.name, fields });
550
+ this.persistence.registerEntity({ name: entity.name, id: entity.id, fields });
525
551
  if (this.config.debug) {
526
552
  persistLog.debug("mock:seeded", { entity: entity.name, count: this.persistence.count(entity.name) });
527
553
  }
@@ -536,7 +562,7 @@ var OrbitalServerRuntime = class {
536
562
  const auxFields = auxEntity.fields.filter(
537
563
  (f) => typeof f.name === "string" && f.name.length > 0
538
564
  );
539
- this.persistence.registerEntity({ name: auxEntity.name, fields: auxFields });
565
+ this.persistence.registerEntity({ name: auxEntity.name, id: auxEntity.id, fields: auxFields });
540
566
  if (this.config.debug) {
541
567
  persistLog.debug("mock:seeded-auxiliary", {
542
568
  entity: auxEntity.name,
@@ -576,7 +602,8 @@ var OrbitalServerRuntime = class {
576
602
  if (!trait.listens) continue;
577
603
  for (const listener of trait.listens) {
578
604
  const { bareEvent, matcher } = parseListenSource(listener, orbitalName);
579
- const cleanup = this.eventBus.on(bareEvent, async (event) => {
605
+ const routeKey = eventRouteKey(bareEvent, listener.eventId);
606
+ const cleanup = this.eventBus.on(routeKey, async (event) => {
580
607
  if (!matcher(event.source)) return;
581
608
  if (this.config.debug) {
582
609
  xOrbitalLog.debug("listen:received", () => ({
@@ -607,8 +634,10 @@ var OrbitalServerRuntime = class {
607
634
  const forwardedEntityId = pickId("entityId") ?? pickId("orbitalName");
608
635
  await this.processOrbitalEvent(orbitalName, {
609
636
  event: listener.triggers,
637
+ eventId: listener.triggersId,
610
638
  payload: mappedPayload,
611
- entityId: forwardedEntityId
639
+ entityId: forwardedEntityId,
640
+ targetTrait: trait.name
612
641
  });
613
642
  });
614
643
  this.listenerCleanups.push(cleanup);
@@ -812,7 +841,7 @@ var OrbitalServerRuntime = class {
812
841
  const fields = entity.fields.filter(
813
842
  (f) => typeof f.name === "string" && f.name.length > 0
814
843
  );
815
- this.persistence.registerEntity({ name: entity.name, fields });
844
+ this.persistence.registerEntity({ name: entity.name, id: entity.id, fields });
816
845
  }
817
846
  }
818
847
  }
@@ -871,7 +900,8 @@ var OrbitalServerRuntime = class {
871
900
  request.payload?.["_activeTraits"] ?? null
872
901
  )
873
902
  }));
874
- const { event, payload, entityId, user } = request;
903
+ const { event, eventId, payload, entityId, user } = request;
904
+ const targetTrait = request.targetTrait ?? payload?.["_targetTrait"];
875
905
  const validationFailures = [];
876
906
  for (const trait of registered.traits) {
877
907
  const eventSchema = trait.stateMachine?.events?.find((e) => e.key === event);
@@ -899,6 +929,7 @@ var OrbitalServerRuntime = class {
899
929
  const cleanPayload = payload ? { ...payload } : void 0;
900
930
  if (cleanPayload) {
901
931
  delete cleanPayload._activeTraits;
932
+ delete cleanPayload._targetTrait;
902
933
  }
903
934
  let entityData = {};
904
935
  if (entityId) {
@@ -920,7 +951,9 @@ var OrbitalServerRuntime = class {
920
951
  event,
921
952
  cleanPayload,
922
953
  entityData,
923
- entityByTrait
954
+ entityByTrait,
955
+ eventId,
956
+ targetTrait
924
957
  );
925
958
  const filteredResults = activeTraits && activeTraits.length > 0 ? results.filter(({ traitName }) => activeTraits.includes(traitName)) : results;
926
959
  if (this.config.debug && activeTraits) {
@@ -1008,11 +1041,22 @@ var OrbitalServerRuntime = class {
1008
1041
  sourceTrait: source?.trait
1009
1042
  }));
1010
1043
  }
1044
+ const emittingTrait = registered.traits.find((t) => t.name === traitName);
1045
+ const emitContract = emittingTrait?.emits?.find((e) => e.event === event);
1011
1046
  const stamp = source ?? {
1012
1047
  orbital: registered.schema.name,
1013
1048
  trait: traitName
1014
1049
  };
1015
- this.eventBus.emit(event, eventPayload, stamp);
1050
+ if (stamp.orbitalId === void 0 && registered.schema.id !== void 0) {
1051
+ stamp.orbitalId = registered.schema.id;
1052
+ }
1053
+ if (stamp.traitId === void 0 && emittingTrait?.id !== void 0) {
1054
+ stamp.traitId = emittingTrait.id;
1055
+ }
1056
+ if (stamp.eventId === void 0 && emitContract?.eventId !== void 0) {
1057
+ stamp.eventId = emitContract.eventId;
1058
+ }
1059
+ this.eventBus.emit(event, eventPayload, stamp, eventRouteKey(event, stamp.eventId));
1016
1060
  const emittedItem = { event, payload: eventPayload, source: stamp };
1017
1061
  emittedEvents.push(emittedItem);
1018
1062
  onPush?.({ type: "event", data: emittedItem });
@@ -1683,7 +1727,15 @@ var OrbitalServerRuntime = class {
1683
1727
  continue;
1684
1728
  }
1685
1729
  const foreignKeyField = relationField.name;
1686
- const relatedEntityType = relationField.relation.entity;
1730
+ let relatedEntityType = relationField.relation.entity;
1731
+ if (relationField.relation.entityId) {
1732
+ for (const registered of this.orbitals.values()) {
1733
+ if (registered.entity.id === relationField.relation.entityId) {
1734
+ relatedEntityType = registered.entity.name;
1735
+ break;
1736
+ }
1737
+ }
1738
+ }
1687
1739
  const cardinality = relationField.relation.cardinality || "one";
1688
1740
  const foreignKeyIds = /* @__PURE__ */ new Set();
1689
1741
  for (const entity of entities) {
@@ -1921,7 +1973,7 @@ function parseListenSource(listener, listenerOrbital) {
1921
1973
  if (explicit && typeof explicit === "object") {
1922
1974
  return {
1923
1975
  bareEvent: listener.event,
1924
- matcher: buildMatcher(explicit, listenerOrbital)
1976
+ matcher: buildSourceMatcher(explicit, listenerOrbital)
1925
1977
  };
1926
1978
  }
1927
1979
  const key = listener.event;
@@ -1936,7 +1988,7 @@ function parseListenSource(listener, listenerOrbital) {
1936
1988
  }
1937
1989
  return {
1938
1990
  bareEvent: eventName,
1939
- matcher: buildMatcher(
1991
+ matcher: buildSourceMatcher(
1940
1992
  { kind: "trait", trait: sourceOrStar },
1941
1993
  listenerOrbital
1942
1994
  )
@@ -1948,20 +2000,10 @@ function parseListenSource(listener, listenerOrbital) {
1948
2000
  const orbital = parts.slice(0, parts.length - 2).join(".");
1949
2001
  return {
1950
2002
  bareEvent: eventName,
1951
- matcher: buildMatcher({ kind: "orbital", orbital, trait }, listenerOrbital)
2003
+ matcher: buildSourceMatcher({ kind: "orbital", orbital, trait }, listenerOrbital)
1952
2004
  };
1953
2005
  }
1954
2006
  return { bareEvent: key, matcher: () => true };
1955
2007
  }
1956
- function buildMatcher(src, listenerOrbital) {
1957
- if (src.kind === "any") return () => true;
1958
- if (src.kind === "trait") {
1959
- const wantedTrait2 = src.trait;
1960
- return (source) => !!source && source.orbital === listenerOrbital && source.trait === wantedTrait2;
1961
- }
1962
- const wantedOrbital = src.orbital;
1963
- const wantedTrait = src.trait;
1964
- return (source) => !!source && source.orbital === wantedOrbital && source.trait === wantedTrait;
1965
- }
1966
2008
 
1967
2009
  export { OrbitalServerRuntime, createOrbitalServerRuntime };
@@ -1,4 +1,4 @@
1
- import { I as IEventBus } from './types-ConZnrpe.js';
1
+ import { I as IEventBus } from './types-C8RsO0xa.js';
2
2
  import { EventPayload } from '@almadar/core';
3
3
 
4
4
  /**