@almadar/runtime 5.0.0 → 5.2.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 +67 -2
- package/dist/OrbitalServerRuntime.js.map +1 -1
- package/dist/ServerBridge.d.ts +1 -1
- package/dist/{chunk-S5OLFFHT.js → chunk-OG2NHXES.js} +198 -10
- package/dist/chunk-OG2NHXES.js.map +1 -0
- package/dist/index.d.ts +45 -6
- package/dist/index.js +26 -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-S5OLFFHT.js.map +0 -1
package/dist/ServerBridge.d.ts
CHANGED
|
@@ -175,9 +175,8 @@ function createLogger(namespace) {
|
|
|
175
175
|
error: (msg, data) => log("ERROR", msg, data)
|
|
176
176
|
};
|
|
177
177
|
}
|
|
178
|
-
|
|
179
|
-
// src/BindingResolver.ts
|
|
180
178
|
var bindLog = createLogger("almadar:runtime:bindings");
|
|
179
|
+
var renderLog = createLogger("almadar:runtime:render-ui");
|
|
181
180
|
var CLIENT_ONLY_BINDING_ROOTS = /* @__PURE__ */ new Set(["trait"]);
|
|
182
181
|
function isClientOnlyBinding(value) {
|
|
183
182
|
if (!value.startsWith("@")) return false;
|
|
@@ -188,10 +187,27 @@ function isClientOnlyBinding(value) {
|
|
|
188
187
|
}
|
|
189
188
|
function interpolateProps(props, ctx) {
|
|
190
189
|
const result = {};
|
|
190
|
+
let anyChanged = false;
|
|
191
191
|
for (const [key, value] of Object.entries(props)) {
|
|
192
|
-
|
|
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
|
+
if (typeof entityBindingRaw === "string") {
|
|
199
|
+
const resolvedEntity = result["entity"];
|
|
200
|
+
const resolvedRow = resolvedEntity !== null && typeof resolvedEntity === "object" && !Array.isArray(resolvedEntity) ? resolvedEntity : null;
|
|
201
|
+
const ctxRow = ctx.payload["row"];
|
|
202
|
+
renderLog.debug("interpolateProps:entity", {
|
|
203
|
+
patternType: typeof typeBindingRaw === "string" ? typeBindingRaw : void 0,
|
|
204
|
+
entityBinding: entityBindingRaw,
|
|
205
|
+
resolvedIsObject: resolvedRow !== null,
|
|
206
|
+
resolvedEqualsCtxRow: ctxRow !== void 0 && resolvedRow !== null && resolvedRow === ctxRow,
|
|
207
|
+
resolvedRowId: resolvedRow?.id
|
|
208
|
+
});
|
|
193
209
|
}
|
|
194
|
-
return result;
|
|
210
|
+
return anyChanged ? result : props;
|
|
195
211
|
}
|
|
196
212
|
function interpolateValue(value, ctx) {
|
|
197
213
|
if (value === null || value === void 0) {
|
|
@@ -237,12 +253,20 @@ function interpolateEmbeddedBindings(value, ctx) {
|
|
|
237
253
|
}
|
|
238
254
|
function interpolateArray(value, ctx) {
|
|
239
255
|
if (value.length === 0) {
|
|
240
|
-
return
|
|
256
|
+
return value;
|
|
241
257
|
}
|
|
242
258
|
if (isSExpression(value)) {
|
|
243
259
|
return evaluate(value, ctx);
|
|
244
260
|
}
|
|
245
|
-
|
|
261
|
+
const mapped = [];
|
|
262
|
+
let anyChanged = false;
|
|
263
|
+
for (let i = 0; i < value.length; i++) {
|
|
264
|
+
const item = value[i];
|
|
265
|
+
const interpolated = interpolateValue(item, ctx);
|
|
266
|
+
mapped.push(interpolated);
|
|
267
|
+
if (interpolated !== item) anyChanged = true;
|
|
268
|
+
}
|
|
269
|
+
return anyChanged ? mapped : value;
|
|
246
270
|
}
|
|
247
271
|
function isSExpression(value) {
|
|
248
272
|
if (value.length === 0) return false;
|
|
@@ -343,6 +367,7 @@ function processEvent(options) {
|
|
|
343
367
|
eventKey,
|
|
344
368
|
payload,
|
|
345
369
|
entityData,
|
|
370
|
+
config,
|
|
346
371
|
guardMode = "permissive",
|
|
347
372
|
strictBindings = false,
|
|
348
373
|
contextExtensions
|
|
@@ -377,7 +402,13 @@ function processEvent(options) {
|
|
|
377
402
|
const ctx = createContextFromBindings({
|
|
378
403
|
entity: entityData,
|
|
379
404
|
payload,
|
|
380
|
-
state: traitState.currentState
|
|
405
|
+
state: traitState.currentState,
|
|
406
|
+
// Surface call-site config so `@config.X` resolves inside
|
|
407
|
+
// guard expressions (matches the validator's allowed sigil
|
|
408
|
+
// list as of v3.12.0). createContextFromBindings already
|
|
409
|
+
// propagates `bindings.config` to ctx.config when present —
|
|
410
|
+
// we just need to pass it through here.
|
|
411
|
+
config
|
|
381
412
|
}, strictBindings, contextExtensions);
|
|
382
413
|
try {
|
|
383
414
|
const guardPasses = evaluateGuard(
|
|
@@ -457,6 +488,15 @@ function compositeKey(traitName, scope) {
|
|
|
457
488
|
}
|
|
458
489
|
var StateMachineManager = class {
|
|
459
490
|
traits = /* @__PURE__ */ new Map();
|
|
491
|
+
/**
|
|
492
|
+
* Per-trait call-site config, surfaced to guard expressions so
|
|
493
|
+
* `@config.X` resolves at runtime. Populated by the orbital's
|
|
494
|
+
* registration step (see `OrbitalServerRuntime.registerOrbital`'s
|
|
495
|
+
* `configByTrait` projection). Empty for atom-scope traits whose
|
|
496
|
+
* call-site config wasn't supplied — guards that read `@config.X`
|
|
497
|
+
* in that case will see `undefined` and short-circuit accordingly.
|
|
498
|
+
*/
|
|
499
|
+
traitConfigs = /* @__PURE__ */ new Map();
|
|
460
500
|
/**
|
|
461
501
|
* State map keyed by `${traitName}::${entityId | __singleton__}`.
|
|
462
502
|
*
|
|
@@ -499,6 +539,19 @@ var StateMachineManager = class {
|
|
|
499
539
|
addTrait(trait) {
|
|
500
540
|
this.traits.set(trait.name, trait);
|
|
501
541
|
}
|
|
542
|
+
/**
|
|
543
|
+
* Bind the call-site config for a trait so guard `@config.X`
|
|
544
|
+
* resolves at runtime. Typically called by the orbital
|
|
545
|
+
* registration step right after `addTrait`. Idempotent; passing
|
|
546
|
+
* `undefined` clears the binding.
|
|
547
|
+
*/
|
|
548
|
+
setTraitConfig(traitName, config) {
|
|
549
|
+
if (config === void 0) {
|
|
550
|
+
this.traitConfigs.delete(traitName);
|
|
551
|
+
} else {
|
|
552
|
+
this.traitConfigs.set(traitName, config);
|
|
553
|
+
}
|
|
554
|
+
}
|
|
502
555
|
/**
|
|
503
556
|
* Remove a trait from the manager.
|
|
504
557
|
*/
|
|
@@ -614,6 +667,7 @@ var StateMachineManager = class {
|
|
|
614
667
|
eventKey,
|
|
615
668
|
payload,
|
|
616
669
|
entityData,
|
|
670
|
+
config: this.traitConfigs.get(traitName),
|
|
617
671
|
guardMode: this.config.guardMode,
|
|
618
672
|
strictBindings: this.config.strictBindings,
|
|
619
673
|
contextExtensions: this.config.contextExtensions
|
|
@@ -683,6 +737,7 @@ var StateMachineManager = class {
|
|
|
683
737
|
eventKey: entry.eventKey,
|
|
684
738
|
payload: entry.payload,
|
|
685
739
|
entityData: entry.entityData,
|
|
740
|
+
config: this.traitConfigs.get(traitName),
|
|
686
741
|
guardMode: this.config.guardMode,
|
|
687
742
|
strictBindings: this.config.strictBindings,
|
|
688
743
|
contextExtensions: this.config.contextExtensions
|
|
@@ -1108,18 +1163,51 @@ var EffectExecutor = class {
|
|
|
1108
1163
|
const action = args[0];
|
|
1109
1164
|
const last = args[args.length - 1];
|
|
1110
1165
|
const emitCfg = last && typeof last === "object" && !Array.isArray(last) && "emit" in last ? this.extractEmitConfig(last) : void 0;
|
|
1166
|
+
effectLog.debug("persist:dispatch", {
|
|
1167
|
+
action,
|
|
1168
|
+
argCount: args.length,
|
|
1169
|
+
argTypes: args.map((a) => Array.isArray(a) ? "array" : a === null ? "null" : typeof a).join(","),
|
|
1170
|
+
traitName: this.context.traitName,
|
|
1171
|
+
transition: this.context.transition
|
|
1172
|
+
});
|
|
1173
|
+
effectLog.debug("persist:emit-config", {
|
|
1174
|
+
action,
|
|
1175
|
+
hasEmitCfg: emitCfg !== void 0,
|
|
1176
|
+
success: emitCfg?.success,
|
|
1177
|
+
failure: emitCfg?.failure
|
|
1178
|
+
});
|
|
1111
1179
|
try {
|
|
1112
1180
|
if (action === "batch") {
|
|
1113
1181
|
const operations = args[1];
|
|
1114
1182
|
await this.handlers.persist("batch", "", { operations });
|
|
1183
|
+
effectLog.debug("persist:success", {
|
|
1184
|
+
action,
|
|
1185
|
+
entityType: "batch",
|
|
1186
|
+
opCount: operations.length,
|
|
1187
|
+
willEmit: emitCfg?.success
|
|
1188
|
+
});
|
|
1115
1189
|
this.emitSuccess(emitCfg, "success", operations);
|
|
1190
|
+
effectLog.debug("persist:emit-fired", { action, eventName: emitCfg?.success });
|
|
1116
1191
|
} else {
|
|
1117
1192
|
const entityType = args[1];
|
|
1118
1193
|
const data = args[2];
|
|
1119
1194
|
await this.handlers.persist(action, entityType, data);
|
|
1195
|
+
const dataId = typeof data === "string" ? data : data && typeof data === "object" ? data.id : void 0;
|
|
1196
|
+
effectLog.debug("persist:success", {
|
|
1197
|
+
action,
|
|
1198
|
+
entityType,
|
|
1199
|
+
dataId,
|
|
1200
|
+
willEmit: emitCfg?.success
|
|
1201
|
+
});
|
|
1120
1202
|
this.emitSuccess(emitCfg, "success", data);
|
|
1203
|
+
effectLog.debug("persist:emit-fired", { action, eventName: emitCfg?.success });
|
|
1121
1204
|
}
|
|
1122
1205
|
} catch (err) {
|
|
1206
|
+
effectLog.error("persist:error", {
|
|
1207
|
+
action,
|
|
1208
|
+
entityType: action === "batch" ? "batch" : args[1],
|
|
1209
|
+
error: err instanceof Error ? err.message : String(err)
|
|
1210
|
+
});
|
|
1123
1211
|
this.emitFailure(emitCfg, err);
|
|
1124
1212
|
throw err;
|
|
1125
1213
|
}
|
|
@@ -1482,6 +1570,106 @@ function createTestExecutor(overrides = {}) {
|
|
|
1482
1570
|
debug: true
|
|
1483
1571
|
});
|
|
1484
1572
|
}
|
|
1573
|
+
|
|
1574
|
+
// src/PayloadValidator.ts
|
|
1575
|
+
function validateEventPayload(eventKey, payload, schema) {
|
|
1576
|
+
if (!schema || schema.length === 0) return [];
|
|
1577
|
+
const failures = [];
|
|
1578
|
+
for (const field of schema) {
|
|
1579
|
+
if (!field.required) continue;
|
|
1580
|
+
const value = payload?.[field.name];
|
|
1581
|
+
if (value === void 0) {
|
|
1582
|
+
failures.push({ event: eventKey, field: field.name, reason: "missing", expectedType: field.type });
|
|
1583
|
+
continue;
|
|
1584
|
+
}
|
|
1585
|
+
if (value === null) {
|
|
1586
|
+
failures.push({ event: eventKey, field: field.name, reason: "null", expectedType: field.type });
|
|
1587
|
+
}
|
|
1588
|
+
}
|
|
1589
|
+
return failures;
|
|
1590
|
+
}
|
|
1591
|
+
function formatPayloadValidationError(failures) {
|
|
1592
|
+
if (failures.length === 0) return "";
|
|
1593
|
+
const parts = failures.map(
|
|
1594
|
+
(f) => `${f.field} (${f.reason}${f.expectedType ? `, expected ${f.expectedType}` : ""})`
|
|
1595
|
+
);
|
|
1596
|
+
return `Payload validation failed for event '${failures[0].event}': ${parts.join("; ")}`;
|
|
1597
|
+
}
|
|
1598
|
+
function validatePayloadShapes(traits, emits) {
|
|
1599
|
+
const mismatches = [];
|
|
1600
|
+
const emitIndex = /* @__PURE__ */ new Map();
|
|
1601
|
+
for (const [traitName, declarations] of emits) {
|
|
1602
|
+
for (const decl of declarations) {
|
|
1603
|
+
const fields = decl.payloadSchema?.map((p) => p.name) ?? [];
|
|
1604
|
+
emitIndex.set(decl.event, { traitName, fields });
|
|
1605
|
+
}
|
|
1606
|
+
}
|
|
1607
|
+
for (const trait of traits) {
|
|
1608
|
+
if (!trait.listens) continue;
|
|
1609
|
+
for (const listener of trait.listens) {
|
|
1610
|
+
const emitter = emitIndex.get(listener.event);
|
|
1611
|
+
if (!emitter) continue;
|
|
1612
|
+
if (!listener.payloadMapping) continue;
|
|
1613
|
+
const payloadRefs = extractPayloadReferences(listener.payloadMapping);
|
|
1614
|
+
for (const ref of payloadRefs) {
|
|
1615
|
+
if (!emitter.fields.includes(ref)) {
|
|
1616
|
+
mismatches.push({
|
|
1617
|
+
listenerTrait: trait.name,
|
|
1618
|
+
emitterTrait: emitter.traitName,
|
|
1619
|
+
event: listener.event,
|
|
1620
|
+
referencedField: ref,
|
|
1621
|
+
availableFields: emitter.fields
|
|
1622
|
+
});
|
|
1623
|
+
}
|
|
1624
|
+
}
|
|
1625
|
+
}
|
|
1626
|
+
}
|
|
1627
|
+
return mismatches;
|
|
1628
|
+
}
|
|
1629
|
+
function extractPayloadReferences(mapping) {
|
|
1630
|
+
const refs = [];
|
|
1631
|
+
function collect(value) {
|
|
1632
|
+
if (typeof value === "string") {
|
|
1633
|
+
const match = value.match(/^@payload\.(\w+)$/);
|
|
1634
|
+
if (match) {
|
|
1635
|
+
refs.push(match[1]);
|
|
1636
|
+
}
|
|
1637
|
+
} else if (typeof value === "object" && value !== null) {
|
|
1638
|
+
if (Array.isArray(value)) {
|
|
1639
|
+
value.forEach(collect);
|
|
1640
|
+
} else {
|
|
1641
|
+
Object.values(value).forEach(collect);
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
Object.values(mapping).forEach(collect);
|
|
1646
|
+
return [...new Set(refs)];
|
|
1647
|
+
}
|
|
1648
|
+
function buildEmitsFromTraits(traits, explicitEmits) {
|
|
1649
|
+
const result = new Map(explicitEmits ?? []);
|
|
1650
|
+
for (const trait of traits) {
|
|
1651
|
+
if (result.has(trait.name)) continue;
|
|
1652
|
+
const emitDecls = [];
|
|
1653
|
+
for (const transition of trait.transitions) {
|
|
1654
|
+
if (!transition.effects) continue;
|
|
1655
|
+
for (const effect of transition.effects) {
|
|
1656
|
+
if (!Array.isArray(effect)) continue;
|
|
1657
|
+
if (effect[0] === "emit" && typeof effect[1] === "string") {
|
|
1658
|
+
const event = effect[1];
|
|
1659
|
+
const payloadObj = effect[2];
|
|
1660
|
+
const payloadSchema = payloadObj ? Object.keys(payloadObj).map((name) => ({ name })) : void 0;
|
|
1661
|
+
if (!emitDecls.some((d) => d.event === event)) {
|
|
1662
|
+
emitDecls.push({ event, payloadSchema });
|
|
1663
|
+
}
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1666
|
+
}
|
|
1667
|
+
if (emitDecls.length > 0) {
|
|
1668
|
+
result.set(trait.name, emitDecls);
|
|
1669
|
+
}
|
|
1670
|
+
}
|
|
1671
|
+
return result;
|
|
1672
|
+
}
|
|
1485
1673
|
var MockPersistenceAdapter = class {
|
|
1486
1674
|
stores = /* @__PURE__ */ new Map();
|
|
1487
1675
|
schemas = /* @__PURE__ */ new Map();
|
|
@@ -3193,6 +3381,6 @@ var InMemoryPersistence = class {
|
|
|
3193
3381
|
}
|
|
3194
3382
|
};
|
|
3195
3383
|
|
|
3196
|
-
export { EffectExecutor, EventBus, HANDLER_MANIFEST, InMemoryPersistence, MockPersistenceAdapter, StateMachineManager, containsBindings, createContextFromBindings, createInitialTraitState, createMockPersistence, createTestExecutor, createUnifiedLoader, extractBindings, findInitialState, findTransition, getIsolatedCollectionName, getNamespacedEvent, interpolateProps, interpolateValue, isBrowser, isElectron, isNamespacedEvent, isNode, normalizeEventKey, parseNamespacedEvent, preprocessSchema, processEvent };
|
|
3197
|
-
//# sourceMappingURL=chunk-
|
|
3198
|
-
//# sourceMappingURL=chunk-
|
|
3384
|
+
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 };
|
|
3385
|
+
//# sourceMappingURL=chunk-OG2NHXES.js.map
|
|
3386
|
+
//# sourceMappingURL=chunk-OG2NHXES.js.map
|