@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.
@@ -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-CWaIuEQn.js';
3
- import { EventPayload, EventId, EntityRow, UserContext, ServiceParams, EntityAccessPolicies, Orbital, ServiceCallResult, TraitConfig, DeclaredTraitConfig, Entity, OrbitalSchema, Trait, PatternConfig, ResolvedPatternProps, SExpr, BusEventSource, RawUserClaims, OrbitalDefinition, TraitTick } from '@almadar/core';
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` / `notify` / `navigate` — these are client-side, provided
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 three effect kinds the client knows how to apply to the
1034
- * UI: render-ui (slot mutation), navigate (route change), notify (toast).
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` / `NotifyEffect` from `@almadar/core/types/effect.ts` in
1040
- * two places — the runtime appends an interpolated `props` object plus an
1041
- * optional `priority` slot to render-ui (slot 4 + 5), and `navigate` /
1042
- * `notify` may surface their optional argument as `undefined` rather than
1043
- * absent. The local union spells those variants out explicitly so consumers
1044
- * (ServerBridge.tsx parser, debugger inspector) get accurate completions
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 ClientNotifyTuple = ['notify', string, string | SExpr | undefined] | ['notify', string, string | SExpr | undefined, {
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, notify) */
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 ClientNotifyTuple as Q, type RegisteredOrbital as R, type SchemaLoader as S, type ClientRenderUITuple as T, type UnifiedLoaderOptions as U, type LiveBroadcastItem as V, type LoaderConfig as W, OrbitalServerRuntime as X, type RuntimeTraitTick as Y, createOrbitalServerRuntime as Z, 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 };
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 ClientNotifyTuple, T as ClientRenderUITuple, e as InMemoryPersistence, V as LiveBroadcastItem, W as LoaderConfig, O as OrbitalEventRequest, g as OrbitalEventResponse, X as OrbitalServerRuntime, h as OrbitalServerRuntimeConfig, P as PersistenceAdapter, R as RegisteredOrbital, m as RuntimeOrbital, n as RuntimeOrbitalSchema, o as RuntimeTrait, Y as RuntimeTraitTick, r as collectDeclaredConfigDefaults, Z as createOrbitalServerRuntime } from './OrbitalServerRuntime-BiEntWd-.js';
3
- import './types-CWaIuEQn.js';
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-QUW37QRQ.js';
2
- export { InMemoryPersistence, collectDeclaredConfigDefaults } from './chunk-QUW37QRQ.js';
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 registered.traits) {
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 createData = opRest[0] || {};
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 updateData = opRest[1] || {};
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
  }
@@ -1,4 +1,4 @@
1
- import { I as IEventBus } from './types-CWaIuEQn.js';
1
+ import { I as IEventBus } from './types-BO4tbEPp.js';
2
2
  import { EventPayload } from '@almadar/core';
3
3
 
4
4
  /**
@@ -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", "notify", "emit", "set", "log", "ref", "deref", "watch", "send-server", "browser/open-file-picker", "browser/clipboard-read", "browser/clipboard-write", "browser/geolocation-current"],
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 operations = args[1];
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
- if (this.handlers.notify) {
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. A
2480
- * SELF-referential `one`-cardinality relation (a parent column like
2481
- * `Tag.parentId : Tag`) is linked deterministically instead: row 0 stays a
2482
- * root (`""`) and row *i* parents to row ⌊(i−1)/2⌋ — a proper forest with
2483
- * real roots and no cycles, so tree views and `parentId = ""` root fetches
2484
- * render sensibly. Self-referential `many` relations keep the 2–4 random
2485
- * sibling IDs (excluding self). The runtime caps recursion at depth=2 in
2486
- * `populateRelations`, so deep chains render two levels then stop.
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 && (cardinality === "one" || cardinality === "many-to-one")) {
2529
- rows.forEach((row, i) => {
2530
- if (this.config.ownerId !== void 0 && row[field.name] === this.config.ownerId || stampedCellKeys.has(stampedCellKey(storeKey, row["id"], field.name))) {
2531
- return;
2532
- }
2533
- row[field.name] = i === 0 ? "" : rows[Math.floor((i - 1) / 2)]["id"];
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 !== "id" && f.name !== "createdAt" && f.name !== "updatedAt"
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 === "id" || field.name === "createdAt" || field.name === "updatedAt") continue;
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
  }
@@ -1,4 +1,4 @@
1
- import { E as EffectHandlers } from './types-CWaIuEQn.js';
1
+ import { E as EffectHandlers } from './types-BO4tbEPp.js';
2
2
  import { EventPayload } from '@almadar/core';
3
3
 
4
4
  /**
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-CWaIuEQn.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-CWaIuEQn.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-BiEntWd-.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-BiEntWd-.js';
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, EntityField, EntityRow, EntityPersistence, PayloadField, OrbitalDefinition, OrbitalSchema } from '@almadar/core';
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, notify, sendServer
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. A
751
- * SELF-referential `one`-cardinality relation (a parent column like
752
- * `Tag.parentId : Tag`) is linked deterministically instead: row 0 stays a
753
- * root (`""`) and row *i* parents to row ⌊(i−1)/2⌋ — a proper forest with
754
- * real roots and no cycles, so tree views and `parentId = ""` root fetches
755
- * render sensibly. Self-referential `many` relations keep the 2–4 random
756
- * sibling IDs (excluding self). The runtime caps recursion at depth=2 in
757
- * `populateRelations`, so deep chains render two levels then stop.
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-QUW37QRQ.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-QUW37QRQ.js';
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, notify, sendServer, orbitalName = "" } = options;
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.74.0",
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.89.0",
60
+ "@almadar/core": "^10.91.0",
61
61
  "@almadar/evaluator": "^2.45.0",
62
62
  "@almadar/logger": "^1.12.0",
63
- "@almadar/server": "^2.40.0",
64
- "@almadar/std": "^16.210.0"
63
+ "@almadar/server": "^2.41.0",
64
+ "@almadar/std": "^16.212.0"
65
65
  },
66
66
  "peerDependencies": {
67
67
  "express": "^5.0.0"