@dreamboard-games/sdk 0.4.0-alpha.6 → 0.4.0-alpha.7

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.
Files changed (46) hide show
  1. package/REFERENCE.md +408 -8
  2. package/dist/authoring/index.d.ts +5 -4
  3. package/dist/authoring/index.js +3 -3
  4. package/dist/authoring/index.js.map +1 -1
  5. package/dist/{chunk-PAPIAMYX.js → chunk-3QIZKXJ2.js} +6 -4
  6. package/dist/{chunk-PAPIAMYX.js.map → chunk-3QIZKXJ2.js.map} +1 -1
  7. package/dist/chunk-5IHU5CUK.js +575 -0
  8. package/dist/chunk-5IHU5CUK.js.map +1 -0
  9. package/dist/chunk-IU6KGPY7.js +659 -0
  10. package/dist/chunk-IU6KGPY7.js.map +1 -0
  11. package/dist/{chunk-HK3WN4U7.js → chunk-KYEWGZ2Y.js} +2 -2
  12. package/dist/{chunk-HK3WN4U7.js.map → chunk-KYEWGZ2Y.js.map} +1 -1
  13. package/dist/{chunk-DDST4U2P.js → chunk-NO537WSS.js} +2 -2
  14. package/dist/{chunk-SOBNI363.js → chunk-Q322XCY2.js} +11 -6
  15. package/dist/{chunk-SOBNI363.js.map → chunk-Q322XCY2.js.map} +1 -1
  16. package/dist/{chunk-OV6JC2BM.js → chunk-RKGJ64UN.js} +19 -15
  17. package/dist/chunk-RKGJ64UN.js.map +1 -0
  18. package/dist/chunk-VLG4YST5.js +564 -0
  19. package/dist/chunk-VLG4YST5.js.map +1 -0
  20. package/dist/{stale-contract-artifact-error-BelRiIDR.d.ts → diagnostics-1BWjRo6-.d.ts} +1 -18
  21. package/dist/index.js +2 -2
  22. package/dist/package-set.d.ts +2 -2
  23. package/dist/package-set.js +2 -2
  24. package/dist/reducer.d.ts +3 -2
  25. package/dist/reducer.js +5 -5
  26. package/dist/reducer.js.map +1 -1
  27. package/dist/reference-games/index.d.ts +223 -0
  28. package/dist/reference-games/index.js +230 -0
  29. package/dist/reference-games/index.js.map +1 -0
  30. package/dist/runtime/primitives.js +4 -3
  31. package/dist/runtime/workspace-contract.js +5 -4
  32. package/dist/runtime.js +15 -205
  33. package/dist/runtime.js.map +1 -1
  34. package/dist/stale-contract-artifact-error-C5AaZPJ8.d.ts +18 -0
  35. package/dist/testing-runtime.d.ts +42 -0
  36. package/dist/testing-runtime.js +153 -0
  37. package/dist/testing-runtime.js.map +1 -0
  38. package/dist/testing.d.ts +5 -191
  39. package/dist/testing.js +22 -640
  40. package/dist/testing.js.map +1 -1
  41. package/dist/types-DcADVHe9.d.ts +192 -0
  42. package/package.json +13 -2
  43. package/dist/chunk-OV6JC2BM.js.map +0 -1
  44. package/dist/chunk-UUQNOGZV.js +0 -1266
  45. package/dist/chunk-UUQNOGZV.js.map +0 -1
  46. /package/dist/{chunk-DDST4U2P.js.map → chunk-NO537WSS.js.map} +0 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/runtime/core/create-plugin-runtime-client.ts","../src/runtime/components/PluginRuntimeBoundary.tsx"],"sourcesContent":["import {\n HostToPluginEnvelopeSchema,\n RuntimeJsonSchema,\n type HostToPluginEnvelope,\n type PluginGameplayFrame,\n type PluginInteractionBasis,\n type PluginSessionDescriptor,\n type SubmissionResult,\n type ValidationResult,\n} from \"@dreamboard-games/plugin-runtime-contract\";\nimport type {\n PluginRuntimeClient,\n PluginRuntimeClientOptions,\n RuntimeClock,\n RuntimeIdFactory,\n} from \"./types.js\";\n\nconst defaultClock: RuntimeClock = {\n now: () => Date.now(),\n};\n\nconst defaultIdFactory: RuntimeIdFactory = {\n nextId: (prefix) => {\n const cryptoLike = (\n globalThis as typeof globalThis & {\n crypto?: { randomUUID?: () => string };\n }\n ).crypto;\n if (cryptoLike?.randomUUID) {\n try {\n return `${prefix}-${cryptoLike.randomUUID()}`;\n } catch {\n // Fall through to deterministic-enough fallback for older test DOMs.\n }\n }\n return `${prefix}-${Date.now().toString(16)}-${Math.random()\n .toString(16)\n .slice(2)}`;\n },\n};\n\nfunction createSubmissionError(\n result: Extract<SubmissionResult, { accepted: false }>,\n): Error & { errorCode?: string } {\n const error = new Error(result.message ?? \"Submission failed\") as Error & {\n errorCode?: string;\n };\n error.name = \"SubmissionError\";\n error.errorCode = result.errorCode;\n return error;\n}\n\nfunction disconnectedValidationResult(): ValidationResult {\n return {\n valid: false,\n errorCode: \"runtime-disconnected\",\n message: \"Plugin runtime disconnected\",\n };\n}\n\nexport function createPluginRuntimeClient(\n options: PluginRuntimeClientOptions,\n): PluginRuntimeClient {\n const clock = options.clock ?? defaultClock;\n const idFactory = options.idFactory ?? defaultIdFactory;\n const requestTimeoutMs = options.requestTimeoutMs ?? 10_000;\n let session: PluginSessionDescriptor | null = null;\n let frame: PluginGameplayFrame | null = null;\n let disconnected = false;\n let stopTransport: (() => void) | undefined;\n const sessionListeners = new Set<() => void>();\n const frameListeners = new Set<() => void>();\n const pendingValidations = new Map<\n string,\n (result: ValidationResult) => void\n >();\n const pendingSubmissions = new Map<\n string,\n {\n resolve: () => void;\n reject: (error: Error & { errorCode?: string }) => void;\n }\n >();\n\n const notifySession = () => {\n for (const listener of sessionListeners) {\n listener();\n }\n };\n\n const notifyFrame = () => {\n for (const listener of frameListeners) {\n listener();\n }\n };\n\n const currentBasis = (): PluginInteractionBasis => {\n if (!frame) {\n throw new Error(\"Plugin runtime has not received a gameplay frame.\");\n }\n return {\n gameVersion: frame.gameVersion,\n actionSetVersion: frame.actionSetVersion,\n perspectivePlayerId: frame.perspectivePlayerId,\n };\n };\n\n const sendRuntimeError = (message: string, code?: string) => {\n if (disconnected) {\n return;\n }\n options.transport.send({\n type: \"runtime.error\",\n message,\n ...(code ? { code } : {}),\n });\n };\n\n const handleHostMessage = (rawMessage: HostToPluginEnvelope) => {\n if (disconnected) {\n return;\n }\n const parseResult = HostToPluginEnvelopeSchema.safeParse(rawMessage);\n if (!parseResult.success) {\n sendRuntimeError(\n \"Invalid host-to-plugin protocol envelope.\",\n \"invalid-envelope\",\n );\n return;\n }\n const message = parseResult.data;\n const clientReceivedAtMs = clock.now();\n\n switch (message.payload.type) {\n case \"runtime.init\": {\n session = message.payload.session;\n notifySession();\n options.transport.send({ type: \"runtime.ready\" });\n options.transport.send({\n type: \"runtime.ack\",\n sequence: message.sequence,\n clientReceivedAtMs,\n });\n break;\n }\n case \"gameplay.frame\": {\n frame = message.payload.frame;\n notifyFrame();\n const sendRenderedAck = () => {\n if (disconnected) {\n return;\n }\n options.transport.send({\n type: \"runtime.ack\",\n sequence: message.sequence,\n clientReceivedAtMs,\n clientRenderedAtMs: clock.now(),\n });\n };\n if (typeof requestAnimationFrame === \"function\") {\n requestAnimationFrame(sendRenderedAck);\n } else {\n queueMicrotask(sendRenderedAck);\n }\n break;\n }\n case \"interaction.validation-result\": {\n const resolve = pendingValidations.get(message.payload.requestId);\n if (resolve) {\n pendingValidations.delete(message.payload.requestId);\n resolve(message.payload.result);\n }\n break;\n }\n case \"interaction.submit-result\": {\n const pending = pendingSubmissions.get(message.payload.requestId);\n if (!pending) {\n break;\n }\n pendingSubmissions.delete(message.payload.requestId);\n if (message.payload.result.accepted) {\n pending.resolve();\n } else {\n pending.reject(createSubmissionError(message.payload.result));\n }\n break;\n }\n default: {\n const _exhaustive: never = message.payload;\n return _exhaustive;\n }\n }\n };\n\n stopTransport = options.transport.start(handleHostMessage);\n\n const rejectPending = () => {\n for (const resolve of pendingValidations.values()) {\n resolve(disconnectedValidationResult());\n }\n pendingValidations.clear();\n for (const pending of pendingSubmissions.values()) {\n const error = new Error(\"Plugin runtime disconnected\") as Error & {\n errorCode?: string;\n };\n error.name = \"SubmissionError\";\n error.errorCode = \"runtime-disconnected\";\n pending.reject(error);\n }\n pendingSubmissions.clear();\n };\n\n return {\n getSession: () => session,\n subscribeSession: (listener) => {\n sessionListeners.add(listener);\n return () => {\n sessionListeners.delete(listener);\n };\n },\n getFrame: () => frame,\n subscribeFrame: (listener) => {\n frameListeners.add(listener);\n return () => {\n frameListeners.delete(listener);\n };\n },\n validateInteraction: async (interactionId, params) =>\n new Promise((resolve) => {\n if (disconnected) {\n resolve(disconnectedValidationResult());\n return;\n }\n let basis: PluginInteractionBasis;\n try {\n basis = currentBasis();\n } catch (error) {\n resolve({\n valid: false,\n errorCode: \"runtime-not-ready\",\n message:\n error instanceof Error\n ? error.message\n : \"Plugin runtime is not ready.\",\n });\n return;\n }\n const parsedParams = RuntimeJsonSchema.safeParse(params);\n if (!parsedParams.success) {\n resolve({\n valid: false,\n errorCode: \"invalid-runtime-json\",\n message: \"Interaction params must be runtime JSON.\",\n });\n return;\n }\n const requestId = idFactory.nextId(\"validate\");\n pendingValidations.set(requestId, resolve);\n options.transport.send({\n type: \"interaction.validate\",\n requestId,\n basis,\n interactionId,\n params: parsedParams.data,\n });\n setTimeout(() => {\n if (!pendingValidations.delete(requestId)) {\n return;\n }\n resolve({\n valid: false,\n errorCode: \"validation-timeout\",\n message: \"Validation request timed out\",\n });\n }, requestTimeoutMs);\n }),\n submitInteraction: async (interactionId, params) =>\n new Promise((resolve, reject) => {\n if (disconnected) {\n const error = new Error(\"Plugin runtime disconnected\") as Error & {\n errorCode?: string;\n };\n error.name = \"SubmissionError\";\n error.errorCode = \"runtime-disconnected\";\n reject(error);\n return;\n }\n const parsedParams = RuntimeJsonSchema.safeParse(params);\n if (!parsedParams.success) {\n const error = new Error(\n \"Interaction params must be runtime JSON.\",\n ) as Error & { errorCode?: string };\n error.name = \"SubmissionError\";\n error.errorCode = \"invalid-runtime-json\";\n reject(error);\n return;\n }\n let basis: PluginInteractionBasis;\n try {\n basis = currentBasis();\n } catch (error) {\n const submissionError = new Error(\n error instanceof Error\n ? error.message\n : \"Plugin runtime is not ready.\",\n ) as Error & { errorCode?: string };\n submissionError.name = \"SubmissionError\";\n submissionError.errorCode = \"runtime-not-ready\";\n reject(submissionError);\n return;\n }\n const requestId = idFactory.nextId(\"submit\");\n pendingSubmissions.set(requestId, { resolve, reject });\n options.transport.send({\n type: \"interaction.submit\",\n requestId,\n basis,\n interactionId,\n params: parsedParams.data,\n });\n setTimeout(() => {\n const pending = pendingSubmissions.get(requestId);\n if (!pending) {\n return;\n }\n pendingSubmissions.delete(requestId);\n const error = new Error(\"Submission request timed out\") as Error & {\n errorCode?: string;\n };\n error.name = \"SubmissionError\";\n error.errorCode = \"submission-timeout\";\n pending.reject(error);\n }, requestTimeoutMs);\n }),\n disconnect: () => {\n disconnected = true;\n stopTransport?.();\n stopTransport = undefined;\n sessionListeners.clear();\n frameListeners.clear();\n rejectPending();\n session = null;\n frame = null;\n },\n };\n}\n","import React, { useMemo } from \"react\";\nimport { InteractionUiProvider } from \"../context/InteractionDraftContext.js\";\nimport {\n PluginSessionProvider,\n usePluginSession,\n} from \"../context/PluginSessionContext.js\";\nimport { PluginGameplayFrameProvider } from \"../context/PluginGameplayFrameContext.js\";\nimport { RuntimeContext } from \"../context/RuntimeContext.js\";\nimport type { PluginRuntimeClient } from \"../core/types.js\";\nimport type { RuntimeAPI } from \"../types/runtime-api.js\";\n\nfunction runtimeApiFromClient(runtime: PluginRuntimeClient): RuntimeAPI {\n return {\n validateInteraction: (interactionId, params) =>\n runtime.validateInteraction(interactionId, params),\n submitInteraction: (interactionId, params) =>\n runtime.submitInteraction(interactionId, params),\n getSessionState: () => {\n const session = runtime.getSession();\n const frame = runtime.getFrame();\n return {\n status: session ? \"ready\" : \"loading\",\n sessionId: session?.sessionId ?? null,\n controllingPlayerId: frame?.perspectivePlayerId ?? null,\n };\n },\n disconnect: () => runtime.disconnect(),\n };\n}\n\nexport function PluginRuntimeBoundary({\n runtime,\n children,\n}: {\n runtime: PluginRuntimeClient;\n children: React.ReactNode;\n}) {\n const runtimeApi = useMemo(() => runtimeApiFromClient(runtime), [runtime]);\n\n return (\n <RuntimeContext.Provider value={runtimeApi}>\n <PluginSessionProvider runtime={runtime}>\n <PluginGameplayFrameProvider runtime={runtime}>\n <SessionScopedInteractionUiProvider>\n {children}\n </SessionScopedInteractionUiProvider>\n </PluginGameplayFrameProvider>\n </PluginSessionProvider>\n </RuntimeContext.Provider>\n );\n}\n\nfunction SessionScopedInteractionUiProvider({\n children,\n}: {\n children: React.ReactNode;\n}) {\n const { controllingPlayerId } = usePluginSession();\n return (\n <InteractionUiProvider key={controllingPlayerId ?? \"__no_player__\"}>\n {children}\n </InteractionUiProvider>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/runtime/core/create-plugin-runtime-client.ts","../src/runtime/components/PluginRuntimeBoundary.tsx"],"sourcesContent":["import {\n HostToPluginEnvelopeSchema,\n RuntimeJsonSchema,\n type HostToPluginEnvelope,\n type PluginGameplayFrame,\n type PluginInteractionBasis,\n type PluginSessionDescriptor,\n type SubmissionResult,\n type ValidationResult,\n} from \"@dreamboard-games/plugin-runtime-contract\";\nimport type {\n PluginRuntimeClient,\n PluginRuntimeClientOptions,\n RuntimeClock,\n RuntimeIdFactory,\n} from \"./types.js\";\n\nconst defaultClock: RuntimeClock = {\n now: () => Date.now(),\n};\n\nconst defaultIdFactory: RuntimeIdFactory = {\n nextId: (prefix) => {\n const cryptoLike = (\n globalThis as typeof globalThis & {\n crypto?: { randomUUID?: () => string };\n }\n ).crypto;\n if (cryptoLike?.randomUUID) {\n try {\n return `${prefix}-${cryptoLike.randomUUID()}`;\n } catch {\n // Fall through to deterministic-enough fallback for older test DOMs.\n }\n }\n return `${prefix}-${Date.now().toString(16)}-${Math.random()\n .toString(16)\n .slice(2)}`;\n },\n};\n\nfunction createSubmissionError(\n result: Extract<SubmissionResult, { accepted: false }>,\n): Error & { errorCode?: string } {\n const error = new Error(result.message ?? \"Submission failed\") as Error & {\n errorCode?: string;\n };\n error.name = \"SubmissionError\";\n error.errorCode = result.errorCode;\n return error;\n}\n\nfunction disconnectedValidationResult(): ValidationResult {\n return {\n valid: false,\n errorCode: \"runtime-disconnected\",\n message: \"Plugin runtime disconnected\",\n };\n}\n\nexport function createPluginRuntimeClient(\n options: PluginRuntimeClientOptions,\n): PluginRuntimeClient {\n const clock = options.clock ?? defaultClock;\n const idFactory = options.idFactory ?? defaultIdFactory;\n const requestTimeoutMs = options.requestTimeoutMs ?? 10_000;\n let session: PluginSessionDescriptor | null = null;\n let frame: PluginGameplayFrame | null = null;\n let disconnected = false;\n let stopTransport: (() => void) | undefined;\n const sessionListeners = new Set<() => void>();\n const frameListeners = new Set<() => void>();\n const pendingValidations = new Map<\n string,\n (result: ValidationResult) => void\n >();\n const pendingSubmissions = new Map<\n string,\n {\n resolve: () => void;\n reject: (error: Error & { errorCode?: string }) => void;\n }\n >();\n\n const notifySession = () => {\n for (const listener of sessionListeners) {\n listener();\n }\n };\n\n const notifyFrame = () => {\n for (const listener of frameListeners) {\n listener();\n }\n };\n\n const currentBasis = (): PluginInteractionBasis => {\n if (!frame) {\n throw new Error(\"Plugin runtime has not received a gameplay frame.\");\n }\n return {\n gameVersion: frame.gameVersion,\n actionSetVersion: frame.actionSetVersion,\n perspectivePlayerId: frame.perspectivePlayerId,\n };\n };\n\n const sendRuntimeError = (message: string, code?: string) => {\n if (disconnected) {\n return;\n }\n options.transport.send({\n type: \"runtime.error\",\n message,\n ...(code ? { code } : {}),\n });\n };\n\n const handleHostMessage = (rawMessage: HostToPluginEnvelope) => {\n if (disconnected) {\n return;\n }\n const parseResult = HostToPluginEnvelopeSchema.safeParse(rawMessage);\n if (!parseResult.success) {\n sendRuntimeError(\n \"Invalid host-to-plugin protocol envelope.\",\n \"invalid-envelope\",\n );\n return;\n }\n const message = parseResult.data;\n const clientReceivedAtMs = clock.now();\n\n switch (message.payload.type) {\n case \"runtime.init\": {\n session = message.payload.session;\n notifySession();\n options.transport.send({ type: \"runtime.ready\" });\n options.transport.send({\n type: \"runtime.ack\",\n sequence: message.sequence,\n clientReceivedAtMs,\n });\n break;\n }\n case \"gameplay.frame\": {\n frame = message.payload.frame;\n notifyFrame();\n const sendRenderedAck = () => {\n if (disconnected) {\n return;\n }\n options.transport.send({\n type: \"runtime.ack\",\n sequence: message.sequence,\n clientReceivedAtMs,\n clientRenderedAtMs: clock.now(),\n });\n };\n if (typeof requestAnimationFrame === \"function\") {\n requestAnimationFrame(sendRenderedAck);\n } else {\n queueMicrotask(sendRenderedAck);\n }\n break;\n }\n case \"interaction.validation-result\": {\n const resolve = pendingValidations.get(message.payload.requestId);\n if (resolve) {\n pendingValidations.delete(message.payload.requestId);\n resolve(message.payload.result);\n }\n break;\n }\n case \"interaction.submit-result\": {\n const pending = pendingSubmissions.get(message.payload.requestId);\n if (!pending) {\n break;\n }\n pendingSubmissions.delete(message.payload.requestId);\n if (message.payload.result.accepted) {\n pending.resolve();\n } else {\n pending.reject(createSubmissionError(message.payload.result));\n }\n break;\n }\n default: {\n const _exhaustive: never = message.payload;\n return _exhaustive;\n }\n }\n };\n\n stopTransport = options.transport.start(handleHostMessage);\n\n const rejectPending = () => {\n for (const resolve of pendingValidations.values()) {\n resolve(disconnectedValidationResult());\n }\n pendingValidations.clear();\n for (const pending of pendingSubmissions.values()) {\n const error = new Error(\"Plugin runtime disconnected\") as Error & {\n errorCode?: string;\n };\n error.name = \"SubmissionError\";\n error.errorCode = \"runtime-disconnected\";\n pending.reject(error);\n }\n pendingSubmissions.clear();\n };\n\n return {\n getSession: () => session,\n subscribeSession: (listener) => {\n sessionListeners.add(listener);\n return () => {\n sessionListeners.delete(listener);\n };\n },\n getFrame: () => frame,\n subscribeFrame: (listener) => {\n frameListeners.add(listener);\n return () => {\n frameListeners.delete(listener);\n };\n },\n validateInteraction: async (interactionId, params) =>\n new Promise((resolve) => {\n if (disconnected) {\n resolve(disconnectedValidationResult());\n return;\n }\n let basis: PluginInteractionBasis;\n try {\n basis = currentBasis();\n } catch (error) {\n resolve({\n valid: false,\n errorCode: \"runtime-not-ready\",\n message:\n error instanceof Error\n ? error.message\n : \"Plugin runtime is not ready.\",\n });\n return;\n }\n const parsedParams = RuntimeJsonSchema.safeParse(params);\n if (!parsedParams.success) {\n resolve({\n valid: false,\n errorCode: \"invalid-runtime-json\",\n message: \"Interaction params must be runtime JSON.\",\n });\n return;\n }\n const requestId = idFactory.nextId(\"validate\");\n pendingValidations.set(requestId, resolve);\n options.transport.send({\n type: \"interaction.validate\",\n requestId,\n basis,\n interactionId,\n params: parsedParams.data,\n });\n setTimeout(() => {\n if (!pendingValidations.delete(requestId)) {\n return;\n }\n resolve({\n valid: false,\n errorCode: \"validation-timeout\",\n message: \"Validation request timed out\",\n });\n }, requestTimeoutMs);\n }),\n submitInteraction: async (interactionId, params) =>\n new Promise((resolve, reject) => {\n if (disconnected) {\n const error = new Error(\"Plugin runtime disconnected\") as Error & {\n errorCode?: string;\n };\n error.name = \"SubmissionError\";\n error.errorCode = \"runtime-disconnected\";\n reject(error);\n return;\n }\n const parsedParams = RuntimeJsonSchema.safeParse(params);\n if (!parsedParams.success) {\n const error = new Error(\n \"Interaction params must be runtime JSON.\",\n ) as Error & { errorCode?: string };\n error.name = \"SubmissionError\";\n error.errorCode = \"invalid-runtime-json\";\n reject(error);\n return;\n }\n let basis: PluginInteractionBasis;\n try {\n basis = currentBasis();\n } catch (error) {\n const submissionError = new Error(\n error instanceof Error\n ? error.message\n : \"Plugin runtime is not ready.\",\n ) as Error & { errorCode?: string };\n submissionError.name = \"SubmissionError\";\n submissionError.errorCode = \"runtime-not-ready\";\n reject(submissionError);\n return;\n }\n const requestId = idFactory.nextId(\"submit\");\n pendingSubmissions.set(requestId, { resolve, reject });\n options.transport.send({\n type: \"interaction.submit\",\n requestId,\n basis,\n interactionId,\n params: parsedParams.data,\n });\n setTimeout(() => {\n const pending = pendingSubmissions.get(requestId);\n if (!pending) {\n return;\n }\n pendingSubmissions.delete(requestId);\n const error = new Error(\"Submission request timed out\") as Error & {\n errorCode?: string;\n };\n error.name = \"SubmissionError\";\n error.errorCode = \"submission-timeout\";\n pending.reject(error);\n }, requestTimeoutMs);\n }),\n disconnect: () => {\n disconnected = true;\n stopTransport?.();\n stopTransport = undefined;\n sessionListeners.clear();\n frameListeners.clear();\n rejectPending();\n session = null;\n frame = null;\n },\n };\n}\n","import React, { useMemo } from \"react\";\nimport { InteractionUiProvider } from \"../context/InteractionDraftContext.js\";\nimport {\n PluginSessionProvider,\n usePluginSession,\n} from \"../context/PluginSessionContext.js\";\nimport { PluginGameplayFrameProvider } from \"../context/PluginGameplayFrameContext.js\";\nimport { RuntimeContext } from \"../context/RuntimeContext.js\";\nimport type { PluginRuntimeClient } from \"../core/types.js\";\nimport type { RuntimeAPI } from \"../types/runtime-api.js\";\n\nfunction runtimeApiFromClient(runtime: PluginRuntimeClient): RuntimeAPI {\n return {\n validateInteraction: (interactionId, params) =>\n runtime.validateInteraction(interactionId, params),\n submitInteraction: (interactionId, params) =>\n runtime.submitInteraction(interactionId, params),\n getSessionState: () => {\n const session = runtime.getSession();\n const frame = runtime.getFrame();\n return {\n status: session ? \"ready\" : \"loading\",\n sessionId: session?.sessionId ?? null,\n controllingPlayerId: frame?.perspectivePlayerId ?? null,\n };\n },\n disconnect: () => runtime.disconnect(),\n };\n}\n\nexport function PluginRuntimeBoundary({\n runtime,\n children,\n}: {\n runtime: PluginRuntimeClient;\n children: React.ReactNode;\n}) {\n const runtimeApi = useMemo(() => runtimeApiFromClient(runtime), [runtime]);\n\n return (\n <RuntimeContext.Provider value={runtimeApi}>\n <PluginSessionProvider runtime={runtime}>\n <PluginGameplayFrameProvider runtime={runtime}>\n <SessionScopedInteractionUiProvider>\n {children}\n </SessionScopedInteractionUiProvider>\n </PluginGameplayFrameProvider>\n </PluginSessionProvider>\n </RuntimeContext.Provider>\n );\n}\n\nfunction SessionScopedInteractionUiProvider({\n children,\n}: {\n children: React.ReactNode;\n}) {\n const { controllingPlayerId } = usePluginSession();\n return (\n <InteractionUiProvider key={controllingPlayerId ?? \"__no_player__\"}>\n {children}\n </InteractionUiProvider>\n );\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,575 @@
1
+ // ../plugin-runtime-contract/dist/chunk-GX5V4QL7.js
2
+ import { sha256 } from "@noble/hashes/sha2.js";
3
+ import { bytesToHex, utf8ToBytes } from "@noble/hashes/utils.js";
4
+ function canonicalizePluginRuntimeJson(value) {
5
+ assertRuntimeJson(value);
6
+ return canonicalizeJson(value);
7
+ }
8
+ function encodeCanonicalPluginRuntimeJson(value) {
9
+ return JSON.stringify(canonicalizePluginRuntimeJson(value));
10
+ }
11
+ function digestPluginRuntimeJson(value) {
12
+ return `sha256:${sha256Hex(encodeCanonicalPluginRuntimeJson(value))}`;
13
+ }
14
+ function canonicalizeJson(value) {
15
+ if (value === null || typeof value === "boolean" || typeof value === "string") {
16
+ return value;
17
+ }
18
+ if (typeof value === "number") {
19
+ if (!Number.isFinite(value)) {
20
+ throw new Error("canonical JSON contains a non-finite number");
21
+ }
22
+ return value;
23
+ }
24
+ if (Array.isArray(value)) {
25
+ return value.map((item) => canonicalizeJson(item));
26
+ }
27
+ return Object.fromEntries(
28
+ Object.entries(value).filter(([, item]) => item !== void 0).sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0).map(([key, item]) => [key, canonicalizeJson(item)])
29
+ );
30
+ }
31
+ function assertRuntimeJson(value) {
32
+ if (value === null || typeof value === "boolean" || typeof value === "string") {
33
+ return;
34
+ }
35
+ if (typeof value === "number") {
36
+ if (!Number.isFinite(value)) {
37
+ throw new Error("runtime JSON contains a non-finite number");
38
+ }
39
+ return;
40
+ }
41
+ if (Array.isArray(value)) {
42
+ value.forEach(assertRuntimeJson);
43
+ return;
44
+ }
45
+ if (typeof value === "object") {
46
+ Object.values(value).forEach((item) => {
47
+ if (item !== void 0) {
48
+ assertRuntimeJson(item);
49
+ }
50
+ });
51
+ return;
52
+ }
53
+ throw new Error(`runtime JSON contains unsupported ${typeof value} value`);
54
+ }
55
+ function sha256Hex(input) {
56
+ return bytesToHex(sha256(utf8ToBytes(input)));
57
+ }
58
+ function computePluginActionSetVersion(input) {
59
+ return digestPluginRuntimeJson({
60
+ digestVersion: "plugin-action-set@3",
61
+ gameVersion: input.gameVersion,
62
+ availableInteractions: input.availableInteractions
63
+ });
64
+ }
65
+ function digestPluginGameplayFrame(frame) {
66
+ return digestPluginRuntimeJson({
67
+ digestVersion: "plugin-gameplay-frame@3",
68
+ frame
69
+ });
70
+ }
71
+
72
+ // ../plugin-runtime-contract/dist/chunk-E4Y6SAWF.js
73
+ import { z } from "zod";
74
+ var DREAMBOARD_PLUGIN_PROTOCOL = "dreamboard-plugin";
75
+ var DREAMBOARD_PLUGIN_PROTOCOL_VERSION = 3;
76
+ var RuntimeJsonSchema = z.lazy(
77
+ () => z.union([
78
+ z.null(),
79
+ z.boolean(),
80
+ z.number().finite(),
81
+ z.string(),
82
+ z.array(RuntimeJsonSchema),
83
+ z.record(z.string(), RuntimeJsonSchema)
84
+ ])
85
+ );
86
+ var BoardStaticProjectionSchema = z.object({
87
+ view: RuntimeJsonSchema,
88
+ hash: z.string().optional(),
89
+ manifestVersion: z.string().optional()
90
+ }).strict();
91
+ var SetupGuidanceStepSchema = z.object({
92
+ id: z.string().min(1),
93
+ label: z.string().min(1),
94
+ description: z.string().optional()
95
+ }).strict();
96
+ var GameGuidanceProjectionSchema = z.object({
97
+ phase: z.object({
98
+ id: z.string().min(1),
99
+ label: z.string().min(1),
100
+ summary: z.string().optional(),
101
+ objective: z.string().optional()
102
+ }).strict(),
103
+ setup: z.object({
104
+ profileId: z.string().min(1),
105
+ name: z.string().min(1),
106
+ summary: z.string().optional(),
107
+ steps: z.array(SetupGuidanceStepSchema)
108
+ }).strict().optional()
109
+ }).strict();
110
+ var GameEventDetailSchema = z.object({
111
+ label: z.string().min(1),
112
+ value: z.union([z.string(), z.number().finite(), z.boolean()])
113
+ }).strict();
114
+ var SystemActionEventSchema = z.object({
115
+ kind: z.literal("systemAction"),
116
+ procedureId: z.string().min(1),
117
+ title: z.string().min(1),
118
+ summary: z.string().optional(),
119
+ details: z.array(GameEventDetailSchema).optional()
120
+ }).strict();
121
+ var GameEventSchema = z.discriminatedUnion("kind", [
122
+ SystemActionEventSchema
123
+ ]);
124
+ var ProjectedGameEventSchema = GameEventSchema.and(
125
+ z.object({
126
+ version: z.number().int().nonnegative(),
127
+ index: z.number().int().nonnegative()
128
+ }).strict()
129
+ );
130
+ var SeatProjectionBundleSchema = z.object({
131
+ currentStage: z.string().nullable().optional(),
132
+ stageSeats: z.array(z.string()).optional(),
133
+ simultaneousPhase: z.unknown().nullable().optional(),
134
+ guidance: GameGuidanceProjectionSchema.nullable().optional(),
135
+ recentEvents: z.array(ProjectedGameEventSchema).optional(),
136
+ interactionsByRef: z.record(z.string(), z.unknown()).optional(),
137
+ seats: z.record(
138
+ z.string(),
139
+ z.object({
140
+ view: z.unknown().optional(),
141
+ availableInteractionRefs: z.array(z.string()).optional(),
142
+ zones: z.unknown().optional()
143
+ }).strict()
144
+ )
145
+ }).strict();
146
+ var PlayerIdSchema = z.string().min(1);
147
+ var PluginPlayerSummarySchema = z.object({
148
+ playerId: PlayerIdSchema,
149
+ displayName: z.string(),
150
+ color: z.string().optional()
151
+ }).strict();
152
+ var PluginSessionDescriptorSchema = z.object({
153
+ sessionId: z.string().min(1),
154
+ players: z.array(PluginPlayerSummarySchema)
155
+ }).strict();
156
+ var InteractionCommitPolicySchema = z.discriminatedUnion("mode", [
157
+ z.object({ mode: z.literal("manual") }).strict(),
158
+ z.object({ mode: z.literal("autoWhenReady") }).strict()
159
+ ]);
160
+ var InputSelectionSchema = z.discriminatedUnion("mode", [
161
+ z.object({ mode: z.literal("single") }).strict(),
162
+ z.object({
163
+ mode: z.literal("many"),
164
+ min: z.number().int(),
165
+ max: z.number().int().optional(),
166
+ distinct: z.boolean().optional()
167
+ }).strict()
168
+ ]);
169
+ var InputDomainDependencyCaseSchema = z.lazy(
170
+ () => z.object({
171
+ when: z.record(z.string(), z.string()),
172
+ domain: InputDomainSchema
173
+ }).strict()
174
+ );
175
+ var InputDomainDependenciesSchema = z.discriminatedUnion("mode", [
176
+ z.object({
177
+ mode: z.literal("eager"),
178
+ dependentCases: z.array(InputDomainDependencyCaseSchema)
179
+ }).strict(),
180
+ z.object({
181
+ mode: z.literal("lazy"),
182
+ dependsOn: z.array(z.string()),
183
+ resolver: z.object({
184
+ interactionKey: z.string().optional(),
185
+ inputKey: z.string()
186
+ }).strict()
187
+ }).strict()
188
+ ]);
189
+ var InputDomainSchema = z.object({
190
+ type: z.string(),
191
+ selection: InputSelectionSchema.optional(),
192
+ dependencies: InputDomainDependenciesSchema.optional()
193
+ }).catchall(RuntimeJsonSchema);
194
+ var InteractionChoiceOptionSchema = z.object({
195
+ value: z.string().nullable(),
196
+ label: z.string(),
197
+ icon: z.string().optional(),
198
+ badge: z.string().optional(),
199
+ description: z.string().optional(),
200
+ disabled: z.boolean().optional(),
201
+ disabledReason: z.string().optional()
202
+ }).strict();
203
+ var InteractionInputDescriptorSchema = z.object({
204
+ key: z.string().min(1),
205
+ kind: z.string().min(1),
206
+ domain: InputDomainSchema,
207
+ defaultValue: RuntimeJsonSchema.optional()
208
+ }).strict();
209
+ var InteractionAvailabilitySchema = z.discriminatedUnion("status", [
210
+ z.object({ status: z.literal("available") }).strict(),
211
+ z.object({ status: z.literal("notYourTurn"), reason: z.string() }).strict(),
212
+ z.object({
213
+ status: z.literal("insufficientResources"),
214
+ reason: z.string(),
215
+ missingResources: z.record(z.string(), z.number().finite())
216
+ }).strict(),
217
+ z.object({
218
+ status: z.literal("blocked"),
219
+ reason: z.string(),
220
+ code: z.string().optional()
221
+ }).strict()
222
+ ]);
223
+ var InteractionBaseSchema = z.object({
224
+ phaseName: z.string().min(1),
225
+ interactionKey: z.string().min(1),
226
+ interactionId: z.string().min(1),
227
+ label: z.string().min(1),
228
+ help: z.string().optional(),
229
+ zoneId: z.string().optional(),
230
+ zoneIds: z.array(z.string()).optional(),
231
+ commit: InteractionCommitPolicySchema,
232
+ descriptorDigest: z.string().optional(),
233
+ actorSeat: z.number().int().optional(),
234
+ draftDigest: z.string().optional(),
235
+ inputs: z.array(InteractionInputDescriptorSchema),
236
+ cost: z.record(z.string(), RuntimeJsonSchema).optional(),
237
+ currentResources: z.record(z.string(), RuntimeJsonSchema).optional(),
238
+ availability: InteractionAvailabilitySchema,
239
+ reasons: z.array(
240
+ z.object({
241
+ ruleId: z.string(),
242
+ errorCode: z.string()
243
+ }).strict()
244
+ ).optional()
245
+ }).strict();
246
+ var InteractionContextSchema = z.object({
247
+ to: z.string().min(1),
248
+ title: z.string().optional(),
249
+ payload: z.record(z.string(), RuntimeJsonSchema).optional(),
250
+ options: z.array(
251
+ z.object({
252
+ id: z.string(),
253
+ label: z.string().optional()
254
+ }).strict()
255
+ ).optional()
256
+ }).strict();
257
+ var InteractionDescriptorSchema = z.discriminatedUnion("kind", [
258
+ InteractionBaseSchema.extend({ kind: z.literal("action") }).strict(),
259
+ InteractionBaseSchema.extend({
260
+ kind: z.literal("prompt"),
261
+ context: InteractionContextSchema
262
+ }).strict()
263
+ ]);
264
+ var ZoneHandlesSnapshotSchema = z.object({
265
+ cardIds: z.array(z.string()),
266
+ cardViewsById: z.record(z.string(), z.string()),
267
+ playableByCardId: z.record(
268
+ z.string(),
269
+ z.array(InteractionDescriptorSchema)
270
+ )
271
+ }).strict();
272
+ var SimultaneousPhaseSnapshotSchema = z.object({
273
+ phaseName: z.string().min(1),
274
+ interactionId: z.string().min(1),
275
+ actorIds: z.array(PlayerIdSchema),
276
+ sealedPlayerIds: z.array(PlayerIdSchema),
277
+ pendingPlayerIds: z.array(PlayerIdSchema)
278
+ }).strict();
279
+ var PluginGameplayFrameSchema = z.object({
280
+ gameVersion: z.number().int().nonnegative(),
281
+ actionSetVersion: z.string().min(1),
282
+ perspectivePlayerId: PlayerIdSchema.nullable(),
283
+ view: RuntimeJsonSchema.nullable(),
284
+ flow: z.object({
285
+ currentPhase: z.string().nullable(),
286
+ currentStage: z.string().nullable(),
287
+ activePlayers: z.array(PlayerIdSchema),
288
+ simultaneousPhase: SimultaneousPhaseSnapshotSchema.nullable()
289
+ }).strict(),
290
+ availableInteractions: z.array(InteractionDescriptorSchema),
291
+ guidance: GameGuidanceProjectionSchema.nullable().optional(),
292
+ recentEvents: z.array(ProjectedGameEventSchema),
293
+ zones: z.record(z.string(), ZoneHandlesSnapshotSchema)
294
+ }).strict();
295
+ var ValidationResultSchema = z.object({
296
+ valid: z.boolean(),
297
+ errorCode: z.string().optional(),
298
+ message: z.string().optional()
299
+ }).strict();
300
+ var SubmissionResultSchema = z.discriminatedUnion("accepted", [
301
+ z.object({ accepted: z.literal(true) }).strict(),
302
+ z.object({
303
+ accepted: z.literal(false),
304
+ errorCode: z.string().min(1),
305
+ message: z.string().optional()
306
+ }).strict()
307
+ ]);
308
+ var PluginInteractionBasisSchema = z.object({
309
+ gameVersion: z.number().int().nonnegative(),
310
+ actionSetVersion: z.string().min(1),
311
+ perspectivePlayerId: PlayerIdSchema.nullable()
312
+ }).strict();
313
+ var ValidateInteractionCommandSchema = z.object({
314
+ type: z.literal("interaction.validate"),
315
+ requestId: z.string().min(1),
316
+ basis: PluginInteractionBasisSchema,
317
+ interactionId: z.string().min(1),
318
+ params: RuntimeJsonSchema
319
+ }).strict();
320
+ var SubmitInteractionCommandSchema = z.object({
321
+ type: z.literal("interaction.submit"),
322
+ requestId: z.string().min(1),
323
+ basis: PluginInteractionBasisSchema,
324
+ interactionId: z.string().min(1),
325
+ params: RuntimeJsonSchema
326
+ }).strict();
327
+ var HostToPluginPayloadSchema = z.discriminatedUnion("type", [
328
+ z.object({
329
+ type: z.literal("runtime.init"),
330
+ session: PluginSessionDescriptorSchema
331
+ }).strict(),
332
+ z.object({
333
+ type: z.literal("gameplay.frame"),
334
+ frame: PluginGameplayFrameSchema
335
+ }).strict(),
336
+ z.object({
337
+ type: z.literal("interaction.validation-result"),
338
+ requestId: z.string().min(1),
339
+ result: ValidationResultSchema
340
+ }).strict(),
341
+ z.object({
342
+ type: z.literal("interaction.submit-result"),
343
+ requestId: z.string().min(1),
344
+ result: SubmissionResultSchema
345
+ }).strict()
346
+ ]);
347
+ var PluginToHostPayloadSchema = z.discriminatedUnion("type", [
348
+ z.object({ type: z.literal("runtime.ready") }).strict(),
349
+ z.object({
350
+ type: z.literal("runtime.ack"),
351
+ sequence: z.number().int().nonnegative(),
352
+ clientReceivedAtMs: z.number().finite().optional(),
353
+ clientRenderedAtMs: z.number().finite().optional()
354
+ }).strict(),
355
+ ValidateInteractionCommandSchema,
356
+ SubmitInteractionCommandSchema,
357
+ z.object({
358
+ type: z.literal("runtime.error"),
359
+ message: z.string(),
360
+ code: z.string().optional()
361
+ }).strict()
362
+ ]);
363
+ function createPluginProtocolEnvelopeSchema(payloadSchema) {
364
+ return z.object({
365
+ protocol: z.literal(DREAMBOARD_PLUGIN_PROTOCOL),
366
+ version: z.literal(DREAMBOARD_PLUGIN_PROTOCOL_VERSION),
367
+ channelId: z.string().min(1),
368
+ sequence: z.number().int().nonnegative(),
369
+ payload: payloadSchema
370
+ }).strict();
371
+ }
372
+ var HostToPluginEnvelopeSchema = createPluginProtocolEnvelopeSchema(
373
+ HostToPluginPayloadSchema
374
+ );
375
+ var PluginToHostEnvelopeSchema = createPluginProtocolEnvelopeSchema(
376
+ PluginToHostPayloadSchema
377
+ );
378
+ var PluginProtocolFrameSchema = z.object({
379
+ id: z.string().min(1),
380
+ frame: PluginGameplayFrameSchema,
381
+ projectionDigest: z.string().regex(/^sha256:[a-f0-9]{64}$/)
382
+ }).strict();
383
+ var PluginProtocolStepSchema = z.discriminatedUnion("kind", [
384
+ z.object({
385
+ id: z.string().min(1),
386
+ kind: z.literal("host.frame"),
387
+ frameId: z.string().min(1)
388
+ }).strict(),
389
+ z.object({
390
+ id: z.string().min(1),
391
+ kind: z.literal("client.validate"),
392
+ fromFrameId: z.string().min(1),
393
+ requestDigest: z.string().regex(/^sha256:[a-f0-9]{64}$/),
394
+ response: ValidationResultSchema
395
+ }).strict(),
396
+ z.object({
397
+ id: z.string().min(1),
398
+ kind: z.literal("client.submit"),
399
+ fromFrameId: z.string().min(1),
400
+ requestDigest: z.string().regex(/^sha256:[a-f0-9]{64}$/),
401
+ response: SubmissionResultSchema
402
+ }).strict()
403
+ ]);
404
+ var PluginProtocolTapeSchema = z.object({
405
+ session: PluginSessionDescriptorSchema,
406
+ frames: z.array(PluginProtocolFrameSchema),
407
+ steps: z.array(PluginProtocolStepSchema)
408
+ }).strict();
409
+
410
+ // ../plugin-runtime-contract/dist/index.js
411
+ function materializePluginGameplayFrame(input) {
412
+ const dynamicProjection = SeatProjectionBundleSchema.parse(
413
+ canonicalizeReducerProjection(input.dynamicProjection)
414
+ );
415
+ const staticProjection = input.staticProjection == null ? null : BoardStaticProjectionSchema.parse(
416
+ canonicalizeReducerProjection(input.staticProjection)
417
+ );
418
+ const registry = parseInteractionRegistry(
419
+ dynamicProjection.interactionsByRef
420
+ );
421
+ const seat = input.perspectivePlayerId == null ? null : dynamicProjection.seats[input.perspectivePlayerId] ?? null;
422
+ const availableInteractions = seat == null ? [] : hydrateInteractionRefs(
423
+ registry,
424
+ seat.availableInteractionRefs,
425
+ "availableInteractionRefs"
426
+ );
427
+ const zones = seat?.zones == null ? {} : hydrateZones(registry, seat.zones, "zones");
428
+ const frame = {
429
+ gameVersion: input.gameVersion,
430
+ actionSetVersion: input.actionSetVersion,
431
+ perspectivePlayerId: input.perspectivePlayerId,
432
+ view: composeProjectionView(staticProjection, seat?.view),
433
+ flow: {
434
+ currentPhase: input.currentPhase,
435
+ currentStage: dynamicProjection.currentStage ?? null,
436
+ activePlayers: [...input.activePlayers],
437
+ simultaneousPhase: dynamicProjection.simultaneousPhase ?? null
438
+ },
439
+ availableInteractions,
440
+ guidance: dynamicProjection.guidance ?? null,
441
+ recentEvents: [...dynamicProjection.recentEvents ?? []],
442
+ zones
443
+ };
444
+ return PluginGameplayFrameSchema.parse(frame);
445
+ }
446
+ function canonicalizeReducerProjection(value) {
447
+ return canonicalizePluginRuntimeJson(value);
448
+ }
449
+ function parseInteractionRegistry(value) {
450
+ if (value == null) return {};
451
+ if (!isRecord(value)) {
452
+ throw new Error("Seat projection interactionsByRef must be an object.");
453
+ }
454
+ return Object.fromEntries(
455
+ Object.entries(value).map(([ref, descriptor]) => [
456
+ ref,
457
+ InteractionDescriptorSchema.parse(descriptor)
458
+ ])
459
+ );
460
+ }
461
+ function hydrateZones(registry, value, path) {
462
+ if (!isRecord(value)) {
463
+ throw new Error(`Seat projection ${path} must be an object.`);
464
+ }
465
+ return Object.fromEntries(
466
+ Object.entries(value).map(([zoneId, zoneValue]) => [
467
+ zoneId,
468
+ hydrateZone(registry, zoneValue, `${path}.${zoneId}`)
469
+ ])
470
+ );
471
+ }
472
+ function hydrateZone(registry, value, path) {
473
+ if (!isRecord(value)) {
474
+ throw new Error(`Seat projection ${path} must be an object.`);
475
+ }
476
+ const cardIds = arrayOfStrings(value.cardIds, `${path}.cardIds`);
477
+ const cardViewsById = stringRecord(
478
+ value.cardViewsById,
479
+ `${path}.cardViewsById`
480
+ );
481
+ if (!isRecord(value.playableByCardId)) {
482
+ throw new Error(
483
+ `Seat projection ${path}.playableByCardId must be an object.`
484
+ );
485
+ }
486
+ const playableByCardId = Object.fromEntries(
487
+ Object.entries(value.playableByCardId).map(([cardId, refs]) => [
488
+ cardId,
489
+ hydrateInteractionRefs(
490
+ registry,
491
+ refs,
492
+ `${path}.playableByCardId.${cardId}`
493
+ )
494
+ ])
495
+ );
496
+ return ZoneHandlesSnapshotSchema.parse({
497
+ cardIds,
498
+ cardViewsById,
499
+ playableByCardId
500
+ });
501
+ }
502
+ function hydrateInteractionRefs(registry, value, path) {
503
+ if (!Array.isArray(value)) {
504
+ throw new Error(`Seat projection ${path} must be an array of refs.`);
505
+ }
506
+ return value.map((ref, index) => {
507
+ if (typeof ref !== "string") {
508
+ throw new Error(
509
+ `Seat projection ${path}[${index}] must be a ref string.`
510
+ );
511
+ }
512
+ const descriptor = registry[ref];
513
+ if (!descriptor) {
514
+ throw new Error(
515
+ `Seat projection ${path}[${index}] references '${ref}', which is missing from interactionsByRef.`
516
+ );
517
+ }
518
+ return descriptor;
519
+ });
520
+ }
521
+ function composeProjectionView(staticProjection, dynamicView) {
522
+ const normalizedDynamicView = dynamicView === void 0 ? null : dynamicView;
523
+ if (staticProjection == null) {
524
+ return normalizedDynamicView;
525
+ }
526
+ if (normalizedDynamicView == null) {
527
+ return staticProjection.view;
528
+ }
529
+ if (isRecord(staticProjection.view) && isRecord(normalizedDynamicView)) {
530
+ return {
531
+ ...staticProjection.view,
532
+ ...normalizedDynamicView
533
+ };
534
+ }
535
+ return {
536
+ static: staticProjection.view,
537
+ dynamic: normalizedDynamicView
538
+ };
539
+ }
540
+ function arrayOfStrings(value, path) {
541
+ if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) {
542
+ throw new Error(`Seat projection ${path} must be an array of strings.`);
543
+ }
544
+ return value;
545
+ }
546
+ function stringRecord(value, path) {
547
+ if (!isRecord(value)) {
548
+ throw new Error(`Seat projection ${path} must be an object.`);
549
+ }
550
+ const result = {};
551
+ for (const [key, item] of Object.entries(value)) {
552
+ if (typeof item !== "string") {
553
+ throw new Error(`Seat projection ${path}.${key} must be a string.`);
554
+ }
555
+ result[key] = item;
556
+ }
557
+ return result;
558
+ }
559
+ function isRecord(value) {
560
+ return value !== null && typeof value === "object" && !Array.isArray(value) && (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null);
561
+ }
562
+
563
+ export {
564
+ computePluginActionSetVersion,
565
+ digestPluginGameplayFrame,
566
+ DREAMBOARD_PLUGIN_PROTOCOL,
567
+ DREAMBOARD_PLUGIN_PROTOCOL_VERSION,
568
+ RuntimeJsonSchema,
569
+ HostToPluginEnvelopeSchema,
570
+ PluginProtocolFrameSchema,
571
+ PluginProtocolStepSchema,
572
+ PluginProtocolTapeSchema,
573
+ materializePluginGameplayFrame
574
+ };
575
+ //# sourceMappingURL=chunk-5IHU5CUK.js.map