@memberjunction/core-entities 6.1.2 → 6.1.4

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.
@@ -834,12 +834,27 @@ export declare class ConversationEngine extends BaseEngine<ConversationEngine> {
834
834
  */
835
835
  protected HandleIndividualBaseEntityEvent(event: BaseEntityEvent): Promise<boolean>;
836
836
  /**
837
- * Extracts record data from a BaseEntityEvent.
838
- * For local events: uses baseEntity directly.
839
- * For remote-invalidate events: parses recordData JSON from the payload.
840
- * Returns null if no data is available.
837
+ * Will anything below actually use the row, or does the primary key suffice?
838
+ *
839
+ * Asked BEFORE hydrating, because for a remote event whose entity is not on the server's
840
+ * broadcast allowlist, hydrating costs a read through the provider. This dispatcher runs in
841
+ * every connected browser, for every save of these entities anywhere in the system — and
842
+ * conversation details are the hottest write path in the product, so an unconditional read
843
+ * here is one round trip per connected client per message, nearly all of them refused for a
844
+ * session with no claim to the record.
845
+ *
846
+ * Every `false` below is a case where the handler already returns early without touching the
847
+ * row, so skipping the read changes nothing a caller can observe.
848
+ */
849
+ private eventNeedsRow;
850
+ /**
851
+ * This record's id, from the primary key the event always carries.
852
+ *
853
+ * Prefers the key over the row: the key is broadcast unconditionally, whereas the row is only
854
+ * present for allowlisted entities or after a re-read. Falls back to the row's `ID` so a
855
+ * single-column entity still resolves if the key is ever absent.
841
856
  */
842
- private extractRecordData;
857
+ private eventRecordID;
843
858
  /**
844
859
  * Safely merges data onto a target object that may or may not be a BaseEntity.
845
860
  * If the target has SetMany (i.e., it's a BaseEntity), uses that to properly handle
@@ -1 +1 @@
1
- {"version":3,"file":"conversations.d.ts","sourceRoot":"","sources":["../../src/engines/conversations.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"conversations.d.ts","sourceRoot":"","sources":["../../src/engines/conversations.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { BaseEngine, RunQuery, RunView, TransformSimpleObjectToEntityObject } from "@memberjunction/core";
1
+ import { BaseEngine, EntityEventRowIsFree, ResolveEntityEventKey, ResolveEntityEventRow, RunQuery, RunView, TransformSimpleObjectToEntityObject } from "@memberjunction/core";
2
2
  import { NormalizeUUID, ToEpochMs, UUIDsEqual } from "@memberjunction/global";
3
3
  import { BehaviorSubject } from "rxjs";
4
4
  import { ArtifactMetadataEngine } from "./artifacts.js";
@@ -1782,46 +1782,130 @@ export class ConversationEngine extends BaseEngine {
1782
1782
  const effectiveType = event.type === 'remote-invalidate'
1783
1783
  ? event.payload?.action || 'save'
1784
1784
  : event.type;
1785
- if (normalizedName === 'mj: conversations') {
1786
- return this.handleConversationEntityEvent(event, effectiveType);
1787
- }
1788
- if (normalizedName === 'mj: conversation details') {
1789
- return this.handleConversationDetailEntityEvent(event, effectiveType);
1790
- }
1791
- if (normalizedName === 'mj: projects') {
1792
- return this.handleProjectEntityEvent(event, effectiveType);
1793
- }
1794
- if (normalizedName === 'mj: ai agent runs') {
1795
- return this.handleAgentRunEntityEvent(event, effectiveType);
1796
- }
1797
- if (normalizedName === 'mj: conversation detail artifacts' || normalizedName === 'mj: conversation detail ratings') {
1798
- return this.handlePeripheralJunctionEntityEvent(event);
1785
+ const handled = normalizedName === 'mj: conversations' ||
1786
+ normalizedName === 'mj: conversation details' ||
1787
+ normalizedName === 'mj: projects' ||
1788
+ normalizedName === 'mj: ai agent runs' ||
1789
+ normalizedName === 'mj: conversation detail artifacts' ||
1790
+ normalizedName === 'mj: conversation detail ratings';
1791
+ if (handled) {
1792
+ // Hydrate ONCE, here, because this is the only async frame on the path. A remote event
1793
+ // carries the row only for entities on the server's broadcast allowlist (see
1794
+ // `cacheSettings.recordDataBroadcastEntities`); otherwise this re-reads the single
1795
+ // record through the provider, as this user, so access control decides what comes
1796
+ // back. Handlers below stay synchronous and simply receive the row — passing it down
1797
+ // rather than letting each fetch its own keeps this to one read per event and avoids
1798
+ // turning five handlers async for a value the dispatcher can obtain once.
1799
+ // ...and only when something below will actually use it — see eventNeedsRow. On a
1800
+ // remote event without `recordData`, hydrating means a read through the provider, and
1801
+ // this dispatcher runs in EVERY connected browser for every save of these entities
1802
+ // anywhere in the system, whether or not this session has any claim to the record.
1803
+ const row = this.eventNeedsRow(event, normalizedName, effectiveType)
1804
+ ? await ResolveEntityEventRow(event, this.ProviderToUse, this.ContextUser)
1805
+ : null;
1806
+ if (normalizedName === 'mj: conversations') {
1807
+ return this.handleConversationEntityEvent(event, effectiveType, row);
1808
+ }
1809
+ if (normalizedName === 'mj: conversation details') {
1810
+ return this.handleConversationDetailEntityEvent(event, effectiveType, row);
1811
+ }
1812
+ if (normalizedName === 'mj: projects') {
1813
+ return this.handleProjectEntityEvent(event, effectiveType, row);
1814
+ }
1815
+ if (normalizedName === 'mj: ai agent runs') {
1816
+ return this.handleAgentRunEntityEvent(event, effectiveType, row);
1817
+ }
1818
+ return this.handlePeripheralJunctionEntityEvent(event, row);
1799
1819
  }
1800
1820
  // Not a conversation entity — let BaseEngine handle it
1801
1821
  return await super.HandleIndividualBaseEntityEvent(event);
1802
1822
  }
1803
1823
  /**
1804
- * Extracts record data from a BaseEntityEvent.
1805
- * For local events: uses baseEntity directly.
1806
- * For remote-invalidate events: parses recordData JSON from the payload.
1807
- * Returns null if no data is available.
1824
+ * Will anything below actually use the row, or does the primary key suffice?
1825
+ *
1826
+ * Asked BEFORE hydrating, because for a remote event whose entity is not on the server's
1827
+ * broadcast allowlist, hydrating costs a read through the provider. This dispatcher runs in
1828
+ * every connected browser, for every save of these entities anywhere in the system — and
1829
+ * conversation details are the hottest write path in the product, so an unconditional read
1830
+ * here is one round trip per connected client per message, nearly all of them refused for a
1831
+ * session with no claim to the record.
1832
+ *
1833
+ * Every `false` below is a case where the handler already returns early without touching the
1834
+ * row, so skipping the read changes nothing a caller can observe.
1808
1835
  */
1809
- extractRecordData(event) {
1810
- // Local event — entity is available directly
1811
- if (event.baseEntity) {
1812
- return event.baseEntity.GetAll();
1836
+ eventNeedsRow(event, normalizedName, effectiveType) {
1837
+ // A row that costs nothing is never worth skipping. Free for a local event (the row IS the
1838
+ // live entity) and for a remote event whose payload already carries `recordData`, because
1839
+ // the entity is on the server's broadcast allowlist.
1840
+ //
1841
+ // This has to come first, and the per-entity reasoning below has to be read as being about
1842
+ // THE READ. Applying it to a free row is what dropped a remote project save: the save
1843
+ // branch uses EnvironmentID and IsArchived off the row, and with the row nulled it read
1844
+ // them as undefined/false, concluded the project was outside the loaded environment, and
1845
+ // filtered it out of the list — for exactly the sessions displaying it.
1846
+ if (EntityEventRowIsFree(event)) {
1847
+ return true;
1813
1848
  }
1814
- // Remote event — parse from payload
1815
- const payload = event.payload;
1816
- if (payload?.recordData) {
1817
- try {
1818
- return JSON.parse(payload.recordData);
1849
+ // Projects: a DELETE needs only the id. A SAVE genuinely uses the row — `EnvironmentID` and
1850
+ // `IsArchived` are what decide whether the project stays in the list — so it has to be
1851
+ // hydrated whenever we hold the project.
1852
+ //
1853
+ // The earlier reasoning here ("a save that got this far has nothing to merge regardless")
1854
+ // was true of the MERGE and false of the branch beside it: without the row, `EnvironmentID`
1855
+ // reads as absent, `inLoadedEnvironment` comes out false for any session that has loaded an
1856
+ // environment, and the save REMOVES the project instead. That is precisely the cross-client
1857
+ // case this change exists to serve, so it is gated like conversations are: when we do not
1858
+ // hold the project the remote path does nothing anyway (the append branch below requires
1859
+ // `event.baseEntity`), and the row would be fetched only to be discarded.
1860
+ if (normalizedName === 'mj: projects') {
1861
+ if (effectiveType !== 'save') {
1862
+ return false;
1819
1863
  }
1820
- catch {
1821
- return null;
1864
+ const id = this.eventRecordID(event, null);
1865
+ return !!id && this._projects$.value.some(p => UUIDsEqual(p.ID, id));
1866
+ }
1867
+ if (normalizedName === 'mj: conversations') {
1868
+ // A delete needs only the id. A save merges fields onto a conversation we already
1869
+ // hold — and when we do not hold it, the remote branch of the handler does nothing,
1870
+ // so the row would be fetched only to be discarded.
1871
+ if (effectiveType !== 'save') {
1872
+ return false;
1822
1873
  }
1874
+ const id = this.eventRecordID(event, null);
1875
+ return !!id && !!this.GetConversation(id);
1876
+ }
1877
+ // Details, agent runs and the junction entities all resolve through the detail cache and
1878
+ // return early when the conversation they name is not in it.
1879
+ //
1880
+ // A remote DELETE can never be served: the record is gone, so the re-read comes back null
1881
+ // and every one of those handlers early-returns on the missing foreign key. The round trip
1882
+ // could not change an outcome, so it is not made. (That these handlers cannot act on a
1883
+ // remote delete at all is a pre-existing gap — deletes never carried `recordData` at either
1884
+ // publish site — and is not what this method is for.) Local deletes are unaffected: the
1885
+ // free-row check above already returned true for them.
1886
+ if (effectiveType === 'delete') {
1887
+ return false;
1888
+ }
1889
+ // With the cache empty — any session that has not opened a conversation — none of them can
1890
+ // do anything, whatever the row says. (A non-empty cache still needs the read:
1891
+ // ConversationID is a foreign key, so the primary key cannot tell us whether this detail
1892
+ // belongs to a conversation we hold.)
1893
+ return this._detailCache.size > 0;
1894
+ }
1895
+ /**
1896
+ * This record's id, from the primary key the event always carries.
1897
+ *
1898
+ * Prefers the key over the row: the key is broadcast unconditionally, whereas the row is only
1899
+ * present for allowlisted entities or after a re-read. Falls back to the row's `ID` so a
1900
+ * single-column entity still resolves if the key is ever absent.
1901
+ */
1902
+ eventRecordID(event, data) {
1903
+ const key = ResolveEntityEventKey(event);
1904
+ const fromKey = key?.KeyValuePairs?.find(kv => kv.FieldName?.toLowerCase() === 'id')?.Value;
1905
+ if (fromKey != null && String(fromKey).length > 0) {
1906
+ return String(fromKey);
1823
1907
  }
1824
- return null;
1908
+ return data?.['ID'];
1825
1909
  }
1826
1910
  /**
1827
1911
  * Safely merges data onto a target object that may or may not be a BaseEntity.
@@ -1840,12 +1924,21 @@ export class ConversationEngine extends BaseEngine {
1840
1924
  /**
1841
1925
  * Handles save/delete events on Conversation entities from local or remote code.
1842
1926
  */
1843
- handleConversationEntityEvent(event, action) {
1844
- const data = this.extractRecordData(event);
1845
- const id = data?.['ID'];
1927
+ handleConversationEntityEvent(event, action, data) {
1928
+ // Identity comes from the primary key, which is broadcast unconditionally — a delete needs
1929
+ // nothing else, so it no longer depends on the row being available.
1930
+ const id = this.eventRecordID(event, data);
1846
1931
  if (!id)
1847
1932
  return true;
1848
1933
  if (action === 'save') {
1934
+ // Same reasoning as handleProjectEntityEvent: the row can be null even when
1935
+ // `eventNeedsRow` said yes, because the re-read can be refused, find the record gone,
1936
+ // or simply fail — all of which `ResolveEntityEventRow` reports as null rather than
1937
+ // throwing. `mergeDataOntoRecord` would hand that null to `BaseEntity.SetMany`, which
1938
+ // throws, and nothing above this frame catches it. A conversation we cannot re-read
1939
+ // stays as it was; the next `LoadConversations` corrects it.
1940
+ if (!data)
1941
+ return true;
1849
1942
  const existing = this.GetConversation(id);
1850
1943
  if (existing) {
1851
1944
  this.mergeDataOntoRecord(existing, data);
@@ -1879,10 +1972,11 @@ export class ConversationEngine extends BaseEngine {
1879
1972
  /**
1880
1973
  * Handles save/delete events on ConversationDetail entities from local or remote code.
1881
1974
  */
1882
- handleConversationDetailEntityEvent(event, action) {
1975
+ handleConversationDetailEntityEvent(event, action, data) {
1883
1976
  const entity = event.baseEntity;
1884
- const data = this.extractRecordData(event);
1885
- const id = data?.['ID'];
1977
+ const id = this.eventRecordID(event, data);
1978
+ // ConversationID is a foreign key, so the primary key cannot supply it — this is the field
1979
+ // the dispatcher's re-read exists to obtain.
1886
1980
  const conversationId = data?.['ConversationID'];
1887
1981
  if (!id || !conversationId)
1888
1982
  return true;
@@ -1924,9 +2018,8 @@ export class ConversationEngine extends BaseEngine {
1924
2018
  * deleted via the project form modal. Only tracks projects in the currently-loaded
1925
2019
  * environment; archived projects are dropped from the active list.
1926
2020
  */
1927
- handleProjectEntityEvent(event, action) {
1928
- const data = this.extractRecordData(event);
1929
- const id = data?.['ID'];
2021
+ handleProjectEntityEvent(event, action, data) {
2022
+ const id = this.eventRecordID(event, data);
1930
2023
  if (!id)
1931
2024
  return true;
1932
2025
  const current = this._projects$.value;
@@ -1937,9 +2030,32 @@ export class ConversationEngine extends BaseEngine {
1937
2030
  }
1938
2031
  return true;
1939
2032
  }
1940
- // save — only track projects in the loaded environment; drop archived ones
1941
- const environmentId = data?.['EnvironmentID'];
1942
- const isArchived = data?.['IsArchived'] === true;
2033
+ // WITHOUT THE ROW WE KNOW NOTHING, and must not guess. Every field below would read as
2034
+ // absent: `IsArchived` becomes false, which is harmless, but `EnvironmentID` becomes
2035
+ // undefined — and "no environment" is indistinguishable from "moved to another one", so the
2036
+ // branch that DROPS the project is the one that runs. Leaving it untouched is the only
2037
+ // honest response to a row we do not have.
2038
+ //
2039
+ // `eventNeedsRow` is supposed to guarantee this never happens for a project we hold, but
2040
+ // that contract is enforced by a comment and `strictNullChecks` is off in this package, so
2041
+ // the guarantee is restated here where the damage would be done. Hydration can also simply
2042
+ // fail: `ResolveEntityEventRow` returns null rather than throwing.
2043
+ //
2044
+ // KEEPING IT IS DELIBERATE EVEN WHEN THE ROW WAS WITHHELD ON PURPOSE. A null can mean the
2045
+ // re-read was refused — the viewer may no longer read this project — or that it failed.
2046
+ // The two are indistinguishable here, and they want opposite responses, so this takes the
2047
+ // one whose wrong case is recoverable: a stale row in a sidebar is corrected by the next
2048
+ // `LoadProjects`, whereas a project deleted from the UI on a transient read failure is
2049
+ // gone until the user reloads and cannot be told why.
2050
+ if (!data) {
2051
+ return true;
2052
+ }
2053
+ // save — only track projects in the loaded environment; drop archived ones.
2054
+ // No `?.` past the guard above: `data` is non-null here, and optional chaining would say
2055
+ // otherwise. It is also what let `mergeDataOntoRecord(…, data)` accept a nullable argument
2056
+ // without complaint, since `strictNullChecks` is off in this package.
2057
+ const environmentId = data['EnvironmentID'];
2058
+ const isArchived = data['IsArchived'] === true;
1943
2059
  const inLoadedEnvironment = !this._lastProjectsEnvironmentId ||
1944
2060
  (environmentId != null && UUIDsEqual(environmentId, this._lastProjectsEnvironmentId));
1945
2061
  if (existingIdx >= 0) {
@@ -1961,9 +2077,9 @@ export class ConversationEngine extends BaseEngine {
1961
2077
  * Handles save/delete events on AI Agent Run entities from local or remote code.
1962
2078
  * Updates the AgentRunsByDetailId map so timers and status reflect reality.
1963
2079
  */
1964
- handleAgentRunEntityEvent(event, action) {
1965
- const data = this.extractRecordData(event);
1966
- const id = data?.['ID'];
2080
+ handleAgentRunEntityEvent(event, action, data) {
2081
+ const id = this.eventRecordID(event, data);
2082
+ // Foreign key, not part of this row's primary key — supplied by the dispatcher's re-read.
1967
2083
  const detailId = data?.['ConversationDetailID'];
1968
2084
  if (!id || !detailId)
1969
2085
  return true;
@@ -2000,8 +2116,9 @@ export class ConversationEngine extends BaseEngine {
2000
2116
  * reconstructed from the entity event alone, so we flag the cache as stale.
2001
2117
  * The UI component checks PeripheralDataStale and force-refreshes when needed.
2002
2118
  */
2003
- handlePeripheralJunctionEntityEvent(event) {
2004
- const data = this.extractRecordData(event);
2119
+ handlePeripheralJunctionEntityEvent(event, data) {
2120
+ // The junction's own primary key is not useful here; what matters is which detail it hangs
2121
+ // off, which is a foreign key and therefore only available from the row.
2005
2122
  const detailId = data?.['ConversationDetailID'];
2006
2123
  if (!detailId)
2007
2124
  return true;