@almadar/runtime 2.5.0 → 2.6.1

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.
package/dist/index.d.ts CHANGED
@@ -1,11 +1,11 @@
1
- import { B as BindingContext, T as TraitState, a as TraitDefinition, R as RuntimeConfig, b as TransitionObserver, c as TransitionResult, E as EffectHandlers, d as EffectContext, e as ExecutionEnvironment, f as EffectResult } from './types-BrbvZxzX.js';
2
- export { g as Effect, h as EventListener, H as HANDLER_MANIFEST, I as IEventBus, i as RuntimeEvent, U as Unsubscribe } from './types-BrbvZxzX.js';
3
- export { E as EntitySharingMap, a as EventBus, b as EventNamespaceMap, O as OrbitalEventRequest, c as OrbitalEventResponse, d as OrbitalServerRuntimeConfig, P as PersistenceAdapter, e as PreprocessOptions, f as PreprocessResult, g as PreprocessedSchema, R as RuntimeOrbital, h as RuntimeOrbitalSchema, i as RuntimeTrait, j as getIsolatedCollectionName, k as getNamespacedEvent, l as isNamespacedEvent, p as parseNamespacedEvent, m as preprocessSchema } from './OrbitalServerRuntime-Bel-jQ_o.js';
1
+ import { B as BindingContext, P as PatternProps, E as EffectHandlers, a as EffectContext, b as ExecutionEnvironment, c as EffectResult, T as TraitDefinition } from './types-DYcUvi4H.js';
2
+ export { d as Effect, e as EventListener, H as HANDLER_MANIFEST, I as IEventBus, R as RuntimeConfig, f as RuntimeEvent, g as TraitState, h as TransitionObserver, i as TransitionResult, U as Unsubscribe } from './types-DYcUvi4H.js';
3
+ export { E as EntitySharingMap, a as EventBus, b as EventNamespaceMap, O as OrbitalEventRequest, c as OrbitalEventResponse, d as OrbitalServerRuntimeConfig, P as PersistenceAdapter, e as PreprocessOptions, f as PreprocessResult, g as PreprocessedSchema, h as ProcessEventOptions, R as RegisteredOrbital, i as RuntimeOrbital, j as RuntimeOrbitalSchema, k as RuntimeTrait, S as StateMachineManager, l as createInitialTraitState, m as findInitialState, n as findTransition, o as getIsolatedCollectionName, p as getNamespacedEvent, q as isNamespacedEvent, r as normalizeEventKey, s as parseNamespacedEvent, t as preprocessSchema, u as processEvent } from './OrbitalServerRuntime-B-QeKtKd.js';
4
4
  import { EvaluationContext } from '@almadar/evaluator';
5
5
  export { EvaluationContext, createMinimalContext } from '@almadar/evaluator';
6
+ import { EventPayload, EntityRow } from '@almadar/core';
6
7
  export { ServerBridgeConfig, ServerBridgeState } from './ServerBridge.js';
7
8
  import 'express';
8
- import '@almadar/core';
9
9
 
10
10
  /**
11
11
  * BindingResolver - Platform-Agnostic Binding Resolution
@@ -36,7 +36,7 @@ import '@almadar/core';
36
36
  * // { title: 'Project Alpha', total: 52 }
37
37
  * ```
38
38
  */
39
- declare function interpolateProps(props: Record<string, unknown>, ctx: EvaluationContext): Record<string, unknown>;
39
+ declare function interpolateProps(props: PatternProps, ctx: EvaluationContext): PatternProps;
40
40
  /**
41
41
  * Interpolate a single value.
42
42
  */
@@ -57,168 +57,6 @@ declare function extractBindings(value: unknown): string[];
57
57
  */
58
58
  declare function createContextFromBindings(bindings: BindingContext, strictBindings?: boolean): EvaluationContext;
59
59
 
60
- /**
61
- * StateMachineCore - Platform-Agnostic State Machine Logic
62
- *
63
- * Pure TypeScript implementation of trait state machine execution.
64
- * Extracts the core logic from useTraitStateMachine for use on
65
- * both client and server.
66
- *
67
- * @packageDocumentation
68
- */
69
-
70
- /**
71
- * Find the initial state for a trait definition.
72
- */
73
- declare function findInitialState(trait: TraitDefinition): string;
74
- /**
75
- * Create initial trait state for a trait definition.
76
- */
77
- declare function createInitialTraitState(trait: TraitDefinition): TraitState;
78
- /**
79
- * Find a matching transition from the current state for the given event.
80
- */
81
- declare function findTransition(trait: TraitDefinition, currentState: string, eventKey: string): TraitDefinition['transitions'][0] | undefined;
82
- /**
83
- * Normalize event key - strip UI: prefix if present.
84
- */
85
- declare function normalizeEventKey(eventKey: string): string;
86
- /**
87
- * Options for processing an event through the state machine.
88
- */
89
- interface ProcessEventOptions {
90
- /** Current trait state */
91
- traitState: TraitState;
92
- /** Trait definition */
93
- trait: TraitDefinition;
94
- /** Event key to process */
95
- eventKey: string;
96
- /** Event payload */
97
- payload?: Record<string, unknown>;
98
- /** Entity data for binding resolution */
99
- entityData?: Record<string, unknown>;
100
- /**
101
- * Guard evaluation error handling mode. (RCG-02)
102
- * - "permissive": Guard errors allow the transition (default, backwards-compatible)
103
- * - "strict": Guard errors block the transition
104
- */
105
- guardMode?: "strict" | "permissive";
106
- /**
107
- * When true, log warnings when bindings resolve to undefined. (RCG-01)
108
- */
109
- strictBindings?: boolean;
110
- }
111
- /**
112
- * Process an event through a trait's state machine.
113
- *
114
- * This is a pure function that:
115
- * 1. Finds matching transitions
116
- * 2. Evaluates guards
117
- * 3. Returns the transition result (but does not execute effects)
118
- *
119
- * @returns TransitionResult with effects to execute
120
- *
121
- * @example
122
- * ```ts
123
- * const result = processEvent({
124
- * traitState: { traitName: 'Cart', currentState: 'empty', ... },
125
- * trait: cartTraitDefinition,
126
- * eventKey: 'ADD_ITEM',
127
- * payload: { productId: '123' },
128
- * });
129
- *
130
- * if (result.executed) {
131
- * // Execute effects
132
- * for (const effect of result.effects) {
133
- * effectExecutor.execute(effect);
134
- * }
135
- * // Update state
136
- * traitState.currentState = result.newState;
137
- * }
138
- * ```
139
- */
140
- declare function processEvent(options: ProcessEventOptions): TransitionResult;
141
- declare class StateMachineManager {
142
- private traits;
143
- private states;
144
- private config;
145
- private observer?;
146
- private queues;
147
- private processing;
148
- constructor(traits?: TraitDefinition[], config?: RuntimeConfig, observer?: TransitionObserver);
149
- /**
150
- * Set the transition observer for runtime verification.
151
- * Wire this to `verificationRegistry.recordTransition()` to enable
152
- * automatic verification tracking.
153
- */
154
- setObserver(observer: TransitionObserver): void;
155
- /**
156
- * Add a trait to the manager.
157
- */
158
- addTrait(trait: TraitDefinition): void;
159
- /**
160
- * Remove a trait from the manager.
161
- */
162
- removeTrait(traitName: string): void;
163
- /**
164
- * Get current state for a trait.
165
- */
166
- getState(traitName: string): TraitState | undefined;
167
- /**
168
- * Get all current states.
169
- */
170
- getAllStates(): Map<string, TraitState>;
171
- /**
172
- * Check if a trait can handle an event from its current state.
173
- */
174
- canHandleEvent(traitName: string, eventKey: string): boolean;
175
- /**
176
- * Send an event to all traits.
177
- *
178
- * @returns Array of transition results (one per trait that had a matching transition)
179
- */
180
- sendEvent(eventKey: string, payload?: Record<string, unknown>, entityData?: Record<string, unknown>): Array<{
181
- traitName: string;
182
- result: TransitionResult;
183
- }>;
184
- /**
185
- * Enqueue an event into every trait's per-trait queue.
186
- *
187
- * Events are not processed immediately. Call `drainQueue()` for each
188
- * trait to process them sequentially (actor-model guarantee: one event
189
- * at a time per trait, effects fully awaited before the next event).
190
- */
191
- enqueueEvent(eventKey: string, payload?: Record<string, unknown>, entityData?: Record<string, unknown>): void;
192
- /**
193
- * Drain a single trait's event queue, processing events sequentially.
194
- *
195
- * This is the core actor loop: each event is fully processed (including
196
- * awaiting all effects) before the next event is dequeued. If the queue
197
- * is already being drained for this trait, this call is a no-op (the
198
- * running drain will pick up newly enqueued events).
199
- *
200
- * @param traitName - Which trait's queue to drain
201
- * @param executeEffects - Async callback to run effects for a successful transition
202
- */
203
- drainQueue(traitName: string, executeEffects: (traitName: string, result: TransitionResult, payload?: Record<string, unknown>) => Promise<void>): Promise<void>;
204
- /**
205
- * Check whether a trait's queue is currently being drained.
206
- */
207
- isProcessing(traitName: string): boolean;
208
- /**
209
- * Get the number of pending events in a trait's queue.
210
- */
211
- getQueueLength(traitName: string): number;
212
- /**
213
- * Reset a trait to its initial state.
214
- */
215
- resetTrait(traitName: string): void;
216
- /**
217
- * Reset all traits to initial states.
218
- */
219
- resetAll(): void;
220
- }
221
-
222
60
  /**
223
61
  * EffectExecutor - Platform-Agnostic Effect Dispatch
224
62
  *
@@ -342,7 +180,7 @@ declare function createTestExecutor(overrides?: Partial<EffectHandlers>): Effect
342
180
  * Minimal event bus interface required by the factory.
343
181
  */
344
182
  interface ClientEventBus {
345
- emit: (type: string, payload?: Record<string, unknown>) => void;
183
+ emit: (type: string, payload?: EventPayload) => void;
346
184
  }
347
185
  /**
348
186
  * Slot setter interface for render-ui effects.
@@ -350,7 +188,7 @@ interface ClientEventBus {
350
188
  */
351
189
  interface SlotSetter {
352
190
  /** Accumulate a pattern into the pending slot map */
353
- addPattern: (slot: string, pattern: unknown, props?: Record<string, unknown>) => void;
191
+ addPattern: (slot: string, pattern: unknown, props?: PatternProps) => void;
354
192
  /** Mark a slot for clearing */
355
193
  clearSlot: (slot: string) => void;
356
194
  }
@@ -363,7 +201,9 @@ interface CreateClientEffectHandlersOptions {
363
201
  /** Slot setter for render-ui effects */
364
202
  slotSetter: SlotSetter;
365
203
  /** Navigate function for navigate effects */
366
- navigate?: (path: string, params?: Record<string, unknown>) => void;
204
+ navigate?: (path: string, params?: {
205
+ [key: string]: string;
206
+ }) => void;
367
207
  /** Notify function for notification effects */
368
208
  notify?: (message: string, type: 'success' | 'error' | 'warning' | 'info') => void;
369
209
  }
@@ -402,7 +242,7 @@ declare function createClientEffectHandlers(options: CreateClientEffectHandlersO
402
242
 
403
243
  interface OsHandlerContext {
404
244
  /** Emit an event on the EventBus */
405
- emitEvent: (type: string, payload: Record<string, unknown>) => void;
245
+ emitEvent: (type: string, payload: EventPayload) => void;
406
246
  /** Working directory for file watching (defaults to process.cwd()) */
407
247
  cwd?: string;
408
248
  }
@@ -431,7 +271,7 @@ interface EntitySchema {
431
271
  name: string;
432
272
  fields: EntityField[];
433
273
  /** Pre-authored instance data from the schema (used instead of faker generation) */
434
- seedData?: Record<string, unknown>[];
274
+ seedData?: EntityRow[];
435
275
  }
436
276
  interface MockPersistenceConfig {
437
277
  /** Seed for deterministic generation */
@@ -506,4 +346,4 @@ declare function validatePayloadShapes(traits: TraitDefinition[], emits: Map<str
506
346
  */
507
347
  declare function buildEmitsFromTraits(traits: TraitDefinition[], explicitEmits?: Map<string, EmitDeclaration[]>): Map<string, EmitDeclaration[]>;
508
348
 
509
- export { BindingContext, type ClientEventBus, type CreateClientEffectHandlersOptions, EffectContext, EffectExecutor, type EffectExecutorOptions, EffectHandlers, EffectResult, type EntityField, type EntitySchema, ExecutionEnvironment, type MockPersistenceConfig, type OsHandlerContext, type OsHandlerResult, type PayloadMismatch, type ProcessEventOptions, RuntimeConfig, type SlotSetter, StateMachineManager, TraitDefinition, TraitState, TransitionObserver, TransitionResult, buildEmitsFromTraits, containsBindings, createClientEffectHandlers, createContextFromBindings, createInitialTraitState, createTestExecutor, extractBindings, findInitialState, findTransition, interpolateProps, interpolateValue, normalizeEventKey, processEvent, validatePayloadShapes };
349
+ export { BindingContext, type ClientEventBus, type CreateClientEffectHandlersOptions, EffectContext, EffectExecutor, type EffectExecutorOptions, EffectHandlers, EffectResult, type EntityField, type EntitySchema, ExecutionEnvironment, type MockPersistenceConfig, type OsHandlerContext, type OsHandlerResult, type PayloadMismatch, type SlotSetter, TraitDefinition, buildEmitsFromTraits, containsBindings, createClientEffectHandlers, createContextFromBindings, createTestExecutor, extractBindings, interpolateProps, interpolateValue, validatePayloadShapes };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- export { EffectExecutor, EventBus, HANDLER_MANIFEST, StateMachineManager, containsBindings, createContextFromBindings, createInitialTraitState, createMinimalContext, createTestExecutor, extractBindings, findInitialState, findTransition, getIsolatedCollectionName, getNamespacedEvent, interpolateProps, interpolateValue, isNamespacedEvent, normalizeEventKey, parseNamespacedEvent, preprocessSchema, processEvent } from './chunk-ICTFAD3I.js';
1
+ export { EffectExecutor, EventBus, HANDLER_MANIFEST, StateMachineManager, containsBindings, createContextFromBindings, createInitialTraitState, createMinimalContext, createTestExecutor, extractBindings, findInitialState, findTransition, getIsolatedCollectionName, getNamespacedEvent, interpolateProps, interpolateValue, isNamespacedEvent, normalizeEventKey, parseNamespacedEvent, preprocessSchema, processEvent } from './chunk-ESNML4B4.js';
2
2
 
3
3
  // src/ClientEffectHandlers.ts
4
4
  function createClientEffectHandlers(options) {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/ClientEffectHandlers.ts","../src/PayloadValidator.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Client Effect Handlers Factory\n *\n * Creates the standard effect handler set for client-side trait execution.\n * Platform-agnostic — works with any UI framework that provides the required interfaces.\n *\n * @packageDocumentation\n */\n\nimport type { EffectHandlers } from './types.js';\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Minimal event bus interface required by the factory.\n */\nexport interface ClientEventBus {\n emit: (type: string, payload?: Record<string, unknown>) => void;\n}\n\n/**\n * Slot setter interface for render-ui effects.\n * The factory doesn't know about React state — it just calls this function.\n */\nexport interface SlotSetter {\n /** Accumulate a pattern into the pending slot map */\n addPattern: (slot: string, pattern: unknown, props?: Record<string, unknown>) => void;\n /** Mark a slot for clearing */\n clearSlot: (slot: string) => void;\n}\n\n/**\n * Options for creating client effect handlers.\n */\nexport interface CreateClientEffectHandlersOptions {\n /** Event bus for emit effects */\n eventBus: ClientEventBus;\n /** Slot setter for render-ui effects */\n slotSetter: SlotSetter;\n /** Navigate function for navigate effects */\n navigate?: (path: string, params?: Record<string, unknown>) => void;\n /** Notify function for notification effects */\n notify?: (message: string, type: 'success' | 'error' | 'warning' | 'info') => void;\n}\n\n// ============================================================================\n// Factory\n// ============================================================================\n\n/**\n * Create client-side effect handlers for trait state machine execution.\n *\n * Client handles: emit, renderUI, navigate, notify\n * Server handles: persist, set, callService (logged as warnings on client)\n *\n * @example\n * ```ts\n * const handlers = createClientEffectHandlers({\n * eventBus,\n * slotSetter: {\n * addPattern: (slot, pattern, props) => pendingSlots.get(slot)?.push({ pattern, props }),\n * clearSlot: (slot) => pendingSlots.set(slot, []),\n * },\n * navigate: (path) => router.push(path),\n * notify: (msg, type) => toast[type](msg),\n * });\n * ```\n */\nexport function createClientEffectHandlers(\n options: CreateClientEffectHandlersOptions\n): EffectHandlers {\n const { eventBus, slotSetter, navigate, notify } = options;\n\n return {\n emit: (event: string, payload?: Record<string, unknown>) => {\n const prefixedEvent = event.startsWith('UI:') ? event : `UI:${event}`;\n eventBus.emit(prefixedEvent, { payload });\n },\n\n persist: async () => {\n console.warn('[ClientEffectHandlers] persist is server-side only, ignored on client');\n },\n\n set: () => {\n console.warn('[ClientEffectHandlers] set is server-side only, ignored on client');\n },\n\n callService: async () => {\n console.warn('[ClientEffectHandlers] callService is server-side only, ignored on client');\n return {};\n },\n\n renderUI: (slot: string, pattern: unknown, props?: Record<string, unknown>) => {\n if (pattern === null) {\n slotSetter.clearSlot(slot);\n return;\n }\n slotSetter.addPattern(slot, pattern, props);\n },\n\n navigate: navigate ?? ((path: string) => {\n console.warn('[ClientEffectHandlers] No navigate handler, ignoring:', path);\n }),\n\n notify: notify ?? ((msg: string, type?: string) => {\n console.log(`[ClientEffectHandlers] notify (${type}):`, msg);\n }),\n };\n}\n","/**\n * PayloadValidator - Cross-Trait Payload Shape Validation (RCG-10)\n *\n * Validates that listener `payloadMapping` references match the emitter's\n * payload field names. Catches mismatches like `@payload.task_id` when the\n * emitter defines `taskId`.\n *\n * @packageDocumentation\n */\n\nimport type { TraitDefinition } from './types.js';\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Emit declaration from a trait.\n */\ninterface EmitDeclaration {\n event: string;\n payload?: Array<{ name: string; type?: string }>;\n}\n\n/**\n * Payload validation error.\n */\nexport interface PayloadMismatch {\n /** Listening trait name */\n listenerTrait: string;\n /** Emitting trait name */\n emitterTrait: string;\n /** Event name */\n event: string;\n /** The payload field referenced in the listener's payloadMapping */\n referencedField: string;\n /** Available fields from the emitter's payload declaration */\n availableFields: string[];\n}\n\n// ============================================================================\n// Validation\n// ============================================================================\n\n/**\n * Validate that all listener payloadMapping references match emitter payload fields.\n *\n * @param traits - All trait definitions in the schema\n * @param emits - Emit declarations per trait (traitName → EmitDeclaration[])\n * @returns Array of payload mismatches (empty if all valid)\n *\n * @example\n * ```ts\n * const mismatches = validatePayloadShapes(traits, emitsMap);\n * for (const m of mismatches) {\n * console.warn(\n * `Trait \"${m.listenerTrait}\" references @payload.${m.referencedField} ` +\n * `for event \"${m.event}\" but emitter \"${m.emitterTrait}\" only declares: ` +\n * `${m.availableFields.join(', ')}`\n * );\n * }\n * ```\n */\nexport function validatePayloadShapes(\n traits: TraitDefinition[],\n emits: Map<string, EmitDeclaration[]>\n): PayloadMismatch[] {\n const mismatches: PayloadMismatch[] = [];\n\n // Build event→emitter lookup: event name → { traitName, payload fields }\n const emitIndex = new Map<string, { traitName: string; fields: string[] }>();\n for (const [traitName, declarations] of emits) {\n for (const decl of declarations) {\n const fields = decl.payload?.map((p) => p.name) ?? [];\n emitIndex.set(decl.event, { traitName, fields });\n }\n }\n\n // Check each listener's payloadMapping references\n for (const trait of traits) {\n if (!trait.listens) continue;\n\n for (const listener of trait.listens) {\n const emitter = emitIndex.get(listener.event);\n if (!emitter) continue; // No emitter found — separate validation concern\n\n if (!listener.payloadMapping) continue;\n\n // Extract @payload.X references from payloadMapping values\n const payloadRefs = extractPayloadReferences(listener.payloadMapping);\n\n for (const ref of payloadRefs) {\n if (!emitter.fields.includes(ref)) {\n mismatches.push({\n listenerTrait: trait.name,\n emitterTrait: emitter.traitName,\n event: listener.event,\n referencedField: ref,\n availableFields: emitter.fields,\n });\n }\n }\n }\n }\n\n return mismatches;\n}\n\n/**\n * Extract payload field references from a payloadMapping object.\n * Finds all `@payload.fieldName` patterns and returns the field names.\n */\nfunction extractPayloadReferences(mapping: Record<string, unknown>): string[] {\n const refs: string[] = [];\n\n function collect(value: unknown): void {\n if (typeof value === 'string') {\n const match = value.match(/^@payload\\.(\\w+)$/);\n if (match) {\n refs.push(match[1]);\n }\n } else if (typeof value === 'object' && value !== null) {\n if (Array.isArray(value)) {\n value.forEach(collect);\n } else {\n Object.values(value as Record<string, unknown>).forEach(collect);\n }\n }\n }\n\n Object.values(mapping).forEach(collect);\n return [...new Set(refs)];\n}\n\n/**\n * Build emit declarations map from trait definitions.\n * Extracts emits from transitions that use the `emit` effect.\n *\n * Note: This is a heuristic — it parses emit effects from transitions.\n * For full accuracy, the schema should include explicit `emits` declarations.\n */\nexport function buildEmitsFromTraits(\n traits: TraitDefinition[],\n explicitEmits?: Map<string, EmitDeclaration[]>\n): Map<string, EmitDeclaration[]> {\n // Start with explicit emits if provided\n const result = new Map<string, EmitDeclaration[]>(explicitEmits ?? []);\n\n for (const trait of traits) {\n if (result.has(trait.name)) continue; // Explicit declarations take precedence\n\n const emitDecls: EmitDeclaration[] = [];\n for (const transition of trait.transitions) {\n if (!transition.effects) continue;\n\n for (const effect of transition.effects) {\n if (!Array.isArray(effect)) continue;\n if (effect[0] === 'emit' && typeof effect[1] === 'string') {\n const event = effect[1] as string;\n // Payload is in effect[2] if present\n const payloadObj = effect[2] as Record<string, unknown> | undefined;\n const payload = payloadObj\n ? Object.keys(payloadObj).map((name) => ({ name }))\n : undefined;\n\n // Avoid duplicates\n if (!emitDecls.some((d) => d.event === event)) {\n emitDecls.push({ event, payload });\n }\n }\n }\n }\n\n if (emitDecls.length > 0) {\n result.set(trait.name, emitDecls);\n }\n }\n\n return result;\n}\n"]}
1
+ {"version":3,"sources":["../src/ClientEffectHandlers.ts","../src/PayloadValidator.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Client Effect Handlers Factory\n *\n * Creates the standard effect handler set for client-side trait execution.\n * Platform-agnostic — works with any UI framework that provides the required interfaces.\n *\n * @packageDocumentation\n */\n\nimport type { EffectHandlers, EventPayload, PatternProps } from './types.js';\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Minimal event bus interface required by the factory.\n */\nexport interface ClientEventBus {\n emit: (type: string, payload?: EventPayload) => void;\n}\n\n/**\n * Slot setter interface for render-ui effects.\n * The factory doesn't know about React state — it just calls this function.\n */\nexport interface SlotSetter {\n /** Accumulate a pattern into the pending slot map */\n addPattern: (slot: string, pattern: unknown, props?: PatternProps) => void;\n /** Mark a slot for clearing */\n clearSlot: (slot: string) => void;\n}\n\n/**\n * Options for creating client effect handlers.\n */\nexport interface CreateClientEffectHandlersOptions {\n /** Event bus for emit effects */\n eventBus: ClientEventBus;\n /** Slot setter for render-ui effects */\n slotSetter: SlotSetter;\n /** Navigate function for navigate effects */\n navigate?: (path: string, params?: { [key: string]: string }) => void;\n /** Notify function for notification effects */\n notify?: (message: string, type: 'success' | 'error' | 'warning' | 'info') => void;\n}\n\n// ============================================================================\n// Factory\n// ============================================================================\n\n/**\n * Create client-side effect handlers for trait state machine execution.\n *\n * Client handles: emit, renderUI, navigate, notify\n * Server handles: persist, set, callService (logged as warnings on client)\n *\n * @example\n * ```ts\n * const handlers = createClientEffectHandlers({\n * eventBus,\n * slotSetter: {\n * addPattern: (slot, pattern, props) => pendingSlots.get(slot)?.push({ pattern, props }),\n * clearSlot: (slot) => pendingSlots.set(slot, []),\n * },\n * navigate: (path) => router.push(path),\n * notify: (msg, type) => toast[type](msg),\n * });\n * ```\n */\nexport function createClientEffectHandlers(\n options: CreateClientEffectHandlersOptions\n): EffectHandlers {\n const { eventBus, slotSetter, navigate, notify } = options;\n\n return {\n emit: (event: string, payload?: EventPayload) => {\n const prefixedEvent = event.startsWith('UI:') ? event : `UI:${event}`;\n eventBus.emit(prefixedEvent, { payload });\n },\n\n persist: async () => {\n console.warn('[ClientEffectHandlers] persist is server-side only, ignored on client');\n },\n\n set: () => {\n console.warn('[ClientEffectHandlers] set is server-side only, ignored on client');\n },\n\n callService: async () => {\n console.warn('[ClientEffectHandlers] callService is server-side only, ignored on client');\n return {};\n },\n\n renderUI: (slot: string, pattern: unknown, props?: PatternProps) => {\n if (pattern === null) {\n slotSetter.clearSlot(slot);\n return;\n }\n slotSetter.addPattern(slot, pattern, props);\n },\n\n navigate: navigate ?? ((path: string) => {\n console.warn('[ClientEffectHandlers] No navigate handler, ignoring:', path);\n }),\n\n notify: notify ?? ((msg: string, type?: string) => {\n console.log(`[ClientEffectHandlers] notify (${type}):`, msg);\n }),\n };\n}\n","/**\n * PayloadValidator - Cross-Trait Payload Shape Validation (RCG-10)\n *\n * Validates that listener `payloadMapping` references match the emitter's\n * payload field names. Catches mismatches like `@payload.task_id` when the\n * emitter defines `taskId`.\n *\n * @packageDocumentation\n */\n\nimport type { TraitDefinition, EventPayload } from './types.js';\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Emit declaration from a trait.\n */\ninterface EmitDeclaration {\n event: string;\n payload?: Array<{ name: string; type?: string }>;\n}\n\n/**\n * Payload validation error.\n */\nexport interface PayloadMismatch {\n /** Listening trait name */\n listenerTrait: string;\n /** Emitting trait name */\n emitterTrait: string;\n /** Event name */\n event: string;\n /** The payload field referenced in the listener's payloadMapping */\n referencedField: string;\n /** Available fields from the emitter's payload declaration */\n availableFields: string[];\n}\n\n// ============================================================================\n// Validation\n// ============================================================================\n\n/**\n * Validate that all listener payloadMapping references match emitter payload fields.\n *\n * @param traits - All trait definitions in the schema\n * @param emits - Emit declarations per trait (traitName → EmitDeclaration[])\n * @returns Array of payload mismatches (empty if all valid)\n *\n * @example\n * ```ts\n * const mismatches = validatePayloadShapes(traits, emitsMap);\n * for (const m of mismatches) {\n * console.warn(\n * `Trait \"${m.listenerTrait}\" references @payload.${m.referencedField} ` +\n * `for event \"${m.event}\" but emitter \"${m.emitterTrait}\" only declares: ` +\n * `${m.availableFields.join(', ')}`\n * );\n * }\n * ```\n */\nexport function validatePayloadShapes(\n traits: TraitDefinition[],\n emits: Map<string, EmitDeclaration[]>\n): PayloadMismatch[] {\n const mismatches: PayloadMismatch[] = [];\n\n // Build event→emitter lookup: event name → { traitName, payload fields }\n const emitIndex = new Map<string, { traitName: string; fields: string[] }>();\n for (const [traitName, declarations] of emits) {\n for (const decl of declarations) {\n const fields = decl.payload?.map((p) => p.name) ?? [];\n emitIndex.set(decl.event, { traitName, fields });\n }\n }\n\n // Check each listener's payloadMapping references\n for (const trait of traits) {\n if (!trait.listens) continue;\n\n for (const listener of trait.listens) {\n const emitter = emitIndex.get(listener.event);\n if (!emitter) continue; // No emitter found — separate validation concern\n\n if (!listener.payloadMapping) continue;\n\n // Extract @payload.X references from payloadMapping values\n const payloadRefs = extractPayloadReferences(listener.payloadMapping);\n\n for (const ref of payloadRefs) {\n if (!emitter.fields.includes(ref)) {\n mismatches.push({\n listenerTrait: trait.name,\n emitterTrait: emitter.traitName,\n event: listener.event,\n referencedField: ref,\n availableFields: emitter.fields,\n });\n }\n }\n }\n }\n\n return mismatches;\n}\n\n/**\n * Extract payload field references from a payloadMapping object.\n * Finds all `@payload.fieldName` patterns and returns the field names.\n */\nfunction extractPayloadReferences(mapping: EventPayload): string[] {\n const refs: string[] = [];\n\n function collect(value: unknown): void {\n if (typeof value === 'string') {\n const match = value.match(/^@payload\\.(\\w+)$/);\n if (match) {\n refs.push(match[1]);\n }\n } else if (typeof value === 'object' && value !== null) {\n if (Array.isArray(value)) {\n value.forEach(collect);\n } else {\n Object.values(value as EventPayload).forEach(collect);\n }\n }\n }\n\n Object.values(mapping).forEach(collect);\n return [...new Set(refs)];\n}\n\n/**\n * Build emit declarations map from trait definitions.\n * Extracts emits from transitions that use the `emit` effect.\n *\n * Note: This is a heuristic — it parses emit effects from transitions.\n * For full accuracy, the schema should include explicit `emits` declarations.\n */\nexport function buildEmitsFromTraits(\n traits: TraitDefinition[],\n explicitEmits?: Map<string, EmitDeclaration[]>\n): Map<string, EmitDeclaration[]> {\n // Start with explicit emits if provided\n const result = new Map<string, EmitDeclaration[]>(explicitEmits ?? []);\n\n for (const trait of traits) {\n if (result.has(trait.name)) continue; // Explicit declarations take precedence\n\n const emitDecls: EmitDeclaration[] = [];\n for (const transition of trait.transitions) {\n if (!transition.effects) continue;\n\n for (const effect of transition.effects) {\n if (!Array.isArray(effect)) continue;\n if (effect[0] === 'emit' && typeof effect[1] === 'string') {\n const event = effect[1] as string;\n // Payload is in effect[2] if present\n const payloadObj = effect[2] as EventPayload | undefined;\n const payload = payloadObj\n ? Object.keys(payloadObj).map((name) => ({ name }))\n : undefined;\n\n // Avoid duplicates\n if (!emitDecls.some((d) => d.event === event)) {\n emitDecls.push({ event, payload });\n }\n }\n }\n }\n\n if (emitDecls.length > 0) {\n result.set(trait.name, emitDecls);\n }\n }\n\n return result;\n}\n"]}
@@ -1,3 +1,5 @@
1
+ import { EventPayload, EntityRow, ServiceParams, ResolvedPatternProps } from '@almadar/core';
2
+
1
3
  /**
2
4
  * Unified Runtime Types
3
5
  *
@@ -5,6 +7,13 @@
5
7
  *
6
8
  * @packageDocumentation
7
9
  */
10
+
11
+ /** Alias for ResolvedPatternProps to avoid breaking internal consumers */
12
+ type PatternProps = ResolvedPatternProps;
13
+ /** Configuration context */
14
+ type ConfigContext = {
15
+ [key: string]: string | number | boolean | null | undefined;
16
+ };
8
17
  /**
9
18
  * Event structure for cross-trait communication
10
19
  */
@@ -12,7 +21,7 @@ interface RuntimeEvent {
12
21
  /** Event type (e.g., "ORDER_CONFIRMED", "TraitName.EVENT_NAME") */
13
22
  type: string;
14
23
  /** Event payload data */
15
- payload?: Record<string, unknown>;
24
+ payload?: EventPayload;
16
25
  /** Timestamp when event was emitted */
17
26
  timestamp: number;
18
27
  /** Source information for debugging */
@@ -30,7 +39,7 @@ type Unsubscribe = () => void;
30
39
  */
31
40
  interface IEventBus {
32
41
  /** Emit an event */
33
- emit(type: string, payload?: Record<string, unknown>, source?: RuntimeEvent['source']): void;
42
+ emit(type: string, payload?: EventPayload, source?: RuntimeEvent['source']): void;
34
43
  /** Subscribe to an event */
35
44
  on(type: string, listener: EventListener): Unsubscribe;
36
45
  /** Subscribe to ALL events (wildcard listener) */
@@ -55,7 +64,7 @@ interface TraitState {
55
64
  /** Last event that caused a transition */
56
65
  lastEvent: string | null;
57
66
  /** Custom context data */
58
- context: Record<string, unknown>;
67
+ context: ConfigContext;
59
68
  }
60
69
  /**
61
70
  * Result of processing an event through a state machine
@@ -103,7 +112,7 @@ interface TraitDefinition {
103
112
  listens?: Array<{
104
113
  event: string;
105
114
  triggers: string;
106
- payloadMapping?: Record<string, unknown>;
115
+ payloadMapping?: EventPayload;
107
116
  }>;
108
117
  }
109
118
  /**
@@ -114,13 +123,13 @@ interface TraitDefinition {
114
123
  */
115
124
  interface EffectHandlers {
116
125
  /** Emit an event to the event bus */
117
- emit: (event: string, payload?: Record<string, unknown>) => void;
126
+ emit: (event: string, payload?: EventPayload) => void;
118
127
  /** Persist data (create/update/delete/batch) */
119
- persist: (action: 'create' | 'update' | 'delete' | 'batch', entityType: string, data?: Record<string, unknown>) => Promise<void>;
128
+ persist: (action: 'create' | 'update' | 'delete' | 'batch', entityType: string, data?: EntityRow) => Promise<void>;
120
129
  /** Set a field value on an entity */
121
130
  set: (entityId: string, field: string, value: unknown) => void;
122
131
  /** Call an external service */
123
- callService: (service: string, action: string, params?: Record<string, unknown>) => Promise<unknown>;
132
+ callService: (service: string, action: string, params?: ServiceParams) => Promise<unknown>;
124
133
  /** Fetch entity data (server only) - returns data for client-side rendering */
125
134
  fetch?: (entityType: string, options?: {
126
135
  id?: string;
@@ -129,15 +138,17 @@ interface EffectHandlers {
129
138
  offset?: number;
130
139
  /** Relation fields to include (populate) in the response */
131
140
  include?: string[];
132
- }) => Promise<Record<string, unknown> | Record<string, unknown>[] | null>;
141
+ }) => Promise<EntityRow | EntityRow[] | null>;
133
142
  /** Spawn a new entity instance */
134
- spawn?: (entityType: string, props?: Record<string, unknown>) => void;
143
+ spawn?: (entityType: string, props?: EntityRow) => void;
135
144
  /** Despawn (delete) an entity instance */
136
145
  despawn?: (entityId: string) => void;
137
146
  /** Render UI to a slot (client only) */
138
- renderUI?: (slot: string, pattern: unknown, props?: Record<string, unknown>, priority?: number) => void;
147
+ renderUI?: (slot: string, pattern: unknown, props?: PatternProps, priority?: number) => void;
139
148
  /** Navigate to a route (client only) */
140
- navigate?: (path: string, params?: Record<string, unknown>) => void;
149
+ navigate?: (path: string, params?: {
150
+ [key: string]: string;
151
+ }) => void;
141
152
  /** Show a notification (client: toast, server: log) */
142
153
  notify?: (message: string, type: 'success' | 'error' | 'warning' | 'info') => void;
143
154
  /** Log a message */
@@ -149,20 +160,27 @@ interface EffectHandlers {
149
160
  limit?: number;
150
161
  offset?: number;
151
162
  include?: string[];
152
- }) => Promise<Record<string, unknown> | Record<string, unknown>[] | null>;
163
+ }) => Promise<EntityRow | EntityRow[] | null>;
153
164
  /** Deref: one-shot data read (server: same as fetch) */
154
165
  deref?: (entityType: string, options?: {
155
166
  id?: string;
156
167
  filter?: unknown;
157
- }) => Promise<Record<string, unknown> | Record<string, unknown>[] | null>;
168
+ }) => Promise<EntityRow | EntityRow[] | null>;
158
169
  /** Swap!: atomic read-modify-write on an entity */
159
- swap?: (entityType: string, entityId: string, transform: unknown) => Promise<Record<string, unknown> | null>;
170
+ swap?: (entityType: string, entityId: string, transform: unknown) => Promise<EntityRow | null>;
160
171
  /** Watch: client-side reactive subscription (no-op on server) */
161
- watch?: (entityType: string, options?: Record<string, unknown>) => void;
172
+ watch?: (entityType: string, options?: {
173
+ id?: string;
174
+ filter?: unknown;
175
+ limit?: number;
176
+ }) => void;
162
177
  /** Atomic: execute inner effects as a transaction */
163
178
  atomic?: (effects: unknown[]) => Promise<void>;
164
179
  /** Watch file system for changes matching glob pattern */
165
- osWatchFiles?: (glob: string, options: Record<string, unknown>) => void;
180
+ osWatchFiles?: (glob: string, options: {
181
+ recursive?: boolean;
182
+ debounce?: number;
183
+ }) => void;
166
184
  /** Monitor a process by name */
167
185
  osWatchProcess?: (name: string, subcommand?: string) => void;
168
186
  /** Monitor a port for open/close */
@@ -183,13 +201,13 @@ interface EffectHandlers {
183
201
  */
184
202
  interface BindingContext {
185
203
  /** Current entity data */
186
- entity?: Record<string, unknown>;
204
+ entity?: EntityRow;
187
205
  /** Event payload data */
188
- payload?: Record<string, unknown>;
206
+ payload?: EventPayload;
189
207
  /** Current state name */
190
208
  state?: string;
191
209
  /** Trait-level state/config */
192
- config?: Record<string, unknown>;
210
+ config?: ConfigContext;
193
211
  /** Additional custom bindings */
194
212
  [key: string]: unknown;
195
213
  }
@@ -293,4 +311,4 @@ interface TransitionObserver {
293
311
  */
294
312
  declare const HANDLER_MANIFEST: Record<ExecutionEnvironment, string[]>;
295
313
 
296
- export { type BindingContext as B, type EffectHandlers as E, HANDLER_MANIFEST as H, type IEventBus as I, type RuntimeConfig as R, type TraitState as T, type Unsubscribe as U, type TraitDefinition as a, type TransitionObserver as b, type TransitionResult as c, type EffectContext as d, type ExecutionEnvironment as e, type EffectResult as f, type Effect as g, type EventListener as h, type RuntimeEvent as i };
314
+ export { type BindingContext as B, type EffectHandlers as E, HANDLER_MANIFEST as H, type IEventBus as I, type PatternProps as P, type RuntimeConfig as R, type TraitDefinition as T, type Unsubscribe as U, type EffectContext as a, type ExecutionEnvironment as b, type EffectResult as c, type Effect as d, type EventListener as e, type RuntimeEvent as f, type TraitState as g, type TransitionObserver as h, type TransitionResult as i };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@almadar/runtime",
3
- "version": "2.5.0",
3
+ "version": "2.6.1",
4
4
  "description": "Interpreted runtime for Almadar orbital applications (OrbitalServerRuntime)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -38,7 +38,7 @@
38
38
  "dependencies": {
39
39
  "@almadar/evaluator": ">=2.5.4",
40
40
  "@almadar/operators": ">=2.1.3",
41
- "@almadar/core": ">=2.6.3",
41
+ "@almadar/core": ">=2.13.0",
42
42
  "@faker-js/faker": "^9.3.0"
43
43
  },
44
44
  "peerDependencies": {
@@ -53,7 +53,7 @@
53
53
  "vitest": "^2.1.0",
54
54
  "eslint": "10.0.0",
55
55
  "@typescript-eslint/parser": "8.56.0",
56
- "@almadar/eslint-plugin": ">=2.3.0"
56
+ "@almadar/eslint-plugin": ">=2.7.0"
57
57
  },
58
58
  "repository": {
59
59
  "type": "git",