@almadar/runtime 6.42.0 → 6.44.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.
@@ -316,7 +316,7 @@ declare class StateMachineManager {
316
316
  *
317
317
  * @returns Array of transition results (one per trait that had a matching transition)
318
318
  */
319
- sendEvent(eventKey: string, payload?: EventPayload, entityData?: EntityRow, entityByTrait?: Record<string, EntityRow>, eventId?: EventId, targetTrait?: string, user?: UserContext): Array<{
319
+ sendEvent(eventKey: string, payload?: EventPayload, entityData?: EntityRow, entityByTrait?: Record<string, EntityRow>, eventId?: EventId, targetTrait?: string, user?: UserContext, allowedTraits?: ReadonlySet<string>): Array<{
320
320
  traitName: string;
321
321
  result: TransitionResult;
322
322
  }>;
@@ -1199,6 +1199,14 @@ declare class OrbitalServerRuntime {
1199
1199
  * std registry on disk.
1200
1200
  */
1201
1201
  register(schema: OrbitalSchema): Promise<void>;
1202
+ /**
1203
+ * Teach the mock seeder which columns hold the viewer's id, derived from the
1204
+ * schema's `[identity]` entity. No-op for a program that declares none, so
1205
+ * unmigrated apps seed exactly as before. Mirrors
1206
+ * `orbital-core/src/runtime/seed.rs::owner_fields_from_schema` — a feature
1207
+ * that lives on only one execution path is not a feature.
1208
+ */
1209
+ private applyIdentityOwnerFields;
1202
1210
  /**
1203
1211
  * Register an OrbitalSchema synchronously (for backward compatibility).
1204
1212
  * Note: This version doesn't wait for instance seeding to complete.
@@ -1,4 +1,4 @@
1
1
  import 'express';
2
- export { F as ClientEffectTuple, G as ClientNavigateTuple, H as ClientNotifyTuple, J as ClientRenderUITuple, K as EffectResult, e as InMemoryPersistence, M as LiveBroadcastItem, N as LoaderConfig, O as OrbitalEventRequest, f as OrbitalEventResponse, Q as OrbitalServerRuntime, g as OrbitalServerRuntimeConfig, P as PersistenceAdapter, R as RegisteredOrbital, l as RuntimeOrbital, m as RuntimeOrbitalSchema, n as RuntimeTrait, T as RuntimeTraitTick, p as collectDeclaredConfigDefaults, V as createOrbitalServerRuntime } from './OrbitalServerRuntime-C3TtYP6I.js';
2
+ export { F as ClientEffectTuple, G as ClientNavigateTuple, H as ClientNotifyTuple, J as ClientRenderUITuple, K as EffectResult, e as InMemoryPersistence, M as LiveBroadcastItem, N as LoaderConfig, O as OrbitalEventRequest, f as OrbitalEventResponse, Q as OrbitalServerRuntime, g as OrbitalServerRuntimeConfig, P as PersistenceAdapter, R as RegisteredOrbital, l as RuntimeOrbital, m as RuntimeOrbitalSchema, n as RuntimeTrait, T as RuntimeTraitTick, p as collectDeclaredConfigDefaults, V as createOrbitalServerRuntime } from './OrbitalServerRuntime-Dz0oubQ_.js';
3
3
  import './types-p_L8-Sk4.js';
4
4
  import '@almadar/core';
@@ -1,5 +1,5 @@
1
- import { createTickScheduler, EventBus, createUnifiedLoader, MockPersistenceAdapter, InMemoryPersistence, preprocessSchema, normalizeCallSiteConfigToValues, StateMachineManager, parseDurationString, createContextFromBindings, validateEventPayload, formatPayloadValidationError, collectDeclaredConfigDefaults, resolveCallSitePayloadCaptures, collectDeclaredEntityDefaults, EffectExecutor } from './chunk-USANWPGZ.js';
2
- export { InMemoryPersistence, collectDeclaredConfigDefaults } from './chunk-USANWPGZ.js';
1
+ import { createTickScheduler, EventBus, createUnifiedLoader, MockPersistenceAdapter, InMemoryPersistence, preprocessSchema, normalizeCallSiteConfigToValues, StateMachineManager, parseDurationString, createContextFromBindings, validateEventPayload, formatPayloadValidationError, collectDeclaredConfigDefaults, resolveCallSitePayloadCaptures, collectDeclaredEntityDefaults, EffectExecutor } from './chunk-EJHMWSUS.js';
2
+ export { InMemoryPersistence, collectDeclaredConfigDefaults } from './chunk-EJHMWSUS.js';
3
3
  import { isValidCronExpression } from './chunk-OU3ITB5S.js';
4
4
  import './chunk-T4VDAB4C.js';
5
5
  import './chunk-SCRAHWOC.js';
@@ -7,7 +7,8 @@ import './chunk-MLKGABMK.js';
7
7
  import { createLogger } from '@almadar/logger';
8
8
  import * as nodeModule from 'module';
9
9
  import { evaluateListenPayloadExpr, evaluateGuard, evaluate } from '@almadar/evaluator';
10
- import { buildResolvedTraitConfigs, isInlineTrait, isEntityCall, applyListenPayloadMapping, normalizeUserContext } from '@almadar/core';
10
+ import { DEFAULT_VIEWER, buildResolvedTraitConfigs, isInlineTrait, isEntityCall, applyListenPayloadMapping, normalizeUserContext } from '@almadar/core';
11
+ import { ownerFieldsFromSchema, identityEntityName } from '@almadar/core/mock';
11
12
 
12
13
  // src/identity/routing.ts
13
14
  function eventRouteKey(eventName, eventId) {
@@ -115,7 +116,12 @@ var OrbitalServerRuntime = class {
115
116
  // Default to mock mode for preview
116
117
  autoPreprocess: false,
117
118
  namespaceEvents: true,
118
- ...config
119
+ ...config,
120
+ // `@user` is never Null. A host that named no viewer (no ALMADAR_PERSONA,
121
+ // no auth yet) otherwise renders every `viewerName: @user.name` blank, so
122
+ // the app cannot say who you are. DEFAULT_VIEWER carries an empty `role`,
123
+ // so no `@user.role` guard changes outcome — see its doc in @almadar/core.
124
+ defaultUser: config.defaultUser ?? DEFAULT_VIEWER
119
125
  };
120
126
  this.eventBus = new EventBus();
121
127
  if (config.loaderConfig?.loader) {
@@ -135,7 +141,9 @@ var OrbitalServerRuntime = class {
135
141
  // Let the dev viewer own some seeded rows, so ownership-scoped views
136
142
  // ("only mine") have data instead of being indistinguishable from a
137
143
  // broken filter. Columns are declared, never inferred by name.
138
- ownerId: config.defaultUser?.id,
144
+ // `this.config`, not `config` — the DEFAULT_VIEWER fallback is applied
145
+ // there, and reading the raw param leaves every seeded row unowned.
146
+ ownerId: this.config.defaultUser?.id,
139
147
  ownerFields: config.mockOwnerFields
140
148
  });
141
149
  if (config.debug) {
@@ -293,6 +301,7 @@ var OrbitalServerRuntime = class {
293
301
  registerLog.warn("register:no-loader", { name: schema.name });
294
302
  }
295
303
  }
304
+ this.applyIdentityOwnerFields(schema);
296
305
  for (const orbital of schema.orbitals) {
297
306
  await this.registerOrbitalAsync(orbital);
298
307
  }
@@ -301,6 +310,23 @@ var OrbitalServerRuntime = class {
301
310
  this.resolvedSchema = schema;
302
311
  this.resolvedTraitConfigs = buildResolvedTraitConfigs(schema);
303
312
  }
313
+ /**
314
+ * Teach the mock seeder which columns hold the viewer's id, derived from the
315
+ * schema's `[identity]` entity. No-op for a program that declares none, so
316
+ * unmigrated apps seed exactly as before. Mirrors
317
+ * `orbital-core/src/runtime/seed.rs::owner_fields_from_schema` — a feature
318
+ * that lives on only one execution path is not a feature.
319
+ */
320
+ applyIdentityOwnerFields(schema) {
321
+ if (!(this.persistence instanceof MockPersistenceAdapter)) return;
322
+ const derived = ownerFieldsFromSchema(schema);
323
+ if (derived.length === 0) return;
324
+ this.persistence.addOwnerFields(derived);
325
+ persistLog.debug("mock:identity-owner-fields", {
326
+ identity: identityEntityName(schema),
327
+ columns: derived
328
+ });
329
+ }
304
330
  /**
305
331
  * Register an OrbitalSchema synchronously (for backward compatibility).
306
332
  * Note: This version doesn't wait for instance seeding to complete.
@@ -310,6 +336,7 @@ var OrbitalServerRuntime = class {
310
336
  if (this.config.debug) {
311
337
  registerLog.debug("register:schema-sync", { name: schema.name });
312
338
  }
339
+ this.applyIdentityOwnerFields(schema);
313
340
  for (const orbital of schema.orbitals) {
314
341
  this.registerOrbital(orbital);
315
342
  }
@@ -317,6 +344,7 @@ var OrbitalServerRuntime = class {
317
344
  this.setupTicks();
318
345
  this.resolvedSchema = schema;
319
346
  this.resolvedTraitConfigs = buildResolvedTraitConfigs(schema);
347
+ this.applyIdentityOwnerFields(schema);
320
348
  }
321
349
  /**
322
350
  * Returns the schema that this runtime is currently executing, post-
@@ -994,7 +1022,8 @@ var OrbitalServerRuntime = class {
994
1022
  entityByTrait,
995
1023
  eventId,
996
1024
  targetTrait,
997
- viewer
1025
+ viewer,
1026
+ activeTraits && activeTraits.length > 0 ? new Set(activeTraits) : void 0
998
1027
  );
999
1028
  const filteredResults = activeTraits && activeTraits.length > 0 ? results.filter(({ traitName }) => activeTraits.includes(traitName)) : results;
1000
1029
  if (this.config.debug && activeTraits) {
@@ -962,11 +962,15 @@ var StateMachineManager = class {
962
962
  *
963
963
  * @returns Array of transition results (one per trait that had a matching transition)
964
964
  */
965
- sendEvent(eventKey, payload, entityData, entityByTrait, eventId, targetTrait, user) {
965
+ sendEvent(eventKey, payload, entityData, entityByTrait, eventId, targetTrait, user, allowedTraits) {
966
966
  const results = [];
967
967
  const scope = scopeOf(entityData);
968
968
  for (const [traitName, trait] of this.traits) {
969
- if (targetTrait !== void 0 && traitName !== targetTrait) continue;
969
+ if (targetTrait !== void 0) {
970
+ if (traitName !== targetTrait) continue;
971
+ } else if (allowedTraits !== void 0 && !allowedTraits.has(traitName)) {
972
+ continue;
973
+ }
970
974
  const traitState = this.getOrInitState(traitName, scope);
971
975
  if (!traitState) continue;
972
976
  const key = compositeKey(traitName, scope);
@@ -2342,6 +2346,22 @@ var MockPersistenceAdapter = class {
2342
2346
  seedRandom(this.config.seed);
2343
2347
  mockLog.debug("mock:adapter:init", { seed: this.config.seed });
2344
2348
  }
2349
+ /**
2350
+ * Add owner columns discovered after construction.
2351
+ *
2352
+ * The adapter is built before any schema is registered, so schema-DERIVED
2353
+ * owner columns (relation fields pointing at the `[identity]` entity) can
2354
+ * only arrive later.
2355
+ *
2356
+ * ⚠️ Seeding is EAGER — `registerEntity()` seeds immediately — so callers must
2357
+ * supply these columns BEFORE registering any orbital. Calling this afterwards
2358
+ * silently stamps nothing.
2359
+ */
2360
+ addOwnerFields(fields) {
2361
+ if (fields.length === 0) return;
2362
+ const merged = /* @__PURE__ */ new Set([...this.config.ownerFields ?? [], ...fields]);
2363
+ this.config.ownerFields = [...merged];
2364
+ }
2345
2365
  /** Re-anchor the PRNG to the configured seed. Called before every
2346
2366
  * re-seed loop so identical reseed sequences produce identical rows
2347
2367
  * (timestamps + generated fields). Without this, the first
@@ -2418,6 +2438,9 @@ var MockPersistenceAdapter = class {
2418
2438
  if (relationFields.length === 0) continue;
2419
2439
  for (const row of store.values()) {
2420
2440
  for (const field of relationFields) {
2441
+ if (this.config.ownerId !== void 0 && row[field.name] === this.config.ownerId) {
2442
+ continue;
2443
+ }
2421
2444
  const targetNormalized = (field.relation.entityId && this.storeNameById.get(field.relation.entityId)) ?? field.relation.entity.toLowerCase();
2422
2445
  const targetStore = this.stores.get(targetNormalized);
2423
2446
  if (!targetStore || targetStore.size === 0) continue;
@@ -3638,6 +3661,96 @@ function renameEntityInEffect(effect, rename, props) {
3638
3661
  }
3639
3662
  return effect;
3640
3663
  }
3664
+ var TRAIT_EMBED_PREFIX = "@trait.";
3665
+ function renameTraitEmbedsInValue(node, subs) {
3666
+ if (node === null || node === void 0) return node;
3667
+ if (typeof node === "string") {
3668
+ if (!node.startsWith(TRAIT_EMBED_PREFIX)) return node;
3669
+ const rest = node.slice(TRAIT_EMBED_PREFIX.length);
3670
+ const dot = rest.search(/[.[]/);
3671
+ const name = dot === -1 ? rest : rest.slice(0, dot);
3672
+ const suffix = dot === -1 ? "" : rest.slice(dot);
3673
+ const to = subs.get(name);
3674
+ return to === void 0 ? node : `${TRAIT_EMBED_PREFIX}${to}${suffix}`;
3675
+ }
3676
+ if (Array.isArray(node)) return node.map((item) => renameTraitEmbedsInValue(item, subs));
3677
+ if (typeof node !== "object") return node;
3678
+ const next = {};
3679
+ for (const [key, value] of Object.entries(node)) {
3680
+ next[key] = renameTraitEmbedsInValue(value, subs);
3681
+ }
3682
+ return next;
3683
+ }
3684
+ function renameTraitEmbeds(trait, subs) {
3685
+ if (subs.size === 0) return trait;
3686
+ const sm = trait.stateMachine;
3687
+ const next = { ...trait };
3688
+ if (sm) {
3689
+ next.stateMachine = {
3690
+ ...sm,
3691
+ transitions: (sm.transitions ?? []).map(
3692
+ (t) => t.effects ? { ...t, effects: renameTraitEmbedsInValue(t.effects, subs) } : t
3693
+ )
3694
+ };
3695
+ }
3696
+ if (trait.ticks) {
3697
+ next.ticks = trait.ticks.map(
3698
+ (tick) => tick.effects ? { ...tick, effects: renameTraitEmbedsInValue(tick.effects, subs) } : tick
3699
+ );
3700
+ }
3701
+ if (trait.config) {
3702
+ const nextConfig = {};
3703
+ for (const [key, field] of Object.entries(trait.config)) {
3704
+ nextConfig[key] = field.default === void 0 ? field : {
3705
+ ...field,
3706
+ default: renameTraitEmbedsInValue(
3707
+ field.default,
3708
+ subs
3709
+ )
3710
+ };
3711
+ }
3712
+ next.config = nextConfig;
3713
+ }
3714
+ return next;
3715
+ }
3716
+ function findTraitEntryInOrbital(orbital, localName) {
3717
+ for (const traitRef of orbital.traits ?? []) {
3718
+ if (typeof traitRef === "string") continue;
3719
+ if ("stateMachine" in traitRef) {
3720
+ if (traitRef.name === localName) return traitRef;
3721
+ continue;
3722
+ }
3723
+ if (!("ref" in traitRef)) continue;
3724
+ const refObj = traitRef;
3725
+ const declared = refObj.name ?? parseImportedTraitRef(refObj.ref)?.traitName;
3726
+ if (declared === localName) return traitRef;
3727
+ }
3728
+ return null;
3729
+ }
3730
+ function traitEmbedNamesOf(trait) {
3731
+ const found = /* @__PURE__ */ new Set();
3732
+ const walk = (node) => {
3733
+ if (node === null || node === void 0) return;
3734
+ if (typeof node === "string") {
3735
+ if (!node.startsWith(TRAIT_EMBED_PREFIX)) return;
3736
+ const rest = node.slice(TRAIT_EMBED_PREFIX.length);
3737
+ const dot = rest.search(/[.[]/);
3738
+ const name = dot === -1 ? rest : rest.slice(0, dot);
3739
+ if (name.length > 0) found.add(name);
3740
+ return;
3741
+ }
3742
+ if (Array.isArray(node)) {
3743
+ for (const item of node) walk(item);
3744
+ return;
3745
+ }
3746
+ if (typeof node !== "object") return;
3747
+ for (const value of Object.values(node)) walk(value);
3748
+ };
3749
+ for (const t of trait.stateMachine?.transitions ?? []) walk(t.effects);
3750
+ for (const tick of trait.ticks ?? []) walk(tick.effects);
3751
+ for (const field of Object.values(trait.config ?? {})) walk(field.default);
3752
+ return [...found];
3753
+ }
3641
3754
  function applyLinkedEntityRename(trait, linkedEntity) {
3642
3755
  const atomLinked = trait.linkedEntity;
3643
3756
  if (!linkedEntity || !atomLinked || linkedEntity === atomLinked) return trait;
@@ -3809,6 +3922,8 @@ var ReferenceResolver = class {
3809
3922
  localTraits;
3810
3923
  /** id-keyed mirror of `localTraits`, populated wherever the trait carries an `id`. */
3811
3924
  localTraitsById = /* @__PURE__ */ new Map();
3925
+ /** Import scope of each loaded source orbital, keyed by its source path. */
3926
+ sourceImportsCache = /* @__PURE__ */ new Map();
3812
3927
  loaderInitialized = false;
3813
3928
  constructor(options) {
3814
3929
  this.options = options;
@@ -3865,6 +3980,10 @@ var ReferenceResolver = class {
3865
3980
  if (!entityResult.success || !traitsResult.success || !pagesResult.success) {
3866
3981
  return { success: false, errors: ["Internal error: unexpected failure state"] };
3867
3982
  }
3983
+ const pullErrors = await this.pullSiblingTraits(traitsResult.data, imports, importChain);
3984
+ if (pullErrors.length > 0) {
3985
+ return { success: false, errors: pullErrors };
3986
+ }
3868
3987
  for (const resolvedTrait of traitsResult.data) {
3869
3988
  resolvedTrait.trait = resolveEntityTokensById(resolvedTrait.trait, imports.idIndex);
3870
3989
  resolvedTrait.trait = resolveConfigRefsById(resolvedTrait.trait, imports.idIndex);
@@ -4038,6 +4157,191 @@ var ReferenceResolver = class {
4038
4157
  }
4039
4158
  return { success: true, data: resolved, warnings: [] };
4040
4159
  }
4160
+ /**
4161
+ * The import scope of an already-loaded orbital, memoised per source path.
4162
+ * A sibling that is itself a ref names its target through the SOURCE atom's
4163
+ * aliases, so it can only be resolved against those.
4164
+ */
4165
+ async importsOfSource(imported, chain) {
4166
+ const key = imported.sourcePath ?? `${imported.alias}:${imported.from}`;
4167
+ const cached = this.sourceImportsCache.get(key);
4168
+ if (cached) return cached;
4169
+ const result = await this.resolveImports(
4170
+ imported.orbital.uses ?? [],
4171
+ imported.sourcePath,
4172
+ chain
4173
+ );
4174
+ if (!result.success) return null;
4175
+ result.data.idIndex = buildIdIndex(imported.orbital, result.data.orbitals);
4176
+ this.sourceImportsCache.set(key, result.data);
4177
+ return result.data;
4178
+ }
4179
+ /**
4180
+ * Sibling-trait auto-pull — the JS twin of the compiler's
4181
+ * `phases/inline/trait.rs` pass, appending to `resolved` in place.
4182
+ *
4183
+ * An atom's main trait embeds its own sub-views as `@trait.<Sibling>` string
4184
+ * literals (`std-browse`'s `bodyContent: {children: [@trait.DataGrid1]}`).
4185
+ * A consumer that imports only the main trait never instantiates those
4186
+ * siblings, so the token dangles: no state machine, no fetch, an empty list
4187
+ * where the grid should be. The compiled path materialises them; without this
4188
+ * pass the interpreted path did not, so a raw `.orb` handed straight to
4189
+ * `OrbitalServerRuntime` rendered a different app than the same `.orb` run
4190
+ * through `orbital resolve`.
4191
+ *
4192
+ * Pulls are keyed per EMBEDDER (`owner` = the top-level consumer trait that
4193
+ * started the chain), not per atom. Two rebinds of one atom in one orbital
4194
+ * (`ChannelRail -> Channel` and `ChatThread -> ChatMessage`, both std-browse)
4195
+ * carry different entity rebinds and different hosts to listen to, so one
4196
+ * shared copy can only ever serve one of them. The first owner keeps the
4197
+ * source name; later owners pull under `<Owner><Sibling>` — unique by
4198
+ * construction, since owner names are unique within the orbital.
4199
+ */
4200
+ async pullSiblingTraits(resolved, imports, chain) {
4201
+ const work = [];
4202
+ const ownerSubs = /* @__PURE__ */ new Map();
4203
+ const subsFor = (owner) => {
4204
+ let m = ownerSubs.get(owner);
4205
+ if (!m) {
4206
+ m = /* @__PURE__ */ new Map();
4207
+ ownerSubs.set(owner, m);
4208
+ }
4209
+ return m;
4210
+ };
4211
+ for (const rt of resolved) {
4212
+ if (rt.source.type !== "imported" || !rt.trait.name) continue;
4213
+ const owner = rt.trait.name;
4214
+ subsFor(owner).set(rt.source.traitName, owner);
4215
+ for (const sibling of traitEmbedNamesOf(rt.trait)) {
4216
+ work.push({ alias: rt.source.alias, sibling, linkedEntity: rt.linkedEntity, parent: owner, owner });
4217
+ }
4218
+ }
4219
+ if (work.length === 0) return [];
4220
+ const errors = [];
4221
+ const consumerDeclared = new Set(resolved.map((r) => r.trait.name).filter(Boolean));
4222
+ const seen = new Set(consumerDeclared);
4223
+ const visited = /* @__PURE__ */ new Set();
4224
+ const pulledAs = /* @__PURE__ */ new Map();
4225
+ const ownerOf = /* @__PURE__ */ new Map();
4226
+ const parentRewrites = /* @__PURE__ */ new Map();
4227
+ const pulled = [];
4228
+ const noteRewrite = (parent, from, to) => {
4229
+ let m = parentRewrites.get(parent);
4230
+ if (!m) {
4231
+ m = /* @__PURE__ */ new Map();
4232
+ parentRewrites.set(parent, m);
4233
+ }
4234
+ m.set(from, to);
4235
+ };
4236
+ for (let item = work.pop(); item !== void 0; item = work.pop()) {
4237
+ const { alias, sibling, linkedEntity, parent, owner } = item;
4238
+ const pullKey = `${alias}\0${sibling}\0${owner}`;
4239
+ const existing = pulledAs.get(pullKey);
4240
+ if (existing !== void 0) {
4241
+ if (existing !== sibling) noteRewrite(parent, sibling, existing);
4242
+ continue;
4243
+ }
4244
+ let finalName = sibling;
4245
+ if (seen.has(sibling)) {
4246
+ if (consumerDeclared.has(sibling)) continue;
4247
+ finalName = `${owner}${sibling}`;
4248
+ if (seen.has(finalName)) continue;
4249
+ }
4250
+ if (visited.has(pullKey)) continue;
4251
+ visited.add(pullKey);
4252
+ const imported = imports.orbitals.get(alias);
4253
+ if (!imported) continue;
4254
+ const atomEntry = findTraitEntryInOrbital(imported.orbital, sibling);
4255
+ if (!atomEntry) continue;
4256
+ let atomTrait;
4257
+ if ("stateMachine" in atomEntry) {
4258
+ const { trait: cfgResolved, errors: cfgErrors } = resolveConfigRefEmitNames(
4259
+ atomEntry
4260
+ );
4261
+ if (cfgErrors.length > 0) {
4262
+ errors.push(...cfgErrors);
4263
+ continue;
4264
+ }
4265
+ atomTrait = cfgResolved;
4266
+ } else {
4267
+ const srcImports = await this.importsOfSource(imported, chain);
4268
+ if (!srcImports) continue;
4269
+ const refObj = atomEntry;
4270
+ const nested = this.resolveTraitRefString(
4271
+ refObj.ref,
4272
+ srcImports,
4273
+ refObj.config,
4274
+ refObj.linkedEntity,
4275
+ refObj.name ?? sibling,
4276
+ refObj.events,
4277
+ refObj.listens,
4278
+ refObj.refId
4279
+ );
4280
+ if (!nested.success) {
4281
+ errors.push(...nested.errors);
4282
+ continue;
4283
+ }
4284
+ atomTrait = nested.data.trait;
4285
+ }
4286
+ let copy = applyLinkedEntityRename(atomTrait, linkedEntity);
4287
+ if (finalName !== sibling) {
4288
+ copy = { ...copy, name: finalName };
4289
+ noteRewrite(parent, sibling, finalName);
4290
+ }
4291
+ for (const next of traitEmbedNamesOf(copy)) {
4292
+ work.push({ alias, sibling: next, linkedEntity, parent: finalName, owner });
4293
+ }
4294
+ pulledAs.set(pullKey, finalName);
4295
+ subsFor(owner).set(sibling, finalName);
4296
+ ownerOf.set(finalName, owner);
4297
+ seen.add(finalName);
4298
+ pulled.push({
4299
+ trait: copy,
4300
+ source: { type: "imported", alias, traitName: sibling },
4301
+ ...linkedEntity !== void 0 ? { linkedEntity } : {}
4302
+ });
4303
+ }
4304
+ if (pulled.length === 0 && parentRewrites.size === 0) return errors;
4305
+ const applyRewrites = (rt) => {
4306
+ const subs = rt.trait.name ? parentRewrites.get(rt.trait.name) : void 0;
4307
+ if (subs) rt.trait = renameTraitEmbeds(rt.trait, subs);
4308
+ };
4309
+ for (const rt of resolved) applyRewrites(rt);
4310
+ for (const rt of pulled) applyRewrites(rt);
4311
+ const idByName = /* @__PURE__ */ new Map();
4312
+ for (const rt of [...resolved, ...pulled]) {
4313
+ if (rt.trait.name) idByName.set(rt.trait.name, rt.trait.id);
4314
+ }
4315
+ for (const rt of pulled) {
4316
+ const listens = rt.trait.listens;
4317
+ const owner = rt.trait.name ? ownerOf.get(rt.trait.name) : void 0;
4318
+ const subs = owner ? ownerSubs.get(owner) : void 0;
4319
+ if (!listens || listens.length === 0 || !subs) continue;
4320
+ let changed = false;
4321
+ const nextListens = listens.map((listen) => {
4322
+ const source = listen.source;
4323
+ if (!source || source.kind !== "trait") return listen;
4324
+ const target = subs.get(source.trait);
4325
+ if (target === void 0 || target === source.trait) return listen;
4326
+ changed = true;
4327
+ const traitId = idByName.get(target);
4328
+ return {
4329
+ ...listen,
4330
+ source: { kind: "trait", trait: target, ...traitId ? { traitId } : {} }
4331
+ };
4332
+ });
4333
+ if (changed) rt.trait = { ...rt.trait, listens: nextListens };
4334
+ }
4335
+ refResolverLog.info("sibling-pull", {
4336
+ pulled: pulled.map((p) => ({
4337
+ trait: p.trait.name,
4338
+ owner: p.trait.name ? ownerOf.get(p.trait.name) : void 0,
4339
+ linkedEntity: p.linkedEntity ?? p.trait.linkedEntity
4340
+ }))
4341
+ });
4342
+ resolved.push(...pulled);
4343
+ return errors;
4344
+ }
4041
4345
  /**
4042
4346
  * Resolve a single trait reference.
4043
4347
  */
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { B as BindingContext, f as EvaluationContextExtensions, P as PatternProps, E as EffectHandlers, g as EffectContext, h as ExecutionEnvironment, i as EffectResult, T as TraitDefinition } from './types-p_L8-Sk4.js';
2
2
  export { j as BrowserFileMeta, k as BrowserFilePickerOptions, l as BrowserGeolocationOptions, m as BrowserGeolocationPosition, C as ConfigContext, n 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-p_L8-Sk4.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-C3TtYP6I.js';
4
- export { E as EntitySharingMap, c as EventBus, d as EventNamespaceMap, e as InMemoryPersistence, O as OrbitalEventRequest, f as OrbitalEventResponse, g as OrbitalServerRuntimeConfig, h as PreprocessOptions, i as PreprocessResult, j as PreprocessedSchema, k as ProcessEventOptions, R as RegisteredOrbital, l as RuntimeOrbital, m as RuntimeOrbitalSchema, n as RuntimeTrait, o as StateMachineManager, p as collectDeclaredConfigDefaults, q as collectDeclaredEntityDefaults, r as createInitialTraitState, s as findInitialState, t as findTransition, u as getIsolatedCollectionName, v as getNamespacedEvent, w as isBrowser, x as isElectron, y as isNamespacedEvent, z as isNode, A as normalizeEventKey, B as parseNamespacedEvent, C as preprocessSchema, D as processEvent } from './OrbitalServerRuntime-C3TtYP6I.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-Dz0oubQ_.js';
4
+ export { E as EntitySharingMap, c as EventBus, d as EventNamespaceMap, e as InMemoryPersistence, O as OrbitalEventRequest, f as OrbitalEventResponse, g as OrbitalServerRuntimeConfig, h as PreprocessOptions, i as PreprocessResult, j as PreprocessedSchema, k as ProcessEventOptions, R as RegisteredOrbital, l as RuntimeOrbital, m as RuntimeOrbitalSchema, n as RuntimeTrait, o as StateMachineManager, p as collectDeclaredConfigDefaults, q as collectDeclaredEntityDefaults, r as createInitialTraitState, s as findInitialState, t as findTransition, u as getIsolatedCollectionName, v as getNamespacedEvent, w as isBrowser, x as isElectron, y as isNamespacedEvent, z as isNode, A as normalizeEventKey, B as parseNamespacedEvent, C as preprocessSchema, D as processEvent } from './OrbitalServerRuntime-Dz0oubQ_.js';
5
5
  import { EvaluationContext, SExpressionEvaluator } from '@almadar/evaluator';
6
6
  export { EvaluationContext, createMinimalContext } from '@almadar/evaluator';
7
7
  import { TraitConfigObject, EventPayload, PatternConfig, EntityId, EntityField, EntityRow, EntityPersistence, ServiceParams, PayloadField, OrbitalDefinition, OrbitalSchema } from '@almadar/core';
@@ -585,6 +585,18 @@ declare class MockPersistenceAdapter implements PersistenceAdapter {
585
585
  private storeNameById;
586
586
  private config;
587
587
  constructor(config?: MockPersistenceConfig);
588
+ /**
589
+ * Add owner columns discovered after construction.
590
+ *
591
+ * The adapter is built before any schema is registered, so schema-DERIVED
592
+ * owner columns (relation fields pointing at the `[identity]` entity) can
593
+ * only arrive later.
594
+ *
595
+ * ⚠️ Seeding is EAGER — `registerEntity()` seeds immediately — so callers must
596
+ * supply these columns BEFORE registering any orbital. Calling this afterwards
597
+ * silently stamps nothing.
598
+ */
599
+ addOwnerFields(fields: readonly string[]): void;
588
600
  /** Re-anchor the PRNG to the configured seed. Called before every
589
601
  * re-seed loop so identical reseed sequences produce identical rows
590
602
  * (timestamps + generated fields). Without this, the first
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { EffectExecutor, createContextFromBindings } from './chunk-USANWPGZ.js';
2
- export { CALLSITE_PAYLOAD_PREFIX, EffectExecutor, EventBus, HANDLER_MANIFEST, InMemoryPersistence, MockPersistenceAdapter, StateMachineManager, TickScheduler, buildEmitsFromTraits, collectDeclaredConfigDefaults, collectDeclaredEntityDefaults, containsBindings, createContextFromBindings, createInitialTraitState, createMinimalContext, createMockPersistence, createTestExecutor, createTickScheduler, createUnifiedLoader, extractBindings, findInitialState, findTransition, formatPayloadValidationError, getIsolatedCollectionName, getNamespacedEvent, interpolateProps, interpolateValue, isBrowser, isElectron, isNamespacedEvent, isNode, isValidDurationString, normalizeCallSiteConfigToValues, normalizeEventKey, parseDurationString, parseNamespacedEvent, preprocessSchema, processEvent, resolveCallSitePayloadCaptures, validateEventPayload, validatePayloadShapes } from './chunk-USANWPGZ.js';
1
+ import { EffectExecutor, createContextFromBindings } from './chunk-EJHMWSUS.js';
2
+ export { CALLSITE_PAYLOAD_PREFIX, EffectExecutor, EventBus, HANDLER_MANIFEST, InMemoryPersistence, MockPersistenceAdapter, StateMachineManager, TickScheduler, buildEmitsFromTraits, collectDeclaredConfigDefaults, collectDeclaredEntityDefaults, containsBindings, createContextFromBindings, createInitialTraitState, createMinimalContext, createMockPersistence, createTestExecutor, createTickScheduler, createUnifiedLoader, extractBindings, findInitialState, findTransition, formatPayloadValidationError, getIsolatedCollectionName, getNamespacedEvent, interpolateProps, interpolateValue, isBrowser, isElectron, isNamespacedEvent, isNode, isValidDurationString, normalizeCallSiteConfigToValues, normalizeEventKey, parseDurationString, parseNamespacedEvent, preprocessSchema, processEvent, resolveCallSitePayloadCaptures, validateEventPayload, validatePayloadShapes } from './chunk-EJHMWSUS.js';
3
3
  export { cronMatches, cronMinuteKey, isValidCronExpression, parseCron, parseCronField } from './chunk-OU3ITB5S.js';
4
4
  import './chunk-T4VDAB4C.js';
5
5
  export { PERF_NAMESPACE, RendererContractViolationError, adjustSchemaForMockData, aggregateSlotContent, assertIsMultiSourceSlotManager, assertIsSlotManager, bindEventBus, bindTraitStateGetter, buildMockData, buildOrbitalsByTrait, clearPerf, createSlotSetter, ensureVerificationApi, getOrbitalVerification, perfEnd, perfStart, perfStore, perfTime, prepareSchemaForPreview, pushPerfEntry, validateSlotContent, wrapCallbackForEvent } from './chunk-FOFLEZRJ.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@almadar/runtime",
3
- "version": "6.42.0",
3
+ "version": "6.44.0",
4
4
  "description": "Interpreted runtime for Almadar orbital applications (OrbitalServerRuntime)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -52,11 +52,11 @@
52
52
  "access": "public"
53
53
  },
54
54
  "dependencies": {
55
- "@almadar/core": "^10.38.0",
55
+ "@almadar/core": "^10.42.0",
56
56
  "@almadar/evaluator": "^2.37.0",
57
57
  "@almadar/logger": "^1.10.0",
58
58
  "@almadar/server": "^2.29.0",
59
- "@almadar/std": "^16.149.0"
59
+ "@almadar/std": "^16.152.0"
60
60
  },
61
61
  "peerDependencies": {
62
62
  "express": "^5.0.0"