@puzzmo/sdk 1.0.12 → 1.0.14
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.
- package/dist/createSimulator-CGTMmToi.cjs.map +1 -1
- package/dist/createSimulator-IMuPxYe-.js.map +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/sdk.d.ts.map +1 -1
- package/dist/simulator/createSimulator.d.ts +1 -1
- package/dist/vite.cjs.map +1 -1
- package/dist/vite.d.ts +1 -1
- package/dist/vite.d.ts.map +1 -1
- package/dist/vite.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":[],"sources":["../src/sdk.ts","../src/keyboard.ts","../src/editor.ts"],"sourcesContent":["import type {\n MessagesSentFromEmbed,\n MessagesReceived,\n GamePlay,\n AugmentationConfig,\n CheckpointConfig,\n Theme,\n Deed,\n KeyboardConfig,\n} from \"./types\"\n\nexport type SDK = ReturnType<typeof createPuzzmoSDK>\n\nexport interface PuzzmoSDKOptions {\n /** Optional timeout in ms to wait for READY_DATA (default: 5000) */\n timeout?: number\n}\n\ntype SupportedOutgoingMessages = Pick<\n MessagesSentFromEmbed,\n | \"READY\"\n | \"READY_GAME_LOADED\"\n | \"GAME_COMPLETED\"\n | \"SHOW_GAME_COMPLETE_SCREEN\"\n | \"TIMER_TICK\"\n | \"TIMER_SYNC\"\n | \"UPLOAD_NEW_GAME_STATE\"\n | \"HIT_CHECKPOINT\"\n | \"KEYBOARD_UPDATE_CONFIG\"\n>\n\ntype SupportedIncomingMessages = Pick<\n MessagesReceived,\n | \"READY_DATA\"\n | \"START_GAME\"\n | \"PAUSE_GAME\"\n | \"RESUME_GAME\"\n | \"SETTINGS_UPDATE\"\n | \"RETRY_PUZZLE\"\n | \"KEYBOARD_KEY_PRESS\"\n | \"KEYBOARD_CURSOR_CHANGE\"\n | \"KEYBOARD_CURSOR_END\"\n>\n\ntype MessageHandler<T extends keyof SupportedIncomingMessages> = (data: SupportedIncomingMessages[T]) => void\n\nexport type SDKEventMap = {\n start: void\n pause: void\n resume: void\n retry: void\n settingsUpdate: any\n /** A key on the on-screen keyboard was tapped. */\n keyboardKeyPress: { key: string }\n /** The drag cursor moved across the keyboard. Only fires when `supportsDragCursor` is true. */\n keyboardCursorChange: { position: [number, number] }\n /** The drag cursor was released. Only fires when `supportsDragCursor` is true. */\n keyboardCursorEnd: void\n}\n\nexport type SDKEventType = keyof SDKEventMap\n\nexport interface SDKTimer {\n /** Get elapsed time in milliseconds */\n timeMs: () => number\n /** Get elapsed time in seconds */\n timeSecs: () => number\n /** Get added/penalty time in milliseconds */\n addedTimeMs: () => number\n /** Get added/penalty time in seconds */\n addedTimeSecs: () => number\n /** Get elapsed time without penalties in seconds */\n timeWithoutPenaltySecs: () => number\n /** Get formatted display strings [elapsed, added] */\n display: () => [string, string]\n /** Add penalty time in milliseconds */\n addPenalty: (ms: number) => void\n /** Check if timer is paused */\n isPaused: () => boolean\n /** Check if timer has been started */\n isRunning: () => boolean\n}\n\nfunction formatTime(timeMs: number): string {\n const isAnHourOrMore = timeMs >= 60 * 60 * 1000\n return new Date(timeMs)\n .toISOString()\n .slice(isAnHourOrMore ? 11 : 14, -1)\n .split(\".\")[0]\n}\n\nfunction createTimer(\n initialTimeMs = 0,\n initialAddedTimeMs = 0,\n): SDKTimer & {\n _init: () => void\n _pause: () => void\n _resume: () => void\n _reset: (initialTimeMs?: number, initialAddedTimeMs?: number) => void\n _conclude: () => void\n} {\n let baseTime = initialTimeMs\n let addedTime = initialAddedTimeMs\n let pausedTime = 0\n let concludeTime: number | undefined\n\n let startDate: number | undefined = undefined\n let pausedDate: number | undefined = undefined\n\n const getTime = (): number => {\n if (startDate === undefined) return baseTime + addedTime\n if (concludeTime !== undefined) return concludeTime\n const now = pausedDate ?? performance.now()\n const elapsed = now - startDate - pausedTime\n return baseTime + addedTime + elapsed\n }\n\n return {\n _init: () => {\n if (startDate === undefined) {\n startDate = performance.now()\n pausedDate = undefined\n }\n },\n _pause: () => {\n if (pausedDate !== undefined || startDate === undefined) return\n pausedDate = performance.now()\n },\n _resume: () => {\n if (pausedDate === undefined) return\n pausedTime += performance.now() - pausedDate\n pausedDate = undefined\n },\n _reset: (newInitialTimeMs = 0, newInitialAddedTimeMs = 0) => {\n baseTime = newInitialTimeMs\n addedTime = newInitialAddedTimeMs\n pausedTime = 0\n concludeTime = undefined\n startDate = undefined\n pausedDate = undefined\n },\n _conclude: () => {\n if (pausedDate !== undefined) {\n concludeTime = getTime()\n return\n }\n if (startDate === undefined) {\n concludeTime = baseTime + addedTime\n return\n }\n concludeTime = getTime()\n },\n timeMs: () => getTime(),\n timeSecs: () => getTime() / 1000,\n addedTimeMs: () => addedTime,\n addedTimeSecs: () => addedTime / 1000,\n timeWithoutPenaltySecs: () => (getTime() - addedTime) / 1000,\n addPenalty: (ms: number) => {\n addedTime += ms\n },\n isPaused: () => pausedDate !== undefined || startDate === undefined,\n isRunning: () => startDate !== undefined && pausedDate === undefined,\n display: () => {\n const elapsed = getTime() - addedTime\n const elapsedStr = formatTime(Math.max(0, elapsed))\n const addedStr = addedTime === 0 ? \"\" : formatTime(addedTime)\n return [elapsedStr, addedStr]\n },\n }\n}\n\nfunction createHostAPI() {\n const messageHandlers = new Map<string, Set<(data: any) => void>>()\n\n const sendMessage = <T extends keyof SupportedOutgoingMessages>(type: T, json: SupportedOutgoingMessages[T]) => {\n const message = { type, json, _: \"p\", __: \"mp\", private: true }\n\n if (\"parent\" in window && window.parent !== window) window.parent.postMessage(message, \"*\")\n\n window.postMessage(message, \"*\")\n\n if (\"webkit\" in window && (window as any).webkit?.messageHandlers?.app) (window as any).webkit.messageHandlers.app.postMessage(message)\n\n if (\"puzzmoMessageString\" in window) (window as any).puzzmoMessageString(JSON.stringify(message))\n\n if (\"ReactNativeWebView\" in window && (window as any).ReactNativeWebView?.postMessage)\n (window as any).ReactNativeWebView.postMessage(JSON.stringify(message))\n\n if (type !== \"TIMER_TICK\" && type !== \"TIMER_SYNC\") console.log(\"[Puzzmo SDK] sent:\", type, json)\n }\n\n const onMessage = <T extends keyof SupportedIncomingMessages>(type: T, handler: MessageHandler<T>) => {\n if (!messageHandlers.has(type)) messageHandlers.set(type, new Set())\n messageHandlers.get(type)!.add(handler)\n\n return () => {\n messageHandlers.get(type)?.delete(handler)\n }\n }\n\n if (typeof window !== \"undefined\") {\n window.addEventListener(\"message\", (event) => {\n if (!event?.data?.type) return\n const msgType = event.data.type as string\n const handlers = messageHandlers.get(msgType)\n if (handlers) {\n const msgData = event.data.data ?? event.data.json ?? {}\n if (msgType !== \"TIMER_TICK\" && msgType !== \"TIMER_SYNC\") console.log(\"[Puzzmo SDK] received:\", msgType, msgData)\n handlers.forEach((handler) => handler(msgData))\n }\n })\n }\n\n return { sendMessage, onMessage }\n}\n\nconst hostAPI = createHostAPI()\n\n/** Creates a Puzzmo SDK instance for communicating with the Puzzmo host */\nexport const createPuzzmoSDK = (options: PuzzmoSDKOptions = {}) => {\n let readyData: MessagesReceived[\"READY_DATA\"] | null = null\n let readyDataResolve: ((data: MessagesReceived[\"READY_DATA\"]) => void) | null = null\n\n const getGameplay = () => readyData?.startOrFindGameplay?.gamePlayed\n const getGameplayID = () => getGameplay()?.id ?? null\n const getPuzzleString = () => getGameplay()?.puzzle.puzzle ?? null\n const getBoardState = () => getGameplay()?.boardState ?? null\n const getTheme = () => readyData?.theme ?? null\n const getCompleted = () => getGameplay()?.completed ?? false\n\n const eventListeners = new Map<SDKEventType, Set<(data?: any) => void>>()\n\n const internalTimer = createTimer()\n let timerTickInterval: ReturnType<typeof setInterval> | null = null\n let timerSyncInterval: ReturnType<typeof setInterval> | null = null\n\n const startTimerIntervals = () => {\n if (timerTickInterval) return\n\n timerTickInterval = setInterval(() => {\n if (internalTimer.isPaused()) return\n const [elapsed, added] = internalTimer.display()\n hostAPI.sendMessage(\"TIMER_TICK\", { display: [elapsed, added] })\n }, 500)\n\n timerSyncInterval = setInterval(() => {\n if (internalTimer.isPaused()) return\n hostAPI.sendMessage(\"TIMER_SYNC\", Math.floor(internalTimer.timeWithoutPenaltySecs()))\n }, 10000)\n }\n\n const stopTimerIntervals = () => {\n if (timerTickInterval) {\n clearInterval(timerTickInterval)\n timerTickInterval = null\n }\n if (timerSyncInterval) {\n clearInterval(timerSyncInterval)\n timerSyncInterval = null\n }\n }\n\n const emit = <T extends SDKEventType>(event: T, data?: SDKEventMap[T]) => {\n const listeners = eventListeners.get(event)\n if (listeners) listeners.forEach((listener) => listener(data))\n }\n\n hostAPI.onMessage(\"START_GAME\", () => {\n internalTimer._init()\n startTimerIntervals()\n emit(\"start\")\n })\n\n hostAPI.onMessage(\"PAUSE_GAME\", () => {\n internalTimer._pause()\n emit(\"pause\")\n })\n\n hostAPI.onMessage(\"RESUME_GAME\", () => {\n internalTimer._resume()\n emit(\"resume\")\n })\n\n hostAPI.onMessage(\"SETTINGS_UPDATE\", (data) => emit(\"settingsUpdate\", data))\n\n hostAPI.onMessage(\"KEYBOARD_KEY_PRESS\", (data) => emit(\"keyboardKeyPress\", data))\n hostAPI.onMessage(\"KEYBOARD_CURSOR_CHANGE\", (data) => emit(\"keyboardCursorChange\", data))\n hostAPI.onMessage(\"KEYBOARD_CURSOR_END\", () => emit(\"keyboardCursorEnd\"))\n\n hostAPI.onMessage(\"RETRY_PUZZLE\", () => {\n internalTimer._reset()\n stopTimerIntervals()\n emit(\"retry\")\n })\n\n hostAPI.onMessage(\"READY_DATA\", (data) => {\n const bootstrapData = data as MessagesReceived[\"READY_DATA\"]\n readyData = bootstrapData\n\n const gamePlayed = bootstrapData.startOrFindGameplay?.gamePlayed\n if (gamePlayed) {\n const existingTime = (gamePlayed.elapsedTimeSecs ?? 0) * 1000\n const existingAddedTime = (gamePlayed.additionalTimeAddedSecs ?? 0) * 1000\n internalTimer._reset(existingTime, existingAddedTime)\n }\n\n if (readyDataResolve) {\n readyDataResolve(bootstrapData)\n readyDataResolve = null\n }\n })\n\n const timer: SDKTimer = {\n timeMs: () => internalTimer.timeMs(),\n timeSecs: () => internalTimer.timeSecs(),\n addedTimeMs: () => internalTimer.addedTimeMs(),\n addedTimeSecs: () => internalTimer.addedTimeSecs(),\n timeWithoutPenaltySecs: () => internalTimer.timeWithoutPenaltySecs(),\n display: () => internalTimer.display(),\n addPenalty: (ms: number) => internalTimer.addPenalty(ms),\n isPaused: () => internalTimer.isPaused(),\n isRunning: () => internalTimer.isRunning(),\n }\n\n return {\n timer,\n\n gameReady: async (): Promise<{\n puzzleString: string\n inputString: string | null\n theme: Theme | null\n completed: boolean\n readyData: MessagesReceived[\"READY_DATA\"] | null\n }> => {\n hostAPI.sendMessage(\"READY\", {})\n\n if (getPuzzleString()) {\n const inputString = getBoardState()\n return {\n puzzleString: getPuzzleString()!,\n inputString,\n boardState: inputString,\n theme: getTheme(),\n completed: getCompleted(),\n readyData,\n } as any\n }\n\n const timeout = options.timeout ?? 5000\n const readyDataPromise = new Promise<MessagesReceived[\"READY_DATA\"]>((resolve, reject) => {\n readyDataResolve = resolve\n setTimeout(() => {\n if (readyDataResolve) {\n readyDataResolve = null\n reject(new Error(`Timeout waiting for READY_DATA after ${timeout}ms`))\n }\n }, timeout)\n })\n\n await readyDataPromise\n\n const puzzleString = getPuzzleString()\n if (!puzzleString) throw new Error(\"READY_DATA received but no puzzle data found\")\n\n const inputString = getBoardState()\n return {\n puzzleString,\n inputString,\n boardState: inputString,\n theme: getTheme(),\n completed: getCompleted(),\n readyData,\n } as any\n },\n\n gameLoaded: (state: any = {}) => {\n hostAPI.sendMessage(\"READY_GAME_LOADED\", {\n state,\n gameRuntimeContract: \"1.0\",\n embedRuntimeContract: \"1.0\",\n })\n },\n\n on: <T extends SDKEventType>(event: T, listener: (data?: SDKEventMap[T]) => void): (() => void) => {\n if (!eventListeners.has(event)) eventListeners.set(event, new Set())\n eventListeners.get(event)!.add(listener)\n return () => {\n eventListeners.get(event)?.delete(listener)\n }\n },\n\n off: <T extends SDKEventType>(event: T, listener: (data?: SDKEventMap[T]) => void) => {\n eventListeners.get(event)?.delete(listener)\n },\n\n updateGameState: (inputString: string, play?: Partial<GamePlay>) => {\n const gameplayID = getGameplayID()\n if (!gameplayID) return\n\n hostAPI.sendMessage(\"UPLOAD_NEW_GAME_STATE\", {\n id: gameplayID,\n input: {\n boardState: inputString,\n elapsedTimeSecs: play?.elapsedTimeSecs ?? internalTimer.timeWithoutPenaltySecs(),\n additionalTimeAddedSecs: play?.additionalTimeAddedSecs ?? internalTimer.addedTimeSecs(),\n collabUserReferences: [],\n },\n })\n },\n\n gameCompleted: (play: Partial<GamePlay>, config?: AugmentationConfig) => {\n internalTimer._conclude()\n stopTimerIntervals()\n\n const finalPlay: Partial<GamePlay> = {\n ...play,\n elapsedTimeSecs: play.elapsedTimeSecs ?? internalTimer.timeWithoutPenaltySecs(),\n additionalTimeAddedSecs: play.additionalTimeAddedSecs ?? internalTimer.addedTimeSecs(),\n }\n\n const deeds: Deed[] = (config?.deeds as any) ?? []\n deeds.push({ id: \"points\", value: play.pointsAwarded })\n deeds.push({\n id: \"time\",\n value: Math.round(finalPlay.elapsedTimeSecs ?? 0) + Math.round(finalPlay.additionalTimeAddedSecs ?? 0),\n })\n\n const gameplayID = getGameplayID()\n if (gameplayID) {\n hostAPI.sendMessage(\"GAME_COMPLETED\", {\n id: gameplayID,\n input: finalPlay,\n config,\n })\n }\n },\n\n showCompletionScreen: (results: any[], gameplay: GamePlay, showRetry = true) => {\n hostAPI.sendMessage(\"SHOW_GAME_COMPLETE_SCREEN\", {\n results,\n showRetry,\n gameplay,\n })\n },\n\n hitCheckpoint: (checkpointName: string, checkpointConfig: CheckpointConfig, config?: AugmentationConfig) => {\n const gameplayID = getGameplayID()\n if (!gameplayID) return\n\n const inputStr = getBoardState() ?? \"\"\n const play: Partial<GamePlay> = {\n elapsedTimeSecs: internalTimer.timeWithoutPenaltySecs(),\n additionalTimeAddedSecs: internalTimer.addedTimeSecs(),\n }\n\n hostAPI.sendMessage(\"HIT_CHECKPOINT\", {\n checkpointName,\n gameplay: { inputStr, play },\n checkpointConfig,\n augConfig: config ?? {},\n })\n },\n\n keyboard: {\n /** Show the on-screen keyboard with the given config. Call again to update state (e.g. to change disabled keys). */\n show: (config: KeyboardConfig) => {\n hostAPI.sendMessage(\"KEYBOARD_UPDATE_CONFIG\", config)\n },\n /** Hide the on-screen keyboard. */\n hide: () => {\n hostAPI.sendMessage(\"KEYBOARD_UPDATE_CONFIG\", {\n layout: [],\n symbols: {},\n highlight: [],\n disabled: [],\n xl: [],\n l: [],\n supportsDragCursor: false,\n })\n },\n },\n\n _hostAPI: hostAPI,\n }\n}\n","import type { KeyboardConfig } from \"./types\"\n\n/**\n * A standard QWERTY layout with Enter and Backspace — a reasonable default for\n * any game that needs text input. Customize from here by spreading and overriding.\n *\n * @example\n * // Use as-is\n * sdk.keyboard.show(defaultKeyboardConfig)\n *\n * @example\n * // Extend with dynamic disabled letters\n * sdk.keyboard.show({ ...defaultKeyboardConfig, disabled: usedLetters })\n */\nexport const defaultKeyboardConfig: KeyboardConfig = {\n layout: [\"qwertyuiop\", \"asdfghjkl\", \"↵zxcvbnm⌫\", undefined],\n symbols: { \"↵\": \"Enter\", \"⌫\": \"bsp\" },\n highlight: [\"↵\", \"⌫\"],\n disabled: [],\n xl: [],\n l: [\"↵\", \"⌫\"],\n supportsDragCursor: false,\n}\n","/** Severity level for validation issues */\nexport type ValidationLevel = \"error\" | \"warning\" | \"info\"\n\n/** Represents a single validation issue found during puzzle validation */\nexport interface ValidationIssue {\n level: ValidationLevel\n message: string\n line?: number\n col?: number\n length?: number\n}\n\n/** Complete validation report for a puzzle */\nexport interface ValidationReport {\n success: boolean\n issues: ValidationIssue[]\n}\n\nexport type ImportErrorType = \"invalid_format\" | \"parsing_error\" | \"unknown\"\n\n/** Custom error class for workshop import failures */\nexport class EditorImportError extends Error {\n constructor(\n public type: ImportErrorType,\n message: string,\n public originalError?: unknown,\n ) {\n super(message)\n this.name = \"EditorImportError\"\n }\n}\n\n/** Result of a successful puzzle import operation */\nexport interface ImportResult {\n data: string\n warnings?: ValidationIssue[]\n title?: string\n authors?: string[]\n editors?: string[]\n}\n\n/** Settings UI descriptor returned by an editor bundle */\nexport interface EditorBundleSettings<TComponent = unknown> {\n components: TComponent[]\n defaults: Record<string, unknown>\n}\n\n/** Main interface for a Workshop bundle */\nexport interface EditorBundle<TSettingsComponent = unknown> {\n validator: {\n validate(data: string): Promise<ValidationReport> | ValidationReport\n }\n importer?: {\n onImport(filename: string, contents: string | ArrayBuffer): Promise<ImportResult> | ImportResult\n }\n /** Embed-level settings UI, populated from the bundle's declared settings */\n settings?: EditorBundleSettings<TSettingsComponent>\n /** Editor-level settings UI, populated from the bundle's declared editor settings */\n editorSettings?: EditorBundleSettings<TSettingsComponent>\n /** Custom puzzle editor, if provided by the bundle */\n editor?: {\n mount(...args: unknown[]): unknown\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":[],"sources":["../src/sdk.ts","../src/keyboard.ts","../src/editor.ts"],"sourcesContent":["import type {\n MessagesSentFromEmbed,\n MessagesReceived,\n GamePlay,\n AugmentationConfig,\n CheckpointConfig,\n Theme,\n Deed,\n KeyboardConfig,\n} from \"./types\"\n\nexport type SDK = ReturnType<typeof createPuzzmoSDK>\n\nexport interface PuzzmoSDKOptions {\n /** Optional timeout in ms to wait for READY_DATA (default: 5000) */\n timeout?: number\n}\n\ntype SupportedOutgoingMessages = Pick<\n MessagesSentFromEmbed,\n | \"READY\"\n | \"READY_GAME_LOADED\"\n | \"GAME_COMPLETED\"\n | \"SHOW_GAME_COMPLETE_SCREEN\"\n | \"TIMER_TICK\"\n | \"TIMER_SYNC\"\n | \"UPLOAD_NEW_GAME_STATE\"\n | \"HIT_CHECKPOINT\"\n | \"KEYBOARD_UPDATE_CONFIG\"\n>\n\ntype SupportedIncomingMessages = Pick<\n MessagesReceived,\n | \"READY_DATA\"\n | \"START_GAME\"\n | \"PAUSE_GAME\"\n | \"RESUME_GAME\"\n | \"SETTINGS_UPDATE\"\n | \"RETRY_PUZZLE\"\n | \"KEYBOARD_KEY_PRESS\"\n | \"KEYBOARD_CURSOR_CHANGE\"\n | \"KEYBOARD_CURSOR_END\"\n>\n\ntype MessageHandler<T extends keyof SupportedIncomingMessages> = (data: SupportedIncomingMessages[T]) => void\n\nexport type SDKEventMap = {\n start: void\n pause: void\n resume: void\n retry: void\n settingsUpdate: any\n /** A key on the on-screen keyboard was tapped. */\n keyboardKeyPress: { key: string }\n /** The drag cursor moved across the keyboard. Only fires when `supportsDragCursor` is true. */\n keyboardCursorChange: { position: [number, number] }\n /** The drag cursor was released. Only fires when `supportsDragCursor` is true. */\n keyboardCursorEnd: void\n}\n\nexport type SDKEventType = keyof SDKEventMap\n\nexport interface SDKTimer {\n /** Get elapsed time in milliseconds */\n timeMs: () => number\n /** Get elapsed time in seconds */\n timeSecs: () => number\n /** Get added/penalty time in milliseconds */\n addedTimeMs: () => number\n /** Get added/penalty time in seconds */\n addedTimeSecs: () => number\n /** Get elapsed time without penalties in seconds */\n timeWithoutPenaltySecs: () => number\n /** Get formatted display strings [elapsed, added] */\n display: () => [string, string]\n /** Add penalty time in milliseconds */\n addPenalty: (ms: number) => void\n /** Check if timer is paused */\n isPaused: () => boolean\n /** Check if timer has been started */\n isRunning: () => boolean\n}\n\nfunction formatTime(timeMs: number): string {\n const isAnHourOrMore = timeMs >= 60 * 60 * 1000\n return new Date(timeMs)\n .toISOString()\n .slice(isAnHourOrMore ? 11 : 14, -1)\n .split(\".\")[0]\n}\n\nfunction createTimer(\n initialTimeMs = 0,\n initialAddedTimeMs = 0,\n): SDKTimer & {\n _init: () => void\n _pause: () => void\n _resume: () => void\n _reset: (initialTimeMs?: number, initialAddedTimeMs?: number) => void\n _conclude: () => void\n} {\n let baseTime = initialTimeMs\n let addedTime = initialAddedTimeMs\n let pausedTime = 0\n let concludeTime: number | undefined\n\n let startDate: number | undefined = undefined\n let pausedDate: number | undefined = undefined\n\n const getTime = (): number => {\n if (startDate === undefined) return baseTime + addedTime\n if (concludeTime !== undefined) return concludeTime\n const now = pausedDate ?? performance.now()\n const elapsed = now - startDate - pausedTime\n return baseTime + addedTime + elapsed\n }\n\n return {\n _init: () => {\n if (startDate === undefined) {\n startDate = performance.now()\n pausedDate = undefined\n }\n },\n _pause: () => {\n if (pausedDate !== undefined || startDate === undefined) return\n pausedDate = performance.now()\n },\n _resume: () => {\n if (pausedDate === undefined) return\n pausedTime += performance.now() - pausedDate\n pausedDate = undefined\n },\n _reset: (newInitialTimeMs = 0, newInitialAddedTimeMs = 0) => {\n baseTime = newInitialTimeMs\n addedTime = newInitialAddedTimeMs\n pausedTime = 0\n concludeTime = undefined\n startDate = undefined\n pausedDate = undefined\n },\n _conclude: () => {\n if (pausedDate !== undefined) {\n concludeTime = getTime()\n return\n }\n if (startDate === undefined) {\n concludeTime = baseTime + addedTime\n return\n }\n concludeTime = getTime()\n },\n timeMs: () => getTime(),\n timeSecs: () => getTime() / 1000,\n addedTimeMs: () => addedTime,\n addedTimeSecs: () => addedTime / 1000,\n timeWithoutPenaltySecs: () => (getTime() - addedTime) / 1000,\n addPenalty: (ms: number) => {\n addedTime += ms\n },\n isPaused: () => pausedDate !== undefined || startDate === undefined,\n isRunning: () => startDate !== undefined && pausedDate === undefined,\n display: () => {\n const elapsed = getTime() - addedTime\n const elapsedStr = formatTime(Math.max(0, elapsed))\n const addedStr = addedTime === 0 ? \"\" : formatTime(addedTime)\n return [elapsedStr, addedStr]\n },\n }\n}\n\nfunction createHostAPI() {\n const messageHandlers = new Map<string, Set<(data: any) => void>>()\n\n const sendMessage = <T extends keyof SupportedOutgoingMessages>(type: T, json: SupportedOutgoingMessages[T]) => {\n const message = { type, json, _: \"p\", __: \"mp\", private: true }\n\n if (\"parent\" in window && window.parent !== window) window.parent.postMessage(message, \"*\")\n\n window.postMessage(message, \"*\")\n\n if (\"webkit\" in window && (window as any).webkit?.messageHandlers?.app) (window as any).webkit.messageHandlers.app.postMessage(message)\n\n if (\"puzzmoMessageString\" in window) (window as any).puzzmoMessageString(JSON.stringify(message))\n\n if (\"ReactNativeWebView\" in window && (window as any).ReactNativeWebView?.postMessage)\n (window as any).ReactNativeWebView.postMessage(JSON.stringify(message))\n\n if (type !== \"TIMER_TICK\" && type !== \"TIMER_SYNC\") console.log(\"[Puzzmo SDK] sent:\", type, json)\n }\n\n const onMessage = <T extends keyof SupportedIncomingMessages>(type: T, handler: MessageHandler<T>) => {\n if (!messageHandlers.has(type)) messageHandlers.set(type, new Set())\n messageHandlers.get(type)!.add(handler)\n\n return () => {\n messageHandlers.get(type)?.delete(handler)\n }\n }\n\n if (typeof window !== \"undefined\") {\n window.addEventListener(\"message\", (event) => {\n if (!event?.data?.type) return\n const msgType = event.data.type as string\n const handlers = messageHandlers.get(msgType)\n if (handlers) {\n const msgData = event.data.data ?? event.data.json ?? {}\n if (msgType !== \"TIMER_TICK\" && msgType !== \"TIMER_SYNC\") console.log(\"[Puzzmo SDK] received:\", msgType, msgData)\n handlers.forEach((handler) => handler(msgData))\n }\n })\n }\n\n return { sendMessage, onMessage }\n}\n\nconst hostAPI = createHostAPI()\n\n/** Creates a Puzzmo SDK instance for communicating with the Puzzmo host */\nexport const createPuzzmoSDK = (options: PuzzmoSDKOptions = {}) => {\n let readyData: MessagesReceived[\"READY_DATA\"] | null = null\n let readyDataResolve: ((data: MessagesReceived[\"READY_DATA\"]) => void) | null = null\n\n const getGameplay = () => readyData?.startOrFindGameplay?.gamePlayed\n const getGameplayID = () => getGameplay()?.id ?? null\n const getPuzzleString = () => getGameplay()?.puzzle.puzzle ?? null\n const getBoardState = () => getGameplay()?.boardState ?? null\n const getTheme = () => readyData?.theme ?? null\n const getCompleted = () => getGameplay()?.completed ?? false\n\n const eventListeners = new Map<SDKEventType, Set<(data?: any) => void>>()\n\n const internalTimer = createTimer()\n let timerTickInterval: ReturnType<typeof setInterval> | null = null\n let timerSyncInterval: ReturnType<typeof setInterval> | null = null\n\n const startTimerIntervals = () => {\n if (timerTickInterval) return\n\n timerTickInterval = setInterval(() => {\n if (internalTimer.isPaused()) return\n const [elapsed, added] = internalTimer.display()\n hostAPI.sendMessage(\"TIMER_TICK\", { display: [elapsed, added] })\n }, 500)\n\n timerSyncInterval = setInterval(() => {\n if (internalTimer.isPaused()) return\n hostAPI.sendMessage(\"TIMER_SYNC\", Math.floor(internalTimer.timeWithoutPenaltySecs()))\n }, 10000)\n }\n\n const stopTimerIntervals = () => {\n if (timerTickInterval) {\n clearInterval(timerTickInterval)\n timerTickInterval = null\n }\n if (timerSyncInterval) {\n clearInterval(timerSyncInterval)\n timerSyncInterval = null\n }\n }\n\n const emit = <T extends SDKEventType>(event: T, data?: SDKEventMap[T]) => {\n const listeners = eventListeners.get(event)\n if (listeners) listeners.forEach((listener) => listener(data))\n }\n\n hostAPI.onMessage(\"START_GAME\", () => {\n internalTimer._init()\n startTimerIntervals()\n emit(\"start\")\n })\n\n hostAPI.onMessage(\"PAUSE_GAME\", () => {\n internalTimer._pause()\n emit(\"pause\")\n })\n\n hostAPI.onMessage(\"RESUME_GAME\", () => {\n internalTimer._resume()\n emit(\"resume\")\n })\n\n hostAPI.onMessage(\"SETTINGS_UPDATE\", (data) => emit(\"settingsUpdate\", data))\n\n hostAPI.onMessage(\"KEYBOARD_KEY_PRESS\", (data) => emit(\"keyboardKeyPress\", data))\n hostAPI.onMessage(\"KEYBOARD_CURSOR_CHANGE\", (data) => emit(\"keyboardCursorChange\", data))\n hostAPI.onMessage(\"KEYBOARD_CURSOR_END\", () => emit(\"keyboardCursorEnd\"))\n\n hostAPI.onMessage(\"RETRY_PUZZLE\", () => {\n internalTimer._reset()\n stopTimerIntervals()\n emit(\"retry\")\n })\n\n hostAPI.onMessage(\"READY_DATA\", (data) => {\n const bootstrapData = data as MessagesReceived[\"READY_DATA\"]\n readyData = bootstrapData\n\n const gamePlayed = bootstrapData.startOrFindGameplay?.gamePlayed\n if (gamePlayed) {\n const existingTime = (gamePlayed.elapsedTimeSecs ?? 0) * 1000\n const existingAddedTime = (gamePlayed.additionalTimeAddedSecs ?? 0) * 1000\n internalTimer._reset(existingTime, existingAddedTime)\n }\n\n if (readyDataResolve) {\n readyDataResolve(bootstrapData)\n readyDataResolve = null\n }\n })\n\n const timer: SDKTimer = {\n timeMs: () => internalTimer.timeMs(),\n timeSecs: () => internalTimer.timeSecs(),\n addedTimeMs: () => internalTimer.addedTimeMs(),\n addedTimeSecs: () => internalTimer.addedTimeSecs(),\n timeWithoutPenaltySecs: () => internalTimer.timeWithoutPenaltySecs(),\n display: () => internalTimer.display(),\n addPenalty: (ms: number) => internalTimer.addPenalty(ms),\n isPaused: () => internalTimer.isPaused(),\n isRunning: () => internalTimer.isRunning(),\n }\n\n return {\n timer,\n\n gameReady: async (): Promise<{\n puzzleString: string\n inputString: string | null\n theme: Theme | null\n completed: boolean\n readyData: MessagesReceived[\"READY_DATA\"] | null\n }> => {\n hostAPI.sendMessage(\"READY\", {})\n\n if (getPuzzleString()) {\n const inputString = getBoardState()\n return {\n puzzleString: getPuzzleString()!,\n inputString,\n // @ts-expect-error - this is backwards compat\n boardState: inputString,\n theme: getTheme(),\n completed: getCompleted(),\n readyData,\n }\n }\n\n const timeout = options.timeout ?? 5000\n const readyDataPromise = new Promise<MessagesReceived[\"READY_DATA\"]>((resolve, reject) => {\n readyDataResolve = resolve\n setTimeout(() => {\n if (readyDataResolve) {\n readyDataResolve = null\n reject(new Error(`Timeout waiting for READY_DATA after ${timeout}ms`))\n }\n }, timeout)\n })\n\n await readyDataPromise\n\n const puzzleString = getPuzzleString()\n if (!puzzleString) throw new Error(\"READY_DATA received but no puzzle data found\")\n\n const inputString = getBoardState()\n return {\n puzzleString,\n inputString,\n // @ts-expect-error - this is backwards compat\n boardState: inputString,\n theme: getTheme(),\n completed: getCompleted(),\n readyData,\n }\n },\n\n gameLoaded: (state: any = {}) => {\n hostAPI.sendMessage(\"READY_GAME_LOADED\", {\n state,\n gameRuntimeContract: \"1.0\",\n embedRuntimeContract: \"1.0\",\n })\n },\n\n on: <T extends SDKEventType>(event: T, listener: (data?: SDKEventMap[T]) => void): (() => void) => {\n if (!eventListeners.has(event)) eventListeners.set(event, new Set())\n eventListeners.get(event)!.add(listener)\n return () => {\n eventListeners.get(event)?.delete(listener)\n }\n },\n\n off: <T extends SDKEventType>(event: T, listener: (data?: SDKEventMap[T]) => void) => {\n eventListeners.get(event)?.delete(listener)\n },\n\n updateGameState: (inputString: string, play?: Partial<GamePlay>) => {\n const gameplayID = getGameplayID()\n if (!gameplayID) return\n\n hostAPI.sendMessage(\"UPLOAD_NEW_GAME_STATE\", {\n id: gameplayID,\n input: {\n boardState: inputString,\n elapsedTimeSecs: play?.elapsedTimeSecs ?? internalTimer.timeWithoutPenaltySecs(),\n additionalTimeAddedSecs: play?.additionalTimeAddedSecs ?? internalTimer.addedTimeSecs(),\n collabUserReferences: [],\n },\n })\n },\n\n gameCompleted: (play: Partial<GamePlay>, config?: AugmentationConfig) => {\n internalTimer._conclude()\n stopTimerIntervals()\n\n const finalPlay: Partial<GamePlay> = {\n ...play,\n elapsedTimeSecs: play.elapsedTimeSecs ?? internalTimer.timeWithoutPenaltySecs(),\n additionalTimeAddedSecs: play.additionalTimeAddedSecs ?? internalTimer.addedTimeSecs(),\n }\n\n const deeds: Deed[] = (config?.deeds as any) ?? []\n deeds.push({ id: \"points\", value: play.pointsAwarded })\n deeds.push({\n id: \"time\",\n value: Math.round(finalPlay.elapsedTimeSecs ?? 0) + Math.round(finalPlay.additionalTimeAddedSecs ?? 0),\n })\n\n const gameplayID = getGameplayID()\n if (gameplayID) {\n hostAPI.sendMessage(\"GAME_COMPLETED\", {\n id: gameplayID,\n input: finalPlay,\n config,\n })\n }\n },\n\n showCompletionScreen: (results: any[], gameplay: GamePlay, showRetry = true) => {\n hostAPI.sendMessage(\"SHOW_GAME_COMPLETE_SCREEN\", {\n results,\n showRetry,\n gameplay,\n })\n },\n\n hitCheckpoint: (checkpointName: string, checkpointConfig: CheckpointConfig, config?: AugmentationConfig) => {\n const gameplayID = getGameplayID()\n if (!gameplayID) return\n\n const inputStr = getBoardState() ?? \"\"\n const play: Partial<GamePlay> = {\n elapsedTimeSecs: internalTimer.timeWithoutPenaltySecs(),\n additionalTimeAddedSecs: internalTimer.addedTimeSecs(),\n }\n\n hostAPI.sendMessage(\"HIT_CHECKPOINT\", {\n checkpointName,\n gameplay: { inputStr, play },\n checkpointConfig,\n augConfig: config ?? {},\n })\n },\n\n keyboard: {\n /** Show the on-screen keyboard with the given config. Call again to update state (e.g. to change disabled keys). */\n show: (config: KeyboardConfig) => {\n hostAPI.sendMessage(\"KEYBOARD_UPDATE_CONFIG\", config)\n },\n /** Hide the on-screen keyboard. */\n hide: () => {\n hostAPI.sendMessage(\"KEYBOARD_UPDATE_CONFIG\", {\n layout: [],\n symbols: {},\n highlight: [],\n disabled: [],\n xl: [],\n l: [],\n supportsDragCursor: false,\n })\n },\n },\n\n _hostAPI: hostAPI,\n }\n}\n","import type { KeyboardConfig } from \"./types\"\n\n/**\n * A standard QWERTY layout with Enter and Backspace — a reasonable default for\n * any game that needs text input. Customize from here by spreading and overriding.\n *\n * @example\n * // Use as-is\n * sdk.keyboard.show(defaultKeyboardConfig)\n *\n * @example\n * // Extend with dynamic disabled letters\n * sdk.keyboard.show({ ...defaultKeyboardConfig, disabled: usedLetters })\n */\nexport const defaultKeyboardConfig: KeyboardConfig = {\n layout: [\"qwertyuiop\", \"asdfghjkl\", \"↵zxcvbnm⌫\", undefined],\n symbols: { \"↵\": \"Enter\", \"⌫\": \"bsp\" },\n highlight: [\"↵\", \"⌫\"],\n disabled: [],\n xl: [],\n l: [\"↵\", \"⌫\"],\n supportsDragCursor: false,\n}\n","/** Severity level for validation issues */\nexport type ValidationLevel = \"error\" | \"warning\" | \"info\"\n\n/** Represents a single validation issue found during puzzle validation */\nexport interface ValidationIssue {\n level: ValidationLevel\n message: string\n line?: number\n col?: number\n length?: number\n}\n\n/** Complete validation report for a puzzle */\nexport interface ValidationReport {\n success: boolean\n issues: ValidationIssue[]\n}\n\nexport type ImportErrorType = \"invalid_format\" | \"parsing_error\" | \"unknown\"\n\n/** Custom error class for workshop import failures */\nexport class EditorImportError extends Error {\n constructor(\n public type: ImportErrorType,\n message: string,\n public originalError?: unknown,\n ) {\n super(message)\n this.name = \"EditorImportError\"\n }\n}\n\n/** Result of a successful puzzle import operation */\nexport interface ImportResult {\n data: string\n warnings?: ValidationIssue[]\n title?: string\n authors?: string[]\n editors?: string[]\n}\n\n/** Settings UI descriptor returned by an editor bundle */\nexport interface EditorBundleSettings<TComponent = unknown> {\n components: TComponent[]\n defaults: Record<string, unknown>\n}\n\n/** Main interface for a Workshop bundle */\nexport interface EditorBundle<TSettingsComponent = unknown> {\n validator: {\n validate(data: string): Promise<ValidationReport> | ValidationReport\n }\n importer?: {\n onImport(filename: string, contents: string | ArrayBuffer): Promise<ImportResult> | ImportResult\n }\n /** Embed-level settings UI, populated from the bundle's declared settings */\n settings?: EditorBundleSettings<TSettingsComponent>\n /** Editor-level settings UI, populated from the bundle's declared editor settings */\n editorSettings?: EditorBundleSettings<TSettingsComponent>\n /** Custom puzzle editor, if provided by the bundle */\n editor?: {\n mount(...args: unknown[]): unknown\n }\n}\n"],"mappings":"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"}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/sdk.ts","../src/keyboard.ts","../src/editor.ts"],"sourcesContent":["import type {\n MessagesSentFromEmbed,\n MessagesReceived,\n GamePlay,\n AugmentationConfig,\n CheckpointConfig,\n Theme,\n Deed,\n KeyboardConfig,\n} from \"./types\"\n\nexport type SDK = ReturnType<typeof createPuzzmoSDK>\n\nexport interface PuzzmoSDKOptions {\n /** Optional timeout in ms to wait for READY_DATA (default: 5000) */\n timeout?: number\n}\n\ntype SupportedOutgoingMessages = Pick<\n MessagesSentFromEmbed,\n | \"READY\"\n | \"READY_GAME_LOADED\"\n | \"GAME_COMPLETED\"\n | \"SHOW_GAME_COMPLETE_SCREEN\"\n | \"TIMER_TICK\"\n | \"TIMER_SYNC\"\n | \"UPLOAD_NEW_GAME_STATE\"\n | \"HIT_CHECKPOINT\"\n | \"KEYBOARD_UPDATE_CONFIG\"\n>\n\ntype SupportedIncomingMessages = Pick<\n MessagesReceived,\n | \"READY_DATA\"\n | \"START_GAME\"\n | \"PAUSE_GAME\"\n | \"RESUME_GAME\"\n | \"SETTINGS_UPDATE\"\n | \"RETRY_PUZZLE\"\n | \"KEYBOARD_KEY_PRESS\"\n | \"KEYBOARD_CURSOR_CHANGE\"\n | \"KEYBOARD_CURSOR_END\"\n>\n\ntype MessageHandler<T extends keyof SupportedIncomingMessages> = (data: SupportedIncomingMessages[T]) => void\n\nexport type SDKEventMap = {\n start: void\n pause: void\n resume: void\n retry: void\n settingsUpdate: any\n /** A key on the on-screen keyboard was tapped. */\n keyboardKeyPress: { key: string }\n /** The drag cursor moved across the keyboard. Only fires when `supportsDragCursor` is true. */\n keyboardCursorChange: { position: [number, number] }\n /** The drag cursor was released. Only fires when `supportsDragCursor` is true. */\n keyboardCursorEnd: void\n}\n\nexport type SDKEventType = keyof SDKEventMap\n\nexport interface SDKTimer {\n /** Get elapsed time in milliseconds */\n timeMs: () => number\n /** Get elapsed time in seconds */\n timeSecs: () => number\n /** Get added/penalty time in milliseconds */\n addedTimeMs: () => number\n /** Get added/penalty time in seconds */\n addedTimeSecs: () => number\n /** Get elapsed time without penalties in seconds */\n timeWithoutPenaltySecs: () => number\n /** Get formatted display strings [elapsed, added] */\n display: () => [string, string]\n /** Add penalty time in milliseconds */\n addPenalty: (ms: number) => void\n /** Check if timer is paused */\n isPaused: () => boolean\n /** Check if timer has been started */\n isRunning: () => boolean\n}\n\nfunction formatTime(timeMs: number): string {\n const isAnHourOrMore = timeMs >= 60 * 60 * 1000\n return new Date(timeMs)\n .toISOString()\n .slice(isAnHourOrMore ? 11 : 14, -1)\n .split(\".\")[0]\n}\n\nfunction createTimer(\n initialTimeMs = 0,\n initialAddedTimeMs = 0,\n): SDKTimer & {\n _init: () => void\n _pause: () => void\n _resume: () => void\n _reset: (initialTimeMs?: number, initialAddedTimeMs?: number) => void\n _conclude: () => void\n} {\n let baseTime = initialTimeMs\n let addedTime = initialAddedTimeMs\n let pausedTime = 0\n let concludeTime: number | undefined\n\n let startDate: number | undefined = undefined\n let pausedDate: number | undefined = undefined\n\n const getTime = (): number => {\n if (startDate === undefined) return baseTime + addedTime\n if (concludeTime !== undefined) return concludeTime\n const now = pausedDate ?? performance.now()\n const elapsed = now - startDate - pausedTime\n return baseTime + addedTime + elapsed\n }\n\n return {\n _init: () => {\n if (startDate === undefined) {\n startDate = performance.now()\n pausedDate = undefined\n }\n },\n _pause: () => {\n if (pausedDate !== undefined || startDate === undefined) return\n pausedDate = performance.now()\n },\n _resume: () => {\n if (pausedDate === undefined) return\n pausedTime += performance.now() - pausedDate\n pausedDate = undefined\n },\n _reset: (newInitialTimeMs = 0, newInitialAddedTimeMs = 0) => {\n baseTime = newInitialTimeMs\n addedTime = newInitialAddedTimeMs\n pausedTime = 0\n concludeTime = undefined\n startDate = undefined\n pausedDate = undefined\n },\n _conclude: () => {\n if (pausedDate !== undefined) {\n concludeTime = getTime()\n return\n }\n if (startDate === undefined) {\n concludeTime = baseTime + addedTime\n return\n }\n concludeTime = getTime()\n },\n timeMs: () => getTime(),\n timeSecs: () => getTime() / 1000,\n addedTimeMs: () => addedTime,\n addedTimeSecs: () => addedTime / 1000,\n timeWithoutPenaltySecs: () => (getTime() - addedTime) / 1000,\n addPenalty: (ms: number) => {\n addedTime += ms\n },\n isPaused: () => pausedDate !== undefined || startDate === undefined,\n isRunning: () => startDate !== undefined && pausedDate === undefined,\n display: () => {\n const elapsed = getTime() - addedTime\n const elapsedStr = formatTime(Math.max(0, elapsed))\n const addedStr = addedTime === 0 ? \"\" : formatTime(addedTime)\n return [elapsedStr, addedStr]\n },\n }\n}\n\nfunction createHostAPI() {\n const messageHandlers = new Map<string, Set<(data: any) => void>>()\n\n const sendMessage = <T extends keyof SupportedOutgoingMessages>(type: T, json: SupportedOutgoingMessages[T]) => {\n const message = { type, json, _: \"p\", __: \"mp\", private: true }\n\n if (\"parent\" in window && window.parent !== window) window.parent.postMessage(message, \"*\")\n\n window.postMessage(message, \"*\")\n\n if (\"webkit\" in window && (window as any).webkit?.messageHandlers?.app) (window as any).webkit.messageHandlers.app.postMessage(message)\n\n if (\"puzzmoMessageString\" in window) (window as any).puzzmoMessageString(JSON.stringify(message))\n\n if (\"ReactNativeWebView\" in window && (window as any).ReactNativeWebView?.postMessage)\n (window as any).ReactNativeWebView.postMessage(JSON.stringify(message))\n\n if (type !== \"TIMER_TICK\" && type !== \"TIMER_SYNC\") console.log(\"[Puzzmo SDK] sent:\", type, json)\n }\n\n const onMessage = <T extends keyof SupportedIncomingMessages>(type: T, handler: MessageHandler<T>) => {\n if (!messageHandlers.has(type)) messageHandlers.set(type, new Set())\n messageHandlers.get(type)!.add(handler)\n\n return () => {\n messageHandlers.get(type)?.delete(handler)\n }\n }\n\n if (typeof window !== \"undefined\") {\n window.addEventListener(\"message\", (event) => {\n if (!event?.data?.type) return\n const msgType = event.data.type as string\n const handlers = messageHandlers.get(msgType)\n if (handlers) {\n const msgData = event.data.data ?? event.data.json ?? {}\n if (msgType !== \"TIMER_TICK\" && msgType !== \"TIMER_SYNC\") console.log(\"[Puzzmo SDK] received:\", msgType, msgData)\n handlers.forEach((handler) => handler(msgData))\n }\n })\n }\n\n return { sendMessage, onMessage }\n}\n\nconst hostAPI = createHostAPI()\n\n/** Creates a Puzzmo SDK instance for communicating with the Puzzmo host */\nexport const createPuzzmoSDK = (options: PuzzmoSDKOptions = {}) => {\n let readyData: MessagesReceived[\"READY_DATA\"] | null = null\n let readyDataResolve: ((data: MessagesReceived[\"READY_DATA\"]) => void) | null = null\n\n const getGameplay = () => readyData?.startOrFindGameplay?.gamePlayed\n const getGameplayID = () => getGameplay()?.id ?? null\n const getPuzzleString = () => getGameplay()?.puzzle.puzzle ?? null\n const getBoardState = () => getGameplay()?.boardState ?? null\n const getTheme = () => readyData?.theme ?? null\n const getCompleted = () => getGameplay()?.completed ?? false\n\n const eventListeners = new Map<SDKEventType, Set<(data?: any) => void>>()\n\n const internalTimer = createTimer()\n let timerTickInterval: ReturnType<typeof setInterval> | null = null\n let timerSyncInterval: ReturnType<typeof setInterval> | null = null\n\n const startTimerIntervals = () => {\n if (timerTickInterval) return\n\n timerTickInterval = setInterval(() => {\n if (internalTimer.isPaused()) return\n const [elapsed, added] = internalTimer.display()\n hostAPI.sendMessage(\"TIMER_TICK\", { display: [elapsed, added] })\n }, 500)\n\n timerSyncInterval = setInterval(() => {\n if (internalTimer.isPaused()) return\n hostAPI.sendMessage(\"TIMER_SYNC\", Math.floor(internalTimer.timeWithoutPenaltySecs()))\n }, 10000)\n }\n\n const stopTimerIntervals = () => {\n if (timerTickInterval) {\n clearInterval(timerTickInterval)\n timerTickInterval = null\n }\n if (timerSyncInterval) {\n clearInterval(timerSyncInterval)\n timerSyncInterval = null\n }\n }\n\n const emit = <T extends SDKEventType>(event: T, data?: SDKEventMap[T]) => {\n const listeners = eventListeners.get(event)\n if (listeners) listeners.forEach((listener) => listener(data))\n }\n\n hostAPI.onMessage(\"START_GAME\", () => {\n internalTimer._init()\n startTimerIntervals()\n emit(\"start\")\n })\n\n hostAPI.onMessage(\"PAUSE_GAME\", () => {\n internalTimer._pause()\n emit(\"pause\")\n })\n\n hostAPI.onMessage(\"RESUME_GAME\", () => {\n internalTimer._resume()\n emit(\"resume\")\n })\n\n hostAPI.onMessage(\"SETTINGS_UPDATE\", (data) => emit(\"settingsUpdate\", data))\n\n hostAPI.onMessage(\"KEYBOARD_KEY_PRESS\", (data) => emit(\"keyboardKeyPress\", data))\n hostAPI.onMessage(\"KEYBOARD_CURSOR_CHANGE\", (data) => emit(\"keyboardCursorChange\", data))\n hostAPI.onMessage(\"KEYBOARD_CURSOR_END\", () => emit(\"keyboardCursorEnd\"))\n\n hostAPI.onMessage(\"RETRY_PUZZLE\", () => {\n internalTimer._reset()\n stopTimerIntervals()\n emit(\"retry\")\n })\n\n hostAPI.onMessage(\"READY_DATA\", (data) => {\n const bootstrapData = data as MessagesReceived[\"READY_DATA\"]\n readyData = bootstrapData\n\n const gamePlayed = bootstrapData.startOrFindGameplay?.gamePlayed\n if (gamePlayed) {\n const existingTime = (gamePlayed.elapsedTimeSecs ?? 0) * 1000\n const existingAddedTime = (gamePlayed.additionalTimeAddedSecs ?? 0) * 1000\n internalTimer._reset(existingTime, existingAddedTime)\n }\n\n if (readyDataResolve) {\n readyDataResolve(bootstrapData)\n readyDataResolve = null\n }\n })\n\n const timer: SDKTimer = {\n timeMs: () => internalTimer.timeMs(),\n timeSecs: () => internalTimer.timeSecs(),\n addedTimeMs: () => internalTimer.addedTimeMs(),\n addedTimeSecs: () => internalTimer.addedTimeSecs(),\n timeWithoutPenaltySecs: () => internalTimer.timeWithoutPenaltySecs(),\n display: () => internalTimer.display(),\n addPenalty: (ms: number) => internalTimer.addPenalty(ms),\n isPaused: () => internalTimer.isPaused(),\n isRunning: () => internalTimer.isRunning(),\n }\n\n return {\n timer,\n\n gameReady: async (): Promise<{\n puzzleString: string\n inputString: string | null\n theme: Theme | null\n completed: boolean\n readyData: MessagesReceived[\"READY_DATA\"] | null\n }> => {\n hostAPI.sendMessage(\"READY\", {})\n\n if (getPuzzleString()) {\n const inputString = getBoardState()\n return {\n puzzleString: getPuzzleString()!,\n inputString,\n boardState: inputString,\n theme: getTheme(),\n completed: getCompleted(),\n readyData,\n } as any\n }\n\n const timeout = options.timeout ?? 5000\n const readyDataPromise = new Promise<MessagesReceived[\"READY_DATA\"]>((resolve, reject) => {\n readyDataResolve = resolve\n setTimeout(() => {\n if (readyDataResolve) {\n readyDataResolve = null\n reject(new Error(`Timeout waiting for READY_DATA after ${timeout}ms`))\n }\n }, timeout)\n })\n\n await readyDataPromise\n\n const puzzleString = getPuzzleString()\n if (!puzzleString) throw new Error(\"READY_DATA received but no puzzle data found\")\n\n const inputString = getBoardState()\n return {\n puzzleString,\n inputString,\n boardState: inputString,\n theme: getTheme(),\n completed: getCompleted(),\n readyData,\n } as any\n },\n\n gameLoaded: (state: any = {}) => {\n hostAPI.sendMessage(\"READY_GAME_LOADED\", {\n state,\n gameRuntimeContract: \"1.0\",\n embedRuntimeContract: \"1.0\",\n })\n },\n\n on: <T extends SDKEventType>(event: T, listener: (data?: SDKEventMap[T]) => void): (() => void) => {\n if (!eventListeners.has(event)) eventListeners.set(event, new Set())\n eventListeners.get(event)!.add(listener)\n return () => {\n eventListeners.get(event)?.delete(listener)\n }\n },\n\n off: <T extends SDKEventType>(event: T, listener: (data?: SDKEventMap[T]) => void) => {\n eventListeners.get(event)?.delete(listener)\n },\n\n updateGameState: (inputString: string, play?: Partial<GamePlay>) => {\n const gameplayID = getGameplayID()\n if (!gameplayID) return\n\n hostAPI.sendMessage(\"UPLOAD_NEW_GAME_STATE\", {\n id: gameplayID,\n input: {\n boardState: inputString,\n elapsedTimeSecs: play?.elapsedTimeSecs ?? internalTimer.timeWithoutPenaltySecs(),\n additionalTimeAddedSecs: play?.additionalTimeAddedSecs ?? internalTimer.addedTimeSecs(),\n collabUserReferences: [],\n },\n })\n },\n\n gameCompleted: (play: Partial<GamePlay>, config?: AugmentationConfig) => {\n internalTimer._conclude()\n stopTimerIntervals()\n\n const finalPlay: Partial<GamePlay> = {\n ...play,\n elapsedTimeSecs: play.elapsedTimeSecs ?? internalTimer.timeWithoutPenaltySecs(),\n additionalTimeAddedSecs: play.additionalTimeAddedSecs ?? internalTimer.addedTimeSecs(),\n }\n\n const deeds: Deed[] = (config?.deeds as any) ?? []\n deeds.push({ id: \"points\", value: play.pointsAwarded })\n deeds.push({\n id: \"time\",\n value: Math.round(finalPlay.elapsedTimeSecs ?? 0) + Math.round(finalPlay.additionalTimeAddedSecs ?? 0),\n })\n\n const gameplayID = getGameplayID()\n if (gameplayID) {\n hostAPI.sendMessage(\"GAME_COMPLETED\", {\n id: gameplayID,\n input: finalPlay,\n config,\n })\n }\n },\n\n showCompletionScreen: (results: any[], gameplay: GamePlay, showRetry = true) => {\n hostAPI.sendMessage(\"SHOW_GAME_COMPLETE_SCREEN\", {\n results,\n showRetry,\n gameplay,\n })\n },\n\n hitCheckpoint: (checkpointName: string, checkpointConfig: CheckpointConfig, config?: AugmentationConfig) => {\n const gameplayID = getGameplayID()\n if (!gameplayID) return\n\n const inputStr = getBoardState() ?? \"\"\n const play: Partial<GamePlay> = {\n elapsedTimeSecs: internalTimer.timeWithoutPenaltySecs(),\n additionalTimeAddedSecs: internalTimer.addedTimeSecs(),\n }\n\n hostAPI.sendMessage(\"HIT_CHECKPOINT\", {\n checkpointName,\n gameplay: { inputStr, play },\n checkpointConfig,\n augConfig: config ?? {},\n })\n },\n\n keyboard: {\n /** Show the on-screen keyboard with the given config. Call again to update state (e.g. to change disabled keys). */\n show: (config: KeyboardConfig) => {\n hostAPI.sendMessage(\"KEYBOARD_UPDATE_CONFIG\", config)\n },\n /** Hide the on-screen keyboard. */\n hide: () => {\n hostAPI.sendMessage(\"KEYBOARD_UPDATE_CONFIG\", {\n layout: [],\n symbols: {},\n highlight: [],\n disabled: [],\n xl: [],\n l: [],\n supportsDragCursor: false,\n })\n },\n },\n\n _hostAPI: hostAPI,\n }\n}\n","import type { KeyboardConfig } from \"./types\"\n\n/**\n * A standard QWERTY layout with Enter and Backspace — a reasonable default for\n * any game that needs text input. Customize from here by spreading and overriding.\n *\n * @example\n * // Use as-is\n * sdk.keyboard.show(defaultKeyboardConfig)\n *\n * @example\n * // Extend with dynamic disabled letters\n * sdk.keyboard.show({ ...defaultKeyboardConfig, disabled: usedLetters })\n */\nexport const defaultKeyboardConfig: KeyboardConfig = {\n layout: [\"qwertyuiop\", \"asdfghjkl\", \"↵zxcvbnm⌫\", undefined],\n symbols: { \"↵\": \"Enter\", \"⌫\": \"bsp\" },\n highlight: [\"↵\", \"⌫\"],\n disabled: [],\n xl: [],\n l: [\"↵\", \"⌫\"],\n supportsDragCursor: false,\n}\n","/** Severity level for validation issues */\nexport type ValidationLevel = \"error\" | \"warning\" | \"info\"\n\n/** Represents a single validation issue found during puzzle validation */\nexport interface ValidationIssue {\n level: ValidationLevel\n message: string\n line?: number\n col?: number\n length?: number\n}\n\n/** Complete validation report for a puzzle */\nexport interface ValidationReport {\n success: boolean\n issues: ValidationIssue[]\n}\n\nexport type ImportErrorType = \"invalid_format\" | \"parsing_error\" | \"unknown\"\n\n/** Custom error class for workshop import failures */\nexport class EditorImportError extends Error {\n constructor(\n public type: ImportErrorType,\n message: string,\n public originalError?: unknown,\n ) {\n super(message)\n this.name = \"EditorImportError\"\n }\n}\n\n/** Result of a successful puzzle import operation */\nexport interface ImportResult {\n data: string\n warnings?: ValidationIssue[]\n title?: string\n authors?: string[]\n editors?: string[]\n}\n\n/** Settings UI descriptor returned by an editor bundle */\nexport interface EditorBundleSettings<TComponent = unknown> {\n components: TComponent[]\n defaults: Record<string, unknown>\n}\n\n/** Main interface for a Workshop bundle */\nexport interface EditorBundle<TSettingsComponent = unknown> {\n validator: {\n validate(data: string): Promise<ValidationReport> | ValidationReport\n }\n importer?: {\n onImport(filename: string, contents: string | ArrayBuffer): Promise<ImportResult> | ImportResult\n }\n /** Embed-level settings UI, populated from the bundle's declared settings */\n settings?: EditorBundleSettings<TSettingsComponent>\n /** Editor-level settings UI, populated from the bundle's declared editor settings */\n editorSettings?: EditorBundleSettings<TSettingsComponent>\n /** Custom puzzle editor, if provided by the bundle */\n editor?: {\n mount(...args: unknown[]): unknown\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/sdk.ts","../src/keyboard.ts","../src/editor.ts"],"sourcesContent":["import type {\n MessagesSentFromEmbed,\n MessagesReceived,\n GamePlay,\n AugmentationConfig,\n CheckpointConfig,\n Theme,\n Deed,\n KeyboardConfig,\n} from \"./types\"\n\nexport type SDK = ReturnType<typeof createPuzzmoSDK>\n\nexport interface PuzzmoSDKOptions {\n /** Optional timeout in ms to wait for READY_DATA (default: 5000) */\n timeout?: number\n}\n\ntype SupportedOutgoingMessages = Pick<\n MessagesSentFromEmbed,\n | \"READY\"\n | \"READY_GAME_LOADED\"\n | \"GAME_COMPLETED\"\n | \"SHOW_GAME_COMPLETE_SCREEN\"\n | \"TIMER_TICK\"\n | \"TIMER_SYNC\"\n | \"UPLOAD_NEW_GAME_STATE\"\n | \"HIT_CHECKPOINT\"\n | \"KEYBOARD_UPDATE_CONFIG\"\n>\n\ntype SupportedIncomingMessages = Pick<\n MessagesReceived,\n | \"READY_DATA\"\n | \"START_GAME\"\n | \"PAUSE_GAME\"\n | \"RESUME_GAME\"\n | \"SETTINGS_UPDATE\"\n | \"RETRY_PUZZLE\"\n | \"KEYBOARD_KEY_PRESS\"\n | \"KEYBOARD_CURSOR_CHANGE\"\n | \"KEYBOARD_CURSOR_END\"\n>\n\ntype MessageHandler<T extends keyof SupportedIncomingMessages> = (data: SupportedIncomingMessages[T]) => void\n\nexport type SDKEventMap = {\n start: void\n pause: void\n resume: void\n retry: void\n settingsUpdate: any\n /** A key on the on-screen keyboard was tapped. */\n keyboardKeyPress: { key: string }\n /** The drag cursor moved across the keyboard. Only fires when `supportsDragCursor` is true. */\n keyboardCursorChange: { position: [number, number] }\n /** The drag cursor was released. Only fires when `supportsDragCursor` is true. */\n keyboardCursorEnd: void\n}\n\nexport type SDKEventType = keyof SDKEventMap\n\nexport interface SDKTimer {\n /** Get elapsed time in milliseconds */\n timeMs: () => number\n /** Get elapsed time in seconds */\n timeSecs: () => number\n /** Get added/penalty time in milliseconds */\n addedTimeMs: () => number\n /** Get added/penalty time in seconds */\n addedTimeSecs: () => number\n /** Get elapsed time without penalties in seconds */\n timeWithoutPenaltySecs: () => number\n /** Get formatted display strings [elapsed, added] */\n display: () => [string, string]\n /** Add penalty time in milliseconds */\n addPenalty: (ms: number) => void\n /** Check if timer is paused */\n isPaused: () => boolean\n /** Check if timer has been started */\n isRunning: () => boolean\n}\n\nfunction formatTime(timeMs: number): string {\n const isAnHourOrMore = timeMs >= 60 * 60 * 1000\n return new Date(timeMs)\n .toISOString()\n .slice(isAnHourOrMore ? 11 : 14, -1)\n .split(\".\")[0]\n}\n\nfunction createTimer(\n initialTimeMs = 0,\n initialAddedTimeMs = 0,\n): SDKTimer & {\n _init: () => void\n _pause: () => void\n _resume: () => void\n _reset: (initialTimeMs?: number, initialAddedTimeMs?: number) => void\n _conclude: () => void\n} {\n let baseTime = initialTimeMs\n let addedTime = initialAddedTimeMs\n let pausedTime = 0\n let concludeTime: number | undefined\n\n let startDate: number | undefined = undefined\n let pausedDate: number | undefined = undefined\n\n const getTime = (): number => {\n if (startDate === undefined) return baseTime + addedTime\n if (concludeTime !== undefined) return concludeTime\n const now = pausedDate ?? performance.now()\n const elapsed = now - startDate - pausedTime\n return baseTime + addedTime + elapsed\n }\n\n return {\n _init: () => {\n if (startDate === undefined) {\n startDate = performance.now()\n pausedDate = undefined\n }\n },\n _pause: () => {\n if (pausedDate !== undefined || startDate === undefined) return\n pausedDate = performance.now()\n },\n _resume: () => {\n if (pausedDate === undefined) return\n pausedTime += performance.now() - pausedDate\n pausedDate = undefined\n },\n _reset: (newInitialTimeMs = 0, newInitialAddedTimeMs = 0) => {\n baseTime = newInitialTimeMs\n addedTime = newInitialAddedTimeMs\n pausedTime = 0\n concludeTime = undefined\n startDate = undefined\n pausedDate = undefined\n },\n _conclude: () => {\n if (pausedDate !== undefined) {\n concludeTime = getTime()\n return\n }\n if (startDate === undefined) {\n concludeTime = baseTime + addedTime\n return\n }\n concludeTime = getTime()\n },\n timeMs: () => getTime(),\n timeSecs: () => getTime() / 1000,\n addedTimeMs: () => addedTime,\n addedTimeSecs: () => addedTime / 1000,\n timeWithoutPenaltySecs: () => (getTime() - addedTime) / 1000,\n addPenalty: (ms: number) => {\n addedTime += ms\n },\n isPaused: () => pausedDate !== undefined || startDate === undefined,\n isRunning: () => startDate !== undefined && pausedDate === undefined,\n display: () => {\n const elapsed = getTime() - addedTime\n const elapsedStr = formatTime(Math.max(0, elapsed))\n const addedStr = addedTime === 0 ? \"\" : formatTime(addedTime)\n return [elapsedStr, addedStr]\n },\n }\n}\n\nfunction createHostAPI() {\n const messageHandlers = new Map<string, Set<(data: any) => void>>()\n\n const sendMessage = <T extends keyof SupportedOutgoingMessages>(type: T, json: SupportedOutgoingMessages[T]) => {\n const message = { type, json, _: \"p\", __: \"mp\", private: true }\n\n if (\"parent\" in window && window.parent !== window) window.parent.postMessage(message, \"*\")\n\n window.postMessage(message, \"*\")\n\n if (\"webkit\" in window && (window as any).webkit?.messageHandlers?.app) (window as any).webkit.messageHandlers.app.postMessage(message)\n\n if (\"puzzmoMessageString\" in window) (window as any).puzzmoMessageString(JSON.stringify(message))\n\n if (\"ReactNativeWebView\" in window && (window as any).ReactNativeWebView?.postMessage)\n (window as any).ReactNativeWebView.postMessage(JSON.stringify(message))\n\n if (type !== \"TIMER_TICK\" && type !== \"TIMER_SYNC\") console.log(\"[Puzzmo SDK] sent:\", type, json)\n }\n\n const onMessage = <T extends keyof SupportedIncomingMessages>(type: T, handler: MessageHandler<T>) => {\n if (!messageHandlers.has(type)) messageHandlers.set(type, new Set())\n messageHandlers.get(type)!.add(handler)\n\n return () => {\n messageHandlers.get(type)?.delete(handler)\n }\n }\n\n if (typeof window !== \"undefined\") {\n window.addEventListener(\"message\", (event) => {\n if (!event?.data?.type) return\n const msgType = event.data.type as string\n const handlers = messageHandlers.get(msgType)\n if (handlers) {\n const msgData = event.data.data ?? event.data.json ?? {}\n if (msgType !== \"TIMER_TICK\" && msgType !== \"TIMER_SYNC\") console.log(\"[Puzzmo SDK] received:\", msgType, msgData)\n handlers.forEach((handler) => handler(msgData))\n }\n })\n }\n\n return { sendMessage, onMessage }\n}\n\nconst hostAPI = createHostAPI()\n\n/** Creates a Puzzmo SDK instance for communicating with the Puzzmo host */\nexport const createPuzzmoSDK = (options: PuzzmoSDKOptions = {}) => {\n let readyData: MessagesReceived[\"READY_DATA\"] | null = null\n let readyDataResolve: ((data: MessagesReceived[\"READY_DATA\"]) => void) | null = null\n\n const getGameplay = () => readyData?.startOrFindGameplay?.gamePlayed\n const getGameplayID = () => getGameplay()?.id ?? null\n const getPuzzleString = () => getGameplay()?.puzzle.puzzle ?? null\n const getBoardState = () => getGameplay()?.boardState ?? null\n const getTheme = () => readyData?.theme ?? null\n const getCompleted = () => getGameplay()?.completed ?? false\n\n const eventListeners = new Map<SDKEventType, Set<(data?: any) => void>>()\n\n const internalTimer = createTimer()\n let timerTickInterval: ReturnType<typeof setInterval> | null = null\n let timerSyncInterval: ReturnType<typeof setInterval> | null = null\n\n const startTimerIntervals = () => {\n if (timerTickInterval) return\n\n timerTickInterval = setInterval(() => {\n if (internalTimer.isPaused()) return\n const [elapsed, added] = internalTimer.display()\n hostAPI.sendMessage(\"TIMER_TICK\", { display: [elapsed, added] })\n }, 500)\n\n timerSyncInterval = setInterval(() => {\n if (internalTimer.isPaused()) return\n hostAPI.sendMessage(\"TIMER_SYNC\", Math.floor(internalTimer.timeWithoutPenaltySecs()))\n }, 10000)\n }\n\n const stopTimerIntervals = () => {\n if (timerTickInterval) {\n clearInterval(timerTickInterval)\n timerTickInterval = null\n }\n if (timerSyncInterval) {\n clearInterval(timerSyncInterval)\n timerSyncInterval = null\n }\n }\n\n const emit = <T extends SDKEventType>(event: T, data?: SDKEventMap[T]) => {\n const listeners = eventListeners.get(event)\n if (listeners) listeners.forEach((listener) => listener(data))\n }\n\n hostAPI.onMessage(\"START_GAME\", () => {\n internalTimer._init()\n startTimerIntervals()\n emit(\"start\")\n })\n\n hostAPI.onMessage(\"PAUSE_GAME\", () => {\n internalTimer._pause()\n emit(\"pause\")\n })\n\n hostAPI.onMessage(\"RESUME_GAME\", () => {\n internalTimer._resume()\n emit(\"resume\")\n })\n\n hostAPI.onMessage(\"SETTINGS_UPDATE\", (data) => emit(\"settingsUpdate\", data))\n\n hostAPI.onMessage(\"KEYBOARD_KEY_PRESS\", (data) => emit(\"keyboardKeyPress\", data))\n hostAPI.onMessage(\"KEYBOARD_CURSOR_CHANGE\", (data) => emit(\"keyboardCursorChange\", data))\n hostAPI.onMessage(\"KEYBOARD_CURSOR_END\", () => emit(\"keyboardCursorEnd\"))\n\n hostAPI.onMessage(\"RETRY_PUZZLE\", () => {\n internalTimer._reset()\n stopTimerIntervals()\n emit(\"retry\")\n })\n\n hostAPI.onMessage(\"READY_DATA\", (data) => {\n const bootstrapData = data as MessagesReceived[\"READY_DATA\"]\n readyData = bootstrapData\n\n const gamePlayed = bootstrapData.startOrFindGameplay?.gamePlayed\n if (gamePlayed) {\n const existingTime = (gamePlayed.elapsedTimeSecs ?? 0) * 1000\n const existingAddedTime = (gamePlayed.additionalTimeAddedSecs ?? 0) * 1000\n internalTimer._reset(existingTime, existingAddedTime)\n }\n\n if (readyDataResolve) {\n readyDataResolve(bootstrapData)\n readyDataResolve = null\n }\n })\n\n const timer: SDKTimer = {\n timeMs: () => internalTimer.timeMs(),\n timeSecs: () => internalTimer.timeSecs(),\n addedTimeMs: () => internalTimer.addedTimeMs(),\n addedTimeSecs: () => internalTimer.addedTimeSecs(),\n timeWithoutPenaltySecs: () => internalTimer.timeWithoutPenaltySecs(),\n display: () => internalTimer.display(),\n addPenalty: (ms: number) => internalTimer.addPenalty(ms),\n isPaused: () => internalTimer.isPaused(),\n isRunning: () => internalTimer.isRunning(),\n }\n\n return {\n timer,\n\n gameReady: async (): Promise<{\n puzzleString: string\n inputString: string | null\n theme: Theme | null\n completed: boolean\n readyData: MessagesReceived[\"READY_DATA\"] | null\n }> => {\n hostAPI.sendMessage(\"READY\", {})\n\n if (getPuzzleString()) {\n const inputString = getBoardState()\n return {\n puzzleString: getPuzzleString()!,\n inputString,\n // @ts-expect-error - this is backwards compat\n boardState: inputString,\n theme: getTheme(),\n completed: getCompleted(),\n readyData,\n }\n }\n\n const timeout = options.timeout ?? 5000\n const readyDataPromise = new Promise<MessagesReceived[\"READY_DATA\"]>((resolve, reject) => {\n readyDataResolve = resolve\n setTimeout(() => {\n if (readyDataResolve) {\n readyDataResolve = null\n reject(new Error(`Timeout waiting for READY_DATA after ${timeout}ms`))\n }\n }, timeout)\n })\n\n await readyDataPromise\n\n const puzzleString = getPuzzleString()\n if (!puzzleString) throw new Error(\"READY_DATA received but no puzzle data found\")\n\n const inputString = getBoardState()\n return {\n puzzleString,\n inputString,\n // @ts-expect-error - this is backwards compat\n boardState: inputString,\n theme: getTheme(),\n completed: getCompleted(),\n readyData,\n }\n },\n\n gameLoaded: (state: any = {}) => {\n hostAPI.sendMessage(\"READY_GAME_LOADED\", {\n state,\n gameRuntimeContract: \"1.0\",\n embedRuntimeContract: \"1.0\",\n })\n },\n\n on: <T extends SDKEventType>(event: T, listener: (data?: SDKEventMap[T]) => void): (() => void) => {\n if (!eventListeners.has(event)) eventListeners.set(event, new Set())\n eventListeners.get(event)!.add(listener)\n return () => {\n eventListeners.get(event)?.delete(listener)\n }\n },\n\n off: <T extends SDKEventType>(event: T, listener: (data?: SDKEventMap[T]) => void) => {\n eventListeners.get(event)?.delete(listener)\n },\n\n updateGameState: (inputString: string, play?: Partial<GamePlay>) => {\n const gameplayID = getGameplayID()\n if (!gameplayID) return\n\n hostAPI.sendMessage(\"UPLOAD_NEW_GAME_STATE\", {\n id: gameplayID,\n input: {\n boardState: inputString,\n elapsedTimeSecs: play?.elapsedTimeSecs ?? internalTimer.timeWithoutPenaltySecs(),\n additionalTimeAddedSecs: play?.additionalTimeAddedSecs ?? internalTimer.addedTimeSecs(),\n collabUserReferences: [],\n },\n })\n },\n\n gameCompleted: (play: Partial<GamePlay>, config?: AugmentationConfig) => {\n internalTimer._conclude()\n stopTimerIntervals()\n\n const finalPlay: Partial<GamePlay> = {\n ...play,\n elapsedTimeSecs: play.elapsedTimeSecs ?? internalTimer.timeWithoutPenaltySecs(),\n additionalTimeAddedSecs: play.additionalTimeAddedSecs ?? internalTimer.addedTimeSecs(),\n }\n\n const deeds: Deed[] = (config?.deeds as any) ?? []\n deeds.push({ id: \"points\", value: play.pointsAwarded })\n deeds.push({\n id: \"time\",\n value: Math.round(finalPlay.elapsedTimeSecs ?? 0) + Math.round(finalPlay.additionalTimeAddedSecs ?? 0),\n })\n\n const gameplayID = getGameplayID()\n if (gameplayID) {\n hostAPI.sendMessage(\"GAME_COMPLETED\", {\n id: gameplayID,\n input: finalPlay,\n config,\n })\n }\n },\n\n showCompletionScreen: (results: any[], gameplay: GamePlay, showRetry = true) => {\n hostAPI.sendMessage(\"SHOW_GAME_COMPLETE_SCREEN\", {\n results,\n showRetry,\n gameplay,\n })\n },\n\n hitCheckpoint: (checkpointName: string, checkpointConfig: CheckpointConfig, config?: AugmentationConfig) => {\n const gameplayID = getGameplayID()\n if (!gameplayID) return\n\n const inputStr = getBoardState() ?? \"\"\n const play: Partial<GamePlay> = {\n elapsedTimeSecs: internalTimer.timeWithoutPenaltySecs(),\n additionalTimeAddedSecs: internalTimer.addedTimeSecs(),\n }\n\n hostAPI.sendMessage(\"HIT_CHECKPOINT\", {\n checkpointName,\n gameplay: { inputStr, play },\n checkpointConfig,\n augConfig: config ?? {},\n })\n },\n\n keyboard: {\n /** Show the on-screen keyboard with the given config. Call again to update state (e.g. to change disabled keys). */\n show: (config: KeyboardConfig) => {\n hostAPI.sendMessage(\"KEYBOARD_UPDATE_CONFIG\", config)\n },\n /** Hide the on-screen keyboard. */\n hide: () => {\n hostAPI.sendMessage(\"KEYBOARD_UPDATE_CONFIG\", {\n layout: [],\n symbols: {},\n highlight: [],\n disabled: [],\n xl: [],\n l: [],\n supportsDragCursor: false,\n })\n },\n },\n\n _hostAPI: hostAPI,\n }\n}\n","import type { KeyboardConfig } from \"./types\"\n\n/**\n * A standard QWERTY layout with Enter and Backspace — a reasonable default for\n * any game that needs text input. Customize from here by spreading and overriding.\n *\n * @example\n * // Use as-is\n * sdk.keyboard.show(defaultKeyboardConfig)\n *\n * @example\n * // Extend with dynamic disabled letters\n * sdk.keyboard.show({ ...defaultKeyboardConfig, disabled: usedLetters })\n */\nexport const defaultKeyboardConfig: KeyboardConfig = {\n layout: [\"qwertyuiop\", \"asdfghjkl\", \"↵zxcvbnm⌫\", undefined],\n symbols: { \"↵\": \"Enter\", \"⌫\": \"bsp\" },\n highlight: [\"↵\", \"⌫\"],\n disabled: [],\n xl: [],\n l: [\"↵\", \"⌫\"],\n supportsDragCursor: false,\n}\n","/** Severity level for validation issues */\nexport type ValidationLevel = \"error\" | \"warning\" | \"info\"\n\n/** Represents a single validation issue found during puzzle validation */\nexport interface ValidationIssue {\n level: ValidationLevel\n message: string\n line?: number\n col?: number\n length?: number\n}\n\n/** Complete validation report for a puzzle */\nexport interface ValidationReport {\n success: boolean\n issues: ValidationIssue[]\n}\n\nexport type ImportErrorType = \"invalid_format\" | \"parsing_error\" | \"unknown\"\n\n/** Custom error class for workshop import failures */\nexport class EditorImportError extends Error {\n constructor(\n public type: ImportErrorType,\n message: string,\n public originalError?: unknown,\n ) {\n super(message)\n this.name = \"EditorImportError\"\n }\n}\n\n/** Result of a successful puzzle import operation */\nexport interface ImportResult {\n data: string\n warnings?: ValidationIssue[]\n title?: string\n authors?: string[]\n editors?: string[]\n}\n\n/** Settings UI descriptor returned by an editor bundle */\nexport interface EditorBundleSettings<TComponent = unknown> {\n components: TComponent[]\n defaults: Record<string, unknown>\n}\n\n/** Main interface for a Workshop bundle */\nexport interface EditorBundle<TSettingsComponent = unknown> {\n validator: {\n validate(data: string): Promise<ValidationReport> | ValidationReport\n }\n importer?: {\n onImport(filename: string, contents: string | ArrayBuffer): Promise<ImportResult> | ImportResult\n }\n /** Embed-level settings UI, populated from the bundle's declared settings */\n settings?: EditorBundleSettings<TSettingsComponent>\n /** Editor-level settings UI, populated from the bundle's declared editor settings */\n editorSettings?: EditorBundleSettings<TSettingsComponent>\n /** Custom puzzle editor, if provided by the bundle */\n editor?: {\n mount(...args: unknown[]): unknown\n }\n}\n"],"mappings":"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"}
|
package/dist/sdk.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sdk.d.ts","sourceRoot":"","sources":["../src/sdk.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,qBAAqB,EACrB,gBAAgB,EAChB,QAAQ,EACR,kBAAkB,EAClB,gBAAgB,EAChB,KAAK,EAEL,cAAc,EACf,MAAM,SAAS,CAAA;AAEhB,MAAM,MAAM,GAAG,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAA;AAEpD,MAAM,WAAW,gBAAgB;IAC/B,oEAAoE;IACpE,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,KAAK,yBAAyB,GAAG,IAAI,CACnC,qBAAqB,EACnB,OAAO,GACP,mBAAmB,GACnB,gBAAgB,GAChB,2BAA2B,GAC3B,YAAY,GACZ,YAAY,GACZ,uBAAuB,GACvB,gBAAgB,GAChB,wBAAwB,CAC3B,CAAA;AAED,KAAK,yBAAyB,GAAG,IAAI,CACnC,gBAAgB,EACd,YAAY,GACZ,YAAY,GACZ,YAAY,GACZ,aAAa,GACb,iBAAiB,GACjB,cAAc,GACd,oBAAoB,GACpB,wBAAwB,GACxB,qBAAqB,CACxB,CAAA;AAED,KAAK,cAAc,CAAC,CAAC,SAAS,MAAM,yBAAyB,IAAI,CAAC,IAAI,EAAE,yBAAyB,CAAC,CAAC,CAAC,KAAK,IAAI,CAAA;AAE7G,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,EAAE,IAAI,CAAA;IACX,KAAK,EAAE,IAAI,CAAA;IACX,MAAM,EAAE,IAAI,CAAA;IACZ,KAAK,EAAE,IAAI,CAAA;IACX,cAAc,EAAE,GAAG,CAAA;IACnB,kDAAkD;IAClD,gBAAgB,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAA;IACjC,+FAA+F;IAC/F,oBAAoB,EAAE;QAAE,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,CAAA;IACpD,kFAAkF;IAClF,iBAAiB,EAAE,IAAI,CAAA;CACxB,CAAA;AAED,MAAM,MAAM,YAAY,GAAG,MAAM,WAAW,CAAA;AAE5C,MAAM,WAAW,QAAQ;IACvB,uCAAuC;IACvC,MAAM,EAAE,MAAM,MAAM,CAAA;IACpB,kCAAkC;IAClC,QAAQ,EAAE,MAAM,MAAM,CAAA;IACtB,6CAA6C;IAC7C,WAAW,EAAE,MAAM,MAAM,CAAA;IACzB,wCAAwC;IACxC,aAAa,EAAE,MAAM,MAAM,CAAA;IAC3B,oDAAoD;IACpD,sBAAsB,EAAE,MAAM,MAAM,CAAA;IACpC,qDAAqD;IACrD,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC/B,uCAAuC;IACvC,UAAU,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IAChC,+BAA+B;IAC/B,QAAQ,EAAE,MAAM,OAAO,CAAA;IACvB,sCAAsC;IACtC,SAAS,EAAE,MAAM,OAAO,CAAA;CACzB;AAyID,2EAA2E;AAC3E,eAAO,MAAM,eAAe,GAAI,UAAS,gBAAqB;;qBA4GrC,OAAO,CAAC;QAC3B,YAAY,EAAE,MAAM,CAAA;QACpB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;QAC1B,KAAK,EAAE,KAAK,GAAG,IAAI,CAAA;QACnB,SAAS,EAAE,OAAO,CAAA;QAClB,SAAS,EAAE,gBAAgB,CAAC,YAAY,CAAC,GAAG,IAAI,CAAA;KACjD,CAAC;
|
|
1
|
+
{"version":3,"file":"sdk.d.ts","sourceRoot":"","sources":["../src/sdk.ts"],"names":[],"mappings":"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"}
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* // vite.config.ts
|
|
13
13
|
* import { puzzmoSimulator } from "@puzzmo/sdk/vite"
|
|
14
14
|
* export default defineConfig({
|
|
15
|
-
* plugins: [puzzmoSimulator({ slug: "my-game", fixturesGlob: "
|
|
15
|
+
* plugins: [puzzmoSimulator({ slug: "my-game", fixturesGlob: "/fixtures/puzzles/**\/*.json" })]
|
|
16
16
|
* })
|
|
17
17
|
* ```
|
|
18
18
|
*
|
package/dist/vite.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vite.cjs","names":[],"sources":["../__vite-browser-external","../src/vite.ts"],"sourcesContent":["module.exports = {}","import type { Plugin } from \"vite\"\nimport { build } from \"vite\"\nimport path from \"path\"\n\nexport type PuzzmoSimulatorPluginOptions = {\n /** Whether to auto-start the game after READY (default: true) */\n autoStart?: boolean\n /** Initial collapsed state (default: true) */\n collapsed?: boolean\n /** Game slug for API features (e.g. \"crossword\", \"my-game\") */\n slug?: string\n /** Glob pattern for fixture files, passed to import.meta.glob (e.g. \"
|
|
1
|
+
{"version":3,"file":"vite.cjs","names":[],"sources":["../__vite-browser-external","../src/vite.ts"],"sourcesContent":["module.exports = {}","import type { Plugin } from \"vite\"\nimport { build } from \"vite\"\nimport path from \"path\"\n\nexport type PuzzmoSimulatorPluginOptions = {\n /** Whether to auto-start the game after READY (default: true) */\n autoStart?: boolean\n /** Initial collapsed state (default: true) */\n collapsed?: boolean\n /** Game slug for API features (e.g. \"crossword\", \"my-game\") */\n slug?: string\n /** Glob pattern for fixture files, passed to import.meta.glob (e.g. \"/fixtures/puzzles/**\\/*.json\") */\n fixturesGlob?: string\n}\n\nconst SIMULATOR_URL = \"/@puzzmo-simulator-init.js\"\nconst VIRTUAL_ID = \"virtual:puzzmo-simulator\"\nconst RESOLVED_VIRTUAL_ID = \"\\0\" + VIRTUAL_ID\n\n/** Vite plugin that injects the Puzzmo simulator in dev mode and handles OAuth callbacks. */\nexport function puzzmoSimulator(options: PuzzmoSimulatorPluginOptions = {}): Plugin {\n function generateCode(): string {\n const { fixturesGlob, ...config } = options\n\n const lines = [`import { createSimulator } from \"@puzzmo/sdk/simulator\"`]\n\n if (fixturesGlob) {\n lines.push(`const fixtures = import.meta.glob(${JSON.stringify(fixturesGlob)}, { eager: true })`)\n }\n\n const configEntries = Object.entries(config).filter(([, v]) => v !== undefined)\n const configParts = configEntries.map(([k, v]) => `${k}: ${JSON.stringify(v)}`)\n if (fixturesGlob) configParts.push(\"fixtures\")\n\n lines.push(`createSimulator({ ${configParts.join(\", \")} })`)\n\n return lines.join(\"\\n\")\n }\n\n return {\n name: \"puzzmo-simulator\",\n apply: \"serve\",\n\n // Virtual module used internally for code generation and transformation.\n // Uses \\0 prefix so Vite's HTML processor won't try to inline it.\n resolveId(id) {\n if (id === VIRTUAL_ID) return RESOLVED_VIRTUAL_ID\n },\n\n load(id) {\n if (id === RESOLVED_VIRTUAL_ID) return generateCode()\n },\n\n configureServer(server) {\n server.middlewares.use(\"/oauth/callback\", (_req, res) => {\n res.setHeader(\"Content-Type\", \"text/html\")\n res.end(`<!DOCTYPE html>\n<html><head><title>Puzzmo OAuth</title></head>\n<body><script>\nvar params = new URLSearchParams(window.location.search);\nvar returnUrl = sessionStorage.getItem(\"oauth_return_url\") || \"/\";\nvar url = new URL(returnUrl);\nparams.forEach(function(v, k) { url.searchParams.set(k, v); });\nwindow.location.href = url.toString();\n</script></body></html>`)\n })\n\n // Serve the simulator init module. Registered before Vite's internal\n // middleware so the SPA fallback doesn't intercept it.\n server.middlewares.use(async (req, res, next) => {\n if (req.url?.split(\"?\")[0] !== SIMULATOR_URL) return next()\n\n const result = await server.transformRequest(VIRTUAL_ID)\n if (!result) return next()\n res.setHeader(\"Content-Type\", \"application/javascript\")\n res.end(result.code)\n })\n },\n\n // Inject a script tag whose src the browser will fetch.\n // The URL is NOT resolvable via resolveId (intentionally), so Vite's\n // HTML processor won't inline it. The middleware above serves it instead.\n transformIndexHtml() {\n return [\n {\n tag: \"script\",\n attrs: { type: \"module\", src: SIMULATOR_URL },\n injectTo: \"head\",\n },\n ]\n },\n }\n}\n\ntype BundlePluginOptions = {\n /** Entry file for the bundle */\n entry: string\n /** Output file name */\n outputFile: string\n}\n\nfunction createBundlePlugin(pluginName: string, defaults: BundlePluginOptions) {\n return (options: Partial<BundlePluginOptions> = {}): Plugin => {\n const { entry, outputFile } = { ...defaults, ...options }\n return {\n name: pluginName,\n apply: \"build\",\n async closeBundle() {\n try {\n await build({\n configFile: false,\n logLevel: \"warn\",\n build: {\n lib: {\n entry: path.isAbsolute(entry) ? entry : path.resolve(process.cwd(), entry),\n formats: [\"es\"],\n fileName: () => outputFile,\n },\n outDir: \"dist\",\n emptyOutDir: false,\n },\n })\n } catch (error) {\n console.error(`[${pluginName}] build failed:`, error)\n throw error\n }\n },\n }\n }\n}\n\nexport type AppBundlePluginOptions = Partial<BundlePluginOptions>\n\n/**\n * Vite plugin that produces dist/app-bundle.js after the main build for app-level integrations.\n *\n * The bundle exports `renderThumbnail(puzzleStr, inputStr?, config?)` — a pure\n * SVG-string renderer used by the Puzzmo platform for puzzle previews.\n */\nexport const appBundlePlugin = createBundlePlugin(\"app-bundle\", { entry: \"src/appBundle.js\", outputFile: \"app-bundle.js\" })\n\nexport type EditorBundlePluginOptions = Partial<BundlePluginOptions>\n\n/** Vite plugin that produces dist/editor-bundle.js after the main build for editor-level integrations. */\nexport const editorBundlePlugin = createBundlePlugin(\"editor-bundle\", { entry: \"src/editorBundle.js\", outputFile: \"editor-bundle.js\" })\n"],"mappings":"iuBAAA,EAAO,QAAU,EAAA,0XCsBL,eAAA,CAPN,EAAgB,6BAChB,EAAa,2BACb,EAAsB,KAAO,EAGnC,SAAgB,EAAgB,EAAwC,EAAE,CAAU,CAClF,SAAS,GAAuB,CAC9B,GAAM,CAAE,gBAAA,EAAiB,EAAA,EAAW,EAAA,EAAA,CAE9B,EAAQ,CAAC,0DAA0D,CAErE,GACF,EAAM,KAAK,qCAAqC,KAAK,UAAU,EAAa,CAAC,oBAAoB,CAInG,IAAM,EADgB,OAAO,QAAQ,EAAO,CAAC,QAAQ,EAAG,KAAO,IAAM,IAAA,GAAU,CAC7C,KAAK,CAAC,EAAG,KAAO,GAAG,EAAE,IAAI,KAAK,UAAU,EAAE,GAAG,CAK/E,OAJI,GAAc,EAAY,KAAK,WAAW,CAE9C,EAAM,KAAK,qBAAqB,EAAY,KAAK,KAAK,CAAC,KAAK,CAErD,EAAM,KAAK;EAAK,CAGzB,MAAO,CACL,KAAM,mBACN,MAAO,QAIP,UAAU,EAAI,CACZ,GAAI,IAAO,EAAY,OAAO,GAGhC,KAAK,EAAI,CACP,GAAI,IAAO,EAAqB,OAAO,GAAc,EAGvD,gBAAgB,EAAQ,CACtB,EAAO,YAAY,IAAI,mBAAoB,EAAM,IAAQ,CACvD,EAAI,UAAU,eAAgB,YAAY,CAC1C,EAAI,IAAI;;;;;;;;0BAQS,EACjB,CAIF,EAAO,YAAY,IAAA,UAAA,qBAAW,EAAK,EAAK,EAAS,OAC/C,KAAA,EAAI,EAAI,MAAA,KAAA,IAAA,GAAA,EAAK,MAAM,IAAI,CAAC,MAAO,EAAe,OAAO,GAAM,CAE3D,IAAM,EAAS,MAAM,EAAO,iBAAiB,EAAW,CACxD,GAAI,CAAC,EAAQ,OAAO,GAAM,CAC1B,EAAI,UAAU,eAAgB,yBAAyB,CACvD,EAAI,IAAI,EAAO,KAAK,mBANQ,EAAK,EAAK,EAAA,oCAOtC,EAMJ,oBAAqB,CACnB,MAAO,CACL,CACE,IAAK,SACL,MAAO,CAAE,KAAM,SAAU,IAAK,EAAe,CAC7C,SAAU,OACX,CACF,EAEJ,CAUH,SAAS,EAAmB,EAAoB,EAA+B,CAC7E,OAAQ,EAAwC,EAAE,GAAa,CAC7D,GAAM,CAAE,QAAO,cAAA,EAAA,EAAA,EAAA,EAAA,EAAA,CAAoB,EAAA,CAAa,EAAS,CACzD,MAAO,CACL,KAAM,EACN,MAAO,QACP,aAAM,uBAAc,CAClB,GAAI,CACF,MAAA,EAAA,EAAA,OAAY,CACV,WAAY,GACZ,SAAU,OACV,MAAO,CACL,IAAK,CACH,MAAO,EAAA,QAAK,WAAW,EAAM,CAAG,EAAQ,EAAA,QAAK,QAAQ,QAAQ,KAAK,CAAE,EAAM,CAC1E,QAAS,CAAC,KAAK,CACf,aAAgB,EACjB,CACD,OAAQ,OACR,YAAa,GACd,CACF,CAAC,OACK,EAAO,CAEd,MADA,QAAQ,MAAM,IAAI,EAAW,iBAAkB,EAAM,CAC/C,QAGX,EAYL,MAAa,EAAkB,EAAmB,aAAc,CAAE,MAAO,mBAAoB,WAAY,gBAAiB,CAAC,CAK9G,EAAqB,EAAmB,gBAAiB,CAAE,MAAO,sBAAuB,WAAY,mBAAoB,CAAC"}
|
package/dist/vite.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ export type PuzzmoSimulatorPluginOptions = {
|
|
|
6
6
|
collapsed?: boolean;
|
|
7
7
|
/** Game slug for API features (e.g. "crossword", "my-game") */
|
|
8
8
|
slug?: string;
|
|
9
|
-
/** Glob pattern for fixture files, passed to import.meta.glob (e.g. "
|
|
9
|
+
/** Glob pattern for fixture files, passed to import.meta.glob (e.g. "/fixtures/puzzles/**\/*.json") */
|
|
10
10
|
fixturesGlob?: string;
|
|
11
11
|
};
|
|
12
12
|
/** Vite plugin that injects the Puzzmo simulator in dev mode and handles OAuth callbacks. */
|
package/dist/vite.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vite.d.ts","sourceRoot":"","sources":["../src/vite.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAIlC,MAAM,MAAM,4BAA4B,GAAG;IACzC,iEAAiE;IACjE,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,8CAA8C;IAC9C,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,+DAA+D;IAC/D,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,
|
|
1
|
+
{"version":3,"file":"vite.d.ts","sourceRoot":"","sources":["../src/vite.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAIlC,MAAM,MAAM,4BAA4B,GAAG;IACzC,iEAAiE;IACjE,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,8CAA8C;IAC9C,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,+DAA+D;IAC/D,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,uGAAuG;IACvG,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB,CAAA;AAMD,6FAA6F;AAC7F,wBAAgB,eAAe,CAAC,OAAO,GAAE,4BAAiC,GAAG,MAAM,CAwElF;AAED,KAAK,mBAAmB,GAAG;IACzB,gCAAgC;IAChC,KAAK,EAAE,MAAM,CAAA;IACb,uBAAuB;IACvB,UAAU,EAAE,MAAM,CAAA;CACnB,CAAA;AAgCD,MAAM,MAAM,sBAAsB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAA;AAEjE;;;;;GAKG;AACH,eAAO,MAAM,eAAe,aArCT,OAAO,CAAC,mBAAmB,CAAC,KAAQ,MAqCoE,CAAA;AAE3H,MAAM,MAAM,yBAAyB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAA;AAEpE,0GAA0G;AAC1G,eAAO,MAAM,kBAAkB,aA1CZ,OAAO,CAAC,mBAAmB,CAAC,KAAQ,MA0CgF,CAAA"}
|
package/dist/vite.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vite.js","names":[],"sources":["../__vite-browser-external","../src/vite.ts"],"sourcesContent":["module.exports = {}","import type { Plugin } from \"vite\"\nimport { build } from \"vite\"\nimport path from \"path\"\n\nexport type PuzzmoSimulatorPluginOptions = {\n /** Whether to auto-start the game after READY (default: true) */\n autoStart?: boolean\n /** Initial collapsed state (default: true) */\n collapsed?: boolean\n /** Game slug for API features (e.g. \"crossword\", \"my-game\") */\n slug?: string\n /** Glob pattern for fixture files, passed to import.meta.glob (e.g. \"
|
|
1
|
+
{"version":3,"file":"vite.js","names":[],"sources":["../__vite-browser-external","../src/vite.ts"],"sourcesContent":["module.exports = {}","import type { Plugin } from \"vite\"\nimport { build } from \"vite\"\nimport path from \"path\"\n\nexport type PuzzmoSimulatorPluginOptions = {\n /** Whether to auto-start the game after READY (default: true) */\n autoStart?: boolean\n /** Initial collapsed state (default: true) */\n collapsed?: boolean\n /** Game slug for API features (e.g. \"crossword\", \"my-game\") */\n slug?: string\n /** Glob pattern for fixture files, passed to import.meta.glob (e.g. \"/fixtures/puzzles/**\\/*.json\") */\n fixturesGlob?: string\n}\n\nconst SIMULATOR_URL = \"/@puzzmo-simulator-init.js\"\nconst VIRTUAL_ID = \"virtual:puzzmo-simulator\"\nconst RESOLVED_VIRTUAL_ID = \"\\0\" + VIRTUAL_ID\n\n/** Vite plugin that injects the Puzzmo simulator in dev mode and handles OAuth callbacks. */\nexport function puzzmoSimulator(options: PuzzmoSimulatorPluginOptions = {}): Plugin {\n function generateCode(): string {\n const { fixturesGlob, ...config } = options\n\n const lines = [`import { createSimulator } from \"@puzzmo/sdk/simulator\"`]\n\n if (fixturesGlob) {\n lines.push(`const fixtures = import.meta.glob(${JSON.stringify(fixturesGlob)}, { eager: true })`)\n }\n\n const configEntries = Object.entries(config).filter(([, v]) => v !== undefined)\n const configParts = configEntries.map(([k, v]) => `${k}: ${JSON.stringify(v)}`)\n if (fixturesGlob) configParts.push(\"fixtures\")\n\n lines.push(`createSimulator({ ${configParts.join(\", \")} })`)\n\n return lines.join(\"\\n\")\n }\n\n return {\n name: \"puzzmo-simulator\",\n apply: \"serve\",\n\n // Virtual module used internally for code generation and transformation.\n // Uses \\0 prefix so Vite's HTML processor won't try to inline it.\n resolveId(id) {\n if (id === VIRTUAL_ID) return RESOLVED_VIRTUAL_ID\n },\n\n load(id) {\n if (id === RESOLVED_VIRTUAL_ID) return generateCode()\n },\n\n configureServer(server) {\n server.middlewares.use(\"/oauth/callback\", (_req, res) => {\n res.setHeader(\"Content-Type\", \"text/html\")\n res.end(`<!DOCTYPE html>\n<html><head><title>Puzzmo OAuth</title></head>\n<body><script>\nvar params = new URLSearchParams(window.location.search);\nvar returnUrl = sessionStorage.getItem(\"oauth_return_url\") || \"/\";\nvar url = new URL(returnUrl);\nparams.forEach(function(v, k) { url.searchParams.set(k, v); });\nwindow.location.href = url.toString();\n</script></body></html>`)\n })\n\n // Serve the simulator init module. Registered before Vite's internal\n // middleware so the SPA fallback doesn't intercept it.\n server.middlewares.use(async (req, res, next) => {\n if (req.url?.split(\"?\")[0] !== SIMULATOR_URL) return next()\n\n const result = await server.transformRequest(VIRTUAL_ID)\n if (!result) return next()\n res.setHeader(\"Content-Type\", \"application/javascript\")\n res.end(result.code)\n })\n },\n\n // Inject a script tag whose src the browser will fetch.\n // The URL is NOT resolvable via resolveId (intentionally), so Vite's\n // HTML processor won't inline it. The middleware above serves it instead.\n transformIndexHtml() {\n return [\n {\n tag: \"script\",\n attrs: { type: \"module\", src: SIMULATOR_URL },\n injectTo: \"head\",\n },\n ]\n },\n }\n}\n\ntype BundlePluginOptions = {\n /** Entry file for the bundle */\n entry: string\n /** Output file name */\n outputFile: string\n}\n\nfunction createBundlePlugin(pluginName: string, defaults: BundlePluginOptions) {\n return (options: Partial<BundlePluginOptions> = {}): Plugin => {\n const { entry, outputFile } = { ...defaults, ...options }\n return {\n name: pluginName,\n apply: \"build\",\n async closeBundle() {\n try {\n await build({\n configFile: false,\n logLevel: \"warn\",\n build: {\n lib: {\n entry: path.isAbsolute(entry) ? entry : path.resolve(process.cwd(), entry),\n formats: [\"es\"],\n fileName: () => outputFile,\n },\n outDir: \"dist\",\n emptyOutDir: false,\n },\n })\n } catch (error) {\n console.error(`[${pluginName}] build failed:`, error)\n throw error\n }\n },\n }\n }\n}\n\nexport type AppBundlePluginOptions = Partial<BundlePluginOptions>\n\n/**\n * Vite plugin that produces dist/app-bundle.js after the main build for app-level integrations.\n *\n * The bundle exports `renderThumbnail(puzzleStr, inputStr?, config?)` — a pure\n * SVG-string renderer used by the Puzzmo platform for puzzle previews.\n */\nexport const appBundlePlugin = createBundlePlugin(\"app-bundle\", { entry: \"src/appBundle.js\", outputFile: \"app-bundle.js\" })\n\nexport type EditorBundlePluginOptions = Partial<BundlePluginOptions>\n\n/** Vite plugin that produces dist/editor-bundle.js after the main build for editor-level integrations. */\nexport const editorBundlePlugin = createBundlePlugin(\"editor-bundle\", { entry: \"src/editorBundle.js\", outputFile: \"editor-bundle.js\" })\n"],"mappings":";;;;;;;;;;;;;AAAA,GAAO,UAAU,EAAA;;;;;;;;;;;;;;;;;;;;SCsBL,eAAA,EAPN,IAAgB,8BAChB,IAAa,4BACb,IAAsB,OAAO;;AAGnC,SAAgB,EAAgB,IAAwC,EAAE,EAAU;CAClF,SAAS,IAAuB;EAC9B,IAAM,EAAE,oBAAA,GAAiB,IAAA,EAAW,GAAA,EAAA,EAE9B,IAAQ,CAAC,4DAA0D;AAEzE,EAAI,KACF,EAAM,KAAK,qCAAqC,KAAK,UAAU,EAAa,CAAC,oBAAoB;EAInG,IAAM,IADgB,OAAO,QAAQ,EAAO,CAAC,QAAQ,GAAG,OAAO,MAAM,KAAA,EAAU,CAC7C,KAAK,CAAC,GAAG,OAAO,GAAG,EAAE,IAAI,KAAK,UAAU,EAAE,GAAG;AAK/E,SAJI,KAAc,EAAY,KAAK,WAAW,EAE9C,EAAM,KAAK,qBAAqB,EAAY,KAAK,KAAK,CAAC,KAAK,EAErD,EAAM,KAAK,KAAK;;AAGzB,QAAO;EACL,MAAM;EACN,OAAO;EAIP,UAAU,GAAI;AACZ,OAAI,MAAO,EAAY,QAAO;;EAGhC,KAAK,GAAI;AACP,OAAI,MAAO,EAAqB,QAAO,GAAc;;EAGvD,gBAAgB,GAAQ;AAgBtB,GAfA,EAAO,YAAY,IAAI,oBAAoB,GAAM,MAAQ;AAEvD,IADA,EAAI,UAAU,gBAAgB,YAAY,EAC1C,EAAI,IAAI,uXAQS;KACjB,EAIF,EAAO,YAAY,IAAA,WAAA;yBAAW,GAAK,GAAK,GAAS;;AAC/C,WAAA,IAAI,EAAI,QAAA,OAAA,KAAA,IAAA,EAAK,MAAM,IAAI,CAAC,QAAO,EAAe,QAAO,GAAM;KAE3D,IAAM,IAAS,MAAM,EAAO,iBAAiB,EAAW;AACxD,SAAI,CAAC,EAAQ,QAAO,GAAM;AAE1B,KADA,EAAI,UAAU,gBAAgB,yBAAyB,EACvD,EAAI,IAAI,EAAO,KAAK;;oBANQ,GAAK,GAAK,GAAA;;;OAOtC;;EAMJ,qBAAqB;AACnB,UAAO,CACL;IACE,KAAK;IACL,OAAO;KAAE,MAAM;KAAU,KAAK;KAAe;IAC7C,UAAU;IACX,CACF;;EAEJ;;AAUH,SAAS,EAAmB,GAAoB,GAA+B;AAC7E,SAAQ,IAAwC,EAAE,KAAa;EAC7D,IAAM,EAAE,UAAO,kBAAA,EAAA,EAAA,EAAA,EAAoB,EAAA,EAAa,EAAS;AACzD,SAAO;GACL,MAAM;GACN,OAAO;GACP,cAAM;0BAAc;AAClB,SAAI;AACF,YAAM,EAAM;OACV,YAAY;OACZ,UAAU;OACV,OAAO;QACL,KAAK;SACH,OAAO,EAAA,QAAK,WAAW,EAAM,GAAG,IAAQ,EAAA,QAAK,QAAQ,QAAQ,KAAK,EAAE,EAAM;SAC1E,SAAS,CAAC,KAAK;SACf,gBAAgB;SACjB;QACD,QAAQ;QACR,aAAa;QACd;OACF,CAAC;cACK,GAAO;AAEd,YADA,QAAQ,MAAM,IAAI,EAAW,kBAAkB,EAAM,EAC/C;;;;GAGX;;;;;;;;;AAYL,MAAa,IAAkB,EAAmB,cAAc;CAAE,OAAO;CAAoB,YAAY;CAAiB,CAAC,EAK9G,IAAqB,EAAmB,iBAAiB;CAAE,OAAO;CAAuB,YAAY;CAAoB,CAAC"}
|