@smoregg/sdk 0.6.2 → 1.1.0

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 (70) hide show
  1. package/README.md +29 -38
  2. package/dist/cjs/controller.cjs +312 -144
  3. package/dist/cjs/controller.cjs.map +1 -1
  4. package/dist/cjs/errors.cjs +36 -0
  5. package/dist/cjs/errors.cjs.map +1 -0
  6. package/dist/cjs/events.cjs +43 -19
  7. package/dist/cjs/events.cjs.map +1 -1
  8. package/dist/cjs/index.cjs +3 -8
  9. package/dist/cjs/index.cjs.map +1 -1
  10. package/dist/cjs/logger.cjs +75 -0
  11. package/dist/cjs/logger.cjs.map +1 -0
  12. package/dist/cjs/screen.cjs +315 -215
  13. package/dist/cjs/screen.cjs.map +1 -1
  14. package/dist/cjs/testing.cjs +297 -22
  15. package/dist/cjs/testing.cjs.map +1 -1
  16. package/dist/cjs/transport/DirectTransport.cjs.map +1 -1
  17. package/dist/cjs/transport/PostMessageTransport.cjs +11 -6
  18. package/dist/cjs/transport/PostMessageTransport.cjs.map +1 -1
  19. package/dist/cjs/transport/protocol.cjs +25 -5
  20. package/dist/cjs/transport/protocol.cjs.map +1 -1
  21. package/dist/esm/controller.js +305 -136
  22. package/dist/esm/controller.js.map +1 -1
  23. package/dist/esm/errors.js +34 -0
  24. package/dist/esm/errors.js.map +1 -0
  25. package/dist/esm/events.js +41 -18
  26. package/dist/esm/events.js.map +1 -1
  27. package/dist/esm/index.js +3 -4
  28. package/dist/esm/index.js.map +1 -1
  29. package/dist/esm/logger.js +73 -0
  30. package/dist/esm/logger.js.map +1 -0
  31. package/dist/esm/screen.js +303 -202
  32. package/dist/esm/screen.js.map +1 -1
  33. package/dist/esm/testing.js +297 -22
  34. package/dist/esm/testing.js.map +1 -1
  35. package/dist/esm/transport/DirectTransport.js.map +1 -1
  36. package/dist/esm/transport/PostMessageTransport.js +12 -7
  37. package/dist/esm/transport/PostMessageTransport.js.map +1 -1
  38. package/dist/esm/transport/protocol.js +23 -4
  39. package/dist/esm/transport/protocol.js.map +1 -1
  40. package/dist/types/controller.d.ts +1 -14
  41. package/dist/types/controller.d.ts.map +1 -1
  42. package/dist/types/errors.d.ts +45 -0
  43. package/dist/types/errors.d.ts.map +1 -0
  44. package/dist/types/events.d.ts +54 -12
  45. package/dist/types/events.d.ts.map +1 -1
  46. package/dist/types/index.d.ts +4 -6
  47. package/dist/types/index.d.ts.map +1 -1
  48. package/dist/types/logger.d.ts +35 -0
  49. package/dist/types/logger.d.ts.map +1 -0
  50. package/dist/types/screen.d.ts +1 -14
  51. package/dist/types/screen.d.ts.map +1 -1
  52. package/dist/types/testing.d.ts +0 -1
  53. package/dist/types/testing.d.ts.map +1 -1
  54. package/dist/types/transport/DirectTransport.d.ts +2 -1
  55. package/dist/types/transport/DirectTransport.d.ts.map +1 -1
  56. package/dist/types/transport/PostMessageTransport.d.ts +17 -2
  57. package/dist/types/transport/PostMessageTransport.d.ts.map +1 -1
  58. package/dist/types/transport/index.d.ts +2 -2
  59. package/dist/types/transport/index.d.ts.map +1 -1
  60. package/dist/types/transport/protocol.d.ts +71 -23
  61. package/dist/types/transport/protocol.d.ts.map +1 -1
  62. package/dist/types/transport/types.d.ts +24 -2
  63. package/dist/types/transport/types.d.ts.map +1 -1
  64. package/dist/types/types.d.ts +373 -215
  65. package/dist/types/types.d.ts.map +1 -1
  66. package/dist/umd/smore-sdk.umd.js +1011 -349
  67. package/dist/umd/smore-sdk.umd.js.map +1 -1
  68. package/dist/umd/smore-sdk.umd.min.js +1 -1
  69. package/dist/umd/smore-sdk.umd.min.js.map +1 -1
  70. package/package.json +8 -13
@@ -16,14 +16,19 @@ class PostMessageTransport {
16
16
  emit(event, ...args) {
17
17
  let data = args[0];
18
18
  let ackId;
19
- if (args.length >= 2 && typeof args[args.length - 1] === "function") {
20
- data = args.length === 2 ? args[0] : args[0];
19
+ if (args.length === 1 && typeof args[0] === "function") {
20
+ data = void 0;
21
+ const callback = args[0];
22
+ ackId = `ack_${++this.ackCounter}`;
23
+ this.ackCallbacks.set(ackId, callback);
24
+ } else if (args.length >= 2 && typeof args[args.length - 1] === "function") {
25
+ data = args[0];
21
26
  const callback = args[args.length - 1];
22
27
  ackId = `ack_${++this.ackCounter}`;
23
28
  this.ackCallbacks.set(ackId, callback);
24
29
  }
25
30
  window.parent.postMessage(
26
- { type: "smore:emit", payload: { event, data, ackId } },
31
+ { type: "_bridge:emit", payload: { event, data, ackId } },
27
32
  this.parentOrigin
28
33
  );
29
34
  }
@@ -50,14 +55,14 @@ class PostMessageTransport {
50
55
  handleMessage(e) {
51
56
  if (this.parentOrigin !== "*" && e.origin !== this.parentOrigin) return;
52
57
  const msg = e.data;
53
- if (!protocol.isSmoreMessage(msg)) return;
54
- if (msg.type === "smore:event") {
58
+ if (!protocol.isBridgeMessage(msg)) return;
59
+ if (msg.type === "_bridge:event") {
55
60
  const { event, data } = msg.payload;
56
61
  const set = this.handlers.get(event);
57
62
  if (set) {
58
63
  set.forEach((handler) => handler(data));
59
64
  }
60
- } else if (msg.type === "smore:ack") {
65
+ } else if (msg.type === "_bridge:ack") {
61
66
  const { ackId, data } = msg.payload;
62
67
  const cb = this.ackCallbacks.get(ackId);
63
68
  if (cb) {
@@ -1 +1 @@
1
- {"version":3,"file":"PostMessageTransport.cjs","sources":["../../../src/transport/PostMessageTransport.ts"],"sourcesContent":["/**\n * PostMessageTransport - Transport over window.postMessage for iframe-hosted games.\n *\n * Used inside an iframe. Sends `smore:emit` to parent and listens for `smore:event` from parent.\n */\n\nimport type { Transport, TransportEventHandler } from './types';\nimport type { SmoreEventMessage, SmoreAckMessage } from './protocol';\nimport { isSmoreMessage } from './protocol';\n\nexport class PostMessageTransport implements Transport {\n private handlers = new Map<string, Set<TransportEventHandler>>();\n private ackCallbacks = new Map<string, (...args: any[]) => void>();\n private ackCounter = 0;\n private parentOrigin: string;\n private boundMessageHandler: (e: MessageEvent) => void;\n\n constructor(parentOrigin: string = '*') {\n this.parentOrigin = parentOrigin;\n this.boundMessageHandler = this.handleMessage.bind(this);\n window.addEventListener('message', this.boundMessageHandler);\n }\n\n emit(event: string, ...args: any[]): void {\n // Detect if last arg is a callback (ack pattern)\n let data: any = args[0];\n let ackId: string | undefined;\n\n if (args.length >= 2 && typeof args[args.length - 1] === 'function') {\n data = args.length === 2 ? args[0] : args[0];\n const callback = args[args.length - 1];\n ackId = `ack_${++this.ackCounter}`;\n this.ackCallbacks.set(ackId, callback);\n }\n\n window.parent.postMessage(\n { type: 'smore:emit', payload: { event, data, ackId } },\n this.parentOrigin,\n );\n }\n\n on(event: string, handler: TransportEventHandler): void {\n let set = this.handlers.get(event);\n if (!set) {\n set = new Set();\n this.handlers.set(event, set);\n }\n set.add(handler);\n }\n\n off(event: string, handler?: TransportEventHandler): void {\n if (!handler) {\n this.handlers.delete(event);\n return;\n }\n this.handlers.get(event)?.delete(handler);\n }\n\n destroy(): void {\n window.removeEventListener('message', this.boundMessageHandler);\n this.handlers.clear();\n this.ackCallbacks.clear();\n }\n\n private handleMessage(e: MessageEvent): void {\n // Origin validation: only accept messages from the expected parent\n if (this.parentOrigin !== '*' && e.origin !== this.parentOrigin) return;\n\n const msg = e.data;\n if (!isSmoreMessage(msg)) return;\n\n if (msg.type === 'smore:event') {\n const { event, data } = (msg as SmoreEventMessage).payload;\n const set = this.handlers.get(event);\n if (set) {\n set.forEach((handler) => handler(data));\n }\n } else if (msg.type === 'smore:ack') {\n const { ackId, data } = (msg as SmoreAckMessage).payload;\n const cb = this.ackCallbacks.get(ackId);\n if (cb) {\n this.ackCallbacks.delete(ackId);\n cb(data);\n }\n }\n }\n}\n"],"names":["isSmoreMessage"],"mappings":"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"}
1
+ {"version":3,"file":"PostMessageTransport.cjs","sources":["../../../src/transport/PostMessageTransport.ts"],"sourcesContent":["/**\n * PostMessageTransport - Transport over window.postMessage for iframe-hosted games.\n *\n * Used inside an iframe. Sends `_bridge:emit` to parent and listens for `_bridge:event` from parent.\n */\n\nimport type { Transport, TransportEventHandler } from './types';\nimport type { BridgeEventMessage, BridgeAckMessage } from './protocol';\nimport { isBridgeMessage } from './protocol';\n\n/**\n * PostMessage-based transport for iframe-hosted games.\n *\n * Handles bi-directional communication between iframe game and parent platform using _bridge:* protocol.\n * - Outbound: `_bridge:emit` messages sent to parent\n * - Inbound: `_bridge:event` messages received from parent\n * - Acknowledgment: `_bridge:ack` pattern for request-response flows\n *\n * @example\n * ```ts\n * const transport = new PostMessageTransport('https://smore.gg');\n * transport.on('game-start', (data) => console.log('Game started', data));\n * transport.emit('player-ready', { playerIndex: 0 });\n * ```\n */\nexport class PostMessageTransport implements Transport {\n private handlers = new Map<string, Set<TransportEventHandler>>();\n private ackCallbacks = new Map<string, (...args: unknown[]) => void>();\n private ackCounter = 0;\n private parentOrigin: string;\n private boundMessageHandler: (e: MessageEvent) => void;\n\n constructor(parentOrigin: string = '*') {\n this.parentOrigin = parentOrigin;\n this.boundMessageHandler = this.handleMessage.bind(this);\n window.addEventListener('message', this.boundMessageHandler);\n }\n\n emit(event: string, ...args: unknown[]): void {\n // Detect if last arg is a callback (ack pattern)\n let data: unknown = args[0];\n let ackId: string | undefined;\n\n // Branch 1: emit('event', callback) shorthand — no data, callback only\n // Callback will be invoked when parent sends _bridge:ack with matching ackId\n if (args.length === 1 && typeof args[0] === 'function') {\n data = undefined;\n const callback = args[0] as (...cbArgs: unknown[]) => void;\n ackId = `ack_${++this.ackCounter}`;\n this.ackCallbacks.set(ackId, callback);\n }\n // Branch 2: emit('event', data, callback) — data + callback (request-response pattern)\n // Parent receives event with data and can send _bridge:ack response\n else if (args.length >= 2 && typeof args[args.length - 1] === 'function') {\n data = args[0];\n const callback = args[args.length - 1] as (...cbArgs: unknown[]) => void;\n ackId = `ack_${++this.ackCounter}`;\n this.ackCallbacks.set(ackId, callback);\n }\n\n window.parent.postMessage(\n { type: '_bridge:emit', payload: { event, data, ackId } },\n this.parentOrigin,\n );\n }\n\n on(event: string, handler: TransportEventHandler): void {\n let set = this.handlers.get(event);\n if (!set) {\n set = new Set();\n this.handlers.set(event, set);\n }\n set.add(handler);\n }\n\n off(event: string, handler?: TransportEventHandler): void {\n if (!handler) {\n this.handlers.delete(event);\n return;\n }\n this.handlers.get(event)?.delete(handler);\n }\n\n destroy(): void {\n window.removeEventListener('message', this.boundMessageHandler);\n this.handlers.clear();\n this.ackCallbacks.clear();\n }\n\n private handleMessage(e: MessageEvent): void {\n // Origin validation: only accept messages from the expected parent\n if (this.parentOrigin !== '*' && e.origin !== this.parentOrigin) return;\n\n const msg = e.data;\n if (!isBridgeMessage(msg)) return;\n\n // Branch 1: _bridge:event — state sync from bridge (server → bridge → game)\n // Parent relays socket events to iframe via this type. Game subscribes with on().\n if (msg.type === '_bridge:event') {\n const { event, data } = (msg as BridgeEventMessage).payload;\n const set = this.handlers.get(event);\n if (set) {\n set.forEach((handler) => handler(data));\n }\n }\n // Branch 2: _bridge:ack — response to emit with callback (request-response flow)\n // Fires the callback that was passed to emit('event', data, callback).\n // Edge case: In postMessage environment, callbacks cannot be truly serialized,\n // so only data types that survive JSON serialization roundtrip will work.\n else if (msg.type === '_bridge:ack') {\n const { ackId, data } = (msg as BridgeAckMessage).payload;\n const cb = this.ackCallbacks.get(ackId);\n if (cb) {\n this.ackCallbacks.delete(ackId);\n cb(data);\n }\n }\n }\n}\n"],"names":["isBridgeMessage"],"mappings":"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"}
@@ -1,10 +1,30 @@
1
1
  'use strict';
2
2
 
3
- const SMORE_MSG_PREFIX = "smore:";
4
- function isSmoreMessage(data) {
5
- return data && typeof data === "object" && typeof data.type === "string" && data.type.startsWith(SMORE_MSG_PREFIX);
3
+ const BRIDGE_MSG_PREFIX = "_bridge:";
4
+ function isBridgeMessage(data) {
5
+ return data !== null && typeof data === "object" && "type" in data && typeof data.type === "string" && data.type.startsWith(BRIDGE_MSG_PREFIX);
6
+ }
7
+ function validateInitPayload(payload) {
8
+ if (!payload || typeof payload !== "object") {
9
+ throw new Error("[SDK] _bridge:init payload must be an object");
10
+ }
11
+ const p = payload;
12
+ if (typeof p.side !== "string" || !["host", "player"].includes(p.side)) {
13
+ throw new Error(`[SDK] _bridge:init payload.side must be "host" or "player", got: ${p.side}`);
14
+ }
15
+ if (typeof p.roomCode !== "string" || p.roomCode.length === 0) {
16
+ throw new Error("[SDK] _bridge:init payload.roomCode must be a non-empty string");
17
+ }
18
+ if (!Array.isArray(p.players)) {
19
+ throw new Error("[SDK] _bridge:init payload.players must be an array");
20
+ }
21
+ if (p.myIndex !== void 0 && typeof p.myIndex !== "number") {
22
+ throw new Error("[SDK] _bridge:init payload.myIndex must be a number if provided");
23
+ }
24
+ return true;
6
25
  }
7
26
 
8
- exports.SMORE_MSG_PREFIX = SMORE_MSG_PREFIX;
9
- exports.isSmoreMessage = isSmoreMessage;
27
+ exports.BRIDGE_MSG_PREFIX = BRIDGE_MSG_PREFIX;
28
+ exports.isBridgeMessage = isBridgeMessage;
29
+ exports.validateInitPayload = validateInitPayload;
10
30
  //# sourceMappingURL=protocol.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"protocol.cjs","sources":["../../../src/transport/protocol.ts"],"sourcesContent":["/**\n * postMessage protocol types for iframe ↔ parent communication.\n */\n\nexport const SMORE_MSG_PREFIX = 'smore:' as const;\n\nexport interface SmoreReadyMessage {\n type: 'smore:ready';\n}\n\nexport interface SmoreInitMessage {\n type: 'smore:init';\n payload: {\n side: 'host' | 'player';\n roomCode: string;\n players: any[];\n leaderId: string | null;\n myIndex?: number;\n isLeader?: boolean;\n };\n}\n\nexport interface SmoreEmitMessage {\n type: 'smore:emit';\n payload: {\n event: string;\n data?: any;\n ackId?: string;\n };\n}\n\nexport interface SmoreEventMessage {\n type: 'smore:event';\n payload: {\n event: string;\n data?: any;\n };\n}\n\nexport interface SmoreAckMessage {\n type: 'smore:ack';\n payload: {\n ackId: string;\n data?: any;\n };\n}\n\nexport interface SmoreUpdateMessage {\n type: 'smore:update';\n payload: {\n players?: any[];\n leaderId?: string | null;\n };\n}\n\n// DEPRECATED: SmoreLoadedMessage removed - no longer used in protocol\n// Previously: interface SmoreLoadedMessage { type: 'smore:loaded' }\n\nexport type SmoreMessage =\n | SmoreReadyMessage\n | SmoreInitMessage\n | SmoreEmitMessage\n | SmoreEventMessage\n | SmoreAckMessage\n | SmoreUpdateMessage;\n\nexport function isSmoreMessage(data: any): data is SmoreMessage {\n return data && typeof data === 'object' && typeof data.type === 'string' && data.type.startsWith(SMORE_MSG_PREFIX);\n}\n"],"names":[],"mappings":";;AAIO,MAAM,gBAAA,GAAmB;AA8DzB,SAAS,eAAe,IAAA,EAAiC;AAC9D,EAAA,OAAO,IAAA,IAAQ,OAAO,IAAA,KAAS,QAAA,IAAY,OAAO,IAAA,CAAK,IAAA,KAAS,QAAA,IAAY,IAAA,CAAK,IAAA,CAAK,UAAA,CAAW,gBAAgB,CAAA;AACnH;;;;;"}
1
+ {"version":3,"file":"protocol.cjs","sources":["../../../src/transport/protocol.ts"],"sourcesContent":["/**\n * postMessage protocol types for iframe ↔ parent communication.\n *\n * Uses `_bridge:` prefix to clearly distinguish from `smore:*` socket events.\n * - `_bridge:*` = internal iframe postMessage protocol (never on socket)\n * - `smore:*` = platform service events (socket-level, e.g. smore:player-joined)\n */\n\n/**\n * Cross-reference: `CharacterAppearance` (SDK type) has an identical shape to\n * `CharacterDTO` (server type in game-project/types/src/types.ts).\n * If either type changes, the other must be updated to stay in sync.\n */\nimport type { CharacterAppearance } from '../types';\n\nexport const BRIDGE_MSG_PREFIX = '_bridge:' as const;\n\nexport interface BridgeReadyMessage {\n type: '_bridge:ready';\n}\n\n/**\n * BridgeInitMessage contains player data sent from the platform to the game iframe.\n *\n * **Field naming convention:**\n * The server uses `name` and `character` fields (matching server/Player model naming).\n * SDK code may reference fallback fields like `nickname` and `appearance` for defensive\n * compatibility with potential future field name changes, but currently the server\n * always sends `name` and `character`.\n */\nexport interface BridgeInitMessage {\n type: '_bridge:init';\n payload: {\n // 'host' = screen side, 'player' = controller side (legacy naming)\n side: 'host' | 'player';\n roomCode: string;\n players: Array<{\n playerIndex: number;\n name: string;\n connected: boolean;\n character: CharacterAppearance | null;\n }>;\n myIndex?: number;\n };\n}\n\nexport interface BridgeEmitMessage {\n type: '_bridge:emit';\n payload: {\n event: string;\n data?: unknown;\n ackId?: string;\n };\n}\n\nexport interface BridgeEventMessage {\n type: '_bridge:event';\n payload: {\n event: string;\n data?: unknown;\n };\n}\n\nexport interface BridgeAckMessage {\n type: '_bridge:ack';\n payload: {\n ackId: string;\n data?: unknown;\n };\n}\n\nexport interface BridgeUpdateMessage {\n type: '_bridge:update';\n payload: {\n players?: Array<{\n playerIndex: number;\n name: string;\n connected: boolean;\n character: CharacterAppearance | null;\n }>;\n };\n}\n\nexport type BridgeMessage =\n | BridgeReadyMessage\n | BridgeInitMessage\n | BridgeEmitMessage\n | BridgeEventMessage\n | BridgeAckMessage\n | BridgeUpdateMessage;\n\nexport function isBridgeMessage(data: unknown): data is BridgeMessage {\n return (\n data !== null &&\n typeof data === 'object' &&\n 'type' in data &&\n typeof data.type === 'string' &&\n data.type.startsWith(BRIDGE_MSG_PREFIX)\n );\n}\n\n/**\n * Validates the structure of a _bridge:init payload.\n *\n * Performs runtime validation to ensure the payload contains all required fields\n * with correct types. This provides early error detection if the parent frame\n * sends malformed initialization data.\n *\n * @param payload - The payload to validate\n * @returns true if payload is valid\n * @throws {Error} if validation fails with a descriptive error message\n *\n * @example\n * ```ts\n * try {\n * validateInitPayload(msg.payload);\n * // proceed with initialization\n * } catch (err) {\n * console.error('Invalid init payload:', err.message);\n * }\n * ```\n */\nexport function validateInitPayload(payload: unknown): payload is BridgeInitMessage['payload'] {\n if (!payload || typeof payload !== 'object') {\n throw new Error('[SDK] _bridge:init payload must be an object');\n }\n\n const p = payload as Record<string, unknown>;\n\n // Required: side\n if (typeof p.side !== 'string' || !['host', 'player'].includes(p.side)) {\n throw new Error(`[SDK] _bridge:init payload.side must be \"host\" or \"player\", got: ${p.side}`);\n }\n\n // Required: roomCode\n if (typeof p.roomCode !== 'string' || p.roomCode.length === 0) {\n throw new Error('[SDK] _bridge:init payload.roomCode must be a non-empty string');\n }\n\n // Required: players (array)\n if (!Array.isArray(p.players)) {\n throw new Error('[SDK] _bridge:init payload.players must be an array');\n }\n\n // Optional but validated if present: myIndex (controller-side only)\n if (p.myIndex !== undefined && typeof p.myIndex !== 'number') {\n throw new Error('[SDK] _bridge:init payload.myIndex must be a number if provided');\n }\n\n return true;\n}\n"],"names":[],"mappings":";;AAeO,MAAM,iBAAA,GAAoB;AA4E1B,SAAS,gBAAgB,IAAA,EAAsC;AACpE,EAAA,OACE,IAAA,KAAS,IAAA,IACT,OAAO,IAAA,KAAS,YAChB,MAAA,IAAU,IAAA,IACV,OAAO,IAAA,CAAK,IAAA,KAAS,QAAA,IACrB,IAAA,CAAK,IAAA,CAAK,WAAW,iBAAiB,CAAA;AAE1C;AAuBO,SAAS,oBAAoB,OAAA,EAA2D;AAC7F,EAAA,IAAI,CAAC,OAAA,IAAW,OAAO,OAAA,KAAY,QAAA,EAAU;AAC3C,IAAA,MAAM,IAAI,MAAM,8CAA8C,CAAA;AAAA,EAChE;AAEA,EAAA,MAAM,CAAA,GAAI,OAAA;AAGV,EAAA,IAAI,OAAO,CAAA,CAAE,IAAA,KAAS,QAAA,IAAY,CAAC,CAAC,MAAA,EAAQ,QAAQ,CAAA,CAAE,QAAA,CAAS,CAAA,CAAE,IAAI,CAAA,EAAG;AACtE,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,iEAAA,EAAoE,CAAA,CAAE,IAAI,CAAA,CAAE,CAAA;AAAA,EAC9F;AAGA,EAAA,IAAI,OAAO,CAAA,CAAE,QAAA,KAAa,YAAY,CAAA,CAAE,QAAA,CAAS,WAAW,CAAA,EAAG;AAC7D,IAAA,MAAM,IAAI,MAAM,gEAAgE,CAAA;AAAA,EAClF;AAGA,EAAA,IAAI,CAAC,KAAA,CAAM,OAAA,CAAQ,CAAA,CAAE,OAAO,CAAA,EAAG;AAC7B,IAAA,MAAM,IAAI,MAAM,qDAAqD,CAAA;AAAA,EACvE;AAGA,EAAA,IAAI,EAAE,OAAA,KAAY,MAAA,IAAa,OAAO,CAAA,CAAE,YAAY,QAAA,EAAU;AAC5D,IAAA,MAAM,IAAI,MAAM,iEAAiE,CAAA;AAAA,EACnF;AAEA,EAAA,OAAO,IAAA;AACT;;;;;;"}