api-ape 3.0.2 → 4.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 (186) hide show
  1. package/README.md +59 -572
  2. package/client/README.md +73 -14
  3. package/client/auth/crypto/aead.js +214 -0
  4. package/client/auth/crypto/constants.js +32 -0
  5. package/client/auth/crypto/encoding.js +104 -0
  6. package/client/auth/crypto/files.md +27 -0
  7. package/client/auth/crypto/kdf.js +217 -0
  8. package/client/auth/crypto-utils.js +118 -0
  9. package/client/auth/files.md +52 -0
  10. package/client/auth/key-recovery.js +288 -0
  11. package/client/auth/recovery/constants.js +37 -0
  12. package/client/auth/recovery/files.md +23 -0
  13. package/client/auth/recovery/key-derivation.js +61 -0
  14. package/client/auth/recovery/sss-browser.js +189 -0
  15. package/client/auth/share-storage.js +205 -0
  16. package/client/auth/storage/constants.js +18 -0
  17. package/client/auth/storage/db.js +132 -0
  18. package/client/auth/storage/files.md +27 -0
  19. package/client/auth/storage/keys.js +173 -0
  20. package/client/auth/storage/shares.js +200 -0
  21. package/client/browser.js +190 -23
  22. package/client/connectSocket.js +418 -988
  23. package/client/connection/README.md +23 -0
  24. package/client/connection/fileDownload.js +256 -0
  25. package/client/connection/fileHandling.js +450 -0
  26. package/client/connection/fileUtils.js +346 -0
  27. package/client/connection/files.md +71 -0
  28. package/client/connection/messageHandler.js +105 -0
  29. package/client/connection/network.js +350 -0
  30. package/client/connection/proxy.js +233 -0
  31. package/client/connection/sender.js +333 -0
  32. package/client/connection/state.js +321 -0
  33. package/client/connection/subscriptions.js +151 -0
  34. package/client/files.md +53 -0
  35. package/client/index.js +298 -142
  36. package/client/transports/README.md +50 -0
  37. package/client/transports/files.md +41 -0
  38. package/client/transports/streamParser.js +195 -0
  39. package/client/transports/streaming.js +555 -203
  40. package/dist/ape.js +6 -1
  41. package/dist/ape.js.map +4 -4
  42. package/index.d.ts +38 -16
  43. package/package.json +31 -6
  44. package/server/README.md +272 -67
  45. package/server/adapters/README.md +23 -14
  46. package/server/adapters/files.md +68 -0
  47. package/server/adapters/firebase.js +543 -160
  48. package/server/adapters/index.js +362 -112
  49. package/server/adapters/mongo.js +530 -140
  50. package/server/adapters/postgres.js +534 -155
  51. package/server/adapters/redis.js +508 -143
  52. package/server/adapters/supabase.js +555 -186
  53. package/server/client/README.md +43 -0
  54. package/server/client/connection.js +586 -0
  55. package/server/client/files.md +40 -0
  56. package/server/client/index.js +342 -0
  57. package/server/files.md +54 -0
  58. package/server/index.js +322 -71
  59. package/server/lib/README.md +26 -0
  60. package/server/lib/broadcast/clients.js +219 -0
  61. package/server/lib/broadcast/files.md +58 -0
  62. package/server/lib/broadcast/index.js +57 -0
  63. package/server/lib/broadcast/publishProxy.js +110 -0
  64. package/server/lib/broadcast/pubsub.js +137 -0
  65. package/server/lib/broadcast/sendProxy.js +103 -0
  66. package/server/lib/bun.js +315 -99
  67. package/server/lib/fileTransfer/README.md +63 -0
  68. package/server/lib/fileTransfer/files.md +30 -0
  69. package/server/lib/fileTransfer/streaming.js +435 -0
  70. package/server/lib/fileTransfer.js +710 -326
  71. package/server/lib/files.md +111 -0
  72. package/server/lib/httpUtils.js +283 -0
  73. package/server/lib/loader.js +208 -7
  74. package/server/lib/longPolling/README.md +63 -0
  75. package/server/lib/longPolling/files.md +44 -0
  76. package/server/lib/longPolling/getHandler.js +365 -0
  77. package/server/lib/longPolling/postHandler.js +327 -0
  78. package/server/lib/longPolling.js +174 -219
  79. package/server/lib/main.js +369 -532
  80. package/server/lib/runtimes/README.md +42 -0
  81. package/server/lib/runtimes/bun.js +586 -0
  82. package/server/lib/runtimes/files.md +56 -0
  83. package/server/lib/runtimes/node.js +511 -0
  84. package/server/lib/wiring.js +539 -98
  85. package/server/lib/ws/README.md +35 -0
  86. package/server/lib/ws/adapters/README.md +54 -0
  87. package/server/lib/ws/adapters/bun.js +538 -170
  88. package/server/lib/ws/adapters/deno.js +623 -149
  89. package/server/lib/ws/adapters/files.md +42 -0
  90. package/server/lib/ws/files.md +74 -0
  91. package/server/lib/ws/frames.js +532 -154
  92. package/server/lib/ws/index.js +207 -10
  93. package/server/lib/ws/server.js +385 -92
  94. package/server/lib/ws/socket.js +549 -181
  95. package/server/lib/wsProvider.js +363 -89
  96. package/server/plugins/binary.js +282 -0
  97. package/server/security/README.md +92 -0
  98. package/server/security/auth/README.md +319 -0
  99. package/server/security/auth/adapters/files.md +95 -0
  100. package/server/security/auth/adapters/ldap/constants.js +37 -0
  101. package/server/security/auth/adapters/ldap/files.md +19 -0
  102. package/server/security/auth/adapters/ldap/helpers.js +111 -0
  103. package/server/security/auth/adapters/ldap.js +353 -0
  104. package/server/security/auth/adapters/oauth2/constants.js +41 -0
  105. package/server/security/auth/adapters/oauth2/files.md +19 -0
  106. package/server/security/auth/adapters/oauth2/helpers.js +123 -0
  107. package/server/security/auth/adapters/oauth2.js +273 -0
  108. package/server/security/auth/adapters/opaque-handlers.js +314 -0
  109. package/server/security/auth/adapters/opaque.js +205 -0
  110. package/server/security/auth/adapters/saml/constants.js +52 -0
  111. package/server/security/auth/adapters/saml/files.md +19 -0
  112. package/server/security/auth/adapters/saml/helpers.js +74 -0
  113. package/server/security/auth/adapters/saml.js +173 -0
  114. package/server/security/auth/adapters/totp.js +703 -0
  115. package/server/security/auth/adapters/webauthn.js +625 -0
  116. package/server/security/auth/files.md +61 -0
  117. package/server/security/auth/framework/constants.js +27 -0
  118. package/server/security/auth/framework/files.md +23 -0
  119. package/server/security/auth/framework/handlers.js +272 -0
  120. package/server/security/auth/framework/socket-auth.js +177 -0
  121. package/server/security/auth/handlers/auth-messages.js +143 -0
  122. package/server/security/auth/handlers/files.md +28 -0
  123. package/server/security/auth/index.js +290 -0
  124. package/server/security/auth/mfa/crypto/aead.js +148 -0
  125. package/server/security/auth/mfa/crypto/constants.js +35 -0
  126. package/server/security/auth/mfa/crypto/files.md +27 -0
  127. package/server/security/auth/mfa/crypto/kdf.js +120 -0
  128. package/server/security/auth/mfa/crypto/utils.js +68 -0
  129. package/server/security/auth/mfa/crypto-utils.js +80 -0
  130. package/server/security/auth/mfa/files.md +77 -0
  131. package/server/security/auth/mfa/ledger/constants.js +75 -0
  132. package/server/security/auth/mfa/ledger/errors.js +73 -0
  133. package/server/security/auth/mfa/ledger/files.md +23 -0
  134. package/server/security/auth/mfa/ledger/share-record.js +32 -0
  135. package/server/security/auth/mfa/ledger.js +255 -0
  136. package/server/security/auth/mfa/recovery/constants.js +67 -0
  137. package/server/security/auth/mfa/recovery/files.md +19 -0
  138. package/server/security/auth/mfa/recovery/handlers.js +216 -0
  139. package/server/security/auth/mfa/recovery.js +191 -0
  140. package/server/security/auth/mfa/sss/constants.js +21 -0
  141. package/server/security/auth/mfa/sss/files.md +23 -0
  142. package/server/security/auth/mfa/sss/gf256.js +103 -0
  143. package/server/security/auth/mfa/sss/serialization.js +82 -0
  144. package/server/security/auth/mfa/sss.js +161 -0
  145. package/server/security/auth/mfa/two-of-three/constants.js +58 -0
  146. package/server/security/auth/mfa/two-of-three/files.md +23 -0
  147. package/server/security/auth/mfa/two-of-three/handlers.js +241 -0
  148. package/server/security/auth/mfa/two-of-three/helpers.js +71 -0
  149. package/server/security/auth/mfa/two-of-three.js +136 -0
  150. package/server/security/auth/nonce-manager.js +89 -0
  151. package/server/security/auth/state-machine-mfa.js +269 -0
  152. package/server/security/auth/state-machine.js +257 -0
  153. package/server/security/extractRootDomain.js +144 -16
  154. package/server/security/files.md +51 -0
  155. package/server/security/origin.js +197 -15
  156. package/server/security/reply.js +274 -16
  157. package/server/socket/README.md +119 -0
  158. package/server/socket/authMiddleware.js +299 -0
  159. package/server/socket/files.md +86 -0
  160. package/server/socket/open.js +154 -8
  161. package/server/socket/pluginHooks.js +334 -0
  162. package/server/socket/receive.js +184 -224
  163. package/server/socket/receiveContext.js +117 -0
  164. package/server/socket/send.js +416 -78
  165. package/server/socket/tagUtils.js +402 -0
  166. package/server/utils/README.md +19 -0
  167. package/server/utils/deepRequire.js +255 -30
  168. package/server/utils/files.md +57 -0
  169. package/server/utils/genId.js +182 -20
  170. package/server/utils/parseUserAgent.js +313 -251
  171. package/server/utils/userAgent/README.md +65 -0
  172. package/server/utils/userAgent/files.md +46 -0
  173. package/server/utils/userAgent/patterns.js +545 -0
  174. package/utils/README.md +21 -0
  175. package/utils/files.md +66 -0
  176. package/utils/jss/README.md +21 -0
  177. package/utils/jss/decode.js +471 -0
  178. package/utils/jss/encode.js +312 -0
  179. package/utils/jss/files.md +68 -0
  180. package/utils/jss/plugins.js +210 -0
  181. package/utils/jss.js +219 -273
  182. package/utils/messageHash.js +238 -35
  183. package/dist/api-ape.min.js +0 -2
  184. package/dist/api-ape.min.js.map +0 -7
  185. package/server/client.js +0 -311
  186. package/server/lib/broadcast.js +0 -146
package/dist/ape.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../utils/messageHash.js", "../utils/jss.js", "../client/connectSocket.js", "../client/transports/streaming.js", "../client/browser.js"],
4
- "sourcesContent": ["const alphabet = \"0123456789ABCDEFGHJKMNPQRSTVWXYZ\"\n/*\nfunction charValue(char){\n return alphabet.indexOf(char.toUpperCase())\n} // END charValue\n\nfunction fromBase32(b32){\n if (0 === b32.length) {\n return 0\n }\n return charValue(b32.slice(-1)) + fromBase32(b32.slice(0,-1)) * 32\n} // END fromBase32\n*/\nfunction toBase32 (n){\n const remainder = Math.floor(n/32)\n const current = n % 32\n if (0 === remainder) {\n return alphabet[current]\n }\n return toBase32(remainder)+alphabet[current]\n} // END toBase32\n\nfunction jenkinsOneAtATimeHash(keyString){\n \n var hash = 0\n \n for (var charIndex = 0; charIndex < keyString.length; ++charIndex)\n {\n hash += keyString.charCodeAt(charIndex);\n hash += hash << 10;\n hash ^= hash >> 6;\n }\n hash += hash << 3;\n hash ^= hash >> 11;\n //4,294,967,295 is FFFFFFFF, the maximum 32 bit unsigned integer value, used here as a mask.\n return (((hash + (hash << 15)) & 4294967295) >>> 0)\n} // END jenkinsOneAtATimeHash\n\nfunction messageHash(messageSt){\n return toBase32(jenkinsOneAtATimeHash(messageSt))\n} // END messageHash\n\nmodule.exports = messageHash", "//JsonSuperSet\n\n// TODO: add tests\n// check for any repeated ref not just cyclical references\n// support nasted array a<![,,[D]]>:[\"a\",\"b\",[Date]]\n// support array for the same type a<![*D]>:[Date,Date,Date]\n\nfunction encode(obj) {\n const tagLookup = {\n '[object RegExp]': 'R',\n '[object Date]': 'D',\n '[object Error]': 'E',\n \"[object Undefined]\": 'U',\n \"[object Map]\": 'M',\n \"[object Set]\": 'S',\n };\n const visited = new WeakMap();\n\n function encodeValue(value, path = '') {\n const type = typeof value;\n const tag = tagLookup[Object.prototype.toString.call(value)];\n // console.log({tag,value,path})\n if (tag !== undefined) {\n if ('D' === tag) return [tag, value.valueOf()];\n if ('E' === tag) return [tag, [value.name, value.message, value.stack]];\n if ('R' === tag) return [tag, value.toString()];\n if ('U' === tag) return [tag, null];\n if ('S' === tag) return [tag, Array.from(value)];\n if ('M' === tag) return [tag, Object.fromEntries(value)];\n\n return [tag, JSON.stringify(value)];\n } else if (type === 'object' && value !== null) {\n /*if (value.$ID) {\n return ['', value.$ID];\n }*/\n if (visitedEncode.has(value)) {\n return ['P', visitedEncode.get(value)];\n }\n visitedEncode.set(value, path);\n const isArray = Array.isArray(value);\n // keep index with undefined in Array\n const keys = isArray ? Array.from(Array(value.length).keys()) : Object.keys(value);\n const result = isArray ? [] : {};\n const typesFound = [];\n for (let i = 0; i < keys.length; i++) {\n const key = keys[i];\n const [t, v] = encodeValue(value[key], key);\n // console.log([t, v])\n if (isArray) {\n typesFound.push(t);\n result.push(v);\n // remove key with undefined from Objects\n } else if (value[key] !== undefined) {\n result[key + (t ? `<!${t}>` : '')] = v;\n }\n }\n\n visited.delete(value);\n if (isArray && typesFound.find((t) => !!t)) {\n return [`[${typesFound.join()}]`, result];\n }\n return ['', result];\n } else {\n return ['', value];\n }\n } // END encodeValue\n\n let keys = [];\n // console.log(obj)\n if (Array.isArray(obj)) {\n keys = Array.from(Array(obj.length).keys())\n } else {\n keys = Object.keys(obj);\n }\n\n // Track root object to handle self-references\n const visitedEncode = new WeakMap();\n visitedEncode.set(obj, []); // Root path is empty array\n\n function encodeValueWithVisited(value, path = []) {\n const type = typeof value;\n const tag = tagLookup[Object.prototype.toString.call(value)];\n if (tag !== undefined) {\n if ('D' === tag) return [tag, value.valueOf()];\n if ('E' === tag) return [tag, [value.name, value.message, value.stack]];\n if ('R' === tag) return [tag, value.toString()];\n if ('U' === tag) return [tag, null];\n if ('S' === tag) return [tag, Array.from(value)];\n if ('M' === tag) return [tag, Object.fromEntries(value)];\n return [tag, JSON.stringify(value)];\n } else if (type === 'object' && value !== null) {\n if (visitedEncode.has(value)) {\n return ['P', visitedEncode.get(value)]; // Return array path\n }\n visitedEncode.set(value, path);\n const isArray = Array.isArray(value);\n const objKeys = isArray ? Array.from(Array(value.length).keys()) : Object.keys(value);\n const result = isArray ? [] : {};\n const typesFound = [];\n for (let i = 0; i < objKeys.length; i++) {\n const key = objKeys[i];\n const [t, v] = encodeValueWithVisited(value[key], [...path, key]); // Append key to path array\n if (isArray) {\n typesFound.push(t);\n result.push(v);\n } else if (value[key] !== undefined) {\n result[key + (t ? `<!${t}>` : '')] = v;\n }\n }\n if (isArray && typesFound.find((t) => !!t)) {\n return [`[${typesFound.join()}]`, result];\n }\n return ['', result];\n } else {\n return ['', value];\n }\n }\n\n const result = {};\n for (let i = 0; i < keys.length; i++) {\n const key = keys[i];\n // remove key with undefined from Objects\n if (obj[key] !== undefined) {\n const [t, v] = encodeValueWithVisited(obj[key], [key]); // Start path with single key\n result[key + (t ? `<!${t}>` : '')] = v;\n }\n }\n return result;\n} // END encode\n\nfunction stringify(obj) {\n return JSON.stringify(encode(obj))\n}\n\n\nfunction parse(encoded) {\n return decode(JSON.parse(encoded))\n}\n\nfunction decode(data) {\n const result = {};\n const pointers2Res = [];\n const tagLookup = {\n R: (s) => new RegExp(s),\n D: (n) => new Date(n),\n P: function (sourceToPointAt, replaceAtThisPlace) {\n // Both paths are now arrays\n pointers2Res.push([sourceToPointAt, replaceAtThisPlace]);\n return null; // Placeholder, will be replaced by changeAttributeReference\n },\n E: ([name, message, stack]) => {\n let err;\n try {\n err = new global[name](message);\n if (err instanceof Error) err.stack = stack;\n else throw {};\n } catch (e) {\n err = new Error(message);\n err.name = name;\n err.stack = stack;\n }\n return err;\n },\n U: () => undefined,\n S: (a) => new Set(a),\n M: (o) => new Map(Object.entries(o))\n };\n const visited = new Map();\n\n function decodeValue(name, tag, val) {\n // this is now an array path\n const currentPath = Array.isArray(this) ? this : [];\n\n if (tag in tagLookup) {\n return tagLookup[tag](val, currentPath);\n } else if (Array.isArray(val)) {\n if (tag && tag.startsWith('[')) {\n const typeTags = tag.slice(1, -1).split(',');\n const res = [];\n for (let i = 0; i < val.length; i++) {\n // Pass path with array index appended\n const itemPath = [...currentPath, i];\n const decodedValue = decodeValue.call(\n itemPath,\n i.toString(),\n typeTags[i],\n val[i]\n );\n res.push(decodedValue);\n }\n return res;\n } else {\n const res = [];\n for (let i = 0; i < val.length; i++) {\n const decodedValue = decodeValue.call([...currentPath, i], '', '', val[i]);\n res.push(decodedValue);\n }\n return res;\n }\n } else if ('object' === typeof val && val !== null) {\n if (visited.has(val)) {\n return visited.get(val);\n }\n visited.set(val, {});\n const res = {};\n for (const key in val) {\n const [nam, t] = parseKeyWithTags(key);\n const decodedValue = decodeValue.call(\n [...currentPath, nam],\n nam,\n t,\n val[key]\n );\n res[nam] = decodedValue;\n }\n visited.set(val, res);\n return res;\n } else {\n return val;\n }\n } // END decodeValue\n\n function parseKeyWithTags(key) {\n const match = key.match(/(.+)(<!(.)>)/);\n if (match) {\n return [match[1], match[3]];\n }\n // Try multi-character tags like array types [,D,]\n const multiMatch = key.match(/(.+)(<!!(.+)>)/);\n if (multiMatch) {\n return [multiMatch[1], multiMatch[3]];\n }\n // Also handle array type tags that start with [\n const arrayMatch = key.match(/(.+)(<!\\[(.*)>)/);\n if (arrayMatch) {\n return [arrayMatch[1], '[' + arrayMatch[3]];\n }\n return [key, undefined];\n } // END parseKeyWithTags\n\n for (const key in data) {\n const [name, tag] = parseKeyWithTags(key);\n // Start with path containing just the key name\n result[name] = decodeValue.call([name], name, tag, data[key]);\n }\n pointers2Res.forEach(changeAttributeReference.bind(null, result));\n return result;\n} // END decode\n\nfunction changeAttributeReference(obj, [refPath, attrPath]) {\n // refPath and attrPath are now arrays, no splitting needed\n const refKeys = refPath || [];\n const attrKeys = attrPath || [];\n\n // Get the reference target by traversing refPath\n let ref = obj;\n for (let i = 0; i < refKeys.length; i++) {\n ref = ref[refKeys[i]];\n }\n\n // Get the parent of the attribute to set\n let attr = obj;\n for (let i = 0; i < attrKeys.length - 1; i++) {\n attr = attr[attrKeys[i]];\n }\n\n // Set the attribute to point to the reference\n attr[attrKeys[attrKeys.length - 1]] = ref;\n return obj;\n} // END changeAttributeReference\n\n\nmodule.exports = { parse, stringify, encode, decode };\n", "import messageHash from '../utils/messageHash'\nimport jss from '../utils/jss'\nimport { createStreamingTransport } from './transports/streaming'\n\nlet connect;\n\n// Connection state enum\nconst ConnectionState = {\n Offline: 'offline', // navigator.onLine = false\n Walled: 'walled', // Captive portal detected (ping failed)\n Disconnected: 'disconnected',\n Connecting: 'connecting',\n Connected: 'connected',\n Closing: 'closing'\n}\n\n// Connection state tracking - start with offline check\nlet connectionState = (typeof navigator !== 'undefined' && !navigator.onLine)\n ? ConnectionState.Offline\n : ConnectionState.Disconnected\nconst connectionChangeListeners = []\n\nfunction notifyConnectionChange(newState) {\n if (connectionState !== newState) {\n connectionState = newState\n connectionChangeListeners.forEach(fn => fn(newState))\n }\n}\n\n// Configuration\nlet configuredTransport = 'auto' // 'auto' | 'websocket' | 'polling'\n\n// Transport state\nlet currentTransport = null // 'websocket' | 'polling'\nlet streamingTransport = null\nlet wsRetryTimer = null\nlet networkCheckTimer = null\nconst WS_FALLBACK_TIMEOUT = 4000 // Time to wait for WS before fallback\nconst WS_RETRY_INTERVAL = 30000 // Retry WebSocket while in polling mode\nconst PING_TIMEOUT = 3000 // Timeout for ping check\nconst MAX_PING_CLOCK_SKEW = 60000 // Max allowed time difference (60s)\n\n/**\n * Check if running in dev/local mode\n */\nfunction isDevMode() {\n if (typeof window === 'undefined') return false\n return ['localhost', '127.0.0.1', '[::1]'].includes(window.location.hostname)\n}\n\n/**\n * Build ping URL for captive portal detection\n */\nfunction getPingUrl() {\n const hostname = window.location.hostname\n const isHttps = window.location.protocol === 'https:'\n const port = window.location.port || (isHttps ? 443 : 80)\n const protocol = isHttps ? 'https' : 'http'\n const portSuffix = (port !== 80 && port !== 443) ? `:${port}` : ''\n return `${protocol}://${hostname}${portSuffix}/api/ape/ping`\n}\n\n/**\n * Check for captive portal by pinging /api/ape/ping\n * Returns 'ok' if real internet, 'walled' if captive portal detected\n */\nasync function checkCaptivePortal() {\n try {\n const controller = new AbortController()\n const timeoutId = setTimeout(() => controller.abort(), PING_TIMEOUT)\n\n const response = await fetch(getPingUrl(), {\n cache: 'no-store',\n signal: controller.signal\n })\n clearTimeout(timeoutId)\n\n if (!response.ok) {\n if (isDevMode()) {\n console.error('\uD83E\uDD8D [DEV] Ping failed: HTTP', response.status)\n }\n return 'walled'\n }\n\n const data = await response.json()\n\n // Verify response is genuine (not a captive portal redirect page)\n if (data?.ok !== true) {\n if (isDevMode()) {\n console.error('\uD83E\uDD8D [DEV] Ping failed: invalid response', data)\n }\n return 'walled'\n }\n\n // Validate timestamp to detect proxy replay attacks\n if (typeof data.ts === 'number') {\n const now = Date.now()\n const skew = Math.abs(now - data.ts)\n if (skew > MAX_PING_CLOCK_SKEW) {\n if (isDevMode()) {\n console.error('\uD83E\uDD8D [DEV] Ping failed: timestamp too old/stale (skew:', skew, 'ms)')\n }\n return 'walled'\n }\n }\n\n return 'ok'\n } catch (err) {\n if (isDevMode()) {\n console.error('\uD83E\uDD8D [DEV] Ping failed:', err.message || err)\n }\n return 'walled'\n }\n}\n\n/**\n * Setup navigator.onLine event listeners\n */\nfunction setupOnlineListeners() {\n if (typeof window === 'undefined') return\n\n window.addEventListener('online', () => {\n console.log('\uD83E\uDD8D Browser went online, checking network...')\n // Trigger reconnection attempt\n attemptConnection()\n })\n\n window.addEventListener('offline', () => {\n console.log('\uD83E\uDD8D Browser went offline')\n notifyConnectionChange(ConnectionState.Offline)\n })\n}\n\n// Setup listeners on module load (browser only)\nif (typeof window !== 'undefined') {\n setupOnlineListeners()\n}\n\n\n\n/**\n * Get WebSocket URL - auto-detects from window.location, keeps /api/ape path\n */\nfunction getSocketUrl() {\n const hostname = window.location.hostname\n const localServers = [\"localhost\", \"127.0.0.1\", \"[::1]\"]\n const isLocal = localServers.includes(hostname)\n const isHttps = window.location.protocol === \"https:\"\n\n // Use window.location.port if available, otherwise fallback (9010 for local dev, 443/80 for prod)\n const port = window.location.port || (isLocal ? 9010 : (isHttps ? 443 : 80))\n\n // Build URL - keep /api/ape path\n const protocol = isHttps ? \"wss\" : \"ws\"\n const portSuffix = (port !== 80 && port !== 443) ? `:${port}` : \"\"\n\n return `${protocol}://${hostname}${portSuffix}/api/ape`\n}\n\nlet reconnect = false\nconst connectTimeout = 5000\nconst totalRequestTimeout = 10000\n//const location = window.location\n\nconst joinKey = \"/\"\n// Properties accessed directly on `ape` that should NOT be intercepted\nconst reservedKeys = new Set(['on', 'onConnectionChange', 'transport'])\nconst handler = {\n get(fn, key) {\n // Skip proxy interception for reserved keys - return actual property\n if (reservedKeys.has(key)) {\n return fn[key]\n }\n const wrapperFn = function (a, b) {\n let path = joinKey + key, body;\n if (2 === arguments.length) {\n path += a\n body = b\n } else {\n body = a\n }\n return fn(path, body)\n }\n return new Proxy(wrapperFn, handler)\n } // END get\n}\n\nfunction wrap(api) {\n return new Proxy(api, handler)\n}\n\nlet __socket = false, ready = false, wsSend = false;\nconst waitingOn = {};\n\nlet aWaitingSend = []\nconst receiverArray = [];\nconst ofTypesOb = {};\n\n/**\n * Switch to streaming transport (HTTP long polling fallback)\n */\nfunction switchToStreaming() {\n console.log('\uD83E\uDD8D Switching to HTTP streaming transport')\n currentTransport = 'polling'\n\n if (!streamingTransport) {\n streamingTransport = createStreamingTransport()\n\n // Handle incoming messages from streaming transport\n streamingTransport.onMessage = async (msg) => {\n const { err, type, data } = msg\n\n // Process linked resources and shared files\n let processedData = data\n if (data && !err) {\n try {\n processedData = await fetchLinkedResources(data)\n processedData = await fetchSharedFiles(processedData)\n } catch (fetchErr) {\n console.error(`\uD83E\uDD8D Failed to hydrate streaming data:`, fetchErr)\n }\n }\n\n // Dispatch to type-specific handlers\n if (ofTypesOb[type]) {\n ofTypesOb[type].forEach(worker => worker({ err, type, data: processedData }))\n }\n // Dispatch to general handlers\n receiverArray.forEach(worker => worker({ err, type, data: processedData }))\n }\n\n streamingTransport.onOpen = () => {\n ready = true\n notifyConnectionChange(ConnectionState.Connected)\n console.log('\uD83E\uDD8D HTTP streaming connected')\n\n // Flush waiting messages\n aWaitingSend.forEach(({ type, data, resolve, reject, waiting, createdAt, timer }) => {\n clearTimeout(timer)\n const resultPromise = streamingSend(type, data, createdAt)\n if (waiting) {\n resultPromise.then(resolve).catch(reject)\n }\n })\n aWaitingSend = []\n\n // Start background WebSocket retry\n startWsRetry()\n }\n\n streamingTransport.onClose = () => {\n ready = false\n notifyConnectionChange(ConnectionState.Disconnected)\n }\n\n streamingTransport.onError = (err) => {\n console.error('\uD83E\uDD8D Streaming error:', err)\n }\n }\n\n streamingTransport.connect()\n}\n\n/**\n * Send via streaming transport\n */\nfunction streamingSend(type, data, createdAt) {\n return streamingTransport.send(type, data, createdAt)\n}\n\n/**\n * Start background retry for WebSocket (while in polling mode)\n */\nfunction startWsRetry() {\n if (wsRetryTimer) return\n if (currentTransport !== 'polling') return\n if (configuredTransport === 'polling') return // User explicitly wants polling only\n\n wsRetryTimer = setInterval(() => {\n if (currentTransport !== 'polling') {\n clearInterval(wsRetryTimer)\n wsRetryTimer = null\n return\n }\n\n console.log('\uD83E\uDD8D Attempting WebSocket reconnection...')\n tryWebSocket(true)\n }, WS_RETRY_INTERVAL)\n}\n\n/**\n * Try to establish WebSocket connection\n * @param {boolean} isRetry - If true, this is a background retry attempt\n */\nfunction tryWebSocket(isRetry = false) {\n const ws = new WebSocket(getSocketUrl())\n let fallbackTimer = null\n\n // Set fallback timeout (only for initial connection, not retries)\n if (!isRetry && configuredTransport === 'auto') {\n fallbackTimer = setTimeout(() => {\n if (ws.readyState !== WebSocket.OPEN) {\n console.log('\uD83E\uDD8D WebSocket timeout, falling back to HTTP streaming')\n ws.close()\n switchToStreaming()\n }\n }, WS_FALLBACK_TIMEOUT)\n }\n\n ws.onopen = () => {\n if (fallbackTimer) clearTimeout(fallbackTimer)\n\n // If this is a retry and we're in polling mode, switch back to WebSocket\n if (isRetry && currentTransport === 'polling') {\n console.log('\uD83E\uDD8D WebSocket reconnected, switching from HTTP streaming')\n if (streamingTransport) {\n streamingTransport.close()\n }\n if (wsRetryTimer) {\n clearInterval(wsRetryTimer)\n wsRetryTimer = null\n }\n }\n\n currentTransport = 'websocket'\n __socket = ws\n ready = true\n notifyConnectionChange(ConnectionState.Connected)\n\n aWaitingSend.forEach(({ type, data, resolve, reject, waiting, createdAt, timer }) => {\n clearTimeout(timer)\n const resultPromise = wsSend(type, data, createdAt)\n if (waiting) {\n resultPromise.then(resolve).catch(reject)\n }\n })\n aWaitingSend = []\n }\n\n ws.onmessage = async function (event) {\n const { err, type, queryId, data } = jss.parse(event.data)\n\n // Messages with queryId must fulfill matching promise\n if (queryId) {\n if (waitingOn[queryId]) {\n // Check for linked resources and fetch them before resolving\n if (data && !err) {\n try {\n let hydratedData = await fetchLinkedResources(data)\n hydratedData = await fetchSharedFiles(hydratedData)\n waitingOn[queryId](err, hydratedData)\n } catch (fetchErr) {\n waitingOn[queryId](fetchErr, null)\n }\n } else {\n waitingOn[queryId](err, data)\n }\n delete waitingOn[queryId]\n } else {\n console.error(`\uD83E\uDD8D No matching queryId: ${queryId}`)\n }\n return\n }\n\n // Only messages WITHOUT queryId go to setOnReceiver\n let processedData = data\n if (data && !err) {\n try {\n processedData = await fetchLinkedResources(data)\n processedData = await fetchSharedFiles(processedData)\n } catch (fetchErr) {\n console.error(`\uD83E\uDD8D Failed to hydrate broadcast data:`, fetchErr)\n }\n }\n\n if (ofTypesOb[type]) {\n ofTypesOb[type].forEach(worker => worker({ err, type, data: processedData }))\n }\n receiverArray.forEach(worker => worker({ err, type, data: processedData }))\n }\n\n ws.onerror = function (err) {\n if (fallbackTimer) clearTimeout(fallbackTimer)\n console.error('socket ERROR:', err)\n\n // On initial connection error in auto mode, fallback to streaming\n if (!isRetry && configuredTransport === 'auto' && !ready) {\n switchToStreaming()\n }\n }\n\n ws.onclose = function (event) {\n if (fallbackTimer) clearTimeout(fallbackTimer)\n console.warn('socket disconnect:', event)\n __socket = false\n ready = false\n\n // Only notify disconnected if we're on websocket transport\n if (currentTransport === 'websocket') {\n notifyConnectionChange(ConnectionState.Disconnected)\n setTimeout(() => reconnect && connectSocket(), 500)\n }\n }\n}\n\n/**\n * Find all L-tagged (binary link) properties in data\n * Returns array of { path, hash }\n */\nfunction findLinkedResources(obj, path = '') {\n const resources = []\n\n if (obj === null || obj === undefined || typeof obj !== 'object') {\n return resources\n }\n\n if (Array.isArray(obj)) {\n for (let i = 0; i < obj.length; i++) {\n resources.push(...findLinkedResources(obj[i], path ? `${path}.${i}` : String(i)))\n }\n return resources\n }\n\n for (const key of Object.keys(obj)) {\n // Check for L-tag in key (from JJS encoding: key<!L>)\n if (key.endsWith('<!L>')) {\n const cleanKey = key.slice(0, -4)\n const hash = obj[key]\n resources.push({\n path: path ? `${path}.${cleanKey}` : cleanKey,\n hash,\n originalKey: key\n })\n } else {\n resources.push(...findLinkedResources(obj[key], path ? `${path}.${key}` : key))\n }\n }\n\n return resources\n}\n\n/**\n * Find all F-tagged (shared file) properties in data\n * Returns array of { path, hash, originalKey }\n */\nfunction findFileTags(obj, path = '') {\n const files = []\n\n if (obj === null || obj === undefined || typeof obj !== 'object') {\n return files\n }\n\n if (Array.isArray(obj)) {\n for (let i = 0; i < obj.length; i++) {\n files.push(...findFileTags(obj[i], path ? `${path}.${i}` : String(i)))\n }\n return files\n }\n\n for (const key of Object.keys(obj)) {\n // Check for F-tag in key (client-to-client shared file marker)\n if (key.endsWith('<!F>')) {\n const cleanKey = key.slice(0, -4)\n const hash = obj[key]\n files.push({\n path: path ? `${path}.${cleanKey}` : cleanKey,\n hash,\n originalKey: key\n })\n } else {\n files.push(...findFileTags(obj[key], path ? `${path}.${key}` : key))\n }\n }\n\n return files\n}\n\n/**\n * Clean up F-tagged keys (rename key<!F> to key)\n */\nfunction cleanFileTags(obj) {\n if (obj === null || obj === undefined || typeof obj !== 'object') {\n return obj\n }\n\n if (Array.isArray(obj)) {\n return obj.map(cleanFileTags)\n }\n\n const cleaned = {}\n for (const key of Object.keys(obj)) {\n if (key.endsWith('<!F>')) {\n const cleanKey = key.slice(0, -4)\n cleaned[cleanKey] = obj[key]\n } else {\n cleaned[key] = cleanFileTags(obj[key])\n }\n }\n return cleaned\n}\n\n/**\n * Fetch shared files (client-to-client transfers)\n * Retries if upload is still in progress\n */\nasync function fetchSharedFiles(data, maxRetries = 5) {\n const files = findFileTags(data)\n\n if (files.length === 0) {\n return data\n }\n\n console.log(`\uD83E\uDD8D Fetching ${files.length} shared file(s)`)\n\n const cleanedData = cleanFileTags(data)\n\n const hostname = window.location.hostname\n const isLocal = [\"localhost\", \"127.0.0.1\", \"[::1]\"].includes(hostname)\n const isHttps = window.location.protocol === \"https:\"\n const port = window.location.port || (isLocal ? 9010 : (isHttps ? 443 : 80))\n const protocol = isHttps ? \"https\" : \"http\"\n const portSuffix = (port !== 80 && port !== 443) ? `:${port}` : \"\"\n const baseUrl = `${protocol}://${hostname}${portSuffix}`\n\n await Promise.all(files.map(async ({ path, hash }) => {\n let retries = 0\n let backoff = 100 // Start with 100ms\n\n while (retries < maxRetries) {\n try {\n const response = await fetch(`${baseUrl}/api/ape/data/${hash}`, {\n credentials: 'include'\n })\n\n if (!response.ok) {\n // 404 might mean file not uploaded yet, retry\n if (response.status === 404 && retries < maxRetries - 1) {\n retries++\n await new Promise(r => setTimeout(r, backoff))\n backoff *= 2 // Exponential backoff\n continue\n }\n throw new Error(`Failed to fetch shared file: ${response.status}`)\n }\n\n const arrayBuffer = await response.arrayBuffer()\n setValueAtPath(cleanedData, path, arrayBuffer)\n\n // Check if upload is still in progress\n const isComplete = response.headers.get('X-Ape-Complete') === '1'\n if (!isComplete) {\n console.log(`\uD83E\uDD8D Shared file ${hash} still uploading (${response.headers.get('X-Ape-Total-Received') || '?'} bytes)`)\n }\n break\n } catch (err) {\n if (retries >= maxRetries - 1) {\n console.error(`\uD83E\uDD8D Failed to fetch shared file at ${path}:`, err)\n setValueAtPath(cleanedData, path, null)\n }\n retries++\n await new Promise(r => setTimeout(r, backoff))\n backoff *= 2\n }\n }\n }))\n\n return cleanedData\n}\n\n/**\n * Set a value at a nested path in an object\n */\nfunction setValueAtPath(obj, path, value) {\n const parts = path.split('.')\n let current = obj\n\n for (let i = 0; i < parts.length - 1; i++) {\n current = current[parts[i]]\n }\n\n current[parts[parts.length - 1]] = value\n}\n\n/**\n * Clean up L-tagged keys (rename key<!L> to key)\n */\nfunction cleanLinkedKeys(obj) {\n if (obj === null || obj === undefined || typeof obj !== 'object') {\n return obj\n }\n\n if (Array.isArray(obj)) {\n return obj.map(cleanLinkedKeys)\n }\n\n const cleaned = {}\n for (const key of Object.keys(obj)) {\n if (key.endsWith('<!L>')) {\n const cleanKey = key.slice(0, -4)\n cleaned[cleanKey] = obj[key]\n } else {\n cleaned[key] = cleanLinkedKeys(obj[key])\n }\n }\n return cleaned\n}\n\n/**\n * Fetch binary resources and hydrate data object\n */\nasync function fetchLinkedResources(data, clientId) {\n const resources = findLinkedResources(data)\n\n if (resources.length === 0) {\n return data\n }\n\n console.log(`\uD83E\uDD8D Fetching ${resources.length} binary resource(s)`)\n\n const cleanedData = cleanLinkedKeys(data)\n\n const hostname = window.location.hostname\n const isLocal = [\"localhost\", \"127.0.0.1\", \"[::1]\"].includes(hostname)\n const isHttps = window.location.protocol === \"https:\"\n const port = window.location.port || (isLocal ? 9010 : (isHttps ? 443 : 80))\n const protocol = isHttps ? \"https\" : \"http\"\n const portSuffix = (port !== 80 && port !== 443) ? `:${port}` : \"\"\n const baseUrl = `${protocol}://${hostname}${portSuffix}`\n\n await Promise.all(resources.map(async ({ path, hash }) => {\n try {\n const response = await fetch(`${baseUrl}/api/ape/data/${hash}`, {\n credentials: 'include',\n headers: {\n 'X-Ape-Client-Id': clientId || ''\n }\n })\n\n if (!response.ok) {\n throw new Error(`Failed to fetch binary resource: ${response.status}`)\n }\n\n const arrayBuffer = await response.arrayBuffer()\n setValueAtPath(cleanedData, path, arrayBuffer)\n } catch (err) {\n console.error(`\uD83E\uDD8D Failed to fetch binary resource at ${path}:`, err)\n setValueAtPath(cleanedData, path, null)\n }\n }))\n\n return cleanedData\n}\n\n/**\n * Attempt to establish connection with network pre-checks\n */\nasync function attemptConnection() {\n // Check if browser is online\n if (typeof navigator !== 'undefined' && !navigator.onLine) {\n notifyConnectionChange(ConnectionState.Offline)\n return\n }\n\n // Perform captive portal check\n notifyConnectionChange(ConnectionState.Connecting)\n const pingResult = await checkCaptivePortal()\n\n if (pingResult === 'walled') {\n notifyConnectionChange(ConnectionState.Walled)\n // Retry network check periodically\n scheduleNetworkRetry()\n return\n }\n\n // Network is good, proceed with socket connection\n proceedWithConnection()\n}\n\n/**\n * Schedule a retry of network check (for walled/offline states)\n */\nfunction scheduleNetworkRetry() {\n if (networkCheckTimer) return\n networkCheckTimer = setTimeout(() => {\n networkCheckTimer = null\n attemptConnection()\n }, WS_RETRY_INTERVAL)\n}\n\n/**\n * Proceed with WebSocket/polling connection after network checks pass\n */\nfunction proceedWithConnection() {\n // Determine which transport to use\n if (configuredTransport === 'polling') {\n switchToStreaming()\n } else {\n // 'auto' or 'websocket' - try WebSocket first\n tryWebSocket(false)\n }\n}\n\nfunction connectSocket() {\n // Skip if already connected or connecting\n if (__socket && __socket.readyState !== WebSocket.CLOSED) {\n return buildClientInterface()\n }\n if (currentTransport === 'polling' && streamingTransport?.isConnected()) {\n return buildClientInterface()\n }\n if (connectionState === ConnectionState.Connecting) {\n return buildClientInterface()\n }\n\n // Start connection with network pre-checks\n attemptConnection()\n\n return buildClientInterface()\n}\n\n/**\n * Check if value is binary data (ArrayBuffer, typed array, or Blob)\n */\nfunction isBinaryData(value) {\n if (value === null || value === undefined) return false\n return value instanceof ArrayBuffer ||\n ArrayBuffer.isView(value) ||\n (typeof Blob !== 'undefined' && value instanceof Blob)\n}\n\n/**\n * Get binary type tag (A for ArrayBuffer, B for Blob)\n */\nfunction getBinaryTag(value) {\n if (typeof Blob !== 'undefined' && value instanceof Blob) return 'B'\n return 'A'\n}\n\n/**\n * Generate a simple hash for binary upload\n */\nfunction generateUploadHash(path) {\n let hash = 0\n for (let i = 0; i < path.length; i++) {\n const char = path.charCodeAt(i)\n hash = ((hash << 5) - hash) + char\n hash = hash & hash\n }\n return Math.abs(hash).toString(36)\n}\n\n/**\n * Find and extract binary data from payload\n * Returns { processedData, uploads: [{ path, hash, data, tag }] }\n */\nfunction processBinaryForUpload(data, path = '') {\n if (data === null || data === undefined) {\n return { processedData: data, uploads: [] }\n }\n\n if (isBinaryData(data)) {\n const tag = getBinaryTag(data)\n const hash = generateUploadHash(path || 'root')\n return {\n processedData: { [`__ape_upload__`]: hash },\n uploads: [{ path, hash, data, tag }]\n }\n }\n\n if (Array.isArray(data)) {\n const processedArray = []\n const allUploads = []\n\n for (let i = 0; i < data.length; i++) {\n const itemPath = path ? `${path}.${i}` : String(i)\n const { processedData, uploads } = processBinaryForUpload(data[i], itemPath)\n processedArray.push(processedData)\n allUploads.push(...uploads)\n }\n\n return { processedData: processedArray, uploads: allUploads }\n }\n\n if (typeof data === 'object') {\n const processedObj = {}\n const allUploads = []\n\n for (const key of Object.keys(data)) {\n const itemPath = path ? `${path}.${key}` : key\n const { processedData, uploads } = processBinaryForUpload(data[key], itemPath)\n\n // If this was binary data, mark the key with <!B> or <!A> tag\n if (uploads.length > 0 && processedData?.__ape_upload__) {\n const tag = uploads[uploads.length - 1].tag\n processedObj[`${key}<!${tag}>`] = processedData.__ape_upload__\n } else {\n processedObj[key] = processedData\n }\n allUploads.push(...uploads)\n }\n\n return { processedData: processedObj, uploads: allUploads }\n }\n\n return { processedData: data, uploads: [] }\n}\n\n/**\n * Find and extract binary data for SHARING (client-to-client)\n * Uses <!F> tag instead of <!A>/<!B>\n * Returns { processedData, shares: [{ path, hash, data }] }\n */\nfunction processBinaryForSharing(data, path = '') {\n if (data === null || data === undefined) {\n return { processedData: data, shares: [] }\n }\n\n if (isBinaryData(data)) {\n const hash = generateUploadHash(path || 'share')\n return {\n processedData: { [`__ape_share__`]: hash },\n shares: [{ path, hash, data }]\n }\n }\n\n if (Array.isArray(data)) {\n const processedArray = []\n const allShares = []\n\n for (let i = 0; i < data.length; i++) {\n const itemPath = path ? `${path}.${i}` : String(i)\n const { processedData, shares } = processBinaryForSharing(data[i], itemPath)\n processedArray.push(processedData)\n allShares.push(...shares)\n }\n\n return { processedData: processedArray, shares: allShares }\n }\n\n if (typeof data === 'object') {\n const processedObj = {}\n const allShares = []\n\n for (const key of Object.keys(data)) {\n const itemPath = path ? `${path}.${key}` : key\n const { processedData, shares } = processBinaryForSharing(data[key], itemPath)\n\n // If this was binary data, mark the key with <!F> tag\n if (shares.length > 0 && processedData?.__ape_share__) {\n processedObj[`${key}<!F>`] = processedData.__ape_share__\n } else {\n processedObj[key] = processedData\n }\n allShares.push(...shares)\n }\n\n return { processedData: processedObj, shares: allShares }\n }\n\n return { processedData: data, shares: [] }\n}\n\n/**\n * Upload shared files via HTTP PUT\n * Uses different endpoint pattern for streaming files\n */\nasync function uploadSharedFiles(shares) {\n if (shares.length === 0) return\n\n // Build base URL\n const hostname = window.location.hostname\n const isLocal = [\"localhost\", \"127.0.0.1\", \"[::1]\"].includes(hostname)\n const isHttps = window.location.protocol === \"https:\"\n const port = window.location.port || (isLocal ? 9010 : (isHttps ? 443 : 80))\n const protocol = isHttps ? \"https\" : \"http\"\n const portSuffix = (port !== 80 && port !== 443) ? `:${port}` : \"\"\n const baseUrl = `${protocol}://${hostname}${portSuffix}`\n\n console.log(`\uD83E\uDD8D Uploading ${shares.length} shared file(s)`)\n\n await Promise.all(shares.map(async ({ hash, data }) => {\n try {\n // For shared files, use upload pattern with hash as both queryId and pathHash\n const response = await fetch(`${baseUrl}/api/ape/data/_share/${hash}`, {\n method: 'PUT',\n credentials: 'include',\n headers: {\n 'Content-Type': 'application/octet-stream'\n },\n body: data\n })\n\n if (!response.ok) {\n throw new Error(`Shared upload failed: ${response.status}`)\n }\n } catch (err) {\n console.error(`\uD83E\uDD8D Failed to upload shared file ${hash}:`, err)\n throw err\n }\n }))\n}\n\n/**\n * Upload binary data via HTTP PUT\n */\nasync function uploadBinaryData(queryId, uploads) {\n if (uploads.length === 0) return\n\n // Build base URL\n const hostname = window.location.hostname\n const isLocal = [\"localhost\", \"127.0.0.1\", \"[::1]\"].includes(hostname)\n const isHttps = window.location.protocol === \"https:\"\n const port = window.location.port || (isLocal ? 9010 : (isHttps ? 443 : 80))\n const protocol = isHttps ? \"https\" : \"http\"\n const portSuffix = (port !== 80 && port !== 443) ? `:${port}` : \"\"\n const baseUrl = `${protocol}://${hostname}${portSuffix}`\n\n console.log(`\uD83E\uDD8D Uploading ${uploads.length} binary file(s)`)\n\n await Promise.all(uploads.map(async ({ hash, data }) => {\n try {\n const response = await fetch(`${baseUrl}/api/ape/data/${queryId}/${hash}`, {\n method: 'PUT',\n credentials: 'include',\n headers: {\n 'Content-Type': 'application/octet-stream'\n },\n body: data\n })\n\n if (!response.ok) {\n throw new Error(`Upload failed: ${response.status}`)\n }\n } catch (err) {\n console.error(`\uD83E\uDD8D Failed to upload binary at ${hash}:`, err)\n throw err\n }\n }))\n}\n\nwsSend = function (type, data, createdAt, dirctCall) {\n let rej, promiseIsLive = false;\n const timeLetForReqToBeMade = (createdAt + totalRequestTimeout) - Date.now()\n\n const timer = setTimeout(() => {\n if (promiseIsLive) {\n rej(new Error(\"Request Timedout for :\" + type))\n }\n }, timeLetForReqToBeMade);\n\n // Process binary data for upload\n const { processedData, uploads } = processBinaryForUpload(data)\n\n const payload = {\n type,\n data: processedData,\n //referer:window.location.href,\n createdAt: new Date(createdAt),\n requestedAt: dirctCall ? undefined\n : new Date()\n }\n const message = jss.stringify(payload)\n const queryId = messageHash(message);\n\n const replyPromise = new Promise((resolve, reject) => {\n rej = reject\n waitingOn[queryId] = (err, result) => {\n clearTimeout(timer)\n replyPromise.then = next.bind(replyPromise)\n if (err) {\n reject(err)\n } else {\n resolve(result)\n }\n }\n __socket.send(message);\n\n // Upload binary data after sending WS message\n if (uploads.length > 0) {\n uploadBinaryData(queryId, uploads).catch(err => {\n console.error('\uD83E\uDD8D Binary upload failed:', err)\n // The server will timeout waiting for the upload\n })\n }\n });\n const next = replyPromise.then;\n replyPromise.then = worker => {\n promiseIsLive = true;\n replyPromise.then = next.bind(replyPromise)\n replyPromise.catch = err.bind(replyPromise)\n return next.call(replyPromise, worker)\n }\n const err = replyPromise.catch;\n replyPromise.catch = worker => {\n promiseIsLive = true;\n replyPromise.catch = err.bind(replyPromise)\n replyPromise.then = next.bind(replyPromise)\n return err.call(replyPromise, worker)\n }\n return replyPromise\n} // END wsSend\n\n\nconst sender = (type, data) => {\n if (\"string\" !== typeof type) {\n throw new Error(\"Missing Path vaule\")\n }\n\n const createdAt = Date.now()\n\n if (ready) {\n return wsSend(type, data, createdAt, true)\n }\n\n const timeLetForReqToBeMade = (createdAt + connectTimeout) - Date.now() // 5sec for reconnect\n\n const timer = setTimeout(() => {\n const errMessage = \"Request not sent for :\" + type\n if (payload.waiting) {\n payload.reject(new Error(errMessage))\n } else {\n throw new Error(errMessage)\n }\n }, timeLetForReqToBeMade);\n\n const payload = { type, data, resolve: undefined, reject: undefined, waiting: false, createdAt, timer };\n const waitingOnOpen = new Promise((res, rej) => { payload.resolve = res; payload.reject = rej; })\n\n const waitingOnOpenThen = waitingOnOpen.then;\n const waitingOnOpenCatch = waitingOnOpen.catch;\n waitingOnOpen.then = worker => {\n payload.waiting = true;\n waitingOnOpen.then = waitingOnOpenThen.bind(waitingOnOpen)\n waitingOnOpen.catch = waitingOnOpenCatch.bind(waitingOnOpen)\n return waitingOnOpenThen.call(waitingOnOpen, worker)\n }\n waitingOnOpen.catch = worker => {\n payload.waiting = true;\n waitingOnOpen.catch = waitingOnOpenCatch.bind(waitingOnOpen)\n waitingOnOpen.then = waitingOnOpenThen.bind(waitingOnOpen)\n return waitingOnOpenCatch.call(waitingOnOpen, worker)\n }\n\n aWaitingSend.push(payload)\n if (!__socket) {\n connectSocket()\n }\n\n return waitingOnOpen\n} // END sender\n\n/**\n * Build the client interface object\n */\nfunction buildClientInterface() {\n return {\n sender: wrap(sender),\n setOnReceiver: (onTypeStFn, handlerFn) => {\n if (\"string\" === typeof onTypeStFn) {\n // Replace handler for this type (prevents duplicates in React StrictMode)\n ofTypesOb[onTypeStFn] = [handlerFn]\n } else {\n // For general receivers, prevent duplicates by checking\n if (!receiverArray.includes(onTypeStFn)) {\n receiverArray.push(onTypeStFn)\n }\n }\n },\n onConnectionChange: (handler) => {\n connectionChangeListeners.push(handler)\n // Immediately call with current state\n handler(connectionState)\n // Return unsubscribe function\n return () => {\n const idx = connectionChangeListeners.indexOf(handler)\n if (idx > -1) connectionChangeListeners.splice(idx, 1)\n }\n },\n // Expose current transport type (read-only)\n get transport() { return currentTransport }\n }\n}\n\nconnectSocket.autoReconnect = () => reconnect = true\nconnectSocket.ConnectionState = ConnectionState\nconnect = connectSocket\n\nexport default connect;\nexport { ConnectionState };\n", "import jss from '../../utils/jss'\n\n/**\n * HTTP Streaming transport - fallback when WebSocket is blocked\n * Uses fetch + ReadableStream for receiving, POST for sending\n */\n\n/**\n * Get base URL for polling endpoints\n */\nfunction getPollUrl() {\n const hostname = window.location.hostname\n const localServers = [\"localhost\", \"127.0.0.1\", \"[::1]\"]\n const isLocal = localServers.includes(hostname)\n const isHttps = window.location.protocol === \"https:\"\n\n // Use window.location.port if available, otherwise fallback (9010 for local dev, 443/80 for prod)\n const port = window.location.port || (isLocal ? 9010 : (isHttps ? 443 : 80))\n\n const protocol = isHttps ? \"https\" : \"http\"\n const portSuffix = (port !== 80 && port !== 443) ? `:${port}` : \"\"\n\n return `${protocol}://${hostname}${portSuffix}/api/ape/poll`\n}\n\n/**\n * Parse JSON objects from a streaming buffer by counting braces\n * Handles strings containing braces correctly\n */\nfunction parseStreamBuffer(buffer) {\n const messages = []\n let start = -1\n let depth = 0\n let inString = false\n let escaped = false\n\n for (let i = 0; i < buffer.length; i++) {\n const char = buffer[i]\n\n if (escaped) {\n escaped = false\n continue\n }\n\n if (char === '\\\\' && inString) {\n escaped = true\n continue\n }\n\n if (char === '\"') {\n inString = !inString\n continue\n }\n\n if (inString) continue\n\n if (char === '{') {\n if (depth === 0) {\n start = i\n }\n depth++\n } else if (char === '}') {\n depth--\n if (depth === 0 && start !== -1) {\n const jsonStr = buffer.slice(start, i + 1)\n try {\n messages.push(jss.parse(jsonStr))\n } catch (e) {\n console.error('\uD83E\uDD8D Failed to parse stream message:', e)\n }\n start = -1\n }\n }\n }\n\n // Return remaining buffer (incomplete message)\n const remaining = start !== -1 ? buffer.slice(start) : ''\n return { messages, remaining }\n}\n\n/**\n * Create streaming transport instance\n */\nfunction createStreamingTransport() {\n let isActive = false\n let abortController = null\n let streamBuffer = ''\n let reconnectTimer = null\n\n // Callbacks\n let onMessage = () => { }\n let onOpen = () => { }\n let onClose = () => { }\n let onError = () => { }\n\n /**\n * Start the streaming connection\n */\n async function connect() {\n if (isActive) return\n\n isActive = true\n abortController = new AbortController()\n\n try {\n const response = await fetch(getPollUrl(), {\n method: 'GET',\n credentials: 'include',\n signal: abortController.signal,\n headers: {\n 'Accept': 'application/json'\n }\n })\n\n if (!response.ok) {\n throw new Error(`Stream connect failed: ${response.status}`)\n }\n\n onOpen()\n\n const reader = response.body.getReader()\n const decoder = new TextDecoder()\n\n async function read() {\n while (isActive) {\n try {\n const { done, value } = await reader.read()\n\n if (done) {\n // Stream ended - reconnect\n scheduleReconnect()\n return\n }\n\n streamBuffer += decoder.decode(value, { stream: true })\n const { messages, remaining } = parseStreamBuffer(streamBuffer)\n streamBuffer = remaining\n\n for (const msg of messages) {\n // Skip heartbeat messages\n if (msg.type === '__heartbeat__') continue\n onMessage(msg)\n }\n } catch (readErr) {\n if (readErr.name === 'AbortError') return\n console.error('\uD83E\uDD8D Stream read error:', readErr)\n scheduleReconnect()\n return\n }\n }\n }\n\n read()\n\n } catch (err) {\n if (err.name === 'AbortError') return\n\n console.error('\uD83E\uDD8D Stream connection error:', err)\n onError(err)\n scheduleReconnect()\n }\n }\n\n /**\n * Schedule reconnection with small delay\n */\n function scheduleReconnect() {\n if (!isActive) return\n\n if (reconnectTimer) {\n clearTimeout(reconnectTimer)\n }\n\n reconnectTimer = setTimeout(() => {\n if (isActive) {\n connect()\n }\n }, 500)\n }\n\n /**\n * Send a message via POST\n */\n async function send(type, data, createdAt) {\n const payload = {\n type,\n data,\n createdAt: new Date(createdAt)\n }\n\n const response = await fetch(getPollUrl(), {\n method: 'POST',\n credentials: 'include',\n headers: {\n 'Content-Type': 'application/json'\n },\n body: jss.stringify(payload)\n })\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({ error: 'Unknown error' }))\n throw new Error(error.error || `Request failed: ${response.status}`)\n }\n\n const result = jss.parse(await response.text())\n return result.data\n }\n\n /**\n * Close the streaming connection\n */\n function close() {\n isActive = false\n\n if (reconnectTimer) {\n clearTimeout(reconnectTimer)\n reconnectTimer = null\n }\n\n if (abortController) {\n abortController.abort()\n abortController = null\n }\n\n streamBuffer = ''\n onClose()\n }\n\n return {\n connect,\n send,\n close,\n isConnected: () => isActive,\n set onMessage(fn) { onMessage = fn },\n set onOpen(fn) { onOpen = fn },\n set onClose(fn) { onClose = fn },\n set onError(fn) { onError = fn }\n }\n}\n\nexport { createStreamingTransport, getPollUrl }\n", "import connectSocket from './connectSocket.js'\n\nconst client = connectSocket()\nconnectSocket.autoReconnect()\n\n// Global API - use defineProperty to bypass Proxy interception\nwindow.api = client.sender\nObject.defineProperty(window.api, 'on', {\n value: client.setOnReceiver,\n writable: false,\n enumerable: false,\n configurable: false\n})\nObject.defineProperty(window.api, 'onConnectionChange', {\n value: client.onConnectionChange,\n writable: false,\n enumerable: false,\n configurable: false\n})\n// Read-only transport property - only ape can change this internally\nObject.defineProperty(window.api, 'transport', {\n get: () => client.transport,\n enumerable: false,\n configurable: false\n})\n"],
5
- "mappings": "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",
6
- "names": ["require_messageHash", "__commonJSMin", "exports", "module", "alphabet", "toBase32", "n", "remainder", "current", "jenkinsOneAtATimeHash", "keyString", "hash", "charIndex", "messageHash", "messageSt", "require_jss", "__commonJSMin", "exports", "module", "encode", "obj", "tagLookup", "visited", "encodeValue", "value", "path", "type", "tag", "visitedEncode", "isArray", "keys", "result", "typesFound", "i", "key", "t", "v", "encodeValueWithVisited", "objKeys", "stringify", "parse", "encoded", "decode", "data", "pointers2Res", "s", "n", "sourceToPointAt", "replaceAtThisPlace", "name", "message", "stack", "err", "a", "o", "decodeValue", "val", "currentPath", "typeTags", "res", "itemPath", "decodedValue", "nam", "parseKeyWithTags", "match", "multiMatch", "arrayMatch", "changeAttributeReference", "refPath", "attrPath", "refKeys", "attrKeys", "ref", "attr", "import_messageHash", "import_jss", "import_jss", "getPollUrl", "hostname", "isLocal", "isHttps", "port", "protocol", "portSuffix", "parseStreamBuffer", "buffer", "messages", "start", "depth", "inString", "escaped", "i", "char", "jsonStr", "jss", "e", "remaining", "createStreamingTransport", "isActive", "abortController", "streamBuffer", "reconnectTimer", "onMessage", "onOpen", "onClose", "onError", "connect", "response", "reader", "decoder", "read", "done", "value", "scheduleReconnect", "msg", "readErr", "err", "send", "type", "data", "createdAt", "payload", "error", "close", "fn", "connect", "ConnectionState", "connectionState", "connectionChangeListeners", "notifyConnectionChange", "newState", "fn", "configuredTransport", "currentTransport", "streamingTransport", "wsRetryTimer", "networkCheckTimer", "WS_FALLBACK_TIMEOUT", "WS_RETRY_INTERVAL", "PING_TIMEOUT", "MAX_PING_CLOCK_SKEW", "isDevMode", "getPingUrl", "hostname", "isHttps", "port", "protocol", "portSuffix", "checkCaptivePortal", "controller", "timeoutId", "response", "data", "now", "skew", "err", "setupOnlineListeners", "attemptConnection", "getSocketUrl", "isLocal", "reconnect", "connectTimeout", "totalRequestTimeout", "joinKey", "reservedKeys", "handler", "key", "wrapperFn", "a", "b", "path", "body", "wrap", "api", "__socket", "ready", "wsSend", "waitingOn", "aWaitingSend", "receiverArray", "ofTypesOb", "switchToStreaming", "createStreamingTransport", "msg", "type", "processedData", "fetchLinkedResources", "fetchSharedFiles", "fetchErr", "worker", "resolve", "reject", "waiting", "createdAt", "timer", "resultPromise", "streamingSend", "startWsRetry", "tryWebSocket", "isRetry", "ws", "fallbackTimer", "event", "queryId", "jss", "hydratedData", "connectSocket", "findLinkedResources", "obj", "resources", "i", "cleanKey", "hash", "findFileTags", "files", "cleanFileTags", "cleaned", "maxRetries", "cleanedData", "baseUrl", "retries", "backoff", "r", "arrayBuffer", "setValueAtPath", "value", "parts", "current", "cleanLinkedKeys", "clientId", "scheduleNetworkRetry", "proceedWithConnection", "buildClientInterface", "isBinaryData", "getBinaryTag", "generateUploadHash", "char", "processBinaryForUpload", "tag", "processedArray", "allUploads", "itemPath", "uploads", "processedObj", "uploadBinaryData", "queryId", "uploads", "hostname", "isLocal", "isHttps", "port", "protocol", "portSuffix", "baseUrl", "hash", "data", "response", "err", "wsSend", "type", "createdAt", "dirctCall", "rej", "promiseIsLive", "timeLetForReqToBeMade", "totalRequestTimeout", "timer", "processedData", "processBinaryForUpload", "payload", "message", "jss", "messageHash", "replyPromise", "resolve", "reject", "waitingOn", "result", "next", "__socket", "worker", "sender", "ready", "connectTimeout", "errMessage", "waitingOnOpen", "res", "waitingOnOpenThen", "waitingOnOpenCatch", "aWaitingSend", "connectSocket", "buildClientInterface", "wrap", "onTypeStFn", "handlerFn", "ofTypesOb", "receiverArray", "handler", "connectionChangeListeners", "connectionState", "idx", "currentTransport", "reconnect", "ConnectionState", "connect", "connectSocket_default", "client", "connectSocket_default"]
3
+ "sources": ["../utils/jss/plugins.js", "../utils/jss/encode.js", "../utils/jss/decode.js", "../utils/jss.js", "../utils/messageHash.js", "../client/connectSocket.js", "../client/transports/streaming.js", "../client/transports/streamParser.js", "../client/connection/state.js", "../client/connection/network.js", "../client/connection/subscriptions.js", "../client/connection/proxy.js", "../client/connection/sender.js", "../client/connection/fileUtils.js", "../client/connection/fileHandling.js", "../client/connection/fileDownload.js", "../client/connection/messageHandler.js", "../client/browser.js"],
4
+ "sourcesContent": ["/**\n * @fileoverview JSS Plugin Registry\n *\n * This module provides the plugin registration system for JSS (JSON Super Set).\n * It allows developers to register custom type handlers that extend JSS beyond\n * its built-in types (Date, RegExp, Error, etc.).\n *\n * ## Plugin Architecture\n *\n * Plugins are registered with a single-character tag and provide:\n * - `check(key, value)` - Determines if this plugin handles a value\n * - `encode(path, key, value, context)` - Transforms value for serialization\n * - `decode(value, path, context)` - Restores value from serialization\n * - `onSend(path, key, value, context)` - Optional: handles external resources during send\n * - `onReceive(path, key, value, context)` - Optional: handles external resources during receive\n *\n * ## Usage\n *\n * ```javascript\n * const jss = require('./jss')\n *\n * jss.custom('X', {\n * check: (key, value) => value instanceof CustomType,\n * encode: (path, key, value, ctx) => value.toSerializable(),\n * decode: (value, path, ctx) => CustomType.fromSerializable(value)\n * })\n * ```\n *\n * @module utils/jss/plugins\n * @see {@link module:utils/jss} for main JSS module\n */\n\n/**\n * Built-in type tags that cannot be overridden\n * @constant {string[]}\n */\nconst builtInTags = [\"D\", \"R\", \"E\", \"U\", \"M\", \"S\", \"P\", \"I\"];\n\n/**\n * Registry of custom plugins\n * Maps tag character to plugin configuration\n * @type {Map<string, PluginConfig>}\n * @private\n */\nconst plugins = new Map();\n\n/**\n * @typedef {Object} PluginConfig\n * @property {function(string|number, any): boolean} check - Determines if plugin handles value\n * @property {function(string[], string|number, any, Object): any} encode - Transforms for serialization\n * @property {function(any, string[], Object): any} decode - Restores from serialization\n * @property {function(string[], string|number, any, Object): {replace: any, cleanup?: function}=} onSend - Optional send hook\n * @property {function(string[], string|number, any, Object): Promise<any>=} onReceive - Optional receive hook\n */\n\n/**\n * Register a custom type handler plugin\n *\n * Plugins extend JSS to handle custom types beyond the built-in set.\n * Each plugin is identified by a single-character tag that appears in\n * the serialized format (e.g., `\"key<!X>\": value`).\n *\n * ## Behavior Rules\n *\n * - **Check gates encode**: The `check` function determines if this plugin\n * should handle a value. If it returns true, encode is called.\n * - **Error on conflict**: Throws if the tag conflicts with a built-in type\n * or an already-registered custom plugin.\n *\n * @param {string} tag - Single character tag identifier (e.g., 'X', 'Z')\n * @param {PluginConfig} config - Plugin configuration object\n * @throws {Error} If tag is not a single character\n * @throws {Error} If tag conflicts with built-in type\n * @throws {Error} If tag is already registered\n * @throws {Error} If required functions are missing\n *\n * @example\n * // Register a plugin for a custom Point type\n * register('P', {\n * check: (key, value) => value instanceof Point,\n * encode: (path, key, value) => [value.x, value.y],\n * decode: (value) => new Point(value[0], value[1])\n * })\n *\n * @example\n * // Register a plugin with lifecycle hooks for external resources\n * register('L', {\n * check: (key, value) => Buffer.isBuffer(value),\n * encode: (path, key, value) => '__pending__',\n * decode: (value) => value, // Hash returned as-is\n * onSend: (path, key, value, ctx) => {\n * const hash = generateHash(ctx.queryId, path.join('.'))\n * ctx.fileTransfer.registerDownload(hash, value, 'application/octet-stream', ctx.clientId)\n * return { replace: hash }\n * }\n * })\n */\nfunction register(tag, config) {\n // Validate tag format\n if (typeof tag !== \"string\" || tag.length !== 1) {\n throw new Error(`Tag must be a single character, got: '${tag}'`);\n }\n\n // Check for built-in tag conflict\n if (builtInTags.includes(tag)) {\n throw new Error(`Tag '${tag}' conflicts with built-in type`);\n }\n\n // Check for duplicate registration\n if (plugins.has(tag)) {\n throw new Error(`Tag '${tag}' is already registered`);\n }\n\n // Validate required functions\n if (typeof config.check !== \"function\") {\n throw new Error(\"Plugin must provide a 'check' function\");\n }\n if (typeof config.encode !== \"function\") {\n throw new Error(\"Plugin must provide an 'encode' function\");\n }\n if (typeof config.decode !== \"function\") {\n throw new Error(\"Plugin must provide a 'decode' function\");\n }\n\n // Validate optional functions if provided\n if (config.onSend !== undefined && typeof config.onSend !== \"function\") {\n throw new Error(\"Plugin 'onSend' must be a function if provided\");\n }\n if (\n config.onReceive !== undefined &&\n typeof config.onReceive !== \"function\"\n ) {\n throw new Error(\"Plugin 'onReceive' must be a function if provided\");\n }\n\n plugins.set(tag, config);\n}\n\n/**\n * Get a plugin by its tag\n *\n * @param {string} tag - The tag character to look up\n * @returns {PluginConfig|undefined} The plugin config or undefined if not found\n *\n * @example\n * const plugin = getPlugin('X')\n * if (plugin) {\n * const decoded = plugin.decode(value, path, context)\n * }\n */\nfunction getPlugin(tag) {\n return plugins.get(tag);\n}\n\n/**\n * Get all registered plugins\n *\n * Returns the internal Map for iteration during encoding.\n *\n * @returns {Map<string, PluginConfig>} Map of tag -> plugin config\n *\n * @example\n * for (const [tag, plugin] of getAllPlugins()) {\n * if (plugin.check(key, value)) {\n * return [tag, plugin.encode(path, key, value, context)]\n * }\n * }\n */\nfunction getAllPlugins() {\n return plugins;\n}\n\n/**\n * Check if a tag has a registered plugin\n *\n * @param {string} tag - The tag to check\n * @returns {boolean} True if a plugin is registered for this tag\n *\n * @example\n * if (hasPlugin('X')) {\n * // Handle custom type\n * }\n */\nfunction hasPlugin(tag) {\n return plugins.has(tag);\n}\n\n/**\n * Clear all registered plugins\n *\n * Used primarily for testing to reset the registry between tests.\n * Does not affect built-in types.\n *\n * @example\n * beforeEach(() => {\n * clearPlugins()\n * })\n */\nfunction clearPlugins() {\n plugins.clear();\n}\n\nmodule.exports = {\n register,\n getPlugin,\n getAllPlugins,\n hasPlugin,\n clearPlugins,\n builtInTags,\n};\n", "/**\n * @fileoverview JSS Encoder - Encodes JavaScript objects to JSS format\n *\n * This module provides the encoding functionality for JSON Super Set (JSS).\n * It converts JavaScript objects containing extended types (Date, RegExp,\n * Error, Map, Set, undefined) into a JSON-compatible format using tagged keys.\n *\n * ## Encoding Process\n *\n * The encoder traverses the object tree and:\n * 1. Detects extended types using `Object.prototype.toString`\n * 2. Converts them to primitive representations\n * 3. Tags the key with a type indicator (e.g., `<!D>` for Date)\n * 4. Tracks visited objects to handle circular references\n *\n * ## Tag System\n *\n * | Type | Tag | Encoded Value |\n * |-----------|-----|----------------------------------------|\n * | Date | `D` | Unix timestamp (milliseconds) |\n * | RegExp | `R` | String representation (e.g., \"/a/gi\") |\n * | Error | `E` | Array: [name, message, stack] |\n * | undefined | `U` | null |\n * | Map | `M` | Object from entries |\n * | Set | `S` | Array of values |\n * | Pointer | `P` | Path array to referenced object |\n *\n * ## Array Type Tags\n *\n * For arrays containing extended types, the tag includes all element types:\n * ```javascript\n * [new Date(), new Date()] \u2192 { \"[D,D]\": [timestamp1, timestamp2] }\n * ```\n *\n * @module utils/jss/encode\n * @see {@link module:utils/jss/decode} for decoding implementation\n * @see {@link module:utils/jss} for main JSS module\n *\n * @example\n * const { encode, stringify } = require('./encode')\n *\n * // Encode without stringifying (returns plain object)\n * const encoded = encode({\n * created: new Date('2024-01-01'),\n * pattern: /test/i\n * })\n * // { \"created<!D>\": 1704067200000, \"pattern<!R>\": \"/test/i\" }\n *\n * // Stringify (encode + JSON.stringify)\n * const str = stringify({ date: new Date() })\n * // Ready for transmission\n *\n * @example\n * // Circular reference handling\n * const obj = { name: 'root' }\n * obj.self = obj\n *\n * const encoded = encode(obj)\n * // { name: 'root', 'self<!P>': [] }\n * // The empty array [] is the path to the root object\n */\n\n/**\n * Lookup table mapping Object.prototype.toString results to type tags\n *\n * Used for fast type detection during encoding. Only types that require\n * special handling are included - standard JSON types (string, number,\n * boolean, null, array, object) are handled separately.\n *\n * @constant {Object.<string, string>}\n * @private\n *\n * @example\n * Object.prototype.toString.call(new Date()) // '[object Date]'\n * tagLookup['[object Date]'] // 'D'\n */\nconst { getAllPlugins } = require(\"./plugins\");\n\nconst tagLookup = {\n /**\n * RegExp objects - serialized as string pattern\n */\n \"[object RegExp]\": \"R\",\n\n /**\n * Date objects - serialized as Unix timestamp\n */\n \"[object Date]\": \"D\",\n\n /**\n * Error objects - serialized as [name, message, stack] array\n */\n \"[object Error]\": \"E\",\n\n /**\n * Undefined values - explicitly encoded (unlike JSON which omits them)\n */\n \"[object Undefined]\": \"U\",\n\n /**\n * Map objects - serialized as plain object from entries\n */\n \"[object Map]\": \"M\",\n\n /**\n * Set objects - serialized as array of values\n */\n \"[object Set]\": \"S\",\n};\n\n/**\n * Encode a JavaScript object to JSS format\n *\n * Recursively traverses the object tree, converting extended types to\n * their tagged representations. Handles circular references by tracking\n * visited objects and replacing subsequent references with path pointers.\n *\n * ## Algorithm\n *\n * 1. Create a WeakMap to track visited objects and their paths\n * 2. For each value in the object:\n * - If it's an extended type (Date, RegExp, etc.), encode it\n * - If it's an object/array, recurse (checking for circularity)\n * - If it's a primitive, pass through unchanged\n * 3. Return the encoded object structure\n *\n * ## Circular Reference Detection\n *\n * When an object is first encountered, its path is stored in `visitedEncode`.\n * If the same object is encountered again, a pointer (`P` tag) is created\n * with the stored path.\n *\n * @param {any} obj - The object to encode\n * @returns {Object} Encoded object with tagged keys for extended types\n *\n * @example\n * // Simple types\n * encode({ date: new Date('2024-01-01') })\n * // { \"date<!D>\": 1704067200000 }\n *\n * @example\n * // Nested structures\n * encode({\n * user: {\n * name: 'Alice',\n * createdAt: new Date(),\n * roles: new Set(['admin', 'user'])\n * }\n * })\n * // {\n * // user: {\n * // name: 'Alice',\n * // \"createdAt<!D>\": 1704067200000,\n * // \"roles<!S>\": ['admin', 'user']\n * // }\n * // }\n *\n * @example\n * // Circular references\n * const a = { name: 'a' }\n * const b = { name: 'b', ref: a }\n * a.ref = b\n *\n * encode({ a, b })\n * // {\n * // a: { name: 'a', 'ref<!P>': ['b'] },\n * // b: { name: 'b', 'ref<!P>': ['a'] }\n * // }\n *\n * @example\n * // Error objects\n * encode({ error: new TypeError('Invalid input') })\n * // { \"error<!E>\": ['TypeError', 'Invalid input', 'TypeError: Invalid input\\n at ...'] }\n *\n * @example\n * // Mixed array with extended types\n * encode({ dates: [new Date(), new Date()] })\n * // { \"dates<![D,D]>\": [1704067200000, 1704067300000] }\n */\nfunction encode(obj) {\n /**\n * WeakMap tracking visited objects to detect circular references\n * Maps each visited object to its path in the object tree\n * @type {WeakMap<Object, Array<string|number>>}\n */\n const visitedEncode = new WeakMap();\n visitedEncode.set(obj, []);\n\n /**\n * Recursively encode a value with circular reference tracking\n *\n * @param {any} value - The value to encode\n * @param {Array<string|number>} path - Current path in the object tree\n * @returns {[string, any]} Tuple of [tag, encodedValue]\n * @private\n */\n function encodeValueWithVisited(value, path = []) {\n const type = typeof value;\n const tag = tagLookup[Object.prototype.toString.call(value)];\n\n // Handle special types with known tags\n if (tag !== undefined) {\n if (\"D\" === tag) return [tag, value.valueOf()];\n if (\"E\" === tag) return [tag, [value.name, value.message, value.stack]];\n if (\"R\" === tag) return [tag, value.toString()];\n if (\"U\" === tag) return [tag, null];\n if (\"S\" === tag) return [tag, Array.from(value)];\n if (\"M\" === tag) return [tag, Object.fromEntries(value)];\n }\n\n // Check custom plugins\n for (const [customTag, plugin] of getAllPlugins()) {\n const key = path.length > 0 ? path[path.length - 1] : undefined;\n if (plugin.check(key, value)) {\n return [customTag, plugin.encode(path, key, value, {})];\n }\n }\n\n // Handle objects and arrays (potential circular references)\n if (type === \"object\" && value !== null) {\n // Check for circular reference\n if (visitedEncode.has(value)) return [\"P\", visitedEncode.get(value)];\n\n // Mark as visited with current path\n visitedEncode.set(value, path);\n\n const isArray = Array.isArray(value);\n const objKeys = isArray\n ? Array.from(Array(value.length).keys())\n : Object.keys(value);\n const result = isArray ? [] : {};\n const typesFound = [];\n\n // Process each property/element\n for (let i = 0; i < objKeys.length; i++) {\n const key = objKeys[i];\n const [t, v] = encodeValueWithVisited(value[key], [...path, key]);\n\n if (isArray) {\n typesFound.push(t);\n result.push(v);\n } else if (value[key] !== undefined) {\n // Add tag to key if value was special type\n result[key + (t ? `<!${t}>` : \"\")] = v;\n }\n }\n\n // For arrays with special types, create compound tag\n if (isArray && typesFound.find((t) => !!t))\n return [`[${typesFound.join()}]`, result];\n return [\"\", result];\n }\n // Primitive values pass through unchanged\n else {\n return [\"\", value];\n }\n }\n\n // Process root object properties\n let keys = Array.isArray(obj)\n ? Array.from(Array(obj.length).keys())\n : Object.keys(obj);\n const result = {};\n\n for (let i = 0; i < keys.length; i++) {\n const key = keys[i];\n if (obj[key] !== undefined) {\n const [t, v] = encodeValueWithVisited(obj[key], [key]);\n result[key + (t ? `<!${t}>` : \"\")] = v;\n }\n }\n\n return result;\n}\n\n/**\n * Stringify an object to JSS format\n *\n * Combines `encode()` with `JSON.stringify()` to produce a string\n * representation suitable for transmission over WebSocket or storage.\n *\n * This is the high-level API - use this for most cases.\n *\n * @param {any} obj - The object to stringify\n * @returns {string} JSS-encoded JSON string\n *\n * @example\n * // Basic usage\n * const str = stringify({\n * message: 'Hello',\n * timestamp: new Date(),\n * pattern: /world/i\n * })\n * // '{\"message\":\"Hello\",\"timestamp<!D>\":1704067200000,\"pattern<!R>\":\"/world/i\"}'\n *\n * @example\n * // With nested objects\n * const str = stringify({\n * user: {\n * settings: new Map([['theme', 'dark']])\n * }\n * })\n *\n * @example\n * // Ready for WebSocket transmission\n * socket.send(stringify({ type: '/chat', data: { text: 'Hi!' } }))\n */\nfunction stringify(obj) {\n return JSON.stringify(encode(obj));\n}\n\nmodule.exports = { encode, stringify };\n", "/**\n * @fileoverview JSS Decoder - Decodes JSS Format Back to JavaScript Objects\n *\n * This module provides the decoding/parsing functionality for JSS (JSON Super Set).\n * It reverses the encoding process, restoring JavaScript types from their tagged\n * string representations.\n *\n * ## Decoding Process\n *\n * 1. Parse the JSON string (if using `parse()`)\n * 2. Recursively traverse the object structure\n * 3. Detect tagged keys (e.g., `key<!D>` for Date)\n * 4. Apply the appropriate decoder for each tag\n * 5. Resolve circular reference pointers\n * 6. Return the fully restored object\n *\n * ## Supported Tags\n *\n * | Tag | Type | Decoder Behavior |\n * |-----|-----------|-------------------------------------------|\n * | `D` | Date | `new Date(timestamp)` |\n * | `R` | RegExp | `new RegExp(pattern)` from string |\n * | `E` | Error | Reconstructs with name, message, stack |\n * | `U` | undefined | Returns `undefined` value |\n * | `M` | Map | `new Map(Object.entries(obj))` |\n * | `S` | Set | `new Set(array)` |\n * | `P` | Pointer | Circular reference (resolved after parse) |\n *\n * ## Circular Reference Resolution\n *\n * Circular references are encoded as path pointers. During decoding:\n * 1. First pass: Decode all values, storing pointer locations\n * 2. Second pass: Resolve pointers by following stored paths\n *\n * ```javascript\n * // Encoded: { \"self<!P>\": [] } // Pointer to root\n * // Decoded: obj.self === obj // Circular reference restored\n * ```\n *\n * @module utils/jss/decode\n * @see {@link module:utils/jss/encode} for the encoding counterpart\n * @see {@link module:utils/jss} for the main JSS module\n *\n * @example\n * // Basic decoding\n * const { parse } = require('./decode')\n *\n * const result = parse('{\"timestamp<!D>\":1704067200000}')\n * console.log(result.timestamp instanceof Date) // true\n * console.log(result.timestamp.toISOString()) // '2024-01-01T00:00:00.000Z'\n *\n * @example\n * // Decoding errors with preserved stack traces\n * const { parse } = require('./decode')\n *\n * const result = parse('{\"err<!E>\":[\"TypeError\",\"Invalid input\",\"Error: Invalid input\\\\n at ...\"]}')\n * console.log(result.err instanceof TypeError) // true\n * console.log(result.err.message) // 'Invalid input'\n * console.log(result.err.stack) // Original stack trace\n *\n * @example\n * // Low-level decode without JSON parsing\n * const { decode } = require('./decode')\n *\n * const encoded = { \"items<!S>\": [1, 2, 3], \"config<!M>\": { a: 1, b: 2 } }\n * const decoded = decode(encoded)\n * console.log(decoded.items instanceof Set) // true\n * console.log(decoded.config instanceof Map) // true\n */\n\n/**\n * Temporary storage for circular reference pointers during decoding\n *\n * Each entry is a tuple of [sourcePath, targetPath] where:\n * - sourcePath: Path to the referenced object\n * - targetPath: Path where the reference should be placed\n *\n * This is reset at the start of each decode() call.\n *\n * @type {Array<[string[], string[]]>}\n * @private\n */\nconst { getPlugin } = require(\"./plugins\");\n\nlet pointers2Res = [];\n\n/**\n * Tag decoder lookup table\n *\n * Maps single-character tags to their decoder functions.\n * Each decoder takes the encoded value and returns the decoded JavaScript value.\n *\n * @type {Object.<string, function(any, string[]?): any>}\n * @private\n *\n * @property {function(string): RegExp} R - Decode RegExp from string pattern\n * @property {function(number): Date} D - Decode Date from timestamp\n * @property {function(string[], string[]): null} P - Register pointer for later resolution\n * @property {function([string, string, string]): Error} E - Decode Error with name, message, stack\n * @property {function(): undefined} U - Return undefined\n * @property {function(any[]): Set} S - Decode Set from array\n * @property {function(Object): Map} M - Decode Map from object entries\n */\nconst tagLookup = {\n /**\n * Decode RegExp from string pattern\n * @param {string} s - RegExp string (e.g., '/pattern/flags')\n * @returns {RegExp} Reconstructed RegExp instance\n */\n R: (s) => {\n // Parse /pattern/flags format\n const match = s.match(/^\\/(.*)\\/([gimsuy]*)$/);\n if (match) {\n return new RegExp(match[1], match[2]);\n }\n // Fallback: treat as raw pattern with no flags\n return new RegExp(s);\n },\n\n /**\n * Decode Date from timestamp\n * @param {number} n - Unix timestamp in milliseconds\n * @returns {Date} Reconstructed Date instance\n */\n D: (n) => new Date(n),\n\n /**\n * Register circular reference pointer for later resolution\n * @param {string[]} sourcePath - Path to the referenced object\n * @param {string[]} currentPath - Path where reference should be placed\n * @returns {null} Placeholder (will be replaced during resolution)\n */\n P: (sourcePath, currentPath) => {\n pointers2Res.push([sourcePath, currentPath]);\n return null;\n },\n\n /**\n * Decode Error with preserved type, message, and stack\n *\n * Attempts to reconstruct the original error type (TypeError, RangeError, etc.)\n * Falls back to generic Error if the type is not available globally.\n *\n * @param {[string, string, string]} errorData - Tuple of [name, message, stack]\n * @returns {Error} Reconstructed Error instance\n */\n E: ([name, message, stack]) => {\n let err;\n try {\n // Try to create the specific error type (TypeError, RangeError, etc.)\n err = new global[name](message);\n if (err instanceof Error) {\n err.stack = stack;\n } else {\n throw {}; // Force fallback if not a real Error\n }\n } catch (e) {\n // Fallback to generic Error with custom name\n err = new Error(message);\n err.name = name;\n err.stack = stack;\n }\n return err;\n },\n\n /**\n * Decode undefined value\n * @returns {undefined}\n */\n U: () => undefined,\n\n /**\n * Decode Set from array\n * @param {any[]} a - Array of set elements\n * @returns {Set} Reconstructed Set instance\n */\n S: (a) => new Set(a),\n\n /**\n * Decode Map from object entries\n * @param {Object} o - Object with key-value pairs\n * @returns {Map} Reconstructed Map instance\n */\n M: (o) => new Map(Object.entries(o)),\n\n /**\n * Decode inline base64 binary data\n * @param {string} s - Base64 encoded string\n * @returns {Buffer|ArrayBuffer} Decoded binary data (Buffer in Node, ArrayBuffer in browser)\n */\n I: (s) => {\n // In Node.js, return Buffer\n if (typeof Buffer !== \"undefined\") {\n return Buffer.from(s, \"base64\");\n }\n // In browser, return ArrayBuffer\n const binaryStr = atob(s);\n const bytes = new Uint8Array(binaryStr.length);\n for (let i = 0; i < binaryStr.length; i++) {\n bytes[i] = binaryStr.charCodeAt(i);\n }\n return bytes.buffer;\n },\n};\n\n/**\n * Parse a tagged key to extract the property name and tag\n *\n * JSS encodes type information in property keys using the format `name<!tag>`.\n * This function separates the original property name from its type tag.\n *\n * Also handles array type tags which have the format `[tag1,tag2,...]`.\n *\n * @param {string} key - Property key potentially containing a tag\n * @returns {[string, string|undefined]} Tuple of [propertyName, tag]\n * Tag is undefined if key has no tag\n * @private\n *\n * @example\n * parseKeyWithTags('createdAt<!D>') // ['createdAt', 'D']\n * parseKeyWithTags('pattern<!R>') // ['pattern', 'R']\n * parseKeyWithTags('name') // ['name', undefined]\n * parseKeyWithTags('items<![D,D,D]') // ['items', '[D,D,D]']\n */\nfunction parseKeyWithTags(key) {\n const match = key.match(/(.+)<!(.*)>/);\n\n if (match) {\n const name = match[1];\n let tag = match[2];\n\n // Handle array type tags that may be split across chunks\n // e.g., '[D,D,D' needs the closing ']'\n if (tag.startsWith(\"[\") && !tag.endsWith(\"]\")) {\n tag += \"]\";\n }\n\n return [name, tag];\n }\n\n return [key, undefined];\n}\n\n/**\n * Recursively decode a value based on its tag\n *\n * This is the core decoding function that handles all JSS types.\n * It processes:\n * - Tagged values using the tagLookup decoders\n * - Arrays (including typed arrays with per-element tags)\n * - Objects (recursively decoding nested properties)\n * - Primitive values (passed through unchanged)\n *\n * @param {any} val - The value to decode\n * @param {string|undefined} tag - Type tag (D, R, E, U, M, S, P, or array format)\n * @param {string[]} [path=[]] - Current path for circular reference tracking\n * @returns {any} The decoded value with original JavaScript type\n * @private\n *\n * @example\n * // Decode a Date\n * decodeValue(1704067200000, 'D')\n * // Returns: Date instance\n *\n * @example\n * // Decode a nested object\n * decodeValue({ \"name<!>\": \"test\", \"date<!D>\": 1704067200000 }, undefined)\n * // Returns: { name: 'test', date: Date }\n *\n * @example\n * // Decode a typed array\n * decodeValue([1704067200000, 1704153600000], '[D,D]')\n * // Returns: [Date, Date]\n */\nfunction decodeValue(val, tag, path = []) {\n // If we have a known tag, use the appropriate decoder\n if (tag in tagLookup) {\n return tagLookup[tag](val, path);\n }\n\n // Check custom plugins\n const plugin = getPlugin(tag);\n if (plugin) {\n return plugin.decode(val, path, {});\n }\n\n // Handle arrays\n if (Array.isArray(val)) {\n const res = [];\n\n // Check if this is a typed array (tag format: '[D,D,D]')\n const isTaggedArray = tag && tag.startsWith(\"[\");\n const typeTags = isTaggedArray ? tag.slice(1, -1).split(\",\") : [];\n\n for (let i = 0; i < val.length; i++) {\n res.push(decodeValue(val[i], typeTags[i], [...path, i]));\n }\n\n return res;\n }\n\n // Handle objects\n if (val !== null && typeof val === \"object\") {\n const res = {};\n\n for (const key in val) {\n const [name, t] = parseKeyWithTags(key);\n res[name] = decodeValue(val[key], t, [...path, name]);\n }\n\n return res;\n }\n\n // Primitive values - return as-is\n return val;\n}\n\n/**\n * Resolve a circular reference pointer\n *\n * After initial decoding, circular references are represented as null values\n * with their paths stored in pointers2Res. This function resolves each pointer\n * by navigating to the referenced object and placing it at the target location.\n *\n * @param {Object} obj - The root decoded object\n * @param {[string[], string[]]} pointerInfo - Tuple of [refPath, attrPath]\n * - refPath: Path to the object being referenced\n * - attrPath: Path where the reference should be placed\n * @returns {void} Modifies obj in place\n * @private\n *\n * @example\n * // Given: obj = { child: { name: 'test' }, ref: null }\n * // With pointer: [['child'], ['ref']]\n * // After resolution: obj.ref === obj.child\n *\n * resolvePointers(obj, [['child'], ['ref']])\n * console.log(obj.ref === obj.child) // true\n */\nfunction resolvePointers(obj, [refPath, attrPath]) {\n // Navigate to the referenced object\n let ref = obj;\n for (const key of refPath) {\n ref = ref[key];\n }\n\n // Navigate to the parent of the target location\n let attrParent = obj;\n for (let i = 0; i < attrPath.length - 1; i++) {\n attrParent = attrParent[attrPath[i]];\n }\n\n // Set the reference at the target location\n attrParent[attrPath[attrPath.length - 1]] = ref;\n}\n\n/**\n * Decode a JSS-encoded object back to its original form\n *\n * This is the low-level decode function that operates on already-parsed\n * JavaScript objects. Use `parse()` if you have a JSON string.\n *\n * ## Processing Steps\n *\n * 1. Reset pointer storage for circular references\n * 2. Recursively decode all values using decodeValue()\n * 3. Resolve all circular reference pointers\n * 4. Return the fully restored object\n *\n * @param {Object} data - JSS-encoded plain object (already parsed from JSON)\n * @returns {any} Decoded object with original JavaScript types restored\n *\n * @example\n * // Decode a Date\n * const decoded = decode({ \"created<!D>\": 1704067200000 })\n * console.log(decoded.created instanceof Date) // true\n *\n * @example\n * // Decode multiple types\n * const decoded = decode({\n * \"date<!D>\": 1704067200000,\n * \"regex<!R>\": \"/test/gi\",\n * \"items<!S>\": [1, 2, 3],\n * \"config<!M>\": { key: \"value\" }\n * })\n *\n * console.log(decoded.date instanceof Date) // true\n * console.log(decoded.regex instanceof RegExp) // true\n * console.log(decoded.items instanceof Set) // true\n * console.log(decoded.config instanceof Map) // true\n *\n * @example\n * // Decode with circular reference\n * const decoded = decode({\n * name: \"root\",\n * \"self<!P>\": [] // Pointer to root\n * })\n *\n * console.log(decoded.self === decoded) // true\n */\nfunction decode(data) {\n // Reset pointer storage for this decode operation\n pointers2Res = [];\n\n // Decode all values recursively\n const result = decodeValue(data, undefined, []);\n\n // Resolve all circular reference pointers\n pointers2Res.forEach((p) => resolvePointers(result, p));\n\n return result;\n}\n\n/**\n * Parse a JSS-encoded JSON string back to its original form\n *\n * This is the high-level parse function that combines JSON.parse with\n * JSS decoding. It's the counterpart to `stringify()` from the encode module.\n *\n * ## Usage\n *\n * ```javascript\n * const { parse } = require('./decode')\n * const original = parse(jssString)\n * ```\n *\n * ## Error Handling\n *\n * - Throws `SyntaxError` if the string is not valid JSON\n * - Invalid tags are silently ignored (value passed through as-is)\n * - Missing referenced objects in pointers will cause runtime errors\n *\n * @param {string} encoded - JSS-encoded JSON string\n * @returns {any} Decoded object with original JavaScript types restored\n * @throws {SyntaxError} If the input is not valid JSON\n *\n * @example\n * // Parse a complete JSS message\n * const result = parse(`{\n * \"type\": \"message\",\n * \"timestamp<!D>\": 1704067200000,\n * \"pattern<!R>\": \"/hello/i\",\n * \"data\": {\n * \"items<!S>\": [1, 2, 3]\n * }\n * }`)\n *\n * console.log(result.type) // 'message'\n * console.log(result.timestamp instanceof Date) // true\n * console.log(result.pattern instanceof RegExp) // true\n * console.log(result.data.items instanceof Set) // true\n *\n * @example\n * // Round-trip with encode\n * const { stringify } = require('./encode')\n * const { parse } = require('./decode')\n *\n * const original = {\n * date: new Date(),\n * items: new Set([1, 2, 3])\n * }\n *\n * const restored = parse(stringify(original))\n * console.log(restored.date.getTime() === original.date.getTime()) // true\n * console.log([...restored.items]) // [1, 2, 3]\n */\nfunction parse(encoded) {\n return decode(JSON.parse(encoded));\n}\n\nmodule.exports = { decode, parse };\n", "/**\n * @fileoverview JSON Super Set (JSS) - Extended JSON Serialization\n *\n * JSS extends standard JSON to support additional JavaScript types that\n * JSON.stringify/parse cannot handle. This enables api-ape to transparently\n * serialize and deserialize rich data types over WebSocket connections.\n *\n * ## Supported Types\n *\n * | Type | Tag | Description |\n * |----------------|-----|---------------------------------------|\n * | Date | `D` | Serialized as timestamp |\n * | RegExp | `R` | Serialized as string pattern |\n * | Error | `E` | Preserves name, message, and stack |\n * | undefined | `U` | Explicitly represents undefined |\n * | Map | `M` | Converted to/from object entries |\n * | Set | `S` | Converted to/from array |\n * | Circular Refs | `P` | Preserved via path pointers |\n *\n * ## Wire Format\n *\n * JSS encodes type information into object keys using a tag suffix:\n *\n * ```javascript\n * // Original object\n * { createdAt: new Date('2024-01-01'), pattern: /hello/i }\n *\n * // JSS encoded\n * { \"createdAt<!D>\": 1704067200000, \"pattern<!R>\": \"/hello/i\" }\n * ```\n *\n * ## Usage\n *\n * JSS provides a drop-in replacement for JSON.stringify/parse:\n *\n * ```javascript\n * const jss = require('./jss')\n *\n * // Stringify (like JSON.stringify but handles extended types)\n * const str = jss.stringify({ date: new Date(), regex: /test/ })\n *\n * // Parse (like JSON.parse but restores extended types)\n * const obj = jss.parse(str)\n * // obj.date is a Date instance\n * // obj.regex is a RegExp instance\n * ```\n *\n * ## API Methods\n *\n * - `stringify(obj)` - Convert object to JSS string (high-level)\n * - `parse(str)` - Parse JSS string back to object (high-level)\n * - `encode(obj)` - Convert object to JSS-encoded plain object\n * - `decode(obj)` - Convert JSS-encoded object back to original\n *\n * ## Circular Reference Handling\n *\n * JSS can handle circular references using path pointers:\n *\n * ```javascript\n * const obj = { name: 'root' }\n * obj.self = obj // Circular reference\n *\n * const str = jss.stringify(obj) // No error!\n * const restored = jss.parse(str)\n * console.log(restored.self === restored) // true\n * ```\n *\n * @module utils/jss\n * @see {@link module:utils/jss/encode} for encoding implementation\n * @see {@link module:utils/jss/decode} for decoding implementation\n *\n * @example\n * // Basic usage with dates\n * const jss = require('./jss')\n *\n * const data = {\n * user: 'Alice',\n * loginAt: new Date(),\n * settings: new Map([['theme', 'dark'], ['lang', 'en']])\n * }\n *\n * const serialized = jss.stringify(data)\n * // Can be sent over WebSocket\n *\n * const restored = jss.parse(serialized)\n * console.log(restored.loginAt instanceof Date) // true\n * console.log(restored.settings instanceof Map) // true\n *\n * @example\n * // Error serialization\n * const jss = require('./jss')\n *\n * try {\n * throw new TypeError('Invalid input')\n * } catch (err) {\n * const serialized = jss.stringify({ error: err })\n * const restored = jss.parse(serialized)\n *\n * console.log(restored.error instanceof TypeError) // true\n * console.log(restored.error.message) // 'Invalid input'\n * console.log(restored.error.stack) // Original stack trace\n * }\n *\n * @example\n * // Low-level encode/decode for inspection\n * const jss = require('./jss')\n *\n * const encoded = jss.encode({\n * date: new Date('2024-01-01'),\n * items: new Set([1, 2, 3])\n * })\n *\n * console.log(encoded)\n * // {\n * // \"date<!D>\": 1704067200000,\n * // \"items<!S>\": [1, 2, 3]\n * // }\n *\n * const decoded = jss.decode(encoded)\n * // Original types restored\n */\n\nconst { encode, stringify } = require(\"./jss/encode\");\nconst { decode, parse } = require(\"./jss/decode\");\nconst { register: custom, clearPlugins } = require(\"./jss/plugins\");\n\n/**\n * Parse a JSS-encoded string back into an object with restored types\n *\n * This is the primary method for deserializing JSS data. It combines\n * JSON.parse with type restoration for Date, RegExp, Error, Map, Set,\n * undefined, and circular references.\n *\n * @function parse\n * @param {string} encoded - JSS-encoded JSON string\n * @returns {any} Decoded object with original types restored\n * @throws {SyntaxError} If the string is not valid JSON\n *\n * @example\n * const obj = jss.parse('{\"date<!D>\":1704067200000}')\n * console.log(obj.date instanceof Date) // true\n */\n\n/**\n * Convert an object to a JSS-encoded JSON string\n *\n * This is the primary method for serializing objects with extended types.\n * It handles Date, RegExp, Error, Map, Set, undefined, and circular\n * references that would cause JSON.stringify to fail or lose information.\n *\n * @function stringify\n * @param {any} obj - Object to serialize\n * @returns {string} JSS-encoded JSON string\n *\n * @example\n * const str = jss.stringify({\n * when: new Date(),\n * pattern: /\\d+/g,\n * items: new Set([1, 2, 3])\n * })\n */\n\n/**\n * Encode an object to JSS format (without stringifying)\n *\n * Low-level method that converts extended types to their tagged\n * representations. Useful for inspection or custom serialization.\n *\n * @function encode\n * @param {any} obj - Object to encode\n * @returns {Object} Plain object with tagged keys for extended types\n *\n * @example\n * const encoded = jss.encode({ date: new Date() })\n * // { \"date<!D>\": 1704067200000 }\n */\n\n/**\n * Decode a JSS-encoded object (without parsing from string)\n *\n * Low-level method that restores extended types from their tagged\n * representations. Useful when working with already-parsed data.\n *\n * @function decode\n * @param {Object} data - JSS-encoded plain object\n * @returns {any} Object with original types restored\n *\n * @example\n * const decoded = jss.decode({ \"date<!D>\": 1704067200000 })\n * console.log(decoded.date instanceof Date) // true\n */\n\n/**\n * Register a custom type handler plugin\n *\n * Plugins extend JSS to handle custom types beyond the built-in set.\n * Each plugin is identified by a single-character tag that appears in\n * the serialized format (e.g., `\"key<!X>\": value`).\n *\n * @function custom\n * @param {string} tag - Single character tag identifier (e.g., 'X', 'Z')\n * @param {Object} config - Plugin configuration object\n * @param {function(string|number, any): boolean} config.check - Determines if plugin handles value\n * @param {function(string[], string|number, any, Object): any} config.encode - Transform for serialization\n * @param {function(any, string[], Object): any} config.decode - Restore from serialization\n * @param {function=} config.onSend - Optional send lifecycle hook\n * @param {function=} config.onReceive - Optional receive lifecycle hook\n * @throws {Error} If tag conflicts with built-in or existing custom type\n *\n * @example\n * // Register a custom BigInt handler\n * jss.custom('B', {\n * check: (key, value) => typeof value === 'bigint',\n * encode: (path, key, value) => value.toString(),\n * decode: (value) => BigInt(value)\n * })\n */\n\nmodule.exports = { parse, stringify, encode, decode, custom, clearPlugins };\n", "/**\n * @fileoverview Message Hashing Utilities for api-ape\n *\n * This module provides deterministic hash generation for api-ape messages.\n * Hashes are used to correlate WebSocket requests with their responses,\n * enabling the request/response pattern over a bidirectional channel.\n *\n * ## Hash Algorithm\n *\n * Uses the Jenkins one-at-a-time hash algorithm, which provides:\n * - Good distribution (avalanche effect)\n * - Fast computation\n * - Low collision rate for typical message sizes\n * - Deterministic output (same input always produces same hash)\n *\n * ## Encoding\n *\n * Hash values are encoded using Crockford Base32, which:\n * - Excludes ambiguous characters (O, I, L) to avoid confusion\n * - Produces compact, URL-safe strings\n * - Is case-insensitive for human readability\n *\n * ## Use Case\n *\n * When a client sends a message over WebSocket:\n * 1. Message is serialized to JSON string\n * 2. Hash is computed from the string\n * 3. Hash becomes the `queryId` for request/response correlation\n * 4. Server includes `queryId` in response\n * 5. Client matches response to original request\n *\n * @module utils/messageHash\n * @see {@link module:client/connection/sender} for client-side usage\n * @see {@link module:server/socket/receive} for server-side usage\n *\n * @example\n * import messageHash from './messageHash'\n *\n * const message = JSON.stringify({ type: '/chat', data: { text: 'Hello!' } })\n * const queryId = messageHash(message)\n * // queryId: 'K7M3NP2Q' (example output)\n *\n * @example\n * // Correlation pattern\n * const queryId = messageHash(serializedMessage)\n * pendingRequests[queryId] = { resolve, reject }\n * socket.send(serializedMessage)\n *\n * // Later, when response arrives:\n * socket.onmessage = (event) => {\n * const { queryId, data } = JSON.parse(event.data)\n * if (pendingRequests[queryId]) {\n * pendingRequests[queryId].resolve(data)\n * delete pendingRequests[queryId]\n * }\n * }\n */\n\n/**\n * Crockford Base32 alphabet\n *\n * Uses a modified Base32 alphabet that excludes visually ambiguous characters:\n * - Excludes: I, L, O, U\n * - I and L look like 1\n * - O looks like 0\n * - U can be confused with V\n *\n * This makes hashes easier to read, transcribe, and communicate verbally.\n *\n * @constant {string}\n * @see {@link https://www.crockford.com/base32.html} Crockford Base32 specification\n */\nconst alphabet = \"0123456789ABCDEFGHJKMNPQRSTVWXYZ\";\n\n/**\n * Convert a number to Crockford Base32 string\n *\n * Recursively divides the number by 32 and builds the string\n * from the remainders. This produces a compact representation\n * that's safe for URLs and easy to read.\n *\n * ## Algorithm\n *\n * ```\n * n = 12345\n *\n * 12345 / 32 = 385 remainder 25 \u2192 'R'\n * 385 / 32 = 12 remainder 1 \u2192 '1'\n * 12 / 32 = 0 remainder 12 \u2192 'C'\n *\n * Result: 'C1R' (read bottom to top)\n * ```\n *\n * @param {number} n - Non-negative integer to convert\n * @returns {string} Base32 encoded string (uppercase)\n *\n * @example\n * toBase32(0) // '0'\n * toBase32(31) // 'Z'\n * toBase32(32) // '10'\n * toBase32(1000) // 'Z8'\n * toBase32(12345) // 'C1R'\n *\n * @example\n * // Used internally by messageHash\n * const hash = jenkinsOneAtATimeHash('hello') // Returns a large number\n * const encoded = toBase32(hash) // Compact string representation\n */\nfunction toBase32(n) {\n const remainder = Math.floor(n / 32);\n const current = n % 32;\n\n if (0 === remainder) {\n return alphabet[current];\n }\n\n return toBase32(remainder) + alphabet[current];\n}\n\n/**\n * Jenkins one-at-a-time hash function\n *\n * A non-cryptographic hash function designed by Bob Jenkins.\n * It provides good distribution and avalanche properties while\n * being simple and fast to compute.\n *\n * ## Properties\n *\n * - **Deterministic**: Same input always produces same output\n * - **Uniform distribution**: Output values are evenly distributed\n * - **Avalanche effect**: Small input changes cause large output changes\n * - **Fast**: Simple bitwise operations only\n *\n * ## Algorithm Steps\n *\n * For each character in the input:\n * 1. Add character code to hash\n * 2. Add (hash << 10) to hash\n * 3. XOR (hash >> 6) into hash\n *\n * Final mixing:\n * 4. Add (hash << 3) to hash\n * 5. XOR (hash >> 11) into hash\n * 6. Add (hash << 15) to hash\n * 7. Mask to 32-bit unsigned integer\n *\n * @param {string} keyString - The string to hash\n * @returns {number} 32-bit unsigned integer hash value (0 to 4,294,967,295)\n *\n * @see {@link https://en.wikipedia.org/wiki/Jenkins_hash_function} Wikipedia article\n *\n * @example\n * jenkinsOneAtATimeHash('hello') // 1335831723\n * jenkinsOneAtATimeHash('Hello') // 3287579938 (case sensitive)\n * jenkinsOneAtATimeHash('hello world') // 1824966837\n * jenkinsOneAtATimeHash('') // 0\n *\n * @example\n * // Demonstrates avalanche effect\n * jenkinsOneAtATimeHash('test1') // Very different from...\n * jenkinsOneAtATimeHash('test2') // ...even though input differs by 1 char\n */\nfunction jenkinsOneAtATimeHash(keyString) {\n var hash = 0;\n\n for (var charIndex = 0; charIndex < keyString.length; ++charIndex) {\n hash += keyString.charCodeAt(charIndex);\n hash += hash << 10;\n hash ^= hash >> 6;\n }\n\n hash += hash << 3;\n hash ^= hash >> 11;\n\n // 4,294,967,295 is 0xFFFFFFFF, the maximum 32-bit unsigned integer value\n // Used here as a mask to ensure the result is a valid 32-bit unsigned int\n // The >>> 0 converts the signed result to unsigned\n return ((hash + (hash << 15)) & 4294967295) >>> 0;\n}\n\n/**\n * Generate a Base32 hash from a message string\n *\n * This is the main export of the module. It combines the Jenkins\n * hash function with Base32 encoding to produce compact, readable\n * hash strings suitable for use as query IDs.\n *\n * ## Output Characteristics\n *\n * - **Length**: 1-7 characters (depending on hash value)\n * - **Character set**: 0-9, A-Z (excluding I, L, O, U)\n * - **Case**: Uppercase\n * - **URL-safe**: Yes\n *\n * ## Collision Probability\n *\n * With 32-bit hash space (~4 billion values), collision probability\n * follows the birthday problem:\n * - 10,000 messages: ~0.001% collision chance\n * - 100,000 messages: ~0.1% collision chance\n * - 1,000,000 messages: ~12% collision chance\n *\n * For typical api-ape usage (thousands of active requests), collisions\n * are extremely unlikely.\n *\n * @param {string} messageSt - The message string to hash (typically serialized JSON)\n * @returns {string} Base32 encoded hash string\n *\n * @example\n * // Basic usage\n * const queryId = messageHash('{\"type\":\"/chat\",\"data\":{\"text\":\"Hi\"}}')\n * console.log(queryId) // e.g., 'K7M3NP2Q'\n *\n * @example\n * // Request/response correlation\n * import messageHash from './messageHash'\n * import jss from './jss'\n *\n * const payload = { type: '/users', data: { action: 'list' } }\n * const message = jss.stringify(payload)\n * const queryId = messageHash(message)\n *\n * // Store pending request\n * pendingRequests.set(queryId, {\n * resolve,\n * reject,\n * timeout: setTimeout(() => reject(new Error('Timeout')), 10000)\n * })\n *\n * // Send message\n * socket.send(message)\n *\n * @example\n * // Same input always produces same output\n * const msg = '{\"test\":true}'\n * messageHash(msg) === messageHash(msg) // true\n *\n * @example\n * // Different inputs produce different outputs\n * messageHash('{\"a\":1}') !== messageHash('{\"a\":2}') // true (almost always)\n */\nfunction messageHash(messageSt) {\n return toBase32(jenkinsOneAtATimeHash(messageSt));\n}\n\nmodule.exports = messageHash;\n", "/**\n * @fileoverview Core client socket connection module for api-ape\n *\n * This module manages WebSocket connections with automatic fallback to HTTP streaming\n * when WebSocket connections fail or are blocked (e.g., by corporate firewalls).\n *\n * ## Connection Flow\n * 1. Attempts WebSocket connection first (preferred for low latency)\n * 2. Falls back to HTTP streaming if WebSocket fails within 4 seconds\n * 3. Periodically retries WebSocket even when using HTTP streaming\n * 4. Handles reconnection automatically when connections drop\n *\n * ## Transport Modes\n * - `websocket` - Real-time bidirectional WebSocket connection\n * - `polling` - HTTP streaming fallback (GET for receiving, POST for sending)\n * - `auto` - Automatically selects best transport (default)\n *\n * ## Binary Data Support\n * The module transparently handles binary data (ArrayBuffer, Blob) by:\n * - Converting binary payloads to HTTP uploads\n * - Hydrating responses with linked binary resources\n * - Supporting client-to-client file sharing\n *\n * @module client/connectSocket\n * @see {@link module:client/connection/state} for connection state management\n * @see {@link module:client/transports/streaming} for HTTP fallback transport\n *\n * @example\n * // Basic usage\n * import connectSocket from './connectSocket.js'\n *\n * const client = connectSocket()\n * connectSocket.autoReconnect()\n *\n * // Send messages\n * client.sender.chat({ message: 'Hello!' })\n * .then(response => console.log(response))\n *\n * // Receive broadcasts\n * client.setOnReceiver('notification', (msg) => {\n * console.log('Received:', msg.data)\n * })\n *\n * // Monitor connection state\n * client.onConnectionChange((state) => {\n * console.log('Connection state:', state)\n * })\n */\n\nimport jss from \"../utils/jss\";\nimport { createStreamingTransport } from \"./transports/streaming\";\nimport {\n ConnectionState,\n notifyConnectionChange,\n onConnectionChange,\n} from \"./connection/state\";\nimport {\n getSocketUrl,\n checkCaptivePortal,\n scheduleNetworkRetry,\n setupOnlineListeners,\n WS_RETRY_INTERVAL,\n} from \"./connection/network\";\nimport { wrap } from \"./connection/proxy\";\nimport { createWsSend, createSender } from \"./connection/sender\";\nimport { setSendFn, resubscribeAll } from \"./connection/subscriptions\";\nimport {\n processIncomingData,\n dispatchMessage,\n setOnReceiver,\n} from \"./connection/messageHandler\";\n\n/**\n * Configured transport mode\n * @type {'auto'|'websocket'|'polling'}\n * @private\n */\nlet configuredTransport = \"auto\";\n\n/**\n * Currently active transport type\n * @type {'websocket'|'polling'|null}\n * @private\n */\nlet currentTransport = null;\n\n/**\n * HTTP streaming transport instance (created lazily)\n * @type {import('./transports/streaming').StreamingTransport|null}\n * @private\n */\nlet streamingTransport = null;\n\n/**\n * Timer for periodic WebSocket retry attempts\n * @type {number|null}\n * @private\n */\nlet wsRetryTimer = null;\n\n/**\n * Timeout before falling back to HTTP streaming (ms)\n * @constant {number}\n */\nconst WS_FALLBACK_TIMEOUT = 4000;\n\n/**\n * Current WebSocket instance, or false if not connected\n * @type {WebSocket|false}\n * @private\n */\nlet __socket = false;\n\n/**\n * Whether the connection is ready to send/receive messages\n * @type {boolean}\n * @private\n */\nlet ready = false;\n\n/**\n * Map of pending query IDs to their response callbacks\n * Used to match responses to their original requests\n * @type {Object.<string, function(Error|null, any): void>}\n * @private\n */\nconst waitingOn = {};\n\n/**\n * Queue of messages waiting to be sent when connection becomes ready\n * @type {Array<{type: string, data: any, resolve: function, reject: function, waiting: boolean, createdAt: number, timer: number}>}\n * @private\n */\nlet aWaitingSend = [];\n\n/**\n * Whether auto-reconnect is enabled\n * @type {boolean}\n * @private\n */\nlet reconnect = false;\n\n/**\n * WebSocket send function bound to current socket\n * @type {function(string, any, number, boolean=): Promise<any>}\n * @private\n */\nconst wsSend = createWsSend(() => __socket, waitingOn);\n\n// Setup browser online/offline listeners on module load\nif (typeof window !== \"undefined\") {\n setupOnlineListeners(attemptConnection);\n}\n\n/**\n * Flush all queued messages through the provided send function\n *\n * Called when connection becomes ready to send pending messages\n * that were queued while disconnected.\n *\n * @param {function(string, any, number): Promise<any>} sendFn - Send function to use\n * @private\n */\nfunction flushWaitingMessages(sendFn) {\n aWaitingSend.forEach(\n ({ type, data, resolve, reject, waiting, createdAt, timer }) => {\n clearTimeout(timer);\n const result = sendFn(type, data, createdAt);\n if (waiting) result.then(resolve).catch(reject);\n },\n );\n aWaitingSend = [];\n}\n\n/**\n * Switch from WebSocket to HTTP streaming transport\n *\n * Creates the streaming transport if needed and sets up event handlers.\n * This is called when WebSocket connection fails or times out.\n *\n * @private\n */\nfunction switchToStreaming() {\n console.log(\"\uD83E\uDD8D Switching to HTTP streaming transport\");\n currentTransport = \"polling\";\n\n if (!streamingTransport) {\n streamingTransport = createStreamingTransport();\n\n /**\n * Handle incoming messages from streaming transport\n * @param {{type: string, data: any, err: any}} msg - Parsed message\n */\n streamingTransport.onMessage = async (msg) => {\n const data = await processIncomingData(msg.data, msg.err);\n dispatchMessage(msg.type, msg.err, data);\n };\n\n /**\n * Handle streaming connection established\n */\n streamingTransport.onOpen = () => {\n ready = true;\n\n // Set up subscription send function for streaming and re-subscribe\n setSendFn((msg) => streamingTransport.sendRaw(msg));\n resubscribeAll();\n\n notifyConnectionChange(ConnectionState.Connected);\n flushWaitingMessages((t, d, c) => streamingTransport.send(t, d, c));\n startWsRetry();\n };\n\n /**\n * Handle streaming connection closed\n */\n streamingTransport.onClose = () => {\n ready = false;\n notifyConnectionChange(ConnectionState.Disconnected);\n };\n\n /**\n * Handle streaming transport errors\n * @param {Error} err - The error that occurred\n */\n streamingTransport.onError = (err) =>\n console.error(\"\uD83E\uDD8D Streaming error:\", err);\n }\n\n streamingTransport.connect();\n}\n\n/**\n * Start periodic WebSocket retry attempts\n *\n * When using HTTP streaming, periodically attempts to upgrade to WebSocket.\n * This allows the connection to upgrade when network conditions improve.\n *\n * @private\n */\nfunction startWsRetry() {\n if (\n wsRetryTimer ||\n currentTransport !== \"polling\" ||\n configuredTransport === \"polling\"\n )\n return;\n wsRetryTimer = setInterval(() => {\n if (currentTransport !== \"polling\") {\n clearInterval(wsRetryTimer);\n wsRetryTimer = null;\n return;\n }\n tryWebSocket(true);\n }, WS_RETRY_INTERVAL);\n}\n\n/**\n * Attempt to establish a WebSocket connection\n *\n * @param {boolean} [isRetry=false] - Whether this is a retry attempt from HTTP streaming mode\n * @private\n *\n * @description\n * Connection flow:\n * 1. Creates WebSocket to server's /api/ape endpoint\n * 2. Sets up fallback timer (only on initial connection with auto transport)\n * 3. On success: marks ready, flushes queued messages\n * 4. On failure: falls back to HTTP streaming (if auto mode)\n * 5. On close: schedules reconnection if auto-reconnect enabled\n */\nfunction tryWebSocket(isRetry = false) {\n const ws = new WebSocket(getSocketUrl());\n let fallbackTimer = null;\n\n // Set up fallback to HTTP streaming if WebSocket doesn't connect in time\n if (!isRetry && configuredTransport === \"auto\") {\n fallbackTimer = setTimeout(() => {\n if (ws.readyState !== WebSocket.OPEN) {\n ws.close();\n switchToStreaming();\n }\n }, WS_FALLBACK_TIMEOUT);\n }\n\n /**\n * Handle WebSocket connection opened\n */\n ws.onopen = () => {\n if (fallbackTimer) clearTimeout(fallbackTimer);\n\n // If retrying from polling mode, close the streaming transport\n if (isRetry && currentTransport === \"polling\") {\n if (streamingTransport) streamingTransport.close();\n if (wsRetryTimer) {\n clearInterval(wsRetryTimer);\n wsRetryTimer = null;\n }\n }\n\n currentTransport = \"websocket\";\n __socket = ws;\n ready = true;\n\n // Set up subscription send function and re-subscribe to all channels\n setSendFn((msg) => ws.send(jss.stringify(msg)));\n resubscribeAll();\n\n notifyConnectionChange(ConnectionState.Connected);\n flushWaitingMessages(wsSend);\n };\n\n /**\n * Handle incoming WebSocket messages\n * @param {MessageEvent} event - WebSocket message event\n */\n ws.onmessage = async (event) => {\n const { err, type, queryId, data } = jss.parse(event.data);\n\n // Check if this is a response to a pending request\n if (queryId && waitingOn[queryId]) {\n const hydratedData = await processIncomingData(data, err);\n waitingOn[queryId](err, hydratedData);\n delete waitingOn[queryId];\n return;\n }\n\n // Otherwise dispatch as a broadcast/push message\n const processed = await processIncomingData(data, err);\n dispatchMessage(type, err, processed);\n };\n\n /**\n * Handle WebSocket errors\n * @param {Event} err - Error event\n */\n ws.onerror = (err) => {\n if (fallbackTimer) clearTimeout(fallbackTimer);\n // Fall back to streaming on initial connection failure\n if (!isRetry && configuredTransport === \"auto\" && !ready)\n switchToStreaming();\n };\n\n /**\n * Handle WebSocket connection closed\n */\n ws.onclose = () => {\n if (fallbackTimer) clearTimeout(fallbackTimer);\n __socket = false;\n ready = false;\n\n // Only handle reconnection if we were using WebSocket transport\n if (currentTransport === \"websocket\") {\n notifyConnectionChange(ConnectionState.Disconnected);\n setTimeout(() => reconnect && connectSocket(), 500);\n }\n };\n}\n\n/**\n * Attempt to establish a connection to the server\n *\n * This function orchestrates the connection process:\n * 1. Checks if browser is online\n * 2. Detects captive portals (hotel/airport WiFi login pages)\n * 3. Initiates appropriate transport based on configuration\n *\n * @async\n * @private\n */\nasync function attemptConnection() {\n // Check browser online status first\n if (typeof navigator !== \"undefined\" && !navigator.onLine) {\n notifyConnectionChange(ConnectionState.Offline);\n return;\n }\n\n notifyConnectionChange(ConnectionState.Connecting);\n\n // Check for captive portal\n if ((await checkCaptivePortal()) === \"walled\") {\n notifyConnectionChange(ConnectionState.Walled);\n scheduleNetworkRetry(attemptConnection);\n return;\n }\n\n // Start appropriate transport\n configuredTransport === \"polling\" ? switchToStreaming() : tryWebSocket(false);\n}\n\n/**\n * Create the sender function with current connection state\n * @type {function(string, any): Promise<any>}\n * @private\n */\nconst sender = createSender(\n () => ready,\n () => wsSend,\n aWaitingSend,\n connectSocket,\n);\n\n/**\n * Initialize or retrieve the client connection\n *\n * This is the main entry point for establishing connections.\n * Calling it multiple times returns the same client interface.\n *\n * @returns {ClientInterface} Client interface with sender, receivers, and state management\n *\n * @example\n * const client = connectSocket()\n *\n * // Access proxied sender\n * client.sender.myEndpoint({ data: 'value' })\n *\n * // Subscribe to messages\n * client.setOnReceiver('eventType', handler)\n *\n * // Check current transport\n * console.log(client.transport) // 'websocket' or 'polling'\n */\nfunction connectSocket() {\n // Return existing interface if already connected\n if (__socket && __socket.readyState !== WebSocket.CLOSED)\n return buildClientInterface();\n if (currentTransport === \"polling\" && streamingTransport?.isConnected())\n return buildClientInterface();\n\n // Otherwise initiate connection\n attemptConnection();\n return buildClientInterface();\n}\n\n/**\n * Build the public client interface object\n *\n * @returns {ClientInterface} The client interface\n * @private\n *\n * @typedef {Object} ClientInterface\n * @property {Proxy} sender - Proxied sender for calling server endpoints\n * @property {function(string|function, function=): void} setOnReceiver - Register message handlers\n * @property {function(function): function} onConnectionChange - Subscribe to connection state changes\n * @property {'websocket'|'polling'|null} transport - Current transport type (read-only)\n */\nfunction buildClientInterface() {\n return {\n /**\n * Proxied sender object for calling server endpoints\n *\n * Properties accessed on this object are converted to API paths.\n *\n * @example\n * // Calls /chat endpoint\n * sender.chat({ message: 'Hi' })\n *\n * // Calls /users/123 endpoint\n * sender.users('/123', { action: 'get' })\n *\n * @type {Proxy}\n */\n sender: wrap(sender),\n\n /**\n * Register a message receiver/handler\n * @see {@link module:client/connection/messageHandler.setOnReceiver}\n */\n setOnReceiver,\n\n /**\n * Subscribe to connection state changes\n * @type {function(function(ConnectionStateValue): void): function(): void}\n */\n onConnectionChange,\n\n /**\n * Current transport type\n * @type {'websocket'|'polling'|null}\n * @readonly\n */\n get transport() {\n return currentTransport;\n },\n };\n}\n\n/**\n * Enable automatic reconnection on connection loss\n *\n * When enabled, the client will automatically attempt to reconnect\n * when the WebSocket connection is closed unexpectedly.\n *\n * @static\n * @memberof connectSocket\n *\n * @example\n * connectSocket.autoReconnect()\n */\nconnectSocket.autoReconnect = () => (reconnect = true);\n\n/**\n * Connection state enum reference\n *\n * @static\n * @memberof connectSocket\n * @type {typeof ConnectionState}\n *\n * @example\n * client.onConnectionChange((state) => {\n * if (state === connectSocket.ConnectionState.Connected) {\n * console.log('Connected!')\n * }\n * })\n */\nconnectSocket.ConnectionState = ConnectionState;\n\nexport default connectSocket;\nexport { ConnectionState };\n", "/**\n * @fileoverview HTTP Streaming Transport for api-ape Client\n *\n * This module provides an HTTP-based fallback transport when WebSocket\n * connections are blocked or unavailable (e.g., corporate firewalls,\n * restrictive proxies, or networks that block WebSocket upgrades).\n *\n * ## Transport Architecture\n *\n * The streaming transport uses two HTTP channels:\n * 1. **GET channel** (long-polling): Receives server messages via chunked transfer\n * 2. **POST channel**: Sends client messages as individual HTTP requests\n *\n * ```\n * \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n * \u2502 Client \u2502\n * \u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n * \u2502 Outgoing Messages (POST) \u2502 Incoming Messages (GET) \u2502\n * \u2502 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 \u2502 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 \u2502\n * \u2502 POST /api/ape/poll \u2502 GET /api/ape/poll \u2502\n * \u2502 Body: { type, data } \u2502 Chunked Response Stream \u2502\n * \u2502 Response: { data } \u2502 \u2190 JSON messages \u2502\n * \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n * ```\n *\n * ## Connection Lifecycle\n *\n * 1. `connect()` - Opens GET stream to receive server messages\n * 2. Messages arrive as chunked JSON in the response body\n * 3. `send()` - Sends messages via POST requests\n * 4. `close()` - Closes the stream and cleans up\n *\n * ## Heartbeat Mechanism\n *\n * The server sends periodic `__heartbeat__` messages to keep the connection\n * alive. These are filtered out and not passed to message handlers.\n *\n * ## Auto-Reconnection\n *\n * If the stream is interrupted, the transport automatically reconnects\n * after a short delay (500ms).\n *\n * @module client/transports/streaming\n * @see {@link module:client/connectSocket} for transport selection logic\n *\n * @example\n * // Creating and using the streaming transport\n * import { createStreamingTransport } from './streaming'\n *\n * const transport = createStreamingTransport()\n *\n * // Set up event handlers\n * transport.onOpen = () => console.log('Connected!')\n * transport.onMessage = (msg) => console.log('Received:', msg)\n * transport.onClose = () => console.log('Disconnected')\n * transport.onError = (err) => console.error('Error:', err)\n *\n * // Connect\n * transport.connect()\n *\n * // Send a message\n * const response = await transport.send('/chat', { text: 'Hello!' }, Date.now())\n *\n * // Close when done\n * transport.close()\n */\n\nimport jss from \"../../utils/jss\";\nimport { parseStreamBuffer } from \"./streamParser\";\n\n/**\n * Build the polling endpoint URL based on current page location\n *\n * Constructs the full URL to the /api/ape/poll endpoint, handling:\n * - Protocol (http/https)\n * - Hostname\n * - Port (with special handling for localhost development)\n *\n * @returns {string} Full URL to the polling endpoint\n * @private\n *\n * @example\n * // On https://example.com\n * getPollUrl() // 'https://example.com/api/ape/poll'\n *\n * // On http://localhost (no port specified)\n * getPollUrl() // 'http://localhost:9010/api/ape/poll'\n *\n * // On http://localhost:3000\n * getPollUrl() // 'http://localhost:3000/api/ape/poll'\n */\nfunction getPollUrl() {\n const hostname = window.location.hostname;\n const localServers = [\"localhost\", \"127.0.0.1\", \"[::1]\"];\n const isLocal = localServers.includes(hostname);\n const isHttps = window.location.protocol === \"https:\";\n const port = window.location.port || (isLocal ? 9010 : isHttps ? 443 : 80);\n const protocol = isHttps ? \"https\" : \"http\";\n const portSuffix = port !== 80 && port !== 443 ? `:${port}` : \"\";\n return `${protocol}://${hostname}${portSuffix}/api/ape/poll`;\n}\n\n/**\n * Create an HTTP streaming transport instance\n *\n * Factory function that creates a streaming transport object with methods\n * for connecting, sending messages, and handling events.\n *\n * ## Transport Object Properties\n *\n * | Property | Type | Description |\n * |---------------|----------|------------------------------------------|\n * | `connect` | Function | Initiates the streaming connection |\n * | `send` | Function | Sends a message via POST |\n * | `close` | Function | Closes the connection |\n * | `isConnected` | Function | Returns current connection status |\n * | `onMessage` | Setter | Handler for incoming messages |\n * | `onOpen` | Setter | Handler for connection open event |\n * | `onClose` | Setter | Handler for connection close event |\n * | `onError` | Setter | Handler for error events |\n *\n * ## Internal State\n *\n * The transport maintains internal state for:\n * - Active connection status\n * - Abort controller for cancelling fetch requests\n * - Stream buffer for partial JSON parsing\n * - Reconnection timer\n *\n * @returns {StreamingTransport} The streaming transport instance\n *\n * @typedef {Object} StreamingTransport\n * @property {function(): Promise<void>} connect - Connect to the server\n * @property {function(string, any, number): Promise<any>} send - Send a message\n * @property {function(): void} close - Close the connection\n * @property {function(): boolean} isConnected - Check if connected\n *\n * @example\n * // Basic usage\n * const transport = createStreamingTransport()\n *\n * transport.onMessage = (msg) => {\n * console.log('Type:', msg.type)\n * console.log('Data:', msg.data)\n * if (msg.err) console.error('Error:', msg.err)\n * }\n *\n * transport.onOpen = () => {\n * console.log('Stream connected')\n * }\n *\n * await transport.connect()\n *\n * @example\n * // Sending messages\n * const transport = createStreamingTransport()\n * await transport.connect()\n *\n * try {\n * const result = await transport.send('/users', { action: 'list' }, Date.now())\n * console.log('Users:', result)\n * } catch (err) {\n * console.error('Request failed:', err)\n * }\n *\n * @example\n * // Integration with connectSocket\n * if (shouldFallbackToStreaming) {\n * const streaming = createStreamingTransport()\n * streaming.onMessage = handleMessage\n * streaming.onOpen = () => {\n * notifyConnectionChange(ConnectionState.Connected)\n * flushPendingMessages(streaming.send)\n * }\n * streaming.connect()\n * }\n */\nfunction createStreamingTransport() {\n /**\n * Whether the transport is currently active/connected\n * @type {boolean}\n * @private\n */\n let isActive = false;\n\n /**\n * AbortController for cancelling the fetch request\n * @type {AbortController|null}\n * @private\n */\n let abortController = null;\n\n /**\n * Buffer for accumulating partial JSON from the stream\n * @type {string}\n * @private\n */\n let streamBuffer = \"\";\n\n /**\n * Timer for scheduled reconnection attempts\n * @type {number|null}\n * @private\n */\n let reconnectTimer = null;\n\n /**\n * Handler called when a message is received\n * @type {function({type: string, data: any, err?: any}): void}\n * @private\n */\n let onMessage = () => {};\n\n /**\n * Handler called when connection is established\n * @type {function(): void}\n * @private\n */\n let onOpen = () => {};\n\n /**\n * Handler called when connection is closed\n * @type {function(): void}\n * @private\n */\n let onClose = () => {};\n\n /**\n * Handler called when an error occurs\n * @type {function(Error): void}\n * @private\n */\n let onError = () => {};\n\n /**\n * Schedule a reconnection attempt after a delay\n *\n * Used when the stream is interrupted unexpectedly.\n * Only schedules if the transport is still active.\n *\n * @private\n */\n function scheduleReconnect() {\n if (!isActive) return;\n if (reconnectTimer) clearTimeout(reconnectTimer);\n reconnectTimer = setTimeout(() => {\n if (isActive) connect();\n }, 500);\n }\n\n /**\n * Connect to the server and start receiving messages\n *\n * Initiates a GET request to the polling endpoint. The server keeps\n * this connection open and streams JSON messages as chunked responses.\n *\n * ## Connection Flow\n *\n * 1. Abort any existing connection\n * 2. Create new AbortController for cancellation\n * 3. Start GET request to /api/ape/poll\n * 4. On success, call onOpen and start reading stream\n * 5. Parse incoming data for complete JSON objects\n * 6. Dispatch messages (except heartbeats) to onMessage\n * 7. On stream end, schedule reconnection\n *\n * ## Error Handling\n *\n * - Network errors trigger onError and reconnection\n * - AbortError (from close()) is silently ignored\n * - HTTP errors throw and trigger reconnection\n *\n * @async\n * @returns {Promise<void>}\n *\n * @example\n * const transport = createStreamingTransport()\n * transport.onOpen = () => console.log('Connected!')\n * transport.onMessage = (msg) => console.log('Message:', msg)\n * await transport.connect()\n */\n async function connect() {\n if (isActive) return;\n isActive = true;\n abortController = new AbortController();\n\n try {\n const response = await fetch(getPollUrl(), {\n method: \"GET\",\n credentials: \"include\",\n signal: abortController.signal,\n headers: { Accept: \"application/json\" },\n });\n\n if (!response.ok)\n throw new Error(`Stream connect failed: ${response.status}`);\n\n // Connection established\n onOpen();\n\n // Start reading the stream\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n\n /**\n * Read loop - continuously reads from the stream\n * @async\n * @private\n */\n async function read() {\n while (isActive) {\n try {\n const { done, value } = await reader.read();\n\n if (done) {\n // Stream ended - reconnect\n scheduleReconnect();\n return;\n }\n\n // Decode chunk and add to buffer\n streamBuffer += decoder.decode(value, { stream: true });\n\n // Parse complete JSON objects from buffer\n const { messages, remaining } = parseStreamBuffer(streamBuffer);\n streamBuffer = remaining;\n\n // Dispatch messages (filter out heartbeats)\n for (const msg of messages) {\n if (msg.type === \"__heartbeat__\") continue;\n onMessage(msg);\n }\n } catch (readErr) {\n // Ignore abort errors (from close())\n if (readErr.name === \"AbortError\") return;\n\n console.error(\"\uD83E\uDD8D Stream read error:\", readErr);\n scheduleReconnect();\n return;\n }\n }\n }\n\n // Start the read loop\n read();\n } catch (err) {\n // Ignore abort errors (from close())\n if (err.name === \"AbortError\") return;\n\n console.error(\"\uD83E\uDD8D Stream connection error:\", err);\n onError(err);\n scheduleReconnect();\n }\n }\n\n /**\n * Send a message to the server via HTTP POST\n *\n * Each send() call creates a new POST request to the polling endpoint.\n * The server processes the message and returns the response.\n *\n * ## Request Format\n *\n * ```json\n * {\n * \"type\": \"/chat\",\n * \"data\": { \"message\": \"Hello\" },\n * \"createdAt\": \"2024-01-01T00:00:00.000Z\"\n * }\n * ```\n *\n * ## Response Format\n *\n * ```json\n * {\n * \"data\": { \"result\": \"success\" }\n * }\n * ```\n *\n * or on error:\n *\n * ```json\n * {\n * \"error\": \"Error message\"\n * }\n * ```\n *\n * @async\n * @param {string} type - The message type/endpoint path (e.g., '/chat')\n * @param {any} data - The payload data to send\n * @param {number} createdAt - Timestamp when the request was initiated\n * @returns {Promise<any>} The server's response data\n * @throws {Error} If the request fails or server returns an error\n *\n * @example\n * // Simple request\n * const result = await transport.send('/ping', {}, Date.now())\n *\n * @example\n * // With data\n * const user = await transport.send('/users/create', {\n * name: 'Alice',\n * email: 'alice@example.com'\n * }, Date.now())\n *\n * @example\n * // Error handling\n * try {\n * await transport.send('/protected', {}, Date.now())\n * } catch (err) {\n * console.error('Request failed:', err.message)\n * }\n */\n async function send(type, data, createdAt) {\n const payload = { type, data, createdAt: new Date(createdAt) };\n\n const response = await fetch(getPollUrl(), {\n method: \"POST\",\n credentials: \"include\",\n headers: { \"Content-Type\": \"application/json\" },\n body: jss.stringify(payload),\n });\n\n if (!response.ok) {\n const error = await response\n .json()\n .catch(() => ({ error: \"Unknown error\" }));\n throw new Error(error.error || `Request failed: ${response.status}`);\n }\n\n return jss.parse(await response.text()).data;\n }\n\n /**\n * Send a raw message object to the server (for subscribe/unsubscribe)\n *\n * Unlike send(), this sends the object directly without wrapping it\n * in the standard type/data/createdAt format.\n *\n * @async\n * @param {Object} msg - The raw message object to send\n * @returns {Promise<void>}\n *\n * @example\n * // Subscribe to a channel\n * await transport.sendRaw({ subscribe: '/news/banking' })\n *\n * // Unsubscribe from a channel\n * await transport.sendRaw({ unsubscribe: '/news/banking' })\n */\n async function sendRaw(msg) {\n await fetch(getPollUrl(), {\n method: \"POST\",\n credentials: \"include\",\n headers: { \"Content-Type\": \"application/json\" },\n body: jss.stringify(msg),\n });\n }\n\n /**\n * Close the streaming connection\n *\n * Gracefully shuts down the transport:\n * 1. Marks transport as inactive\n * 2. Clears any pending reconnection timer\n * 3. Aborts the active fetch request\n * 4. Clears the stream buffer\n * 5. Calls the onClose handler\n *\n * After close(), the transport can be reconnected by calling connect() again.\n *\n * @returns {void}\n *\n * @example\n * // Normal shutdown\n * transport.close()\n *\n * @example\n * // Close and reconnect\n * transport.close()\n * await transport.connect() // Can reconnect after close\n *\n * @example\n * // Close on page unload\n * window.addEventListener('beforeunload', () => {\n * transport.close()\n * })\n */\n function close() {\n isActive = false;\n\n // Clear reconnection timer\n if (reconnectTimer) {\n clearTimeout(reconnectTimer);\n reconnectTimer = null;\n }\n\n // Abort the fetch request\n if (abortController) {\n abortController.abort();\n abortController = null;\n }\n\n // Clear the buffer\n streamBuffer = \"\";\n\n // Notify listeners\n onClose();\n }\n\n /**\n * The streaming transport instance\n *\n * @type {StreamingTransport}\n */\n return {\n /**\n * Connect to the server\n * @type {function(): Promise<void>}\n */\n connect,\n\n /**\n * Send a message to the server\n * @type {function(string, any, number): Promise<any>}\n */\n send,\n\n /**\n * Send a raw message object (for subscribe/unsubscribe)\n * @type {function(Object): Promise<void>}\n */\n sendRaw,\n\n /**\n * Close the connection\n * @type {function(): void}\n */\n close,\n\n /**\n * Check if the transport is currently connected\n * @type {function(): boolean}\n */\n isConnected: () => isActive,\n\n /**\n * Set the message handler\n * @param {function({type: string, data: any, err?: any}): void} fn - Handler function\n */\n set onMessage(fn) {\n onMessage = fn;\n },\n\n /**\n * Set the open handler\n * @param {function(): void} fn - Handler function\n */\n set onOpen(fn) {\n onOpen = fn;\n },\n\n /**\n * Set the close handler\n * @param {function(): void} fn - Handler function\n */\n set onClose(fn) {\n onClose = fn;\n },\n\n /**\n * Set the error handler\n * @param {function(Error): void} fn - Handler function\n */\n set onError(fn) {\n onError = fn;\n },\n };\n}\n\n/**\n * Export the factory function and URL helper\n *\n * @example\n * import { createStreamingTransport, getPollUrl } from './streaming'\n *\n * // Create a new transport\n * const transport = createStreamingTransport()\n *\n * // Get the polling URL (useful for debugging)\n * console.log('Polling URL:', getPollUrl())\n */\nexport { createStreamingTransport, getPollUrl };\n", "/**\n * @fileoverview Stream Buffer Parser for api-ape HTTP Streaming Transport\n *\n * This module provides utilities for parsing JSON objects from streaming\n * HTTP response data. When using HTTP streaming as a fallback transport,\n * the server sends multiple JSON objects over a single connection. This\n * parser extracts complete JSON objects from the incoming byte stream.\n *\n * ## The Challenge\n *\n * HTTP streaming delivers data in arbitrary chunks - a single JSON object\n * might span multiple chunks, or one chunk might contain multiple objects.\n * This parser maintains a buffer and uses brace-counting to detect complete\n * JSON objects.\n *\n * ## Parsing Strategy\n *\n * The parser counts `{` and `}` characters while properly handling:\n * - String literals (braces inside strings don't count)\n * - Escape sequences (backslash escaping)\n * - Nested objects and arrays\n *\n * When brace depth returns to zero, a complete JSON object has been found.\n *\n * @module client/transports/streamParser\n * @see {@link module:client/transports/streaming} for the streaming transport\n *\n * @example\n * import { parseStreamBuffer } from './streamParser'\n *\n * let buffer = ''\n *\n * // As chunks arrive from the stream\n * socket.on('data', (chunk) => {\n * buffer += chunk\n * const { messages, remaining } = parseStreamBuffer(buffer)\n * buffer = remaining\n *\n * for (const msg of messages) {\n * handleMessage(msg)\n * }\n * })\n */\n\nimport jss from \"../../utils/jss\";\n\n/**\n * Parse JSON objects from a streaming buffer using brace counting\n *\n * This function extracts complete JSON objects from a buffer that may\n * contain partial data. It correctly handles strings containing braces\n * by tracking the in-string state.\n *\n * ## Algorithm\n *\n * 1. Iterate through each character in the buffer\n * 2. Track escape sequences (`\\`) to handle `\\\"` properly\n * 3. Track string boundaries (`\"`) to ignore braces inside strings\n * 4. Count `{` as +1 depth, `}` as -1 depth\n * 5. When depth returns to 0, extract and parse the complete JSON\n * 6. Continue parsing for more objects\n * 7. Return unparsed remainder for the next chunk\n *\n * ## Edge Cases Handled\n *\n * - Escaped quotes: `\\\"` doesn't toggle string state\n * - Nested objects: `{\"a\":{\"b\":1}}` parses as one object\n * - Multiple objects: `{}{}{}` yields three objects\n * - Partial data: `{\"a\":1` returns empty messages, full buffer as remaining\n * - Braces in strings: `{\"text\":\"{hello}\"}` parses correctly\n *\n * @param {string} buffer - Raw streaming buffer content (accumulated chunks)\n * @returns {{messages: Object[], remaining: string}} Parse result object\n * @property {Object[]} messages - Array of successfully parsed JSON objects\n * @property {string} remaining - Unparsed portion of the buffer (partial JSON)\n *\n * @example\n * // Single complete object\n * const result = parseStreamBuffer('{\"type\":\"ping\"}')\n * // result.messages = [{ type: 'ping' }]\n * // result.remaining = ''\n *\n * @example\n * // Multiple complete objects\n * const result = parseStreamBuffer('{\"a\":1}{\"b\":2}{\"c\":3}')\n * // result.messages = [{ a: 1 }, { b: 2 }, { c: 3 }]\n * // result.remaining = ''\n *\n * @example\n * // Partial object at end\n * const result = parseStreamBuffer('{\"a\":1}{\"b\":')\n * // result.messages = [{ a: 1 }]\n * // result.remaining = '{\"b\":'\n *\n * @example\n * // Object with nested structure\n * const result = parseStreamBuffer('{\"user\":{\"name\":\"Alice\",\"age\":30}}')\n * // result.messages = [{ user: { name: 'Alice', age: 30 } }]\n *\n * @example\n * // String containing braces (handled correctly)\n * const result = parseStreamBuffer('{\"template\":\"{name} says {message}\"}')\n * // result.messages = [{ template: '{name} says {message}' }]\n *\n * @example\n * // Continuous streaming usage\n * let buffer = ''\n *\n * function onChunk(chunk) {\n * buffer += chunk\n * const { messages, remaining } = parseStreamBuffer(buffer)\n * buffer = remaining // Save partial data for next chunk\n *\n * messages.forEach(msg => {\n * if (msg.type === '__heartbeat__') return // Skip heartbeats\n * processMessage(msg)\n * })\n * }\n *\n * @example\n * // Escaped characters in strings\n * const result = parseStreamBuffer('{\"quote\":\"\\\\\"Hello\\\\\"\"}')\n * // result.messages = [{ quote: '\"Hello\"' }]\n */\nexport function parseStreamBuffer(buffer) {\n /** @type {Object[]} Array of successfully parsed messages */\n const messages = [];\n\n /** @type {number} Start index of current JSON object (-1 if not in object) */\n let start = -1;\n\n /** @type {number} Current brace nesting depth */\n let depth = 0;\n\n /** @type {boolean} Whether currently inside a string literal */\n let inString = false;\n\n /** @type {boolean} Whether the previous character was a backslash escape */\n let escaped = false;\n\n // Process each character in the buffer\n for (let i = 0; i < buffer.length; i++) {\n const char = buffer[i];\n\n // If previous char was backslash, this char is escaped - skip it\n if (escaped) {\n escaped = false;\n continue;\n }\n\n // Backslash inside string starts an escape sequence\n if (char === \"\\\\\" && inString) {\n escaped = true;\n continue;\n }\n\n // Toggle string state on unescaped quotes\n if (char === '\"') {\n inString = !inString;\n continue;\n }\n\n // Skip brace counting while inside strings\n if (inString) continue;\n\n // Track brace depth\n if (char === \"{\") {\n if (depth === 0) start = i; // Mark start of new JSON object\n depth++;\n } else if (char === \"}\") {\n depth--;\n\n // Depth returning to 0 means we have a complete object\n if (depth === 0 && start !== -1) {\n const jsonStr = buffer.slice(start, i + 1);\n\n try {\n // Parse with JSS to handle extended types (Date, RegExp, etc.)\n messages.push(jss.parse(jsonStr));\n } catch (e) {\n console.error(\"\uD83E\uDD8D Failed to parse stream message:\", e);\n }\n\n start = -1; // Reset for next object\n }\n }\n }\n\n // Calculate remaining unparsed data\n // If we're mid-object (start !== -1), keep from start\n // Otherwise, buffer is empty or only whitespace\n const remaining = start !== -1 ? buffer.slice(start) : \"\";\n\n return { messages, remaining };\n}\n", "/**\n * @fileoverview Connection State Management for api-ape Client\n *\n * This module manages WebSocket/HTTP connection state and provides\n * a subscription mechanism for components to react to state changes.\n *\n * ## Connection States\n *\n * The connection lifecycle follows these states:\n *\n * ```\n * \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n * \u2502 offline \u2502 (navigator.onLine = false)\n * \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n * \u2502 browser goes online\n * \u25BC\n * \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n * \u2502 walled \u2502\u25C4\u2500\u2500\u2500\u2502 connecting \u2502\n * \u2502 (captive \u2502 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n * \u2502 portal) \u2502 \u2502 connection succeeds\n * \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u25BC\n * \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n * \u2502 connected \u2502\n * \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n * \u2502 connection lost\n * \u25BC\n * \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n * \u2502disconnected \u2502\u2500\u2500\u25BA (retry connecting)\n * \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n * ```\n *\n * ## Usage\n *\n * Internal modules use `notifyConnectionChange()` to update state.\n * External code uses `onConnectionChange()` to subscribe to changes.\n *\n * @module client/connection/state\n * @author api-ape\n *\n * @example\n * // Subscribe to connection changes\n * import { onConnectionChange, ConnectionState } from './state'\n *\n * const unsubscribe = onConnectionChange((state) => {\n * if (state === ConnectionState.Connected) {\n * console.log('\u2705 Connected to server')\n * } else if (state === ConnectionState.Offline) {\n * console.log('\uD83D\uDCF5 Device is offline')\n * }\n * })\n *\n * // Later: stop listening\n * unsubscribe()\n *\n * @example\n * // Internal usage - updating state\n * import { notifyConnectionChange, ConnectionState } from './state'\n *\n * notifyConnectionChange(ConnectionState.Connecting)\n * // ... attempt connection ...\n * notifyConnectionChange(ConnectionState.Connected)\n */\n\n/**\n * Valid connection state string values\n *\n * @typedef {'offline'|'walled'|'disconnected'|'connecting'|'connected'|'closing'} ConnectionStateValue\n */\n\n/**\n * Connection state enumeration\n *\n * Provides named constants for all possible connection states.\n * Use these constants instead of string literals for type safety.\n *\n * @readonly\n * @enum {ConnectionStateValue}\n *\n * @property {string} Offline - Browser's navigator.onLine reports false.\n * The device has no network connectivity.\n * @property {string} Walled - Captive portal detected (e.g., hotel/airport WiFi).\n * The ping endpoint returned an unexpected response,\n * indicating network traffic is being intercepted.\n * @property {string} Disconnected - Not currently connected to the server.\n * This is the default state before first connection\n * and after a connection is lost.\n * @property {string} Connecting - Actively attempting to establish a connection.\n * WebSocket handshake or HTTP streaming setup in progress.\n * @property {string} Connected - Successfully connected and ready to send/receive messages.\n * @property {string} Closing - Connection is being gracefully closed.\n * No new messages will be sent.\n *\n * @example\n * import { ConnectionState } from './state'\n *\n * function handleState(state) {\n * switch (state) {\n * case ConnectionState.Offline:\n * showOfflineIndicator()\n * break\n * case ConnectionState.Walled:\n * showCaptivePortalWarning()\n * break\n * case ConnectionState.Connected:\n * hideConnectionWarnings()\n * enableUI()\n * break\n * case ConnectionState.Disconnected:\n * showReconnectingIndicator()\n * break\n * }\n * }\n */\nexport const ConnectionState = {\n /**\n * Browser's navigator.onLine is false - no network connectivity\n * @type {ConnectionStateValue}\n */\n Offline: \"offline\",\n\n /**\n * Captive portal detected - network is intercepting traffic\n * @type {ConnectionStateValue}\n */\n Walled: \"walled\",\n\n /**\n * Not connected - either initial state or connection was lost\n * @type {ConnectionStateValue}\n */\n Disconnected: \"disconnected\",\n\n /**\n * Connection attempt in progress\n * @type {ConnectionStateValue}\n */\n Connecting: \"connecting\",\n\n /**\n * Successfully connected and ready for communication\n * @type {ConnectionStateValue}\n */\n Connected: \"connected\",\n\n /**\n * Connection is being gracefully closed\n * @type {ConnectionStateValue}\n */\n Closing: \"closing\",\n};\n\n/**\n * Current connection state\n *\n * Initialized based on navigator.onLine status if available,\n * otherwise defaults to 'disconnected'.\n *\n * @type {ConnectionStateValue}\n * @private\n */\nlet connectionState =\n typeof navigator !== \"undefined\" && !navigator.onLine\n ? ConnectionState.Offline\n : ConnectionState.Disconnected;\n\n/**\n * Array of registered connection change listener functions\n *\n * Each listener is called with the new state whenever it changes.\n *\n * @type {Array<function(ConnectionStateValue): void>}\n * @private\n */\nconst connectionChangeListeners = [];\n\n/**\n * Update the connection state and notify all listeners\n *\n * This function is called internally by connection management code\n * (connectSocket, network utilities) when the connection state changes.\n * It only notifies listeners if the state actually changed.\n *\n * @param {ConnectionStateValue} newState - The new connection state\n * @returns {void}\n *\n * @example\n * // Internal usage in connectSocket.js\n * import { notifyConnectionChange, ConnectionState } from './state'\n *\n * // When WebSocket opens\n * ws.onopen = () => {\n * notifyConnectionChange(ConnectionState.Connected)\n * }\n *\n * // When WebSocket closes\n * ws.onclose = () => {\n * notifyConnectionChange(ConnectionState.Disconnected)\n * }\n *\n * @example\n * // Detecting captive portal\n * if (await checkCaptivePortal() === 'walled') {\n * notifyConnectionChange(ConnectionState.Walled)\n * }\n */\nexport function notifyConnectionChange(newState) {\n if (connectionState !== newState) {\n connectionState = newState;\n connectionChangeListeners.forEach((fn) => fn(newState));\n }\n}\n\n/**\n * Get the current connection state\n *\n * Returns the current state without subscribing to changes.\n * For reactive updates, use `onConnectionChange()` instead.\n *\n * @returns {ConnectionStateValue} The current connection state\n *\n * @example\n * import { getConnectionState, ConnectionState } from './state'\n *\n * if (getConnectionState() === ConnectionState.Connected) {\n * sendMessage({ text: 'Hello!' })\n * } else {\n * queueMessage({ text: 'Hello!' })\n * }\n *\n * @example\n * // Check if online before making request\n * function canSendMessage() {\n * const state = getConnectionState()\n * return state === ConnectionState.Connected\n * }\n */\nexport function getConnectionState() {\n return connectionState;\n}\n\n/**\n * Subscribe to connection state changes\n *\n * Registers a handler function that will be called whenever the\n * connection state changes. The handler is immediately invoked\n * with the current state upon registration.\n *\n * @param {function(ConnectionStateValue): void} handler - Callback function\n * that receives the new state value whenever it changes\n * @returns {function(): void} Unsubscribe function - call this to remove\n * the handler and stop receiving updates\n *\n * @example\n * // Basic usage with cleanup\n * import { onConnectionChange } from './state'\n *\n * const unsubscribe = onConnectionChange((state) => {\n * console.log('Connection state:', state)\n * })\n *\n * // When component unmounts or cleanup needed:\n * unsubscribe()\n *\n * @example\n * // React component integration\n * function useConnectionState() {\n * const [state, setState] = useState('disconnected')\n *\n * useEffect(() => {\n * const unsubscribe = onConnectionChange(setState)\n * return unsubscribe\n * }, [])\n *\n * return state\n * }\n *\n * @example\n * // Show/hide UI elements based on state\n * onConnectionChange((state) => {\n * const offlineBanner = document.getElementById('offline-banner')\n * const sendButton = document.getElementById('send-btn')\n *\n * switch (state) {\n * case 'connected':\n * offlineBanner.hidden = true\n * sendButton.disabled = false\n * break\n * case 'offline':\n * case 'walled':\n * offlineBanner.hidden = false\n * sendButton.disabled = true\n * break\n * case 'connecting':\n * offlineBanner.textContent = 'Reconnecting...'\n * offlineBanner.hidden = false\n * break\n * }\n * })\n *\n * @example\n * // Logging connection events\n * onConnectionChange((state) => {\n * const timestamp = new Date().toISOString()\n * console.log(`[${timestamp}] Connection: ${state}`)\n *\n * // Send to analytics\n * analytics.track('connection_state_change', { state, timestamp })\n * })\n */\nexport function onConnectionChange(handler) {\n connectionChangeListeners.push(handler);\n\n // Immediately call with current state so subscriber knows initial state\n handler(connectionState);\n\n // Return unsubscribe function\n return () => {\n const idx = connectionChangeListeners.indexOf(handler);\n if (idx > -1) connectionChangeListeners.splice(idx, 1);\n };\n}\n", "/**\n * @fileoverview Network detection and captive portal utilities for api-ape client\n *\n * This module provides network-related functionality for the api-ape client:\n * - Captive portal detection (hotel/airport WiFi login pages)\n * - Online/offline browser event handling\n * - URL construction for WebSocket and HTTP endpoints\n * - Network retry scheduling\n *\n * ## Captive Portal Detection\n * Captive portals intercept HTTP requests and redirect to login pages.\n * This module detects them by:\n * 1. Pinging a known endpoint (/api/ape/ping)\n * 2. Verifying the response contains expected JSON structure\n * 3. Validating timestamp to detect proxy replay attacks\n *\n * @module client/connection/network\n * @see {@link module:client/connection/state} for connection state management\n *\n * @example\n * import {\n * checkCaptivePortal,\n * getSocketUrl,\n * getBaseUrl,\n * setupOnlineListeners\n * } from './network'\n *\n * // Check if we're behind a captive portal\n * const status = await checkCaptivePortal()\n * if (status === 'walled') {\n * console.log('Please complete WiFi login')\n * }\n *\n * // Get WebSocket URL\n * const wsUrl = getSocketUrl() // 'wss://example.com/api/ape'\n */\n\nimport { ConnectionState, notifyConnectionChange } from \"./state\";\n\n/**\n * Timeout duration for ping requests (milliseconds)\n * If the ping doesn't complete within this time, assume captive portal\n * @constant {number}\n * @private\n */\nconst PING_TIMEOUT = 3000;\n\n/**\n * Maximum allowed clock skew between client and server (milliseconds)\n * Used to detect stale/replayed ping responses from caching proxies\n * @constant {number}\n * @private\n */\nconst MAX_PING_CLOCK_SKEW = 60000;\n\n/**\n * Interval between WebSocket retry attempts when in polling mode (milliseconds)\n * Also used as the interval for network retry checks\n * @constant {number}\n */\nexport const WS_RETRY_INTERVAL = 30000;\n\n/**\n * Timer ID for scheduled network retry\n * @type {number|null}\n * @private\n */\nlet networkCheckTimer = null;\n\n/**\n * Check if running in development/local mode\n *\n * Used to enable additional debug logging when running locally.\n * Returns true for localhost, 127.0.0.1, and IPv6 loopback addresses.\n *\n * @returns {boolean} True if running on a local development server\n *\n * @example\n * if (isDevMode()) {\n * console.log('Debug: Connection attempt started')\n * }\n */\nexport function isDevMode() {\n if (typeof window === \"undefined\") return false;\n return [\"localhost\", \"127.0.0.1\", \"[::1]\"].includes(window.location.hostname);\n}\n\n/**\n * Build the ping URL for captive portal detection\n *\n * Constructs the full URL to the /api/ape/ping endpoint based on\n * the current page's protocol, hostname, and port.\n *\n * @returns {string} Full URL to the ping endpoint\n *\n * @example\n * // On https://example.com\n * getPingUrl() // Returns: 'https://example.com/api/ape/ping'\n *\n * // On http://localhost:3000\n * getPingUrl() // Returns: 'http://localhost:3000/api/ape/ping'\n */\nexport function getPingUrl() {\n const hostname = window.location.hostname;\n const isHttps = window.location.protocol === \"https:\";\n const port = window.location.port || (isHttps ? 443 : 80);\n const protocol = isHttps ? \"https\" : \"http\";\n const portSuffix = port !== 80 && port !== 443 ? `:${port}` : \"\";\n return `${protocol}://${hostname}${portSuffix}/api/ape/ping`;\n}\n\n/**\n * Check for captive portal by pinging the api-ape ping endpoint\n *\n * This function detects captive portals (hotel/airport WiFi login pages)\n * by making a request to a known endpoint and validating the response.\n *\n * ## Detection Logic\n * 1. Sends GET request to /api/ape/ping\n * 2. Verifies HTTP 200 response\n * 3. Checks response JSON contains `{ ok: true }`\n * 4. Validates timestamp is within acceptable clock skew\n *\n * If any check fails, assumes we're behind a captive portal.\n *\n * @returns {Promise<'ok'|'walled'>} 'ok' if real internet connection,\n * 'walled' if captive portal detected\n *\n * @example\n * const status = await checkCaptivePortal()\n *\n * if (status === 'walled') {\n * // Show message to user to complete WiFi login\n * showCaptivePortalWarning()\n * } else {\n * // Proceed with WebSocket connection\n * connectWebSocket()\n * }\n *\n * @example\n * // In connection flow\n * async function attemptConnection() {\n * if (await checkCaptivePortal() === 'walled') {\n * notifyConnectionChange(ConnectionState.Walled)\n * scheduleNetworkRetry(attemptConnection)\n * return\n * }\n * // Continue with connection...\n * }\n */\nexport async function checkCaptivePortal() {\n try {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), PING_TIMEOUT);\n\n const response = await fetch(getPingUrl(), {\n cache: \"no-store\",\n signal: controller.signal,\n });\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n if (isDevMode()) {\n console.error(\"\uD83E\uDD8D [DEV] Ping failed: HTTP\", response.status);\n }\n return \"walled\";\n }\n\n const data = await response.json();\n\n // Verify response is genuine (not a captive portal redirect page)\n if (data?.ok !== true) {\n if (isDevMode()) {\n console.error(\"\uD83E\uDD8D [DEV] Ping failed: invalid response\", data);\n }\n return \"walled\";\n }\n\n // Validate timestamp to detect proxy replay attacks\n if (typeof data.ts === \"number\") {\n const now = Date.now();\n const skew = Math.abs(now - data.ts);\n if (skew > MAX_PING_CLOCK_SKEW) {\n if (isDevMode()) {\n const direction =\n now > data.ts ? \"Server time is behind\" : \"Server time is ahead\";\n console.error(\n `\uD83E\uDD8D [DEV] Ping failed: timestamp too old/stale\\n` +\n ` Skew: ${skew}ms (~${Math.round(skew / 1000)}s)\\n` +\n ` Client time: ${new Date(now).toISOString()}\\n` +\n ` Server time: ${new Date(data.ts).toISOString()}\\n` +\n ` ${direction} by ${Math.round(skew / 1000)}s\\n` +\n ` Max allowed: ${MAX_PING_CLOCK_SKEW}ms`,\n );\n }\n return \"walled\";\n }\n }\n\n return \"ok\";\n } catch (err) {\n if (isDevMode()) {\n console.error(\"\uD83E\uDD8D [DEV] Ping failed:\", err.message || err);\n }\n return \"walled\";\n }\n}\n\n/**\n * Schedule a retry of network connectivity check\n *\n * Used when in 'walled' or 'offline' states to periodically\n * check if network connectivity has been restored.\n *\n * Only one retry can be scheduled at a time - calling this\n * function while a retry is already scheduled has no effect.\n *\n * @param {Function} attemptConnectionFn - Function to call for connection attempt\n * @returns {void}\n *\n * @example\n * // In connection handler\n * if (status === 'walled') {\n * notifyConnectionChange(ConnectionState.Walled)\n * scheduleNetworkRetry(attemptConnection)\n * }\n *\n * @example\n * // The scheduled function will be called after WS_RETRY_INTERVAL\n * scheduleNetworkRetry(() => {\n * console.log('Retrying connection...')\n * attemptConnection()\n * })\n */\nexport function scheduleNetworkRetry(attemptConnectionFn) {\n if (networkCheckTimer) return;\n networkCheckTimer = setTimeout(() => {\n networkCheckTimer = null;\n attemptConnectionFn();\n }, WS_RETRY_INTERVAL);\n}\n\n/**\n * Setup browser online/offline event listeners\n *\n * Registers event listeners for the browser's online/offline events.\n * When the browser goes online, triggers a connection attempt.\n * When the browser goes offline, notifies the connection state.\n *\n * This function is safe to call in non-browser environments (no-op).\n *\n * @param {Function} attemptConnectionFn - Function to call when browser goes online\n * @returns {void}\n *\n * @example\n * // Setup listeners on module load\n * setupOnlineListeners(attemptConnection)\n *\n * // Now these events are handled automatically:\n * // - Browser goes offline -> ConnectionState.Offline\n * // - Browser goes online -> attemptConnection() is called\n */\nexport function setupOnlineListeners(attemptConnectionFn) {\n if (typeof window === \"undefined\") return;\n\n window.addEventListener(\"online\", () => {\n console.log(\"\uD83E\uDD8D Browser went online, checking network...\");\n attemptConnectionFn();\n });\n\n window.addEventListener(\"offline\", () => {\n console.log(\"\uD83E\uDD8D Browser went offline\");\n notifyConnectionChange(ConnectionState.Offline);\n });\n}\n\n/**\n * Get the base URL for HTTP API endpoints\n *\n * Constructs the base URL (protocol + hostname + port) for making\n * HTTP requests to the api-ape server. Used for file uploads/downloads\n * and HTTP streaming fallback.\n *\n * For local development servers (localhost, 127.0.0.1, [::1]),\n * defaults to port 9010 if no port is specified.\n *\n * @returns {string} Base URL without trailing slash\n *\n * @example\n * // On https://example.com\n * getBaseUrl() // Returns: 'https://example.com'\n *\n * // On http://localhost (no port specified)\n * getBaseUrl() // Returns: 'http://localhost:9010'\n *\n * // On http://localhost:3000\n * getBaseUrl() // Returns: 'http://localhost:3000'\n *\n * @example\n * // Usage for file downloads\n * const baseUrl = getBaseUrl()\n * const fileUrl = `${baseUrl}/api/ape/data/${fileHash}`\n * const response = await fetch(fileUrl)\n */\nexport function getBaseUrl() {\n const hostname = window.location.hostname;\n const isLocal = [\"localhost\", \"127.0.0.1\", \"[::1]\"].includes(hostname);\n const isHttps = window.location.protocol === \"https:\";\n const port = window.location.port || (isLocal ? 9010 : isHttps ? 443 : 80);\n const protocol = isHttps ? \"https\" : \"http\";\n const portSuffix = port !== 80 && port !== 443 ? `:${port}` : \"\";\n return `${protocol}://${hostname}${portSuffix}`;\n}\n\n/**\n * Get the WebSocket URL for api-ape connections\n *\n * Constructs the full WebSocket URL for connecting to the api-ape server.\n * Automatically selects ws:// or wss:// based on the page's protocol.\n *\n * For local development servers (localhost, 127.0.0.1, [::1]),\n * defaults to port 9010 if no port is specified.\n *\n * @returns {string} WebSocket URL (ws:// or wss://)\n *\n * @example\n * // On https://example.com\n * getSocketUrl() // Returns: 'wss://example.com/api/ape'\n *\n * // On http://localhost:3000\n * getSocketUrl() // Returns: 'ws://localhost:3000/api/ape'\n *\n * // On http://localhost (no port)\n * getSocketUrl() // Returns: 'ws://localhost:9010/api/ape'\n *\n * @example\n * // Usage in connection code\n * const ws = new WebSocket(getSocketUrl())\n * ws.onopen = () => console.log('Connected!')\n */\nexport function getSocketUrl() {\n const hostname = window.location.hostname;\n const localServers = [\"localhost\", \"127.0.0.1\", \"[::1]\"];\n const isLocal = localServers.includes(hostname);\n const isHttps = window.location.protocol === \"https:\";\n const port = window.location.port || (isLocal ? 9010 : isHttps ? 443 : 80);\n const protocol = isHttps ? \"wss\" : \"ws\";\n const portSuffix = port !== 80 && port !== 443 ? `:${port}` : \"\";\n return `${protocol}://${hostname}${portSuffix}/api/ape`;\n}\n", "/**\n * @fileoverview Subscription Manager for api-ape Client\n *\n * Manages channel subscriptions for the chained subscription syntax.\n * Tracks local callbacks, sends subscribe/unsubscribe messages to server,\n * and handles reconnection re-subscription.\n *\n * @module client/connection/subscriptions\n *\n * @example\n * // Subscribe to a channel\n * const unsub = subscribe('/news/banking', (data) => {\n * console.log('Received:', data)\n * }, sendFn)\n *\n * // Unsubscribe\n * unsub()\n */\n\n/**\n * Map of channel to Set of callback functions\n * @type {Map<string, Set<Function>>}\n * @private\n */\nconst subscriptions = new Map();\n\n/**\n * Reference to the send function (set during integration)\n * @type {Function|null}\n * @private\n */\nlet _sendFn = null;\n\n/**\n * Set the send function used for subscribe/unsubscribe messages\n *\n * @param {Function} sendFn - Function that sends raw messages to server\n */\nexport function setSendFn(sendFn) {\n _sendFn = sendFn;\n}\n\n/**\n * Subscribe to a channel with a callback\n *\n * @param {string} channel - The channel path (e.g., '/news/banking')\n * @param {Function} callback - Function to call when data is published\n * @returns {Function} Unsubscribe function\n *\n * @example\n * const unsub = subscribe('/news/banking', (data) => {\n * console.log(data.headline)\n * })\n *\n * // Later: unsubscribe\n * unsub()\n */\nexport function subscribe(channel, callback) {\n // Get or create the callback set for this channel\n let callbacks = subscriptions.get(channel);\n const isFirstSubscriber = !callbacks;\n\n if (isFirstSubscriber) {\n callbacks = new Set();\n subscriptions.set(channel, callbacks);\n }\n\n // Add the callback\n callbacks.add(callback);\n\n // If first subscriber, send subscribe message to server\n if (isFirstSubscriber && _sendFn) {\n _sendFn({ subscribe: channel });\n }\n\n // Return unsubscribe function\n return function unsubscribe() {\n const cbs = subscriptions.get(channel);\n if (cbs) {\n cbs.delete(callback);\n\n // If no more callbacks, unsubscribe from server\n if (cbs.size === 0) {\n subscriptions.delete(channel);\n if (_sendFn) {\n _sendFn({ unsubscribe: channel });\n }\n }\n }\n };\n}\n\n/**\n * Check if a channel has any subscribers\n *\n * @param {string} channel - The channel path\n * @returns {boolean} True if channel has subscribers\n */\nexport function hasSubscribers(channel) {\n return subscriptions.has(channel) && subscriptions.get(channel).size > 0;\n}\n\n/**\n * Dispatch incoming data to all subscribers of a channel\n *\n * @param {string} channel - The channel that received data\n * @param {any} data - The data payload from the server\n */\nexport function dispatch(channel, data) {\n const callbacks = subscriptions.get(channel);\n if (callbacks) {\n callbacks.forEach((callback) => {\n try {\n callback(data);\n } catch (err) {\n console.error(`\uD83E\uDD8D Subscription callback error for \"${channel}\":`, err);\n }\n });\n }\n}\n\n/**\n * Re-subscribe to all active channels\n *\n * Called on reconnection to restore all subscriptions.\n */\nexport function resubscribeAll() {\n if (!_sendFn) return;\n\n subscriptions.forEach((callbacks, channel) => {\n if (callbacks.size > 0) {\n _sendFn({ subscribe: channel });\n }\n });\n}\n\n/**\n * Get all active channel names (for debugging/testing)\n *\n * @returns {string[]} Array of subscribed channel names\n */\nexport function getActiveChannels() {\n return Array.from(subscriptions.keys());\n}\n\n/**\n * Clear all subscriptions (for testing)\n */\nexport function clearAll() {\n subscriptions.clear();\n}\n", "/**\n * @fileoverview API Proxy Wrapper for Path-Building Syntax\n *\n * This module provides the Proxy wrapper that enables api-ape's fluent\n * path-building API syntax. It intercepts property access to construct\n * endpoint paths dynamically.\n *\n * ## How It Works\n *\n * The proxy intercepts property access and returns new proxy-wrapped functions.\n * Each property access adds a segment to the path, and calling the function\n * sends the request.\n *\n * ```\n * api.users \u2192 wraps path \"/users\"\n * api.users.create \u2192 wraps path \"/users/create\"\n * api.users({ ... }) \u2192 calls \"/users\" with data\n * api.users('/123') \u2192 calls \"/users/123\"\n * ```\n *\n * ## Reserved Keys\n *\n * Certain properties are reserved and bypass proxy interception:\n * - `on` - Subscribe to broadcasts\n * - `onConnectionChange` - Subscribe to connection state\n * - `transport` - Get current transport type\n *\n * @module client/connection/proxy\n * @see {@link module:client/connectSocket} for usage context\n *\n * @example\n * import { wrap } from './proxy'\n *\n * // Wrap a sender function\n * const api = wrap((path, data) => {\n * console.log(`Calling ${path} with`, data)\n * return fetch(path, { body: JSON.stringify(data) })\n * })\n *\n * // Now use fluent API\n * api.users({ name: 'Alice' }) // Calls \"/users\"\n * api.users.profile({ id: 1 }) // Calls \"/users/profile\"\n * api.chat.messages('/room1', {...}) // Calls \"/chat/messages/room1\"\n */\n\nimport { subscribe } from \"./subscriptions.js\";\n\n/**\n * Path segment separator used when building endpoint paths\n * @constant {string}\n * @private\n */\nconst joinKey = \"/\";\n\n/**\n * Set of property names that should not be intercepted by the proxy\n *\n * These properties are accessed directly on the wrapped function/object\n * rather than being treated as path segments.\n *\n * @constant {Set<string>}\n * @private\n */\nconst reservedKeys = new Set([\"onConnectionChange\", \"transport\"]);\n\n/**\n * Proxy handler object that implements the path-building behavior\n *\n * The `get` trap intercepts property access to either:\n * 1. Return the actual property if it's a reserved key\n * 2. Return a new wrapped function that extends the path\n *\n * @type {ProxyHandler<Function>}\n * @private\n *\n * @example\n * // When you access api.users:\n * // 1. handler.get is called with key=\"users\"\n * // 2. Returns a new function that prepends \"/users\" to the path\n * // 3. That function is also wrapped in a Proxy for chaining\n */\nconst handler = {\n /**\n * Proxy get trap - intercepts property access\n *\n * @param {Function} fn - The wrapped sender function\n * @param {string|symbol} key - The property name being accessed\n * @returns {Function|any} Either the reserved property value or a new wrapped function\n *\n * @description\n * For non-reserved keys, returns a wrapper function that:\n * - Takes 0, 1, or 2 arguments\n * - With 2 args: first is path suffix, second is data\n * - With 1 arg: it's the data (no path suffix)\n * - Prepends the key as a path segment\n * - Returns a Promise from the underlying sender\n *\n * @example\n * // api.users.create({ name: 'Bob' })\n * // \u2192 handler.get(fn, 'users') returns wrappedUsers\n * // \u2192 handler.get(wrappedUsers, 'create') returns wrappedCreate\n * // \u2192 wrappedCreate({ name: 'Bob' }) calls fn('/users/create', { name: 'Bob' })\n */\n get(fn, key) {\n // Skip proxy interception for reserved keys - return actual property\n if (reservedKeys.has(key)) {\n return fn[key];\n }\n\n /**\n * Wrapper function that builds the path and forwards to the sender\n *\n * @param {string|any|Function} a - Either a path suffix (if 2 args), data payload, or subscription callback\n * @param {any} [b] - The data payload (if 2 args)\n * @returns {Promise<any>|Function} Promise resolving to server response, or unsubscribe function for subscriptions\n *\n * @example\n * // Single argument - data only (RPC call)\n * api.users({ name: 'Alice' })\n * // \u2192 path=\"/users\", body={ name: 'Alice' }\n *\n * @example\n * // Single argument - function (subscription)\n * api.news.banking(data => console.log(data))\n * // \u2192 subscribes to \"/news/banking\", returns unsubscribe function\n *\n * @example\n * // Two arguments - path suffix + data\n * api.users('/123', { name: 'Alice' })\n * // \u2192 path=\"/users/123\", body={ name: 'Alice' }\n *\n * @example\n * // Two arguments - nested path + data\n * api.users('/123/profile', { avatar: 'new.png' })\n * // \u2192 path=\"/users/123/profile\", body={ avatar: 'new.png' }\n */\n const wrapperFn = function (a, b) {\n let path = joinKey + key,\n body;\n\n // If single argument is a function, this is a subscription\n if (arguments.length === 1 && typeof a === \"function\") {\n return subscribe(path, a);\n }\n\n if (2 === arguments.length) {\n // Two arguments: first is path suffix, second is body\n path += a;\n body = b;\n } else {\n // One or zero arguments: first arg is the body (or undefined)\n body = a;\n }\n\n return fn(path, body);\n };\n\n // Wrap the new function in another Proxy to allow continued chaining\n return new Proxy(wrapperFn, handler);\n },\n};\n\n/**\n * Wrap an API sender function in a Proxy for path-building syntax\n *\n * This is the main export of the module. It takes a sender function\n * (which accepts path and data) and returns a Proxy that enables\n * the fluent api-ape syntax.\n *\n * @param {Function} api - The sender function to wrap\n * @param {string} api.path - First parameter: the endpoint path\n * @param {any} api.data - Second parameter: the request data/body\n * @returns {Proxy} Proxied API object with path-building capability\n *\n * @example\n * // Basic wrapping\n * const sender = (path, data) => {\n * return fetch(`/api${path}`, {\n * method: 'POST',\n * body: JSON.stringify(data)\n * })\n * }\n *\n * const api = wrap(sender)\n *\n * @example\n * // Using the wrapped API\n *\n * // Simple endpoint call\n * api.ping() // \u2192 sender('/ping', undefined)\n *\n * // With data\n * api.users({ name: 'Alice' }) // \u2192 sender('/users', { name: 'Alice' })\n *\n * // Nested paths\n * api.users.list() // \u2192 sender('/users/list', undefined)\n * api.users.create({ ... }) // \u2192 sender('/users/create', { ... })\n *\n * // With path parameters\n * api.users('/123') // \u2192 sender('/users/123', undefined)\n * api.users('/123', { ... }) // \u2192 sender('/users/123', { ... })\n *\n * // Complex chaining\n * api.chat.rooms('/abc').messages({ text: 'Hi' })\n * // \u2192 sender('/chat/rooms/abc', undefined) \u2014 first call\n * // Note: Each call is independent; chaining creates separate calls\n *\n * @example\n * // With reserved properties preserved\n * const api = wrap(sender)\n *\n * // These bypass the proxy:\n * api.on('event', handler) // Calls sender.on()\n * api.onConnectionChange(handler) // Calls sender.onConnectionChange()\n * console.log(api.transport) // Accesses sender.transport\n *\n * @example\n * // Real-world usage in api-ape client\n * import { wrap } from './proxy'\n * import { createSender } from './sender'\n *\n * const sender = createSender(/* ... *\\/)\n * const client = {\n * sender: wrap(sender),\n * // ... other properties\n * }\n *\n * // User code:\n * client.sender.messages({ text: 'Hello!' })\n */\nexport function wrap(api) {\n return new Proxy(api, handler);\n}\n", "/**\n * @fileoverview Message sending logic for WebSocket transport\n *\n * This module provides functions for sending messages over WebSocket connections\n * with support for:\n * - Request/response correlation via query IDs\n * - Automatic timeout handling\n * - Binary data upload processing\n * - Message queuing when connection is not ready\n *\n * ## Architecture\n *\n * The module provides two main functions:\n * 1. `createWsSend` - Creates a function for sending messages over an active WebSocket\n * 2. `createSender` - Creates a function that queues messages when not connected\n *\n * ## Request Flow\n *\n * ```\n * sender(type, data)\n * \u2502\n * \u251C\u2500\u25BA Connection ready? \u2500\u25BA YES \u2500\u25BA wsSend() \u2500\u25BA WebSocket.send()\n * \u2502 \u2502\n * \u2502 \u2514\u2500\u25BA Wait for response (via queryId)\n * \u2502\n * \u2514\u2500\u25BA NO \u2500\u25BA Queue message \u2500\u25BA Wait for connection \u2500\u25BA Flush queue\n * ```\n *\n * @module client/connection/sender\n * @see {@link module:client/connectSocket} for connection management\n * @see {@link module:client/connection/fileHandling} for binary data processing\n *\n * @example\n * // Create WebSocket sender\n * const wsSend = createWsSend(() => socket, waitingOn)\n *\n * // Send a message and wait for response\n * const response = await wsSend('/chat', { message: 'Hello!' }, Date.now())\n *\n * @example\n * // Create queuing sender for connection management\n * const sender = createSender(\n * () => isReady,\n * () => wsSend,\n * messageQueue,\n * connectFn\n * )\n *\n * // Messages are queued if not connected\n * sender('/users', { name: 'Alice' })\n */\n\nimport messageHash from \"../../utils/messageHash\";\nimport jss from \"../../utils/jss\";\nimport { processBinaryForUpload, uploadBinaryData } from \"./fileHandling\";\n\n/**\n * Total timeout for a request to complete (milliseconds)\n * Includes time waiting for connection + time waiting for response\n * @constant {number}\n */\nconst totalRequestTimeout = 10000;\n\n/**\n * Timeout for initial connection when message is queued (milliseconds)\n * @constant {number}\n */\nconst connectTimeout = 5000;\n\n/**\n * Create a WebSocket send function bound to a socket getter\n *\n * This factory function creates a send function that:\n * 1. Serializes the message payload with JSS (handles Dates, RegExp, etc.)\n * 2. Generates a unique query ID for request/response correlation\n * 3. Extracts and uploads any binary data via HTTP\n * 4. Sends the message over WebSocket\n * 5. Returns a Promise that resolves when the response arrives\n *\n * ## Timeout Behavior\n *\n * The returned promise uses lazy timeout activation - the timeout only\n * starts counting when `.then()` or `.catch()` is called on the promise.\n * This prevents timeouts from triggering for fire-and-forget messages.\n *\n * @param {function(): WebSocket} getSocket - Function that returns the current WebSocket instance\n * @param {Object.<string, function(Error|null, any): void>} waitingOn - Map of query IDs to response callbacks\n * @returns {function(string, any, number, boolean=): Promise<any>} Send function\n *\n * @example\n * const waitingOn = {}\n * const wsSend = createWsSend(() => myWebSocket, waitingOn)\n *\n * // Send message and await response\n * try {\n * const result = await wsSend('/api/users', { name: 'Alice' }, Date.now())\n * console.log('Server responded:', result)\n * } catch (err) {\n * console.error('Request failed:', err)\n * }\n *\n * @example\n * // Fire and forget (no await) - timeout won't trigger\n * wsSend('/api/log', { event: 'pageview' }, Date.now())\n */\nexport function createWsSend(getSocket, waitingOn) {\n /**\n * Send a message over WebSocket and return a Promise for the response\n *\n * @param {string} type - Message type/endpoint path (e.g., '/chat', '/users')\n * @param {any} data - Payload data to send (will be serialized with JSS)\n * @param {number} createdAt - Timestamp when the request was initiated\n * @param {boolean} [directCall] - Reserved for future use (previously controlled requestedAt)\n * @returns {Promise<any>} Promise resolving to the server's response data\n * @throws {Error} If request times out or server returns an error\n */\n return function wsSend(type, data, createdAt, directCall) {\n /** @type {function(Error): void} */\n let rej;\n let promiseIsLive = false;\n const timeLeftForReqToBeMade = createdAt + totalRequestTimeout - Date.now();\n\n // Setup timeout timer\n const timer = setTimeout(() => {\n if (promiseIsLive) {\n rej(new Error(\"Request Timed out for: \" + type));\n }\n }, timeLeftForReqToBeMade);\n\n // Process binary data in the payload\n const { processedData, uploads } = processBinaryForUpload(data);\n\n // Build the message payload\n const payload = {\n type,\n data: processedData,\n createdAt: new Date(createdAt),\n requestedAt: new Date(),\n };\n\n // Serialize and generate query ID for response correlation\n const message = jss.stringify(payload);\n const queryId = messageHash(message);\n\n /**\n * Promise that resolves when server responds to this query\n * @type {Promise<any>}\n */\n const replyPromise = new Promise((resolve, reject) => {\n rej = reject;\n\n // Register callback for when response arrives\n waitingOn[queryId] = (err, result) => {\n clearTimeout(timer);\n // Restore normal promise behavior after response\n replyPromise.then = next.bind(replyPromise);\n if (err) {\n reject(err);\n } else {\n resolve(result);\n }\n };\n\n // Send the message\n getSocket().send(message);\n\n // Upload any binary data via HTTP\n if (uploads.length > 0) {\n uploadBinaryData(queryId, uploads).catch((err) => {\n console.error(\"\uD83E\uDD8D Binary upload failed:\", err);\n });\n }\n });\n\n // Store original then/catch for lazy timeout activation\n const next = replyPromise.then;\n\n /**\n * Wrapped .then() that activates the timeout on first call\n * This implements lazy timeout - timeout only starts when\n * someone actually waits for the response\n */\n replyPromise.then = (worker) => {\n promiseIsLive = true;\n replyPromise.then = next.bind(replyPromise);\n replyPromise.catch = err.bind(replyPromise);\n return next.call(replyPromise, worker);\n };\n\n const err = replyPromise.catch;\n\n /**\n * Wrapped .catch() that activates the timeout on first call\n */\n replyPromise.catch = (worker) => {\n promiseIsLive = true;\n replyPromise.catch = err.bind(replyPromise);\n replyPromise.then = next.bind(replyPromise);\n return err.call(replyPromise, worker);\n };\n\n return replyPromise;\n };\n}\n\n/**\n * Create a sender function that queues messages when not connected\n *\n * This factory creates a sender function that handles the case when\n * the WebSocket connection is not yet ready. Messages are queued and\n * sent once the connection is established.\n *\n * ## Behavior\n *\n * - If connection is ready: Sends immediately via wsSend\n * - If connection is not ready: Queues the message and triggers connection\n * - Queued messages timeout after `connectTimeout` if connection isn't established\n *\n * @param {function(): boolean} isReady - Function returning true if connection is ready\n * @param {function(): function} getSendFn - Function returning the current send function\n * @param {Array<Object>} waitingQueue - Queue array for pending messages\n * @param {function(): void} connectFn - Function to initiate connection\n * @returns {function(string, any): Promise<any>} Sender function\n *\n * @example\n * const messageQueue = []\n * const sender = createSender(\n * () => connectionReady,\n * () => wsSend,\n * messageQueue,\n * connectSocket\n * )\n *\n * // Will queue if not connected, send immediately if connected\n * const result = await sender('/api/data', { key: 'value' })\n *\n * @example\n * // Fire multiple requests - they'll queue and send in order\n * sender('/api/users', { action: 'list' })\n * sender('/api/products', { category: 'electronics' })\n * sender('/api/orders', { status: 'pending' })\n * // All will be sent once connection is ready\n */\nexport function createSender(isReady, getSendFn, waitingQueue, connectFn) {\n /**\n * Send a message to the server\n *\n * @param {string} type - Message type/endpoint path (must be a string)\n * @param {any} data - Payload data to send\n * @returns {Promise<any>} Promise resolving to the server's response\n * @throws {Error} If type is not a string\n * @throws {Error} If connection timeout occurs while message is queued\n */\n return function sender(type, data) {\n if (\"string\" !== typeof type) {\n throw new Error(\"Missing Path value\");\n }\n\n const createdAt = Date.now();\n\n // If ready, send immediately\n if (isReady()) {\n return getSendFn()(type, data, createdAt, true);\n }\n\n // Calculate remaining time before timeout\n const timeLeftForReqToBeMade = createdAt + connectTimeout - Date.now();\n\n /**\n * Payload object stored in the waiting queue\n * @type {Object}\n */\n const payload = {\n type,\n data,\n resolve: undefined,\n reject: undefined,\n waiting: false,\n createdAt,\n timer: null,\n };\n\n // Setup connection timeout\n payload.timer = setTimeout(() => {\n const errMessage = \"Request not sent for: \" + type;\n if (payload.waiting) {\n payload.reject(new Error(errMessage));\n } else {\n throw new Error(errMessage);\n }\n }, timeLeftForReqToBeMade);\n\n /**\n * Promise that resolves when the message is sent and response received\n * @type {Promise<any>}\n */\n const waitingOnOpen = new Promise((res, rej) => {\n payload.resolve = res;\n payload.reject = rej;\n });\n\n // Store original promise methods\n const waitingOnOpenThen = waitingOnOpen.then;\n const waitingOnOpenCatch = waitingOnOpen.catch;\n\n /**\n * Wrapped .then() that marks the payload as being waited on\n * This enables proper timeout handling\n */\n waitingOnOpen.then = (worker) => {\n payload.waiting = true;\n waitingOnOpen.then = waitingOnOpenThen.bind(waitingOnOpen);\n waitingOnOpen.catch = waitingOnOpenCatch.bind(waitingOnOpen);\n return waitingOnOpenThen.call(waitingOnOpen, worker);\n };\n\n /**\n * Wrapped .catch() that marks the payload as being waited on\n */\n waitingOnOpen.catch = (worker) => {\n payload.waiting = true;\n waitingOnOpen.catch = waitingOnOpenCatch.bind(waitingOnOpen);\n waitingOnOpen.then = waitingOnOpenThen.bind(waitingOnOpen);\n return waitingOnOpenCatch.call(waitingOnOpen, worker);\n };\n\n // Add to queue and trigger connection\n waitingQueue.push(payload);\n connectFn();\n\n return waitingOnOpen;\n };\n}\n", "/**\n * @fileoverview Common utilities for file handling in api-ape client\n *\n * This module provides shared utility functions used by both file upload\n * (fileHandling.js) and file download (fileDownload.js) modules.\n *\n * ## Key Functions\n *\n * - **Binary Detection**: `isBinaryData()`, `getBinaryTag()`\n * - **Hash Generation**: `generateUploadHash()`\n * - **Object Traversal**: `setValueAtPath()`, `findTaggedProps()`, `cleanTaggedKeys()`\n *\n * ## Tag System\n *\n * api-ape uses special key suffixes to mark binary data references:\n * - `<!L>` - Server-linked binary (download from server)\n * - `<!F>` - File share (client-to-client transfer)\n * - `<!A>` - ArrayBuffer upload\n * - `<!B>` - Blob upload\n *\n * @module client/connection/fileUtils\n * @see {@link module:client/connection/fileHandling} for upload processing\n * @see {@link module:client/connection/fileDownload} for download processing\n *\n * @example\n * import {\n * isBinaryData,\n * findTaggedProps,\n * setValueAtPath\n * } from './fileUtils'\n *\n * // Check for binary data\n * const data = new ArrayBuffer(100)\n * console.log(isBinaryData(data)) // true\n *\n * // Find tagged properties in response\n * const response = { 'image<!L>': 'abc123', name: 'photo.jpg' }\n * const tags = findTaggedProps(response, 'L')\n * // [{ path: 'image', hash: 'abc123', originalKey: 'image<!L>' }]\n */\n\n/**\n * Check if a value is binary data\n *\n * Detects ArrayBuffer, TypedArray views (Uint8Array, Int32Array, etc.),\n * and Blob objects. Used to determine if a value needs special handling\n * during serialization.\n *\n * @param {any} value - The value to check\n * @returns {boolean} True if the value is binary data, false otherwise\n *\n * @example\n * // ArrayBuffer\n * isBinaryData(new ArrayBuffer(10)) // true\n *\n * // TypedArray views\n * isBinaryData(new Uint8Array(10)) // true\n * isBinaryData(new Float32Array(10)) // true\n *\n * // Blob (browser only)\n * isBinaryData(new Blob(['hello'])) // true\n *\n * // Non-binary\n * isBinaryData('string') // false\n * isBinaryData({ key: 'value' }) // false\n * isBinaryData(null) // false\n * isBinaryData(undefined) // false\n */\nexport function isBinaryData(value) {\n if (value === null || value === undefined) return false;\n return (\n value instanceof ArrayBuffer ||\n ArrayBuffer.isView(value) ||\n (typeof Blob !== \"undefined\" && value instanceof Blob)\n );\n}\n\n/**\n * Get the binary type tag for a value\n *\n * Returns a single character tag indicating the binary data type:\n * - `'A'` for ArrayBuffer and TypedArray views\n * - `'B'` for Blob objects\n *\n * This tag is used in the wire protocol to indicate how to\n * reconstruct the binary data on the receiving end.\n *\n * @param {ArrayBuffer|ArrayBufferView|Blob} value - The binary value to tag\n * @returns {'A'|'B'} The type tag character\n *\n * @example\n * getBinaryTag(new ArrayBuffer(10)) // 'A'\n * getBinaryTag(new Uint8Array(10)) // 'A'\n * getBinaryTag(new Blob(['hello'])) // 'B'\n *\n * @example\n * // Used when building upload metadata\n * const tag = getBinaryTag(fileData)\n * const key = `attachment<!${tag}>` // 'attachment<!A>' or 'attachment<!B>'\n */\nexport function getBinaryTag(value) {\n if (typeof Blob !== \"undefined\" && value instanceof Blob) return \"B\";\n return \"A\";\n}\n\n/**\n * Generate a simple hash for binary upload path identification\n *\n * Creates a short hash string from a path string, used to uniquely\n * identify binary data uploads. Uses a simple string hashing algorithm\n * with base-36 encoding for compact representation.\n *\n * Note: This is not cryptographically secure - it's for identification only.\n *\n * @param {string} path - The property path to hash (e.g., 'user.avatar')\n * @returns {string} Base-36 encoded hash string\n *\n * @example\n * generateUploadHash('image') // e.g., 'k3m9x'\n * generateUploadHash('user.avatar') // e.g., 'p7n2w'\n * generateUploadHash('files.0') // e.g., 'q1r8t'\n *\n * @example\n * // Used in upload processing\n * const hash = generateUploadHash('documents.contract')\n * const uploadUrl = `/api/ape/data/${queryId}/${hash}`\n */\nexport function generateUploadHash(path) {\n let hash = 0;\n for (let i = 0; i < path.length; i++) {\n const char = path.charCodeAt(i);\n hash = (hash << 5) - hash + char;\n hash = hash & hash; // Convert to 32-bit integer\n }\n return Math.abs(hash).toString(36);\n}\n\n/**\n * Set a value at a nested dot-notation path in an object\n *\n * Traverses an object following a dot-separated path and sets\n * the value at the final key. The path must be valid (all intermediate\n * objects must exist).\n *\n * @param {Object} obj - The object to modify\n * @param {string} path - Dot-notation path (e.g., 'user.profile.avatar')\n * @param {any} value - The value to set at the path\n * @returns {void}\n * @throws {TypeError} If intermediate path segments don't exist\n *\n * @example\n * const data = { user: { profile: { name: 'Alice' } } }\n *\n * setValueAtPath(data, 'user.profile.avatar', new ArrayBuffer(100))\n * // data.user.profile.avatar is now the ArrayBuffer\n *\n * @example\n * // Simple path\n * const obj = { image: 'placeholder' }\n * setValueAtPath(obj, 'image', binaryData)\n *\n * @example\n * // Array index path\n * const obj = { files: [null, null] }\n * setValueAtPath(obj, 'files.0', fileBuffer)\n * setValueAtPath(obj, 'files.1', anotherBuffer)\n */\nexport function setValueAtPath(obj, path, value) {\n const parts = path.split(\".\");\n let current = obj;\n\n // Navigate to parent of target property\n for (let i = 0; i < parts.length - 1; i++) {\n current = current[parts[i]];\n }\n\n // Set the value at the final key\n current[parts[parts.length - 1]] = value;\n}\n\n/**\n * Find properties with a specific tag suffix in a nested object\n *\n * Recursively searches through an object (including arrays) for keys\n * ending with a tag suffix like `<!L>`, `<!F>`, `<!A>`, or `<!B>`.\n * Returns information about each tagged property found.\n *\n * ## Tag Types\n * - `L` - Linked binary resource (server sends hash, client downloads)\n * - `F` - File share (client-to-client transfer)\n * - `A` - ArrayBuffer upload marker\n * - `B` - Blob upload marker\n *\n * @param {Object|Array|any} obj - The object to search\n * @param {string} tag - The tag to look for (without <! and >)\n * @param {string} [path=''] - Current path (used internally for recursion)\n * @returns {Array<{path: string, hash: string, originalKey: string}>} Array of found tagged properties\n *\n * @example\n * // Find server-linked binary references\n * const response = {\n * name: 'Report',\n * 'pdf<!L>': 'hash123',\n * 'thumbnail<!L>': 'hash456'\n * }\n *\n * const links = findTaggedProps(response, 'L')\n * // Returns:\n * // [\n * // { path: 'pdf', hash: 'hash123', originalKey: 'pdf<!L>' },\n * // { path: 'thumbnail', hash: 'hash456', originalKey: 'thumbnail<!L>' }\n * // ]\n *\n * @example\n * // Find nested tagged properties\n * const data = {\n * user: {\n * 'avatar<!L>': 'avatarHash',\n * documents: [\n * { 'file<!L>': 'doc1Hash' },\n * { 'file<!L>': 'doc2Hash' }\n * ]\n * }\n * }\n *\n * const links = findTaggedProps(data, 'L')\n * // Returns paths: 'user.avatar', 'user.documents.0.file', 'user.documents.1.file'\n *\n * @example\n * // Find file share tags\n * const message = { 'attachment<!F>': 'shareHash123' }\n * const shares = findTaggedProps(message, 'F')\n */\nexport function findTaggedProps(obj, tag, path = \"\") {\n const results = [];\n\n // Base case: null, undefined, or non-object\n if (obj === null || obj === undefined || typeof obj !== \"object\") {\n return results;\n }\n\n // Handle arrays - recurse into each element\n if (Array.isArray(obj)) {\n for (let i = 0; i < obj.length; i++) {\n results.push(\n ...findTaggedProps(obj[i], tag, path ? `${path}.${i}` : String(i)),\n );\n }\n return results;\n }\n\n // Handle objects - check each key for tag suffix\n const suffix = `<!${tag}>`;\n\n for (const key of Object.keys(obj)) {\n if (key.endsWith(suffix)) {\n // Found a tagged key - extract the clean name and hash\n const cleanKey = key.slice(0, -4); // Remove <!X> suffix (4 chars)\n results.push({\n path: path ? `${path}.${cleanKey}` : cleanKey,\n hash: obj[key],\n originalKey: key,\n });\n } else {\n // Not tagged - recurse into value\n results.push(\n ...findTaggedProps(obj[key], tag, path ? `${path}.${key}` : key),\n );\n }\n }\n\n return results;\n}\n\n/**\n * Clean tagged keys from an object (rename `key<!X>` to `key`)\n *\n * Creates a new object with tag suffixes removed from keys.\n * Recursively processes nested objects and arrays.\n *\n * This is used after finding tagged properties to create a clean\n * object structure where the tags have been replaced with actual values.\n *\n * @param {Object|Array|any} obj - The object to clean\n * @param {string} tag - The tag to remove (without <! and >)\n * @returns {Object|Array|any} New object with cleaned keys\n *\n * @example\n * // Clean L-tagged keys\n * const response = {\n * name: 'Photo',\n * 'image<!L>': 'hash123',\n * size: 1024\n * }\n *\n * const cleaned = cleanTaggedKeys(response, 'L')\n * // Returns: { name: 'Photo', image: 'hash123', size: 1024 }\n *\n * @example\n * // Nested cleaning\n * const data = {\n * user: {\n * 'avatar<!L>': 'avatarHash'\n * }\n * }\n *\n * const cleaned = cleanTaggedKeys(data, 'L')\n * // Returns: { user: { avatar: 'avatarHash' } }\n *\n * @example\n * // Array handling\n * const files = [\n * { 'data<!L>': 'hash1' },\n * { 'data<!L>': 'hash2' }\n * ]\n *\n * const cleaned = cleanTaggedKeys(files, 'L')\n * // Returns: [{ data: 'hash1' }, { data: 'hash2' }]\n */\nexport function cleanTaggedKeys(obj, tag) {\n // Base case: null, undefined, or non-object - return as-is\n if (obj === null || obj === undefined || typeof obj !== \"object\") {\n return obj;\n }\n\n // Handle arrays - map each element\n if (Array.isArray(obj)) {\n return obj.map((item) => cleanTaggedKeys(item, tag));\n }\n\n // Handle objects - process each key\n const cleaned = {};\n const suffix = `<!${tag}>`;\n\n for (const key of Object.keys(obj)) {\n if (key.endsWith(suffix)) {\n // Remove the tag suffix from the key\n cleaned[key.slice(0, -4)] = obj[key];\n } else {\n // Keep key as-is, but recursively clean the value\n cleaned[key] = cleanTaggedKeys(obj[key], tag);\n }\n }\n\n return cleaned;\n}\n", "/**\n * @fileoverview Binary File Upload Utilities for api-ape Client\n *\n * This module provides utilities for processing and uploading binary data\n * (ArrayBuffer, TypedArray, Blob) in api-ape messages. Binary data is extracted\n * from payloads and uploaded via HTTP, with the original payload modified to\n * contain reference hashes.\n *\n * ## Binary Data Flow (Upload)\n *\n * ```\n * Original Payload Processed Payload\n * \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n * { {\n * name: 'doc.pdf', name: 'doc.pdf',\n * file: ArrayBuffer(...) \u2192 'file<!A>': 'abc123'\n * } }\n * \u2502\n * \u25BC\n * HTTP PUT /api/ape/data/{queryId}/abc123\n * \u2502\n * \u25BC\n * Server receives binary + reference\n * ```\n *\n * ## Tag System\n *\n * Binary references use a tag system in property keys:\n * - `<!A>` - ArrayBuffer or TypedArray upload\n * - `<!B>` - Blob upload\n * - `<!F>` - File sharing (client-to-client)\n *\n * ## Two Upload Modes\n *\n * 1. **Standard Upload** (`processBinaryForUpload`) - For sending binary data\n * from client to server as part of a request\n *\n * 2. **File Sharing** (`processBinaryForSharing`) - For client-to-client\n * binary transfers where data is stored temporarily on server\n *\n * @module client/connection/fileHandling\n * @see {@link module:client/connection/fileDownload} for downloading binary data\n * @see {@link module:client/connection/fileUtils} for shared utilities\n *\n * @example\n * // Upload binary data with a message\n * import { processBinaryForUpload, uploadBinaryData } from './fileHandling'\n *\n * const payload = {\n * name: 'photo.jpg',\n * image: myArrayBuffer\n * }\n *\n * const { processedData, uploads } = processBinaryForUpload(payload)\n * // processedData = { name: 'photo.jpg', 'image<!A>': 'hashXYZ' }\n *\n * // Upload binary data separately\n * await uploadBinaryData(queryId, uploads)\n *\n * @example\n * // Share files between clients\n * import { processBinaryForSharing, uploadSharedFiles } from './fileHandling'\n *\n * const { processedData, shares } = processBinaryForSharing({\n * screenshot: screenshotBlob\n * })\n *\n * await uploadSharedFiles(shares)\n * // Other clients can now fetch the shared file\n */\n\nimport { getBaseUrl } from \"./network\";\nimport { isBinaryData, getBinaryTag, generateUploadHash } from \"./fileUtils\";\n\n/**\n * Process a data payload and extract binary data for HTTP upload\n *\n * Recursively traverses the data object, finding any binary data\n * (ArrayBuffer, TypedArray, Blob) and replacing it with a tagged\n * reference hash. The binary data is collected for separate HTTP upload.\n *\n * ## Processing Logic\n *\n * - Primitive values: Passed through unchanged\n * - Binary data: Replaced with `{ __ape_upload__: hash }` and added to uploads\n * - Arrays: Each element processed recursively\n * - Objects: Each property processed recursively, binary keys get tagged\n *\n * @param {any} data - The payload data to process\n * @param {string} [path=''] - Current path in the object tree (for hash generation)\n * @returns {{processedData: any, uploads: Array<{path: string, hash: string, data: any, tag: string}>}}\n * Object containing the processed data and array of binary uploads\n *\n * @example\n * // Simple binary property\n * const { processedData, uploads } = processBinaryForUpload({\n * name: 'file.bin',\n * content: new ArrayBuffer(1024)\n * })\n *\n * console.log(processedData)\n * // { name: 'file.bin', 'content<!A>': '7xyz3' }\n *\n * console.log(uploads)\n * // [{ path: 'content', hash: '7xyz3', data: ArrayBuffer, tag: 'A' }]\n *\n * @example\n * // Nested binary data\n * const { processedData, uploads } = processBinaryForUpload({\n * files: [\n * { name: 'a.png', data: buffer1 },\n * { name: 'b.png', data: buffer2 }\n * ]\n * })\n *\n * console.log(processedData)\n * // {\n * // files: [\n * // { name: 'a.png', 'data<!A>': 'hash1' },\n * // { name: 'b.png', 'data<!A>': 'hash2' }\n * // ]\n * // }\n *\n * @example\n * // No binary data - passthrough\n * const { processedData, uploads } = processBinaryForUpload({\n * message: 'Hello',\n * count: 42\n * })\n *\n * console.log(processedData)\n * // { message: 'Hello', count: 42 }\n *\n * console.log(uploads)\n * // []\n */\nexport function processBinaryForUpload(data, path = \"\") {\n // Handle null/undefined\n if (data === null || data === undefined) {\n return { processedData: data, uploads: [] };\n }\n\n // Handle binary data - extract and replace with hash reference\n if (isBinaryData(data)) {\n const tag = getBinaryTag(data);\n const hash = generateUploadHash(path || \"root\");\n return {\n processedData: { [`__ape_upload__`]: hash },\n uploads: [{ path, hash, data, tag }],\n };\n }\n\n // Handle arrays - process each element recursively\n if (Array.isArray(data)) {\n const processedArray = [];\n const allUploads = [];\n\n for (let i = 0; i < data.length; i++) {\n const itemPath = path ? `${path}.${i}` : String(i);\n const { processedData, uploads } = processBinaryForUpload(\n data[i],\n itemPath,\n );\n processedArray.push(processedData);\n allUploads.push(...uploads);\n }\n\n return { processedData: processedArray, uploads: allUploads };\n }\n\n // Handle objects - process each property recursively\n if (typeof data === \"object\") {\n const processedObj = {};\n const allUploads = [];\n\n for (const key of Object.keys(data)) {\n const itemPath = path ? `${path}.${key}` : key;\n const { processedData, uploads } = processBinaryForUpload(\n data[key],\n itemPath,\n );\n\n // If this property contained binary data, add tag to the key\n if (uploads.length > 0 && processedData?.__ape_upload__) {\n const tag = uploads[uploads.length - 1].tag;\n processedObj[`${key}<!${tag}>`] = processedData.__ape_upload__;\n } else {\n processedObj[key] = processedData;\n }\n\n allUploads.push(...uploads);\n }\n\n return { processedData: processedObj, uploads: allUploads };\n }\n\n // Primitive values - return as-is\n return { processedData: data, uploads: [] };\n}\n\n/**\n * Process a data payload and extract binary data for client-to-client sharing\n *\n * Similar to `processBinaryForUpload`, but uses the `<!F>` tag for file sharing.\n * Shared files are uploaded to a temporary storage endpoint and can be fetched\n * by other clients using the hash reference.\n *\n * ## Difference from Standard Upload\n *\n * - Standard uploads are tied to a specific request/query\n * - Shared files are stored with a content-addressable hash\n * - Shared files can be fetched by any client that knows the hash\n *\n * @param {any} data - The payload data to process\n * @param {string} [path=''] - Current path in the object tree (for hash generation)\n * @returns {{processedData: any, shares: Array<{path: string, hash: string, data: any}>}}\n * Object containing the processed data and array of files to share\n *\n * @example\n * // Share a screenshot with other clients\n * const { processedData, shares } = processBinaryForSharing({\n * type: 'screenshot',\n * image: screenshotArrayBuffer,\n * timestamp: Date.now()\n * })\n *\n * console.log(processedData)\n * // { type: 'screenshot', 'image<!F>': 'shareHash123', timestamp: 1699999999999 }\n *\n * // Upload the shared file\n * await uploadSharedFiles(shares)\n *\n * // Now broadcast to other clients who can fetch the image\n * broadcast('screenshot', processedData)\n *\n * @example\n * // Multiple shared files\n * const { processedData, shares } = processBinaryForSharing({\n * attachments: [\n * { name: 'doc1.pdf', content: pdfBuffer1 },\n * { name: 'doc2.pdf', content: pdfBuffer2 }\n * ]\n * })\n *\n * await uploadSharedFiles(shares)\n */\nexport function processBinaryForSharing(data, path = \"\") {\n // Handle null/undefined\n if (data === null || data === undefined) {\n return { processedData: data, shares: [] };\n }\n\n // Handle binary data - extract and replace with share hash\n if (isBinaryData(data)) {\n const hash = generateUploadHash(path || \"share\");\n return {\n processedData: { [`__ape_share__`]: hash },\n shares: [{ path, hash, data }],\n };\n }\n\n // Handle arrays - process each element recursively\n if (Array.isArray(data)) {\n const processedArray = [];\n const allShares = [];\n\n for (let i = 0; i < data.length; i++) {\n const itemPath = path ? `${path}.${i}` : String(i);\n const { processedData, shares } = processBinaryForSharing(\n data[i],\n itemPath,\n );\n processedArray.push(processedData);\n allShares.push(...shares);\n }\n\n return { processedData: processedArray, shares: allShares };\n }\n\n // Handle objects - process each property recursively\n if (typeof data === \"object\") {\n const processedObj = {};\n const allShares = [];\n\n for (const key of Object.keys(data)) {\n const itemPath = path ? `${path}.${key}` : key;\n const { processedData, shares } = processBinaryForSharing(\n data[key],\n itemPath,\n );\n\n // If this property contained binary data, add F tag to the key\n if (shares.length > 0 && processedData?.__ape_share__) {\n processedObj[`${key}<!F>`] = processedData.__ape_share__;\n } else {\n processedObj[key] = processedData;\n }\n\n allShares.push(...shares);\n }\n\n return { processedData: processedObj, shares: allShares };\n }\n\n // Primitive values - return as-is\n return { processedData: data, shares: [] };\n}\n\n/**\n * Upload binary data via HTTP PUT requests\n *\n * Takes the uploads array from `processBinaryForUpload` and sends each\n * binary payload to the server via HTTP PUT. The uploads are performed\n * in parallel for efficiency.\n *\n * ## Upload Endpoint\n *\n * Binary data is uploaded to: `PUT /api/ape/data/{queryId}/{hash}`\n *\n * The server matches the upload to the original WebSocket message using\n * the queryId, and associates the binary data with the correct property\n * using the hash.\n *\n * @param {string} queryId - The query ID of the associated WebSocket message\n * @param {Array<{hash: string, data: ArrayBuffer|Blob|TypedArray}>} uploads - Array of upload objects\n * @returns {Promise<void>} Resolves when all uploads complete\n * @throws {Error} If any upload fails\n *\n * @example\n * // Standard usage with processBinaryForUpload\n * const { processedData, uploads } = processBinaryForUpload(payload)\n *\n * if (uploads.length > 0) {\n * await uploadBinaryData(queryId, uploads)\n * }\n *\n * // Send the processed message via WebSocket\n * ws.send(JSON.stringify({ queryId, data: processedData }))\n *\n * @example\n * // Error handling\n * try {\n * await uploadBinaryData(queryId, uploads)\n * } catch (err) {\n * console.error('Binary upload failed:', err)\n * // Handle failure - maybe retry or notify user\n * }\n */\nexport async function uploadBinaryData(queryId, uploads) {\n if (uploads.length === 0) return;\n\n const baseUrl = getBaseUrl();\n\n await Promise.all(\n uploads.map(async ({ hash, data }) => {\n const response = await fetch(\n `${baseUrl}/api/ape/data/${queryId}/${hash}`,\n {\n method: \"PUT\",\n credentials: \"include\",\n headers: { \"Content-Type\": \"application/octet-stream\" },\n body: data,\n },\n );\n\n if (!response.ok) {\n throw new Error(`Upload failed: ${response.status}`);\n }\n }),\n );\n}\n\n/**\n * Upload shared files via HTTP PUT for client-to-client transfer\n *\n * Takes the shares array from `processBinaryForSharing` and uploads each\n * file to the server's shared file storage. Other clients can then fetch\n * these files using the hash reference.\n *\n * ## Share Endpoint\n *\n * Shared files are uploaded to: `PUT /api/ape/data/_share/{hash}`\n *\n * The `_share` path indicates this is for client-to-client sharing rather\n * than a request-specific upload.\n *\n * ## Temporary Storage\n *\n * Shared files are stored temporarily on the server and will be cleaned up\n * after a configurable timeout (default: 60 seconds for start, 60 seconds\n * after first access).\n *\n * @param {Array<{hash: string, data: ArrayBuffer|Blob|TypedArray}>} shares - Array of share objects\n * @returns {Promise<void>} Resolves when all uploads complete\n * @throws {Error} If any upload fails\n *\n * @example\n * // Share files with other clients\n * const { processedData, shares } = processBinaryForSharing({\n * image: imageBuffer\n * })\n *\n * await uploadSharedFiles(shares)\n *\n * // Broadcast to other clients\n * broadcast('shared-image', processedData)\n * // Other clients will receive: { 'image<!F>': 'hashXYZ' }\n * // They can fetch it via: GET /api/ape/data/hashXYZ\n *\n * @example\n * // Batch upload multiple files\n * const shares = [\n * { hash: 'hash1', data: buffer1 },\n * { hash: 'hash2', data: buffer2 },\n * { hash: 'hash3', data: buffer3 }\n * ]\n *\n * // All uploads happen in parallel\n * await uploadSharedFiles(shares)\n */\nexport async function uploadSharedFiles(shares) {\n if (shares.length === 0) return;\n\n const baseUrl = getBaseUrl();\n\n await Promise.all(\n shares.map(async ({ hash, data }) => {\n const response = await fetch(`${baseUrl}/api/ape/data/_share/${hash}`, {\n method: \"PUT\",\n credentials: \"include\",\n headers: { \"Content-Type\": \"application/octet-stream\" },\n body: data,\n });\n\n if (!response.ok) {\n throw new Error(`Shared upload failed: ${response.status}`);\n }\n }),\n );\n}\n\n/**\n * Re-export setValueAtPath for fileDownload module\n *\n * This utility is used by the download module to place fetched binary\n * data back into the correct location in the data object.\n *\n * @see {@link module:client/connection/fileUtils.setValueAtPath}\n */\nexport { setValueAtPath } from \"./fileUtils\";\n", "/**\n * @fileoverview Binary file download/fetch utilities for api-ape client\n *\n * This module handles fetching binary data that is referenced in server responses.\n * When the server sends binary data (like images, files, etc.), it doesn't include\n * the raw bytes in the WebSocket message. Instead, it sends a tagged reference\n * that this module resolves by fetching the actual data via HTTP.\n *\n * ## Tag Types\n *\n * - `<!L>` - **Linked Resource**: Binary data from server responses (server \u2192 client)\n * - `<!F>` - **Shared File**: Binary data from other clients (client \u2192 client via server)\n *\n * ## Data Flow\n *\n * ```\n * Server Response:\n * { \"image<!L>\": \"abc123\", \"name\": \"photo.jpg\" }\n *\n * After fetchLinkedResources():\n * { \"image\": ArrayBuffer(...), \"name\": \"photo.jpg\" }\n * ```\n *\n * ## Retry Logic\n *\n * Shared files (F-tagged) use exponential backoff retry because the file\n * might not be immediately available when the message arrives (the sender\n * might still be uploading).\n *\n * @module client/connection/fileDownload\n * @see {@link module:client/connection/fileHandling} for upload utilities\n * @see {@link module:client/connection/fileUtils} for shared utility functions\n *\n * @example\n * // Hydrating a server response\n * import { fetchLinkedResources, fetchSharedFiles } from './fileDownload'\n *\n * const serverData = { \"avatar<!L>\": \"hash123\", username: \"alice\" }\n *\n * // Fetch the binary data\n * const hydrated = await fetchLinkedResources(serverData)\n * // Result: { avatar: ArrayBuffer(...), username: \"alice\" }\n *\n * @example\n * // Handling shared files from other clients\n * const messageData = { \"attachment<!F>\": \"filehash\", text: \"Check this out!\" }\n *\n * // Fetch with retry logic\n * const hydrated = await fetchSharedFiles(messageData)\n * // Result: { attachment: ArrayBuffer(...), text: \"Check this out!\" }\n */\n\nimport { getBaseUrl } from \"./network\";\nimport { setValueAtPath, findTaggedProps, cleanTaggedKeys } from \"./fileUtils\";\n\n/**\n * Fetch binary resources linked from server responses\n *\n * This function processes data objects that contain L-tagged binary references.\n * Each L-tagged property is replaced with the actual binary data fetched from\n * the server's data endpoint.\n *\n * ## Processing Steps\n *\n * 1. Scan the data object for properties ending with `<!L>`\n * 2. For each tagged property, extract the hash value\n * 3. Fetch the binary data from `/api/ape/data/{hash}`\n * 4. Replace the hash with the fetched ArrayBuffer\n * 5. Rename the key to remove the `<!L>` suffix\n *\n * ## Error Handling\n *\n * If a fetch fails, the property is set to `null` rather than throwing.\n * This prevents one failed resource from breaking the entire response.\n *\n * @param {Object} data - Data object potentially containing L-tagged binary references\n * @param {string} [clientId] - Optional client ID for authentication header\n * @returns {Promise<Object>} Hydrated data object with binary resources fetched\n *\n * @example\n * // Server sends avatar as a linked resource\n * const serverResponse = {\n * \"profilePic<!L>\": \"abc123def456\",\n * \"username\": \"alice\",\n * \"bio\": \"Hello world!\"\n * }\n *\n * const hydrated = await fetchLinkedResources(serverResponse)\n * // Result:\n * // {\n * // profilePic: ArrayBuffer(12345), // The actual image data\n * // username: \"alice\",\n * // bio: \"Hello world!\"\n * // }\n *\n * @example\n * // Multiple binary resources\n * const response = {\n * \"thumbnail<!L>\": \"hash1\",\n * \"fullImage<!L>\": \"hash2\",\n * \"metadata\": { width: 1920, height: 1080 }\n * }\n *\n * const hydrated = await fetchLinkedResources(response)\n * // Both thumbnail and fullImage are fetched in parallel\n *\n * @example\n * // Nested binary resources\n * const response = {\n * user: {\n * name: \"Bob\",\n * \"avatar<!L>\": \"avatarhash\"\n * },\n * attachments: [\n * { \"file<!L>\": \"file1hash\", name: \"doc.pdf\" },\n * { \"file<!L>\": \"file2hash\", name: \"image.png\" }\n * ]\n * }\n *\n * const hydrated = await fetchLinkedResources(response)\n * // All nested binary resources are fetched\n */\nexport async function fetchLinkedResources(data, clientId) {\n const resources = findTaggedProps(data, \"L\");\n if (resources.length === 0) return data;\n\n console.log(`\uD83E\uDD8D Fetching ${resources.length} binary resource(s)`);\n const cleanedData = cleanTaggedKeys(data, \"L\");\n const baseUrl = getBaseUrl();\n\n await Promise.all(\n resources.map(async ({ path, hash }) => {\n try {\n const response = await fetch(`${baseUrl}/api/ape/data/${hash}`, {\n credentials: \"include\",\n headers: { \"X-Ape-Client-Id\": clientId || \"\" },\n });\n if (!response.ok) throw new Error(`Failed: ${response.status}`);\n const arrayBuffer = await response.arrayBuffer();\n setValueAtPath(cleanedData, path, arrayBuffer);\n } catch (err) {\n console.error(`\uD83E\uDD8D Failed to fetch binary resource at ${path}:`, err);\n setValueAtPath(cleanedData, path, null);\n }\n }),\n );\n\n return cleanedData;\n}\n\n/**\n * Fetch shared files from client-to-client transfers\n *\n * This function handles F-tagged file references, which represent files\n * shared between clients via the server. Unlike L-tagged resources, F-tagged\n * files use retry logic because the file might not be immediately available\n * (the sending client might still be uploading).\n *\n * ## Retry Behavior\n *\n * - Uses exponential backoff starting at 100ms\n * - Doubles delay after each retry (100ms \u2192 200ms \u2192 400ms \u2192 ...)\n * - Retries up to `maxRetries` times (default: 5)\n * - Only retries on 404 errors (file not yet uploaded)\n *\n * ## Use Case\n *\n * Client-to-client file sharing workflow:\n * 1. Client A sends message with file reference\n * 2. Server broadcasts message to Client B\n * 3. Client A uploads file to server (may take time)\n * 4. Client B receives message, attempts to fetch file\n * 5. If 404, retry until file is available\n *\n * @param {Object} data - Data object potentially containing F-tagged file references\n * @param {number} [maxRetries=5] - Maximum number of retry attempts per file\n * @returns {Promise<Object>} Hydrated data object with shared files fetched\n *\n * @example\n * // Receiving a shared file from another client\n * const message = {\n * \"sharedDoc<!F>\": \"uniquefilehash\",\n * \"sender\": \"alice\",\n * \"text\": \"Here's the document you requested\"\n * }\n *\n * const hydrated = await fetchSharedFiles(message)\n * // Result:\n * // {\n * // sharedDoc: ArrayBuffer(...), // The shared file\n * // sender: \"alice\",\n * // text: \"Here's the document you requested\"\n * // }\n *\n * @example\n * // With custom retry count\n * const hydrated = await fetchSharedFiles(data, 10) // Up to 10 retries\n *\n * @example\n * // Multiple shared files\n * const data = {\n * \"photo<!F>\": \"hash1\",\n * \"video<!F>\": \"hash2\",\n * caption: \"My vacation pics!\"\n * }\n *\n * // Both files fetched in parallel with independent retry logic\n * const hydrated = await fetchSharedFiles(data)\n */\nexport async function fetchSharedFiles(data, maxRetries = 5) {\n const files = findTaggedProps(data, \"F\");\n if (files.length === 0) return data;\n\n console.log(`\uD83E\uDD8D Fetching ${files.length} shared file(s)`);\n const cleanedData = cleanTaggedKeys(data, \"F\");\n const baseUrl = getBaseUrl();\n\n await Promise.all(\n files.map(async ({ path, hash }) => {\n let retries = 0;\n let backoff = 100;\n\n while (retries < maxRetries) {\n try {\n const response = await fetch(`${baseUrl}/api/ape/data/${hash}`, {\n credentials: \"include\",\n });\n\n if (!response.ok) {\n // Retry on 404 (file not yet uploaded)\n if (response.status === 404 && retries < maxRetries - 1) {\n retries++;\n await new Promise((r) => setTimeout(r, backoff));\n backoff *= 2;\n continue;\n }\n throw new Error(`Failed to fetch shared file: ${response.status}`);\n }\n\n setValueAtPath(cleanedData, path, await response.arrayBuffer());\n break;\n } catch (err) {\n if (retries >= maxRetries - 1) {\n console.error(`\uD83E\uDD8D Failed to fetch shared file at ${path}:`, err);\n setValueAtPath(cleanedData, path, null);\n }\n retries++;\n await new Promise((r) => setTimeout(r, backoff));\n backoff *= 2;\n }\n }\n }),\n );\n\n return cleanedData;\n}\n", "/**\n * @fileoverview Message handling utilities for api-ape client\n *\n * Handles incoming message processing, including binary data hydration\n * and dispatching to registered handlers and subscription callbacks.\n *\n * @module client/connection/messageHandler\n */\n\nimport {\n fetchLinkedResources,\n fetchSharedFiles,\n} from \"./fileDownload\";\nimport {\n hasSubscribers,\n dispatch as dispatchToSubscribers,\n} from \"./subscriptions\";\n\n/**\n * Array of universal message receivers (called for all message types)\n * @type {Array<function({err: any, type: string, data: any}): void>}\n * @private\n */\nconst receiverArray = [];\n\n/**\n * Map of type-specific message receivers\n * @type {Object.<string, Array<function({err: any, type: string, data: any}): void>>}\n * @private\n */\nconst ofTypesOb = {};\n\n/**\n * Process incoming message data to hydrate binary resources\n *\n * This function handles two types of binary data references:\n * - L-tagged: Binary data linked from server responses (fetchLinkedResources)\n * - F-tagged: Shared files from other clients (fetchSharedFiles)\n *\n * @param {any} data - Raw data from server message\n * @param {Error|null} err - Error from the message, if any\n * @returns {Promise<any>} Hydrated data with binary resources fetched\n *\n * @example\n * // Server sends: { image<!L>: 'abc123' }\n * // After hydration: { image: ArrayBuffer }\n */\nexport async function processIncomingData(data, err) {\n if (!data || err) return data;\n try {\n let result = await fetchLinkedResources(data);\n return await fetchSharedFiles(result);\n } catch (e) {\n console.error(`\uD83E\uDD8D Failed to hydrate data:`, e);\n return data;\n }\n}\n\n/**\n * Dispatch a received message to all registered handlers\n *\n * Messages are delivered to:\n * 1. Chained subscription callbacks (new v2 syntax)\n * 2. Type-specific handlers registered via setOnReceiver(type, handler)\n * 3. Universal handlers registered via setOnReceiver(handler)\n *\n * @param {string} type - Message type identifier\n * @param {Error|null} err - Error payload, if any\n * @param {any} data - Message data payload\n */\nexport function dispatchMessage(type, err, data) {\n // Dispatch to chained subscription callbacks (v2 syntax)\n if (hasSubscribers(type)) {\n dispatchToSubscribers(type, data);\n }\n\n // Legacy handlers\n if (ofTypesOb[type]) ofTypesOb[type].forEach((w) => w({ err, type, data }));\n receiverArray.forEach((w) => w({ err, type, data }));\n}\n\n/**\n * Register a message receiver/handler\n *\n * @param {string|function} onTypeStFn - Message type to listen for, or universal handler function\n * @param {function=} handlerFn - Handler function (if first arg is type string)\n *\n * @example\n * // Type-specific handler\n * setOnReceiver('notification', (msg) => {\n * console.log('Got notification:', msg.data)\n * })\n *\n * // Universal handler (receives all messages)\n * setOnReceiver((msg) => {\n * console.log('Got message:', msg.type, msg.data)\n * })\n */\nexport function setOnReceiver(onTypeStFn, handlerFn) {\n if (typeof onTypeStFn === \"string\") {\n ofTypesOb[onTypeStFn] = [handlerFn];\n } else if (!receiverArray.includes(onTypeStFn)) {\n receiverArray.push(onTypeStFn);\n }\n}\n", "/**\n * Browser entry point for api-ape client\n *\n * This module sets up the global `window.api` object for direct browser usage.\n * It is designed to be loaded via a `<script>` tag and automatically establishes\n * a WebSocket connection with automatic reconnection enabled.\n *\n * @module client/browser\n * @file Browser bundle entry point - creates global `window.api` object\n *\n * @description\n * When loaded in a browser environment, this module:\n * 1. Establishes a WebSocket connection to the server\n * 2. Enables automatic reconnection on disconnect\n * 3. Exposes the `window.api` global object for making API calls\n *\n * The `window.api` object is a Proxy that allows calling server endpoints\n * using a fluent, path-building syntax.\n *\n * @example <caption>Loading in HTML</caption>\n * <script src=\"/api/ape.js\"></script>\n * <script>\n * // The global `api` object is now available\n * api.users.list().then(users => console.log(users))\n * </script>\n *\n * @example <caption>Making API calls</caption>\n * // Call /users endpoint\n * api.users({ name: 'Alice' })\n *\n * // Call /users/create endpoint\n * api.users.create({ name: 'Bob', email: 'bob@example.com' })\n *\n * // Call /chat/messages endpoint with path parameter\n * api.chat.messages('/room123', { text: 'Hello!' })\n *\n * @example <caption>Subscribing to broadcasts</caption>\n * // Listen for 'notification' broadcasts from the server\n * api.on('notification', (payload) => {\n * console.log('Received:', payload.data)\n * })\n *\n * @example <caption>Monitoring connection state</caption>\n * api.onConnectionChange((state) => {\n * console.log('Connection state:', state)\n * // state: 'offline' | 'walled' | 'disconnected' | 'connecting' | 'connected'\n * })\n *\n * @example <caption>Checking transport type</caption>\n * console.log('Transport:', api.transport) // 'websocket' | 'polling' | null\n *\n * @see {@link module:client/connectSocket} for connection implementation details\n * @see {@link module:client/connection/proxy} for the Proxy-based API syntax\n */\n\nimport connectSocket from \"./connectSocket.js\";\n\n/**\n * The client instance created by connectSocket.\n * Contains the sender proxy, event handlers, and transport information.\n *\n * @type {Object}\n * @property {Proxy} sender - Proxied object for making API calls\n * @property {Function} setOnReceiver - Function to register broadcast handlers\n * @property {Function} onConnectionChange - Function to monitor connection state\n * @property {string|null} transport - Current transport type ('websocket' | 'polling' | null)\n * @private\n */\nconst client = connectSocket();\n\n/**\n * Enable automatic reconnection when the WebSocket connection is lost.\n * This ensures the client will attempt to reconnect after disconnection.\n */\nconnectSocket.autoReconnect();\n\n/**\n * Global API object exposed on `window.api`.\n *\n * This is a Proxy object that intercepts property access to build endpoint paths.\n * Each property access returns a new Proxy, allowing chained path building.\n *\n * @global\n * @name api\n * @type {Proxy}\n *\n * @property {Function} on - Subscribe to server broadcasts\n * @property {Function} onConnectionChange - Subscribe to connection state changes\n * @property {string|null} transport - Current transport type (read-only)\n *\n * @example\n * // These are equivalent:\n * api.users({ id: 1 }) // Calls /users\n * api.users.profile({ id: 1 }) // Calls /users/profile\n */\nwindow.api = client.sender;\n\n/**\n * Register a handler for server broadcasts.\n *\n * @function api.on\n * @param {string} type - The broadcast type/event name to listen for\n * @param {Function} handler - Callback function invoked when broadcast is received\n * @param {Object} handler.payload - The broadcast payload\n * @param {*} handler.payload.data - The broadcast data\n * @param {string} handler.payload.type - The broadcast type\n * @param {Error|null} handler.payload.err - Error if any occurred\n *\n * @example\n * api.on('chat.message', ({ data, type }) => {\n * console.log(`New message: ${data.text}`)\n * })\n *\n * @example\n * api.on('user.joined', ({ data }) => {\n * showNotification(`${data.username} joined the room`)\n * })\n */\nObject.defineProperty(window.api, \"on\", {\n value: client.setOnReceiver,\n writable: false,\n enumerable: false,\n configurable: false,\n});\n\n/**\n * Subscribe to connection state changes.\n *\n * The handler is called immediately with the current state and then\n * again whenever the connection state changes.\n *\n * @function api.onConnectionChange\n * @param {Function} handler - Callback invoked on state changes\n * @param {ConnectionState} handler.state - The new connection state\n * @returns {Function} Unsubscribe function to stop receiving updates\n *\n * @example\n * const unsubscribe = api.onConnectionChange((state) => {\n * switch (state) {\n * case 'connected':\n * hideOfflineBanner()\n * break\n * case 'disconnected':\n * case 'offline':\n * showOfflineBanner()\n * break\n * case 'walled':\n * showCaptivePortalWarning()\n * break\n * }\n * })\n *\n * // Later, to stop listening:\n * unsubscribe()\n */\nObject.defineProperty(window.api, \"onConnectionChange\", {\n value: client.onConnectionChange,\n writable: false,\n enumerable: false,\n configurable: false,\n});\n\n/**\n * Current transport type (read-only).\n *\n * Indicates which transport mechanism is currently being used for communication:\n * - `'websocket'` - Primary WebSocket connection is active\n * - `'polling'` - Fallback HTTP streaming/long-polling is active\n * - `null` - No connection established yet\n *\n * This property is read-only and managed internally by api-ape.\n * The transport may change during the lifecycle of the connection\n * (e.g., falling back from WebSocket to polling if WS is blocked).\n *\n * @name api.transport\n * @type {string|null}\n * @readonly\n *\n * @example\n * console.log('Current transport:', api.transport)\n * // Output: 'websocket', 'polling', or null\n *\n * @example\n * if (api.transport === 'polling') {\n * console.warn('WebSocket unavailable, using fallback transport')\n * }\n */\nObject.defineProperty(window.api, \"transport\", {\n get: () => client.transport,\n enumerable: false,\n configurable: false,\n});\n"],
5
+ "mappings": "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",
6
+ "names": ["require_plugins", "__commonJSMin", "exports", "module", "builtInTags", "plugins", "register", "tag", "config", "getPlugin", "getAllPlugins", "hasPlugin", "clearPlugins", "require_encode", "__commonJSMin", "exports", "module", "getAllPlugins", "tagLookup", "encode", "obj", "visitedEncode", "encodeValueWithVisited", "value", "path", "type", "tag", "customTag", "plugin", "key", "isArray", "objKeys", "result", "typesFound", "i", "t", "v", "keys", "stringify", "require_decode", "__commonJSMin", "exports", "module", "getPlugin", "pointers2Res", "tagLookup", "s", "match", "n", "sourcePath", "currentPath", "name", "message", "stack", "err", "a", "o", "binaryStr", "bytes", "i", "parseKeyWithTags", "key", "tag", "decodeValue", "val", "path", "plugin", "res", "typeTags", "t", "resolvePointers", "obj", "refPath", "attrPath", "ref", "attrParent", "decode", "data", "result", "p", "parse", "encoded", "require_jss", "__commonJSMin", "exports", "module", "encode", "stringify", "decode", "parse", "custom", "clearPlugins", "require_messageHash", "__commonJSMin", "exports", "module", "alphabet", "toBase32", "n", "remainder", "current", "jenkinsOneAtATimeHash", "keyString", "hash", "charIndex", "messageHash", "messageSt", "import_jss", "import_jss", "import_jss", "parseStreamBuffer", "buffer", "messages", "start", "depth", "inString", "escaped", "i", "char", "jsonStr", "jss", "e", "remaining", "getPollUrl", "hostname", "isLocal", "isHttps", "port", "protocol", "portSuffix", "createStreamingTransport", "isActive", "abortController", "streamBuffer", "reconnectTimer", "onMessage", "onOpen", "onClose", "onError", "scheduleReconnect", "connect", "response", "reader", "decoder", "read", "done", "value", "messages", "remaining", "parseStreamBuffer", "msg", "readErr", "err", "send", "type", "data", "createdAt", "payload", "jss", "error", "sendRaw", "close", "fn", "ConnectionState", "connectionState", "connectionChangeListeners", "notifyConnectionChange", "newState", "fn", "onConnectionChange", "handler", "connectionChangeListeners", "connectionState", "idx", "PING_TIMEOUT", "MAX_PING_CLOCK_SKEW", "WS_RETRY_INTERVAL", "networkCheckTimer", "isDevMode", "getPingUrl", "hostname", "isHttps", "port", "protocol", "portSuffix", "checkCaptivePortal", "controller", "timeoutId", "response", "data", "now", "skew", "direction", "err", "scheduleNetworkRetry", "attemptConnectionFn", "setupOnlineListeners", "notifyConnectionChange", "ConnectionState", "getBaseUrl", "isLocal", "getSocketUrl", "subscriptions", "_sendFn", "setSendFn", "sendFn", "subscribe", "channel", "callback", "callbacks", "isFirstSubscriber", "cbs", "hasSubscribers", "dispatch", "data", "err", "resubscribeAll", "joinKey", "reservedKeys", "handler", "fn", "key", "wrapperFn", "a", "b", "path", "body", "subscribe", "wrap", "api", "import_messageHash", "import_jss", "isBinaryData", "value", "getBinaryTag", "generateUploadHash", "path", "hash", "i", "char", "setValueAtPath", "obj", "parts", "current", "findTaggedProps", "tag", "results", "suffix", "key", "cleanKey", "cleanTaggedKeys", "item", "cleaned", "processBinaryForUpload", "data", "path", "isBinaryData", "tag", "getBinaryTag", "hash", "generateUploadHash", "processedArray", "allUploads", "i", "itemPath", "processedData", "uploads", "processedObj", "key", "uploadBinaryData", "queryId", "uploads", "baseUrl", "getBaseUrl", "hash", "data", "response", "totalRequestTimeout", "connectTimeout", "createWsSend", "getSocket", "waitingOn", "type", "data", "createdAt", "directCall", "rej", "promiseIsLive", "timeLeftForReqToBeMade", "timer", "processedData", "uploads", "processBinaryForUpload", "payload", "message", "jss", "queryId", "messageHash", "replyPromise", "resolve", "reject", "err", "result", "next", "uploadBinaryData", "worker", "createSender", "isReady", "getSendFn", "waitingQueue", "connectFn", "errMessage", "waitingOnOpen", "res", "waitingOnOpenThen", "waitingOnOpenCatch", "fetchLinkedResources", "data", "clientId", "resources", "findTaggedProps", "cleanedData", "cleanTaggedKeys", "baseUrl", "getBaseUrl", "path", "hash", "response", "arrayBuffer", "setValueAtPath", "err", "fetchSharedFiles", "maxRetries", "files", "retries", "backoff", "r", "receiverArray", "ofTypesOb", "processIncomingData", "data", "err", "result", "fetchLinkedResources", "fetchSharedFiles", "e", "dispatchMessage", "type", "hasSubscribers", "dispatch", "w", "setOnReceiver", "onTypeStFn", "handlerFn", "configuredTransport", "currentTransport", "streamingTransport", "wsRetryTimer", "WS_FALLBACK_TIMEOUT", "__socket", "ready", "waitingOn", "aWaitingSend", "reconnect", "wsSend", "createWsSend", "setupOnlineListeners", "attemptConnection", "flushWaitingMessages", "sendFn", "type", "data", "resolve", "reject", "waiting", "createdAt", "timer", "result", "switchToStreaming", "createStreamingTransport", "msg", "processIncomingData", "dispatchMessage", "setSendFn", "resubscribeAll", "notifyConnectionChange", "ConnectionState", "t", "d", "c", "startWsRetry", "err", "tryWebSocket", "WS_RETRY_INTERVAL", "isRetry", "ws", "getSocketUrl", "fallbackTimer", "jss", "event", "queryId", "hydratedData", "processed", "connectSocket", "checkCaptivePortal", "scheduleNetworkRetry", "sender", "createSender", "buildClientInterface", "wrap", "setOnReceiver", "onConnectionChange", "connectSocket_default", "client", "connectSocket_default"]
7
7
  }