@almadar/runtime 6.31.0 → 6.33.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.
@@ -0,0 +1,419 @@
1
+ import { UISlot, AnyPatternConfig, ResolvedPatternProps, EventSource, SExpr, ResolvedTrait, EventPayload, EventPayloadValue, OrbitalSchema, EntityData, OrbitalVerificationAPI, BusEvent } from '@almadar/core';
2
+ import { PatternCallbackArg } from '@almadar/core/patterns';
3
+
4
+ /**
5
+ * Runtime contract enforcement for renderer implementations.
6
+ *
7
+ * @packageDocumentation
8
+ */
9
+ /**
10
+ * Error thrown when a renderer implementation violates the
11
+ * `@almadar/runtime/ui` contract (e.g. a `SlotManager` is missing required
12
+ * methods).
13
+ */
14
+ declare class RendererContractViolationError extends Error {
15
+ constructor(message: string);
16
+ }
17
+ /**
18
+ * Structured error returned by {@link validateSlotContent} when a slot content
19
+ * value does not satisfy the runtime contract.
20
+ */
21
+ interface SlotContentValidationError {
22
+ /** Human-readable error message. */
23
+ readonly message: string;
24
+ /** Dotted path to the offending field, or `'.'` for the root value. */
25
+ readonly path: string;
26
+ }
27
+
28
+ /**
29
+ * Renderer-agnostic slot contract.
30
+ *
31
+ * Any UI library (React, Web Components, Vue, etc.) that wants to render an
32
+ * Almadar orbital schema must provide an implementation of {@link SlotManager}.
33
+ * The runtime itself is UI-framework blind: it only knows about UISlots and
34
+ * pattern configs from @almadar/core.
35
+ *
36
+ * @packageDocumentation
37
+ */
38
+
39
+ /**
40
+ * Metadata that attributes a slot write to the trait/transition that produced it.
41
+ *
42
+ * Mirrors the shape previously in `@almadar/ui/types/slot-types`, but grounded
43
+ * entirely in `@almadar/core` types so the contract lives in the runtime layer.
44
+ */
45
+ interface SlotSource extends EventSource {
46
+ /** Trait that emitted the render-ui effect. */
47
+ trait: string;
48
+ /** State the trait was in when the effect fired. */
49
+ state: string;
50
+ /** Transition (or tick) that carried the effect. */
51
+ transition: string;
52
+ /** Effects executed during the transition. */
53
+ effects: SExpr[];
54
+ /** Resolved trait definition that owns the transition (optional attribution). */
55
+ traitDefinition?: ResolvedTrait;
56
+ }
57
+ /**
58
+ * One unit of content the runtime asks a renderer to place in a named slot.
59
+ *
60
+ * A `null` pattern means "clear this slot". Optional `props` carry resolved
61
+ * pattern props (e.g. from a render-ui effect's 4th tuple element). `source`
62
+ * is optional metadata for debug/verification attribution.
63
+ */
64
+ interface SlotContent {
65
+ /** Target UI slot from the core UISlot union. */
66
+ slot: UISlot;
67
+ /** Pattern config to render, or null to clear the slot. */
68
+ pattern: AnyPatternConfig | null;
69
+ /** Resolved props merged on top of the pattern config. */
70
+ props?: ResolvedPatternProps;
71
+ /** Attribution metadata for tracing/debugging. */
72
+ source?: SlotSource;
73
+ }
74
+ /**
75
+ * Minimal slot-management contract every UI renderer must satisfy.
76
+ *
77
+ * The runtime's client effect handlers call `setContent`/`clearSlot` in
78
+ * response to `render-ui` effects. The renderer owns the actual DOM/
79
+ * component-tree update, subscription handling, and multi-source merging
80
+ * policy (e.g. stacking N trait writes into one synthetic container).
81
+ */
82
+ interface SlotManager {
83
+ /** Return the latest content for a slot, or undefined if empty. */
84
+ getContent(slot: UISlot): SlotContent | undefined;
85
+ /** Place (or replace) content for a slot. */
86
+ setContent(content: SlotContent): void;
87
+ /** Clear a slot by writing a null pattern. */
88
+ clearSlot(slot: UISlot): void;
89
+ /** Snapshot of all currently populated slots. */
90
+ getAllSlots(): ReadonlyMap<UISlot, SlotContent>;
91
+ /** Subscribe to slot changes. Returns an unsubscribe function. */
92
+ subscribe(listener: (slot: UISlot, content: SlotContent | undefined) => void): () => void;
93
+ }
94
+ /**
95
+ * Extended slot manager contract that supports the multi-source aggregation
96
+ * and per-trait sidecar model used by the React `useUISlots` implementation.
97
+ *
98
+ * Renderers that need to support `@trait.X` embedding and multiple traits
99
+ * writing the same slot should implement this interface. The base
100
+ * {@link SlotManager} remains the minimal contract for simple renderers.
101
+ */
102
+ interface MultiSourceSlotManager extends SlotManager {
103
+ /** Return the latest content a given trait rendered, or undefined if none. */
104
+ getTraitContent(traitName: string): SlotContent | undefined;
105
+ /** Subscribe to changes in a specific trait's render output. */
106
+ subscribeTrait(traitName: string, listener: (content: SlotContent | undefined) => void): () => void;
107
+ /**
108
+ * Update the per-trait sidecar without writing to a slot.
109
+ *
110
+ * Used for embed-aware routing: when a trait is referenced via `@trait.X` by
111
+ * a sibling layout, its render output should not stack into the layout's
112
+ * slot; instead the layout embeds the trait's frame and the sidecar keeps
113
+ * the trait's latest frame queryable.
114
+ */
115
+ updateTraitContent(traitName: string, content: Omit<SlotContent, 'source'>): void;
116
+ }
117
+ /**
118
+ * Aggregate a slot's per-source content map into a single {@link SlotContent}.
119
+ *
120
+ * - 0 sources → `undefined` (empty slot).
121
+ * - 1 source → that source's content verbatim.
122
+ * - 2+ sources → a synthetic `stack` wrapper whose `children` are the
123
+ * pattern configs of each source in insertion order.
124
+ *
125
+ * This mirrors the React `useUISlots` aggregation behavior and is provided so
126
+ * every renderer implements the same multi-source semantics.
127
+ */
128
+ declare function aggregateSlotContent(slot: UISlot, sources: ReadonlyMap<string, SlotContent>): SlotContent | undefined;
129
+
130
+ /**
131
+ * Assert that a value satisfies the {@link SlotManager} contract at runtime.
132
+ *
133
+ * @throws RendererContractViolationError when the value is not a valid manager.
134
+ */
135
+ declare function assertIsSlotManager(value: unknown): asserts value is SlotManager;
136
+ /**
137
+ * Assert that a value satisfies the {@link MultiSourceSlotManager} contract.
138
+ *
139
+ * @throws RendererContractViolationError when the value is not a valid manager.
140
+ */
141
+ declare function assertIsMultiSourceSlotManager(value: unknown): asserts value is MultiSourceSlotManager;
142
+ /**
143
+ * Validate that a {@link SlotContent} value satisfies the runtime contract.
144
+ *
145
+ * Returns an array of validation errors; an empty array means the value is
146
+ * valid. This lets renderer authors surface contract failures without
147
+ * exceptions.
148
+ */
149
+ declare function validateSlotContent(content: SlotContent): SlotContentValidationError[];
150
+ /**
151
+ * Minimal adapter that turns a `SlotManager` into the shape expected by
152
+ * {@link createClientEffectHandlers} in `ClientEffectHandlers.ts`.
153
+ *
154
+ * This keeps the legacy `SlotSetter.addPattern/clearSlot` surface working while
155
+ * nudging new renderers toward the fuller `SlotManager` contract.
156
+ */
157
+ declare function createSlotSetter(manager: SlotManager): {
158
+ addPattern: (slot: string, pattern: AnyPatternConfig, props?: ResolvedPatternProps) => void;
159
+ clearSlot: (slot: string) => void;
160
+ };
161
+
162
+ /**
163
+ * Build the runtime wrapper that turns a string-typed callback prop
164
+ * (e.g. `onTabChange: "TAB_CHANGED"`) into a function the component can
165
+ * invoke. The wrapper consumes the component's positional callback args
166
+ * by name and dispatches an OBJECT payload `{ argName: value, ... }` so
167
+ * the bus carries the trait's declared event payload, not a raw
168
+ * positional spread. Mirrors the codegen wrapper in
169
+ * `orbital-shell-typescript/src/codegen/pattern.rs` (C2 compiled path).
170
+ *
171
+ * Single-sourcing this logic across runtime and codegen (shared shape,
172
+ * not shared code — codegen emits string source) is what keeps the two
173
+ * paths from silently diverging on payload shape.
174
+ */
175
+ declare function wrapCallbackForEvent(qualifiedEvent: string, callbackArgs: PatternCallbackArg[] | undefined, emit: (eventKey: string, payload?: EventPayload) => void): (...args: EventPayloadValue[]) => void;
176
+
177
+ /**
178
+ * prepareSchemaForPreview
179
+ *
180
+ * Single source of truth for the "give an Orbital schema a runnable preview"
181
+ * pipeline. Used by both:
182
+ * * `<OrbPreview autoMock />` (the docs / MDX path)
183
+ * * `PlaygroundContent` (the interactive playground)
184
+ *
185
+ * Type signature in `@almadar/core` terms:
186
+ *
187
+ * buildMockData : OrbitalSchema → EntityData
188
+ * adjustSchemaForMockData: (OrbitalSchema, EntityData) → OrbitalSchema
189
+ * prepareSchemaForPreview: OrbitalSchema → { schema: OrbitalSchema, mockData: EntityData }
190
+ *
191
+ * Steps:
192
+ * 1. `buildMockData` — for each orbital, generate (or pick up from
193
+ * `entity.instances`) `EntityRow[]` for that entity.
194
+ * 2. `adjustSchemaForMockData` — flip two-state INIT machines so the data
195
+ * state is initial (e.g. `empty -> hasItems` becomes `hasItems` initial).
196
+ *
197
+ * @packageDocumentation
198
+ */
199
+
200
+ /**
201
+ * Build mock entity rows (`EntityData`) for every orbital in the schema.
202
+ *
203
+ * @public
204
+ */
205
+ declare function buildMockData(schema: OrbitalSchema): EntityData;
206
+ /**
207
+ * When mock data exists for a trait's linked entity, swap the state
208
+ * machine's initial state to the state that also handles INIT.
209
+ *
210
+ * @public
211
+ */
212
+ declare function adjustSchemaForMockData(schema: OrbitalSchema, mockData: EntityData): OrbitalSchema;
213
+ /**
214
+ * Result of `prepareSchemaForPreview`.
215
+ *
216
+ * @public
217
+ */
218
+ interface PreparedPreviewSchema {
219
+ /** Schema with state machines flipped so data states are initial. */
220
+ schema: OrbitalSchema;
221
+ /** Mock entity rows keyed by entity name — pass to `OrbPreview.mockData`. */
222
+ mockData: EntityData;
223
+ }
224
+ /**
225
+ * Run the full preview prep pipeline on a schema.
226
+ *
227
+ * Accepts either an already-parsed `OrbitalSchema` or a JSON string.
228
+ *
229
+ * @public
230
+ */
231
+ declare function prepareSchemaForPreview(input: OrbitalSchema | string): PreparedPreviewSchema;
232
+
233
+ /**
234
+ * Build the `trait name → owning orbital` map used to form the qualified
235
+ * `UI:<orbital>.<trait>.<event>` bus keys that trait state machines subscribe
236
+ * to. Pure + dependency-free so it can be unit-tested without a render.
237
+ *
238
+ * Critically, it backfills from the RESOLVED page bindings, not just the source
239
+ * `schema.orbitals[].traits`. An auto-pulled sibling trait (e.g. an embedded
240
+ * `@trait.X` calendar lifted into the orbital by the compiler's sibling-pull)
241
+ * is absent from the source orbital's `traits[]`, so a source-only map misses
242
+ * it — leaving its self-subscription unregistered and its own fetch-success
243
+ * (e.g. `CalendarEventLoaded`) unheard, so the trait sticks in `loading`. A
244
+ * pulled sibling lands in the same orbital as the page that references it, so
245
+ * we backfill from the resolved page's owning orbital. Source-declared mappings
246
+ * win; the IR only fills gaps.
247
+ */
248
+ interface ResolvedPageTraits {
249
+ /** Page route path, matched against the source orbital's page paths. */
250
+ path?: string;
251
+ /** Resolved trait names mounted on the page (includes pulled siblings). */
252
+ traitNames: readonly string[];
253
+ }
254
+ /**
255
+ * Minimal trait-ref shape the mapper actually reads. Structurally widens
256
+ * `TraitRef` so tests and partial schemas don't have to satisfy every required
257
+ * inline-trait field (e.g. `scope`) that the mapper never touches.
258
+ */
259
+ type OrbitalsByTraitTraitInput = string | {
260
+ ref?: string;
261
+ name?: string;
262
+ };
263
+ /**
264
+ * Minimal schema shape the mapper actually reads. Kept local because the
265
+ * function only needs `orbitals[].{name,traits,pages}`; the full
266
+ * `OrbitalSchema` carries many more required fields.
267
+ */
268
+ interface OrbitalsByTraitInput {
269
+ orbitals: ReadonlyArray<{
270
+ name: string;
271
+ traits?: ReadonlyArray<OrbitalsByTraitTraitInput>;
272
+ pages?: ReadonlyArray<string | {
273
+ path?: string;
274
+ }>;
275
+ }>;
276
+ }
277
+ declare function buildOrbitalsByTrait(schema: OrbitalsByTraitInput | undefined, resolvedPages?: ReadonlyArray<ResolvedPageTraits>): Record<string, string>;
278
+
279
+ /**
280
+ * Embed-aware slot routing — static analysis pass.
281
+ *
282
+ * Walks every trait's `transitions[].effects` looking for `render-ui`
283
+ * patterns whose tree contains `@trait.<Name>` string literals. Returns
284
+ * the flat set of trait names that are referenced this way by some
285
+ * sibling layout.
286
+ *
287
+ * Used by `<OrbPreview>` to route `applyServerEffects` — when an
288
+ * embedded trait's render-ui effect arrives, the runtime updates that
289
+ * trait's per-trait sidecar (`traitIndexRef`) only and skips the slot
290
+ * write. The sibling layout owns the slot and embeds the trait's frame
291
+ * via `<TraitFrame>`. Mirrors the compiled-path codegen which inlines
292
+ * the atom views as JSX inside the layout's pattern, never having them
293
+ * write a shared slot.
294
+ *
295
+ * The walker is a structural twin of
296
+ * `packages/almadar-runtime/src/resolver/reference-resolver.ts`'s
297
+ * `renameEventsInRenderUiConfig` — same recursive pattern shape, just
298
+ * collecting `@trait.X` substrings instead of renaming events.
299
+ *
300
+ * @packageDocumentation
301
+ */
302
+
303
+ declare function collectTraitRefsFromResolvedTrait(trait: ResolvedTrait): Set<string>;
304
+ declare function collectEmbeddedTraits(schema: OrbitalSchema | undefined | null): ReadonlySet<string>;
305
+
306
+ /**
307
+ * Renderer-agnostic perf instrumentation.
308
+ *
309
+ * Contains the timing ring + mark/measure primitives used by runtime-side
310
+ * schema prep and other framework-free work. React-specific consumption hooks
311
+ * stay in `@almadar/ui/lib/perf` and read this shared ring.
312
+ *
313
+ * Gated behind `createLogger('almadar:perf:canvas')` so production builds
314
+ * (LOG_LEVEL >= WARN) skip the work.
315
+ *
316
+ * @packageDocumentation
317
+ */
318
+ declare const PERF_NAMESPACE = "almadar:perf:canvas";
319
+ /**
320
+ * Perf metadata values are local instrumentation scalars. There is no
321
+ * corresponding @almadar/core concept, so this local fallback type replaces
322
+ * the previous Record<string, unknown> while keeping the surface concrete.
323
+ */
324
+ type PerfDetailValue = string | number | boolean | null | undefined;
325
+ interface PerfDetail {
326
+ readonly [key: string]: PerfDetailValue;
327
+ }
328
+ interface PerfEntry {
329
+ readonly name: string;
330
+ readonly durationMs: number;
331
+ readonly ts: number;
332
+ readonly detail?: Readonly<PerfDetail>;
333
+ }
334
+ /**
335
+ * Start a phase. Returns an opaque token; pass to {@link perfEnd}.
336
+ * Returns -1 when the namespace is gated off.
337
+ */
338
+ declare function perfStart(name: string): number;
339
+ declare function perfEnd(name: string, startToken: number, detail?: PerfDetail): void;
340
+ /** Synchronous wrapper that times a fn end-to-end. */
341
+ declare function perfTime<T>(name: string, fn: () => T, detail?: PerfDetail): T;
342
+ declare function getSnapshot(): readonly PerfEntry[];
343
+ declare function subscribe(fn: () => void): () => void;
344
+ /** Push a pre-computed entry (e.g. React.Profiler callback). */
345
+ declare function pushPerfEntry(entry: PerfEntry): void;
346
+ /** Clear the ring and notify subscribers. */
347
+ declare function clearPerf(): void;
348
+ /**
349
+ * Primitives a renderer-specific hook (e.g. React's `useSyncExternalStore`)
350
+ * uses to consume the perf ring.
351
+ */
352
+ declare const perfStore: {
353
+ subscribe: typeof subscribe;
354
+ getSnapshot: typeof getSnapshot;
355
+ };
356
+
357
+ /**
358
+ * Renderer-agnostic `window.__orbitalVerification` bridge.
359
+ *
360
+ * The single observation point Playwright-based verifiers
361
+ * (`runtime-verify`, `orbital-verify`, `@almadar-io/verify`) use to read
362
+ * state out of a live app and drive events into it. The wire types live
363
+ * in `@almadar/core` (see `types/verification.ts`); this module owns only
364
+ * the minimal exposure plumbing so any UI library — React, Web
365
+ * Components, or a future renderer — can populate the same contract
366
+ * without duplicating it.
367
+ *
368
+ * Framework-specific state (transition traces, reducer snapshots, bridge
369
+ * health) is left empty by default; a renderer that tracks them can
370
+ * override the readers after {@link ensureVerificationApi} runs. The two
371
+ * bindings every renderer must provide are {@link bindEventBus} (so
372
+ * automation can `sendEvent`) and {@link bindTraitStateGetter} (so
373
+ * automation can `getTraitState`).
374
+ *
375
+ * @packageDocumentation
376
+ */
377
+
378
+ declare global {
379
+ interface Window {
380
+ __orbitalVerification?: OrbitalVerificationAPI;
381
+ }
382
+ }
383
+ /**
384
+ * Minimal structural surface a renderer's event bus must satisfy to be
385
+ * driven by automation. Matches the runtime `EventBus` (`emit` +
386
+ * optional `onAny`) without forcing a concrete class, so any UI
387
+ * library's bus — including `@almadar/runtime`'s own — plugs in.
388
+ */
389
+ interface VerificationBus {
390
+ emit: (type: string, payload?: EventPayload) => void;
391
+ onAny?: (listener: (event: BusEvent) => void) => () => void;
392
+ }
393
+ /**
394
+ * Ensure `window.__orbitalVerification` exists with the required
395
+ * readers, then return it. Returns `undefined` outside a DOM (SSR /
396
+ * tests without a window). Idempotent: an existing object is returned
397
+ * untouched so a renderer that already registered readers keeps them.
398
+ */
399
+ declare function ensureVerificationApi(): OrbitalVerificationAPI | undefined;
400
+ /** Read the current bridge, if any. SSR-safe. */
401
+ declare function getOrbitalVerification(): OrbitalVerificationAPI | undefined;
402
+ /**
403
+ * Bind the renderer's event bus so automation can send events and read
404
+ * a rolling emission log.
405
+ *
406
+ * `sendEvent(event, payload, traitScope)` mirrors the React bridge:
407
+ * when `traitScope` is provided the event is emitted on the qualified
408
+ * key `UI:${traitScope}.${event}` (matching codegen-emitted
409
+ * subscription keys); otherwise the legacy bare form `UI:${event}` is
410
+ * used. An already-`UI:`-prefixed event is emitted verbatim.
411
+ */
412
+ declare function bindEventBus(eventBus: VerificationBus): void;
413
+ /**
414
+ * Bind a trait-state getter so automation can query a trait's current
415
+ * state machine state by name.
416
+ */
417
+ declare function bindTraitStateGetter(getter: (traitName: string) => string | undefined): void;
418
+
419
+ export { type MultiSourceSlotManager, PERF_NAMESPACE, type PerfDetail, type PerfDetailValue, type PerfEntry, type PreparedPreviewSchema, RendererContractViolationError, type ResolvedPageTraits, type SlotContent, type SlotContentValidationError, type SlotManager, type SlotSource, type VerificationBus, adjustSchemaForMockData, aggregateSlotContent, assertIsMultiSourceSlotManager, assertIsSlotManager, bindEventBus, bindTraitStateGetter, buildMockData, buildOrbitalsByTrait, clearPerf, collectEmbeddedTraits, collectTraitRefsFromResolvedTrait, createSlotSetter, ensureVerificationApi, getOrbitalVerification, perfEnd, perfStart, perfStore, perfTime, prepareSchemaForPreview, pushPerfEntry, validateSlotContent, wrapCallbackForEvent };
@@ -0,0 +1,3 @@
1
+ export { PERF_NAMESPACE, RendererContractViolationError, adjustSchemaForMockData, aggregateSlotContent, assertIsMultiSourceSlotManager, assertIsSlotManager, bindEventBus, bindTraitStateGetter, buildMockData, buildOrbitalsByTrait, clearPerf, createSlotSetter, ensureVerificationApi, getOrbitalVerification, perfEnd, perfStart, perfStore, perfTime, prepareSchemaForPreview, pushPerfEntry, validateSlotContent, wrapCallbackForEvent } from '../chunk-O5VGKPTG.js';
2
+ export { collectEmbeddedTraits, collectTraitRefsFromResolvedTrait } from '../chunk-SCRAHWOC.js';
3
+ import '../chunk-MLKGABMK.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@almadar/runtime",
3
- "version": "6.31.0",
3
+ "version": "6.33.0",
4
4
  "description": "Interpreted runtime for Almadar orbital applications (OrbitalServerRuntime)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -32,6 +32,16 @@
32
32
  "types": "./dist/createOsHandlers.d.ts",
33
33
  "import": "./dist/createOsHandlers.js",
34
34
  "require": "./dist/createOsHandlers.js"
35
+ },
36
+ "./mockRandom": {
37
+ "types": "./dist/mockRandom.d.ts",
38
+ "import": "./dist/mockRandom.js",
39
+ "require": "./dist/mockRandom.js"
40
+ },
41
+ "./ui": {
42
+ "types": "./dist/ui/index.d.ts",
43
+ "import": "./dist/ui/index.js",
44
+ "require": "./dist/ui/index.js"
35
45
  }
36
46
  },
37
47
  "files": [
@@ -42,12 +52,11 @@
42
52
  "access": "public"
43
53
  },
44
54
  "dependencies": {
45
- "@almadar/core": "^10.27.0",
55
+ "@almadar/core": "^10.29.0",
46
56
  "@almadar/evaluator": "^2.29.0",
47
57
  "@almadar/logger": "^1.9.0",
48
- "@almadar/server": "^2.20.0",
49
- "@almadar/std": "^16.137.0",
50
- "@faker-js/faker": "^9.3.0"
58
+ "@almadar/server": "^2.25.0",
59
+ "@almadar/std": "^16.138.0"
51
60
  },
52
61
  "peerDependencies": {
53
62
  "express": "^5.0.0"
@@ -65,9 +74,9 @@
65
74
  "eslint": "10.0.0",
66
75
  "express": "^5.0.0",
67
76
  "tsup": "^8.0.0",
77
+ "turbo": "^2.8.17",
68
78
  "typescript": "^5.7.0",
69
- "vitest": "^2.1.0",
70
- "turbo": "^2.8.17"
79
+ "vitest": "^2.1.0"
71
80
  },
72
81
  "repository": {
73
82
  "type": "git",
@@ -83,6 +92,7 @@
83
92
  ],
84
93
  "homepage": "https://github.com/almadar-io/almadar#readme",
85
94
  "scripts": {
95
+ "prebuild": "rm -rf dist",
86
96
  "build": "tsup",
87
97
  "build:watch": "tsup --watch",
88
98
  "lint": "eslint src/",