@pie-players/pie-players-shared 0.3.53 → 0.3.55

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.
@@ -16,6 +16,7 @@ export declare class ItemController {
16
16
  private session;
17
17
  private storageKey;
18
18
  private sessionId;
19
+ private itemId;
19
20
  constructor(options: ItemControllerOptions);
20
21
  hydrate(): Promise<ItemSessionContainer>;
21
22
  persist(): Promise<void>;
@@ -1,5 +1,5 @@
1
1
  import { MemoryItemSessionStorage, } from "./item-controller-storage.js";
2
- import { hasResponseField, hasResponseValue, mergeElementIntoSession, normalizeItemSessionContainer as normalizeSessionContainer, } from "./item-session-contract.js";
2
+ import { hasResponseField, hasResponseValue, mergeElementIntoSession, normalizeItemSessionContainer as normalizeSessionContainer, normalizeItemSessionChange, } from "./item-session-contract.js";
3
3
  const DEFAULT_SESSION_ID = "";
4
4
  function cloneSession(session) {
5
5
  try {
@@ -17,8 +17,10 @@ export class ItemController {
17
17
  session;
18
18
  storageKey;
19
19
  sessionId;
20
+ itemId;
20
21
  constructor(options) {
21
22
  this.storage = options.storage ?? new MemoryItemSessionStorage();
23
+ this.itemId = options.itemId;
22
24
  this.storageKey =
23
25
  options.storageKey ?? `pie:item-controller:v1:${options.itemId}`;
24
26
  this.sessionId = options.sessionId ?? DEFAULT_SESSION_ID;
@@ -75,8 +77,15 @@ export class ItemController {
75
77
  return this.getSession();
76
78
  }
77
79
  updateFromEventDetail(detail, options = {}) {
78
- const payload = detail?.session ?? detail;
79
- return this.setSession(payload, options);
80
+ const normalized = normalizeItemSessionChange({
81
+ itemId: this.session.id || this.itemId,
82
+ sessionDetail: detail,
83
+ previousItemSession: this.session,
84
+ });
85
+ if (normalized.intent === "metadata-only" || !normalized.session) {
86
+ return this.getSession();
87
+ }
88
+ return this.setSession(normalized.session, options);
80
89
  }
81
90
  }
82
91
  export const normalizeItemSessionContainer = normalizeSessionContainer;
@@ -1 +1 @@
1
- {"version":3,"file":"item-controller.js","sourceRoot":"","sources":["../../src/pie/item-controller.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\tMemoryItemSessionStorage,\n\ttype ItemSessionContainer,\n\ttype ItemSessionStorageStrategy,\n} from \"./item-controller-storage.js\";\nimport {\n\thasResponseField,\n\thasResponseValue,\n\tmergeElementIntoSession,\n\tnormalizeItemSessionContainer as normalizeSessionContainer,\n} from \"./item-session-contract.js\";\n\nexport type ItemControllerOptions = {\n\titemId: string;\n\tstorageKey?: string;\n\tsessionId?: string;\n\tinitialSession?: unknown;\n\tstorage?: ItemSessionStorageStrategy;\n};\n\ntype SetSessionOptions = {\n\tpersist?: boolean;\n\tallowMetadataOverwrite?: boolean;\n};\n\nconst DEFAULT_SESSION_ID = \"\";\n\nfunction cloneSession(session: ItemSessionContainer): ItemSessionContainer {\n\ttry {\n\t\treturn structuredClone(session);\n\t} catch {\n\t\treturn {\n\t\t\tid: session.id,\n\t\t\tdata: JSON.parse(JSON.stringify(session.data ?? [])),\n\t\t};\n\t}\n}\n\nexport class ItemController {\n\tprivate storage: ItemSessionStorageStrategy;\n\tprivate session: ItemSessionContainer;\n\tprivate storageKey: string;\n\tprivate sessionId: string;\n\n\tconstructor(options: ItemControllerOptions) {\n\t\tthis.storage = options.storage ?? new MemoryItemSessionStorage();\n\t\tthis.storageKey =\n\t\t\toptions.storageKey ?? `pie:item-controller:v1:${options.itemId}`;\n\t\tthis.sessionId = options.sessionId ?? DEFAULT_SESSION_ID;\n\t\tthis.session = normalizeSessionContainer(\n\t\t\toptions.initialSession,\n\t\t\tthis.sessionId,\n\t\t);\n\t}\n\n\tasync hydrate(): Promise<ItemSessionContainer> {\n\t\tconst loaded = await this.storage.load(this.storageKey);\n\t\tif (loaded) {\n\t\t\tthis.session = normalizeSessionContainer(loaded, this.sessionId);\n\t\t}\n\t\treturn this.getSession();\n\t}\n\n\tasync persist(): Promise<void> {\n\t\tawait this.storage.save(this.storageKey, this.session);\n\t}\n\n\tasync clearPersisted(): Promise<void> {\n\t\tawait this.storage.clear?.(this.storageKey);\n\t}\n\n\tgetSession(): ItemSessionContainer {\n\t\treturn cloneSession(this.session);\n\t}\n\n\tsetSession(\n\t\tinput: unknown,\n\t\toptions: SetSessionOptions = {},\n\t): ItemSessionContainer {\n\t\tconst next = normalizeSessionContainer(input, this.sessionId);\n\t\tconst allowMetadataOverwrite = options.allowMetadataOverwrite ?? false;\n\t\tif (\n\t\t\t!allowMetadataOverwrite &&\n\t\t\thasResponseValue(this.session) &&\n\t\t\t!hasResponseValue(next) &&\n\t\t\t!hasResponseField(next)\n\t\t) {\n\t\t\treturn this.getSession();\n\t\t}\n\t\tthis.session = next;\n\t\tif (options.persist !== false) {\n\t\t\tvoid this.persist();\n\t\t}\n\t\treturn this.getSession();\n\t}\n\n\t/**\n\t * Merge derived, non-response element state (e.g. a controller's persisted\n\t * shuffle order) into the authoritative session for a single element entry.\n\t *\n\t * Unlike {@link setSession}, this deliberately bypasses the response-protection\n\t * guard: the merged `properties` are element-derived metadata (no `value`), so\n\t * they must never be blocked by, nor mistaken for, a student response. The\n\t * shallow merge preserves any existing response on the entry.\n\t *\n\t * Defaults to `persist: false` — shuffle order rides along with the next genuine\n\t * response save rather than triggering its own persistence.\n\t */\n\tmergeElementSession(\n\t\telementId: string,\n\t\tproperties: Record<string, unknown>,\n\t\toptions: { persist?: boolean } = {},\n\t): ItemSessionContainer {\n\t\tthis.session = mergeElementIntoSession(\n\t\t\tthis.session.id || this.sessionId,\n\t\t\tthis.session,\n\t\t\telementId,\n\t\t\t{ id: elementId, ...properties },\n\t\t);\n\t\tif (options.persist === true) {\n\t\t\tvoid this.persist();\n\t\t}\n\t\treturn this.getSession();\n\t}\n\n\tupdateFromEventDetail(\n\t\tdetail: unknown,\n\t\toptions: SetSessionOptions = {},\n\t): ItemSessionContainer {\n\t\tconst payload = (detail as any)?.session ?? detail;\n\t\treturn this.setSession(payload, options);\n\t}\n}\n\nexport const normalizeItemSessionContainer = normalizeSessionContainer;\n"]}
1
+ {"version":3,"file":"item-controller.js","sourceRoot":"","sources":["../../src/pie/item-controller.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\tMemoryItemSessionStorage,\n\ttype ItemSessionContainer,\n\ttype ItemSessionStorageStrategy,\n} from \"./item-controller-storage.js\";\nimport {\n\thasResponseField,\n\thasResponseValue,\n\tmergeElementIntoSession,\n\tnormalizeItemSessionContainer as normalizeSessionContainer,\n\tnormalizeItemSessionChange,\n} from \"./item-session-contract.js\";\n\nexport type ItemControllerOptions = {\n\titemId: string;\n\tstorageKey?: string;\n\tsessionId?: string;\n\tinitialSession?: unknown;\n\tstorage?: ItemSessionStorageStrategy;\n};\n\ntype SetSessionOptions = {\n\tpersist?: boolean;\n\tallowMetadataOverwrite?: boolean;\n};\n\nconst DEFAULT_SESSION_ID = \"\";\n\nfunction cloneSession(session: ItemSessionContainer): ItemSessionContainer {\n\ttry {\n\t\treturn structuredClone(session);\n\t} catch {\n\t\treturn {\n\t\t\tid: session.id,\n\t\t\tdata: JSON.parse(JSON.stringify(session.data ?? [])),\n\t\t};\n\t}\n}\n\nexport class ItemController {\n\tprivate storage: ItemSessionStorageStrategy;\n\tprivate session: ItemSessionContainer;\n\tprivate storageKey: string;\n\tprivate sessionId: string;\n\tprivate itemId: string;\n\n\tconstructor(options: ItemControllerOptions) {\n\t\tthis.storage = options.storage ?? new MemoryItemSessionStorage();\n\t\tthis.itemId = options.itemId;\n\t\tthis.storageKey =\n\t\t\toptions.storageKey ?? `pie:item-controller:v1:${options.itemId}`;\n\t\tthis.sessionId = options.sessionId ?? DEFAULT_SESSION_ID;\n\t\tthis.session = normalizeSessionContainer(\n\t\t\toptions.initialSession,\n\t\t\tthis.sessionId,\n\t\t);\n\t}\n\n\tasync hydrate(): Promise<ItemSessionContainer> {\n\t\tconst loaded = await this.storage.load(this.storageKey);\n\t\tif (loaded) {\n\t\t\tthis.session = normalizeSessionContainer(loaded, this.sessionId);\n\t\t}\n\t\treturn this.getSession();\n\t}\n\n\tasync persist(): Promise<void> {\n\t\tawait this.storage.save(this.storageKey, this.session);\n\t}\n\n\tasync clearPersisted(): Promise<void> {\n\t\tawait this.storage.clear?.(this.storageKey);\n\t}\n\n\tgetSession(): ItemSessionContainer {\n\t\treturn cloneSession(this.session);\n\t}\n\n\tsetSession(\n\t\tinput: unknown,\n\t\toptions: SetSessionOptions = {},\n\t): ItemSessionContainer {\n\t\tconst next = normalizeSessionContainer(input, this.sessionId);\n\t\tconst allowMetadataOverwrite = options.allowMetadataOverwrite ?? false;\n\t\tif (\n\t\t\t!allowMetadataOverwrite &&\n\t\t\thasResponseValue(this.session) &&\n\t\t\t!hasResponseValue(next) &&\n\t\t\t!hasResponseField(next)\n\t\t) {\n\t\t\treturn this.getSession();\n\t\t}\n\t\tthis.session = next;\n\t\tif (options.persist !== false) {\n\t\t\tvoid this.persist();\n\t\t}\n\t\treturn this.getSession();\n\t}\n\n\t/**\n\t * Merge derived, non-response element state (e.g. a controller's persisted\n\t * shuffle order) into the authoritative session for a single element entry.\n\t *\n\t * Unlike {@link setSession}, this deliberately bypasses the response-protection\n\t * guard: the merged `properties` are element-derived metadata (no `value`), so\n\t * they must never be blocked by, nor mistaken for, a student response. The\n\t * shallow merge preserves any existing response on the entry.\n\t *\n\t * Defaults to `persist: false` — shuffle order rides along with the next genuine\n\t * response save rather than triggering its own persistence.\n\t */\n\tmergeElementSession(\n\t\telementId: string,\n\t\tproperties: Record<string, unknown>,\n\t\toptions: { persist?: boolean } = {},\n\t): ItemSessionContainer {\n\t\tthis.session = mergeElementIntoSession(\n\t\t\tthis.session.id || this.sessionId,\n\t\t\tthis.session,\n\t\t\telementId,\n\t\t\t{ id: elementId, ...properties },\n\t\t);\n\t\tif (options.persist === true) {\n\t\t\tvoid this.persist();\n\t\t}\n\t\treturn this.getSession();\n\t}\n\n\tupdateFromEventDetail(\n\t\tdetail: unknown,\n\t\toptions: SetSessionOptions = {},\n\t): ItemSessionContainer {\n\t\tconst normalized = normalizeItemSessionChange({\n\t\t\titemId: this.session.id || this.itemId,\n\t\t\tsessionDetail: detail,\n\t\t\tpreviousItemSession: this.session,\n\t\t});\n\t\tif (normalized.intent === \"metadata-only\" || !normalized.session) {\n\t\t\treturn this.getSession();\n\t\t}\n\t\treturn this.setSession(normalized.session, options);\n\t}\n}\n\nexport const normalizeItemSessionContainer = normalizeSessionContainer;\n"]}
@@ -24,6 +24,14 @@ function areSessionContainersEqual(left, right) {
24
24
  return false;
25
25
  }
26
26
  }
27
+ function alignPreviousSessionIdForRendererSnapshot(candidate, previous) {
28
+ if (!previous || candidate.id !== "")
29
+ return previous;
30
+ return {
31
+ ...previous,
32
+ id: "",
33
+ };
34
+ }
27
35
  export function normalizeItemSessionContainer(input, fallbackSessionId = DEFAULT_SESSION_ID) {
28
36
  if (input && typeof input === "object") {
29
37
  const candidate = input;
@@ -103,6 +111,13 @@ function isElementIdentityOnlyPayload(candidate) {
103
111
  !hasResponseField(candidate) &&
104
112
  candidateKeys.every((key) => ELEMENT_IDENTITY_SESSION_KEYS.has(key)));
105
113
  }
114
+ function isWrappedIdentityOnlyElementEcho(candidate) {
115
+ return (candidate.data.length > 0 &&
116
+ candidate.data.every((entry) => entry &&
117
+ typeof entry === "object" &&
118
+ !Array.isArray(entry) &&
119
+ isElementIdentityOnlyPayload(entry)));
120
+ }
106
121
  function mergeSessionEntry(existing, incoming) {
107
122
  const merged = { ...existing };
108
123
  for (const [key, value] of Object.entries(incoming)) {
@@ -165,6 +180,7 @@ export function normalizeItemSessionChange(args) {
165
180
  const previousNormalized = args.previousItemSession !== undefined
166
181
  ? normalizeItemSessionContainer(args.previousItemSession, safeItemId)
167
182
  : null;
183
+ const previousForMetadataComparison = alignPreviousSessionIdForRendererSnapshot(normalizedCandidate, previousNormalized);
168
184
  const sessionDetailKeys = Object.keys(sessionDetail);
169
185
  const metadataWithSessionOnly = sessionDetailKeys.length > 0 &&
170
186
  sessionDetailKeys.every((key) => key === "session" ||
@@ -172,9 +188,21 @@ export function normalizeItemSessionChange(args) {
172
188
  key === "component" ||
173
189
  key === "timestamp" ||
174
190
  key === "sourceRuntimeId");
191
+ const hasMetadataBeyondSession = sessionDetailKeys.some((key) => key !== "session");
192
+ if (metadataWithSessionOnly &&
193
+ hasMetadataBeyondSession &&
194
+ previousForMetadataComparison &&
195
+ areSessionContainersEqual(normalizedCandidate, previousForMetadataComparison)) {
196
+ return {
197
+ itemId: safeItemId,
198
+ session: null,
199
+ intent: "metadata-only",
200
+ component: getMetadataComponent(sessionDetail),
201
+ complete: getMetadataComplete(sessionDetail),
202
+ };
203
+ }
175
204
  if (metadataWithSessionOnly &&
176
- previousNormalized &&
177
- areSessionContainersEqual(normalizedCandidate, previousNormalized)) {
205
+ isWrappedIdentityOnlyElementEcho(normalizedCandidate)) {
178
206
  return {
179
207
  itemId: safeItemId,
180
208
  session: null,
@@ -1 +1 @@
1
- {"version":3,"file":"item-session-contract.js","sourceRoot":"","sources":["../../src/pie/item-session-contract.ts"],"names":[],"mappings":"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","sourcesContent":["export type ItemSessionContainer = {\n\tid: string;\n\tdata: unknown[];\n};\n\nexport type ItemSessionUpdateIntent =\n\t| \"replace-item-session\"\n\t| \"merge-element-session\"\n\t| \"metadata-only\";\n\nexport type NormalizedItemSessionChange = {\n\titemId: string;\n\tsession: ItemSessionContainer | null;\n\tintent: ItemSessionUpdateIntent;\n\tcomponent?: string;\n\tcomplete?: boolean;\n};\n\nconst DEFAULT_SESSION_ID = \"\";\nconst METADATA_ONLY_SESSION_KEYS = new Set([\n\t\"complete\",\n\t\"component\",\n\t\"timestamp\",\n\t\"sourceRuntimeId\",\n]);\nconst ELEMENT_IDENTITY_SESSION_KEYS = new Set([\n\t\"id\",\n\t\"element\",\n\t...METADATA_ONLY_SESSION_KEYS,\n]);\n\nfunction areSessionContainersEqual(\n\tleft: ItemSessionContainer | null,\n\tright: ItemSessionContainer | null,\n): boolean {\n\tif (!left && !right) return true;\n\tif (!left || !right) return false;\n\tif ((left.id || \"\") !== (right.id || \"\")) return false;\n\ttry {\n\t\treturn JSON.stringify(left.data || []) === JSON.stringify(right.data || []);\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nexport function normalizeItemSessionContainer(\n\tinput: unknown,\n\tfallbackSessionId: string = DEFAULT_SESSION_ID,\n): ItemSessionContainer {\n\tif (input && typeof input === \"object\") {\n\t\tconst candidate = input as Record<string, unknown>;\n\t\tif (Array.isArray(candidate.data)) {\n\t\t\treturn {\n\t\t\t\tid: typeof candidate.id === \"string\" ? candidate.id : fallbackSessionId,\n\t\t\t\tdata: candidate.data,\n\t\t\t};\n\t\t}\n\t\tif (Array.isArray(input)) {\n\t\t\treturn { id: fallbackSessionId, data: input };\n\t\t}\n\t\treturn { id: fallbackSessionId, data: [input] };\n\t}\n\tif (Array.isArray(input)) {\n\t\treturn { id: fallbackSessionId, data: input };\n\t}\n\treturn { id: fallbackSessionId, data: [] };\n}\n\nexport function hasResponseValue(value: unknown): boolean {\n\tif (value == null) return false;\n\tif (Array.isArray(value))\n\t\treturn value.some((entry) => hasResponseValue(entry));\n\tif (typeof value !== \"object\") return false;\n\tfor (const [key, nested] of Object.entries(\n\t\tvalue as Record<string, unknown>,\n\t)) {\n\t\tif (\n\t\t\tkey === \"value\" &&\n\t\t\tnested !== undefined &&\n\t\t\tnested !== null &&\n\t\t\t!(typeof nested === \"string\" && nested.trim() === \"\") &&\n\t\t\t!(Array.isArray(nested) && nested.length === 0)\n\t\t) {\n\t\t\treturn true;\n\t\t}\n\t\tif (hasResponseValue(nested)) return true;\n\t}\n\treturn false;\n}\n\nexport function hasResponseField(value: unknown): boolean {\n\tif (value == null) return false;\n\tif (Array.isArray(value))\n\t\treturn value.some((entry) => hasResponseField(entry));\n\tif (typeof value !== \"object\") return false;\n\tfor (const [key, nested] of Object.entries(\n\t\tvalue as Record<string, unknown>,\n\t)) {\n\t\tif (key === \"value\") {\n\t\t\treturn true;\n\t\t}\n\t\tif (hasResponseField(nested)) return true;\n\t}\n\treturn false;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n\treturn Boolean(value) && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction getMetadataComponent(\n\tsessionDetail: Record<string, unknown>,\n\tcandidate?: Record<string, unknown>,\n): string | undefined {\n\tif (typeof sessionDetail.component === \"string\") return sessionDetail.component;\n\tif (typeof candidate?.component === \"string\") return candidate.component;\n\treturn undefined;\n}\n\nfunction getMetadataComplete(\n\tsessionDetail: Record<string, unknown>,\n\tcandidate?: Record<string, unknown>,\n): boolean | undefined {\n\tif (typeof sessionDetail.complete === \"boolean\") return sessionDetail.complete;\n\tif (typeof candidate?.complete === \"boolean\") return candidate.complete;\n\treturn undefined;\n}\n\nfunction isElementIdentityOnlyPayload(\n\tcandidate: Record<string, unknown>,\n): boolean {\n\tconst candidateKeys = Object.keys(candidate);\n\tconst hasIdentityKey = \"id\" in candidate || \"element\" in candidate;\n\treturn (\n\t\thasIdentityKey &&\n\t\t!hasResponseField(candidate) &&\n\t\tcandidateKeys.every((key) => ELEMENT_IDENTITY_SESSION_KEYS.has(key))\n\t);\n}\n\nfunction mergeSessionEntry(\n\texisting: Record<string, unknown>,\n\tincoming: Record<string, unknown>,\n): Record<string, unknown> {\n\tconst merged = { ...existing };\n\tfor (const [key, value] of Object.entries(incoming)) {\n\t\tconst previous = merged[key];\n\t\tmerged[key] =\n\t\t\tisPlainObject(previous) && isPlainObject(value)\n\t\t\t\t? mergeSessionEntry(previous, value)\n\t\t\t\t: value;\n\t}\n\treturn merged;\n}\n\nexport function mergeElementIntoSession(\n\titemId: string,\n\tpreviousItemSession: unknown,\n\tentryId: string,\n\tentry: Record<string, unknown>,\n): ItemSessionContainer {\n\tconst previous = normalizeItemSessionContainer(previousItemSession, itemId);\n\tconst nextData = [...previous.data];\n\tconst existingIndex = nextData.findIndex((candidate) => {\n\t\tif (!candidate || typeof candidate !== \"object\") return false;\n\t\treturn (candidate as Record<string, unknown>).id === entryId;\n\t});\n\tif (existingIndex >= 0) {\n\t\tconst existing = nextData[existingIndex];\n\t\tnextData[existingIndex] =\n\t\t\texisting && typeof existing === \"object\"\n\t\t\t\t? mergeSessionEntry(existing as Record<string, unknown>, entry)\n\t\t\t\t: entry;\n\t} else {\n\t\tnextData.push(entry);\n\t}\n\treturn {\n\t\tid: previous.id || itemId,\n\t\tdata: nextData,\n\t};\n}\n\nexport function normalizeItemSessionChange(args: {\n\titemId: string;\n\tsessionDetail: unknown;\n\tpreviousItemSession?: unknown;\n}): NormalizedItemSessionChange {\n\tconst sessionDetail = (args.sessionDetail || {}) as Record<string, unknown>;\n\tconst actualSession =\n\t\tsessionDetail &&\n\t\ttypeof sessionDetail === \"object\" &&\n\t\t\"session\" in sessionDetail\n\t\t\t? sessionDetail.session\n\t\t\t: args.sessionDetail;\n\n\tconst safeItemId =\n\t\ttypeof args.itemId === \"string\" && args.itemId\n\t\t\t? args.itemId\n\t\t\t: typeof (actualSession as Record<string, unknown> | null)?.id ===\n\t\t\t\t\t\"string\"\n\t\t\t\t? String((actualSession as Record<string, unknown>).id)\n\t\t\t\t: \"\";\n\n\tif (!actualSession || typeof actualSession !== \"object\") {\n\t\treturn {\n\t\t\titemId: safeItemId,\n\t\t\tsession: null,\n\t\t\tintent: \"metadata-only\",\n\t\t\tcomponent: getMetadataComponent(sessionDetail),\n\t\t\tcomplete: getMetadataComplete(sessionDetail),\n\t\t};\n\t}\n\n\tconst candidate = actualSession as Record<string, unknown>;\n\tif (Array.isArray(candidate.data)) {\n\t\tconst normalizedCandidate = normalizeItemSessionContainer(\n\t\t\tcandidate,\n\t\t\tsafeItemId,\n\t\t);\n\t\tconst previousNormalized =\n\t\t\targs.previousItemSession !== undefined\n\t\t\t\t? normalizeItemSessionContainer(args.previousItemSession, safeItemId)\n\t\t\t\t: null;\n\t\tconst sessionDetailKeys = Object.keys(sessionDetail);\n\t\tconst metadataWithSessionOnly =\n\t\t\tsessionDetailKeys.length > 0 &&\n\t\t\tsessionDetailKeys.every(\n\t\t\t\t(key) =>\n\t\t\t\t\tkey === \"session\" ||\n\t\t\t\t\tkey === \"complete\" ||\n\t\t\t\t\tkey === \"component\" ||\n\t\t\t\t\tkey === \"timestamp\" ||\n\t\t\t\t\tkey === \"sourceRuntimeId\",\n\t\t\t);\n\t\tif (\n\t\t\tmetadataWithSessionOnly &&\n\t\t\tpreviousNormalized &&\n\t\t\tareSessionContainersEqual(normalizedCandidate, previousNormalized)\n\t\t) {\n\t\t\treturn {\n\t\t\t\titemId: safeItemId,\n\t\t\t\tsession: null,\n\t\t\t\tintent: \"metadata-only\",\n\t\t\t\tcomponent: getMetadataComponent(sessionDetail),\n\t\t\t\tcomplete: getMetadataComplete(sessionDetail),\n\t\t\t};\n\t\t}\n\t\treturn {\n\t\t\titemId: safeItemId,\n\t\t\tsession: normalizedCandidate,\n\t\t\tintent: \"replace-item-session\",\n\t\t\tcomponent: getMetadataComponent(sessionDetail),\n\t\t\tcomplete: getMetadataComplete(sessionDetail),\n\t\t};\n\t}\n\n\tconst candidateKeys = Object.keys(candidate);\n\tconst isMetadataOnlyPayload =\n\t\tcandidateKeys.length > 0 &&\n\t\tcandidateKeys.every(\n\t\t\t(key) =>\n\t\t\t\tkey === \"complete\" ||\n\t\t\t\tkey === \"component\" ||\n\t\t\t\tkey === \"timestamp\" ||\n\t\t\t\tkey === \"sourceRuntimeId\",\n\t\t);\n\tif (isMetadataOnlyPayload) {\n\t\treturn {\n\t\t\titemId: safeItemId,\n\t\t\tsession: null,\n\t\t\tintent: \"metadata-only\",\n\t\t\tcomponent: getMetadataComponent(sessionDetail, candidate),\n\t\t\tcomplete: getMetadataComplete(sessionDetail, candidate),\n\t\t};\n\t}\n\tif (isElementIdentityOnlyPayload(candidate)) {\n\t\treturn {\n\t\t\titemId: safeItemId,\n\t\t\tsession: null,\n\t\t\tintent: \"metadata-only\",\n\t\t\tcomponent: getMetadataComponent(sessionDetail, candidate),\n\t\t\tcomplete: getMetadataComplete(sessionDetail, candidate),\n\t\t};\n\t}\n\n\tconst component = getMetadataComponent(sessionDetail, candidate) ?? \"response\";\n\tconst elementEntryId =\n\t\ttypeof candidate.id === \"string\" && candidate.id ? candidate.id : component;\n\tconst merged = mergeElementIntoSession(\n\t\tsafeItemId,\n\t\targs.previousItemSession,\n\t\telementEntryId,\n\t\t{ id: elementEntryId, ...candidate },\n\t);\n\treturn {\n\t\titemId: safeItemId,\n\t\tsession: merged,\n\t\tintent: \"merge-element-session\",\n\t\tcomponent,\n\t\tcomplete: getMetadataComplete(sessionDetail, candidate),\n\t};\n}\n"]}
1
+ {"version":3,"file":"item-session-contract.js","sourceRoot":"","sources":["../../src/pie/item-session-contract.ts"],"names":[],"mappings":"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","sourcesContent":["export type ItemSessionContainer = {\n\tid: string;\n\tdata: unknown[];\n};\n\nexport type ItemSessionUpdateIntent =\n\t| \"replace-item-session\"\n\t| \"merge-element-session\"\n\t| \"metadata-only\";\n\nexport type NormalizedItemSessionChange = {\n\titemId: string;\n\tsession: ItemSessionContainer | null;\n\tintent: ItemSessionUpdateIntent;\n\tcomponent?: string;\n\tcomplete?: boolean;\n};\n\nconst DEFAULT_SESSION_ID = \"\";\nconst METADATA_ONLY_SESSION_KEYS = new Set([\n\t\"complete\",\n\t\"component\",\n\t\"timestamp\",\n\t\"sourceRuntimeId\",\n]);\nconst ELEMENT_IDENTITY_SESSION_KEYS = new Set([\n\t\"id\",\n\t\"element\",\n\t...METADATA_ONLY_SESSION_KEYS,\n]);\n\nfunction areSessionContainersEqual(\n\tleft: ItemSessionContainer | null,\n\tright: ItemSessionContainer | null,\n): boolean {\n\tif (!left && !right) return true;\n\tif (!left || !right) return false;\n\tif ((left.id || \"\") !== (right.id || \"\")) return false;\n\ttry {\n\t\treturn JSON.stringify(left.data || []) === JSON.stringify(right.data || []);\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction alignPreviousSessionIdForRendererSnapshot(\n\tcandidate: ItemSessionContainer,\n\tprevious: ItemSessionContainer | null,\n): ItemSessionContainer | null {\n\tif (!previous || candidate.id !== \"\") return previous;\n\treturn {\n\t\t...previous,\n\t\tid: \"\",\n\t};\n}\n\nexport function normalizeItemSessionContainer(\n\tinput: unknown,\n\tfallbackSessionId: string = DEFAULT_SESSION_ID,\n): ItemSessionContainer {\n\tif (input && typeof input === \"object\") {\n\t\tconst candidate = input as Record<string, unknown>;\n\t\tif (Array.isArray(candidate.data)) {\n\t\t\treturn {\n\t\t\t\tid: typeof candidate.id === \"string\" ? candidate.id : fallbackSessionId,\n\t\t\t\tdata: candidate.data,\n\t\t\t};\n\t\t}\n\t\tif (Array.isArray(input)) {\n\t\t\treturn { id: fallbackSessionId, data: input };\n\t\t}\n\t\treturn { id: fallbackSessionId, data: [input] };\n\t}\n\tif (Array.isArray(input)) {\n\t\treturn { id: fallbackSessionId, data: input };\n\t}\n\treturn { id: fallbackSessionId, data: [] };\n}\n\nexport function hasResponseValue(value: unknown): boolean {\n\tif (value == null) return false;\n\tif (Array.isArray(value))\n\t\treturn value.some((entry) => hasResponseValue(entry));\n\tif (typeof value !== \"object\") return false;\n\tfor (const [key, nested] of Object.entries(\n\t\tvalue as Record<string, unknown>,\n\t)) {\n\t\tif (\n\t\t\tkey === \"value\" &&\n\t\t\tnested !== undefined &&\n\t\t\tnested !== null &&\n\t\t\t!(typeof nested === \"string\" && nested.trim() === \"\") &&\n\t\t\t!(Array.isArray(nested) && nested.length === 0)\n\t\t) {\n\t\t\treturn true;\n\t\t}\n\t\tif (hasResponseValue(nested)) return true;\n\t}\n\treturn false;\n}\n\nexport function hasResponseField(value: unknown): boolean {\n\tif (value == null) return false;\n\tif (Array.isArray(value))\n\t\treturn value.some((entry) => hasResponseField(entry));\n\tif (typeof value !== \"object\") return false;\n\tfor (const [key, nested] of Object.entries(\n\t\tvalue as Record<string, unknown>,\n\t)) {\n\t\tif (key === \"value\") {\n\t\t\treturn true;\n\t\t}\n\t\tif (hasResponseField(nested)) return true;\n\t}\n\treturn false;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n\treturn Boolean(value) && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction getMetadataComponent(\n\tsessionDetail: Record<string, unknown>,\n\tcandidate?: Record<string, unknown>,\n): string | undefined {\n\tif (typeof sessionDetail.component === \"string\")\n\t\treturn sessionDetail.component;\n\tif (typeof candidate?.component === \"string\") return candidate.component;\n\treturn undefined;\n}\n\nfunction getMetadataComplete(\n\tsessionDetail: Record<string, unknown>,\n\tcandidate?: Record<string, unknown>,\n): boolean | undefined {\n\tif (typeof sessionDetail.complete === \"boolean\")\n\t\treturn sessionDetail.complete;\n\tif (typeof candidate?.complete === \"boolean\") return candidate.complete;\n\treturn undefined;\n}\n\nfunction isElementIdentityOnlyPayload(\n\tcandidate: Record<string, unknown>,\n): boolean {\n\tconst candidateKeys = Object.keys(candidate);\n\tconst hasIdentityKey = \"id\" in candidate || \"element\" in candidate;\n\treturn (\n\t\thasIdentityKey &&\n\t\t!hasResponseField(candidate) &&\n\t\tcandidateKeys.every((key) => ELEMENT_IDENTITY_SESSION_KEYS.has(key))\n\t);\n}\n\nfunction isWrappedIdentityOnlyElementEcho(\n\tcandidate: ItemSessionContainer,\n): boolean {\n\treturn (\n\t\tcandidate.data.length > 0 &&\n\t\tcandidate.data.every(\n\t\t\t(entry) =>\n\t\t\t\tentry &&\n\t\t\t\ttypeof entry === \"object\" &&\n\t\t\t\t!Array.isArray(entry) &&\n\t\t\t\tisElementIdentityOnlyPayload(entry as Record<string, unknown>),\n\t\t)\n\t);\n}\n\nfunction mergeSessionEntry(\n\texisting: Record<string, unknown>,\n\tincoming: Record<string, unknown>,\n): Record<string, unknown> {\n\tconst merged = { ...existing };\n\tfor (const [key, value] of Object.entries(incoming)) {\n\t\tconst previous = merged[key];\n\t\tmerged[key] =\n\t\t\tisPlainObject(previous) && isPlainObject(value)\n\t\t\t\t? mergeSessionEntry(previous, value)\n\t\t\t\t: value;\n\t}\n\treturn merged;\n}\n\nexport function mergeElementIntoSession(\n\titemId: string,\n\tpreviousItemSession: unknown,\n\tentryId: string,\n\tentry: Record<string, unknown>,\n): ItemSessionContainer {\n\tconst previous = normalizeItemSessionContainer(previousItemSession, itemId);\n\tconst nextData = [...previous.data];\n\tconst existingIndex = nextData.findIndex((candidate) => {\n\t\tif (!candidate || typeof candidate !== \"object\") return false;\n\t\treturn (candidate as Record<string, unknown>).id === entryId;\n\t});\n\tif (existingIndex >= 0) {\n\t\tconst existing = nextData[existingIndex];\n\t\tnextData[existingIndex] =\n\t\t\texisting && typeof existing === \"object\"\n\t\t\t\t? mergeSessionEntry(existing as Record<string, unknown>, entry)\n\t\t\t\t: entry;\n\t} else {\n\t\tnextData.push(entry);\n\t}\n\treturn {\n\t\tid: previous.id || itemId,\n\t\tdata: nextData,\n\t};\n}\n\nexport function normalizeItemSessionChange(args: {\n\titemId: string;\n\tsessionDetail: unknown;\n\tpreviousItemSession?: unknown;\n}): NormalizedItemSessionChange {\n\tconst sessionDetail = (args.sessionDetail || {}) as Record<string, unknown>;\n\tconst actualSession =\n\t\tsessionDetail &&\n\t\ttypeof sessionDetail === \"object\" &&\n\t\t\"session\" in sessionDetail\n\t\t\t? sessionDetail.session\n\t\t\t: args.sessionDetail;\n\n\tconst safeItemId =\n\t\ttypeof args.itemId === \"string\" && args.itemId\n\t\t\t? args.itemId\n\t\t\t: typeof (actualSession as Record<string, unknown> | null)?.id ===\n\t\t\t\t\t\"string\"\n\t\t\t\t? String((actualSession as Record<string, unknown>).id)\n\t\t\t\t: \"\";\n\n\tif (!actualSession || typeof actualSession !== \"object\") {\n\t\treturn {\n\t\t\titemId: safeItemId,\n\t\t\tsession: null,\n\t\t\tintent: \"metadata-only\",\n\t\t\tcomponent: getMetadataComponent(sessionDetail),\n\t\t\tcomplete: getMetadataComplete(sessionDetail),\n\t\t};\n\t}\n\n\tconst candidate = actualSession as Record<string, unknown>;\n\tif (Array.isArray(candidate.data)) {\n\t\tconst normalizedCandidate = normalizeItemSessionContainer(\n\t\t\tcandidate,\n\t\t\tsafeItemId,\n\t\t);\n\t\tconst previousNormalized =\n\t\t\targs.previousItemSession !== undefined\n\t\t\t\t? normalizeItemSessionContainer(args.previousItemSession, safeItemId)\n\t\t\t\t: null;\n\t\tconst previousForMetadataComparison =\n\t\t\talignPreviousSessionIdForRendererSnapshot(\n\t\t\t\tnormalizedCandidate,\n\t\t\t\tpreviousNormalized,\n\t\t\t);\n\t\tconst sessionDetailKeys = Object.keys(sessionDetail);\n\t\tconst metadataWithSessionOnly =\n\t\t\tsessionDetailKeys.length > 0 &&\n\t\t\tsessionDetailKeys.every(\n\t\t\t\t(key) =>\n\t\t\t\t\tkey === \"session\" ||\n\t\t\t\t\tkey === \"complete\" ||\n\t\t\t\t\tkey === \"component\" ||\n\t\t\t\t\tkey === \"timestamp\" ||\n\t\t\t\t\tkey === \"sourceRuntimeId\",\n\t\t\t);\n\t\tconst hasMetadataBeyondSession = sessionDetailKeys.some(\n\t\t\t(key) => key !== \"session\",\n\t\t);\n\t\tif (\n\t\t\tmetadataWithSessionOnly &&\n\t\t\thasMetadataBeyondSession &&\n\t\t\tpreviousForMetadataComparison &&\n\t\t\tareSessionContainersEqual(\n\t\t\t\tnormalizedCandidate,\n\t\t\t\tpreviousForMetadataComparison,\n\t\t\t)\n\t\t) {\n\t\t\treturn {\n\t\t\t\titemId: safeItemId,\n\t\t\t\tsession: null,\n\t\t\t\tintent: \"metadata-only\",\n\t\t\t\tcomponent: getMetadataComponent(sessionDetail),\n\t\t\t\tcomplete: getMetadataComplete(sessionDetail),\n\t\t\t};\n\t\t}\n\t\tif (\n\t\t\tmetadataWithSessionOnly &&\n\t\t\tisWrappedIdentityOnlyElementEcho(normalizedCandidate)\n\t\t) {\n\t\t\treturn {\n\t\t\t\titemId: safeItemId,\n\t\t\t\tsession: null,\n\t\t\t\tintent: \"metadata-only\",\n\t\t\t\tcomponent: getMetadataComponent(sessionDetail),\n\t\t\t\tcomplete: getMetadataComplete(sessionDetail),\n\t\t\t};\n\t\t}\n\t\treturn {\n\t\t\titemId: safeItemId,\n\t\t\tsession: normalizedCandidate,\n\t\t\tintent: \"replace-item-session\",\n\t\t\tcomponent: getMetadataComponent(sessionDetail),\n\t\t\tcomplete: getMetadataComplete(sessionDetail),\n\t\t};\n\t}\n\n\tconst candidateKeys = Object.keys(candidate);\n\tconst isMetadataOnlyPayload =\n\t\tcandidateKeys.length > 0 &&\n\t\tcandidateKeys.every(\n\t\t\t(key) =>\n\t\t\t\tkey === \"complete\" ||\n\t\t\t\tkey === \"component\" ||\n\t\t\t\tkey === \"timestamp\" ||\n\t\t\t\tkey === \"sourceRuntimeId\",\n\t\t);\n\tif (isMetadataOnlyPayload) {\n\t\treturn {\n\t\t\titemId: safeItemId,\n\t\t\tsession: null,\n\t\t\tintent: \"metadata-only\",\n\t\t\tcomponent: getMetadataComponent(sessionDetail, candidate),\n\t\t\tcomplete: getMetadataComplete(sessionDetail, candidate),\n\t\t};\n\t}\n\tif (isElementIdentityOnlyPayload(candidate)) {\n\t\treturn {\n\t\t\titemId: safeItemId,\n\t\t\tsession: null,\n\t\t\tintent: \"metadata-only\",\n\t\t\tcomponent: getMetadataComponent(sessionDetail, candidate),\n\t\t\tcomplete: getMetadataComplete(sessionDetail, candidate),\n\t\t};\n\t}\n\n\tconst component =\n\t\tgetMetadataComponent(sessionDetail, candidate) ?? \"response\";\n\tconst elementEntryId =\n\t\ttypeof candidate.id === \"string\" && candidate.id ? candidate.id : component;\n\tconst merged = mergeElementIntoSession(\n\t\tsafeItemId,\n\t\targs.previousItemSession,\n\t\telementEntryId,\n\t\t{ id: elementEntryId, ...candidate },\n\t);\n\treturn {\n\t\titemId: safeItemId,\n\t\tsession: merged,\n\t\tintent: \"merge-element-session\",\n\t\tcomponent,\n\t\tcomplete: getMetadataComplete(sessionDetail, candidate),\n\t};\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pie-players/pie-players-shared",
3
- "version": "0.3.53",
3
+ "version": "0.3.55",
4
4
  "type": "module",
5
5
  "description": "Shared runtime + UI utilities for PIE players",
6
6
  "license": "MIT",