@agimon-ai/po-core 0.0.0-alpha.85 → 0.0.0-alpha.86

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.
@@ -161,6 +161,35 @@ function createHeadlessHub(options) {
161
161
  };
162
162
  const pendingHomeTopics = /* @__PURE__ */ new Set();
163
163
  const homeProjections = /* @__PURE__ */ new Map();
164
+ const homeCandidates = /* @__PURE__ */ new Map();
165
+ const homeProjectionCandidates = /* @__PURE__ */ new Map();
166
+ const removeHomeProjection = (key) => {
167
+ homeProjections.delete(key);
168
+ for (const identity of homeProjectionCandidates.get(key) ?? []) {
169
+ const candidates = homeCandidates.get(identity);
170
+ candidates.delete(key);
171
+ if (candidates.size === 0) homeCandidates.delete(identity);
172
+ }
173
+ homeProjectionCandidates.delete(key);
174
+ };
175
+ const indexHomeProjection = (key, frameType, session, projection) => {
176
+ for (const identity of homeProjectionCandidates.get(key) ?? []) {
177
+ const candidates = homeCandidates.get(identity);
178
+ candidates.delete(key);
179
+ if (candidates.size === 0) homeCandidates.delete(identity);
180
+ }
181
+ const identities = new Set(projection.entities.map((entity) => JSON.stringify([
182
+ frameType,
183
+ session.workspaceId,
184
+ entity.id
185
+ ])));
186
+ homeProjectionCandidates.set(key, identities);
187
+ for (const identity of identities) {
188
+ const candidates = homeCandidates.get(identity) ?? /* @__PURE__ */ new Set();
189
+ candidates.add(key);
190
+ homeCandidates.set(identity, candidates);
191
+ }
192
+ };
164
193
  let homeBumpQueued = false;
165
194
  const queueHomeBump = (session) => {
166
195
  pendingHomeTopics.add("global:home");
@@ -393,10 +422,30 @@ function createHeadlessHub(options) {
393
422
  const key = JSON.stringify([sessionId, frameType]);
394
423
  let projection;
395
424
  try {
396
- projection = JSON.stringify(source.homeFor(scopeOf(session))) ?? "";
425
+ const home = require_home.parseHomeProjection(source.homeFor(scopeOf(session)));
426
+ if (!home) throw new Error("Invalid Home projection.");
427
+ indexHomeProjection(key, frameType, session, home);
428
+ projection = JSON.stringify({
429
+ coverage: home.coverage,
430
+ entities: home.entities.map((entity) => {
431
+ if (entity.kind !== "running" && entity.kind !== "queued" && entity.kind !== "completed") return entity;
432
+ const competing = (homeCandidates.get(JSON.stringify([
433
+ frameType,
434
+ session.workspaceId,
435
+ entity.id
436
+ ]))?.size ?? 0) > 1;
437
+ return {
438
+ id: entity.id,
439
+ kind: entity.kind,
440
+ sessionId: entity.sessionId,
441
+ relatedSessionIds: entity.relatedSessionIds,
442
+ ...competing ? { updatedAt: entity.updatedAt } : {}
443
+ };
444
+ })
445
+ });
397
446
  } catch {
398
447
  projection = "";
399
- homeProjections.delete(key);
448
+ removeHomeProjection(key);
400
449
  }
401
450
  if (projection === "" || homeProjections.get(key) !== projection) {
402
451
  if (projection !== "") homeProjections.set(key, projection);
@@ -557,6 +606,16 @@ function createHeadlessHub(options) {
557
606
  } },
558
607
  onNotice: (message) => options.onNotice?.(message)
559
608
  });
609
+ const invalidateHomeSource = (session, frameType) => {
610
+ const key = JSON.stringify([session.id, frameType]);
611
+ removeHomeProjection(key);
612
+ const source = selectedChannel(session, frameType)?.source;
613
+ if (source?.homeFor) try {
614
+ const projection = require_home.parseHomeProjection(source.homeFor(scopeOf(session)));
615
+ if (projection) indexHomeProjection(key, frameType, session, projection);
616
+ } catch {}
617
+ queueHomeBump(session);
618
+ };
560
619
  const startChannel = (channel, mount) => {
561
620
  const key = `${mountKey(mount)}:${channel.frameType}`;
562
621
  if (channels.has(key)) {
@@ -579,8 +638,13 @@ function createHeadlessHub(options) {
579
638
  channels.set(key, started);
580
639
  for (const session of sessions.values()) {
581
640
  if (selectedChannel(session, channel.frameType) !== started) continue;
582
- displaced.get(session.id)?.source.sessionRemoved?.(session.id);
583
- source.sessionAdded?.(scopeOf(session));
641
+ invalidateHomeSource(session, channel.frameType);
642
+ try {
643
+ displaced.get(session.id)?.source.sessionRemoved?.(session.id);
644
+ source.sessionAdded?.(scopeOf(session));
645
+ } finally {
646
+ invalidateHomeSource(session, channel.frameType);
647
+ }
584
648
  }
585
649
  return started;
586
650
  } catch (error) {
@@ -593,8 +657,13 @@ function createHeadlessHub(options) {
593
657
  if (channels.get(key) !== started) return;
594
658
  const served = [...sessions.values()].filter((session) => selectedChannel(session, channel.frameType) === started);
595
659
  channels.delete(key);
660
+ for (const session of served) invalidateHomeSource(session, channel.frameType);
596
661
  started.source.close();
597
- for (const session of served) selectedChannel(session, channel.frameType)?.source.sessionAdded?.(scopeOf(session));
662
+ for (const session of served) try {
663
+ selectedChannel(session, channel.frameType)?.source.sessionAdded?.(scopeOf(session));
664
+ } finally {
665
+ invalidateHomeSource(session, channel.frameType);
666
+ }
598
667
  };
599
668
  const receiveMountedChannel = (mount, sessionId, frameType, payload, connectionId) => {
600
669
  if (!connectionId) return false;
@@ -617,7 +686,7 @@ function createHeadlessHub(options) {
617
686
  } finally {
618
687
  sessions.delete(sessionId);
619
688
  if (current.workspaceId !== void 0) workspacePresence.get(current.workspaceId)?.forget(`session:${sessionId}`);
620
- for (const key of homeProjections.keys()) if (key.startsWith(`[${JSON.stringify(sessionId)},`)) homeProjections.delete(key);
689
+ for (const key of homeProjectionCandidates.keys()) if (key.startsWith(`[${JSON.stringify(sessionId)},`)) removeHomeProjection(key);
621
690
  queueHomeBump(current);
622
691
  pendingSetups.delete(sessionId);
623
692
  activities.delete(sessionId);
@@ -862,7 +931,10 @@ function createHeadlessHub(options) {
862
931
  stopSelection?.();
863
932
  stopBackgroundWork?.();
864
933
  });
865
- for (const { source } of selectedChannels(current)) source.sessionAdded?.(scopeOf(session));
934
+ for (const { channel, source } of selectedChannels(current)) {
935
+ source.sessionAdded?.(scopeOf(session));
936
+ invalidateHomeSource(current, channel.frameType);
937
+ }
866
938
  const active = () => sessions.get(session.id)?.host === session.host && !closed;
867
939
  const cleanup = () => {
868
940
  subscriptions.delete(cleanup);
@@ -1241,6 +1313,7 @@ function createHeadlessHub(options) {
1241
1313
  },
1242
1314
  registerWorkspace(workspace) {
1243
1315
  workspaces.set(workspace.id, workspace);
1316
+ queueHomeBump({ workspaceId: workspace.id });
1244
1317
  emit({
1245
1318
  kind: "workspace_upsert",
1246
1319
  workspace
@@ -1265,6 +1338,7 @@ function createHeadlessHub(options) {
1265
1338
  workspaces.delete(id);
1266
1339
  compositionRevisions.delete(key);
1267
1340
  for (const topic of revisions.keys()) if (topic.startsWith(`workspace:${id}:`)) revisions.delete(topic);
1341
+ queueHomeBump({ workspaceId: id });
1268
1342
  emit({
1269
1343
  kind: "workspace_removed",
1270
1344
  workspaceId: id