@kortix/sdk 0.13.4 → 0.13.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (380) hide show
  1. package/README.md +63 -6
  2. package/dist/browser/cache/idb-sync-cache.d.ts +19 -1
  3. package/dist/browser/cache/idb-sync-cache.d.ts.map +1 -1
  4. package/dist/browser/cache/idb-sync-cache.js +39 -4
  5. package/dist/browser/cache/idb-sync-cache.js.map +1 -1
  6. package/dist/browser/session-sync/server-transcript-mirror.d.ts +82 -0
  7. package/dist/browser/session-sync/server-transcript-mirror.d.ts.map +1 -0
  8. package/dist/browser/session-sync/server-transcript-mirror.js +143 -0
  9. package/dist/browser/session-sync/server-transcript-mirror.js.map +1 -0
  10. package/dist/browser/session-sync/session-sync-registry.d.ts +11 -1
  11. package/dist/browser/session-sync/session-sync-registry.d.ts.map +1 -1
  12. package/dist/browser/session-sync/session-sync-registry.js +117 -9
  13. package/dist/browser/session-sync/session-sync-registry.js.map +1 -1
  14. package/dist/browser/session-sync/visibility.d.ts +19 -0
  15. package/dist/browser/session-sync/visibility.d.ts.map +1 -0
  16. package/dist/browser/session-sync/visibility.js +16 -0
  17. package/dist/browser/session-sync/visibility.js.map +1 -0
  18. package/dist/browser/stores/sandbox-connection-store.d.ts +13 -1
  19. package/dist/browser/stores/sandbox-connection-store.d.ts.map +1 -1
  20. package/dist/browser/stores/sandbox-connection-store.js +6 -3
  21. package/dist/browser/stores/sandbox-connection-store.js.map +1 -1
  22. package/dist/browser/stores/session-working-store.d.ts +10 -5
  23. package/dist/browser/stores/session-working-store.d.ts.map +1 -1
  24. package/dist/browser/stores/session-working-store.js +32 -8
  25. package/dist/browser/stores/session-working-store.js.map +1 -1
  26. package/dist/browser/stores/sync-store.d.ts +56 -2
  27. package/dist/browser/stores/sync-store.d.ts.map +1 -1
  28. package/dist/browser/stores/sync-store.js +682 -402
  29. package/dist/browser/stores/sync-store.js.map +1 -1
  30. package/dist/core/auth/app-viewer.d.ts +57 -0
  31. package/dist/core/auth/app-viewer.d.ts.map +1 -0
  32. package/dist/core/auth/app-viewer.js +82 -0
  33. package/dist/core/auth/app-viewer.js.map +1 -0
  34. package/dist/core/auth/session.d.ts +45 -0
  35. package/dist/core/auth/session.d.ts.map +1 -0
  36. package/dist/core/auth/session.js +113 -0
  37. package/dist/core/auth/session.js.map +1 -0
  38. package/dist/core/client/kortix.d.ts +37 -29
  39. package/dist/core/client/kortix.d.ts.map +1 -1
  40. package/dist/core/client/kortix.js +61 -8
  41. package/dist/core/client/kortix.js.map +1 -1
  42. package/dist/core/http/config.d.ts +4 -2
  43. package/dist/core/http/config.d.ts.map +1 -1
  44. package/dist/core/http/config.js.map +1 -1
  45. package/dist/core/http/opencode-errors.d.ts +17 -0
  46. package/dist/core/http/opencode-errors.d.ts.map +1 -1
  47. package/dist/core/http/opencode-errors.js +25 -0
  48. package/dist/core/http/opencode-errors.js.map +1 -1
  49. package/dist/core/rest/platform-client/auth.d.ts +120 -0
  50. package/dist/core/rest/platform-client/auth.d.ts.map +1 -0
  51. package/dist/core/rest/platform-client/auth.js +98 -0
  52. package/dist/core/rest/platform-client/auth.js.map +1 -0
  53. package/dist/core/rest/platform-client/host-boundary.d.ts +5 -0
  54. package/dist/core/rest/platform-client/host-boundary.d.ts.map +1 -1
  55. package/dist/core/rest/platform-client/host-boundary.js.map +1 -1
  56. package/dist/core/rest/projects-client/accounts.d.ts +57 -0
  57. package/dist/core/rest/projects-client/accounts.d.ts.map +1 -1
  58. package/dist/core/rest/projects-client/accounts.js +31 -0
  59. package/dist/core/rest/projects-client/accounts.js.map +1 -1
  60. package/dist/core/rest/projects-client/apps.d.ts +9 -0
  61. package/dist/core/rest/projects-client/apps.d.ts.map +1 -1
  62. package/dist/core/rest/projects-client/apps.js.map +1 -1
  63. package/dist/core/rest/projects-client/billing.d.ts +2 -0
  64. package/dist/core/rest/projects-client/billing.d.ts.map +1 -1
  65. package/dist/core/rest/projects-client/billing.js.map +1 -1
  66. package/dist/core/rest/projects-client/channels.d.ts +0 -3
  67. package/dist/core/rest/projects-client/channels.d.ts.map +1 -1
  68. package/dist/core/rest/projects-client/channels.js +0 -6
  69. package/dist/core/rest/projects-client/channels.js.map +1 -1
  70. package/dist/core/rest/projects-client/connectors.d.ts +104 -19
  71. package/dist/core/rest/projects-client/connectors.d.ts.map +1 -1
  72. package/dist/core/rest/projects-client/connectors.js +56 -2
  73. package/dist/core/rest/projects-client/connectors.js.map +1 -1
  74. package/dist/core/rest/projects-client/index.d.ts +1 -2
  75. package/dist/core/rest/projects-client/index.d.ts.map +1 -1
  76. package/dist/core/rest/projects-client/index.js +1 -2
  77. package/dist/core/rest/projects-client/index.js.map +1 -1
  78. package/dist/core/rest/projects-client/oauth-clients.d.ts +47 -0
  79. package/dist/core/rest/projects-client/oauth-clients.d.ts.map +1 -0
  80. package/dist/core/rest/projects-client/oauth-clients.js +33 -0
  81. package/dist/core/rest/projects-client/oauth-clients.js.map +1 -0
  82. package/dist/core/rest/projects-client/projects.d.ts +23 -6
  83. package/dist/core/rest/projects-client/projects.d.ts.map +1 -1
  84. package/dist/core/rest/projects-client/projects.js +6 -3
  85. package/dist/core/rest/projects-client/projects.js.map +1 -1
  86. package/dist/core/rest/projects-client/session-sandbox.d.ts +52 -0
  87. package/dist/core/rest/projects-client/session-sandbox.d.ts.map +1 -1
  88. package/dist/core/rest/projects-client/session-sandbox.js.map +1 -1
  89. package/dist/core/rest/projects-client/sessions.d.ts +142 -34
  90. package/dist/core/rest/projects-client/sessions.d.ts.map +1 -1
  91. package/dist/core/rest/projects-client/sessions.js +48 -19
  92. package/dist/core/rest/projects-client/sessions.js.map +1 -1
  93. package/dist/core/runtime/attachment-part.d.ts +26 -0
  94. package/dist/core/runtime/attachment-part.d.ts.map +1 -0
  95. package/dist/core/runtime/attachment-part.js +41 -0
  96. package/dist/core/runtime/attachment-part.js.map +1 -0
  97. package/dist/core/runtime/client.d.ts +4 -2
  98. package/dist/core/runtime/client.d.ts.map +1 -1
  99. package/dist/core/runtime/client.js +3 -2
  100. package/dist/core/runtime/client.js.map +1 -1
  101. package/dist/core/runtime/opencode-v2-event-aliases.d.ts +184 -0
  102. package/dist/core/runtime/opencode-v2-event-aliases.d.ts.map +1 -0
  103. package/dist/core/runtime/opencode-v2-event-aliases.js +2 -0
  104. package/dist/core/runtime/opencode-v2-event-aliases.js.map +1 -0
  105. package/dist/core/runtime/pty.d.ts +11 -1
  106. package/dist/core/runtime/pty.d.ts.map +1 -1
  107. package/dist/core/runtime/pty.js +19 -6
  108. package/dist/core/runtime/pty.js.map +1 -1
  109. package/dist/core/session/connection.d.ts +64 -0
  110. package/dist/core/session/connection.d.ts.map +1 -0
  111. package/dist/core/session/connection.js +36 -0
  112. package/dist/core/session/connection.js.map +1 -0
  113. package/dist/core/session/index.d.ts +2 -0
  114. package/dist/core/session/index.d.ts.map +1 -1
  115. package/dist/core/session/index.js +2 -0
  116. package/dist/core/session/index.js.map +1 -1
  117. package/dist/core/session/open-bundle.d.ts +110 -0
  118. package/dist/core/session/open-bundle.d.ts.map +1 -0
  119. package/dist/core/session/open-bundle.js +183 -0
  120. package/dist/core/session/open-bundle.js.map +1 -0
  121. package/dist/core/session/poll-owner.d.ts +35 -0
  122. package/dist/core/session/poll-owner.d.ts.map +1 -0
  123. package/dist/core/session/poll-owner.js +90 -0
  124. package/dist/core/session/poll-owner.js.map +1 -0
  125. package/dist/core/session/preview-auth.d.ts +25 -0
  126. package/dist/core/session/preview-auth.d.ts.map +1 -1
  127. package/dist/core/session/preview-auth.js +31 -1
  128. package/dist/core/session/preview-auth.js.map +1 -1
  129. package/dist/core/session/preview-config.d.ts +26 -0
  130. package/dist/core/session/preview-config.d.ts.map +1 -0
  131. package/dist/core/session/preview-config.js +95 -0
  132. package/dist/core/session/preview-config.js.map +1 -0
  133. package/dist/core/session/preview-options.d.ts +25 -0
  134. package/dist/core/session/preview-options.d.ts.map +1 -0
  135. package/dist/core/session/preview-options.js +45 -0
  136. package/dist/core/session/preview-options.js.map +1 -0
  137. package/dist/core/session/preview.d.ts +8 -3
  138. package/dist/core/session/preview.d.ts.map +1 -1
  139. package/dist/core/session/preview.js +11 -4
  140. package/dist/core/session/preview.js.map +1 -1
  141. package/dist/core/session/rewind.d.ts +67 -24
  142. package/dist/core/session/rewind.d.ts.map +1 -1
  143. package/dist/core/session/rewind.js +74 -26
  144. package/dist/core/session/rewind.js.map +1 -1
  145. package/dist/core/session/server-store/active.d.ts +10 -12
  146. package/dist/core/session/server-store/active.d.ts.map +1 -1
  147. package/dist/core/session/server-store/active.js +11 -9
  148. package/dist/core/session/server-store/active.js.map +1 -1
  149. package/dist/core/session/server-store/url-helpers.d.ts +11 -2
  150. package/dist/core/session/server-store/url-helpers.d.ts.map +1 -1
  151. package/dist/core/session/server-store/url-helpers.js +11 -2
  152. package/dist/core/session/server-store/url-helpers.js.map +1 -1
  153. package/dist/core/session/static-file-preview.d.ts +50 -0
  154. package/dist/core/session/static-file-preview.d.ts.map +1 -0
  155. package/dist/core/session/static-file-preview.js +91 -0
  156. package/dist/core/session/static-file-preview.js.map +1 -0
  157. package/dist/core/session/url.d.ts +25 -2
  158. package/dist/core/session/url.d.ts.map +1 -1
  159. package/dist/core/session/url.js +87 -11
  160. package/dist/core/session/url.js.map +1 -1
  161. package/dist/core/session/wake-escalation.d.ts +188 -0
  162. package/dist/core/session/wake-escalation.d.ts.map +1 -0
  163. package/dist/core/session/wake-escalation.js +241 -0
  164. package/dist/core/session/wake-escalation.js.map +1 -0
  165. package/dist/core/session/working.d.ts +107 -2
  166. package/dist/core/session/working.d.ts.map +1 -1
  167. package/dist/core/session/working.js +206 -7
  168. package/dist/core/session/working.js.map +1 -1
  169. package/dist/core/session-sync/durable-log.d.ts +96 -0
  170. package/dist/core/session-sync/durable-log.d.ts.map +1 -0
  171. package/dist/core/session-sync/durable-log.js +94 -0
  172. package/dist/core/session-sync/durable-log.js.map +1 -0
  173. package/dist/core/session-sync/fragment.d.ts +27 -0
  174. package/dist/core/session-sync/fragment.d.ts.map +1 -0
  175. package/dist/core/session-sync/fragment.js +24 -0
  176. package/dist/core/session-sync/fragment.js.map +1 -0
  177. package/dist/core/session-sync/session-sync-controller.d.ts +117 -11
  178. package/dist/core/session-sync/session-sync-controller.d.ts.map +1 -1
  179. package/dist/core/session-sync/session-sync-controller.js +278 -26
  180. package/dist/core/session-sync/session-sync-controller.js.map +1 -1
  181. package/dist/core/stream/keepalive.d.ts +17 -0
  182. package/dist/core/stream/keepalive.d.ts.map +1 -0
  183. package/dist/core/stream/keepalive.js +19 -0
  184. package/dist/core/stream/keepalive.js.map +1 -0
  185. package/dist/core/turns/grouping.d.ts.map +1 -1
  186. package/dist/core/turns/grouping.js +73 -23
  187. package/dist/core/turns/grouping.js.map +1 -1
  188. package/dist/core/turns/types.d.ts +5 -0
  189. package/dist/core/turns/types.d.ts.map +1 -1
  190. package/dist/index.d.ts +16 -1
  191. package/dist/index.d.ts.map +1 -1
  192. package/dist/index.js +19 -0
  193. package/dist/index.js.map +1 -1
  194. package/dist/kortix.esm.min.js +25 -25
  195. package/dist/kortix.global.js +25 -25
  196. package/dist/node/app-viewer.d.ts +41 -0
  197. package/dist/node/app-viewer.d.ts.map +1 -0
  198. package/dist/node/app-viewer.js +150 -0
  199. package/dist/node/app-viewer.js.map +1 -0
  200. package/dist/node/auth.d.ts +84 -0
  201. package/dist/node/auth.d.ts.map +1 -0
  202. package/dist/node/auth.js +414 -0
  203. package/dist/node/auth.js.map +1 -0
  204. package/dist/node/server.d.ts +19 -0
  205. package/dist/node/server.d.ts.map +1 -1
  206. package/dist/node/server.js +13 -1
  207. package/dist/node/server.js.map +1 -1
  208. package/dist/platform/auth-core.d.ts +13 -1
  209. package/dist/platform/auth-core.d.ts.map +1 -1
  210. package/dist/platform/auth-core.js +13 -1
  211. package/dist/platform/auth-core.js.map +1 -1
  212. package/dist/react/bedrock-invokable.d.ts +58 -0
  213. package/dist/react/bedrock-invokable.d.ts.map +1 -0
  214. package/dist/react/bedrock-invokable.js +83 -0
  215. package/dist/react/bedrock-invokable.js.map +1 -0
  216. package/dist/react/index.d.ts +3 -0
  217. package/dist/react/index.d.ts.map +1 -1
  218. package/dist/react/index.js +7 -0
  219. package/dist/react/index.js.map +1 -1
  220. package/dist/react/model-flatten.d.ts +14 -1
  221. package/dist/react/model-flatten.d.ts.map +1 -1
  222. package/dist/react/model-flatten.js +3 -4
  223. package/dist/react/model-flatten.js.map +1 -1
  224. package/dist/react/opencode.d.ts +3 -1
  225. package/dist/react/opencode.d.ts.map +1 -1
  226. package/dist/react/opencode.js +5 -1
  227. package/dist/react/opencode.js.map +1 -1
  228. package/dist/react/provider-selection.d.ts +58 -0
  229. package/dist/react/provider-selection.d.ts.map +1 -1
  230. package/dist/react/provider-selection.js +253 -2
  231. package/dist/react/provider-selection.js.map +1 -1
  232. package/dist/react/session-start-giveup.d.ts +12 -0
  233. package/dist/react/session-start-giveup.d.ts.map +1 -0
  234. package/dist/react/session-start-giveup.js +13 -0
  235. package/dist/react/session-start-giveup.js.map +1 -0
  236. package/dist/react/session-title-sync.d.ts.map +1 -1
  237. package/dist/react/session-title-sync.js +15 -5
  238. package/dist/react/session-title-sync.js.map +1 -1
  239. package/dist/react/sign-in-with-kortix.d.ts +346 -0
  240. package/dist/react/sign-in-with-kortix.d.ts.map +1 -0
  241. package/dist/react/sign-in-with-kortix.js +65 -0
  242. package/dist/react/sign-in-with-kortix.js.map +1 -0
  243. package/dist/react/use-admin-accounts.d.ts +36 -1
  244. package/dist/react/use-admin-accounts.d.ts.map +1 -1
  245. package/dist/react/use-admin-accounts.js +19 -0
  246. package/dist/react/use-admin-accounts.js.map +1 -1
  247. package/dist/react/use-gateway-routing-policy.d.ts +1 -0
  248. package/dist/react/use-gateway-routing-policy.d.ts.map +1 -1
  249. package/dist/react/use-gateway-routing-policy.js +12 -1
  250. package/dist/react/use-gateway-routing-policy.js.map +1 -1
  251. package/dist/react/use-kortix-app-viewer.d.ts +13 -0
  252. package/dist/react/use-kortix-app-viewer.d.ts.map +1 -0
  253. package/dist/react/use-kortix-app-viewer.js +29 -0
  254. package/dist/react/use-kortix-app-viewer.js.map +1 -0
  255. package/dist/react/use-model-defaults.d.ts +7 -0
  256. package/dist/react/use-model-defaults.d.ts.map +1 -1
  257. package/dist/react/use-model-defaults.js +7 -1
  258. package/dist/react/use-model-defaults.js.map +1 -1
  259. package/dist/react/use-model-enablement.d.ts.map +1 -1
  260. package/dist/react/use-model-enablement.js +5 -1
  261. package/dist/react/use-model-enablement.js.map +1 -1
  262. package/dist/react/use-model-store.d.ts +31 -0
  263. package/dist/react/use-model-store.d.ts.map +1 -1
  264. package/dist/react/use-model-store.js +31 -2
  265. package/dist/react/use-model-store.js.map +1 -1
  266. package/dist/react/use-opencode-events/handle-event.d.ts.map +1 -1
  267. package/dist/react/use-opencode-events/handle-event.js +26 -0
  268. package/dist/react/use-opencode-events/handle-event.js.map +1 -1
  269. package/dist/react/use-opencode-events/helpers.d.ts +49 -5
  270. package/dist/react/use-opencode-events/helpers.d.ts.map +1 -1
  271. package/dist/react/use-opencode-events/helpers.js +54 -6
  272. package/dist/react/use-opencode-events/helpers.js.map +1 -1
  273. package/dist/react/use-opencode-events/index.d.ts.map +1 -1
  274. package/dist/react/use-opencode-events/index.js +80 -8
  275. package/dist/react/use-opencode-events/index.js.map +1 -1
  276. package/dist/react/use-opencode-events/rehydrate-targets.d.ts +18 -0
  277. package/dist/react/use-opencode-events/rehydrate-targets.d.ts.map +1 -0
  278. package/dist/react/use-opencode-events/rehydrate-targets.js +20 -0
  279. package/dist/react/use-opencode-events/rehydrate-targets.js.map +1 -0
  280. package/dist/react/use-opencode-events/stream-revival.d.ts +37 -0
  281. package/dist/react/use-opencode-events/stream-revival.d.ts.map +1 -0
  282. package/dist/react/use-opencode-events/stream-revival.js +53 -0
  283. package/dist/react/use-opencode-events/stream-revival.js.map +1 -0
  284. package/dist/react/use-opencode-events/use-event-stream-refs.d.ts.map +1 -1
  285. package/dist/react/use-opencode-events/use-event-stream-refs.js +14 -2
  286. package/dist/react/use-opencode-events/use-event-stream-refs.js.map +1 -1
  287. package/dist/react/use-opencode-local.d.ts.map +1 -1
  288. package/dist/react/use-opencode-local.js +27 -8
  289. package/dist/react/use-opencode-local.js.map +1 -1
  290. package/dist/react/use-opencode-pty.d.ts +7 -1
  291. package/dist/react/use-opencode-pty.d.ts.map +1 -1
  292. package/dist/react/use-opencode-pty.js +6 -2
  293. package/dist/react/use-opencode-pty.js.map +1 -1
  294. package/dist/react/use-opencode-sessions/agents.d.ts.map +1 -1
  295. package/dist/react/use-opencode-sessions/agents.js +13 -2
  296. package/dist/react/use-opencode-sessions/agents.js.map +1 -1
  297. package/dist/react/use-opencode-sessions/commands.d.ts +12 -0
  298. package/dist/react/use-opencode-sessions/commands.d.ts.map +1 -1
  299. package/dist/react/use-opencode-sessions/commands.js +15 -3
  300. package/dist/react/use-opencode-sessions/commands.js.map +1 -1
  301. package/dist/react/use-opencode-sessions/index.d.ts +1 -0
  302. package/dist/react/use-opencode-sessions/index.d.ts.map +1 -1
  303. package/dist/react/use-opencode-sessions/index.js +1 -0
  304. package/dist/react/use-opencode-sessions/index.js.map +1 -1
  305. package/dist/react/use-opencode-sessions/keys.d.ts +8 -0
  306. package/dist/react/use-opencode-sessions/keys.d.ts.map +1 -1
  307. package/dist/react/use-opencode-sessions/keys.js +8 -0
  308. package/dist/react/use-opencode-sessions/keys.js.map +1 -1
  309. package/dist/react/use-opencode-sessions/providers.d.ts.map +1 -1
  310. package/dist/react/use-opencode-sessions/providers.js +56 -5
  311. package/dist/react/use-opencode-sessions/providers.js.map +1 -1
  312. package/dist/react/use-opencode-sessions/shared.d.ts +23 -0
  313. package/dist/react/use-opencode-sessions/shared.d.ts.map +1 -1
  314. package/dist/react/use-opencode-sessions/shared.js +31 -0
  315. package/dist/react/use-opencode-sessions/shared.js.map +1 -1
  316. package/dist/react/use-opencode-sessions/vcs.d.ts +28 -0
  317. package/dist/react/use-opencode-sessions/vcs.d.ts.map +1 -0
  318. package/dist/react/use-opencode-sessions/vcs.js +42 -0
  319. package/dist/react/use-opencode-sessions/vcs.js.map +1 -0
  320. package/dist/react/use-poll-owner.d.ts +14 -0
  321. package/dist/react/use-poll-owner.d.ts.map +1 -0
  322. package/dist/react/use-poll-owner.js +32 -0
  323. package/dist/react/use-poll-owner.js.map +1 -0
  324. package/dist/react/use-project-llm-gateway.d.ts +27 -0
  325. package/dist/react/use-project-llm-gateway.d.ts.map +1 -0
  326. package/dist/react/use-project-llm-gateway.js +41 -0
  327. package/dist/react/use-project-llm-gateway.js.map +1 -0
  328. package/dist/react/use-project-models.d.ts +9 -0
  329. package/dist/react/use-project-models.d.ts.map +1 -1
  330. package/dist/react/use-project-models.js +24 -1
  331. package/dist/react/use-project-models.js.map +1 -1
  332. package/dist/react/use-runtime-reconnect.d.ts +11 -0
  333. package/dist/react/use-runtime-reconnect.d.ts.map +1 -1
  334. package/dist/react/use-runtime-reconnect.js +34 -3
  335. package/dist/react/use-runtime-reconnect.js.map +1 -1
  336. package/dist/react/use-session-prompts.d.ts +53 -3
  337. package/dist/react/use-session-prompts.d.ts.map +1 -1
  338. package/dist/react/use-session-prompts.js +133 -21
  339. package/dist/react/use-session-prompts.js.map +1 -1
  340. package/dist/react/use-session-send.d.ts +17 -0
  341. package/dist/react/use-session-send.d.ts.map +1 -1
  342. package/dist/react/use-session-send.js +54 -5
  343. package/dist/react/use-session-send.js.map +1 -1
  344. package/dist/react/use-session-start-give-up.d.ts +20 -0
  345. package/dist/react/use-session-start-give-up.d.ts.map +1 -0
  346. package/dist/react/use-session-start-give-up.js +60 -0
  347. package/dist/react/use-session-start-give-up.js.map +1 -0
  348. package/dist/react/use-session-sync.d.ts +35 -1
  349. package/dist/react/use-session-sync.d.ts.map +1 -1
  350. package/dist/react/use-session-sync.js +135 -53
  351. package/dist/react/use-session-sync.js.map +1 -1
  352. package/dist/react/use-session-working.d.ts +55 -0
  353. package/dist/react/use-session-working.d.ts.map +1 -1
  354. package/dist/react/use-session-working.js +114 -13
  355. package/dist/react/use-session-working.js.map +1 -1
  356. package/dist/react/use-session.d.ts +13 -13
  357. package/dist/react/use-session.d.ts.map +1 -1
  358. package/dist/react/use-session.js +74 -53
  359. package/dist/react/use-session.js.map +1 -1
  360. package/dist/react/use-static-file-preview.d.ts +12 -0
  361. package/dist/react/use-static-file-preview.d.ts.map +1 -0
  362. package/dist/react/use-static-file-preview.js +119 -0
  363. package/dist/react/use-static-file-preview.js.map +1 -0
  364. package/dist/react/use-wake-escalation.d.ts +51 -0
  365. package/dist/react/use-wake-escalation.d.ts.map +1 -0
  366. package/dist/react/use-wake-escalation.js +62 -0
  367. package/dist/react/use-wake-escalation.js.map +1 -0
  368. package/package.json +6 -4
  369. package/dist/browser/session-sync/session-transcript-cache.d.ts +0 -79
  370. package/dist/browser/session-sync/session-transcript-cache.d.ts.map +0 -1
  371. package/dist/browser/session-sync/session-transcript-cache.js +0 -91
  372. package/dist/browser/session-sync/session-transcript-cache.js.map +0 -1
  373. package/dist/core/rest/projects-client/public-voice-join.d.ts +0 -59
  374. package/dist/core/rest/projects-client/public-voice-join.d.ts.map +0 -1
  375. package/dist/core/rest/projects-client/public-voice-join.js +0 -64
  376. package/dist/core/rest/projects-client/public-voice-join.js.map +0 -1
  377. package/dist/core/rest/projects-client/transcription.d.ts +0 -5
  378. package/dist/core/rest/projects-client/transcription.d.ts.map +0 -1
  379. package/dist/core/rest/projects-client/transcription.js +0 -14
  380. package/dist/core/rest/projects-client/transcription.js.map +0 -1
@@ -0,0 +1,183 @@
1
+ /**
2
+ * ONE session-open read, shared by every consumer that would otherwise issue
3
+ * its own.
4
+ *
5
+ * THE PROBLEM. Opening a session made 6 serial control-plane round trips before
6
+ * the first honest frame — the session row, `/turn`, `/prompts`,
7
+ * `/transcript?shape=sync`, `/detail`, `/model-defaults` — and then kept making
8
+ * them, because the hooks that need those facts each own their own query. On a
9
+ * real deployment that is 0.3-2.3 s per read at the median (measured over 20
10
+ * session opens, 2026-08-26), and the last one cannot start until the first
11
+ * lands.
12
+ *
13
+ * THE SHAPE. `GET .../open-bundle` answers all of it at once, and this module
14
+ * is the seam that lets the EXISTING consumers use it without knowing it exists:
15
+ *
16
+ * 1. The open path calls `openSessionBundle()` ONCE, in a LAYOUT effect. React
17
+ * runs every layout effect, tree-wide, before any passive effect, and
18
+ * TanStack starts a query's fetch from a passive effect — so the bundle is
19
+ * always in flight before the first `/turn` or `/prompts` fetch begins.
20
+ * 2. Each consumer's own `queryFn` calls `claimOpenBundle()` FIRST. A claim
21
+ * returns the shared promise while the bundle is in flight, or the resolved
22
+ * bundle inside a short share window, and `null` otherwise.
23
+ * 3. `null` means "answer the way you always did". Every consumer keeps its
24
+ * own endpoint, so an old server, a failed bundle and a steady-state poll
25
+ * all still work. The bundle is an ACCELERATOR, never a dependency.
26
+ *
27
+ * WHY A CLAIM CANNOT START ONE. A `/turn` poll that could start a bundle would
28
+ * re-read the transcript and the model catalogue every 15 s for the life of the
29
+ * tab. Starting is the open path's decision; claiming is everyone else's.
30
+ *
31
+ * WHY THE TRANSCRIPT IS A SEPARATE, ONE-SHOT STASH. The mirror is hydrated once
32
+ * the session's OpenCode root is known, which on a cold box is 18.9-24.5 s after
33
+ * the open (measured) — long past the share window the turn and queue legs want.
34
+ * It is also the one leg that must never be served twice: a second hydrate over
35
+ * a store the runtime has already filled is how a transcript grows ghosts.
36
+ *
37
+ * EVERY LEG IS TRI-STATE. `known: false` projects to `null`, which sends the
38
+ * consumer to its endpoint. It must never project to the NEGATIVE answer — an
39
+ * empty queue, an idle turn, an empty thread — because a default rendered as an
40
+ * answer is the defect the bundle exists to remove.
41
+ */
42
+ import { getSessionOpenBundle, } from '../rest/projects-client/sessions.js';
43
+ /**
44
+ * How long a resolved bundle keeps answering claims.
45
+ *
46
+ * It only has to outlast ONE session open's burst of first reads, not the
47
+ * session. Long enough and the steady-state `/turn` poll (5 s working / 15 s
48
+ * idle) would be answered from a snapshot instead of from the server; this
49
+ * window sits just under the fastest of those cadences on purpose.
50
+ */
51
+ export const OPEN_BUNDLE_SHARE_MS = 5_000;
52
+ /** How long the one-shot transcript stash survives. Sized for the slowest
53
+ * measured cold box (24.5 s Platinum) with headroom, because the hydrate that
54
+ * consumes it cannot run until the runtime identity resolves. */
55
+ export const OPEN_BUNDLE_TRANSCRIPT_TTL_MS = 60_000;
56
+ /** The mirrored-message window an open asks for — the same span the sync
57
+ * controller's own first read covers. */
58
+ export const OPEN_BUNDLE_TRANSCRIPT_LIMIT = 40;
59
+ const entries = new Map();
60
+ const transcripts = new Map();
61
+ function scopeKey(projectId, sessionId) {
62
+ return `${projectId}/${sessionId}`;
63
+ }
64
+ /** Drop everything. Tests only — a module singleton with no reset is a test
65
+ * that passes because of the one before it. */
66
+ export function resetSessionOpenBundles() {
67
+ entries.clear();
68
+ transcripts.clear();
69
+ }
70
+ /**
71
+ * Start the session-open read for `(projectId, sessionId)`.
72
+ *
73
+ * Fire-and-forget and idempotent within the share window: calling it twice for
74
+ * one open (two hooks, StrictMode's double mount) issues one request. Never
75
+ * throws and never rejects — a failed bundle simply resolves `null` and every
76
+ * consumer falls back to its own endpoint.
77
+ */
78
+ export function openSessionBundle(projectId, sessionId, options = {}) {
79
+ if (!projectId || !sessionId)
80
+ return;
81
+ const now = options.now ?? Date.now;
82
+ const nowMs = now();
83
+ const key = scopeKey(projectId, sessionId);
84
+ const existing = entries.get(key);
85
+ if (existing && claimable(existing, nowMs))
86
+ return;
87
+ const entry = {
88
+ startedAtMs: nowMs,
89
+ settledAtMs: null,
90
+ promise: getSessionOpenBundle(projectId, sessionId, {
91
+ transcript: options.transcript ?? OPEN_BUNDLE_TRANSCRIPT_LIMIT,
92
+ })
93
+ .then((bundle) => {
94
+ stashTranscript(key, bundle, now());
95
+ return bundle;
96
+ })
97
+ .catch(() => null),
98
+ };
99
+ entry.promise = entry.promise.then((bundle) => {
100
+ entry.settledAtMs = now();
101
+ return bundle;
102
+ });
103
+ entries.set(key, entry);
104
+ }
105
+ function claimable(entry, nowMs) {
106
+ // In flight is always claimable: the consumer that asks is precisely the one
107
+ // this read was started for.
108
+ if (entry.settledAtMs === null)
109
+ return true;
110
+ return nowMs - entry.settledAtMs <= OPEN_BUNDLE_SHARE_MS;
111
+ }
112
+ /**
113
+ * Claim the in-flight or freshly-resolved bundle for this session.
114
+ *
115
+ * Returns the shared promise, or `null` when there is nothing to share — which
116
+ * is the signal to read the endpoint the normal way. Synchronous in the `null`
117
+ * case on purpose: a consumer must be able to decide without awaiting.
118
+ */
119
+ export function claimOpenBundle(projectId, sessionId, nowMs = Date.now()) {
120
+ if (!projectId || !sessionId)
121
+ return null;
122
+ const key = scopeKey(projectId, sessionId);
123
+ const entry = entries.get(key);
124
+ if (!entry)
125
+ return null;
126
+ if (!claimable(entry, nowMs)) {
127
+ entries.delete(key);
128
+ return null;
129
+ }
130
+ return entry.promise;
131
+ }
132
+ /**
133
+ * Project the turn leg. `null` for an unknown leg — never `{ turns: [] }`,
134
+ * which would be an idle claim made by something that did not know.
135
+ */
136
+ export function openBundleTurn(bundle) {
137
+ const turn = bundle.turn;
138
+ if (!turn || turn.known !== true)
139
+ return null;
140
+ const observedAtMs = Date.parse(bundle.observed_at);
141
+ return {
142
+ turns: turn.turns ?? [],
143
+ last_ended: turn.last_ended,
144
+ atMs: Number.isFinite(observedAtMs) ? observedAtMs : Date.now(),
145
+ };
146
+ }
147
+ /** Project the queue leg. `null` for an unknown leg — never `[]`, which would
148
+ * render as "nothing queued" for a queue nobody could read. */
149
+ export function openBundleQueue(bundle) {
150
+ const queue = bundle.queue;
151
+ if (!queue || queue.known !== true)
152
+ return null;
153
+ return queue.prompts ?? [];
154
+ }
155
+ function stashTranscript(key, bundle, nowMs) {
156
+ const transcript = bundle.transcript;
157
+ if (!transcript || transcript.known !== true || transcript.requested !== true)
158
+ return;
159
+ // An unavailable mirror is not a transcript. Stashing it would let a hydrate
160
+ // paint an empty thread as a complete one.
161
+ if (!transcript.available || transcript.messages.length === 0)
162
+ return;
163
+ const { known: _known, requested: _requested, ...envelope } = transcript;
164
+ transcripts.set(key, { envelope, stashedAtMs: nowMs });
165
+ }
166
+ /**
167
+ * Take the mirrored transcript this open fetched, ONCE.
168
+ *
169
+ * One-shot by design: the hydrate that consumes it is the only reader that may
170
+ * paint a snapshot, and a second paint over a store the runtime has already
171
+ * filled is how a transcript grows ghosts.
172
+ */
173
+ export function takeOpenBundleTranscript(projectId, sessionId, nowMs = Date.now()) {
174
+ const key = scopeKey(projectId, sessionId);
175
+ const stash = transcripts.get(key);
176
+ if (!stash)
177
+ return null;
178
+ transcripts.delete(key);
179
+ if (nowMs - stash.stashedAtMs > OPEN_BUNDLE_TRANSCRIPT_TTL_MS)
180
+ return null;
181
+ return stash.envelope;
182
+ }
183
+ //# sourceMappingURL=open-bundle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"open-bundle.js","sourceRoot":"","sources":["../../../src/core/session/open-bundle.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * ONE cadence per session fact, however many components read it.
3
+ *
4
+ * THE PROBLEM. TanStack schedules `refetchInterval` PER OBSERVER, not per
5
+ * query. Three hooks mount the `/turn` query on a session route (`useSession`,
6
+ * the composer, the session panel) and two mount `/prompts`, so one key ran
7
+ * three timers and the session was polled at three times its declared cadence.
8
+ * Measured on a real deployment: 6 `GET .../turn` inside a single 25 s session
9
+ * open, against a 5 s working / 15 s idle contract.
10
+ *
11
+ * THE RULE. Exactly one observer per scope owns the interval; the rest pass
12
+ * `refetchInterval: false` and read the same cache entry the owner refreshes.
13
+ * They share a query key, so an answer reaches every observer either way —
14
+ * only the SCHEDULING is exclusive.
15
+ *
16
+ * OWNERSHIP MUST SURVIVE THE OWNER. A session whose only poller unmounted would
17
+ * stop learning that a turn started somewhere else (a trigger, a second device,
18
+ * the inbox delivering a queued prompt), so releasing hands the cadence to the
19
+ * next claimant and notifies it.
20
+ *
21
+ * Framework-free on purpose: `react/use-poll-owner.ts` is a thin
22
+ * `useSyncExternalStore` over this, and the rules live where they can be tested
23
+ * without rendering anything.
24
+ */
25
+ /** Register `id` as a candidate poller for `scope`. Idempotent. */
26
+ export declare function claimPoller(scope: string, id: string): void;
27
+ /** Give up `id`'s claim. If it held the cadence, the next claimant takes it. */
28
+ export declare function releasePoller(scope: string, id: string): void;
29
+ /** Does `id` own `scope`'s cadence right now? */
30
+ export declare function isPollOwner(scope: string, id: string): boolean;
31
+ /** Re-render me when this scope's owner changes. Returns the unsubscribe. */
32
+ export declare function subscribePollOwner(scope: string, listener: () => void): () => void;
33
+ /** Drop everything. Tests only. */
34
+ export declare function resetPollOwners(): void;
35
+ //# sourceMappingURL=poll-owner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"poll-owner.d.ts","sourceRoot":"","sources":["../../../src/core/session/poll-owner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AA8BH,mEAAmE;AACnE,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAO3D;AAED,gFAAgF;AAChF,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAS7D;AAED,iDAAiD;AACjD,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAI9D;AAED,6EAA6E;AAC7E,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAOlF;AAED,mCAAmC;AACnC,wBAAgB,eAAe,IAAI,IAAI,CAEtC"}
@@ -0,0 +1,90 @@
1
+ /**
2
+ * ONE cadence per session fact, however many components read it.
3
+ *
4
+ * THE PROBLEM. TanStack schedules `refetchInterval` PER OBSERVER, not per
5
+ * query. Three hooks mount the `/turn` query on a session route (`useSession`,
6
+ * the composer, the session panel) and two mount `/prompts`, so one key ran
7
+ * three timers and the session was polled at three times its declared cadence.
8
+ * Measured on a real deployment: 6 `GET .../turn` inside a single 25 s session
9
+ * open, against a 5 s working / 15 s idle contract.
10
+ *
11
+ * THE RULE. Exactly one observer per scope owns the interval; the rest pass
12
+ * `refetchInterval: false` and read the same cache entry the owner refreshes.
13
+ * They share a query key, so an answer reaches every observer either way —
14
+ * only the SCHEDULING is exclusive.
15
+ *
16
+ * OWNERSHIP MUST SURVIVE THE OWNER. A session whose only poller unmounted would
17
+ * stop learning that a turn started somewhere else (a trigger, a second device,
18
+ * the inbox delivering a queued prompt), so releasing hands the cadence to the
19
+ * next claimant and notifies it.
20
+ *
21
+ * Framework-free on purpose: `react/use-poll-owner.ts` is a thin
22
+ * `useSyncExternalStore` over this, and the rules live where they can be tested
23
+ * without rendering anything.
24
+ */
25
+ const scopes = new Map();
26
+ function stateFor(scope) {
27
+ let state = scopes.get(scope);
28
+ if (!state) {
29
+ state = { claimants: [], listeners: new Set() };
30
+ scopes.set(scope, state);
31
+ }
32
+ return state;
33
+ }
34
+ function notify(state) {
35
+ for (const listener of state.listeners)
36
+ listener();
37
+ }
38
+ function forget(scope, state) {
39
+ // Leak hygiene: a per-session registry that never shrinks is a per-session
40
+ // leak. A scope with no claimants and no listeners has nothing to remember.
41
+ if (state.claimants.length === 0 && state.listeners.size === 0)
42
+ scopes.delete(scope);
43
+ }
44
+ /** Register `id` as a candidate poller for `scope`. Idempotent. */
45
+ export function claimPoller(scope, id) {
46
+ if (!scope || !id)
47
+ return;
48
+ const state = stateFor(scope);
49
+ if (state.claimants.includes(id))
50
+ return;
51
+ const wasOwner = state.claimants[0];
52
+ state.claimants.push(id);
53
+ if (state.claimants[0] !== wasOwner)
54
+ notify(state);
55
+ }
56
+ /** Give up `id`'s claim. If it held the cadence, the next claimant takes it. */
57
+ export function releasePoller(scope, id) {
58
+ const state = scopes.get(scope);
59
+ if (!state)
60
+ return;
61
+ const index = state.claimants.indexOf(id);
62
+ if (index === -1)
63
+ return;
64
+ const wasOwner = state.claimants[0];
65
+ state.claimants.splice(index, 1);
66
+ if (state.claimants[0] !== wasOwner)
67
+ notify(state);
68
+ forget(scope, state);
69
+ }
70
+ /** Does `id` own `scope`'s cadence right now? */
71
+ export function isPollOwner(scope, id) {
72
+ const state = scopes.get(scope);
73
+ if (!state)
74
+ return false;
75
+ return state.claimants[0] === id;
76
+ }
77
+ /** Re-render me when this scope's owner changes. Returns the unsubscribe. */
78
+ export function subscribePollOwner(scope, listener) {
79
+ const state = stateFor(scope);
80
+ state.listeners.add(listener);
81
+ return () => {
82
+ state.listeners.delete(listener);
83
+ forget(scope, state);
84
+ };
85
+ }
86
+ /** Drop everything. Tests only. */
87
+ export function resetPollOwners() {
88
+ scopes.clear();
89
+ }
90
+ //# sourceMappingURL=poll-owner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"poll-owner.js","sourceRoot":"","sources":["../../../src/core/session/poll-owner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AASH,MAAM,MAAM,GAAG,IAAI,GAAG,EAAsB,CAAC;AAE7C,SAAS,QAAQ,CAAC,KAAa;IAC7B,IAAI,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAC9B,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,KAAK,GAAG,EAAE,SAAS,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,GAAG,EAAE,EAAE,CAAC;QAChD,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IAC3B,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,MAAM,CAAC,KAAiB;IAC/B,KAAK,MAAM,QAAQ,IAAI,KAAK,CAAC,SAAS;QAAE,QAAQ,EAAE,CAAC;AACrD,CAAC;AAED,SAAS,MAAM,CAAC,KAAa,EAAE,KAAiB;IAC9C,2EAA2E;IAC3E,4EAA4E;IAC5E,IAAI,KAAK,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,SAAS,CAAC,IAAI,KAAK,CAAC;QAAE,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACvF,CAAC;AAED,mEAAmE;AACnE,MAAM,UAAU,WAAW,CAAC,KAAa,EAAE,EAAU;IACnD,IAAI,CAAC,KAAK,IAAI,CAAC,EAAE;QAAE,OAAO;IAC1B,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC9B,IAAI,KAAK,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC;QAAE,OAAO;IACzC,MAAM,QAAQ,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;IACpC,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACzB,IAAI,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,QAAQ;QAAE,MAAM,CAAC,KAAK,CAAC,CAAC;AACrD,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,aAAa,CAAC,KAAa,EAAE,EAAU;IACrD,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAChC,IAAI,CAAC,KAAK;QAAE,OAAO;IACnB,MAAM,KAAK,GAAG,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC1C,IAAI,KAAK,KAAK,CAAC,CAAC;QAAE,OAAO;IACzB,MAAM,QAAQ,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;IACpC,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IACjC,IAAI,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,QAAQ;QAAE,MAAM,CAAC,KAAK,CAAC,CAAC;IACnD,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;AACvB,CAAC;AAED,iDAAiD;AACjD,MAAM,UAAU,WAAW,CAAC,KAAa,EAAE,EAAU;IACnD,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAChC,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAC;IACzB,OAAO,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC;AACnC,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,kBAAkB,CAAC,KAAa,EAAE,QAAoB;IACpE,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC9B,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC9B,OAAO,GAAG,EAAE;QACV,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QACjC,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IACvB,CAAC,CAAC;AACJ,CAAC;AAED,mCAAmC;AACnC,MAAM,UAAU,eAAe;IAC7B,MAAM,CAAC,KAAK,EAAE,CAAC;AACjB,CAAC"}
@@ -29,4 +29,29 @@
29
29
  export declare function ensurePreviewSessionCookie(previewUrl: string, options?: {
30
30
  serverUrl?: string;
31
31
  }): Promise<boolean>;
32
+ /**
33
+ * The URL a caller should actually open for `previewUrl`, having given it a
34
+ * credential the destination will accept.
35
+ *
36
+ * The two preview forms take credentials in different ways, and using the wrong
37
+ * one fails silently — which is exactly what happened before this existed:
38
+ * `ensurePreviewSessionCookie` returns false for an ORIGIN URL (there is no
39
+ * `/v1/p/` path to derive an auth endpoint from), so "open in a new tab" opened
40
+ * the origin with no cookie and no token, and the person landed on the sign-in
41
+ * gate instead of their file.
42
+ *
43
+ * - Preview ORIGIN (`{env}-p{port}-{sandbox}.{domain}`, or the localhost
44
+ * form): the API's `__preview_session` cookie is host-scoped and can never
45
+ * reach it, so the credential rides in the URL as a one-shot `?token`. The
46
+ * proxy exchanges it for a host cookie and strips it from the address bar.
47
+ * - Path proxy (`/v1/p/{sandbox}/{port}/…`): same origin as the API, so the
48
+ * cookie works; mint it and return the URL untouched.
49
+ *
50
+ * Never throws. With no token, or for a URL that is not a preview at all, the
51
+ * input is returned unchanged — the destination then answers for itself, which
52
+ * is more honest than fabricating a URL that cannot work.
53
+ */
54
+ export declare function authorizePreviewUrl(previewUrl: string, options?: {
55
+ serverUrl?: string;
56
+ }): Promise<string>;
32
57
  //# sourceMappingURL=preview-auth.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"preview-auth.d.ts","sourceRoot":"","sources":["../../../src/core/session/preview-auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAKH;;;;;;;;GAQG;AACH,wBAAsB,0BAA0B,CAC9C,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GACnC,OAAO,CAAC,OAAO,CAAC,CAoBlB"}
1
+ {"version":3,"file":"preview-auth.d.ts","sourceRoot":"","sources":["../../../src/core/session/preview-auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAKH;;;;;;;;GAQG;AACH,wBAAsB,0BAA0B,CAC9C,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GACnC,OAAO,CAAC,OAAO,CAAC,CAoBlB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,mBAAmB,CACvC,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GACnC,OAAO,CAAC,MAAM,CAAC,CAOjB"}
@@ -18,7 +18,7 @@
18
18
  * reloaded, bookmarked, or sent to anyone.
19
19
  */
20
20
  import { getAuthToken } from '../http/auth.js';
21
- import { buildPreviewAuthEndpoint } from './preview.js';
21
+ import { appendPreviewToken, buildPreviewAuthEndpoint, isSubdomainPreviewUrl } from './preview.js';
22
22
  /**
23
23
  * Mint the `__preview_session` cookie for `previewUrl`'s proxy origin.
24
24
  *
@@ -50,4 +50,34 @@ export async function ensurePreviewSessionCookie(previewUrl, options = {}) {
50
50
  return false;
51
51
  }
52
52
  }
53
+ /**
54
+ * The URL a caller should actually open for `previewUrl`, having given it a
55
+ * credential the destination will accept.
56
+ *
57
+ * The two preview forms take credentials in different ways, and using the wrong
58
+ * one fails silently — which is exactly what happened before this existed:
59
+ * `ensurePreviewSessionCookie` returns false for an ORIGIN URL (there is no
60
+ * `/v1/p/` path to derive an auth endpoint from), so "open in a new tab" opened
61
+ * the origin with no cookie and no token, and the person landed on the sign-in
62
+ * gate instead of their file.
63
+ *
64
+ * - Preview ORIGIN (`{env}-p{port}-{sandbox}.{domain}`, or the localhost
65
+ * form): the API's `__preview_session` cookie is host-scoped and can never
66
+ * reach it, so the credential rides in the URL as a one-shot `?token`. The
67
+ * proxy exchanges it for a host cookie and strips it from the address bar.
68
+ * - Path proxy (`/v1/p/{sandbox}/{port}/…`): same origin as the API, so the
69
+ * cookie works; mint it and return the URL untouched.
70
+ *
71
+ * Never throws. With no token, or for a URL that is not a preview at all, the
72
+ * input is returned unchanged — the destination then answers for itself, which
73
+ * is more honest than fabricating a URL that cannot work.
74
+ */
75
+ export async function authorizePreviewUrl(previewUrl, options = {}) {
76
+ if (isSubdomainPreviewUrl(previewUrl)) {
77
+ const token = await getAuthToken();
78
+ return token ? appendPreviewToken(previewUrl, token) : previewUrl;
79
+ }
80
+ await ensurePreviewSessionCookie(previewUrl, options);
81
+ return previewUrl;
82
+ }
53
83
  //# sourceMappingURL=preview-auth.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"preview-auth.js","sourceRoot":"","sources":["../../../src/core/session/preview-auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,wBAAwB,EAAE,MAAM,WAAW,CAAC;AAErD;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,UAAkB,EAClB,UAAkC,EAAE;IAEpC,MAAM,YAAY,GAAG,wBAAwB,CAAC,UAAU,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;IAC7E,IAAI,CAAC,YAAY;QAAE,OAAO,KAAK,CAAC;IAEhC,MAAM,KAAK,GAAG,MAAM,YAAY,EAAE,CAAC;IACnC,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAC;IAEzB,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,YAAY,EAAE;YACzC,MAAM,EAAE,MAAM;YACd,WAAW,EAAE,SAAS;YACtB,OAAO,EAAE;gBACP,aAAa,EAAE,UAAU,KAAK,EAAE;gBAChC,cAAc,EAAE,kBAAkB;aACnC;SACF,CAAC,CAAC;QACH,OAAO,QAAQ,CAAC,EAAE,CAAC;IACrB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"preview-auth.js","sourceRoot":"","sources":["../../../src/core/session/preview-auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,kBAAkB,EAAE,wBAAwB,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AAEhG;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,UAAkB,EAClB,UAAkC,EAAE;IAEpC,MAAM,YAAY,GAAG,wBAAwB,CAAC,UAAU,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;IAC7E,IAAI,CAAC,YAAY;QAAE,OAAO,KAAK,CAAC;IAEhC,MAAM,KAAK,GAAG,MAAM,YAAY,EAAE,CAAC;IACnC,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAC;IAEzB,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,YAAY,EAAE;YACzC,MAAM,EAAE,MAAM;YACd,WAAW,EAAE,SAAS;YACtB,OAAO,EAAE;gBACP,aAAa,EAAE,UAAU,KAAK,EAAE;gBAChC,cAAc,EAAE,kBAAkB;aACnC;SACF,CAAC,CAAC;QACH,OAAO,QAAQ,CAAC,EAAE,CAAC;IACrB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,UAAkB,EAClB,UAAkC,EAAE;IAEpC,IAAI,qBAAqB,CAAC,UAAU,CAAC,EAAE,CAAC;QACtC,MAAM,KAAK,GAAG,MAAM,YAAY,EAAE,CAAC;QACnC,OAAO,KAAK,CAAC,CAAC,CAAC,kBAAkB,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC;IACpE,CAAC;IACD,MAAM,0BAA0B,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IACtD,OAAO,UAAU,CAAC;AACpB,CAAC"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * The cached template for a backend, or null when this deployment serves none —
3
+ * and also null before the first `load` resolves. Both mean "use the path form",
4
+ * which is why they need no distinction at the call site.
5
+ */
6
+ export declare function cachedPreviewUrlTemplate(backendUrl: string): string | null;
7
+ /**
8
+ * Whether this backend has ANSWERED yet — as opposed to "answered null", which
9
+ * is a real answer meaning "this deployment serves no preview domain".
10
+ *
11
+ * The two look identical through `cachedPreviewUrlTemplate`, and conflating them
12
+ * is how a session gets stuck: during a rolling deploy the first `ensureReady()`
13
+ * can hit a task that predates `/v1/p/config`, and without this the handle would
14
+ * keep building path-proxy URLs for the rest of its life even though every later
15
+ * request would have been answered by a new task.
16
+ */
17
+ export declare function hasPreviewConfig(backendUrl: string): boolean;
18
+ /**
19
+ * Fetch and cache the template. Concurrent callers share one request; a failed
20
+ * fetch caches nothing, so a deployment that was briefly unreachable is asked
21
+ * again rather than being remembered as having no preview domain.
22
+ */
23
+ export declare function loadPreviewUrlTemplate(backendUrl: string): Promise<string | null>;
24
+ /** Drop everything remembered — for tests and for a host re-pointing its API. */
25
+ export declare function resetPreviewConfigCache(): void;
26
+ //# sourceMappingURL=preview-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview-config.d.ts","sourceRoot":"","sources":["../../../src/core/session/preview-config.ts"],"names":[],"mappings":"AAiCA;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAE1E;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAE5D;AAED;;;;GAIG;AACH,wBAAsB,sBAAsB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAgCvF;AAED,iFAAiF;AACjF,wBAAgB,uBAAuB,IAAI,IAAI,CAG9C"}
@@ -0,0 +1,95 @@
1
+ /**
2
+ * How this deployment addresses previews, fetched once from `GET /v1/p/config`.
3
+ *
4
+ * The preview hostname shape (`{env}-p{port}-{sandbox}.{domain}`) is a property
5
+ * of the deployment, not of the client: it depends on the wildcard domain the
6
+ * operator serves and on which environment the API is. Re-deriving it here from
7
+ * `backendUrl` would mean two implementations of one rule, which is how they
8
+ * drift. So the API states it as a template and this caches the answer.
9
+ *
10
+ * `previewUrl()` is synchronous — a React render calls it — so the fetch cannot
11
+ * happen there. `ensureReady()` warms this first, and until it resolves callers
12
+ * get the path form, which still works for everything that is not a browser.
13
+ */
14
+ import { backendApi } from '../http/api-client.js';
15
+ const templates = new Map();
16
+ const inFlight = new Map();
17
+ /**
18
+ * Cache key: the backend URL without trailing slashes, stripped LINEARLY. The
19
+ * regex form (`/\/+$/`) backtracks quadratically on adversarial input (CodeQL
20
+ * js/polynomial-redos), and this runs on a host-supplied string.
21
+ */
22
+ function key(backendUrl) {
23
+ let end = backendUrl.length;
24
+ while (end > 0 && backendUrl.charCodeAt(end - 1) === 47 /* '/' */)
25
+ end--;
26
+ return backendUrl.slice(0, end);
27
+ }
28
+ /**
29
+ * The cached template for a backend, or null when this deployment serves none —
30
+ * and also null before the first `load` resolves. Both mean "use the path form",
31
+ * which is why they need no distinction at the call site.
32
+ */
33
+ export function cachedPreviewUrlTemplate(backendUrl) {
34
+ return templates.get(key(backendUrl)) ?? null;
35
+ }
36
+ /**
37
+ * Whether this backend has ANSWERED yet — as opposed to "answered null", which
38
+ * is a real answer meaning "this deployment serves no preview domain".
39
+ *
40
+ * The two look identical through `cachedPreviewUrlTemplate`, and conflating them
41
+ * is how a session gets stuck: during a rolling deploy the first `ensureReady()`
42
+ * can hit a task that predates `/v1/p/config`, and without this the handle would
43
+ * keep building path-proxy URLs for the rest of its life even though every later
44
+ * request would have been answered by a new task.
45
+ */
46
+ export function hasPreviewConfig(backendUrl) {
47
+ return templates.has(key(backendUrl));
48
+ }
49
+ /**
50
+ * Fetch and cache the template. Concurrent callers share one request; a failed
51
+ * fetch caches nothing, so a deployment that was briefly unreachable is asked
52
+ * again rather than being remembered as having no preview domain.
53
+ */
54
+ export async function loadPreviewUrlTemplate(backendUrl) {
55
+ const cacheKey = key(backendUrl);
56
+ const cached = templates.get(cacheKey);
57
+ if (cached !== undefined)
58
+ return cached;
59
+ const pending = inFlight.get(cacheKey);
60
+ if (pending)
61
+ return pending;
62
+ const request = (async () => {
63
+ try {
64
+ const response = await backendApi.get('/p/config', {
65
+ showErrors: false,
66
+ // A deployment without a preview domain, and an API too old to know the
67
+ // route, are both ordinary answers — not incidents to report.
68
+ errorContext: { operation: 'load preview config', silent: true },
69
+ });
70
+ if (!response.success)
71
+ return null;
72
+ const template = typeof response.data?.preview_url_template === 'string'
73
+ ? response.data.preview_url_template
74
+ : null;
75
+ templates.set(cacheKey, template);
76
+ return template;
77
+ }
78
+ catch {
79
+ // An older API has no such route, and a network blip is not an answer.
80
+ // Leave the cache empty so the next ensureReady() asks again.
81
+ return null;
82
+ }
83
+ finally {
84
+ inFlight.delete(cacheKey);
85
+ }
86
+ })();
87
+ inFlight.set(cacheKey, request);
88
+ return request;
89
+ }
90
+ /** Drop everything remembered — for tests and for a host re-pointing its API. */
91
+ export function resetPreviewConfigCache() {
92
+ templates.clear();
93
+ inFlight.clear();
94
+ }
95
+ //# sourceMappingURL=preview-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview-config.js","sourceRoot":"","sources":["../../../src/core/session/preview-config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAMhD,MAAM,SAAS,GAAG,IAAI,GAAG,EAAyB,CAAC;AACnD,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAkC,CAAC;AAE3D;;;;GAIG;AACH,SAAS,GAAG,CAAC,UAAkB;IAC7B,IAAI,GAAG,GAAG,UAAU,CAAC,MAAM,CAAC;IAC5B,OAAO,GAAG,GAAG,CAAC,IAAI,UAAU,CAAC,UAAU,CAAC,GAAG,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,SAAS;QAAE,GAAG,EAAE,CAAC;IACzE,OAAO,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AAClC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,wBAAwB,CAAC,UAAkB;IACzD,OAAO,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,IAAI,IAAI,CAAC;AAChD,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,gBAAgB,CAAC,UAAkB;IACjD,OAAO,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC;AACxC,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAAC,UAAkB;IAC7D,MAAM,QAAQ,GAAG,GAAG,CAAC,UAAU,CAAC,CAAC;IACjC,MAAM,MAAM,GAAG,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACvC,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,MAAM,CAAC;IACxC,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACvC,IAAI,OAAO;QAAE,OAAO,OAAO,CAAC;IAE5B,MAAM,OAAO,GAAG,CAAC,KAAK,IAAI,EAAE;QAC1B,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,UAAU,CAAC,GAAG,CAAwB,WAAW,EAAE;gBACxE,UAAU,EAAE,KAAK;gBACjB,wEAAwE;gBACxE,8DAA8D;gBAC9D,YAAY,EAAE,EAAE,SAAS,EAAE,qBAAqB,EAAE,MAAM,EAAE,IAAI,EAAE;aACjE,CAAC,CAAC;YACH,IAAI,CAAC,QAAQ,CAAC,OAAO;gBAAE,OAAO,IAAI,CAAC;YACnC,MAAM,QAAQ,GAAG,OAAO,QAAQ,CAAC,IAAI,EAAE,oBAAoB,KAAK,QAAQ;gBACtE,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,oBAAoB;gBACpC,CAAC,CAAC,IAAI,CAAC;YACT,SAAS,CAAC,GAAG,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;YAClC,OAAO,QAAQ,CAAC;QAClB,CAAC;QAAC,MAAM,CAAC;YACP,uEAAuE;YACvE,8DAA8D;YAC9D,OAAO,IAAI,CAAC;QACd,CAAC;gBAAS,CAAC;YACT,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC,CAAC,EAAE,CAAC;IAEL,QAAQ,CAAC,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAChC,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,uBAAuB;IACrC,SAAS,CAAC,KAAK,EAAE,CAAC;IAClB,QAAQ,CAAC,KAAK,EAAE,CAAC;AACnB,CAAC"}
@@ -0,0 +1,25 @@
1
+ import type { SubdomainUrlOptions } from './url.js';
2
+ /**
3
+ * A fully-resolved options bag. Every field required — including
4
+ * `previewUrlTemplate`, whose `null` means "this deployment serves no preview
5
+ * domain" and is a real answer, not an omission.
6
+ */
7
+ export interface ResolvedPreviewOptions extends SubdomainUrlOptions {
8
+ previewUrlTemplate: string | null;
9
+ }
10
+ /**
11
+ * Resolve preview options for a sandbox against a backend.
12
+ *
13
+ * Reads the deployment's answer from the `GET /v1/p/config` cache, and asks
14
+ * again in the background when this backend has never answered — a rolling
15
+ * deploy can serve one request from a task that predates the route, and without
16
+ * the retry that single miss would pin the caller to the path proxy for its
17
+ * whole life. A backend that answered "no preview domain" HAS answered and is
18
+ * not re-asked.
19
+ */
20
+ export declare function resolvePreviewOptions(input: {
21
+ sandboxId: string;
22
+ apiBaseUrl: string;
23
+ backendPort: number;
24
+ }): ResolvedPreviewOptions;
25
+ //# sourceMappingURL=preview-options.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview-options.d.ts","sourceRoot":"","sources":["../../../src/core/session/preview-options.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,OAAO,CAAC;AAEjD;;;;GAIG;AACH,MAAM,WAAW,sBAAuB,SAAQ,mBAAmB;IACjE,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;CACnC;AAED;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE;IAC3C,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;CACrB,GAAG,sBAAsB,CASzB"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * The ONE way to build the options a preview URL is derived from.
3
+ *
4
+ * `rewriteLocalhostUrl` and its siblings in `url.ts` are pure: they answer
5
+ * "which URL" from an options bag and never touch the network. That purity is
6
+ * worth keeping — it is why they are exhaustively testable — but it puts the
7
+ * burden of assembling a COMPLETE bag on whoever calls them, and an incomplete
8
+ * bag does not fail. It falls back to the path proxy, silently, forever.
9
+ *
10
+ * That is not hypothetical. There are two call paths — a session handle
11
+ * (`kortix.session(...).previewUrl()`) and the ambient runtime
12
+ * (`deriveSubdomainOpts`, which the web app's preview iframe, `useSandboxProxy`
13
+ * and `buildStaticFilePreviewUrl` all use) — and each assembled the bag itself.
14
+ * When `previewUrlTemplate` was added, only the first was updated, so every
15
+ * preview in the session panel kept rendering `/v1/p/{sandbox}/{port}` however
16
+ * loudly the deployment advertised an origin. Nothing failed; the wrong thing
17
+ * merely kept working.
18
+ *
19
+ * So: one producer, and a return type whose fields are all REQUIRED. A future
20
+ * call path that forgets a field does not compile, which is the only kind of
21
+ * guarantee worth having for a failure mode this quiet.
22
+ */
23
+ import { cachedPreviewUrlTemplate, hasPreviewConfig, loadPreviewUrlTemplate } from './preview-config.js';
24
+ /**
25
+ * Resolve preview options for a sandbox against a backend.
26
+ *
27
+ * Reads the deployment's answer from the `GET /v1/p/config` cache, and asks
28
+ * again in the background when this backend has never answered — a rolling
29
+ * deploy can serve one request from a task that predates the route, and without
30
+ * the retry that single miss would pin the caller to the path proxy for its
31
+ * whole life. A backend that answered "no preview domain" HAS answered and is
32
+ * not re-asked.
33
+ */
34
+ export function resolvePreviewOptions(input) {
35
+ const { apiBaseUrl } = input;
36
+ if (!hasPreviewConfig(apiBaseUrl))
37
+ void loadPreviewUrlTemplate(apiBaseUrl).catch(() => { });
38
+ return {
39
+ sandboxId: input.sandboxId,
40
+ backendPort: input.backendPort,
41
+ apiBaseUrl,
42
+ previewUrlTemplate: cachedPreviewUrlTemplate(apiBaseUrl),
43
+ };
44
+ }
45
+ //# sourceMappingURL=preview-options.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview-options.js","sourceRoot":"","sources":["../../../src/core/session/preview-options.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,EAAE,wBAAwB,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAYtG;;;;;;;;;GASG;AACH,MAAM,UAAU,qBAAqB,CAAC,KAIrC;IACC,MAAM,EAAE,UAAU,EAAE,GAAG,KAAK,CAAC;IAC7B,IAAI,CAAC,gBAAgB,CAAC,UAAU,CAAC;QAAE,KAAK,sBAAsB,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IAC3F,OAAO;QACL,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,UAAU;QACV,kBAAkB,EAAE,wBAAwB,CAAC,UAAU,CAAC;KACzD,CAAC;AACJ,CAAC"}
@@ -14,9 +14,14 @@
14
14
  */
15
15
  export declare function buildPreviewAuthEndpoint(previewUrl: string, serverUrl?: string): string | null;
16
16
  /**
17
- * Subdomain preview form: `p{port}-{sandbox}.{host}/…`. These can't use the
18
- * host-only `/v1/p/` session cookie (it never reaches the preview subdomain),
19
- * so they authenticate via a one-shot `?token` on the first request.
17
+ * Preview ORIGIN form: `p{port}-{sandbox}.{host}` locally, or the deployed
18
+ * `{env}-p{port}-{sandbox}.{domain}`.
19
+ *
20
+ * These can't use the host-only `/v1/p/` session cookie — it never reaches
21
+ * another hostname — so they authenticate the FIRST request with a one-shot
22
+ * `?token`, which the proxy exchanges for a cookie on the preview origin and
23
+ * then strips. Every later request (sub-resources, `fetch`, WebSockets) rides
24
+ * that cookie, which is the only credential an app's own code can carry.
20
25
  */
21
26
  export declare function isSubdomainPreviewUrl(candidateUrl: string): boolean;
22
27
  /** Append a one-shot `?token` to a (subdomain) preview URL. */