@almadar/runtime 5.1.0 → 5.3.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.
- package/dist/{OrbitalServerRuntime-D-b_dg8I.d.ts → OrbitalServerRuntime-BrJ7m0hz.d.ts} +28 -1
- package/dist/OrbitalServerRuntime.d.ts +2 -2
- package/dist/OrbitalServerRuntime.js +58 -2
- package/dist/OrbitalServerRuntime.js.map +1 -1
- package/dist/ServerBridge.d.ts +1 -1
- package/dist/{chunk-T343XTYB.js → chunk-FAWWBERN.js} +179 -8
- package/dist/chunk-FAWWBERN.js.map +1 -0
- package/dist/index.d.ts +45 -6
- package/dist/index.js +3 -80
- package/dist/index.js.map +1 -1
- package/dist/{types-DwDhc9Jt.d.ts → types-SmmabGZk.d.ts} +1 -2
- package/package.json +1 -1
- package/dist/chunk-T343XTYB.js.map +0 -1
package/dist/ServerBridge.d.ts
CHANGED
|
@@ -176,6 +176,7 @@ function createLogger(namespace) {
|
|
|
176
176
|
};
|
|
177
177
|
}
|
|
178
178
|
var bindLog = createLogger("almadar:runtime:bindings");
|
|
179
|
+
var renderLog = createLogger("almadar:runtime:render-ui");
|
|
179
180
|
var CLIENT_ONLY_BINDING_ROOTS = /* @__PURE__ */ new Set(["trait"]);
|
|
180
181
|
function isClientOnlyBinding(value) {
|
|
181
182
|
if (!value.startsWith("@")) return false;
|
|
@@ -186,10 +187,41 @@ function isClientOnlyBinding(value) {
|
|
|
186
187
|
}
|
|
187
188
|
function interpolateProps(props, ctx) {
|
|
188
189
|
const result = {};
|
|
190
|
+
let anyChanged = false;
|
|
189
191
|
for (const [key, value] of Object.entries(props)) {
|
|
190
|
-
|
|
192
|
+
const interpolated = interpolateValue(value, ctx);
|
|
193
|
+
result[key] = interpolated;
|
|
194
|
+
if (interpolated !== value) anyChanged = true;
|
|
195
|
+
}
|
|
196
|
+
const entityBindingRaw = props["entity"];
|
|
197
|
+
const typeBindingRaw = props["type"];
|
|
198
|
+
const patternType = typeof typeBindingRaw === "string" ? typeBindingRaw : void 0;
|
|
199
|
+
if (typeof entityBindingRaw === "string") {
|
|
200
|
+
const resolvedEntity = result["entity"];
|
|
201
|
+
const resolvedRow = resolvedEntity !== null && typeof resolvedEntity === "object" && !Array.isArray(resolvedEntity) ? resolvedEntity : null;
|
|
202
|
+
const ctxRow = ctx.payload["row"];
|
|
203
|
+
renderLog.debug("interpolateProps:entity", {
|
|
204
|
+
patternType,
|
|
205
|
+
entityBinding: entityBindingRaw,
|
|
206
|
+
resolvedIsObject: resolvedRow !== null,
|
|
207
|
+
resolvedEqualsCtxRow: ctxRow !== void 0 && resolvedRow !== null && resolvedRow === ctxRow,
|
|
208
|
+
resolvedRowId: resolvedRow?.id
|
|
209
|
+
});
|
|
191
210
|
}
|
|
192
|
-
|
|
211
|
+
if (patternType === "form-section" || patternType === "form") {
|
|
212
|
+
const modeRaw = result["mode"];
|
|
213
|
+
const submitRaw = result["submitEvent"];
|
|
214
|
+
const cancelRaw = result["cancelEvent"];
|
|
215
|
+
bindLog.debug("form-binding", {
|
|
216
|
+
patternType,
|
|
217
|
+
mode: typeof modeRaw === "string" ? modeRaw : void 0,
|
|
218
|
+
submitEvent: typeof submitRaw === "string" ? submitRaw : void 0,
|
|
219
|
+
cancelEvent: typeof cancelRaw === "string" ? cancelRaw : void 0,
|
|
220
|
+
entity: JSON.stringify(result["entity"] ?? null),
|
|
221
|
+
fields: JSON.stringify(result["fields"] ?? null)
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
return anyChanged ? result : props;
|
|
193
225
|
}
|
|
194
226
|
function interpolateValue(value, ctx) {
|
|
195
227
|
if (value === null || value === void 0) {
|
|
@@ -235,12 +267,20 @@ function interpolateEmbeddedBindings(value, ctx) {
|
|
|
235
267
|
}
|
|
236
268
|
function interpolateArray(value, ctx) {
|
|
237
269
|
if (value.length === 0) {
|
|
238
|
-
return
|
|
270
|
+
return value;
|
|
239
271
|
}
|
|
240
272
|
if (isSExpression(value)) {
|
|
241
273
|
return evaluate(value, ctx);
|
|
242
274
|
}
|
|
243
|
-
|
|
275
|
+
const mapped = [];
|
|
276
|
+
let anyChanged = false;
|
|
277
|
+
for (let i = 0; i < value.length; i++) {
|
|
278
|
+
const item = value[i];
|
|
279
|
+
const interpolated = interpolateValue(item, ctx);
|
|
280
|
+
mapped.push(interpolated);
|
|
281
|
+
if (interpolated !== item) anyChanged = true;
|
|
282
|
+
}
|
|
283
|
+
return anyChanged ? mapped : value;
|
|
244
284
|
}
|
|
245
285
|
function isSExpression(value) {
|
|
246
286
|
if (value.length === 0) return false;
|
|
@@ -341,6 +381,7 @@ function processEvent(options) {
|
|
|
341
381
|
eventKey,
|
|
342
382
|
payload,
|
|
343
383
|
entityData,
|
|
384
|
+
config,
|
|
344
385
|
guardMode = "permissive",
|
|
345
386
|
strictBindings = false,
|
|
346
387
|
contextExtensions
|
|
@@ -375,7 +416,13 @@ function processEvent(options) {
|
|
|
375
416
|
const ctx = createContextFromBindings({
|
|
376
417
|
entity: entityData,
|
|
377
418
|
payload,
|
|
378
|
-
state: traitState.currentState
|
|
419
|
+
state: traitState.currentState,
|
|
420
|
+
// Surface call-site config so `@config.X` resolves inside
|
|
421
|
+
// guard expressions (matches the validator's allowed sigil
|
|
422
|
+
// list as of v3.12.0). createContextFromBindings already
|
|
423
|
+
// propagates `bindings.config` to ctx.config when present —
|
|
424
|
+
// we just need to pass it through here.
|
|
425
|
+
config
|
|
379
426
|
}, strictBindings, contextExtensions);
|
|
380
427
|
try {
|
|
381
428
|
const guardPasses = evaluateGuard(
|
|
@@ -455,6 +502,15 @@ function compositeKey(traitName, scope) {
|
|
|
455
502
|
}
|
|
456
503
|
var StateMachineManager = class {
|
|
457
504
|
traits = /* @__PURE__ */ new Map();
|
|
505
|
+
/**
|
|
506
|
+
* Per-trait call-site config, surfaced to guard expressions so
|
|
507
|
+
* `@config.X` resolves at runtime. Populated by the orbital's
|
|
508
|
+
* registration step (see `OrbitalServerRuntime.registerOrbital`'s
|
|
509
|
+
* `configByTrait` projection). Empty for atom-scope traits whose
|
|
510
|
+
* call-site config wasn't supplied — guards that read `@config.X`
|
|
511
|
+
* in that case will see `undefined` and short-circuit accordingly.
|
|
512
|
+
*/
|
|
513
|
+
traitConfigs = /* @__PURE__ */ new Map();
|
|
458
514
|
/**
|
|
459
515
|
* State map keyed by `${traitName}::${entityId | __singleton__}`.
|
|
460
516
|
*
|
|
@@ -497,6 +553,19 @@ var StateMachineManager = class {
|
|
|
497
553
|
addTrait(trait) {
|
|
498
554
|
this.traits.set(trait.name, trait);
|
|
499
555
|
}
|
|
556
|
+
/**
|
|
557
|
+
* Bind the call-site config for a trait so guard `@config.X`
|
|
558
|
+
* resolves at runtime. Typically called by the orbital
|
|
559
|
+
* registration step right after `addTrait`. Idempotent; passing
|
|
560
|
+
* `undefined` clears the binding.
|
|
561
|
+
*/
|
|
562
|
+
setTraitConfig(traitName, config) {
|
|
563
|
+
if (config === void 0) {
|
|
564
|
+
this.traitConfigs.delete(traitName);
|
|
565
|
+
} else {
|
|
566
|
+
this.traitConfigs.set(traitName, config);
|
|
567
|
+
}
|
|
568
|
+
}
|
|
500
569
|
/**
|
|
501
570
|
* Remove a trait from the manager.
|
|
502
571
|
*/
|
|
@@ -612,6 +681,7 @@ var StateMachineManager = class {
|
|
|
612
681
|
eventKey,
|
|
613
682
|
payload,
|
|
614
683
|
entityData,
|
|
684
|
+
config: this.traitConfigs.get(traitName),
|
|
615
685
|
guardMode: this.config.guardMode,
|
|
616
686
|
strictBindings: this.config.strictBindings,
|
|
617
687
|
contextExtensions: this.config.contextExtensions
|
|
@@ -681,6 +751,7 @@ var StateMachineManager = class {
|
|
|
681
751
|
eventKey: entry.eventKey,
|
|
682
752
|
payload: entry.payload,
|
|
683
753
|
entityData: entry.entityData,
|
|
754
|
+
config: this.traitConfigs.get(traitName),
|
|
684
755
|
guardMode: this.config.guardMode,
|
|
685
756
|
strictBindings: this.config.strictBindings,
|
|
686
757
|
contextExtensions: this.config.contextExtensions
|
|
@@ -1513,6 +1584,106 @@ function createTestExecutor(overrides = {}) {
|
|
|
1513
1584
|
debug: true
|
|
1514
1585
|
});
|
|
1515
1586
|
}
|
|
1587
|
+
|
|
1588
|
+
// src/PayloadValidator.ts
|
|
1589
|
+
function validateEventPayload(eventKey, payload, schema) {
|
|
1590
|
+
if (!schema || schema.length === 0) return [];
|
|
1591
|
+
const failures = [];
|
|
1592
|
+
for (const field of schema) {
|
|
1593
|
+
if (!field.required) continue;
|
|
1594
|
+
const value = payload?.[field.name];
|
|
1595
|
+
if (value === void 0) {
|
|
1596
|
+
failures.push({ event: eventKey, field: field.name, reason: "missing", expectedType: field.type });
|
|
1597
|
+
continue;
|
|
1598
|
+
}
|
|
1599
|
+
if (value === null) {
|
|
1600
|
+
failures.push({ event: eventKey, field: field.name, reason: "null", expectedType: field.type });
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
return failures;
|
|
1604
|
+
}
|
|
1605
|
+
function formatPayloadValidationError(failures) {
|
|
1606
|
+
if (failures.length === 0) return "";
|
|
1607
|
+
const parts = failures.map(
|
|
1608
|
+
(f) => `${f.field} (${f.reason}${f.expectedType ? `, expected ${f.expectedType}` : ""})`
|
|
1609
|
+
);
|
|
1610
|
+
return `Payload validation failed for event '${failures[0].event}': ${parts.join("; ")}`;
|
|
1611
|
+
}
|
|
1612
|
+
function validatePayloadShapes(traits, emits) {
|
|
1613
|
+
const mismatches = [];
|
|
1614
|
+
const emitIndex = /* @__PURE__ */ new Map();
|
|
1615
|
+
for (const [traitName, declarations] of emits) {
|
|
1616
|
+
for (const decl of declarations) {
|
|
1617
|
+
const fields = decl.payloadSchema?.map((p) => p.name) ?? [];
|
|
1618
|
+
emitIndex.set(decl.event, { traitName, fields });
|
|
1619
|
+
}
|
|
1620
|
+
}
|
|
1621
|
+
for (const trait of traits) {
|
|
1622
|
+
if (!trait.listens) continue;
|
|
1623
|
+
for (const listener of trait.listens) {
|
|
1624
|
+
const emitter = emitIndex.get(listener.event);
|
|
1625
|
+
if (!emitter) continue;
|
|
1626
|
+
if (!listener.payloadMapping) continue;
|
|
1627
|
+
const payloadRefs = extractPayloadReferences(listener.payloadMapping);
|
|
1628
|
+
for (const ref of payloadRefs) {
|
|
1629
|
+
if (!emitter.fields.includes(ref)) {
|
|
1630
|
+
mismatches.push({
|
|
1631
|
+
listenerTrait: trait.name,
|
|
1632
|
+
emitterTrait: emitter.traitName,
|
|
1633
|
+
event: listener.event,
|
|
1634
|
+
referencedField: ref,
|
|
1635
|
+
availableFields: emitter.fields
|
|
1636
|
+
});
|
|
1637
|
+
}
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1640
|
+
}
|
|
1641
|
+
return mismatches;
|
|
1642
|
+
}
|
|
1643
|
+
function extractPayloadReferences(mapping) {
|
|
1644
|
+
const refs = [];
|
|
1645
|
+
function collect(value) {
|
|
1646
|
+
if (typeof value === "string") {
|
|
1647
|
+
const match = value.match(/^@payload\.(\w+)$/);
|
|
1648
|
+
if (match) {
|
|
1649
|
+
refs.push(match[1]);
|
|
1650
|
+
}
|
|
1651
|
+
} else if (typeof value === "object" && value !== null) {
|
|
1652
|
+
if (Array.isArray(value)) {
|
|
1653
|
+
value.forEach(collect);
|
|
1654
|
+
} else {
|
|
1655
|
+
Object.values(value).forEach(collect);
|
|
1656
|
+
}
|
|
1657
|
+
}
|
|
1658
|
+
}
|
|
1659
|
+
Object.values(mapping).forEach(collect);
|
|
1660
|
+
return [...new Set(refs)];
|
|
1661
|
+
}
|
|
1662
|
+
function buildEmitsFromTraits(traits, explicitEmits) {
|
|
1663
|
+
const result = new Map(explicitEmits ?? []);
|
|
1664
|
+
for (const trait of traits) {
|
|
1665
|
+
if (result.has(trait.name)) continue;
|
|
1666
|
+
const emitDecls = [];
|
|
1667
|
+
for (const transition of trait.transitions) {
|
|
1668
|
+
if (!transition.effects) continue;
|
|
1669
|
+
for (const effect of transition.effects) {
|
|
1670
|
+
if (!Array.isArray(effect)) continue;
|
|
1671
|
+
if (effect[0] === "emit" && typeof effect[1] === "string") {
|
|
1672
|
+
const event = effect[1];
|
|
1673
|
+
const payloadObj = effect[2];
|
|
1674
|
+
const payloadSchema = payloadObj ? Object.keys(payloadObj).map((name) => ({ name })) : void 0;
|
|
1675
|
+
if (!emitDecls.some((d) => d.event === event)) {
|
|
1676
|
+
emitDecls.push({ event, payloadSchema });
|
|
1677
|
+
}
|
|
1678
|
+
}
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1681
|
+
if (emitDecls.length > 0) {
|
|
1682
|
+
result.set(trait.name, emitDecls);
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1685
|
+
return result;
|
|
1686
|
+
}
|
|
1516
1687
|
var MockPersistenceAdapter = class {
|
|
1517
1688
|
stores = /* @__PURE__ */ new Map();
|
|
1518
1689
|
schemas = /* @__PURE__ */ new Map();
|
|
@@ -3224,6 +3395,6 @@ var InMemoryPersistence = class {
|
|
|
3224
3395
|
}
|
|
3225
3396
|
};
|
|
3226
3397
|
|
|
3227
|
-
export { EffectExecutor, EventBus, HANDLER_MANIFEST, InMemoryPersistence, MockPersistenceAdapter, StateMachineManager, containsBindings, createContextFromBindings, createInitialTraitState, createLogger, createMockPersistence, createTestExecutor, createUnifiedLoader, extractBindings, findInitialState, findTransition, getIsolatedCollectionName, getNamespacedEvent, interpolateProps, interpolateValue, isBrowser, isElectron, isNamespacedEvent, isNode, normalizeEventKey, parseNamespacedEvent, preprocessSchema, processEvent };
|
|
3228
|
-
//# sourceMappingURL=chunk-
|
|
3229
|
-
//# sourceMappingURL=chunk-
|
|
3398
|
+
export { EffectExecutor, EventBus, HANDLER_MANIFEST, InMemoryPersistence, MockPersistenceAdapter, StateMachineManager, buildEmitsFromTraits, containsBindings, createContextFromBindings, createInitialTraitState, createLogger, createMockPersistence, createTestExecutor, createUnifiedLoader, extractBindings, findInitialState, findTransition, formatPayloadValidationError, getIsolatedCollectionName, getNamespacedEvent, interpolateProps, interpolateValue, isBrowser, isElectron, isNamespacedEvent, isNode, normalizeEventKey, parseNamespacedEvent, preprocessSchema, processEvent, validateEventPayload, validatePayloadShapes };
|
|
3399
|
+
//# sourceMappingURL=chunk-FAWWBERN.js.map
|
|
3400
|
+
//# sourceMappingURL=chunk-FAWWBERN.js.map
|