@pyric/cli 0.1.0-alpha.19 → 0.1.0-alpha.21

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 (642) hide show
  1. package/README.md +64 -89
  2. package/dist/assurance/.generated/conformance-verdicts.d.ts +68 -1
  3. package/dist/assurance/.generated/conformance-verdicts.d.ts.map +1 -1
  4. package/dist/assurance/.generated/conformance-verdicts.js +68 -1
  5. package/dist/assurance/.generated/conformance-verdicts.js.map +1 -1
  6. package/dist/bridge/client/dispatch.d.ts +25 -19
  7. package/dist/bridge/client/dispatch.d.ts.map +1 -1
  8. package/dist/bridge/client/dispatch.js +41 -48
  9. package/dist/bridge/client/dispatch.js.map +1 -1
  10. package/dist/bridge/client/tool-family-factories.d.ts +26 -0
  11. package/dist/bridge/client/tool-family-factories.d.ts.map +1 -0
  12. package/dist/bridge/client/tool-family-factories.js +10 -0
  13. package/dist/bridge/client/tool-family-factories.js.map +1 -0
  14. package/dist/bridge/server/mcp-contract.d.ts +7 -6
  15. package/dist/bridge/server/mcp-contract.d.ts.map +1 -1
  16. package/dist/bridge/server/mcp-contract.js +9 -48
  17. package/dist/bridge/server/mcp-contract.js.map +1 -1
  18. package/dist/bridge/server/peer.d.ts +11 -1
  19. package/dist/bridge/server/peer.d.ts.map +1 -1
  20. package/dist/bridge/server/peer.js +22 -1
  21. package/dist/bridge/server/peer.js.map +1 -1
  22. package/dist/bridge/server/tool-family-factories.d.ts +30 -0
  23. package/dist/bridge/server/tool-family-factories.d.ts.map +1 -0
  24. package/dist/bridge/server/tool-family-factories.js +22 -0
  25. package/dist/bridge/server/tool-family-factories.js.map +1 -0
  26. package/dist/bridge/server/tool-metadata.d.ts +17 -18
  27. package/dist/bridge/server/tool-metadata.d.ts.map +1 -1
  28. package/dist/bridge/server/tool-metadata.js +24 -59
  29. package/dist/bridge/server/tool-metadata.js.map +1 -1
  30. package/dist/bridge/tool-families.d.ts +48 -0
  31. package/dist/bridge/tool-families.d.ts.map +1 -0
  32. package/dist/bridge/tool-families.generated.d.ts +32 -0
  33. package/dist/bridge/tool-families.generated.d.ts.map +1 -0
  34. package/dist/bridge/tool-families.generated.js +15 -0
  35. package/dist/bridge/tool-families.generated.js.map +1 -0
  36. package/dist/bridge/tool-families.js +51 -0
  37. package/dist/bridge/tool-families.js.map +1 -0
  38. package/dist/bridge/tool-family-records/conformance.d.ts +7 -0
  39. package/dist/bridge/tool-family-records/conformance.d.ts.map +1 -0
  40. package/dist/bridge/tool-family-records/conformance.js +6 -0
  41. package/dist/bridge/tool-family-records/conformance.js.map +1 -0
  42. package/dist/bridge/tool-family-records/firestore-data.d.ts +7 -0
  43. package/dist/bridge/tool-family-records/firestore-data.d.ts.map +1 -0
  44. package/dist/bridge/tool-family-records/firestore-data.js +15 -0
  45. package/dist/bridge/tool-family-records/firestore-data.js.map +1 -0
  46. package/dist/bridge/tool-family-records/firestore-inspect.d.ts +7 -0
  47. package/dist/bridge/tool-family-records/firestore-inspect.d.ts.map +1 -0
  48. package/dist/bridge/tool-family-records/firestore-inspect.js +6 -0
  49. package/dist/bridge/tool-family-records/firestore-inspect.js.map +1 -0
  50. package/dist/bridge/tool-family-records/firestore-rules.d.ts +7 -0
  51. package/dist/bridge/tool-family-records/firestore-rules.d.ts.map +1 -0
  52. package/dist/bridge/tool-family-records/firestore-rules.js +15 -0
  53. package/dist/bridge/tool-family-records/firestore-rules.js.map +1 -0
  54. package/dist/bridge/tool-family-records/firestore-simulator.d.ts +7 -0
  55. package/dist/bridge/tool-family-records/firestore-simulator.d.ts.map +1 -0
  56. package/dist/bridge/tool-family-records/firestore-simulator.js +16 -0
  57. package/dist/bridge/tool-family-records/firestore-simulator.js.map +1 -0
  58. package/dist/bridge/tool-family-records/rtdb-inspection.d.ts +7 -0
  59. package/dist/bridge/tool-family-records/rtdb-inspection.d.ts.map +1 -0
  60. package/dist/bridge/tool-family-records/rtdb-inspection.js +6 -0
  61. package/dist/bridge/tool-family-records/rtdb-inspection.js.map +1 -0
  62. package/dist/bundler/externals.d.ts +37 -0
  63. package/dist/bundler/externals.d.ts.map +1 -0
  64. package/dist/bundler/externals.js +52 -0
  65. package/dist/bundler/externals.js.map +1 -0
  66. package/dist/bundler/index.d.ts +17 -0
  67. package/dist/bundler/index.d.ts.map +1 -0
  68. package/dist/bundler/index.js +17 -0
  69. package/dist/bundler/index.js.map +1 -0
  70. package/dist/cli/index.d.ts +2 -2
  71. package/dist/cli/index.d.ts.map +1 -1
  72. package/dist/cli/index.js +48 -42
  73. package/dist/cli/index.js.map +1 -1
  74. package/dist/cli/inlined-sdk-scanner.d.ts +6 -0
  75. package/dist/cli/inlined-sdk-scanner.d.ts.map +1 -0
  76. package/dist/cli/inlined-sdk-scanner.js +82 -0
  77. package/dist/cli/inlined-sdk-scanner.js.map +1 -0
  78. package/dist/cli/mcp-proxy.js +3 -3
  79. package/dist/cli/mcp-proxy.js.map +1 -1
  80. package/dist/cli/parse-args.d.ts +1 -1
  81. package/dist/cli/parse-args.d.ts.map +1 -1
  82. package/dist/cli/parse-args.js +51 -15
  83. package/dist/cli/parse-args.js.map +1 -1
  84. package/dist/cli/pyric-config.d.ts +24 -0
  85. package/dist/cli/pyric-config.d.ts.map +1 -0
  86. package/dist/cli/pyric-config.js +28 -0
  87. package/dist/cli/pyric-config.js.map +1 -0
  88. package/dist/cli/sandbox-interlock.d.ts +97 -0
  89. package/dist/cli/sandbox-interlock.d.ts.map +1 -0
  90. package/dist/cli/sandbox-interlock.js +112 -0
  91. package/dist/cli/sandbox-interlock.js.map +1 -0
  92. package/dist/cli/{dev-runner.d.ts → sandbox-runner.d.ts} +53 -48
  93. package/dist/cli/sandbox-runner.d.ts.map +1 -0
  94. package/dist/cli/{dev-runner.js → sandbox-runner.js} +105 -67
  95. package/dist/cli/sandbox-runner.js.map +1 -0
  96. package/dist/cli/serve.d.ts +12 -14
  97. package/dist/cli/serve.d.ts.map +1 -1
  98. package/dist/cli/serve.js +155 -116
  99. package/dist/cli/serve.js.map +1 -1
  100. package/dist/cli/snapshot.js +5 -5
  101. package/dist/cli/snapshot.js.map +1 -1
  102. package/dist/cli/unsupported-runtime.d.ts +27 -0
  103. package/dist/cli/unsupported-runtime.d.ts.map +1 -0
  104. package/dist/cli/unsupported-runtime.js +63 -0
  105. package/dist/cli/unsupported-runtime.js.map +1 -0
  106. package/dist/cli/verify.js +1 -1
  107. package/dist/cli/verify.js.map +1 -1
  108. package/dist/conformance/.generated/can-i-use-browser.d.ts +1 -1
  109. package/dist/conformance/.generated/can-i-use-browser.d.ts.map +1 -1
  110. package/dist/conformance/.generated/can-i-use-browser.js +1 -1
  111. package/dist/conformance/.generated/can-i-use-browser.js.map +1 -1
  112. package/dist/conformance/.generated/can-i-use.d.ts +1 -1
  113. package/dist/conformance/.generated/can-i-use.d.ts.map +1 -1
  114. package/dist/conformance/.generated/can-i-use.js +1 -1
  115. package/dist/conformance/.generated/can-i-use.js.map +1 -1
  116. package/dist/conformance/.generated/conformance-docs.d.ts.map +1 -1
  117. package/dist/conformance/.generated/conformance-docs.js +1 -1
  118. package/dist/conformance/.generated/conformance-docs.js.map +1 -1
  119. package/dist/functions-rtdb/child.d.ts +37 -0
  120. package/dist/functions-rtdb/child.d.ts.map +1 -1
  121. package/dist/functions-rtdb/child.js +90 -21
  122. package/dist/functions-rtdb/child.js.map +1 -1
  123. package/dist/functions-rtdb/development-runtime.d.ts +3 -0
  124. package/dist/functions-rtdb/development-runtime.d.ts.map +1 -1
  125. package/dist/functions-rtdb/development-runtime.js +2 -1
  126. package/dist/functions-rtdb/development-runtime.js.map +1 -1
  127. package/dist/functions-rtdb/project.js +8 -8
  128. package/dist/functions-rtdb/project.js.map +1 -1
  129. package/dist/google-endpoints.d.ts +67 -0
  130. package/dist/google-endpoints.d.ts.map +1 -0
  131. package/dist/google-endpoints.js +119 -0
  132. package/dist/google-endpoints.js.map +1 -0
  133. package/dist/next/guard.js +1 -1
  134. package/dist/next/guard.js.map +1 -1
  135. package/dist/next/runtime-env.d.ts.map +1 -1
  136. package/dist/next/runtime-env.js +5 -0
  137. package/dist/next/runtime-env.js.map +1 -1
  138. package/dist/next/with-pyric.d.ts +1 -1
  139. package/dist/next/with-pyric.js +1 -1
  140. package/dist/register/beacon.d.ts +69 -0
  141. package/dist/register/beacon.d.ts.map +1 -0
  142. package/dist/register/beacon.js +199 -0
  143. package/dist/register/beacon.js.map +1 -0
  144. package/dist/register/index.d.ts.map +1 -1
  145. package/dist/register/index.js +65 -6
  146. package/dist/register/index.js.map +1 -1
  147. package/dist/register/net-guard.d.ts +110 -0
  148. package/dist/register/net-guard.d.ts.map +1 -0
  149. package/dist/register/net-guard.js +462 -0
  150. package/dist/register/net-guard.js.map +1 -0
  151. package/dist/remote/index.d.ts +1 -1
  152. package/dist/remote/index.js +9 -9
  153. package/dist/remote/index.js.map +1 -1
  154. package/dist/serve/ai-diagnostics-relay.d.ts +63 -0
  155. package/dist/serve/ai-diagnostics-relay.d.ts.map +1 -0
  156. package/dist/serve/ai-diagnostics-relay.js +125 -0
  157. package/dist/serve/ai-diagnostics-relay.js.map +1 -0
  158. package/dist/serve/ai-proxy.d.ts +92 -0
  159. package/dist/serve/ai-proxy.d.ts.map +1 -0
  160. package/dist/serve/ai-proxy.js +269 -0
  161. package/dist/serve/ai-proxy.js.map +1 -0
  162. package/dist/serve/ai-status.d.ts +44 -0
  163. package/dist/serve/ai-status.d.ts.map +1 -0
  164. package/dist/serve/ai-status.js +77 -0
  165. package/dist/serve/ai-status.js.map +1 -0
  166. package/dist/serve/ai-terminal-blocks.d.ts +97 -0
  167. package/dist/serve/ai-terminal-blocks.d.ts.map +1 -0
  168. package/dist/serve/ai-terminal-blocks.js +128 -0
  169. package/dist/serve/ai-terminal-blocks.js.map +1 -0
  170. package/dist/serve/ai-terminal-text.d.ts +36 -0
  171. package/dist/serve/ai-terminal-text.d.ts.map +1 -0
  172. package/dist/serve/ai-terminal-text.js +48 -0
  173. package/dist/serve/ai-terminal-text.js.map +1 -0
  174. package/dist/serve/beacon-route.d.ts +53 -0
  175. package/dist/serve/beacon-route.d.ts.map +1 -0
  176. package/dist/serve/beacon-route.js +101 -0
  177. package/dist/serve/beacon-route.js.map +1 -0
  178. package/dist/serve/bundler.js +10 -10
  179. package/dist/serve/bundler.js.map +1 -1
  180. package/dist/serve/entries/active-auth-registry.d.ts +16 -0
  181. package/dist/serve/entries/active-auth-registry.d.ts.map +1 -0
  182. package/dist/serve/entries/active-auth-registry.js +48 -0
  183. package/dist/serve/entries/active-auth-registry.js.map +1 -0
  184. package/dist/serve/entries/active-auth.d.ts +18 -0
  185. package/dist/serve/entries/active-auth.d.ts.map +1 -0
  186. package/dist/serve/entries/active-auth.js +84 -0
  187. package/dist/serve/entries/active-auth.js.map +1 -0
  188. package/dist/serve/entries/ai.d.ts.map +1 -1
  189. package/dist/serve/entries/ai.js +27 -1
  190. package/dist/serve/entries/ai.js.map +1 -1
  191. package/dist/serve/entries/auth-helper-core.d.ts +1 -0
  192. package/dist/serve/entries/auth-helper-core.d.ts.map +1 -1
  193. package/dist/serve/entries/auth-helper-core.js +4 -0
  194. package/dist/serve/entries/auth-helper-core.js.map +1 -1
  195. package/dist/serve/entries/auth-helper-dom.d.ts.map +1 -1
  196. package/dist/serve/entries/auth-helper-dom.js +29 -3
  197. package/dist/serve/entries/auth-helper-dom.js.map +1 -1
  198. package/dist/serve/entries/auth-helper-runtime.js +1 -1
  199. package/dist/serve/entries/auth-helper-runtime.js.map +1 -1
  200. package/dist/serve/entries/auth.d.ts.map +1 -1
  201. package/dist/serve/entries/auth.js +8 -0
  202. package/dist/serve/entries/auth.js.map +1 -1
  203. package/dist/serve/entries/bridge-url.d.ts +1 -1
  204. package/dist/serve/entries/bridge-url.js +1 -1
  205. package/dist/serve/entries/firestore.js +3 -3
  206. package/dist/serve/entries/firestore.js.map +1 -1
  207. package/dist/serve/entries/init.d.ts.map +1 -1
  208. package/dist/serve/entries/init.js +70 -4
  209. package/dist/serve/entries/init.js.map +1 -1
  210. package/dist/serve/entries/keepalive.js +1 -1
  211. package/dist/serve/entries/keepalive.js.map +1 -1
  212. package/dist/serve/entries/runtime.d.ts.map +1 -1
  213. package/dist/serve/entries/runtime.js +30 -14
  214. package/dist/serve/entries/runtime.js.map +1 -1
  215. package/dist/serve/entries/storage.js +1 -1
  216. package/dist/serve/entries/storage.js.map +1 -1
  217. package/dist/serve/entries/worker-runtime.js +1 -1
  218. package/dist/serve/entries/worker-runtime.js.map +1 -1
  219. package/dist/serve/init-payload.d.ts +4 -0
  220. package/dist/serve/init-payload.d.ts.map +1 -1
  221. package/dist/serve/namespace.d.ts +20 -5
  222. package/dist/serve/namespace.d.ts.map +1 -1
  223. package/dist/serve/namespace.js +93 -129
  224. package/dist/serve/namespace.js.map +1 -1
  225. package/dist/serve/rules.d.ts.map +1 -1
  226. package/dist/serve/rules.js +44 -21
  227. package/dist/serve/rules.js.map +1 -1
  228. package/dist/serve/runtime/chip-config.d.ts.map +1 -1
  229. package/dist/serve/runtime/chip-config.js +16 -2
  230. package/dist/serve/runtime/chip-config.js.map +1 -1
  231. package/dist/serve/runtime/chip-dialog.d.ts +18 -0
  232. package/dist/serve/runtime/chip-dialog.d.ts.map +1 -0
  233. package/dist/serve/runtime/chip-dialog.js +315 -0
  234. package/dist/serve/runtime/chip-dialog.js.map +1 -0
  235. package/dist/serve/runtime/chip-install.d.ts +2 -0
  236. package/dist/serve/runtime/chip-install.d.ts.map +1 -1
  237. package/dist/serve/runtime/chip-install.js +9 -4
  238. package/dist/serve/runtime/chip-install.js.map +1 -1
  239. package/dist/serve/runtime/chip-user-search.d.ts +16 -0
  240. package/dist/serve/runtime/chip-user-search.d.ts.map +1 -0
  241. package/dist/serve/runtime/chip-user-search.js +354 -0
  242. package/dist/serve/runtime/chip-user-search.js.map +1 -0
  243. package/dist/serve/runtime/chip.d.ts +10 -0
  244. package/dist/serve/runtime/chip.d.ts.map +1 -1
  245. package/dist/serve/runtime/chip.js +149 -15
  246. package/dist/serve/runtime/chip.js.map +1 -1
  247. package/dist/serve/runtime/identity.d.ts +17 -0
  248. package/dist/serve/runtime/identity.d.ts.map +1 -0
  249. package/dist/serve/runtime/identity.js +10 -0
  250. package/dist/serve/runtime/identity.js.map +1 -0
  251. package/dist/serve/runtime/manifest.d.ts +1 -1
  252. package/dist/serve/runtime/manifest.d.ts.map +1 -1
  253. package/dist/serve/runtime/manifest.js +1 -1
  254. package/dist/serve/runtime/manifest.js.map +1 -1
  255. package/dist/serve/runtime/status.d.ts.map +1 -1
  256. package/dist/serve/runtime/status.js +23 -0
  257. package/dist/serve/runtime/status.js.map +1 -1
  258. package/dist/serve/sandbox-session.d.ts +8 -0
  259. package/dist/serve/sandbox-session.d.ts.map +1 -1
  260. package/dist/serve/sandbox-session.js +12 -4
  261. package/dist/serve/sandbox-session.js.map +1 -1
  262. package/dist/serve/server.d.ts +7 -0
  263. package/dist/serve/server.d.ts.map +1 -1
  264. package/dist/serve/server.js +15 -1
  265. package/dist/serve/server.js.map +1 -1
  266. package/dist/serve/site-ui/404.html +1 -1
  267. package/dist/serve/site-ui/_astro/{RulesCodeEditor.Bl-XMkhD.js → RulesCodeEditor.DsTAsKIa.js} +1 -1
  268. package/dist/serve/site-ui/_astro/_slug_.Da5VLhW1.css +1 -0
  269. package/dist/serve/site-ui/_astro/_studio_.BnfYCKoI.css +1 -0
  270. package/dist/serve/site-ui/_astro/app.BOr2kwae.js +115 -0
  271. package/dist/serve/site-ui/_astro/app.DhAcImFD.js +1 -0
  272. package/dist/serve/site-ui/_astro/app.pMSnN6Qw.css +1 -0
  273. package/dist/serve/site-ui/_astro/{chess-showcase.Dh5D8kqe.js → chess-showcase.Bn_pP6UJ.js} +2 -2
  274. package/dist/serve/site-ui/_astro/{example-runtime.CuyAjjiG.js → example-runtime.CXopTGv7.js} +1 -1
  275. package/dist/serve/site-ui/_astro/index.Tb-31efc.css +1 -0
  276. package/dist/serve/site-ui/_astro/index.UkCBr-_0.css +1 -0
  277. package/dist/serve/site-ui/_astro/sandbox-controls.rOuZTOUG.js +510 -0
  278. package/dist/serve/site-ui/_astro/{seed.CSUkloMi.js → seed.03a08f9t.js} +2 -2
  279. package/dist/serve/site-ui/_astro/writes.BsPBG3dp.js +653 -0
  280. package/dist/serve/site-ui/auth/index.html +5 -3
  281. package/dist/serve/site-ui/david-east.jpg +0 -0
  282. package/dist/serve/site-ui/docs/_rhythm/index.html +6 -3
  283. package/dist/serve/site-ui/docs/_rhythm.md +1 -1
  284. package/dist/serve/site-ui/docs/agent/set-up-your-agent/index.html +18 -15
  285. package/dist/serve/site-ui/docs/agent/set-up-your-agent.md +16 -16
  286. package/dist/serve/site-ui/docs/agent/watch-and-review/index.html +6 -5
  287. package/dist/serve/site-ui/docs/agent/watch-and-review.md +1 -5
  288. package/dist/serve/site-ui/docs/agent/work-with-an-agent/index.html +8 -5
  289. package/dist/serve/site-ui/docs/agent/work-with-an-agent.md +4 -4
  290. package/dist/serve/site-ui/docs/ai-compat/index.html +5 -2
  291. package/dist/serve/site-ui/docs/api-reference/index.html +13 -2
  292. package/dist/serve/site-ui/docs/api-reference.md +8 -0
  293. package/dist/serve/site-ui/docs/app-compat/index.html +5 -2
  294. package/dist/serve/site-ui/docs/auth-compat/index.html +7 -4
  295. package/dist/serve/site-ui/docs/auth-compat.md +2 -2
  296. package/dist/serve/site-ui/docs/build/ai-logic/index.html +10 -2
  297. package/dist/serve/site-ui/docs/build/ai-logic.md +6 -0
  298. package/dist/serve/site-ui/docs/build/authentication/index.html +7 -4
  299. package/dist/serve/site-ui/docs/build/authentication.md +2 -2
  300. package/dist/serve/site-ui/docs/build/cloud-firestore/index.html +5 -2
  301. package/dist/serve/site-ui/docs/build/cloud-messaging/index.html +5 -2
  302. package/dist/serve/site-ui/docs/build/cloud-storage/index.html +6 -3
  303. package/dist/serve/site-ui/docs/build/cloud-storage.md +1 -1
  304. package/dist/serve/site-ui/docs/build/realtime-database/index.html +9 -6
  305. package/dist/serve/site-ui/docs/build/realtime-database.md +4 -4
  306. package/dist/serve/site-ui/docs/conformance-scores/index.html +10 -3
  307. package/dist/serve/site-ui/docs/conformance-scores.md +5 -1
  308. package/dist/serve/site-ui/docs/create-pyric-reference-api/index.html +6 -3
  309. package/dist/serve/site-ui/docs/create-pyric-reference-api.md +1 -1
  310. package/dist/serve/site-ui/docs/database-compat/index.html +48 -45
  311. package/dist/serve/site-ui/docs/database-compat.md +43 -43
  312. package/dist/serve/site-ui/docs/examples/chess/index.html +5 -2
  313. package/dist/serve/site-ui/docs/examples/index.html +5 -2
  314. package/dist/serve/site-ui/docs/firestore-compat/index.html +8 -5
  315. package/dist/serve/site-ui/docs/firestore-compat.md +3 -3
  316. package/dist/serve/site-ui/docs/firestore-flutter-compat/index.html +319 -0
  317. package/dist/serve/site-ui/docs/firestore-flutter-compat.md +344 -0
  318. package/dist/serve/site-ui/docs/functions-rtdb-compat/index.html +5 -2
  319. package/dist/serve/site-ui/docs/get-started/backend-bundlers/index.html +105 -0
  320. package/dist/serve/site-ui/docs/get-started/backend-bundlers.md +165 -0
  321. package/dist/serve/site-ui/docs/get-started/how-the-swap-works/index.html +9 -6
  322. package/dist/serve/site-ui/docs/get-started/how-the-swap-works.md +5 -5
  323. package/dist/serve/site-ui/docs/get-started/nextjs/index.html +9 -6
  324. package/dist/serve/site-ui/docs/get-started/nextjs.md +4 -4
  325. package/dist/serve/site-ui/docs/get-started/start-building/index.html +14 -9
  326. package/dist/serve/site-ui/docs/get-started/start-building.md +16 -10
  327. package/dist/serve/site-ui/docs/get-started/vite/index.html +6 -3
  328. package/dist/serve/site-ui/docs/get-started/vite.md +1 -1
  329. package/dist/serve/site-ui/docs/index.json +3128 -1613
  330. package/dist/serve/site-ui/docs/messaging-compat/index.html +5 -2
  331. package/dist/serve/site-ui/docs/observe/resolve-runtime-status/index.html +5 -2
  332. package/dist/serve/site-ui/docs/observe/see-whats-happening/index.html +6 -4
  333. package/dist/serve/site-ui/docs/observe/see-whats-happening.md +2 -4
  334. package/dist/serve/site-ui/docs/observe/shape-your-data/index.html +9 -6
  335. package/dist/serve/site-ui/docs/observe/shape-your-data.md +4 -4
  336. package/dist/serve/site-ui/docs/overview/index.html +16 -10
  337. package/dist/serve/site-ui/docs/overview.md +19 -11
  338. package/dist/serve/site-ui/docs/pyric-admin-app-reference-api/index.html +5 -2
  339. package/dist/serve/site-ui/docs/pyric-admin-auth-reference-api/index.html +5 -2
  340. package/dist/serve/site-ui/docs/pyric-admin-database-reference-api/index.html +5 -2
  341. package/dist/serve/site-ui/docs/pyric-admin-firestore-reference-api/index.html +8 -5
  342. package/dist/serve/site-ui/docs/pyric-admin-firestore-reference-api.md +3 -3
  343. package/dist/serve/site-ui/docs/pyric-admin-messaging-reference-api/index.html +5 -2
  344. package/dist/serve/site-ui/docs/pyric-admin-reference-api/index.html +501 -0
  345. package/dist/serve/site-ui/docs/pyric-admin-reference-api.md +587 -0
  346. package/dist/serve/site-ui/docs/pyric-admin-storage-reference-api/index.html +5 -2
  347. package/dist/serve/site-ui/docs/pyric-ai-reference-api/index.html +5 -2
  348. package/dist/serve/site-ui/docs/pyric-ai-scripting-reference-api/index.html +5 -2
  349. package/dist/serve/site-ui/docs/pyric-analytics-reference-api/index.html +124 -0
  350. package/dist/serve/site-ui/docs/pyric-analytics-reference-api.md +287 -0
  351. package/dist/serve/site-ui/docs/pyric-app-check-reference-api/index.html +87 -0
  352. package/dist/serve/site-ui/docs/pyric-app-check-reference-api.md +199 -0
  353. package/dist/serve/site-ui/docs/pyric-app-reference-api/index.html +5 -2
  354. package/dist/serve/site-ui/docs/pyric-auth-reference-api/index.html +10 -3
  355. package/dist/serve/site-ui/docs/pyric-auth-reference-api.md +2 -1
  356. package/dist/serve/site-ui/docs/pyric-cli-assurance-browser-reference-api/index.html +5 -2
  357. package/dist/serve/site-ui/docs/pyric-cli-assurance-reference-api/index.html +5 -2
  358. package/dist/serve/site-ui/docs/pyric-cli-bridge-client-reference-api/index.html +6 -3
  359. package/dist/serve/site-ui/docs/pyric-cli-bridge-client-reference-api.md +1 -1
  360. package/dist/serve/site-ui/docs/pyric-cli-bridge-reference-api/index.html +5 -2
  361. package/dist/serve/site-ui/docs/pyric-cli-bundler-reference-api/index.html +56 -0
  362. package/dist/serve/site-ui/docs/pyric-cli-bundler-reference-api.md +72 -0
  363. package/dist/serve/site-ui/docs/pyric-cli-conformance-browser-reference-api/index.html +6 -2
  364. package/dist/serve/site-ui/docs/pyric-cli-conformance-browser-reference-api.md +1 -0
  365. package/dist/serve/site-ui/docs/pyric-cli-conformance-docs-reference-api/index.html +5 -2
  366. package/dist/serve/site-ui/docs/pyric-cli-conformance-reference-api/index.html +6 -2
  367. package/dist/serve/site-ui/docs/pyric-cli-conformance-reference-api.md +1 -0
  368. package/dist/serve/site-ui/docs/pyric-cli-credentials-node-reference-api/index.html +5 -2
  369. package/dist/serve/site-ui/docs/pyric-cli-discover-reference-api/index.html +5 -2
  370. package/dist/serve/site-ui/docs/pyric-cli-next-reference-api/index.html +6 -3
  371. package/dist/serve/site-ui/docs/pyric-cli-next-reference-api.md +1 -1
  372. package/dist/serve/site-ui/docs/pyric-cli-register-app-bridge-reference-api/index.html +5 -2
  373. package/dist/serve/site-ui/docs/pyric-cli-register-reference-api/index.html +5 -2
  374. package/dist/serve/site-ui/docs/pyric-cli-remote-reference-api/index.html +39 -11
  375. package/dist/serve/site-ui/docs/pyric-cli-remote-reference-api.md +17 -9
  376. package/dist/serve/site-ui/docs/pyric-cli-serve-worker-reference-api/index.html +137 -94
  377. package/dist/serve/site-ui/docs/pyric-cli-serve-worker-reference-api.md +61 -1
  378. package/dist/serve/site-ui/docs/pyric-cli-verify-reference-api/index.html +6 -3
  379. package/dist/serve/site-ui/docs/pyric-cli-verify-reference-api.md +1 -1
  380. package/dist/serve/site-ui/docs/pyric-cli-vite-reference-api/index.html +6 -3
  381. package/dist/serve/site-ui/docs/pyric-cli-vite-reference-api.md +2 -2
  382. package/dist/serve/site-ui/docs/pyric-database-reference-api/index.html +167 -96
  383. package/dist/serve/site-ui/docs/pyric-database-reference-api.md +108 -2
  384. package/dist/serve/site-ui/docs/pyric-firestore-lite-reference-api/index.html +559 -0
  385. package/dist/serve/site-ui/docs/pyric-firestore-lite-reference-api.md +1240 -0
  386. package/dist/serve/site-ui/docs/pyric-firestore-reference-api/index.html +44 -12
  387. package/dist/serve/site-ui/docs/pyric-firestore-reference-api.md +18 -8
  388. package/dist/serve/site-ui/docs/pyric-functions-reference-api/index.html +67 -0
  389. package/dist/serve/site-ui/docs/pyric-functions-reference-api.md +149 -0
  390. package/dist/serve/site-ui/docs/pyric-messaging-reference-api/index.html +10 -4
  391. package/dist/serve/site-ui/docs/pyric-messaging-reference-api.md +5 -2
  392. package/dist/serve/site-ui/docs/pyric-messaging-sw-reference-api/index.html +10 -4
  393. package/dist/serve/site-ui/docs/pyric-messaging-sw-reference-api.md +5 -2
  394. package/dist/serve/site-ui/docs/pyric-performance-reference-api/index.html +35 -0
  395. package/dist/serve/site-ui/docs/pyric-performance-reference-api.md +69 -0
  396. package/dist/serve/site-ui/docs/pyric-remote-config-reference-api/index.html +132 -0
  397. package/dist/serve/site-ui/docs/pyric-remote-config-reference-api.md +307 -0
  398. package/dist/serve/site-ui/docs/pyric-rules-reference-api/index.html +5 -2
  399. package/dist/serve/site-ui/docs/pyric-sandbox-database-reference-api/index.html +56 -18
  400. package/dist/serve/site-ui/docs/pyric-sandbox-database-reference-api.md +48 -9
  401. package/dist/serve/site-ui/docs/pyric-sandbox-firestore-reference-api/index.html +6 -3
  402. package/dist/serve/site-ui/docs/pyric-sandbox-firestore-reference-api.md +2 -2
  403. package/dist/serve/site-ui/docs/pyric-sandbox-reference-api/index.html +243 -50
  404. package/dist/serve/site-ui/docs/pyric-sandbox-reference-api.md +148 -31
  405. package/dist/serve/site-ui/docs/pyric-storage-reference-api/index.html +26 -5
  406. package/dist/serve/site-ui/docs/pyric-storage-reference-api.md +24 -2
  407. package/dist/serve/site-ui/docs/pyric-ui-agents-reference-api/index.html +30 -97
  408. package/dist/serve/site-ui/docs/pyric-ui-agents-reference-api.md +20 -51
  409. package/dist/serve/site-ui/docs/pyric-ui-auth-hooks-reference-api/index.html +5 -2
  410. package/dist/serve/site-ui/docs/pyric-ui-auth-reference-api/index.html +6 -3
  411. package/dist/serve/site-ui/docs/pyric-ui-auth-reference-api.md +1 -1
  412. package/dist/serve/site-ui/docs/pyric-ui-events-hooks-reference-api/index.html +5 -2
  413. package/dist/serve/site-ui/docs/pyric-ui-events-reference-api/index.html +5 -2
  414. package/dist/serve/site-ui/docs/pyric-ui-firestore-hooks-reference-api/index.html +5 -2
  415. package/dist/serve/site-ui/docs/pyric-ui-firestore-reference-api/index.html +6 -3
  416. package/dist/serve/site-ui/docs/pyric-ui-firestore-reference-api.md +1 -1
  417. package/dist/serve/site-ui/docs/pyric-ui-primitives-reference-api/index.html +128 -27
  418. package/dist/serve/site-ui/docs/pyric-ui-primitives-reference-api.md +58 -5
  419. package/dist/serve/site-ui/docs/pyric-ui-rtdb-reference-api/index.html +5 -2
  420. package/dist/serve/site-ui/docs/pyric-ui-rules-hooks-reference-api/index.html +5 -2
  421. package/dist/serve/site-ui/docs/pyric-ui-rules-reference-api/index.html +5 -2
  422. package/dist/serve/site-ui/docs/pyric-ui-storage-hooks-reference-api/index.html +5 -2
  423. package/dist/serve/site-ui/docs/pyric-ui-storage-reference-api/index.html +8 -6
  424. package/dist/serve/site-ui/docs/pyric-ui-storage-reference-api.md +3 -4
  425. package/dist/serve/site-ui/docs/pyric-ui-traffic-hooks-reference-api/index.html +5 -2
  426. package/dist/serve/site-ui/docs/pyric-ui-traffic-reference-api/index.html +5 -2
  427. package/dist/serve/site-ui/docs/reference/cli/index.html +156 -0
  428. package/dist/serve/site-ui/docs/reference/cli.md +120 -0
  429. package/dist/serve/site-ui/docs/rules-compat/index.html +14 -3
  430. package/dist/serve/site-ui/docs/rules-compat.md +9 -1
  431. package/dist/serve/site-ui/docs/secure/audit-your-rules/index.html +5 -2
  432. package/dist/serve/site-ui/docs/secure/firestore-rules-limits/index.html +5 -2
  433. package/dist/serve/site-ui/docs/secure/read-a-denial/index.html +6 -3
  434. package/dist/serve/site-ui/docs/secure/read-a-denial.md +1 -1
  435. package/dist/serve/site-ui/docs/secure/rtdb-rules-in-typescript/index.html +5 -2
  436. package/dist/serve/site-ui/docs/secure/rules-standard-library/index.html +5 -2
  437. package/dist/serve/site-ui/docs/secure/secure-it-with-rules/index.html +5 -2
  438. package/dist/serve/site-ui/docs/secure/simulate-and-lint/index.html +5 -2
  439. package/dist/serve/site-ui/docs/secure/write-a-rules-test-suite/index.html +5 -2
  440. package/dist/serve/site-ui/docs/ship/ship-to-production/index.html +7 -4
  441. package/dist/serve/site-ui/docs/ship/ship-to-production.md +2 -2
  442. package/dist/serve/site-ui/docs/ship/test-in-node/index.html +9 -6
  443. package/dist/serve/site-ui/docs/ship/test-in-node.md +4 -4
  444. package/dist/serve/site-ui/docs/storage-compat/index.html +17 -14
  445. package/dist/serve/site-ui/docs/storage-compat.md +12 -12
  446. package/dist/serve/site-ui/docs/trust/how-we-know-it-matches-firebase/index.html +5 -2
  447. package/dist/serve/site-ui/docs/trust/rules-standard-library/index.html +5 -2
  448. package/dist/serve/site-ui/docs/trust/versioning-and-compatibility/index.html +5 -2
  449. package/dist/serve/site-ui/docs/tutorial/index.html +10 -7
  450. package/dist/serve/site-ui/docs/tutorial.md +4 -4
  451. package/dist/serve/site-ui/examples/chess/index.html +4 -2
  452. package/dist/serve/site-ui/firestore/index.html +5 -3
  453. package/dist/serve/site-ui/hero-charcoal.jpg +0 -0
  454. package/dist/serve/site-ui/index.html +9 -7
  455. package/dist/serve/site-ui/llms.txt +13 -3
  456. package/dist/serve/site-ui/pyric-campfire-full.png +0 -0
  457. package/dist/serve/site-ui/pyric-campfire.png +0 -0
  458. package/dist/serve/site-ui/rtdb/index.html +5 -3
  459. package/dist/serve/site-ui/settings/index.html +5 -3
  460. package/dist/serve/site-ui/storage/index.html +5 -3
  461. package/dist/serve/site-ui/studio/index.html +13 -0
  462. package/dist/serve/site-ui/studio-routes.json +1 -1
  463. package/dist/serve/site-ui/traffic/index.html +5 -3
  464. package/dist/serve/studio/disk-workspace.d.ts.map +1 -1
  465. package/dist/serve/studio/disk-workspace.js +64 -2
  466. package/dist/serve/studio/disk-workspace.js.map +1 -1
  467. package/dist/serve/studio/routes.d.ts +9 -0
  468. package/dist/serve/studio/routes.d.ts.map +1 -1
  469. package/dist/serve/studio/routes.js +47 -7
  470. package/dist/serve/studio/routes.js.map +1 -1
  471. package/dist/serve/vite-ai-config.d.ts.map +1 -1
  472. package/dist/serve/vite-ai-config.js +38 -8
  473. package/dist/serve/vite-ai-config.js.map +1 -1
  474. package/dist/serve/vite-module-swap.d.ts.map +1 -1
  475. package/dist/serve/vite-module-swap.js +0 -28
  476. package/dist/serve/vite-module-swap.js.map +1 -1
  477. package/dist/serve/vite-plugin.d.ts +7 -7
  478. package/dist/serve/vite-plugin.js +1 -1
  479. package/dist/serve/vite-plugin.js.map +1 -1
  480. package/dist/serve/vite-sandbox-generation.d.ts +1 -1
  481. package/dist/serve/vite-sandbox-generation.d.ts.map +1 -1
  482. package/dist/serve/vite-sandbox-generation.js +13 -1
  483. package/dist/serve/vite-sandbox-generation.js.map +1 -1
  484. package/dist/serve/worker/client/auth.js +1 -2
  485. package/dist/serve/worker/client/auth.js.map +1 -1
  486. package/dist/serve/worker/client/core.d.ts +21 -1
  487. package/dist/serve/worker/client/core.d.ts.map +1 -1
  488. package/dist/serve/worker/client/core.js +79 -3
  489. package/dist/serve/worker/client/core.js.map +1 -1
  490. package/dist/serve/worker/client/disconnect.js +1 -1
  491. package/dist/serve/worker/client/disconnect.js.map +1 -1
  492. package/dist/serve/worker/client/presence.d.ts.map +1 -1
  493. package/dist/serve/worker/client/presence.js +18 -5
  494. package/dist/serve/worker/client/presence.js.map +1 -1
  495. package/dist/serve/worker/client.d.ts +1 -1
  496. package/dist/serve/worker/client.d.ts.map +1 -1
  497. package/dist/serve/worker/client.js +1 -1
  498. package/dist/serve/worker/client.js.map +1 -1
  499. package/dist/serve/worker/context-resolver.d.ts +30 -0
  500. package/dist/serve/worker/context-resolver.d.ts.map +1 -0
  501. package/dist/serve/worker/context-resolver.js +44 -0
  502. package/dist/serve/worker/context-resolver.js.map +1 -0
  503. package/dist/serve/worker/entry.js +23 -19
  504. package/dist/serve/worker/entry.js.map +1 -1
  505. package/dist/serve/worker/host/core.d.ts +1 -0
  506. package/dist/serve/worker/host/core.d.ts.map +1 -1
  507. package/dist/serve/worker/host/core.js +12 -6
  508. package/dist/serve/worker/host/core.js.map +1 -1
  509. package/dist/serve/worker/host/storage.js +2 -2
  510. package/dist/serve/worker/host-ai.d.ts.map +1 -1
  511. package/dist/serve/worker/host-ai.js +15 -0
  512. package/dist/serve/worker/host-ai.js.map +1 -1
  513. package/dist/serve/worker/host-auth.js +1 -2
  514. package/dist/serve/worker/host-auth.js.map +1 -1
  515. package/dist/serve/worker/index.d.ts +1 -1
  516. package/dist/serve/worker/index.d.ts.map +1 -1
  517. package/dist/serve/worker/index.js +2 -2
  518. package/dist/serve/worker/index.js.map +1 -1
  519. package/dist/serve/worker/port-lifecycle.d.ts +18 -0
  520. package/dist/serve/worker/port-lifecycle.d.ts.map +1 -0
  521. package/dist/serve/worker/port-lifecycle.js +28 -0
  522. package/dist/serve/worker/port-lifecycle.js.map +1 -0
  523. package/dist/serve/worker/protocol.d.ts +4 -0
  524. package/dist/serve/worker/protocol.d.ts.map +1 -1
  525. package/dist/serve/worker/protocol.js.map +1 -1
  526. package/dist/serve/worker/serve-init.d.ts.map +1 -1
  527. package/dist/serve/worker/serve-init.js +15 -3
  528. package/dist/serve/worker/serve-init.js.map +1 -1
  529. package/dist/verify/fixture.d.ts +1 -1
  530. package/package.json +17 -6
  531. package/src/assurance/.generated/conformance-verdicts.ts +68 -1
  532. package/src/bridge/client/dispatch.ts +52 -56
  533. package/src/bridge/client/tool-family-factories.ts +34 -0
  534. package/src/bridge/server/mcp-contract.ts +15 -59
  535. package/src/bridge/server/peer.ts +24 -1
  536. package/src/bridge/server/tool-family-factories.ts +40 -0
  537. package/src/bridge/server/tool-metadata.ts +29 -61
  538. package/src/bridge/tool-families.generated.ts +17 -0
  539. package/src/bridge/tool-families.ts +87 -0
  540. package/src/bridge/tool-family-records/conformance.ts +6 -0
  541. package/src/bridge/tool-family-records/firestore-data.ts +15 -0
  542. package/src/bridge/tool-family-records/firestore-inspect.ts +6 -0
  543. package/src/bridge/tool-family-records/firestore-rules.ts +15 -0
  544. package/src/bridge/tool-family-records/firestore-simulator.ts +16 -0
  545. package/src/bridge/tool-family-records/rtdb-inspection.ts +6 -0
  546. package/src/bundler/externals.ts +55 -0
  547. package/src/bundler/index.ts +21 -0
  548. package/src/cli/cli.test.ts +2 -46
  549. package/src/cli/index.ts +48 -42
  550. package/src/cli/inlined-sdk-scanner.ts +77 -0
  551. package/src/cli/mcp-proxy.ts +4 -4
  552. package/src/cli/parse-args.ts +61 -15
  553. package/src/cli/pyric-config.ts +45 -0
  554. package/src/cli/sandbox-interlock.ts +167 -0
  555. package/src/cli/{dev-runner.ts → sandbox-runner.ts} +142 -86
  556. package/src/cli/serve.ts +182 -115
  557. package/src/cli/snapshot.ts +5 -5
  558. package/src/cli/unsupported-runtime.ts +68 -0
  559. package/src/cli/verify.ts +1 -1
  560. package/src/conformance/.generated/can-i-use-browser.ts +2 -2
  561. package/src/conformance/.generated/can-i-use.ts +2 -2
  562. package/src/conformance/.generated/conformance-docs.ts +1 -1
  563. package/src/functions-rtdb/child.ts +115 -21
  564. package/src/functions-rtdb/development-runtime.ts +5 -1
  565. package/src/functions-rtdb/project.ts +8 -8
  566. package/src/google-endpoints.ts +155 -0
  567. package/src/next/guard.ts +1 -1
  568. package/src/next/runtime-env.ts +5 -0
  569. package/src/next/with-pyric.ts +1 -1
  570. package/src/register/beacon.ts +232 -0
  571. package/src/register/index.ts +68 -6
  572. package/src/register/net-guard.ts +567 -0
  573. package/src/remote/index.ts +9 -9
  574. package/src/serve/ai-diagnostics-relay.ts +243 -0
  575. package/src/serve/ai-proxy.ts +327 -0
  576. package/src/serve/ai-status.ts +109 -0
  577. package/src/serve/ai-terminal-blocks.ts +210 -0
  578. package/src/serve/ai-terminal-text.ts +52 -0
  579. package/src/serve/beacon-route.ts +141 -0
  580. package/src/serve/bundler.ts +10 -10
  581. package/src/serve/entries/active-auth-registry.ts +56 -0
  582. package/src/serve/entries/active-auth.ts +100 -0
  583. package/src/serve/entries/ai.ts +27 -1
  584. package/src/serve/entries/auth-helper-core.ts +5 -0
  585. package/src/serve/entries/auth-helper-dom.ts +27 -3
  586. package/src/serve/entries/auth-helper-runtime.ts +1 -1
  587. package/src/serve/entries/auth.ts +8 -0
  588. package/src/serve/entries/bridge-url.ts +1 -1
  589. package/src/serve/entries/firestore.ts +3 -3
  590. package/src/serve/entries/init.ts +78 -3
  591. package/src/serve/entries/keepalive.ts +1 -1
  592. package/src/serve/entries/runtime.ts +29 -14
  593. package/src/serve/entries/storage.ts +1 -1
  594. package/src/serve/entries/worker-runtime.ts +1 -1
  595. package/src/serve/init-payload.ts +4 -0
  596. package/src/serve/namespace.ts +117 -145
  597. package/src/serve/rules.ts +47 -22
  598. package/src/serve/runtime/chip-config.ts +16 -2
  599. package/src/serve/runtime/chip-dialog.ts +366 -0
  600. package/src/serve/runtime/chip-install.ts +10 -4
  601. package/src/serve/runtime/chip-user-search.ts +391 -0
  602. package/src/serve/runtime/chip.ts +177 -15
  603. package/src/serve/runtime/identity.ts +26 -0
  604. package/src/serve/runtime/manifest.ts +1 -1
  605. package/src/serve/runtime/status.ts +24 -0
  606. package/src/serve/sandbox-session.ts +19 -4
  607. package/src/serve/server.ts +18 -1
  608. package/src/serve/studio/disk-workspace.ts +76 -2
  609. package/src/serve/studio/routes.ts +63 -7
  610. package/src/serve/vite-ai-config.ts +37 -7
  611. package/src/serve/vite-module-swap.ts +0 -28
  612. package/src/serve/vite-plugin.ts +8 -8
  613. package/src/serve/vite-sandbox-generation.ts +16 -1
  614. package/src/serve/worker/client/auth.ts +1 -1
  615. package/src/serve/worker/client/core.ts +80 -3
  616. package/src/serve/worker/client/disconnect.ts +1 -1
  617. package/src/serve/worker/client/presence.ts +25 -5
  618. package/src/serve/worker/client.ts +1 -1
  619. package/src/serve/worker/context-resolver.ts +60 -0
  620. package/src/serve/worker/entry.ts +22 -18
  621. package/src/serve/worker/host/core.ts +9 -7
  622. package/src/serve/worker/host/storage.ts +2 -2
  623. package/src/serve/worker/host-ai.ts +15 -0
  624. package/src/serve/worker/host-auth.ts +1 -1
  625. package/src/serve/worker/index.ts +4 -1
  626. package/src/serve/worker/port-lifecycle.ts +45 -0
  627. package/src/serve/worker/protocol.ts +2 -1
  628. package/src/serve/worker/serve-init.ts +14 -3
  629. package/src/verify/fixture.ts +1 -1
  630. package/README.md.orig +0 -145
  631. package/dist/cli/dev-runner.d.ts.map +0 -1
  632. package/dist/cli/dev-runner.js.map +0 -1
  633. package/dist/serve/site-ui/_astro/_slug_.CXh9yTP1.css +0 -1
  634. package/dist/serve/site-ui/_astro/_slug_.CupVK4TM.css +0 -1
  635. package/dist/serve/site-ui/_astro/_studio_.CfxiQWkk.css +0 -1
  636. package/dist/serve/site-ui/_astro/app.BsFsMjn0.css +0 -1
  637. package/dist/serve/site-ui/_astro/app.Ce3MTLQm.js +0 -1
  638. package/dist/serve/site-ui/_astro/app.taqzyjD0.js +0 -11
  639. package/dist/serve/site-ui/_astro/sandbox-controls.BP4M5f1M.js +0 -1169
  640. package/dist/serve/site-ui/_astro/writes.C57paAj2.js +0 -99
  641. package/dist/serve/site-ui/docs/trust/storage-rules-stdlib-research/index.html +0 -582
  642. package/dist/serve/site-ui/docs/trust/storage-rules-stdlib-research.md +0 -448
@@ -33,12 +33,22 @@ import type {
33
33
  WorkspaceChange,
34
34
  WorkspaceStore,
35
35
  } from './store-types.js';
36
+ import { isAllowedHost, isAllowedOrigin, getHeader } from '../server.js';
37
+ import type { WriterLock } from '../writer-lock.js';
36
38
 
37
39
  export interface StudioRouteOptions {
38
40
  /** The single-project file tree served at `/__pyric/workspace`. */
39
41
  workspace?: WorkspaceStore;
40
42
  /** The project list served at `/__pyric/projects`. */
41
43
  projects?: ProjectStore;
44
+ /** Per-boot session capability token required on workspace & project endpoints. */
45
+ sessionToken?: string;
46
+ /** Single-writer lock validator for file mutation & deletion endpoints. */
47
+ writerLock?: WriterLock;
48
+ /** Bound host for DNS rebinding guard. */
49
+ boundHost?: string;
50
+ /** Extra hostnames allowed for DNS rebinding/origin guard. */
51
+ allowedHosts?: string[];
42
52
  }
43
53
 
44
54
  /** Collect the raw request body as a UTF-8 string. */
@@ -207,6 +217,10 @@ async function handleProjects(
207
217
  .end('method not allowed');
208
218
  }
209
219
 
220
+ function isWorkspaceMutation(method?: string): boolean {
221
+ return method === 'PUT' || method === 'POST' || method === 'PATCH' || method === 'DELETE';
222
+ }
223
+
210
224
  /**
211
225
  * Build a namespace fragment for the Studio storage routes. Returns a handler
212
226
  * shaped like the other `/__pyric/*` fragments: it resolves `true` when it
@@ -219,13 +233,55 @@ export function createStudioRoutes(opts: StudioRouteOptions) {
219
233
  url: URL,
220
234
  ): Promise<boolean> => {
221
235
  try {
222
- if (opts.workspace && url.pathname.startsWith('/__pyric/workspace')) {
223
- await handleWorkspace(opts.workspace, req, res, url);
224
- return true;
225
- }
226
- if (opts.projects && url.pathname.startsWith('/__pyric/projects')) {
227
- await handleProjects(opts.projects, req, res, url);
228
- return true;
236
+ const isWorkspaceRoute = opts.workspace && url.pathname.startsWith('/__pyric/workspace');
237
+ const isProjectsRoute = opts.projects && url.pathname.startsWith('/__pyric/projects');
238
+
239
+ if (isWorkspaceRoute || isProjectsRoute) {
240
+ const hostHeader = getHeader(req, 'host');
241
+ const originHeader = getHeader(req, 'origin');
242
+
243
+ // 1. Host and Origin guard (Requirement 1)
244
+ if (!isAllowedHost(hostHeader, opts.boundHost ?? 'localhost', opts.allowedHosts)) {
245
+ res.writeHead(403, { 'content-type': 'text/plain' }).end('Forbidden: host not allowed');
246
+ return true;
247
+ }
248
+ if (originHeader && !isAllowedOrigin(originHeader, opts.boundHost ?? 'localhost', opts.allowedHosts)) {
249
+ res.writeHead(403, { 'content-type': 'text/plain' }).end('Forbidden: origin mismatch');
250
+ return true;
251
+ }
252
+
253
+ // 2. Per-boot session capability token guard (Requirement 2)
254
+ if (opts.sessionToken) {
255
+ const reqToken =
256
+ getHeader(req, 'x-pyric-session-token') ??
257
+ url.searchParams.get('token');
258
+ if (!reqToken || reqToken !== opts.sessionToken) {
259
+ res.writeHead(401, { 'content-type': 'text/plain' }).end('Unauthorized: invalid session capability token');
260
+ return true;
261
+ }
262
+ }
263
+
264
+ // 3. Single-writer lock guard for mutation endpoints (Requirement 4)
265
+ if (opts.writerLock && isWorkspaceMutation(req.method)) {
266
+ const writerId = getHeader(req, 'x-pyric-writer');
267
+ if (!writerId) {
268
+ res.writeHead(423, { 'content-type': 'text/plain' }).end('Locked: missing active writer lock header');
269
+ return true;
270
+ }
271
+ if (!opts.writerLock.claim(writerId, Date.now())) {
272
+ res.writeHead(423, { 'content-type': 'text/plain' }).end('Locked: another tab holds the writer lock');
273
+ return true;
274
+ }
275
+ }
276
+
277
+ if (isWorkspaceRoute) {
278
+ await handleWorkspace(opts.workspace!, req, res, url);
279
+ return true;
280
+ }
281
+ if (isProjectsRoute) {
282
+ await handleProjects(opts.projects!, req, res, url);
283
+ return true;
284
+ }
229
285
  }
230
286
  } catch (e) {
231
287
  if (!res.headersSent) sendError(res, e);
@@ -44,7 +44,18 @@ export function loadViteAiEnv(
44
44
  const resolvedEnvDir = envDir === undefined
45
45
  ? resolvedRoot
46
46
  : path.resolve(resolvedRoot, envDir);
47
- return loadEnv(mode, resolvedEnvDir, '');
47
+ const loaded = loadEnv(mode, resolvedEnvDir, '');
48
+ for (const [key, val] of Object.entries(loaded)) {
49
+ const isAuthKey =
50
+ key === 'GEMINI_API_KEY' ||
51
+ key === 'GOOGLE_GENAI_API_KEY' ||
52
+ key === 'VITE_GEMINI_API_KEY' ||
53
+ key === 'PYRIC_API_KEY';
54
+ if (isAuthKey && process.env[key] === undefined) {
55
+ process.env[key] = val;
56
+ }
57
+ }
58
+ return loaded;
48
59
  }
49
60
 
50
61
  /** Convert a public declarative engine into the JSON-safe worker wire shape. */
@@ -65,6 +76,21 @@ export function engineConfigToWire(engine: PyricAiEngineConfig): AiEngineConfigW
65
76
  }
66
77
  return result as AiEngineConfigWire;
67
78
  }
79
+ const isGeminiEngine = engine.kind === 'gemini';
80
+ if (isGeminiEngine) {
81
+ const result: Record<string, unknown> = {
82
+ kind: 'gemini',
83
+ };
84
+ const hasBaseUrl = engine.baseUrl !== undefined;
85
+ if (hasBaseUrl) {
86
+ result.baseUrl = engine.baseUrl;
87
+ }
88
+ const hasApiKey = engine.apiKey !== undefined;
89
+ if (hasApiKey) {
90
+ result.apiKey = engine.apiKey;
91
+ }
92
+ return result as AiEngineConfigWire;
93
+ }
68
94
  const result: Record<string, unknown> = {
69
95
  kind: 'scripted',
70
96
  };
@@ -123,12 +149,16 @@ export function resolveViteAiConfig(
123
149
  }
124
150
 
125
151
  let engineWire: AiEngineConfigWire | undefined = undefined;
126
- if (!isProductionMode) {
127
- if (explicitEngine !== undefined) {
128
- engineWire = engineConfigToWire(explicitEngine);
129
- } else if (model !== undefined) {
130
- engineWire = engineConfigToWire({ kind: 'openai', baseUrl: AI_PROXY_PATH, model });
131
- }
152
+ if (isProductionMode) {
153
+ const apiKey = env.GEMINI_API_KEY ?? env.GOOGLE_GENAI_API_KEY ?? env.VITE_GEMINI_API_KEY;
154
+ engineWire = {
155
+ kind: 'gemini',
156
+ ...(apiKey !== undefined && apiKey.trim() !== '' ? { apiKey: apiKey.trim() } : {}),
157
+ };
158
+ } else if (explicitEngine !== undefined) {
159
+ engineWire = engineConfigToWire(explicitEngine);
160
+ } else if (model !== undefined) {
161
+ engineWire = engineConfigToWire({ kind: 'openai', baseUrl: AI_PROXY_PATH, model });
132
162
  }
133
163
 
134
164
  let proxyUpstream: string | undefined = undefined;
@@ -86,20 +86,6 @@ export function createViteModuleSwap(
86
86
 
87
87
  const match = FIREBASE_SPECIFIER.exec(args.path);
88
88
  const subpath = match !== null && match[1] !== undefined ? match[1] : '';
89
- const isProductionAiMode = aiMode() === 'production';
90
- if (isProductionAiMode) {
91
- const isAiSubpath = subpath === 'ai';
92
- if (isAiSubpath) {
93
- return null;
94
- }
95
- const isAppSubpath = subpath === 'app';
96
- const passthroughEntry = entries['app-ai-passthrough'];
97
- const hasPassthroughEntry = passthroughEntry !== undefined;
98
- const shouldUsePassthroughBridge = isAppSubpath && hasPassthroughEntry;
99
- if (shouldUsePassthroughBridge) {
100
- return { path: passthroughEntry };
101
- }
102
- }
103
89
 
104
90
  const isServedSubpath = SERVED_FIREBASE_SUBPATHS.has(subpath);
105
91
  if (isServedSubpath) {
@@ -173,20 +159,6 @@ export function createViteModuleSwap(
173
159
  const isFirebaseSpecifier = firebaseMatch !== null;
174
160
  if (isFirebaseSpecifier) {
175
161
  const subpath = firebaseMatch[1] !== undefined ? firebaseMatch[1] : '';
176
- const isProductionAiMode = aiMode() === 'production';
177
- if (isProductionAiMode) {
178
- const isAiSubpath = subpath === 'ai';
179
- if (isAiSubpath) {
180
- return null;
181
- }
182
- const isAppSubpath = subpath === 'app';
183
- const passthroughEntry = entries['app-ai-passthrough'];
184
- const hasPassthroughEntry = passthroughEntry !== undefined;
185
- const shouldUsePassthroughBridge = isAppSubpath && hasPassthroughEntry;
186
- if (shouldUsePassthroughBridge) {
187
- return passthroughEntry;
188
- }
189
- }
190
162
  const isServedSubpath = SERVED_FIREBASE_SUBPATHS.has(subpath);
191
163
  if (isServedSubpath) {
192
164
  const key = entryKey(subpath);
@@ -1,18 +1,18 @@
1
1
  /**
2
2
  * `@pyric/cli/vite` — the firebase→pyric-sandbox swap as a Vite plugin.
3
3
  *
4
- * The serve analog for SOURCE-driven apps: instead of `vite build && pyric dev
4
+ * The serve analog for SOURCE-driven apps: instead of `vite build && pyric sandbox
5
5
  * dist`, a team keeps `vite dev` (HMR, source maps) with the in-process sandbox
6
6
  * standing in for Firebase. The app's `firebase/*` imports are UNCHANGED — the
7
7
  * plugin swaps them at the module-resolution layer (`resolveId`), the same way
8
- * `pyric dev` swaps via a runtime import map. (Design: `plans/pyric-vite-plugin.md`.)
8
+ * `pyric sandbox` swaps via a runtime import map. (Design: `plans/pyric-vite-plugin.md`.)
9
9
  *
10
10
  * Two flavors, one `apply` function. Under `vite dev` the swap is ALWAYS on.
11
11
  * For `vite build` the swap is MODE-gated: a plain `vite build` (mode
12
12
  * `production`) ships the real `firebase` package — the swap never reaches the
13
13
  * prod artifact — while `vite build --mode development` (any NON-production
14
14
  * mode) produces a SANDBOX build that bundles pyric's in-page adapters instead
15
- * of the real SDK: self-contained, meant to be previewed under `pyric dev`, and
15
+ * of the real SDK: self-contained, meant to be previewed under `pyric sandbox`, and
16
16
  * stamped with the sandbox-build marker so it can never be deployed (`pyric
17
17
  * deploy hosting` refuses it). `pyric({ swapInBuild })` forces the build
18
18
  * behavior on/off regardless of mode. See `sandbox-marker.ts`.
@@ -24,11 +24,11 @@
24
24
  * - the host-neutral sandbox session — rules, fixtures, stores, live payload,
25
25
  * Studio routes, `/__pyric/*` namespace, and owned cleanup;
26
26
  * - the SharedWorker host — `bundleWorker` served at `/__pyric/sdk/worker.js`;
27
- * - the bridge mount shared with `pyric dev --bridge`.
27
+ * - the bridge mount shared with `pyric sandbox --bridge`.
28
28
  *
29
29
  * Scope = M1 (swap + rules) + M2 (SharedWorker multi-tab + persist/capture/seed)
30
30
  * + M3 (the MCP bridge fold — `{ bridge }`). M3 reuses `createBridgeMount` (the
31
- * proven serve-flavored bridge behind `pyric dev --bridge`), composing it into
31
+ * proven serve-flavored bridge behind `pyric sandbox --bridge`), composing it into
32
32
  * the same `/__pyric` middleware; on the worker path the bridge peer routes
33
33
  * agent tool-calls THROUGH the SharedWorker (`connectBridgePeer`), so the agent
34
34
  * and the app share one backend without forcing the page in-page.
@@ -82,7 +82,7 @@ export interface PyricOptions {
82
82
  * only when the worker bundle itself fails, bridge or not. */
83
83
  bridge?: boolean | { project?: string; disableAuditLog?: boolean };
84
84
  /** Serve the **Pyric Studio** app at `/__pyric/ui/` on Vite's dev origin (the
85
- * `pyric dev --ui` equivalent). Mounts the disk-backed workspace/project
85
+ * `pyric sandbox --ui` equivalent). Mounts the disk-backed workspace/project
86
86
  * routes Studio's `local` mode talks to AND serves the unified Astro site
87
87
  * (vendored in this package at `dist/serve/site-ui`). **On by default**,
88
88
  * including under `bridge` (the bridge peer routes through the SharedWorker,
@@ -93,7 +93,7 @@ export interface PyricOptions {
93
93
  * dev/builds. On by default. Pass `false` to hide it, or
94
94
  * `{ initiallyOpen: true }` when actively debugging runtime errors. */
95
95
  runtimeChip?: PyricRuntimeChipOption;
96
- /** RTDB-triggered Cloud Functions under this dev server (the `pyric dev`
96
+ /** RTDB-triggered Cloud Functions under this dev server (the `pyric sandbox`
97
97
  * parity fold). By default a `functions` block in `firebase.json` is
98
98
  * discovered automatically: its `onValueCreated` triggers run in an isolated
99
99
  * node child against the shared sandbox (other trigger kinds warn and are
@@ -216,7 +216,7 @@ export function pyric(options: PyricOptions = {}): Plugin {
216
216
  // `vite build` keeps the real firebase package — the swap never reaches
217
217
  // production output. A sandbox build applies the same swap so the output
218
218
  // bundles pyric's in-page adapters (self-contained; preview it under
219
- // `pyric dev`, never deploy it).
219
+ // `pyric sandbox`, never deploy it).
220
220
  apply(config, env) {
221
221
  void config;
222
222
  return pageRuntime.applies(env);
@@ -1,12 +1,13 @@
1
1
  import { existsSync } from 'node:fs';
2
2
  import type { ViteDevServer } from 'vite';
3
3
  import { readFirebaseJson, readFirebaseRc, type FirebaseJson } from '../cli/firebase-json.js';
4
- import { registerModuleUrl } from '../cli/dev-runner.js';
4
+ import { registerModuleUrl } from '../cli/sandbox-runner.js';
5
5
  import {
6
6
  discoverFunctionsRtdbProject,
7
7
  type FunctionsRtdbProject,
8
8
  } from '../functions-rtdb/project.js';
9
9
  import { formatActivityWarning } from './activity-warning.js';
10
+ import { formatAiStatusNote } from './ai-status.js';
10
11
  import {
11
12
  createBridgeMount,
12
13
  type BridgeHostAttachment,
@@ -217,6 +218,20 @@ export async function createViteSandboxGeneration(
217
218
  server.config.logger.info(' ⓘ [pyric] fresh: discarded the existing state file; re-seeding');
218
219
  }
219
220
 
221
+ // Say what AI resolved to. This is the one front door where the engine and
222
+ // its model binding ARE known at boot (`ai.engine` / `ai.model` reduced to
223
+ // the wire shape above); the broker itself is still built lazily, in the
224
+ // page or the worker, on the first `ai.*` op. Every generation prints it,
225
+ // so a Vite restart on an `.env`/config edit re-announces the new binding
226
+ // through the same formatter instead of swapping engines in silence.
227
+ server.config.logger.info(
228
+ formatAiStatusNote({
229
+ engine: ai.engineWire,
230
+ mode: ai.mode,
231
+ proxyUpstream: ai.proxyUpstream,
232
+ }),
233
+ );
234
+
220
235
  const middlewareInput = { server, bridge, session };
221
236
  const disposeMiddleware = attachViteGenerationMiddleware(middlewareInput);
222
237
  listenerDisposers.push(disposeMiddleware);
@@ -312,7 +312,7 @@ export async function restorePortSession(
312
312
  t: 'op', id: nextId(), method: 'auth.restorePortSession', uid,
313
313
  })) as SerializedUser | null;
314
314
  const user = toClientUser(auth.port, raw);
315
- if (user) auth.currentUser = user;
315
+ auth.currentUser = user;
316
316
  return user;
317
317
  }
318
318
 
@@ -259,26 +259,103 @@ export function wirePort(port: ClientPort): void {
259
259
  // ops (`auth.*`) and `getVersion` are NEVER lensed — they operate the worker's
260
260
  // session, not data — so the lens is stamped only on the data-op path.
261
261
 
262
+ export const AUTH_LENS_STORAGE_KEY = 'pyric:auth-lens';
263
+
264
+ type PersistedAuthLens =
265
+ | Extract<AuthLens, { mode: 'admin' }>
266
+ | Extract<AuthLens, { mode: 'as' }>;
267
+
268
+ export function isPersistedAuthLens(value: unknown): value is PersistedAuthLens {
269
+ if (!value || typeof value !== 'object') return false;
270
+ const candidate = value as { mode?: unknown; uid?: unknown };
271
+ if (candidate.mode === 'admin') return true;
272
+ return candidate.mode === 'as' && typeof candidate.uid === 'string';
273
+ }
274
+
275
+ function normalizePersistedAuthLens(value: unknown): PersistedAuthLens | undefined {
276
+ if (isPersistedAuthLens(value)) return value;
277
+ return undefined;
278
+ }
279
+
280
+ /** Read persisted auth lens from sessionStorage safely. */
281
+ export function hydrateLensFromStorage(): AuthLens | undefined {
282
+ if (typeof sessionStorage === 'undefined') return undefined;
283
+ try {
284
+ const raw = sessionStorage.getItem(AUTH_LENS_STORAGE_KEY);
285
+ if (!raw) return undefined;
286
+ const parsed: unknown = JSON.parse(raw);
287
+ const persistedLens = normalizePersistedAuthLens(parsed);
288
+ if (persistedLens) return persistedLens;
289
+ sessionStorage.removeItem(AUTH_LENS_STORAGE_KEY);
290
+ return undefined;
291
+ } catch {
292
+ try {
293
+ sessionStorage.removeItem(AUTH_LENS_STORAGE_KEY);
294
+ } catch {
295
+ // Ignore storage restrictions
296
+ }
297
+ return undefined;
298
+ }
299
+ }
300
+
262
301
  /** Module-level default lens. `undefined` ⇒ the worker treats the op as the
263
302
  * app's session (the additive default — existing senders omit `actAs`).
264
303
  *
265
304
  * Exported as a LIVE binding: family modules read it to stamp the lens onto
266
305
  * subscriptions (`onSnapshot`, `rtdbOnValue`), and see `setLens(...)` updates
267
306
  * through this one singleton. */
268
- export let _defaultLens: AuthLens | undefined;
307
+ export let _defaultLens: AuthLens | undefined = hydrateLensFromStorage();
308
+
309
+ const lensListeners = new Set<(lens: AuthLens | undefined) => void>();
310
+
311
+ /**
312
+ * Register a listener for auth lens mutations.
313
+ * Returns an unsubscribe function that removes the listener.
314
+ */
315
+ export function subscribeLens(listener: (lens: AuthLens | undefined) => void): () => void {
316
+ lensListeners.add(listener);
317
+ return () => {
318
+ lensListeners.delete(listener);
319
+ };
320
+ }
269
321
 
270
322
  /**
271
323
  * Set the default auth lens applied to subsequent Firestore DATA ops from this
272
- * client (Pyric Studio). Pass `{ mode: 'as', uid }` to read/write AS a user
324
+ * client (Pyric Studio & Runtime Chip). Pass `{ mode: 'as', uid }` to read/write AS a user
273
325
  * (rules apply), `{ mode: 'admin' }` for the admin lens, or
274
326
  * `{ mode: 'app-session' }` / `undefined` to revert to the app's own session.
275
327
  *
276
328
  * The lens is process-wide for this client module (one served page = one
277
329
  * worker port), mirroring how Studio drives a single active identity at a time.
278
330
  * Auth ops are unaffected — they always operate the real session.
331
+ *
332
+ * Persists active admin and impersonation lenses to sessionStorage under
333
+ * AUTH_LENS_STORAGE_KEY, and removes the key when cleared or reverted to the
334
+ * app session.
335
+ * Notifies all registered subscribeLens listeners.
279
336
  */
280
337
  export function setLens(lens: AuthLens | undefined): void {
281
- _defaultLens = lens && lens.mode === 'app-session' ? undefined : lens;
338
+ _defaultLens = normalizePersistedAuthLens(lens);
339
+
340
+ if (typeof sessionStorage !== 'undefined') {
341
+ try {
342
+ if (_defaultLens) {
343
+ sessionStorage.setItem(AUTH_LENS_STORAGE_KEY, JSON.stringify(_defaultLens));
344
+ } else {
345
+ sessionStorage.removeItem(AUTH_LENS_STORAGE_KEY);
346
+ }
347
+ } catch {
348
+ // Ignore quota or disabled storage restrictions
349
+ }
350
+ }
351
+
352
+ for (const listener of [...lensListeners]) {
353
+ try {
354
+ listener(_defaultLens);
355
+ } catch (err) {
356
+ console.error('Error in subscribeLens listener:', err);
357
+ }
358
+ }
282
359
  }
283
360
 
284
361
  /** The active default lens (read-only view), for Studio UI to reflect state. */
@@ -15,7 +15,7 @@ function deleteTimeoutError(): Error & { code: string } {
15
15
  return Object.assign(
16
16
  new Error(
17
17
  'Timed out waiting for SharedWorker app cleanup acknowledgment. ' +
18
- 'Close all tabs using this pyric dev origin so the worker can restart.',
18
+ 'Close all tabs using this pyric sandbox origin so the worker can restart.',
19
19
  ),
20
20
  { code: 'app/delete-timeout' },
21
21
  );
@@ -25,9 +25,29 @@ import type { ClientDb, Unsubscribe } from './handles.js';
25
25
  export type { PresenceClientKind, PresenceSnapshot, PresenceVisibility };
26
26
  export { PRESENCE_HEARTBEAT_INTERVAL_MS, PRESENCE_STALE_MS };
27
27
 
28
+ function supportsRandomUUID(): boolean {
29
+ return typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function';
30
+ }
31
+
32
+ function supportsDocumentEventListeners(): boolean {
33
+ return (
34
+ typeof document !== 'undefined' &&
35
+ typeof document.addEventListener === 'function' &&
36
+ typeof document.removeEventListener === 'function'
37
+ );
38
+ }
39
+
40
+ function supportsWindowEventListeners(): boolean {
41
+ return (
42
+ typeof window !== 'undefined' &&
43
+ typeof window.addEventListener === 'function' &&
44
+ typeof window.removeEventListener === 'function'
45
+ );
46
+ }
47
+
28
48
  /** Mint a random client id (page-lifetime). */
29
49
  export function mintPresenceClientId(): string {
30
- if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {
50
+ if (supportsRandomUUID()) {
31
51
  return crypto.randomUUID();
32
52
  }
33
53
  return `page-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
@@ -114,10 +134,10 @@ export function startPresence(opts: StartPresenceOptions): PresenceSession {
114
134
 
115
135
  register();
116
136
 
117
- if (typeof document !== 'undefined') {
137
+ if (supportsDocumentEventListeners()) {
118
138
  document.addEventListener('visibilitychange', update);
119
139
  }
120
- if (typeof window !== 'undefined') {
140
+ if (supportsWindowEventListeners()) {
121
141
  window.addEventListener('popstate', update);
122
142
  window.addEventListener('hashchange', update);
123
143
  // Best-effort clean disconnect; lease expiry covers unclean shutdown.
@@ -139,10 +159,10 @@ export function startPresence(opts: StartPresenceOptions): PresenceSession {
139
159
  clearInterval(timer);
140
160
  timer = null;
141
161
  }
142
- if (typeof document !== 'undefined') {
162
+ if (supportsDocumentEventListeners()) {
143
163
  document.removeEventListener('visibilitychange', update);
144
164
  }
145
- if (typeof window !== 'undefined') {
165
+ if (supportsWindowEventListeners()) {
146
166
  window.removeEventListener('popstate', update);
147
167
  window.removeEventListener('hashchange', update);
148
168
  window.removeEventListener('pagehide', onPageHide);
@@ -26,7 +26,7 @@
26
26
  */
27
27
 
28
28
  // Shared core: lens/issuer controls + the handle and snapshot types.
29
- export { setLens, getLens, setOpIssuer } from './client/core.js';
29
+ export { setLens, getLens, subscribeLens, AUTH_LENS_STORAGE_KEY, hydrateLensFromStorage, setOpIssuer } from './client/core.js';
30
30
  export type {
31
31
  ClientDb,
32
32
  ClientRtdb,
@@ -0,0 +1,60 @@
1
+ /**
2
+ * SharedWorker context resolver.
3
+ *
4
+ * Coordinates asynchronous initialization of a singleton resource across
5
+ * concurrent inbound connection requests or burst messages.
6
+ *
7
+ * Invariants:
8
+ * 1. Single In-Flight Attempt: When multiple callers request the context
9
+ * concurrently while initialization is in flight, all callers share the
10
+ * single in-flight promise rather than starting parallel initializations.
11
+ * 2. Unpoisoned on Rejection: If initialization rejects, the memoized promise
12
+ * is cleared immediately, allowing subsequent requests to retry initialization
13
+ * rather than permanently caching the rejection.
14
+ * 3. Synchronous Retrieval: Once successfully resolved, the resource is
15
+ * available synchronously for shutdown and cleanup handlers.
16
+ */
17
+
18
+ export interface ContextResolver<T> {
19
+ /**
20
+ * Return the resolved resource or the in-flight initialization promise.
21
+ * If a previous attempt rejected, starts a new attempt.
22
+ */
23
+ get(): Promise<T>;
24
+
25
+ /**
26
+ * Return the successfully resolved resource, or null if not yet resolved
27
+ * or currently in-flight.
28
+ */
29
+ current(): T | null;
30
+ }
31
+
32
+ export function createContextResolver<T>(factory: () => Promise<T>): ContextResolver<T> {
33
+ let value: T | null = null;
34
+ let inFlight: Promise<T> | null = null;
35
+
36
+ return {
37
+ async get(): Promise<T> {
38
+ if (value !== null) {
39
+ return value;
40
+ }
41
+ if (inFlight === null) {
42
+ inFlight = factory()
43
+ .then((resolved) => {
44
+ value = resolved;
45
+ return resolved;
46
+ })
47
+ .catch((error: unknown) => {
48
+ inFlight = null;
49
+ value = null;
50
+ throw error;
51
+ });
52
+ }
53
+ return inFlight;
54
+ },
55
+
56
+ current(): T | null {
57
+ return value;
58
+ },
59
+ };
60
+ }
@@ -67,36 +67,33 @@ import {
67
67
  } from './service-worker-channel.js';
68
68
  import { createServiceWorkerRelay } from './service-worker-relay.js';
69
69
  import { createWorkerRetirement } from './retirement.js';
70
+ import { createContextResolver } from './context-resolver.js';
71
+ import { createPortLifecycleManager } from './port-lifecycle.js';
70
72
 
71
73
  declare const __PYRIC_WORKER_VERSION__: string;
72
74
  const workerEpoch = typeof __PYRIC_WORKER_VERSION__ !== 'undefined'
73
75
  ? __PYRIC_WORKER_VERSION__
74
76
  : 'dev';
75
77
  const workerScope = self as unknown as SharedWorkerGlobalScope;
78
+ const contextResolver = createContextResolver(buildCtx);
79
+ const portLifecycle = createPortLifecycleManager();
76
80
  const retirement = createWorkerRetirement({
77
81
  closeWorker: () => workerScope.close(),
78
- beforeAnnounce: async () => { await _ctx?.captureFlush?.(); },
82
+ beforeAnnounce: async () => { await contextResolver.current()?.captureFlush?.(); },
79
83
  });
80
84
 
81
85
  // ─── Singleton context ────────────────────────────────────────────────────
82
86
 
83
- // `_ctx` is the RESOLVED context, kept for the synchronous `close` handler.
84
- // The build is memoized as a PROMISE (`_ctxPromise`) — NOT as the resolved
85
- // value — because init is async (it awaits `/__pyric/init.json` + persistence
86
- // restore). The first messages arrive in a BURST (the authState sub, the first
87
- // firestore sub, an op — possibly across two tabs at once); if each re-checked
88
- // only the resolved `_ctx` they would every one start a fresh init and build a
89
- // SEPARATE sandbox, so a listener and a write could bind to different
90
- // instances and never see each other. The promise memo guarantees ONE init.
91
- let _ctx: HostCtx | null = null;
92
- let _ctxPromise: Promise<HostCtx> | null = null;
93
-
94
87
  /**
95
88
  * Return the shared context, memoizing the in-flight init promise so
96
- * concurrent first messages await ONE build (see `_ctxPromise` above).
89
+ * concurrent first messages await ONE build. If initialization rejects,
90
+ * the rejection is cleared so subsequent connection attempts can retry.
91
+ * Any ports that closed while initialization was in flight are drained immediately.
97
92
  */
98
- function getCtx(): Promise<HostCtx> {
99
- return (_ctxPromise ??= buildCtx());
93
+ async function getCtx(): Promise<HostCtx> {
94
+ const ctx = await contextResolver.get();
95
+ portLifecycle.drainClosedPorts(ctx);
96
+ return ctx;
100
97
  }
101
98
 
102
99
  /**
@@ -125,7 +122,6 @@ async function buildCtx(): Promise<HostCtx> {
125
122
  ? (url) => new EventSource(url) as unknown as EventSourceLike
126
123
  : null,
127
124
  });
128
- _ctx = ctx; // publish the resolved ctx for the synchronous close handler
129
125
  return ctx;
130
126
  }
131
127
 
@@ -141,6 +137,7 @@ workerScope.onconnect = (e: MessageEvent) => {
141
137
  const port = e.ports[0];
142
138
  port.start();
143
139
  retirement.connect(port);
140
+ void getCtx().catch(() => undefined);
144
141
 
145
142
  let messageQueue = Promise.resolve();
146
143
  port.onmessage = (ev: MessageEvent<InboundMessage>) => {
@@ -172,9 +169,16 @@ workerScope.onconnect = (e: MessageEvent) => {
172
169
  // cannot overtake an already-posted mutation, and later frames see the
173
170
  // disconnected-port tombstone instead of touching the shared backend.
174
171
  messageQueue = messageQueue.then(async () => {
172
+ const portLike = port as unknown as PortLike;
173
+ if (portLifecycle.isPortClosed(portLike)) {
174
+ return;
175
+ }
175
176
  try {
176
177
  const ctx = await getCtx();
177
- await handleMessage(ctx, port as unknown as PortLike, ev.data);
178
+ if (portLifecycle.isPortClosed(portLike)) {
179
+ return;
180
+ }
181
+ await handleMessage(ctx, portLike, ev.data);
178
182
  } catch (e) {
179
183
  // eslint-disable-next-line no-console
180
184
  console.error('[pyric worker] message handler error:', (e as Error)?.stack ?? e, 'msg:', ev.data);
@@ -188,7 +192,7 @@ workerScope.onconnect = (e: MessageEvent) => {
188
192
  // best-effort; subscriptions also GC when the worker itself dies.
189
193
  port.addEventListener('close', () => {
190
194
  retirement.disconnect(port);
191
- if (_ctx) void cleanupPortWithDisconnect(_ctx, port as unknown as PortLike).catch(() => undefined);
195
+ portLifecycle.onPortClosed(port as unknown as PortLike, contextResolver.current());
192
196
  });
193
197
  };
194
198