@pyric/cli 0.1.0-alpha.10 → 0.1.0-alpha.12

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 (498) hide show
  1. package/README.md.orig +2 -2
  2. package/dist/assurance/.generated/conformance-verdicts.d.ts +16 -0
  3. package/dist/assurance/.generated/conformance-verdicts.d.ts.map +1 -1
  4. package/dist/assurance/.generated/conformance-verdicts.js +16 -0
  5. package/dist/assurance/.generated/conformance-verdicts.js.map +1 -1
  6. package/dist/bridge/server/peer.d.ts +1 -1
  7. package/dist/bridge/server.d.ts +1 -1
  8. package/dist/bridge/server.js +1 -1
  9. package/dist/cli/index.d.ts +11 -0
  10. package/dist/cli/index.d.ts.map +1 -1
  11. package/dist/cli/index.js +13 -1
  12. package/dist/cli/index.js.map +1 -1
  13. package/dist/cli/init.d.ts +3 -3
  14. package/dist/cli/init.d.ts.map +1 -1
  15. package/dist/cli/init.js +5 -5
  16. package/dist/cli/init.js.map +1 -1
  17. package/dist/cli/serve.d.ts.map +1 -1
  18. package/dist/cli/serve.js +8 -2
  19. package/dist/cli/serve.js.map +1 -1
  20. package/dist/conformance/.generated/can-i-use-browser.d.ts.map +1 -1
  21. package/dist/conformance/.generated/can-i-use-browser.js +1 -1
  22. package/dist/conformance/.generated/can-i-use-browser.js.map +1 -1
  23. package/dist/conformance/.generated/can-i-use.d.ts.map +1 -1
  24. package/dist/conformance/.generated/can-i-use.js +2 -2
  25. package/dist/conformance/.generated/can-i-use.js.map +1 -1
  26. package/dist/conformance/.generated/conformance-docs.d.ts +12 -0
  27. package/dist/conformance/.generated/conformance-docs.d.ts.map +1 -0
  28. package/dist/conformance/.generated/conformance-docs.js +2 -0
  29. package/dist/conformance/.generated/conformance-docs.js.map +1 -0
  30. package/dist/conformance/docs.d.ts +6 -0
  31. package/dist/conformance/docs.d.ts.map +1 -0
  32. package/dist/conformance/docs.js +6 -0
  33. package/dist/conformance/docs.js.map +1 -0
  34. package/dist/remote/index.d.ts.map +1 -1
  35. package/dist/remote/index.js +1 -0
  36. package/dist/remote/index.js.map +1 -1
  37. package/dist/serve/activity-guard.d.ts +4 -0
  38. package/dist/serve/activity-guard.d.ts.map +1 -0
  39. package/dist/serve/activity-guard.js +22 -0
  40. package/dist/serve/activity-guard.js.map +1 -0
  41. package/dist/serve/activity-route.d.ts +4 -0
  42. package/dist/serve/activity-route.d.ts.map +1 -0
  43. package/dist/serve/activity-route.js +157 -0
  44. package/dist/serve/activity-route.js.map +1 -0
  45. package/dist/serve/activity-warning.d.ts +4 -0
  46. package/dist/serve/activity-warning.d.ts.map +1 -0
  47. package/dist/serve/activity-warning.js +21 -0
  48. package/dist/serve/activity-warning.js.map +1 -0
  49. package/dist/serve/bridge-mount.d.ts +5 -0
  50. package/dist/serve/bridge-mount.d.ts.map +1 -1
  51. package/dist/serve/bridge-mount.js +1 -0
  52. package/dist/serve/bridge-mount.js.map +1 -1
  53. package/dist/serve/docs-ui/404.html +3 -0
  54. package/dist/serve/docs-ui/_astro/{_slug_.DPUwShm2.css → _slug_.CXh9yTP1.css} +1 -1
  55. package/dist/serve/docs-ui/_astro/_slug_.DUci-duz.css +1 -0
  56. package/dist/serve/docs-ui/docs/_rhythm/index.html +2 -1
  57. package/dist/serve/docs-ui/docs/agent/set-up-your-agent/index.html +3 -2
  58. package/dist/serve/docs-ui/docs/agent/set-up-your-agent.md +1 -1
  59. package/dist/serve/docs-ui/docs/agent/watch-and-review/index.html +2 -1
  60. package/dist/serve/docs-ui/docs/agent/work-with-an-agent/index.html +2 -1
  61. package/dist/serve/docs-ui/docs/ai-compat/index.html +2 -1
  62. package/dist/serve/docs-ui/docs/api-reference/index.html +51 -49
  63. package/dist/serve/docs-ui/docs/api-reference.md +3 -3
  64. package/dist/serve/docs-ui/docs/app-compat/index.html +2 -1
  65. package/dist/serve/docs-ui/docs/auth-compat/index.html +2 -1
  66. package/dist/serve/docs-ui/docs/build/ai-logic/index.html +18 -3
  67. package/dist/serve/docs-ui/docs/build/ai-logic.md +30 -2
  68. package/dist/serve/docs-ui/docs/build/authentication/index.html +2 -1
  69. package/dist/serve/docs-ui/docs/build/authentication.md +1 -1
  70. package/dist/serve/docs-ui/docs/build/cloud-firestore/index.html +2 -1
  71. package/dist/serve/docs-ui/docs/build/cloud-messaging/index.html +46 -2
  72. package/dist/serve/docs-ui/docs/build/cloud-messaging.md +56 -1
  73. package/dist/serve/docs-ui/docs/build/cloud-storage/index.html +2 -1
  74. package/dist/serve/docs-ui/docs/build/cloud-storage.md +1 -1
  75. package/dist/serve/docs-ui/docs/build/realtime-database/index.html +21 -3
  76. package/dist/serve/docs-ui/docs/build/realtime-database.md +25 -1
  77. package/dist/serve/docs-ui/docs/conformance-scores/index.html +2 -1
  78. package/dist/serve/docs-ui/docs/create-pyric-reference-api/index.html +61 -28
  79. package/dist/serve/docs-ui/docs/create-pyric-reference-api.md +65 -20
  80. package/dist/serve/docs-ui/docs/database-compat/index.html +2 -1
  81. package/dist/serve/docs-ui/docs/firestore-compat/index.html +6 -5
  82. package/dist/serve/docs-ui/docs/firestore-compat.md +4 -4
  83. package/dist/serve/docs-ui/docs/functions-rtdb-compat/index.html +2 -1
  84. package/dist/serve/docs-ui/docs/get-started/how-the-swap-works/index.html +2 -1
  85. package/dist/serve/docs-ui/docs/get-started/start-building/index.html +4 -3
  86. package/dist/serve/docs-ui/docs/get-started/start-building.md +2 -2
  87. package/dist/serve/docs-ui/docs/index.json +979 -579
  88. package/dist/serve/docs-ui/docs/messaging-compat/index.html +2 -1
  89. package/dist/serve/docs-ui/docs/observe/see-whats-happening/index.html +2 -1
  90. package/dist/serve/docs-ui/docs/observe/shape-your-data/index.html +2 -1
  91. package/dist/serve/docs-ui/docs/overview/index.html +2 -1
  92. package/dist/serve/docs-ui/docs/pyric-admin-app-reference-api/index.html +5 -4
  93. package/dist/serve/docs-ui/docs/pyric-admin-app-reference-api.md +3 -4
  94. package/dist/serve/docs-ui/docs/pyric-admin-auth-reference-api/index.html +5 -4
  95. package/dist/serve/docs-ui/docs/pyric-admin-auth-reference-api.md +3 -4
  96. package/dist/serve/docs-ui/docs/pyric-admin-database-reference-api/index.html +5 -4
  97. package/dist/serve/docs-ui/docs/pyric-admin-database-reference-api.md +3 -4
  98. package/dist/serve/docs-ui/docs/pyric-admin-firestore-reference-api/index.html +2503 -12
  99. package/dist/serve/docs-ui/docs/pyric-admin-firestore-reference-api.md +2219 -8
  100. package/dist/serve/docs-ui/docs/pyric-admin-messaging-reference-api/index.html +916 -0
  101. package/dist/serve/docs-ui/docs/pyric-admin-messaging-reference-api.md +706 -0
  102. package/dist/serve/docs-ui/docs/pyric-admin-storage-reference-api/index.html +4 -3
  103. package/dist/serve/docs-ui/docs/pyric-admin-storage-reference-api.md +2 -3
  104. package/dist/serve/docs-ui/docs/pyric-ai-reference-api/index.html +16 -11
  105. package/dist/serve/docs-ui/docs/pyric-ai-reference-api.md +14 -13
  106. package/dist/serve/docs-ui/docs/pyric-ai-scripting-reference-api/index.html +4 -3
  107. package/dist/serve/docs-ui/docs/pyric-ai-scripting-reference-api.md +2 -3
  108. package/dist/serve/docs-ui/docs/pyric-app-reference-api/index.html +7 -2
  109. package/dist/serve/docs-ui/docs/pyric-app-reference-api.md +5 -2
  110. package/dist/serve/docs-ui/docs/pyric-auth-reference-api/index.html +23 -12
  111. package/dist/serve/docs-ui/docs/pyric-auth-reference-api.md +15 -12
  112. package/dist/serve/docs-ui/docs/pyric-cli-assurance-browser-reference-api/index.html +10 -48
  113. package/dist/serve/docs-ui/docs/pyric-cli-assurance-browser-reference-api.md +11 -24
  114. package/dist/serve/docs-ui/docs/pyric-cli-assurance-reference-api/index.html +87 -97
  115. package/dist/serve/docs-ui/docs/pyric-cli-assurance-reference-api.md +53 -65
  116. package/dist/serve/docs-ui/docs/pyric-cli-bridge-client-reference-api/index.html +14 -15
  117. package/dist/serve/docs-ui/docs/pyric-cli-bridge-client-reference-api.md +19 -28
  118. package/dist/serve/docs-ui/docs/pyric-cli-bridge-reference-api/index.html +4 -3
  119. package/dist/serve/docs-ui/docs/pyric-cli-bridge-reference-api.md +2 -3
  120. package/dist/serve/docs-ui/docs/pyric-cli-conformance-browser-reference-api/index.html +3 -2
  121. package/dist/serve/docs-ui/docs/pyric-cli-conformance-browser-reference-api.md +1 -2
  122. package/dist/serve/docs-ui/docs/pyric-cli-conformance-docs-reference-api/index.html +51 -0
  123. package/dist/serve/docs-ui/docs/pyric-cli-conformance-docs-reference-api.md +36 -0
  124. package/dist/serve/docs-ui/docs/pyric-cli-conformance-reference-api/index.html +3 -2
  125. package/dist/serve/docs-ui/docs/pyric-cli-conformance-reference-api.md +1 -2
  126. package/dist/serve/docs-ui/docs/pyric-cli-credentials-node-reference-api/index.html +3 -2
  127. package/dist/serve/docs-ui/docs/pyric-cli-credentials-node-reference-api.md +1 -2
  128. package/dist/serve/docs-ui/docs/pyric-cli-discover-reference-api/index.html +3 -2
  129. package/dist/serve/docs-ui/docs/pyric-cli-discover-reference-api.md +1 -2
  130. package/dist/serve/docs-ui/docs/pyric-cli-register-reference-api/index.html +3 -2
  131. package/dist/serve/docs-ui/docs/pyric-cli-register-reference-api.md +1 -2
  132. package/dist/serve/docs-ui/docs/pyric-cli-remote-reference-api/index.html +1067 -83
  133. package/dist/serve/docs-ui/docs/pyric-cli-remote-reference-api.md +1291 -160
  134. package/dist/serve/docs-ui/docs/pyric-cli-serve-worker-reference-api/index.html +253 -157
  135. package/dist/serve/docs-ui/docs/pyric-cli-serve-worker-reference-api.md +179 -85
  136. package/dist/serve/docs-ui/docs/pyric-cli-verify-reference-api/index.html +72 -7
  137. package/dist/serve/docs-ui/docs/pyric-cli-verify-reference-api.md +22 -7
  138. package/dist/serve/docs-ui/docs/pyric-cli-vite-reference-api/index.html +33 -12
  139. package/dist/serve/docs-ui/docs/pyric-cli-vite-reference-api.md +15 -12
  140. package/dist/serve/docs-ui/docs/pyric-database-reference-api/index.html +18 -17
  141. package/dist/serve/docs-ui/docs/pyric-database-reference-api.md +19 -22
  142. package/dist/serve/docs-ui/docs/pyric-firestore-reference-api/index.html +1512 -545
  143. package/dist/serve/docs-ui/docs/pyric-firestore-reference-api.md +1311 -316
  144. package/dist/serve/docs-ui/docs/pyric-messaging-reference-api/index.html +5 -4
  145. package/dist/serve/docs-ui/docs/pyric-messaging-reference-api.md +3 -4
  146. package/dist/serve/docs-ui/docs/pyric-messaging-sw-reference-api/index.html +69 -369
  147. package/dist/serve/docs-ui/docs/pyric-messaging-sw-reference-api.md +103 -216
  148. package/dist/serve/docs-ui/docs/pyric-rules-reference-api/index.html +10 -9
  149. package/dist/serve/docs-ui/docs/pyric-rules-reference-api.md +9 -10
  150. package/dist/serve/docs-ui/docs/pyric-sandbox-database-reference-api/index.html +8 -7
  151. package/dist/serve/docs-ui/docs/pyric-sandbox-database-reference-api.md +6 -7
  152. package/dist/serve/docs-ui/docs/pyric-sandbox-firestore-reference-api/index.html +10 -9
  153. package/dist/serve/docs-ui/docs/pyric-sandbox-firestore-reference-api.md +9 -10
  154. package/dist/serve/docs-ui/docs/pyric-sandbox-reference-api/index.html +371 -884
  155. package/dist/serve/docs-ui/docs/pyric-sandbox-reference-api.md +369 -883
  156. package/dist/serve/docs-ui/docs/pyric-storage-reference-api/index.html +25 -17
  157. package/dist/serve/docs-ui/docs/pyric-storage-reference-api.md +21 -19
  158. package/dist/serve/docs-ui/docs/pyric-ui-agents-reference-api/index.html +3 -2
  159. package/dist/serve/docs-ui/docs/pyric-ui-agents-reference-api.md +1 -2
  160. package/dist/serve/docs-ui/docs/pyric-ui-auth-hooks-reference-api/index.html +63 -776
  161. package/dist/serve/docs-ui/docs/pyric-ui-auth-hooks-reference-api.md +69 -510
  162. package/dist/serve/docs-ui/docs/pyric-ui-auth-reference-api/index.html +22 -21
  163. package/dist/serve/docs-ui/docs/pyric-ui-auth-reference-api.md +29 -30
  164. package/dist/serve/docs-ui/docs/pyric-ui-events-hooks-reference-api/index.html +15 -168
  165. package/dist/serve/docs-ui/docs/pyric-ui-events-hooks-reference-api.md +17 -87
  166. package/dist/serve/docs-ui/docs/pyric-ui-events-reference-api/index.html +6 -5
  167. package/dist/serve/docs-ui/docs/pyric-ui-events-reference-api.md +5 -6
  168. package/dist/serve/docs-ui/docs/pyric-ui-firestore-hooks-reference-api/index.html +82 -970
  169. package/dist/serve/docs-ui/docs/pyric-ui-firestore-hooks-reference-api.md +104 -479
  170. package/dist/serve/docs-ui/docs/pyric-ui-firestore-reference-api/index.html +30 -29
  171. package/dist/serve/docs-ui/docs/pyric-ui-firestore-reference-api.md +54 -55
  172. package/dist/serve/docs-ui/docs/pyric-ui-primitives-reference-api/index.html +3 -2
  173. package/dist/serve/docs-ui/docs/pyric-ui-primitives-reference-api.md +1 -2
  174. package/dist/serve/docs-ui/docs/pyric-ui-rtdb-reference-api/index.html +5 -4
  175. package/dist/serve/docs-ui/docs/pyric-ui-rtdb-reference-api.md +3 -4
  176. package/dist/serve/docs-ui/docs/pyric-ui-rules-hooks-reference-api/index.html +9 -124
  177. package/dist/serve/docs-ui/docs/pyric-ui-rules-hooks-reference-api.md +7 -58
  178. package/dist/serve/docs-ui/docs/pyric-ui-rules-reference-api/index.html +24 -23
  179. package/dist/serve/docs-ui/docs/pyric-ui-rules-reference-api.md +31 -34
  180. package/dist/serve/docs-ui/docs/pyric-ui-storage-hooks-reference-api/index.html +104 -1385
  181. package/dist/serve/docs-ui/docs/pyric-ui-storage-hooks-reference-api.md +133 -817
  182. package/dist/serve/docs-ui/docs/pyric-ui-storage-reference-api/index.html +34 -33
  183. package/dist/serve/docs-ui/docs/pyric-ui-storage-reference-api.md +37 -38
  184. package/dist/serve/docs-ui/docs/pyric-ui-traffic-hooks-reference-api/index.html +116 -1169
  185. package/dist/serve/docs-ui/docs/pyric-ui-traffic-hooks-reference-api.md +142 -590
  186. package/dist/serve/docs-ui/docs/pyric-ui-traffic-reference-api/index.html +4 -3
  187. package/dist/serve/docs-ui/docs/pyric-ui-traffic-reference-api.md +3 -4
  188. package/dist/serve/docs-ui/docs/rules-compat/index.html +34 -1
  189. package/dist/serve/docs-ui/docs/rules-compat.md +32 -0
  190. package/dist/serve/docs-ui/docs/secure/audit-your-rules/index.html +19 -2
  191. package/dist/serve/docs-ui/docs/secure/audit-your-rules.md +32 -0
  192. package/dist/serve/docs-ui/docs/secure/firestore-rules-limits/index.html +2 -1
  193. package/dist/serve/docs-ui/docs/secure/read-a-denial/index.html +2 -1
  194. package/dist/serve/docs-ui/docs/secure/rtdb-rules-in-typescript/index.html +2 -1
  195. package/dist/serve/docs-ui/docs/secure/rtdb-rules-in-typescript.md +1 -1
  196. package/dist/serve/docs-ui/docs/secure/rules-standard-library/index.html +2 -1
  197. package/dist/serve/docs-ui/docs/secure/secure-it-with-rules/index.html +2 -1
  198. package/dist/serve/docs-ui/docs/secure/simulate-and-lint/index.html +2 -1
  199. package/dist/serve/docs-ui/docs/secure/write-a-rules-test-suite/index.html +2 -1
  200. package/dist/serve/docs-ui/docs/ship/ship-to-production/index.html +2 -1
  201. package/dist/serve/docs-ui/docs/ship/test-in-node/index.html +2 -1
  202. package/dist/serve/docs-ui/docs/storage-compat/index.html +2 -1
  203. package/dist/serve/docs-ui/docs/trust/how-we-know-it-matches-firebase/index.html +2 -1
  204. package/dist/serve/docs-ui/docs/trust/versioning-and-compatibility/index.html +2 -1
  205. package/dist/serve/docs-ui/llms.txt +2 -1
  206. package/dist/serve/entries/ai.d.ts.map +1 -1
  207. package/dist/serve/entries/ai.js +39 -1
  208. package/dist/serve/entries/ai.js.map +1 -1
  209. package/dist/serve/entries/messaging-sw.d.ts.map +1 -1
  210. package/dist/serve/entries/messaging-sw.js +5 -1
  211. package/dist/serve/entries/messaging-sw.js.map +1 -1
  212. package/dist/serve/entries/messaging.d.ts.map +1 -1
  213. package/dist/serve/entries/messaging.js +7 -1
  214. package/dist/serve/entries/messaging.js.map +1 -1
  215. package/dist/serve/entries/runtime.d.ts.map +1 -1
  216. package/dist/serve/entries/runtime.js +20 -1
  217. package/dist/serve/entries/runtime.js.map +1 -1
  218. package/dist/serve/init-payload.d.ts +25 -0
  219. package/dist/serve/init-payload.d.ts.map +1 -1
  220. package/dist/serve/namespace.d.ts +39 -0
  221. package/dist/serve/namespace.d.ts.map +1 -1
  222. package/dist/serve/namespace.js +80 -1
  223. package/dist/serve/namespace.js.map +1 -1
  224. package/dist/serve/standalone-assets.d.ts +11 -9
  225. package/dist/serve/standalone-assets.d.ts.map +1 -1
  226. package/dist/serve/standalone-assets.js +6 -5
  227. package/dist/serve/standalone-assets.js.map +1 -1
  228. package/dist/serve/studio-ui/assets/{RulesCodeEditor-CXdSLKhO.js → RulesCodeEditor-CxVYcZXw.js} +1 -1
  229. package/dist/serve/studio-ui/assets/index-cpSfgQ7H.js +1218 -0
  230. package/dist/serve/studio-ui/assets/seed-DeuFKi4J.js +20 -0
  231. package/dist/serve/studio-ui/index.html +1 -1
  232. package/dist/serve/vite-plugin.d.ts +92 -15
  233. package/dist/serve/vite-plugin.d.ts.map +1 -1
  234. package/dist/serve/vite-plugin.js +281 -11
  235. package/dist/serve/vite-plugin.js.map +1 -1
  236. package/dist/serve/worker/activity-bootstrap.d.ts +8 -0
  237. package/dist/serve/worker/activity-bootstrap.d.ts.map +1 -0
  238. package/dist/serve/worker/activity-bootstrap.js +11 -0
  239. package/dist/serve/worker/activity-bootstrap.js.map +1 -0
  240. package/dist/serve/worker/client/connection.d.ts.map +1 -1
  241. package/dist/serve/worker/client/connection.js +17 -5
  242. package/dist/serve/worker/client/connection.js.map +1 -1
  243. package/dist/serve/worker/client/core.d.ts +2 -2
  244. package/dist/serve/worker/client/core.d.ts.map +1 -1
  245. package/dist/serve/worker/client/core.js +33 -5
  246. package/dist/serve/worker/client/core.js.map +1 -1
  247. package/dist/serve/worker/client/firestore-writes.d.ts.map +1 -1
  248. package/dist/serve/worker/client/firestore-writes.js +1 -0
  249. package/dist/serve/worker/client/firestore-writes.js.map +1 -1
  250. package/dist/serve/worker/client/messaging.d.ts +9 -1
  251. package/dist/serve/worker/client/messaging.d.ts.map +1 -1
  252. package/dist/serve/worker/client/messaging.js +15 -0
  253. package/dist/serve/worker/client/messaging.js.map +1 -1
  254. package/dist/serve/worker/client/studio.d.ts +10 -0
  255. package/dist/serve/worker/client/studio.d.ts.map +1 -1
  256. package/dist/serve/worker/client/studio.js +11 -0
  257. package/dist/serve/worker/client/studio.js.map +1 -1
  258. package/dist/serve/worker/durable-persistence.d.ts +12 -0
  259. package/dist/serve/worker/durable-persistence.d.ts.map +1 -0
  260. package/dist/serve/worker/durable-persistence.js +118 -0
  261. package/dist/serve/worker/durable-persistence.js.map +1 -0
  262. package/dist/serve/worker/entry.d.ts +7 -3
  263. package/dist/serve/worker/entry.d.ts.map +1 -1
  264. package/dist/serve/worker/entry.js +7 -3
  265. package/dist/serve/worker/entry.js.map +1 -1
  266. package/dist/serve/worker/host/core.d.ts +9 -5
  267. package/dist/serve/worker/host/core.d.ts.map +1 -1
  268. package/dist/serve/worker/host/core.js +34 -9
  269. package/dist/serve/worker/host/core.js.map +1 -1
  270. package/dist/serve/worker/host/dispatch.d.ts.map +1 -1
  271. package/dist/serve/worker/host/dispatch.js +9 -2
  272. package/dist/serve/worker/host/dispatch.js.map +1 -1
  273. package/dist/serve/worker/host/firestore-writes.d.ts.map +1 -1
  274. package/dist/serve/worker/host/firestore-writes.js +3 -2
  275. package/dist/serve/worker/host/firestore-writes.js.map +1 -1
  276. package/dist/serve/worker/host/studio.d.ts +1 -1
  277. package/dist/serve/worker/host/studio.d.ts.map +1 -1
  278. package/dist/serve/worker/host/studio.js +36 -1
  279. package/dist/serve/worker/host/studio.js.map +1 -1
  280. package/dist/serve/worker/host/subscriptions.d.ts.map +1 -1
  281. package/dist/serve/worker/host/subscriptions.js +27 -7
  282. package/dist/serve/worker/host/subscriptions.js.map +1 -1
  283. package/dist/serve/worker/host-context.d.ts +15 -0
  284. package/dist/serve/worker/host-context.d.ts.map +1 -1
  285. package/dist/serve/worker/host-context.js +10 -0
  286. package/dist/serve/worker/host-context.js.map +1 -1
  287. package/dist/serve/worker/host-messaging.d.ts.map +1 -1
  288. package/dist/serve/worker/host-messaging.js +9 -2
  289. package/dist/serve/worker/host-messaging.js.map +1 -1
  290. package/dist/serve/worker/index.d.ts +3 -2
  291. package/dist/serve/worker/index.d.ts.map +1 -1
  292. package/dist/serve/worker/index.js +4 -1
  293. package/dist/serve/worker/index.js.map +1 -1
  294. package/dist/serve/worker/protocol.d.ts +25 -3
  295. package/dist/serve/worker/protocol.d.ts.map +1 -1
  296. package/dist/serve/worker/protocol.js +2 -2
  297. package/dist/serve/worker/protocol.js.map +1 -1
  298. package/dist/serve/worker/serve-init.d.ts +23 -37
  299. package/dist/serve/worker/serve-init.d.ts.map +1 -1
  300. package/dist/serve/worker/serve-init.js +95 -172
  301. package/dist/serve/worker/serve-init.js.map +1 -1
  302. package/dist/vite.d.ts +4 -4
  303. package/dist/vite.d.ts.map +1 -1
  304. package/dist/vite.js +3 -3
  305. package/dist/vite.js.map +1 -1
  306. package/package.json +9 -4
  307. package/src/assurance/.generated/conformance-verdicts.ts +1090 -0
  308. package/src/assurance/attachment.ts +211 -0
  309. package/src/assurance/browser.ts +80 -0
  310. package/src/assurance/campaign.ts +496 -0
  311. package/src/assurance/capabilities.ts +446 -0
  312. package/src/assurance/cases.ts +43 -0
  313. package/src/assurance/index.ts +75 -0
  314. package/src/assurance/runner.ts +885 -0
  315. package/src/assurance/tool-names.ts +15 -0
  316. package/src/assurance/tools.ts +830 -0
  317. package/src/assurance/types.ts +352 -0
  318. package/src/assurance/validation.ts +434 -0
  319. package/src/bridge/client/bridge.ts +550 -0
  320. package/src/bridge/client/dispatch.ts +137 -0
  321. package/src/bridge/client.ts +45 -0
  322. package/src/bridge/protocol.ts +351 -0
  323. package/src/bridge/server/audit.ts +53 -0
  324. package/src/bridge/server/bridge.ts +597 -0
  325. package/src/bridge/server/headless.ts +160 -0
  326. package/src/bridge/server/json-schema-to-zod.ts +108 -0
  327. package/src/bridge/server/local-bridge.ts +66 -0
  328. package/src/bridge/server/logger.ts +50 -0
  329. package/src/bridge/server/mcp-contract.ts +93 -0
  330. package/src/bridge/server/mcp.ts +129 -0
  331. package/src/bridge/server/peer.ts +232 -0
  332. package/src/bridge/server/standalone.ts +347 -0
  333. package/src/bridge/server/tool-metadata.ts +99 -0
  334. package/src/bridge/server.ts +28 -0
  335. package/src/cli/can-i-use.ts +50 -0
  336. package/src/cli/cli.test.ts +493 -0
  337. package/src/cli/database-rules.ts +329 -0
  338. package/src/cli/dev-runner.ts +282 -0
  339. package/src/cli/firebase-json.ts +100 -0
  340. package/src/cli/firestore-indexes.ts +70 -0
  341. package/src/cli/index.ts +425 -0
  342. package/src/cli/init.ts +260 -0
  343. package/src/cli/mcp-proxy.ts +196 -0
  344. package/src/cli/parse-args.ts +80 -0
  345. package/src/cli/rules.ts +259 -0
  346. package/src/cli/scope.ts +67 -0
  347. package/src/cli/serve.ts +996 -0
  348. package/src/cli/service-commands.ts +73 -0
  349. package/src/cli/snapshot.ts +164 -0
  350. package/src/cli/storage-rules.ts +172 -0
  351. package/src/cli/verify.ts +416 -0
  352. package/src/conformance/.generated/can-i-use-browser.ts +113 -0
  353. package/src/conformance/.generated/can-i-use.ts +117 -0
  354. package/src/conformance/.generated/conformance-docs.ts +14 -0
  355. package/src/conformance/browser.ts +24 -0
  356. package/src/conformance/can-i-use-tool.ts +51 -0
  357. package/src/conformance/can-i-use.ts +18 -0
  358. package/src/conformance/docs.ts +8 -0
  359. package/src/conformance/index.ts +20 -0
  360. package/src/conformance/tools.ts +16 -0
  361. package/src/credentials/core/memoize-ttl.ts +142 -0
  362. package/src/credentials/core/types.ts +10 -0
  363. package/src/credentials/node/from-adc.ts +96 -0
  364. package/src/credentials/node/from-service-account.ts +133 -0
  365. package/src/credentials/node/index.ts +6 -0
  366. package/src/discover/concurrency.ts +127 -0
  367. package/src/discover/crawler-adapter.ts +142 -0
  368. package/src/discover/crawler.ts +1127 -0
  369. package/src/discover/credential-free.ts +27 -0
  370. package/src/discover/findCollectionGroup.ts +131 -0
  371. package/src/discover/firestore-source.ts +59 -0
  372. package/src/discover/index.ts +5 -0
  373. package/src/discover/merge.ts +523 -0
  374. package/src/discover/session.ts +402 -0
  375. package/src/discover/tools.ts +201 -0
  376. package/src/discover/types.ts +187 -0
  377. package/src/discover/wire.ts +324 -0
  378. package/src/functions-rtdb/child.ts +382 -0
  379. package/src/functions-rtdb/delivery.ts +7 -0
  380. package/src/functions-rtdb/discovery.ts +136 -0
  381. package/src/functions-rtdb/event.ts +64 -0
  382. package/src/functions-rtdb/execution.ts +111 -0
  383. package/src/functions-rtdb/in-memory-delivery.ts +40 -0
  384. package/src/functions-rtdb/project.ts +74 -0
  385. package/src/functions-rtdb/projection.ts +104 -0
  386. package/src/functions-rtdb/reference-pattern.ts +20 -0
  387. package/src/functions-rtdb/remote-delivery.ts +23 -0
  388. package/src/pkg-version.ts +49 -0
  389. package/src/register/esm-exports.ts +63 -0
  390. package/src/register/hooks.ts +30 -0
  391. package/src/register/index.ts +150 -0
  392. package/src/register/mapping.ts +33 -0
  393. package/src/remote/index.ts +1050 -0
  394. package/src/rtdb/crawl-snapshot.ts +118 -0
  395. package/src/rtdb/inspection.ts +137 -0
  396. package/src/rtdb/load-rules-document.ts +56 -0
  397. package/src/rtdb/rules-generation-tool.ts +38 -0
  398. package/src/rtdb/rules-json.ts +29 -0
  399. package/src/serve/activity-guard.ts +29 -0
  400. package/src/serve/activity-route.ts +164 -0
  401. package/src/serve/activity-warning.ts +26 -0
  402. package/src/serve/bridge-mount.ts +200 -0
  403. package/src/serve/bundler.ts +493 -0
  404. package/src/serve/capture-store.ts +65 -0
  405. package/src/serve/discovery.ts +199 -0
  406. package/src/serve/entries/ai.ts +292 -0
  407. package/src/serve/entries/app-backend.ts +4 -0
  408. package/src/serve/entries/app-client.ts +26 -0
  409. package/src/serve/entries/app-session-store.ts +47 -0
  410. package/src/serve/entries/app.ts +11 -0
  411. package/src/serve/entries/auth-helper-core.ts +181 -0
  412. package/src/serve/entries/auth-helper-dom.ts +134 -0
  413. package/src/serve/entries/auth-helper-runtime.ts +20 -0
  414. package/src/serve/entries/auth.ts +255 -0
  415. package/src/serve/entries/bridge-url.ts +31 -0
  416. package/src/serve/entries/database.ts +129 -0
  417. package/src/serve/entries/firestore.ts +232 -0
  418. package/src/serve/entries/init.ts +46 -0
  419. package/src/serve/entries/keepalive.ts +46 -0
  420. package/src/serve/entries/messaging-sw.ts +75 -0
  421. package/src/serve/entries/messaging.ts +96 -0
  422. package/src/serve/entries/runtime.ts +540 -0
  423. package/src/serve/entries/session-store.ts +97 -0
  424. package/src/serve/entries/storage.ts +92 -0
  425. package/src/serve/entries/tab-sync-wiring.ts +274 -0
  426. package/src/serve/entries/worker-runtime.ts +75 -0
  427. package/src/serve/init-payload.ts +42 -0
  428. package/src/serve/namespace.ts +675 -0
  429. package/src/serve/open-browser.ts +68 -0
  430. package/src/serve/rules.ts +272 -0
  431. package/src/serve/sandbox-marker.ts +31 -0
  432. package/src/serve/server.ts +428 -0
  433. package/src/serve/standalone-assets.ts +180 -0
  434. package/src/serve/state-store.ts +166 -0
  435. package/src/serve/studio/disk-project-store.ts +185 -0
  436. package/src/serve/studio/disk-workspace.ts +162 -0
  437. package/src/serve/studio/index.ts +20 -0
  438. package/src/serve/studio/routes.ts +237 -0
  439. package/src/serve/studio/store-types.ts +48 -0
  440. package/src/serve/studio/studio-storage.test.ts +289 -0
  441. package/src/serve/vite-plugin.ts +1102 -0
  442. package/src/serve/worker/activity-bootstrap.ts +24 -0
  443. package/src/serve/worker/client/admin-firestore.ts +38 -0
  444. package/src/serve/worker/client/ai.ts +124 -0
  445. package/src/serve/worker/client/auth.ts +489 -0
  446. package/src/serve/worker/client/connection.ts +202 -0
  447. package/src/serve/worker/client/core.ts +350 -0
  448. package/src/serve/worker/client/disconnect.ts +45 -0
  449. package/src/serve/worker/client/firestore-reads.ts +181 -0
  450. package/src/serve/worker/client/firestore-refs.ts +226 -0
  451. package/src/serve/worker/client/firestore-writes.ts +232 -0
  452. package/src/serve/worker/client/handles.ts +91 -0
  453. package/src/serve/worker/client/messaging.ts +119 -0
  454. package/src/serve/worker/client/presence.ts +174 -0
  455. package/src/serve/worker/client/rtdb.ts +311 -0
  456. package/src/serve/worker/client/rules.ts +63 -0
  457. package/src/serve/worker/client/service-worker-connection.ts +59 -0
  458. package/src/serve/worker/client/snapshots.ts +86 -0
  459. package/src/serve/worker/client/storage.ts +195 -0
  460. package/src/serve/worker/client/studio.ts +88 -0
  461. package/src/serve/worker/client.ts +57 -0
  462. package/src/serve/worker/durable-persistence.ts +137 -0
  463. package/src/serve/worker/entry.ts +179 -0
  464. package/src/serve/worker/host/admin-firestore.ts +88 -0
  465. package/src/serve/worker/host/connection.ts +179 -0
  466. package/src/serve/worker/host/core.ts +387 -0
  467. package/src/serve/worker/host/dispatch.ts +316 -0
  468. package/src/serve/worker/host/firestore-reads.ts +117 -0
  469. package/src/serve/worker/host/firestore-writes.ts +456 -0
  470. package/src/serve/worker/host/presence.ts +312 -0
  471. package/src/serve/worker/host/rtdb.ts +136 -0
  472. package/src/serve/worker/host/rules.ts +128 -0
  473. package/src/serve/worker/host/storage.ts +312 -0
  474. package/src/serve/worker/host/studio.ts +75 -0
  475. package/src/serve/worker/host/subscriptions.ts +238 -0
  476. package/src/serve/worker/host-ai.ts +165 -0
  477. package/src/serve/worker/host-auth.ts +469 -0
  478. package/src/serve/worker/host-context.ts +273 -0
  479. package/src/serve/worker/host-events.ts +94 -0
  480. package/src/serve/worker/host-messaging.ts +240 -0
  481. package/src/serve/worker/host.ts +47 -0
  482. package/src/serve/worker/index.ts +196 -0
  483. package/src/serve/worker/presence-timing.ts +15 -0
  484. package/src/serve/worker/protocol.ts +1112 -0
  485. package/src/serve/worker/serve-init.ts +606 -0
  486. package/src/serve/worker/service-worker-channel.ts +33 -0
  487. package/src/serve/worker/service-worker-relay.ts +86 -0
  488. package/src/serve/writer-lock.ts +53 -0
  489. package/src/verify/cases.ts +233 -0
  490. package/src/verify/fixture.ts +258 -0
  491. package/src/verify/index.ts +519 -0
  492. package/src/verify/tools.ts +108 -0
  493. package/src/version/compat-target.ts +17 -0
  494. package/src/vite.ts +17 -0
  495. package/dist/serve/docs-ui/docs/pyric-firestore-values-reference-api/index.html +0 -30
  496. package/dist/serve/docs-ui/docs/pyric-firestore-values-reference-api.md +0 -30
  497. package/dist/serve/studio-ui/assets/index-BvtQIpGn.js +0 -1211
  498. package/dist/serve/studio-ui/assets/seed-3DMedLL3.js +0 -2
@@ -0,0 +1,540 @@
1
+ /**
2
+ * `pyric dev` in-page runtime — the shared chunk every served SDK bundle
3
+ * closes over. One sandbox per page; init payload (rules, future bridge URL)
4
+ * fetched from `/__pyric/init.json` at module init.
5
+ *
6
+ * TOP-LEVEL AWAIT IS LOAD-BEARING: every importer (the auth/firestore bundles,
7
+ * and therefore the user's app module) is deferred until this module resolves,
8
+ * so by the time app code runs the rules are deployed — no
9
+ * "first write raced the ruleset" class of bugs.
10
+ *
11
+ * This file is BUNDLED FOR THE BROWSER by `../bundler.ts` (esbuild) — it is
12
+ * never imported by node-side @pyric/cli code. Bundle splitting must keep it
13
+ * a single shared chunk: two copies would mean two sandboxes (P0 validation
14
+ * constraint, design rationale section 9).
15
+ */
16
+ import {
17
+ recordBackendOverBlob,
18
+ serializeToBuckets,
19
+ bundleRecords,
20
+ } from 'pyric/sandbox';
21
+ import { getFirestore } from 'pyric/firestore';
22
+ import { seedDocuments, setRules, snapshotDocuments } from 'pyric/sandbox/firestore';
23
+ import { getDatabase, sandbox as rtdbSandbox } from 'pyric/database';
24
+ import { getStorageSandbox } from 'pyric/storage';
25
+ import { getAuth, onAuthStateChanged, signOut, sandbox as authOps, type SeedUser } from 'pyric/auth';
26
+ import {
27
+ callTool as workerCallTool,
28
+ relayWorkerOp,
29
+ relayWorkerSub,
30
+ } from '../worker/client.js';
31
+ import { useWorker, workerDb } from './worker-runtime.js';
32
+ import { keepaliveSafe } from './keepalive.js';
33
+ import { toPageOriginWsUrl } from './bridge-url.js';
34
+ import { buildVerifyFixture } from '../../verify/fixture.js';
35
+ import type { InitPayload } from '../init-payload.js';
36
+ import { setupFirebaseActivityGuard } from '../activity-guard.js';
37
+ export { sandbox } from './app-backend.js';
38
+ import { sandbox } from './app-backend.js';
39
+
40
+ /**
41
+ * The DEFAULT-ON worker path (Phase 3c): when SharedWorker is available the
42
+ * `firebase/*` entry adapters route to the worker-hosted sandbox instead of
43
+ * the in-page one. The worker owns rules/seed/persist/capture/session (it
44
+ * fetches the SAME `/__pyric/init.json`), so on this path the page MUST NOT
45
+ * re-run that backend init below — doing so would double-POST captures and
46
+ * fight the worker over `/__pyric/state`. Unsupported browsers fall back to
47
+ * the in-page sandbox + the shipped BroadcastChannel sync (strict enhancement).
48
+ *
49
+ * `globalThis.__PYRIC_FORCE_INPAGE__` is an explicit opt-out: a host that does
50
+ * NOT serve the worker bundle (`/__pyric/sdk/worker.js`) sets it before this
51
+ * module evaluates so the page takes the in-page path instead of trying to load
52
+ * a worker that 404s. `pyric dev` always serves the worker and never sets it;
53
+ * the `@pyric/cli/vite` plugin sets it until it serves the worker (M2).
54
+ */
55
+ /**
56
+ * The in-page sandbox. On the worker path it is NOT the data backend — it
57
+ * stays mounted as the FALLBACK primitive. Worker-mode provider UI reads the
58
+ * worker user directory directly and never constructs a page-local Auth
59
+ * backend.
60
+ */
61
+
62
+ interface ServeDiagnostics {
63
+ sandboxReady: boolean;
64
+ rulesDeployed: boolean;
65
+ databaseRulesDeployed: boolean;
66
+ storageRulesDeployed: boolean;
67
+ rulesHash: string | null;
68
+ databaseRulesHash: string | null;
69
+ storageRulesHash: string | null;
70
+ initError: string | null;
71
+ bridgeConnected: boolean;
72
+ seededDocs: number;
73
+ persistEnabled: boolean;
74
+ /** Epoch ms of the last successful state flush to the server. */
75
+ lastFlushAt: number | null;
76
+ /** True when another tab holds the writer lock — this tab won't persist. */
77
+ persistReadOnly: boolean;
78
+ /** True when a `--seed` fixture was withheld because the sandbox already
79
+ * held restored/lived data (see the seed-docs block below). */
80
+ seedSkipped: boolean;
81
+ }
82
+
83
+ declare global {
84
+ // eslint-disable-next-line no-var
85
+ var __pyricServe: ServeDiagnostics;
86
+ }
87
+
88
+ const diagnostics: ServeDiagnostics = {
89
+ sandboxReady: true,
90
+ rulesDeployed: false,
91
+ databaseRulesDeployed: false,
92
+ storageRulesDeployed: false,
93
+ rulesHash: null,
94
+ databaseRulesHash: null,
95
+ storageRulesHash: null,
96
+ initError: null,
97
+ bridgeConnected: false,
98
+ seededDocs: 0,
99
+ persistEnabled: false,
100
+ lastFlushAt: null,
101
+ persistReadOnly: false,
102
+ seedSkipped: false,
103
+ };
104
+ globalThis.__pyricServe = diagnostics;
105
+
106
+ let bridgeUrlFromPayload: string | null = null;
107
+ let activityTokenFromPayload: string | null = null;
108
+
109
+ // ── init payload: fetch + apply (top-level await — see header) ────────
110
+ // WORKER PATH: skipped — the worker fetches the same init.json and owns
111
+ // rules/seed/persist/capture/session. Running it here too would double-POST
112
+ // captures and fight the worker over the /__pyric/state writer lock.
113
+ if (!useWorker) try {
114
+ const res = await fetch('/__pyric/init.json');
115
+ if (!res.ok) throw new Error(`/__pyric/init.json → ${res.status}`);
116
+ const payload = (await res.json()) as InitPayload;
117
+ bridgeUrlFromPayload = payload.bridgeUrl;
118
+ activityTokenFromPayload = payload.activityToken ?? null;
119
+ // Register RTDB with the persistence registry BEFORE enablePersistence
120
+ // (below) so the restored tree rides the controller blob and is applied
121
+ // during restore-on-attach — same eager-registration reasoning as the
122
+ // worker path (serve-init.ts). `getDatabase(sandbox)` is idempotent
123
+ // (one backend per sandbox) so a later rules/op call reuses it.
124
+ getDatabase(sandbox);
125
+ if (payload.rules) {
126
+ const lint = setRules(sandbox, payload.rules);
127
+ if (lint.parseError) {
128
+ // The server lints before serving, so this is a defensive surface —
129
+ // loud, but don't brick the page: the sandbox keeps its default rules.
130
+ throw new Error(
131
+ `firestore.rules failed to parse in the sandbox: ${JSON.stringify(lint.parseError)}`,
132
+ );
133
+ }
134
+ diagnostics.rulesDeployed = true;
135
+ diagnostics.rulesHash = payload.rulesHash;
136
+ }
137
+ if (payload.databaseRules) {
138
+ rtdbSandbox.setRules(getDatabase(sandbox), payload.databaseRules);
139
+ diagnostics.databaseRulesDeployed = true;
140
+ diagnostics.databaseRulesHash = payload.databaseRulesHash ?? null;
141
+ }
142
+ // Open the ONE per-sandbox storage service eagerly: the FIRST open wins the
143
+ // rules AND the project-scoped IDB name (`pyric-storage:<projectKey>`,
144
+ // issue #359) — a lazy first open from app code would land on the legacy
145
+ // shared database. `projectKey` is absent on older servers → legacy name.
146
+ getStorageSandbox(sandbox, {
147
+ ...(payload.storageRules ? { rules: payload.storageRules } : {}),
148
+ ...(payload.projectKey ? { projectId: payload.projectKey } : {}),
149
+ });
150
+ if (payload.storageRules) {
151
+ diagnostics.storageRulesDeployed = true;
152
+ diagnostics.storageRulesHash = payload.storageRulesHash;
153
+ }
154
+ // Auth users land first in either mode (persist restore or state-file
155
+ // fixture) so restored docs' owner uids resolve in rules and the session
156
+ // restore below has a DB to restore into.
157
+ if (payload.authUsers && payload.authUsers.length > 0) {
158
+ authOps.seedUsers(getAuth(sandbox), payload.authUsers as unknown as SeedUser[]);
159
+ }
160
+ if (!payload.persist && payload.seedState) {
161
+ // ── ephemeral fixture (`--seed <state-file>`): restore the blob through
162
+ // the persistence controller's OWN deserializer (wrapper re-hydration —
163
+ // seedDocuments on raw marker JSON would break instanceof in rules) via
164
+ // a read-only backend. Writes go nowhere: ephemeral stays ephemeral.
165
+ const seedSnap = payload.seedState as {
166
+ firestore?: Record<string, Record<string, unknown>>;
167
+ services?: Record<string, unknown>;
168
+ };
169
+ const fixtureBundle = bundleRecords(
170
+ serializeToBuckets(seedSnap.firestore ?? {}, seedSnap.services ?? {}, 0),
171
+ );
172
+ // NOTE (pre-mortem #6b): this attaches a PERMANENT controller whose
173
+ // write() is a no-op — so every later mutation in this page pays the
174
+ // whole-state serialize cost to feed a sink. pyric exposes no
175
+ // `disablePersistence`/dispose, so a one-shot restore isn't possible from
176
+ // here without a pyric change (tracked as a pyric follow-up). Accepted:
177
+ // fixture pages are demo-sized; the cost is bounded and invisible to
178
+ // correctness.
179
+ await sandbox.enablePersistence({
180
+ key: 'pyric-serve-fixture',
181
+ injectedBackend: recordBackendOverBlob({
182
+ read: async () => fixtureBundle,
183
+ write: async () => {},
184
+ clear: async () => {},
185
+ }),
186
+ });
187
+ const docs = (payload.seedState as { firestore?: Record<string, unknown> }).firestore;
188
+ diagnostics.seededDocs = docs ? Object.keys(docs).length : 0;
189
+ }
190
+ if (payload.persist) {
191
+ // ── persist mode (flow doc section 3c): the substrate is durable. The
192
+ // sandbox's OWN persistence controller with an HTTP backend over
193
+ // /__pyric/state — it owns restore-on-attach (wrapper re-hydration),
194
+ // debounced auto-flush, and the beforeunload safety flush.
195
+ const section = (name: string): string => `/__pyric/state?section=${name}`;
196
+ // Single-writer lock (pre-mortem #3): a per-page id claims the writer
197
+ // role on first flush; a second tab gets 423 and drops to read-only so
198
+ // it can't erase this tab's world. The server frees a stale lock, so a
199
+ // reload after the writer closed can reclaim it.
200
+ const writerId =
201
+ typeof crypto !== 'undefined' && crypto.randomUUID ? crypto.randomUUID() : `w-${Date.now()}`;
202
+ const writerHeaders = { 'content-type': 'application/json', 'x-pyric-writer': writerId };
203
+ let readOnly = false;
204
+ const lostWriterLock = (): void => {
205
+ if (readOnly) return;
206
+ readOnly = true;
207
+ diagnostics.persistReadOnly = true;
208
+ console.warn(
209
+ '[pyric dev] another tab is the persist writer — THIS tab is read-only ' +
210
+ '(your changes here will NOT be saved). Close the other tab and reload to take over.',
211
+ );
212
+ };
213
+ await sandbox.enablePersistence({
214
+ key: 'pyric-serve',
215
+ injectedBackend: recordBackendOverBlob({
216
+ async read() {
217
+ const res = await fetch(section('firestore'));
218
+ return res.status === 200 ? await res.text() : null;
219
+ },
220
+ async write(value) {
221
+ if (readOnly) return; // a non-writer tab silently no-ops its flushes
222
+ const res = await fetch(section('firestore'), {
223
+ method: 'POST',
224
+ headers: writerHeaders,
225
+ body: value,
226
+ // The controller's beforeunload flush fires THIS write as the
227
+ // page is leaving; a plain fetch is aborted on unload (pre-mortem
228
+ // #1 — the "crash-only" claim was wrong). `keepalive` lets a
229
+ // small final flush survive unload. It's capped (~64KB across
230
+ // all keepalive requests), so large states skip it and warn
231
+ // once — those lose the final unsaved delta on tab close
232
+ // (reload-based flushes are unaffected).
233
+ keepalive: keepaliveSafe(value),
234
+ });
235
+ if (res.status === 423) return lostWriterLock();
236
+ if (!res.ok) throw new Error(`state flush → ${res.status}`);
237
+ diagnostics.lastFlushAt = Date.now();
238
+ },
239
+ async clear() {
240
+ if (readOnly) return;
241
+ await fetch(section('firestore'), { method: 'POST', headers: writerHeaders, body: 'null' });
242
+ },
243
+ }),
244
+ });
245
+ diagnostics.persistEnabled = true;
246
+
247
+ // Heartbeat: keep an alive-but-idle writer's lock fresh. PUT refreshes
248
+ // the lock WITHOUT writing state (a POST would clobber). Release on
249
+ // pagehide so another tab can take over promptly.
250
+ const heartbeat = setInterval(() => {
251
+ if (readOnly) return;
252
+ void fetch('/__pyric/state', { method: 'PUT', headers: writerHeaders }).then((res) => {
253
+ if (res.status === 423) {
254
+ lostWriterLock();
255
+ clearInterval(heartbeat);
256
+ }
257
+ }, () => {});
258
+ }, 20_000);
259
+ if (typeof window !== 'undefined') {
260
+ window.addEventListener('pagehide', () => {
261
+ clearInterval(heartbeat);
262
+ if (!readOnly) {
263
+ fetch('/__pyric/state', { method: 'DELETE', headers: writerHeaders, keepalive: true }).catch(() => {});
264
+ }
265
+ });
266
+ }
267
+
268
+ // Auth user-DB changes flush separately (the controller's blob is
269
+ // firestore-only). Debounced; exportUsers→seedUsers round-trips.
270
+ const auth = getAuth(sandbox);
271
+ let authFlushTimer: ReturnType<typeof setTimeout> | null = null;
272
+ authOps.subscribeUsers(auth, () => {
273
+ if (readOnly) return;
274
+ if (authFlushTimer) clearTimeout(authFlushTimer);
275
+ authFlushTimer = setTimeout(() => {
276
+ void fetch(section('auth'), {
277
+ method: 'POST',
278
+ headers: writerHeaders,
279
+ body: JSON.stringify({ users: authOps.exportUsers(auth) }),
280
+ }).then(
281
+ (res) => {
282
+ if (res.status === 423) lostWriterLock();
283
+ else if (res.ok) diagnostics.lastFlushAt = Date.now();
284
+ },
285
+ (e) => console.warn('[pyric dev] auth state flush failed:', e),
286
+ );
287
+ }, 500);
288
+ });
289
+ }
290
+ if (payload.capture) {
291
+ // ── capture mode (the write-side of the pyric verify loop): whenever
292
+ // the sandbox changes, push a snapshot of the full session fixture
293
+ // (rules + history + firestore state) to /__pyric/capture. The server
294
+ // writes it verbatim to `.pyric/last-session.json` so `pyric verify`
295
+ // can replay without any extra arguments. This is independent of
296
+ // --persist — capture is about the verify loop, persist is about
297
+ // cross-reload durability.
298
+ const flushCapture = (): void => {
299
+ const rtdbState =
300
+ payload.databaseRules || sandbox.history().some((event) => event.service === 'rtdb')
301
+ ? rtdbSandbox.snapshotState(getDatabase(sandbox))
302
+ : undefined;
303
+ const auth = getAuth(sandbox);
304
+ const body = JSON.stringify(buildVerifyFixture({
305
+ sandbox,
306
+ firestoreRules: payload.rules,
307
+ rtdbRules: payload.databaseRules ?? null,
308
+ rtdbState,
309
+ rtdbDatabaseUrl: payload.databaseUrl ?? null,
310
+ authState: {
311
+ users: authOps.exportUsers(auth),
312
+ currentUser: auth.currentUser,
313
+ },
314
+ }));
315
+ fetch('/__pyric/capture', {
316
+ method: 'POST',
317
+ headers: { 'content-type': 'application/json' },
318
+ body,
319
+ keepalive: keepaliveSafe(body),
320
+ }).catch(() => {});
321
+ };
322
+
323
+ // Debounced flush on every sandbox event — coalesces rapid writes (e.g.
324
+ // seeding multiple docs) into a single push. 400ms matches the cadence
325
+ // of the persist auth flush; small enough to feel live, large enough to
326
+ // avoid one-POST-per-write on bulk ops.
327
+ let captureTimer: ReturnType<typeof setTimeout> | null = null;
328
+ sandbox.onEvent(() => {
329
+ if (captureTimer) clearTimeout(captureTimer);
330
+ captureTimer = setTimeout(flushCapture, 400);
331
+ });
332
+
333
+ // Unload flush — best-effort, keepalive. Separate from any persist
334
+ // pagehide listener so they don't interfere.
335
+ if (typeof window !== 'undefined') {
336
+ window.addEventListener('pagehide', () => {
337
+ flushCapture();
338
+ });
339
+ }
340
+ }
341
+ if (payload.seed && Object.keys(payload.seed).length > 0) {
342
+ // A seed fixture applies only into an empty home — mirrors the worker's
343
+ // guard (serve/worker/serve-init.ts): by this point any --persist restore
344
+ // above has already run, so an existing document means lived/restored
345
+ // data, not a blank slate. Never stomp it with a fixture.
346
+ const existing = Object.keys(snapshotDocuments(sandbox)).length > 0;
347
+ if (existing) {
348
+ diagnostics.seedSkipped = true;
349
+ console.info(
350
+ '[pyric dev] --seed skipped: the sandbox already has restored data — the fixture would ' +
351
+ 'have overwritten it. Use `--persist --fresh` to discard existing state and re-seed.',
352
+ );
353
+ } else {
354
+ // Admin-style fixture load (bypasses rules) — runs before app code
355
+ // (top-level await), so the app's first render sees the seeded state.
356
+ seedDocuments(sandbox, payload.seed);
357
+ diagnostics.seededDocs = Object.keys(payload.seed).length;
358
+ }
359
+ }
360
+ } catch (e) {
361
+ diagnostics.initError = e instanceof Error ? e.message : String(e);
362
+ console.error(
363
+ '[pyric dev] init failed — the sandbox is running WITHOUT your project rules:',
364
+ diagnostics.initError,
365
+ );
366
+ }
367
+
368
+ // Unsupported/forced-in-page browsers own a real page-local sandbox rather
369
+ // than the SharedWorker backend, so they install the same default-on guard at
370
+ // this feed seam. Top-level await keeps this in place before application code.
371
+ if (!useWorker && activityTokenFromPayload) {
372
+ setupFirebaseActivityGuard(
373
+ {
374
+ history: () => sandbox.history(),
375
+ subscribe: (listener) => sandbox.onEvent(listener),
376
+ },
377
+ fetch,
378
+ activityTokenFromPayload,
379
+ );
380
+ }
381
+
382
+ // ── provenance (flow doc section 3a): make the shim's presence unmistakable.
383
+ // Source says `firebase/*`; what runs is the pyric sandbox — say so loudly
384
+ // once, and explain any stack frame that points into /__pyric/sdk/. ────────
385
+ console.info(
386
+ `[pyric dev] firebase/* on this page is served by the pyric sandbox` +
387
+ (useWorker
388
+ ? ' in a SharedWorker (one backend for all tabs; rules/seed/persist owned by the worker)'
389
+ : diagnostics.rulesHash || diagnostics.databaseRulesHash || diagnostics.storageRulesHash
390
+ ? ' (project rules loaded)'
391
+ : ' (no project rules)') +
392
+ ` — diagnostics: globalThis.__pyricServe`,
393
+ );
394
+
395
+ let provenanceHintShown = false;
396
+ function explainSandboxFrame(stackOrUrl: string | undefined): void {
397
+ if (provenanceHintShown || !stackOrUrl || !stackOrUrl.includes('/__pyric/sdk/')) return;
398
+ provenanceHintShown = true;
399
+ console.info(
400
+ '[pyric dev] the error above originates in the pyric sandbox shim serving firebase/*, ' +
401
+ 'not the real Firebase SDK — behavior can differ where COMPAT coverage is incomplete.',
402
+ );
403
+ }
404
+ if (typeof window !== 'undefined') {
405
+ window.addEventListener('error', (e) =>
406
+ explainSandboxFrame(e.error instanceof Error ? (e.error.stack ?? e.filename) : e.filename),
407
+ );
408
+ window.addEventListener('unhandledrejection', (e) =>
409
+ explainSandboxFrame(e.reason instanceof Error ? e.reason.stack : undefined),
410
+ );
411
+ }
412
+
413
+ // ── bridge peer (only when `pyric dev --bridge` put a URL in the payload;
414
+ // dynamic import so bridge-less pages never load the client chunk) ─────
415
+ async function connectBridgePeer(rawUrl: string): Promise<void> {
416
+ // Re-anchor the bridge WS to THIS page's origin so it reaches the server the
417
+ // page was actually served from over Tailscale / a LAN IP / https, not the
418
+ // server's baked-in localhost. See bridge-url.ts.
419
+ const url = toPageOriginWsUrl(rawUrl, window.location);
420
+ const { connectBridge } = await import('../../bridge/client/bridge.js');
421
+ // On the worker path, route agent tool-calls THROUGH the SharedWorker so the
422
+ // agent shares the one sandbox the app + Studio use (no separate in-page
423
+ // backend). Off the worker path (in-page fallback), dispatch in-page as before.
424
+ if (useWorker && workerDb) {
425
+ const wdb = workerDb;
426
+ connectBridge(sandbox, {
427
+ url,
428
+ dispatcher: (_sandbox, name, args) => workerCallTool(wdb, name, args),
429
+ // Generic worker relay (remote sandbox, slice 1): server-side Node code
430
+ // (`connectRemoteSandbox`) reaches THIS page's SharedWorker through the
431
+ // bridge — ops and snap-delivering subscriptions pass straight through
432
+ // to the one sandbox the app + Studio + agent share.
433
+ workerRelay: {
434
+ op: (op) => relayWorkerOp(wdb, op),
435
+ subscribe: (sub, onValue) => relayWorkerSub(wdb, sub, onValue),
436
+ },
437
+ });
438
+ } else {
439
+ connectBridge(sandbox, { url });
440
+ }
441
+ diagnostics.bridgeConnected = true;
442
+ console.info('[pyric dev] sandbox registered with the MCP bridge at', url);
443
+ }
444
+ if (bridgeUrlFromPayload) {
445
+ // In-page path: bridgeUrlFromPayload came from the (already-blocking) init
446
+ // block above, so connect inline.
447
+ try {
448
+ await connectBridgePeer(bridgeUrlFromPayload);
449
+ } catch (e) {
450
+ console.error('[pyric dev] bridge connect failed:', e instanceof Error ? e.message : String(e));
451
+ }
452
+ } else if (useWorker) {
453
+ // Worker path: the worker owns serve-init, so the page skipped the init.json
454
+ // fetch. Discover the bridge URL (the agent routes its tool-calls THROUGH the
455
+ // worker) in a FIRE-AND-FORGET task so app boot is NEVER blocked — runtime.ts
456
+ // is on the load-bearing top-level-await path, and the agent connecting a beat
457
+ // later is fine. Bridge-less pages just read a null bridgeUrl and stop.
458
+ void (async () => {
459
+ try {
460
+ const res = await fetch('/__pyric/init.json');
461
+ const url = res.ok ? ((await res.json()) as InitPayload).bridgeUrl : null;
462
+ if (url) await connectBridgePeer(url);
463
+ } catch (e) {
464
+ console.error('[pyric dev] bridge connect failed:', e instanceof Error ? e.message : String(e));
465
+ }
466
+ })();
467
+ }
468
+
469
+ // ── cross-tab realtime sync — wire AFTER sandbox + auth are fully set up ──────
470
+ //
471
+ // Two independent channels, two independent kinds of state:
472
+ //
473
+ // Channel 1 — Firestore writes (`pyric:serve:tabsync`):
474
+ // `sandbox.enableTabSync` owns the channel entirely. It broadcasts every
475
+ // committed Firestore write event to peer tabs and runs the hello/state
476
+ // late-join handshake so a freshly-opened tab inherits the current
477
+ // Firestore documents. A write in Tab A fires `onSnapshot` listeners in Tab B
478
+ // — restoring production's cross-client realtime without a server.
479
+ //
480
+ // Channel 2 — Auth state (`pyric:serve:auth-sync`):
481
+ // Auth lives outside the Firestore environment so `enableTabSync` can't
482
+ // carry it. `wireAuthTabSync` bridges sign-in/sign-out/user-DB changes
483
+ // over its own BroadcastChannel using a full-state protocol (see
484
+ // `tab-sync-wiring.ts` for the detailed protocol + echo-guard rationale).
485
+ //
486
+ // Both are no-ops in non-browser environments (`BroadcastChannel` absent).
487
+ //
488
+ // WORKER PATH: retired. The SharedWorker IS the single backend for all tabs, so
489
+ // cross-tab Firestore + auth are automatic — the in-page sandbox these channels
490
+ // would sync isn't the data backend here. Kept ONLY for the in-page fallback
491
+ // (the tier the plan designates for browsers without SharedWorker).
492
+ if (!useWorker && typeof BroadcastChannel !== 'undefined') {
493
+ // 1. Firestore cross-tab — library primitive does the heavy lifting.
494
+ sandbox.enableTabSync({
495
+ channel: new BroadcastChannel('pyric:serve:tabsync'),
496
+ });
497
+
498
+ // 2. Auth cross-tab — our own bridge (see tab-sync-wiring.ts).
499
+ // Dynamic import keeps the module out of the critical path; the channel
500
+ // is opened synchronously above so `hello` is posted as soon as the
501
+ // listener is wired (wireAuthTabSync handles ordering internally).
502
+ void import('./tab-sync-wiring.js').then(({ wireAuthTabSync }) => {
503
+ wireAuthTabSync(
504
+ getAuth(sandbox),
505
+ // Cast: authOps's generics are narrowed to `Auth` which is exactly what
506
+ // wireAuthTabSync expects; `as` here avoids a deep generic unification
507
+ // that TypeScript can't resolve across module boundaries.
508
+ authOps as import('./tab-sync-wiring.js').AuthOps,
509
+ onAuthStateChanged,
510
+ signOut,
511
+ );
512
+ });
513
+ }
514
+
515
+ // ── rules hot-reload (SSE) — `pyric dev` watches firestore.rules and
516
+ // broadcasts; we re-deploy in place, no page refresh. EventSource is
517
+ // browser-only; guarded so the bundle stays inert under other runtimes. ──
518
+ // IN-PAGE PATH ONLY: open a per-tab SSE to re-deploy rules to the in-page
519
+ // sandbox. On the WORKER path the SharedWorker owns ONE hot-reload stream for
520
+ // the whole origin (see entry.ts), so tabs open NO SSE connection — multi-tab
521
+ // pages would otherwise exhaust Chrome's ~6-connections-per-origin cap and a
522
+ // later tab's navigation would hang until an earlier tab closed.
523
+ if (!useWorker && typeof EventSource !== 'undefined') {
524
+ const events = new EventSource('/__pyric/events');
525
+ events.addEventListener('rules-changed', (e) => {
526
+ try {
527
+ const { rules, rulesHash } = JSON.parse((e as MessageEvent).data as string) as {
528
+ rules: string;
529
+ rulesHash: string;
530
+ };
531
+ const lint = setRules(sandbox, rules);
532
+ if (lint.parseError) throw new Error(JSON.stringify(lint.parseError));
533
+ diagnostics.rulesDeployed = true;
534
+ diagnostics.rulesHash = rulesHash;
535
+ console.info(`[pyric dev] firestore.rules hot-reloaded (hash ${rulesHash})`);
536
+ } catch (err) {
537
+ console.error('[pyric dev] rules hot-reload failed:', err instanceof Error ? err.message : String(err));
538
+ }
539
+ });
540
+ }
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Auth session persistence for `pyric dev` — the one genuine
3
+ * client-fidelity item in the persistence model (flow doc section 3c): real
4
+ * Firebase keeps you signed in across reloads by DEFAULT
5
+ * (`browserLocalPersistence`), so the served sandbox does too.
6
+ *
7
+ * The user *database* persists with the substrate (`--persist`); the
8
+ * *session* persists like the real client — independent of data mode, in
9
+ * web storage, honoring `setPersistence` semantics:
10
+ *
11
+ * LOCAL (default) → localStorage (survives reload + browser restart)
12
+ * SESSION → sessionStorage (survives reload, not the tab)
13
+ * NONE → nothing stored
14
+ *
15
+ * A restored session only resolves when the referenced uid still exists in
16
+ * the sandbox user DB — so ephemeral mode drops helper-created sessions on
17
+ * reload, consistently (`sandbox.restoreSession` throws, we clear).
18
+ *
19
+ * Pure over injected Storage objects so the logic is bun-testable; the
20
+ * browser wiring (real storages, `onAuthStateChanged` recording, restore at
21
+ * init) lives in the entries that import this.
22
+ */
23
+
24
+ export type SessionMode = 'LOCAL' | 'SESSION' | 'NONE';
25
+
26
+ const DEFAULT_KEY = 'pyric:serve:auth-session';
27
+
28
+ export interface SessionStores {
29
+ local: Pick<Storage, 'getItem' | 'setItem' | 'removeItem'>;
30
+ session: Pick<Storage, 'getItem' | 'setItem' | 'removeItem'>;
31
+ }
32
+
33
+ export interface StoredSession {
34
+ uid: string;
35
+ }
36
+
37
+ export class SessionStore {
38
+ private mode: SessionMode = 'LOCAL'; // firebase's default persistence
39
+ private readonly key: string;
40
+
41
+ constructor(
42
+ private readonly stores: SessionStores,
43
+ appName = '[DEFAULT]',
44
+ ) {
45
+ this.key = appName === '[DEFAULT]'
46
+ ? DEFAULT_KEY
47
+ : `${DEFAULT_KEY}:${encodeURIComponent(appName)}`;
48
+ }
49
+
50
+ /** `setPersistence` parity: switch the backing store and MIGRATE any
51
+ * current session into it (the real SDK carries the session over). */
52
+ setMode(mode: SessionMode): void {
53
+ const current = this.load();
54
+ this.clear();
55
+ this.mode = mode;
56
+ if (current) this.save(current.uid);
57
+ }
58
+
59
+ save(uid: string): void {
60
+ this.clear(); // a session lives in exactly one store
61
+ if (this.mode === 'NONE') return;
62
+ this.backing().setItem(this.key, JSON.stringify({ uid } satisfies StoredSession));
63
+ }
64
+
65
+ /** Reads BOTH storages (localStorage first — matches lookup order when a
66
+ * prior page used a different mode). Restoring also restores the mode:
67
+ * the initial Auth observer resaves the user, and must not migrate a
68
+ * SESSION record into the default LOCAL slot. */
69
+ load(): StoredSession | null {
70
+ for (const [mode, store] of [
71
+ ['LOCAL', this.stores.local],
72
+ ['SESSION', this.stores.session],
73
+ ] as const) {
74
+ const raw = store.getItem(this.key);
75
+ if (!raw) continue;
76
+ try {
77
+ const parsed = JSON.parse(raw) as StoredSession;
78
+ if (parsed && typeof parsed.uid === 'string') {
79
+ this.mode = mode;
80
+ return parsed;
81
+ }
82
+ } catch {
83
+ store.removeItem(this.key); // corrupt → drop, never throw at page init
84
+ }
85
+ }
86
+ return null;
87
+ }
88
+
89
+ clear(): void {
90
+ this.stores.local.removeItem(this.key);
91
+ this.stores.session.removeItem(this.key);
92
+ }
93
+
94
+ private backing(): SessionStores['local'] {
95
+ return this.mode === 'SESSION' ? this.stores.session : this.stores.local;
96
+ }
97
+ }