@almadar/runtime 6.74.0 → 6.76.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-BiEntWd-.d.ts → OrbitalServerRuntime-eSaLgPR6.d.ts} +31 -18
- package/dist/OrbitalServerRuntime.d.ts +2 -2
- package/dist/OrbitalServerRuntime.js +55 -15
- package/dist/ServerBridge.d.ts +1 -1
- package/dist/{chunk-QUW37QRQ.js → chunk-KAGXDW33.js} +107 -38
- package/dist/createOsHandlers.d.ts +1 -1
- package/dist/index.d.ts +65 -17
- package/dist/index.js +3 -6
- package/dist/{types-CWaIuEQn.d.ts → types-BO4tbEPp.d.ts} +0 -2
- package/package.json +4 -4
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Router } from 'express';
|
|
2
|
-
import { I as IEventBus, R as RuntimeEvent, a as EventListener, U as Unsubscribe, T as TraitDefinition, b as RuntimeConfig, c as TransitionObserver, d as TraitState, C as ConfigContext, e as TransitionResult, f as EvaluationContextExtensions, B as BindingContext, g as EffectContext, S as ServiceCallContext, E as EffectHandlers } from './types-
|
|
3
|
-
import { EventPayload, EventId, EntityRow, UserContext, ServiceParams, EntityAccessPolicies, Orbital, ServiceCallResult, TraitConfig, DeclaredTraitConfig, Entity, OrbitalSchema, Trait, PatternConfig, ResolvedPatternProps,
|
|
2
|
+
import { I as IEventBus, R as RuntimeEvent, a as EventListener, U as Unsubscribe, T as TraitDefinition, b as RuntimeConfig, c as TransitionObserver, d as TraitState, C as ConfigContext, e as TransitionResult, f as EvaluationContextExtensions, B as BindingContext, g as EffectContext, S as ServiceCallContext, E as EffectHandlers } from './types-BO4tbEPp.js';
|
|
3
|
+
import { EventPayload, EventId, EntityRow, UserContext, ServiceParams, EntityAccessPolicies, Orbital, ServiceCallResult, TraitConfig, DeclaredTraitConfig, Entity, OrbitalSchema, Trait, PatternConfig, ResolvedPatternProps, BusEventSource, RawUserClaims, OrbitalDefinition, TraitTick } from '@almadar/core';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* EventBus - Platform-Agnostic Pub/Sub Implementation
|
|
@@ -538,7 +538,7 @@ interface CreateServerEffectHandlersOptions {
|
|
|
538
538
|
* transition, call this factory again.
|
|
539
539
|
*
|
|
540
540
|
* Intentionally does NOT implement:
|
|
541
|
-
* - `renderUI` / `
|
|
541
|
+
* - `renderUI` / `navigate` — these are client-side, provided
|
|
542
542
|
* by `createClientEffectHandlers`. A mock runtime merges both handler
|
|
543
543
|
* sets.
|
|
544
544
|
* - `os/watch-*` observers — these are a no-op outside the server.
|
|
@@ -1030,19 +1030,19 @@ declare function parseNamespacedEvent(eventName: string): {
|
|
|
1030
1030
|
|
|
1031
1031
|
/**
|
|
1032
1032
|
* Client-side effect tuple shipped in `OrbitalEventResponse.clientEffects`.
|
|
1033
|
-
* Restricted to the
|
|
1034
|
-
* UI: render-ui (slot mutation
|
|
1033
|
+
* Restricted to the effect kinds the client knows how to apply to the
|
|
1034
|
+
* UI: render-ui (slot mutation, including the `toast` slot that carries
|
|
1035
|
+
* what used to be the separate notify effect) and navigate (route change).
|
|
1035
1036
|
* Server-only effects (persist, set, fetch, ...) execute on the server and
|
|
1036
1037
|
* surface their results via `effectResults` and `emittedEvents`, not here.
|
|
1037
1038
|
*
|
|
1038
1039
|
* Shape note: the wire form is broader than `RenderUIEffect` /
|
|
1039
|
-
* `NavigateEffect`
|
|
1040
|
-
*
|
|
1041
|
-
*
|
|
1042
|
-
*
|
|
1043
|
-
*
|
|
1044
|
-
*
|
|
1045
|
-
* instead of `unknown[]`.
|
|
1040
|
+
* `NavigateEffect` from `@almadar/core/types/effect.ts` in two places — the
|
|
1041
|
+
* runtime appends an interpolated `props` object plus an optional `priority`
|
|
1042
|
+
* slot to render-ui (slot 4 + 5), and `navigate`'s optional argument may
|
|
1043
|
+
* surface as `undefined` rather than absent. The local union spells those
|
|
1044
|
+
* variants out explicitly so consumers (ServerBridge.tsx parser, debugger
|
|
1045
|
+
* inspector) get accurate completions instead of `unknown[]`.
|
|
1046
1046
|
*
|
|
1047
1047
|
* `slot` is a plain `string` in this wire shape (not `UISlot`'s literal
|
|
1048
1048
|
* union) because the runtime forwards the raw string from the trait's
|
|
@@ -1054,10 +1054,7 @@ type ClientNavigateTuple = ['navigate', string] | ['navigate', string, Record<st
|
|
|
1054
1054
|
crumb?: string;
|
|
1055
1055
|
}];
|
|
1056
1056
|
type ClientNavigateBackTuple = ['navigate-back'];
|
|
1057
|
-
type
|
|
1058
|
-
type?: string;
|
|
1059
|
-
}];
|
|
1060
|
-
type ClientEffectTuple = ClientRenderUITuple | ClientNavigateTuple | ClientNavigateBackTuple | ClientNotifyTuple;
|
|
1057
|
+
type ClientEffectTuple = ClientRenderUITuple | ClientNavigateTuple | ClientNavigateBackTuple;
|
|
1061
1058
|
|
|
1062
1059
|
/** @deprecated Use OrbitalSchema from @almadar/core */
|
|
1063
1060
|
type RuntimeOrbitalSchema = OrbitalSchema;
|
|
@@ -1174,7 +1171,7 @@ interface OrbitalEventResponse {
|
|
|
1174
1171
|
payload?: EventPayload;
|
|
1175
1172
|
source?: BusEventSource;
|
|
1176
1173
|
}>;
|
|
1177
|
-
/** Client-side effects to execute (render-ui, navigate
|
|
1174
|
+
/** Client-side effects to execute (render-ui, navigate) */
|
|
1178
1175
|
clientEffects?: ClientEffectTuple[];
|
|
1179
1176
|
/**
|
|
1180
1177
|
* Same effects as `clientEffects`, paired with the producing trait name.
|
|
@@ -1638,6 +1635,22 @@ declare class OrbitalServerRuntime {
|
|
|
1638
1635
|
* entity, its auxiliary entities, then other registered orbitals' primary
|
|
1639
1636
|
* entities (cross-orbital binds like a membership rail in the chat page). */
|
|
1640
1637
|
private isSharedEntity;
|
|
1638
|
+
/** Resolve an entity by name the same way `isSharedEntity` does: the
|
|
1639
|
+
* orbital's own entity, its auxiliary entities, then other registered
|
|
1640
|
+
* orbitals' primary entities (cross-orbital binds like a membership rail
|
|
1641
|
+
* in the chat page). The one entity-resolution walk shared by every
|
|
1642
|
+
* field-level lookup (`intrinsicFieldNames`, `entityFieldsFor`) so a new
|
|
1643
|
+
* lookup never grows a second copy of this walk. */
|
|
1644
|
+
private resolveEntityByName;
|
|
1645
|
+
/** Names of an entity's `@intrinsic` fields — trait-owned view state
|
|
1646
|
+
* (`ChatMessage.activeChannel`, `ChannelMember.pendingChannelId`, …) that
|
|
1647
|
+
* is NEVER a persisted column. */
|
|
1648
|
+
private intrinsicFieldNames;
|
|
1649
|
+
/** Full declared field list for an entity — the schema source `persist
|
|
1650
|
+
* create`'s required-column check reads (`EffectExecutor.
|
|
1651
|
+
* resolveEntityFields`), via the same `resolveEntityByName` walk
|
|
1652
|
+
* `intrinsicFieldNames` uses. */
|
|
1653
|
+
private entityFieldsFor;
|
|
1641
1654
|
/**
|
|
1642
1655
|
* Process an event for an orbital
|
|
1643
1656
|
*
|
|
@@ -1763,4 +1776,4 @@ declare class OrbitalServerRuntime {
|
|
|
1763
1776
|
*/
|
|
1764
1777
|
declare function createOrbitalServerRuntime(config?: OrbitalServerRuntimeConfig): OrbitalServerRuntime;
|
|
1765
1778
|
|
|
1766
|
-
export { isElectron as A, isNamespacedEvent as B, type CreateServerEffectHandlersOptions as C, isNode as D, type EntitySharingMap as E, normalizeEventKey as F, parseNamespacedEvent as G, preprocessSchema as H, type ImportChainLike as I, processEvent as J, type ClientEffectTuple as K, type LoadResult as L, type ClientNavigateBackTuple as M, type ClientNavigateTuple as N, type OrbitalEventRequest as O, type PersistenceAdapter as P, type
|
|
1779
|
+
export { isElectron as A, isNamespacedEvent as B, type CreateServerEffectHandlersOptions as C, isNode as D, type EntitySharingMap as E, normalizeEventKey as F, parseNamespacedEvent as G, preprocessSchema as H, type ImportChainLike as I, processEvent as J, type ClientEffectTuple as K, type LoadResult as L, type ClientNavigateBackTuple as M, type ClientNavigateTuple as N, type OrbitalEventRequest as O, type PersistenceAdapter as P, type ClientRenderUITuple as Q, type RegisteredOrbital as R, type SchemaLoader as S, type LiveBroadcastItem as T, type UnifiedLoaderOptions as U, type LoaderConfig as V, OrbitalServerRuntime as W, type RuntimeTraitTick as X, createOrbitalServerRuntime as Y, type LoadedSchema as a, type LoadedOrbital as b, EventBus as c, type EventNamespaceMap as d, InMemoryPersistence as e, LIFECYCLE_EVENTS as f, type OrbitalEventResponse as g, type OrbitalServerRuntimeConfig as h, type PreprocessOptions as i, type PreprocessResult as j, type PreprocessedSchema as k, type ProcessEventOptions as l, type RuntimeOrbital as m, type RuntimeOrbitalSchema as n, type RuntimeTrait as o, type ServerEffectResult as p, StateMachineManager as q, collectDeclaredConfigDefaults as r, collectDeclaredEntityDefaults as s, createInitialTraitState as t, createServerEffectHandlers as u, findInitialState as v, findTransition as w, getIsolatedCollectionName as x, getNamespacedEvent as y, isBrowser as z };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import 'express';
|
|
2
|
-
export { K as ClientEffectTuple, M as ClientNavigateBackTuple, N as ClientNavigateTuple, Q as
|
|
3
|
-
import './types-
|
|
2
|
+
export { K as ClientEffectTuple, M as ClientNavigateBackTuple, N as ClientNavigateTuple, Q as ClientRenderUITuple, e as InMemoryPersistence, T as LiveBroadcastItem, V as LoaderConfig, O as OrbitalEventRequest, g as OrbitalEventResponse, W as OrbitalServerRuntime, h as OrbitalServerRuntimeConfig, P as PersistenceAdapter, R as RegisteredOrbital, m as RuntimeOrbital, n as RuntimeOrbitalSchema, o as RuntimeTrait, X as RuntimeTraitTick, r as collectDeclaredConfigDefaults, Y as createOrbitalServerRuntime } from './OrbitalServerRuntime-eSaLgPR6.js';
|
|
3
|
+
import './types-BO4tbEPp.js';
|
|
4
4
|
import '@almadar/core';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { createTickScheduler, EventBus, createUnifiedLoader, MockPersistenceAdapter, InMemoryPersistence, preprocessSchema, normalizeCallSiteConfigToValues, StateMachineManager, parseDurationString, validateEventPayload, formatPayloadValidationError, collectDeclaredConfigDefaults, collectDeclaredEntityDefaults, EffectExecutor, LIFECYCLE_EVENTS } from './chunk-
|
|
2
|
-
export { InMemoryPersistence, collectDeclaredConfigDefaults } from './chunk-
|
|
1
|
+
import { createTickScheduler, EventBus, createUnifiedLoader, MockPersistenceAdapter, InMemoryPersistence, preprocessSchema, normalizeCallSiteConfigToValues, StateMachineManager, parseDurationString, validateEventPayload, formatPayloadValidationError, collectDeclaredConfigDefaults, collectDeclaredEntityDefaults, EffectExecutor, LIFECYCLE_EVENTS } from './chunk-KAGXDW33.js';
|
|
2
|
+
export { InMemoryPersistence, collectDeclaredConfigDefaults } from './chunk-KAGXDW33.js';
|
|
3
3
|
import { isValidCronExpression } from './chunk-OU3ITB5S.js';
|
|
4
4
|
import { createContextFromBindings, checkMutationAccess, applyRowAccess, accessDeniedMessage } from './chunk-ZWAOJT6R.js';
|
|
5
5
|
import './chunk-T4VDAB4C.js';
|
|
@@ -8,7 +8,7 @@ import './chunk-MLKGABMK.js';
|
|
|
8
8
|
import { createLogger } from '@almadar/logger';
|
|
9
9
|
import * as nodeModule from 'module';
|
|
10
10
|
import { evaluateListenPayloadExpr, evaluateGuard, evaluate } from '@almadar/evaluator';
|
|
11
|
-
import { DEFAULT_VIEWER, buildResolvedTraitConfigs, collectCallsiteCaptureChildren, isInlineTrait, isEntityCall, applyListenPayloadMapping, personaFromIdentityRow, normalizeUserContext, isRuntimeEntity, isPageReference } from '@almadar/core';
|
|
11
|
+
import { DEFAULT_VIEWER, buildResolvedTraitConfigs, collectCallsiteCaptureChildren, isInlineTrait, isEntityCall, applyListenPayloadMapping, personaFromIdentityRow, normalizeUserContext, isRuntimeEntity, omitFrameFields, isPageReference } from '@almadar/core';
|
|
12
12
|
import { ownerFieldsFromSchema, identityEntityName, entityAccessPoliciesByStoreKey, entityAccessPolicies } from '@almadar/core/mock';
|
|
13
13
|
import { getPatternFieldsContract } from '@almadar/core/patterns';
|
|
14
14
|
|
|
@@ -1266,6 +1266,37 @@ var OrbitalServerRuntime = class {
|
|
|
1266
1266
|
}
|
|
1267
1267
|
return false;
|
|
1268
1268
|
}
|
|
1269
|
+
/** Resolve an entity by name the same way `isSharedEntity` does: the
|
|
1270
|
+
* orbital's own entity, its auxiliary entities, then other registered
|
|
1271
|
+
* orbitals' primary entities (cross-orbital binds like a membership rail
|
|
1272
|
+
* in the chat page). The one entity-resolution walk shared by every
|
|
1273
|
+
* field-level lookup (`intrinsicFieldNames`, `entityFieldsFor`) so a new
|
|
1274
|
+
* lookup never grows a second copy of this walk. */
|
|
1275
|
+
resolveEntityByName(registered, entityName) {
|
|
1276
|
+
if (registered.entity.name === entityName) return registered.entity;
|
|
1277
|
+
for (const aux of registered.schema.auxiliaryEntities ?? []) {
|
|
1278
|
+
if (typeof aux === "object" && !isEntityCall(aux) && aux.name === entityName) {
|
|
1279
|
+
return aux;
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
for (const other of this.orbitals.values()) {
|
|
1283
|
+
if (other.entity.name === entityName) return other.entity;
|
|
1284
|
+
}
|
|
1285
|
+
return void 0;
|
|
1286
|
+
}
|
|
1287
|
+
/** Names of an entity's `@intrinsic` fields — trait-owned view state
|
|
1288
|
+
* (`ChatMessage.activeChannel`, `ChannelMember.pendingChannelId`, …) that
|
|
1289
|
+
* is NEVER a persisted column. */
|
|
1290
|
+
intrinsicFieldNames(registered, entityName) {
|
|
1291
|
+
return this.entityFieldsFor(registered, entityName).filter((field) => field.intrinsic === true && typeof field.name === "string").map((field) => field.name);
|
|
1292
|
+
}
|
|
1293
|
+
/** Full declared field list for an entity — the schema source `persist
|
|
1294
|
+
* create`'s required-column check reads (`EffectExecutor.
|
|
1295
|
+
* resolveEntityFields`), via the same `resolveEntityByName` walk
|
|
1296
|
+
* `intrinsicFieldNames` uses. */
|
|
1297
|
+
entityFieldsFor(registered, entityName) {
|
|
1298
|
+
return this.resolveEntityByName(registered, entityName)?.fields ?? [];
|
|
1299
|
+
}
|
|
1269
1300
|
/**
|
|
1270
1301
|
* Process an event for an orbital
|
|
1271
1302
|
*
|
|
@@ -1321,8 +1352,14 @@ var OrbitalServerRuntime = class {
|
|
|
1321
1352
|
const { event, eventId, payload, entityId, user: requestUser, clientId } = request;
|
|
1322
1353
|
const viewer = normalizeUserContext(requestUser) ?? this.config.defaultUser;
|
|
1323
1354
|
const targetTrait = request.targetTrait ?? payload?.["_targetTrait"];
|
|
1355
|
+
const activeTraits = payload?._activeTraits;
|
|
1356
|
+
const dispatchTargetTraits = registered.traits.filter((trait) => {
|
|
1357
|
+
if (targetTrait !== void 0) return trait.name === targetTrait;
|
|
1358
|
+
if (activeTraits && activeTraits.length > 0 && !activeTraits.includes(trait.name)) return false;
|
|
1359
|
+
return registered.manager.canHandleEvent(trait.name, event, entityId, eventId);
|
|
1360
|
+
});
|
|
1324
1361
|
const validationFailures = [];
|
|
1325
|
-
for (const trait of
|
|
1362
|
+
for (const trait of dispatchTargetTraits) {
|
|
1326
1363
|
const eventSchema = trait.stateMachine?.events?.find((e) => e.key === event);
|
|
1327
1364
|
if (eventSchema?.payloadSchema && eventSchema.payloadSchema.length > 0) {
|
|
1328
1365
|
validationFailures.push(
|
|
@@ -1344,7 +1381,6 @@ var OrbitalServerRuntime = class {
|
|
|
1344
1381
|
const clientEffects = [];
|
|
1345
1382
|
const clientEffectsByTrait = [];
|
|
1346
1383
|
const effectResults = [];
|
|
1347
|
-
const activeTraits = payload?._activeTraits;
|
|
1348
1384
|
const cleanPayload = payload ? { ...payload } : void 0;
|
|
1349
1385
|
if (cleanPayload) {
|
|
1350
1386
|
delete cleanPayload._activeTraits;
|
|
@@ -1577,7 +1613,8 @@ var OrbitalServerRuntime = class {
|
|
|
1577
1613
|
try {
|
|
1578
1614
|
switch (opAction) {
|
|
1579
1615
|
case "create": {
|
|
1580
|
-
const
|
|
1616
|
+
const createIntrinsicFields = this.intrinsicFieldNames(registered, opEntityType);
|
|
1617
|
+
const createData = createIntrinsicFields.length > 0 ? omitFrameFields(opRest[0] || {}, createIntrinsicFields) : opRest[0] || {};
|
|
1581
1618
|
const { id: newId } = await this.persistence.create(opEntityType, createData);
|
|
1582
1619
|
batchResults.push({ ...createData, action: "create", entityType: opEntityType, id: newId });
|
|
1583
1620
|
completed.push({ action: "create", entityType: opEntityType, id: newId });
|
|
@@ -1585,7 +1622,8 @@ var OrbitalServerRuntime = class {
|
|
|
1585
1622
|
}
|
|
1586
1623
|
case "update": {
|
|
1587
1624
|
const updateId = opRest[0];
|
|
1588
|
-
const
|
|
1625
|
+
const updateIntrinsicFields = this.intrinsicFieldNames(registered, opEntityType);
|
|
1626
|
+
const updateData = updateIntrinsicFields.length > 0 ? omitFrameFields(opRest[1] || {}, updateIntrinsicFields) : opRest[1] || {};
|
|
1589
1627
|
await this.persistence.update(opEntityType, updateId, updateData);
|
|
1590
1628
|
const updated = await this.persistence.getById(opEntityType, updateId);
|
|
1591
1629
|
batchResults.push({ ...updated || updateData, action: "update", entityType: opEntityType, id: updateId });
|
|
@@ -1625,6 +1663,10 @@ var OrbitalServerRuntime = class {
|
|
|
1625
1663
|
return;
|
|
1626
1664
|
}
|
|
1627
1665
|
const type = targetEntityType || entityType;
|
|
1666
|
+
if ((action === "create" || action === "update") && data !== void 0) {
|
|
1667
|
+
const intrinsicFields = this.intrinsicFieldNames(registered, type);
|
|
1668
|
+
if (intrinsicFields.length > 0) data = omitFrameFields(data, intrinsicFields);
|
|
1669
|
+
}
|
|
1628
1670
|
let resultData;
|
|
1629
1671
|
const sizeBefore = (await this.persistence.list(type)).length;
|
|
1630
1672
|
let deniedReason;
|
|
@@ -1952,7 +1994,9 @@ var OrbitalServerRuntime = class {
|
|
|
1952
1994
|
contextExtensions: this.config.contextExtensions,
|
|
1953
1995
|
// Same render-time marker boundary as the outer executor
|
|
1954
1996
|
// (including the [shared]-defers-regardless-of-persistence rule).
|
|
1955
|
-
deferRenderBindings: registered.entity === void 0 || isRuntimeEntity(registered.entity) || registered.entity.shared === true
|
|
1997
|
+
deferRenderBindings: registered.entity === void 0 || isRuntimeEntity(registered.entity) || registered.entity.shared === true,
|
|
1998
|
+
resolveIntrinsicFields: (type) => this.intrinsicFieldNames(registered, type),
|
|
1999
|
+
resolveEntityFields: (type) => this.entityFieldsFor(registered, type)
|
|
1956
2000
|
});
|
|
1957
2001
|
for (const innerEffect of atomicEffects) {
|
|
1958
2002
|
if (atomicFailed) break;
|
|
@@ -2003,12 +2047,6 @@ var OrbitalServerRuntime = class {
|
|
|
2003
2047
|
navigateBack: () => {
|
|
2004
2048
|
pushClientEffect(["navigate-back"]);
|
|
2005
2049
|
},
|
|
2006
|
-
notify: (message, type) => {
|
|
2007
|
-
if (this.config.debug) {
|
|
2008
|
-
effectLog.info("notify", { type, message });
|
|
2009
|
-
}
|
|
2010
|
-
pushClientEffect(["notify", message, { type }]);
|
|
2011
|
-
},
|
|
2012
2050
|
log: (message, level) => {
|
|
2013
2051
|
if (level === "error") {
|
|
2014
2052
|
dynamicLog.error(message);
|
|
@@ -2117,7 +2155,9 @@ var OrbitalServerRuntime = class {
|
|
|
2117
2155
|
// client-held by contract (one live frame across bound traits), and
|
|
2118
2156
|
// the eager clobber pinned the chat composer's controlled input to
|
|
2119
2157
|
// the server's flush-time "" on every keystroke round-trip.
|
|
2120
|
-
deferRenderBindings: registered.entity === void 0 || isRuntimeEntity(registered.entity) || registered.entity.shared === true
|
|
2158
|
+
deferRenderBindings: registered.entity === void 0 || isRuntimeEntity(registered.entity) || registered.entity.shared === true,
|
|
2159
|
+
resolveIntrinsicFields: (type) => this.intrinsicFieldNames(registered, type),
|
|
2160
|
+
resolveEntityFields: (type) => this.entityFieldsFor(registered, type)
|
|
2121
2161
|
});
|
|
2122
2162
|
await executor.executeAll(effects);
|
|
2123
2163
|
}
|
package/dist/ServerBridge.d.ts
CHANGED
|
@@ -4,9 +4,9 @@ import { seedRandom, randomArrayElement, randomInt, shuffleArray, randomPastDate
|
|
|
4
4
|
import { collectTraitRefsFromValue, collectTraitRefsFromEffects } from './chunk-SCRAHWOC.js';
|
|
5
5
|
import { createLogger, setNamespaceLevel } from '@almadar/logger';
|
|
6
6
|
import { evaluateGuard, SExpressionEvaluator } from '@almadar/evaluator';
|
|
7
|
-
import { OrbitalSchemaSchema, isInlineTrait, isEntityCall, isEntityReference, parseEntityRef, parseImportedTraitRef, isPageReference, isPageReferenceString, isPageReferenceObject, parsePageRef, parseOrbitalRef, overrideDeclaredKnobs, asTraitId, asEntityId, asPageId, asOrbitalId, deriveId, isReferenceConfigType, configRefEventKnob, normalizeCallSiteConfigToValues, resolveConfigRefEventName, isCallSiteConfigDeclaration, asEventId, ledgerRename, idPrefix, eventListPropsOf } from '@almadar/core';
|
|
7
|
+
import { omitFrameFields, OrbitalSchemaSchema, isInlineTrait, isEntityCall, isEntityReference, parseEntityRef, parseImportedTraitRef, isPageReference, isPageReferenceString, isPageReferenceObject, parsePageRef, parseOrbitalRef, overrideDeclaredKnobs, asTraitId, asEntityId, asPageId, asOrbitalId, deriveId, isReferenceConfigType, configRefEventKnob, normalizeCallSiteConfigToValues, resolveConfigRefEventName, isCallSiteConfigDeclaration, asEventId, ledgerRename, idPrefix, eventListPropsOf } from '@almadar/core';
|
|
8
8
|
export { normalizeCallSiteConfigToValues } from '@almadar/core';
|
|
9
|
-
import { sampleRowCount, sampleRow, identityEntitiesOf, roleVocabularyOf } from '@almadar/core/mock';
|
|
9
|
+
import { RESERVED_FIELD_NAMES, sampleRowCount, linkSelfRelationField, sampleRow, identityEntitiesOf, roleVocabularyOf } from '@almadar/core/mock';
|
|
10
10
|
|
|
11
11
|
var log = createLogger("almadar:runtime:eventbus");
|
|
12
12
|
var EventBus = class {
|
|
@@ -922,14 +922,13 @@ var StateMachineManager = class {
|
|
|
922
922
|
|
|
923
923
|
// src/types.ts
|
|
924
924
|
var HANDLER_MANIFEST = {
|
|
925
|
-
client: ["render-ui", "render", "navigate", "navigate-back", "
|
|
925
|
+
client: ["render-ui", "render", "navigate", "navigate-back", "emit", "set", "log", "ref", "deref", "watch", "send-server", "browser/open-file-picker", "browser/clipboard-read", "browser/clipboard-write", "browser/geolocation-current"],
|
|
926
926
|
server: ["persist", "fetch", "fetch-stream", "call-service", "emit", "set", "spawn", "despawn", "log", "ref", "deref", "swap!", "atomic", "os/watch-files", "os/watch-process", "os/watch-port", "os/watch-http", "os/watch-cron", "os/watch-signal", "os/watch-env", "os/debounce"],
|
|
927
927
|
test: [
|
|
928
928
|
"render-ui",
|
|
929
929
|
"render",
|
|
930
930
|
"navigate",
|
|
931
931
|
"navigate-back",
|
|
932
|
-
"notify",
|
|
933
932
|
"emit",
|
|
934
933
|
"set",
|
|
935
934
|
"persist",
|
|
@@ -989,6 +988,8 @@ var EffectExecutor = class _EffectExecutor {
|
|
|
989
988
|
contextExtensions;
|
|
990
989
|
evaluator;
|
|
991
990
|
deferRenderBindings;
|
|
991
|
+
resolveIntrinsicFields;
|
|
992
|
+
resolveEntityFields;
|
|
992
993
|
constructor(options) {
|
|
993
994
|
this.handlers = options.handlers;
|
|
994
995
|
this.bindings = options.bindings;
|
|
@@ -998,6 +999,53 @@ var EffectExecutor = class _EffectExecutor {
|
|
|
998
999
|
this.contextExtensions = options.contextExtensions;
|
|
999
1000
|
this.evaluator = options.evaluator;
|
|
1000
1001
|
this.deferRenderBindings = options.deferRenderBindings ?? false;
|
|
1002
|
+
this.resolveIntrinsicFields = options.resolveIntrinsicFields;
|
|
1003
|
+
this.resolveEntityFields = options.resolveEntityFields;
|
|
1004
|
+
}
|
|
1005
|
+
/**
|
|
1006
|
+
* `@intrinsic` fields are NEVER a persisted column — strips them from
|
|
1007
|
+
* `data` before a `persist create`/`persist update` write, regardless of
|
|
1008
|
+
* what expression produced `data` (bare `@entity`, an explicit literal
|
|
1009
|
+
* that names one, an `object/merge` result, …). Deterministic on the
|
|
1010
|
+
* entity's declared schema, not on how the value was constructed. A
|
|
1011
|
+
* no-op when no schema was supplied (`resolveIntrinsicFields` absent,
|
|
1012
|
+
* e.g. a browser/offline-preview handler set) or the entity declares no
|
|
1013
|
+
* intrinsic fields.
|
|
1014
|
+
*/
|
|
1015
|
+
stripFrameFields(entityType, data) {
|
|
1016
|
+
if (data === void 0 || !this.resolveIntrinsicFields) return data;
|
|
1017
|
+
const intrinsicFields = this.resolveIntrinsicFields(entityType);
|
|
1018
|
+
return intrinsicFields.length > 0 ? omitFrameFields(data, intrinsicFields) : data;
|
|
1019
|
+
}
|
|
1020
|
+
/**
|
|
1021
|
+
* Names of `data`'s missing REQUIRED columns for a `persist create` —
|
|
1022
|
+
* shared with the Rust kernel's definition: an entity field with
|
|
1023
|
+
* `required: true`, excluding the framework-stamped columns
|
|
1024
|
+
* (`RESERVED_FIELD_NAMES` — id/audit timestamps the store mints, the
|
|
1025
|
+
* same set `@almadar/core`'s mock synthesis and `MockPersistenceAdapter`
|
|
1026
|
+
* already exempt), any `@intrinsic` field, any field declaring a
|
|
1027
|
+
* `default` (the store fills it in), and any field carrying `mergedFrom`
|
|
1028
|
+
* (a rebind-merged field's `required` is the imported atom's own write
|
|
1029
|
+
* contract, never this host writer's). "Missing" is key-absent or
|
|
1030
|
+
* `undefined`/`null`/`''`. A no-op (`[]`) when no schema was supplied
|
|
1031
|
+
* (`resolveEntityFields` absent).
|
|
1032
|
+
*/
|
|
1033
|
+
missingRequiredFields(entityType, data) {
|
|
1034
|
+
if (!this.resolveEntityFields) return [];
|
|
1035
|
+
const row = data ?? {};
|
|
1036
|
+
const missing = [];
|
|
1037
|
+
for (const field of this.resolveEntityFields(entityType)) {
|
|
1038
|
+
if (field.required !== true || !field.name) continue;
|
|
1039
|
+
if (RESERVED_FIELD_NAMES.has(field.name)) continue;
|
|
1040
|
+
if (field.intrinsic === true) continue;
|
|
1041
|
+
if (field.default !== void 0) continue;
|
|
1042
|
+
if (field.mergedFrom !== void 0) continue;
|
|
1043
|
+
const value = row[field.name];
|
|
1044
|
+
if (value === void 0 || value === null || value === "") {
|
|
1045
|
+
missing.push(field.name);
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
return missing;
|
|
1001
1049
|
}
|
|
1002
1050
|
_evaluator;
|
|
1003
1051
|
getEvaluator() {
|
|
@@ -1063,7 +1111,6 @@ var EffectExecutor = class _EffectExecutor {
|
|
|
1063
1111
|
"render": this.handlers.renderUI,
|
|
1064
1112
|
"navigate": this.handlers.navigate,
|
|
1065
1113
|
"navigate-back": this.handlers.navigateBack,
|
|
1066
|
-
"notify": this.handlers.notify,
|
|
1067
1114
|
"log": this.handlers.log,
|
|
1068
1115
|
"ref": this.handlers.ref,
|
|
1069
1116
|
"deref": this.handlers.deref,
|
|
@@ -1413,7 +1460,28 @@ var EffectExecutor = class _EffectExecutor {
|
|
|
1413
1460
|
}
|
|
1414
1461
|
try {
|
|
1415
1462
|
if (action === "batch") {
|
|
1416
|
-
const
|
|
1463
|
+
const requiredMissing = [];
|
|
1464
|
+
const operations = args[1].map((op) => {
|
|
1465
|
+
if (!Array.isArray(op) || op.length < 2) return op;
|
|
1466
|
+
const [opAction, opEntityType, ...opRest] = op;
|
|
1467
|
+
if (opAction === "create") {
|
|
1468
|
+
const stripped = this.stripFrameFields(opEntityType, opRest[0]);
|
|
1469
|
+
const missing = this.missingRequiredFields(opEntityType, stripped);
|
|
1470
|
+
if (missing.length > 0) requiredMissing.push({ entityType: opEntityType, missing });
|
|
1471
|
+
return [opAction, opEntityType, stripped, ...opRest.slice(1)];
|
|
1472
|
+
}
|
|
1473
|
+
if (opAction === "update") {
|
|
1474
|
+
const stripped = this.stripFrameFields(opEntityType, opRest[1]);
|
|
1475
|
+
return [opAction, opEntityType, opRest[0], stripped, ...opRest.slice(2)];
|
|
1476
|
+
}
|
|
1477
|
+
return op;
|
|
1478
|
+
});
|
|
1479
|
+
if (requiredMissing.length > 0) {
|
|
1480
|
+
persistLog.error("persist:required-missing", { action, entityType: "batch", requiredMissing });
|
|
1481
|
+
const missingError = requiredMissing.map(({ entityType: t, missing }) => `persist create ${t}: required field(s) ${missing.join(", ")} missing`).join("; ");
|
|
1482
|
+
this.emitFailure(emitCfg, new Error(missingError));
|
|
1483
|
+
return { failed: true, error: missingError };
|
|
1484
|
+
}
|
|
1417
1485
|
const batchSummary = await this.handlers.persist("batch", "", {
|
|
1418
1486
|
operations
|
|
1419
1487
|
});
|
|
@@ -1432,7 +1500,16 @@ var EffectExecutor = class _EffectExecutor {
|
|
|
1432
1500
|
persistLog.debug("persist:emit-fired", { action, eventName: emitCfg?.success });
|
|
1433
1501
|
} else {
|
|
1434
1502
|
const entityType = args[1];
|
|
1435
|
-
const data = args[2];
|
|
1503
|
+
const data = action === "create" || action === "update" ? this.stripFrameFields(entityType, args[2]) : args[2];
|
|
1504
|
+
if (action === "create") {
|
|
1505
|
+
const missing = this.missingRequiredFields(entityType, data);
|
|
1506
|
+
if (missing.length > 0) {
|
|
1507
|
+
persistLog.error("persist:required-missing", { entityType, missing });
|
|
1508
|
+
const missingError = `persist create ${entityType}: required field(s) ${missing.join(", ")} missing`;
|
|
1509
|
+
this.emitFailure(emitCfg, new Error(missingError), { entityType });
|
|
1510
|
+
return { failed: true, error: missingError };
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1436
1513
|
const persisted = await this.handlers.persist(action, entityType, data);
|
|
1437
1514
|
if (persisted === void 0) {
|
|
1438
1515
|
const attemptedId = typeof data === "string" ? data : data && typeof data === "object" ? data.id : void 0;
|
|
@@ -1697,15 +1774,7 @@ var EffectExecutor = class _EffectExecutor {
|
|
|
1697
1774
|
break;
|
|
1698
1775
|
}
|
|
1699
1776
|
case "notify": {
|
|
1700
|
-
|
|
1701
|
-
const message = args[0];
|
|
1702
|
-
const type = args[1] || "info";
|
|
1703
|
-
this.handlers.notify(message, type);
|
|
1704
|
-
} else {
|
|
1705
|
-
const category = typeof args[1] === "string" ? args[1] : "info";
|
|
1706
|
-
const message = typeof args[0] === "string" ? args[0] : JSON.stringify(args[0] ?? null);
|
|
1707
|
-
effectLog.info("notify", { category, message });
|
|
1708
|
-
}
|
|
1777
|
+
this.logUnsupported("notify");
|
|
1709
1778
|
break;
|
|
1710
1779
|
}
|
|
1711
1780
|
// === Compound Effects ===
|
|
@@ -2476,14 +2545,18 @@ var MockPersistenceAdapter = class _MockPersistenceAdapter {
|
|
|
2476
2545
|
* of nested-tree atoms (e.g. std-thread-comments-linear with ThreadPost.
|
|
2477
2546
|
* replies → [ThreadPost]) render empty reply cards.
|
|
2478
2547
|
*
|
|
2479
|
-
* Cross-entity relations pick random IDs from the target store.
|
|
2480
|
-
*
|
|
2481
|
-
*
|
|
2482
|
-
* root (
|
|
2483
|
-
* real roots
|
|
2484
|
-
*
|
|
2485
|
-
*
|
|
2486
|
-
* `
|
|
2548
|
+
* Cross-entity relations pick random IDs from the target store. EVERY
|
|
2549
|
+
* self-referential relation, any cardinality, is linked deterministically
|
|
2550
|
+
* instead via `@almadar/core/mock`'s `linkSelfRelationField`: row 0 stays a
|
|
2551
|
+
* root (never referenced), row *i* parents to row ⌊(i−1)/2⌋ — one shared
|
|
2552
|
+
* forest, real roots, no cycles. `one`/`many-to-one` self-relations (a
|
|
2553
|
+
* parent column like `Tag.parentId : Tag`) store the scalar parent id;
|
|
2554
|
+
* `many`/`one-to-many`/`many-to-many` self-relations (a children list like
|
|
2555
|
+
* `ChatMessage.replies : [ChatMessage]`) store the forest's children-id
|
|
2556
|
+
* list — no more random cross-linking, so the root row is never referenced
|
|
2557
|
+
* and stays deletable under `onDelete: restrict`. The runtime caps
|
|
2558
|
+
* recursion at depth=2 in `populateRelations`, so deep chains render two
|
|
2559
|
+
* levels then stop.
|
|
2487
2560
|
*
|
|
2488
2561
|
* Entities sharing a collection are linked once per STORE, over the union
|
|
2489
2562
|
* of their declared relation fields (first declarer of a field name wins).
|
|
@@ -2525,13 +2598,12 @@ var MockPersistenceAdapter = class _MockPersistenceAdapter {
|
|
|
2525
2598
|
if (!targetStore || targetStore.size === 0) continue;
|
|
2526
2599
|
const sameStore = targetStore === store;
|
|
2527
2600
|
const cardinality = field.relation.cardinality ?? "many";
|
|
2528
|
-
if (sameStore
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
});
|
|
2601
|
+
if (sameStore) {
|
|
2602
|
+
linkSelfRelationField(
|
|
2603
|
+
rows,
|
|
2604
|
+
{ name: field.name, cardinality },
|
|
2605
|
+
(row, i) => this.config.ownerId !== void 0 && row[field.name] === this.config.ownerId || stampedCellKeys.has(stampedCellKey(storeKey, row["id"], field.name))
|
|
2606
|
+
);
|
|
2535
2607
|
continue;
|
|
2536
2608
|
}
|
|
2537
2609
|
for (const row of rows) {
|
|
@@ -2539,11 +2611,7 @@ var MockPersistenceAdapter = class _MockPersistenceAdapter {
|
|
|
2539
2611
|
if (this.config.ownerId !== void 0 && row[field.name] === this.config.ownerId || stampedCellKeys.has(stampedCellKey(storeKey, selfId, field.name))) {
|
|
2540
2612
|
continue;
|
|
2541
2613
|
}
|
|
2542
|
-
let eligible = [];
|
|
2543
|
-
for (const id of targetStore.keys()) {
|
|
2544
|
-
if (sameStore && id === selfId) continue;
|
|
2545
|
-
eligible.push(id);
|
|
2546
|
-
}
|
|
2614
|
+
let eligible = [...targetStore.keys()];
|
|
2547
2615
|
if (isOwnerColumn && this.ownerCandidateGate) {
|
|
2548
2616
|
const gate = this.ownerCandidateGate;
|
|
2549
2617
|
eligible = eligible.filter(
|
|
@@ -2574,7 +2642,7 @@ var MockPersistenceAdapter = class _MockPersistenceAdapter {
|
|
|
2574
2642
|
const store = this.stores.get(storeKey);
|
|
2575
2643
|
if (!store) return;
|
|
2576
2644
|
const candidates = schema.fields.filter(
|
|
2577
|
-
(f) => f.name
|
|
2645
|
+
(f) => f.name === void 0 || !RESERVED_FIELD_NAMES.has(f.name)
|
|
2578
2646
|
);
|
|
2579
2647
|
if (candidates.length === 0) return;
|
|
2580
2648
|
let index = 0;
|
|
@@ -2749,7 +2817,7 @@ var MockPersistenceAdapter = class _MockPersistenceAdapter {
|
|
|
2749
2817
|
if (!schema) return data;
|
|
2750
2818
|
const result = { ...data };
|
|
2751
2819
|
for (const field of schema.fields) {
|
|
2752
|
-
if (field.name
|
|
2820
|
+
if (field.name !== void 0 && RESERVED_FIELD_NAMES.has(field.name)) continue;
|
|
2753
2821
|
if (result[field.name] !== void 0) continue;
|
|
2754
2822
|
if (field.default === void 0) continue;
|
|
2755
2823
|
result[field.name] = field.default === "@now" ? (/* @__PURE__ */ new Date()).toISOString() : field.default;
|
|
@@ -5417,6 +5485,7 @@ function mergeImportedEntityFields(callerEntity, importedEntity, intrinsicOnly,
|
|
|
5417
5485
|
if (existingNames.has(field.name)) continue;
|
|
5418
5486
|
const cloned = structuredClone(field);
|
|
5419
5487
|
rewriteSelfRelationTarget(cloned, oldName, newName, newId);
|
|
5488
|
+
cloned.mergedFrom = oldName;
|
|
5420
5489
|
callerEntity.fields.push(cloned);
|
|
5421
5490
|
}
|
|
5422
5491
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { h as RuntimePatternValue, B as BindingContext, f as EvaluationContextExtensions, P as PatternProps, E as EffectHandlers, g as EffectContext, i as ExecutionEnvironment, j as EffectResult, T as TraitDefinition } from './types-
|
|
2
|
-
export { k as BrowserFileMeta, l as BrowserFilePickerOptions, m as BrowserGeolocationOptions, n as BrowserGeolocationPosition, C as ConfigContext, o as Effect, a as EventListener, H as HANDLER_MANIFEST, I as IEventBus, b as RuntimeConfig, R as RuntimeEvent, d as TraitState, c as TransitionObserver, e as TransitionResult, U as Unsubscribe } from './types-
|
|
3
|
-
import { U as UnifiedLoaderOptions, S as SchemaLoader, I as ImportChainLike, L as LoadResult, a as LoadedSchema, b as LoadedOrbital, P as PersistenceAdapter } from './OrbitalServerRuntime-
|
|
4
|
-
export { C as CreateServerEffectHandlersOptions, E as EntitySharingMap, c as EventBus, d as EventNamespaceMap, e as InMemoryPersistence, f as LIFECYCLE_EVENTS, O as OrbitalEventRequest, g as OrbitalEventResponse, h as OrbitalServerRuntimeConfig, i as PreprocessOptions, j as PreprocessResult, k as PreprocessedSchema, l as ProcessEventOptions, R as RegisteredOrbital, m as RuntimeOrbital, n as RuntimeOrbitalSchema, o as RuntimeTrait, p as ServerEffectResult, q as StateMachineManager, r as collectDeclaredConfigDefaults, s as collectDeclaredEntityDefaults, t as createInitialTraitState, u as createServerEffectHandlers, v as findInitialState, w as findTransition, x as getIsolatedCollectionName, y as getNamespacedEvent, z as isBrowser, A as isElectron, B as isNamespacedEvent, D as isNode, F as normalizeEventKey, G as parseNamespacedEvent, H as preprocessSchema, J as processEvent } from './OrbitalServerRuntime-
|
|
1
|
+
import { h as RuntimePatternValue, B as BindingContext, f as EvaluationContextExtensions, P as PatternProps, E as EffectHandlers, g as EffectContext, i as ExecutionEnvironment, j as EffectResult, T as TraitDefinition } from './types-BO4tbEPp.js';
|
|
2
|
+
export { k as BrowserFileMeta, l as BrowserFilePickerOptions, m as BrowserGeolocationOptions, n as BrowserGeolocationPosition, C as ConfigContext, o as Effect, a as EventListener, H as HANDLER_MANIFEST, I as IEventBus, b as RuntimeConfig, R as RuntimeEvent, d as TraitState, c as TransitionObserver, e as TransitionResult, U as Unsubscribe } from './types-BO4tbEPp.js';
|
|
3
|
+
import { U as UnifiedLoaderOptions, S as SchemaLoader, I as ImportChainLike, L as LoadResult, a as LoadedSchema, b as LoadedOrbital, P as PersistenceAdapter } from './OrbitalServerRuntime-eSaLgPR6.js';
|
|
4
|
+
export { C as CreateServerEffectHandlersOptions, E as EntitySharingMap, c as EventBus, d as EventNamespaceMap, e as InMemoryPersistence, f as LIFECYCLE_EVENTS, O as OrbitalEventRequest, g as OrbitalEventResponse, h as OrbitalServerRuntimeConfig, i as PreprocessOptions, j as PreprocessResult, k as PreprocessedSchema, l as ProcessEventOptions, R as RegisteredOrbital, m as RuntimeOrbital, n as RuntimeOrbitalSchema, o as RuntimeTrait, p as ServerEffectResult, q as StateMachineManager, r as collectDeclaredConfigDefaults, s as collectDeclaredEntityDefaults, t as createInitialTraitState, u as createServerEffectHandlers, v as findInitialState, w as findTransition, x as getIsolatedCollectionName, y as getNamespacedEvent, z as isBrowser, A as isElectron, B as isNamespacedEvent, D as isNode, F as normalizeEventKey, G as parseNamespacedEvent, H as preprocessSchema, J as processEvent } from './OrbitalServerRuntime-eSaLgPR6.js';
|
|
5
5
|
import { EvaluationContext, SExpressionEvaluator } from '@almadar/evaluator';
|
|
6
6
|
export { EvaluationContext, createMinimalContext } from '@almadar/evaluator';
|
|
7
|
-
import { RenderBindingMarker, SExpr, RuntimeValue, EventPayload, PatternConfig, EntityId,
|
|
7
|
+
import { RenderBindingMarker, SExpr, RuntimeValue, EntityField, EventPayload, PatternConfig, EntityId, EntityRow, EntityPersistence, PayloadField, OrbitalDefinition, OrbitalSchema } from '@almadar/core';
|
|
8
8
|
export { EntityField, normalizeCallSiteConfigToValues } from '@almadar/core';
|
|
9
9
|
export { AccessBindings, applyRowAccess, checkMutationAccess } from './entityAccess.js';
|
|
10
10
|
export { ServerBridgeConfig, ServerBridgeState } from './ServerBridge.js';
|
|
@@ -353,6 +353,26 @@ interface EffectExecutorOptions {
|
|
|
353
353
|
* SSE-pushed props stay concrete.
|
|
354
354
|
*/
|
|
355
355
|
deferRenderBindings?: boolean;
|
|
356
|
+
/**
|
|
357
|
+
* Names of an entity's `@intrinsic` fields — trait-owned view state
|
|
358
|
+
* (never a persisted column) — keyed by entity type. Supplied only by a
|
|
359
|
+
* caller holding the registered schema (`OrbitalServerRuntime`); the
|
|
360
|
+
* `persist` case uses it to strip those keys from a bare `@entity`
|
|
361
|
+
* binding before the write reaches the store. Absent means "no schema
|
|
362
|
+
* available" (e.g. a browser/offline-preview handler set), in which case
|
|
363
|
+
* persist writes verbatim exactly as before.
|
|
364
|
+
*/
|
|
365
|
+
resolveIntrinsicFields?: (entityType: string) => readonly string[];
|
|
366
|
+
/**
|
|
367
|
+
* Full declared field list for an entity, keyed by entity type. Supplied
|
|
368
|
+
* by the same caller as `resolveIntrinsicFields` (`OrbitalServerRuntime`,
|
|
369
|
+
* via the same entity-resolution walk — no second lookup path); the
|
|
370
|
+
* `persist create` case uses it to fail a write missing a REQUIRED
|
|
371
|
+
* column before it reaches the store, mirroring the Rust kernel's check.
|
|
372
|
+
* Absent means "no schema available", in which case `persist create`
|
|
373
|
+
* writes verbatim exactly as before.
|
|
374
|
+
*/
|
|
375
|
+
resolveEntityFields?: (entityType: string) => readonly EntityField[];
|
|
356
376
|
}
|
|
357
377
|
/**
|
|
358
378
|
* EffectExecutor - Routes effects to handlers.
|
|
@@ -389,7 +409,34 @@ declare class EffectExecutor {
|
|
|
389
409
|
private contextExtensions?;
|
|
390
410
|
private evaluator?;
|
|
391
411
|
private deferRenderBindings;
|
|
412
|
+
private resolveIntrinsicFields?;
|
|
413
|
+
private resolveEntityFields?;
|
|
392
414
|
constructor(options: EffectExecutorOptions);
|
|
415
|
+
/**
|
|
416
|
+
* `@intrinsic` fields are NEVER a persisted column — strips them from
|
|
417
|
+
* `data` before a `persist create`/`persist update` write, regardless of
|
|
418
|
+
* what expression produced `data` (bare `@entity`, an explicit literal
|
|
419
|
+
* that names one, an `object/merge` result, …). Deterministic on the
|
|
420
|
+
* entity's declared schema, not on how the value was constructed. A
|
|
421
|
+
* no-op when no schema was supplied (`resolveIntrinsicFields` absent,
|
|
422
|
+
* e.g. a browser/offline-preview handler set) or the entity declares no
|
|
423
|
+
* intrinsic fields.
|
|
424
|
+
*/
|
|
425
|
+
private stripFrameFields;
|
|
426
|
+
/**
|
|
427
|
+
* Names of `data`'s missing REQUIRED columns for a `persist create` —
|
|
428
|
+
* shared with the Rust kernel's definition: an entity field with
|
|
429
|
+
* `required: true`, excluding the framework-stamped columns
|
|
430
|
+
* (`RESERVED_FIELD_NAMES` — id/audit timestamps the store mints, the
|
|
431
|
+
* same set `@almadar/core`'s mock synthesis and `MockPersistenceAdapter`
|
|
432
|
+
* already exempt), any `@intrinsic` field, any field declaring a
|
|
433
|
+
* `default` (the store fills it in), and any field carrying `mergedFrom`
|
|
434
|
+
* (a rebind-merged field's `required` is the imported atom's own write
|
|
435
|
+
* contract, never this host writer's). "Missing" is key-absent or
|
|
436
|
+
* `undefined`/`null`/`''`. A no-op (`[]`) when no schema was supplied
|
|
437
|
+
* (`resolveEntityFields` absent).
|
|
438
|
+
*/
|
|
439
|
+
private missingRequiredFields;
|
|
393
440
|
private _evaluator?;
|
|
394
441
|
private getEvaluator;
|
|
395
442
|
/**
|
|
@@ -521,8 +568,6 @@ interface CreateClientEffectHandlersOptions {
|
|
|
521
568
|
}, crumb?: string) => void;
|
|
522
569
|
/** Navigate-back function: pop the orbital-scoped navigation stack. */
|
|
523
570
|
navigateBack?: () => void;
|
|
524
|
-
/** Notify function for notification effects */
|
|
525
|
-
notify?: (message: string, type: 'success' | 'error' | 'warning' | 'info') => void;
|
|
526
571
|
/**
|
|
527
572
|
* Send-server handler for `send-server` effects.
|
|
528
573
|
* When omitted, defaults to a lazy WebSocket transport connecting to the
|
|
@@ -542,7 +587,7 @@ interface CreateClientEffectHandlersOptions {
|
|
|
542
587
|
/**
|
|
543
588
|
* Create client-side effect handlers for trait state machine execution.
|
|
544
589
|
*
|
|
545
|
-
* Client handles: emit, renderUI, navigate,
|
|
590
|
+
* Client handles: emit, renderUI, navigate, sendServer
|
|
546
591
|
* Server handles: persist, set, callService (logged as warnings on client)
|
|
547
592
|
*
|
|
548
593
|
* @example
|
|
@@ -554,7 +599,6 @@ interface CreateClientEffectHandlersOptions {
|
|
|
554
599
|
* clearSlot: (slot) => pendingSlots.set(slot, []),
|
|
555
600
|
* },
|
|
556
601
|
* navigate: (path) => router.push(path),
|
|
557
|
-
* notify: (msg, type) => toast[type](msg),
|
|
558
602
|
* });
|
|
559
603
|
* ```
|
|
560
604
|
*/
|
|
@@ -747,14 +791,18 @@ declare class MockPersistenceAdapter implements PersistenceAdapter {
|
|
|
747
791
|
* of nested-tree atoms (e.g. std-thread-comments-linear with ThreadPost.
|
|
748
792
|
* replies → [ThreadPost]) render empty reply cards.
|
|
749
793
|
*
|
|
750
|
-
* Cross-entity relations pick random IDs from the target store.
|
|
751
|
-
*
|
|
752
|
-
*
|
|
753
|
-
* root (
|
|
754
|
-
* real roots
|
|
755
|
-
*
|
|
756
|
-
*
|
|
757
|
-
* `
|
|
794
|
+
* Cross-entity relations pick random IDs from the target store. EVERY
|
|
795
|
+
* self-referential relation, any cardinality, is linked deterministically
|
|
796
|
+
* instead via `@almadar/core/mock`'s `linkSelfRelationField`: row 0 stays a
|
|
797
|
+
* root (never referenced), row *i* parents to row ⌊(i−1)/2⌋ — one shared
|
|
798
|
+
* forest, real roots, no cycles. `one`/`many-to-one` self-relations (a
|
|
799
|
+
* parent column like `Tag.parentId : Tag`) store the scalar parent id;
|
|
800
|
+
* `many`/`one-to-many`/`many-to-many` self-relations (a children list like
|
|
801
|
+
* `ChatMessage.replies : [ChatMessage]`) store the forest's children-id
|
|
802
|
+
* list — no more random cross-linking, so the root row is never referenced
|
|
803
|
+
* and stays deletable under `onDelete: restrict`. The runtime caps
|
|
804
|
+
* recursion at depth=2 in `populateRelations`, so deep chains render two
|
|
805
|
+
* levels then stop.
|
|
758
806
|
*
|
|
759
807
|
* Entities sharing a collection are linked once per STORE, over the union
|
|
760
808
|
* of their declared relation fields (first declarer of a field name wins).
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { EffectExecutor } from './chunk-
|
|
2
|
-
export { EffectExecutor, EventBus, HANDLER_MANIFEST, InMemoryPersistence, LIFECYCLE_EVENTS, MockPersistenceAdapter, StateMachineManager, TickScheduler, buildEmitsFromTraits, collectDeclaredConfigDefaults, collectDeclaredEntityDefaults, createInitialTraitState, createMockPersistence, createTestExecutor, createTickScheduler, createUnifiedLoader, findInitialState, findTransition, formatPayloadValidationError, getIsolatedCollectionName, getNamespacedEvent, isBrowser, isElectron, isNamespacedEvent, isNode, isValidDurationString, normalizeCallSiteConfigToValues, normalizeEventKey, parseDurationString, parseNamespacedEvent, preprocessSchema, processEvent, validateEventPayload, validatePayloadShapes } from './chunk-
|
|
1
|
+
import { EffectExecutor } from './chunk-KAGXDW33.js';
|
|
2
|
+
export { EffectExecutor, EventBus, HANDLER_MANIFEST, InMemoryPersistence, LIFECYCLE_EVENTS, MockPersistenceAdapter, StateMachineManager, TickScheduler, buildEmitsFromTraits, collectDeclaredConfigDefaults, collectDeclaredEntityDefaults, createInitialTraitState, createMockPersistence, createTestExecutor, createTickScheduler, createUnifiedLoader, findInitialState, findTransition, formatPayloadValidationError, getIsolatedCollectionName, getNamespacedEvent, isBrowser, isElectron, isNamespacedEvent, isNode, isValidDurationString, normalizeCallSiteConfigToValues, normalizeEventKey, parseDurationString, parseNamespacedEvent, preprocessSchema, processEvent, validateEventPayload, validatePayloadShapes } from './chunk-KAGXDW33.js';
|
|
3
3
|
export { cronMatches, cronMinuteKey, isValidCronExpression, parseCron, parseCronField } from './chunk-OU3ITB5S.js';
|
|
4
4
|
import { createContextFromBindings, applyRowAccess, checkMutationAccess, accessDeniedMessage } from './chunk-ZWAOJT6R.js';
|
|
5
5
|
export { applyRowAccess, checkMutationAccess, containsBindings, createContextFromBindings, createMinimalContext, deferEntityBindings, extractBindings, interpolateProps, interpolateValue } from './chunk-ZWAOJT6R.js';
|
|
@@ -68,7 +68,7 @@ function sendServerEvent(orbital, event, payload) {
|
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
70
|
function createClientEffectHandlers(options) {
|
|
71
|
-
const { eventBus, slotSetter, navigate, navigateBack,
|
|
71
|
+
const { eventBus, slotSetter, navigate, navigateBack, sendServer, orbitalName = "" } = options;
|
|
72
72
|
return {
|
|
73
73
|
emit: (event, payload) => {
|
|
74
74
|
const prefixedEvent = event.startsWith("UI:") ? event : `UI:${event}`;
|
|
@@ -101,9 +101,6 @@ function createClientEffectHandlers(options) {
|
|
|
101
101
|
navigateBack: navigateBack ?? (() => {
|
|
102
102
|
log.warn("navigate-back-no-handler");
|
|
103
103
|
}),
|
|
104
|
-
notify: notify ?? ((msg, type) => {
|
|
105
|
-
log.debug("notify", { type: type ?? null, message: msg });
|
|
106
|
-
}),
|
|
107
104
|
sendServer: sendServer ?? ((event, payload) => {
|
|
108
105
|
sendServerEvent(orbitalName, event, payload);
|
|
109
106
|
}),
|
|
@@ -240,8 +240,6 @@ interface EffectHandlers {
|
|
|
240
240
|
/** Pop the orbital-scoped navigation stack and return to the previous
|
|
241
241
|
* entry (client only). */
|
|
242
242
|
navigateBack?: () => void;
|
|
243
|
-
/** Show a notification (client: toast, server: log) */
|
|
244
|
-
notify?: (message: string, type: 'success' | 'error' | 'warning' | 'info') => void;
|
|
245
243
|
/** Log a message */
|
|
246
244
|
log?: (message: string, level?: 'log' | 'warn' | 'error', data?: unknown) => void;
|
|
247
245
|
/** Ref: declarative data subscription (server: same as fetch) */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@almadar/runtime",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.76.0",
|
|
4
4
|
"description": "Interpreted runtime for Almadar orbital applications (OrbitalServerRuntime)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -57,11 +57,11 @@
|
|
|
57
57
|
"access": "public"
|
|
58
58
|
},
|
|
59
59
|
"dependencies": {
|
|
60
|
-
"@almadar/core": "^10.
|
|
60
|
+
"@almadar/core": "^10.91.0",
|
|
61
61
|
"@almadar/evaluator": "^2.45.0",
|
|
62
62
|
"@almadar/logger": "^1.12.0",
|
|
63
|
-
"@almadar/server": "^2.
|
|
64
|
-
"@almadar/std": "^16.
|
|
63
|
+
"@almadar/server": "^2.41.0",
|
|
64
|
+
"@almadar/std": "^16.212.0"
|
|
65
65
|
},
|
|
66
66
|
"peerDependencies": {
|
|
67
67
|
"express": "^5.0.0"
|