@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,200 @@
1
+ /**
2
+ * `--bridge` for `pyric dev` — mounts the MCP bridge on the SAME origin
3
+ * the app is served from, so one URL carries the app, the sandbox SDK, AND
4
+ * the agent endpoint (the retrofit story end-to-end: `pyric dev --bridge`,
5
+ * point an MCP client at `http://localhost:3473/__pyric/mcp`, and the agent
6
+ * drives the sandbox living in the served page).
7
+ *
8
+ * REUSES the bridge internals (`createBridge`, `buildMcpServer`,
9
+ * `attachPeer`, `collectBody`) directly — the shared transport helpers live in
10
+ * `../bridge/server/peer.ts`; this module composes the bridge into the serve /
11
+ * Vite-plugin origin, it is not a fork of the bridge.
12
+ *
13
+ * Routes (composed into the `/__pyric/` namespace handler):
14
+ * POST /__pyric/mcp MCP over streamable HTTP
15
+ * GET /__pyric/health bridge health JSON
16
+ * WS /__pyric/sandbox the in-page sandbox peer (server `upgrade`)
17
+ */
18
+ import { randomUUID } from 'node:crypto';
19
+ import type { IncomingMessage, Server, ServerResponse } from 'node:http';
20
+ import { WebSocketServer, type WebSocket } from 'ws';
21
+ import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
22
+ import { createBridge, type BridgeToolEvent } from '../bridge/server/bridge.js';
23
+ import { buildMcpServer } from '../bridge/server/mcp.js';
24
+ import { getDefaultMcpToolSurface } from '../bridge/server/mcp-contract.js';
25
+ import { createAuditWriter } from '../bridge/server/audit.js';
26
+ import { attachPeer } from '../bridge/server/peer.js';
27
+ import { pyricVersion } from './standalone-assets.js';
28
+ import { isAllowedUpgrade } from './server.js';
29
+
30
+ const WS_PATH = '/__pyric/sandbox';
31
+ const MCP_PATH = '/__pyric/mcp';
32
+ const HEALTH_PATH = '/__pyric/health';
33
+ const BRIDGE_VERSION = pyricVersion();
34
+
35
+ export interface BridgeMountOptions {
36
+ project?: string;
37
+ disableAuditLog?: boolean;
38
+ /** WS-upgrade rebinding/origin guard config. The upgrade path bypasses the
39
+ * static server's request-time `isAllowedHost`, so the mount guards it here
40
+ * with the SAME allow rule (bound host + loopback + `--allowed-host`).
41
+ * `allowedHosts: true` = the caller (vite `server.allowedHosts: true`) opted
42
+ * into all hosts. Omit ⇒ loopback-only. */
43
+ upgradeGuard?: { boundHost: string; allowedHosts?: string[] | true };
44
+ }
45
+
46
+ export interface BridgeMount {
47
+ /** Stable per-process identity (mirrors `/__pyric/health`'s instanceId).
48
+ * The pointer writer records this so the proxy can verify it reached this
49
+ * exact server across a cross-family port collision. */
50
+ readonly instanceId: string;
51
+ /** Namespace-handler tier: returns true when the request was handled. */
52
+ handler(req: IncomingMessage, res: ServerResponse, url: URL): Promise<boolean>;
53
+ /** Attach the WS upgrade listener to the serve server. */
54
+ attachUpgrade(server: Server): void;
55
+ /** Whether a sandbox peer (a browser tab / SharedWorker relay) is currently
56
+ * connected. In-process callers (the Vite plugin's Functions start) poll
57
+ * this instead of self-fetching `/__pyric/health`, which avoids a loopback
58
+ * fetch on the same event loop. Mirrors `health().sandboxConnected`. */
59
+ sandboxConnected(): boolean;
60
+ /** The browser-side WS URL for the init payload (`bridgeUrl`). */
61
+ wsUrl(origin: { host: string; port: number }): string;
62
+ /** The MCP endpoint for the banner. */
63
+ mcpUrl(origin: { host: string; port: number }): string;
64
+ }
65
+
66
+ export function createBridgeMount(opts: BridgeMountOptions = {}): BridgeMount {
67
+ const project = opts.project ?? 'sandbox';
68
+ const auditWriter = opts.disableAuditLog ? null : createAuditWriter(project);
69
+
70
+ const bridge = createBridge({
71
+ project,
72
+ version: BRIDGE_VERSION,
73
+ onToolEvent: auditWriter ? (event: BridgeToolEvent) => auditWriter.write(event) : undefined,
74
+ });
75
+
76
+ // STATEFUL MCP: a per-session transport+server map, mirroring the standalone
77
+ // bridge (`bridge/server/standalone.ts`). Each Streamable-HTTP client gets an
78
+ // `Mcp-Session-Id` on initialize and resumes the SAME session on every later
79
+ // request. The old per-request, `sessionIdGenerator: undefined` build returned
80
+ // no session id, so clients (Claude Code, Cursor) dropped and 404'd on every
81
+ // reconnect/dev-server restart. The long-lived `bridge` (peer + dispatch +
82
+ // audit) is shared; each session owns its transport+server.
83
+ const SESSION_IDLE_MS = 10 * 60_000;
84
+ const MAX_SESSIONS = 64;
85
+ type Session = {
86
+ transport: StreamableHTTPServerTransport;
87
+ close: () => Promise<void>;
88
+ idle: ReturnType<typeof setTimeout> | null;
89
+ sessionId: string | null;
90
+ };
91
+ const sessions = new Map<string, Session>();
92
+
93
+ const bumpIdle = (s: Session): void => {
94
+ if (s.idle) clearTimeout(s.idle);
95
+ s.idle = setTimeout(() => {
96
+ if (s.sessionId) sessions.delete(s.sessionId);
97
+ void s.close();
98
+ }, SESSION_IDLE_MS);
99
+ };
100
+
101
+ const newSession = async (): Promise<Session> => {
102
+ if (sessions.size >= MAX_SESSIONS) {
103
+ const e = new Error(`pyric bridge: at session cap (${MAX_SESSIONS})`);
104
+ (e as { statusCode?: number }).statusCode = 503;
105
+ throw e;
106
+ }
107
+ const session: Session = {
108
+ transport: null as unknown as StreamableHTTPServerTransport,
109
+ close: async () => {},
110
+ idle: null,
111
+ sessionId: null,
112
+ };
113
+ session.transport = new StreamableHTTPServerTransport({
114
+ sessionIdGenerator: () => randomUUID(),
115
+ onsessioninitialized: (id) => {
116
+ session.sessionId = id;
117
+ sessions.set(id, session);
118
+ bumpIdle(session);
119
+ },
120
+ });
121
+ session.transport.onclose = () => {
122
+ if (session.sessionId) sessions.delete(session.sessionId);
123
+ };
124
+ const surface = getDefaultMcpToolSurface();
125
+ const server = buildMcpServer(bridge, {
126
+ forwarded: surface.forwarded,
127
+ inProcess: surface.inProcess,
128
+ });
129
+ session.close = async () => {
130
+ if (session.idle) clearTimeout(session.idle);
131
+ await server.close().catch(() => {});
132
+ await session.transport.close().catch(() => {});
133
+ };
134
+ await server.connect(session.transport);
135
+ return session;
136
+ };
137
+
138
+ return {
139
+ async handler(req, res, url) {
140
+ if (url.pathname === HEALTH_PATH) {
141
+ res.writeHead(200, { 'content-type': 'application/json' }).end(JSON.stringify(bridge.health()));
142
+ return true;
143
+ }
144
+ if (url.pathname === MCP_PATH) {
145
+ try {
146
+ const sessionId = (req.headers['mcp-session-id'] ?? req.headers['Mcp-Session-Id']) as string | undefined;
147
+ let session = sessionId ? sessions.get(sessionId) : undefined;
148
+ if (session) bumpIdle(session);
149
+ else session = await newSession();
150
+
151
+ if (req.method === 'DELETE' && sessionId) {
152
+ await session.close();
153
+ sessions.delete(sessionId);
154
+ res.writeHead(204).end();
155
+ return true;
156
+ }
157
+ // The transport reads the raw request stream itself. Do NOT pre-parse
158
+ // the body (the old `collectBody` path); a double read hangs the
159
+ // initialize POST.
160
+ await session.transport.handleRequest(req, res);
161
+ } catch (err) {
162
+ const statusCode = (err as { statusCode?: number })?.statusCode ?? 500;
163
+ if (!res.headersSent) {
164
+ res.writeHead(statusCode, { 'content-type': 'application/json' });
165
+ res.end(JSON.stringify({ error: err instanceof Error ? err.message : String(err) }));
166
+ }
167
+ }
168
+ return true;
169
+ }
170
+ return false;
171
+ },
172
+ attachUpgrade(server) {
173
+ const wss = new WebSocketServer({ noServer: true });
174
+ const guard = opts.upgradeGuard;
175
+ server.on('upgrade', (req, socket, head) => {
176
+ if ((req.url ?? '') !== WS_PATH) return;
177
+ // DNS-rebinding + cross-origin hijack guard. The static/dev server runs
178
+ // isAllowedHost on the `request` event only; `upgrade` is a separate
179
+ // listener that bypasses it, so re-check both Host and Origin here
180
+ // before registering the peer (which last-wins the tool channel).
181
+ // `allowedHosts: true` means the caller explicitly opted into all hosts.
182
+ if (guard && guard.allowedHosts !== true) {
183
+ const boundHost = guard.boundHost;
184
+ const extra = Array.isArray(guard.allowedHosts) ? guard.allowedHosts : [];
185
+ if (!isAllowedUpgrade(req.headers, boundHost, extra)) {
186
+ socket.destroy();
187
+ return;
188
+ }
189
+ }
190
+ wss.handleUpgrade(req, socket as never, head, (ws: WebSocket) => {
191
+ attachPeer(bridge, ws);
192
+ });
193
+ });
194
+ },
195
+ sandboxConnected: () => bridge.health().sandboxConnected === true,
196
+ instanceId: bridge.instanceId,
197
+ wsUrl: ({ host, port }) => `ws://${host}:${port}${WS_PATH}`,
198
+ mcpUrl: ({ host, port }) => `http://${host}:${port}${MCP_PATH}`,
199
+ };
200
+ }
@@ -0,0 +1,493 @@
1
+ /**
2
+ * `pyric dev` SDK bundler — builds the browser-standalone ESM bundles the
3
+ * import map points `firebase/*` at (`/__pyric/sdk/*.js`).
4
+ *
5
+ * esbuild (a real dependency — the `pyric` bin runs under node via npx, so
6
+ * Bun.build is unavailable) bundles the wrapper entries in `entries/` with
7
+ * SPLITTING so the shared runtime chunk holds exactly ONE sandbox per page:
8
+ * both the auth and firestore bundles must close over the same instance.
9
+ *
10
+ * Two bundler plugins make pyric browser-standalone:
11
+ * - `pyric/*` resolver — resolves `pyric/...` specifiers from THIS package's
12
+ * dependency context (entries may live in a temp dir that lacks pyric).
13
+ * - node-builtin shims (`url`/`fs`/`path`) — still load-bearing despite the
14
+ * Phase U resolver split: `pyric/firestore` re-exports the agent tool
15
+ * factories (`firestore/index.js` → `rules/tools.js`), and tools.js
16
+ * statically imports the NODE module resolver. The browser path never
17
+ * calls into disk reads, so benign shims suffice. Droppable only when
18
+ * pyric breaks that static chain (tracked in #553).
19
+ *
20
+ * Output is cached under `~/.pyric/serve-cache/<pyric-version>-<hash>/` so a
21
+ * warm `pyric dev` start skips the (~seconds) bundle step. `--no-cache`
22
+ * bypasses.
23
+ */
24
+ import { createHash } from 'node:crypto';
25
+ import { mkdirSync, mkdtempSync, readFileSync, readdirSync, existsSync, rmSync, writeFileSync } from 'node:fs';
26
+ import { homedir } from 'node:os';
27
+ import { dirname, join, relative, sep } from 'node:path';
28
+ import { fileURLToPath } from 'node:url';
29
+ import * as esbuild from 'esbuild';
30
+
31
+ /** Modules the import map serves. Keys are the bare specifiers app code uses. */
32
+ export const SDK_MODULES = [
33
+ 'firebase/ai',
34
+ 'firebase/app',
35
+ 'firebase/auth',
36
+ 'firebase/firestore',
37
+ 'firebase/database',
38
+ 'firebase/messaging',
39
+ 'firebase/messaging/sw',
40
+ 'firebase/storage',
41
+ ] as const;
42
+
43
+ /**
44
+ * The wrapper entries shipped with @pyric/cli, located relative to this
45
+ * module: compiled `.js` siblings when running from dist (npx install), `.ts`
46
+ * sources in the workspace (tests / dev). esbuild bundles either.
47
+ */
48
+ export function defaultSdkEntries(): Record<string, string> {
49
+ const here = dirname(fileURLToPath(import.meta.url));
50
+ const pick = (name: string): string => {
51
+ const js = join(here, 'entries', `${name}.js`);
52
+ if (existsSync(js)) return js;
53
+ const ts = join(here, 'entries', `${name}.ts`);
54
+ if (existsSync(ts)) return ts;
55
+ throw new Error(`pyric dev: missing SDK entry '${name}' next to ${here}`);
56
+ };
57
+ return {
58
+ ai: pick('ai'),
59
+ app: pick('app'),
60
+ auth: pick('auth'),
61
+ firestore: pick('firestore'),
62
+ database: pick('database'),
63
+ messaging: pick('messaging'),
64
+ 'messaging-sw': pick('messaging-sw'),
65
+ storage: pick('storage'),
66
+ init: pick('init'),
67
+ };
68
+ }
69
+
70
+ /**
71
+ * Resolve the built Studio app dir (the plugin's `ui` option + the CLI's
72
+ * `--ui`). Reached by file path so @pyric/cli never imports `@pyric/studio`:
73
+ * the packaged location (`dist/serve/studio-ui`, copied at build) or, in the
74
+ * monorepo, the sibling studio build. Null when neither exists. The standalone
75
+ * binary embeds these same bytes instead (see `standalone-assets.ts`).
76
+ */
77
+ export function resolveStudioUiDir(): string | null {
78
+ const candidates = [
79
+ new URL('./studio-ui/', import.meta.url),
80
+ new URL('../../../studio/dist/app/', import.meta.url),
81
+ ];
82
+ for (const candidate of candidates) {
83
+ const dir = fileURLToPath(candidate);
84
+ if (existsSync(dir)) return dir;
85
+ }
86
+ return null;
87
+ }
88
+
89
+ /**
90
+ * Resolve the built docs site dir (site-docs). `pyric dev --ui` embeds it so
91
+ * the Studio Docs tab has local docs without the hosted site. Built with base
92
+ * `/__pyric/ui/` (see scripts/build.sh) so every page/asset/twin URL resolves
93
+ * under the CLI mount: pages at `/__pyric/ui/docs/<slug>/`, assets at
94
+ * `/__pyric/ui/_astro/*`, the search index at `/__pyric/ui/docs/index.json`,
95
+ * and the shell chrome's tab links back at `/__pyric/ui/<tab>`. The dir holds
96
+ * the site-docs `dist/` verbatim: `docs/`, `_astro/`, `index.html`, `llms.txt`.
97
+ */
98
+ export function resolveDocsUiDir(): string | null {
99
+ const candidates = [
100
+ new URL('./docs-ui/', import.meta.url),
101
+ ];
102
+ for (const candidate of candidates) {
103
+ const dir = fileURLToPath(candidate);
104
+ if (existsSync(dir)) return dir;
105
+ }
106
+ return null;
107
+ }
108
+
109
+ // ─── pyric dist discovery ─────────────────────────────────────────────
110
+
111
+ /** Locate the installed pyric package root (works in the workspace and when
112
+ * @pyric/cli is npm-installed — pyric is a direct dependency). */
113
+ export function pyricPackageRoot(): string {
114
+ // Resolve a real exported subpath, then walk up to the package root.
115
+ const entry = fileURLToPath(import.meta.resolve('pyric/firestore'));
116
+ let dir = dirname(entry);
117
+ while (dir.includes(sep) && !existsSync(join(dir, 'package.json'))) {
118
+ dir = dirname(dir);
119
+ }
120
+ const pkg = JSON.parse(readFileSync(join(dir, 'package.json'), 'utf8')) as { name?: string };
121
+ if (pkg.name !== 'pyric') {
122
+ throw new Error(`pyric dev: expected to resolve the pyric package, found '${pkg.name}' at ${dir}`);
123
+ }
124
+ return dir;
125
+ }
126
+
127
+ export function pyricVersion(root: string = pyricPackageRoot()): string {
128
+ const pkg = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8')) as { version?: string };
129
+ return pkg.version ?? '0.0.0';
130
+ }
131
+
132
+ // ─── plugins ──────────────────────────────────────────────────────────
133
+
134
+ /** Resolve `pyric/*` specifiers from THIS package's own dependency context
135
+ * (`import.meta.resolve`) rather than from the entry file's directory —
136
+ * entries may live in a temp dir or the user's project, neither of which
137
+ * has pyric in scope; @pyric/cli always does (direct dependency). */
138
+ function pyricResolvePlugin(): esbuild.Plugin {
139
+ return {
140
+ name: 'pyric-serve-resolve-pyric',
141
+ setup(build) {
142
+ build.onResolve({ filter: /^pyric(\/|$)/ }, (args) => ({
143
+ path: fileURLToPath(import.meta.resolve(args.path)),
144
+ }));
145
+ },
146
+ };
147
+ }
148
+
149
+ /** The node builtins pyric's browser graph reaches for (via the rules module
150
+ * resolver). Bare and `node:`-prefixed both match. */
151
+ export const NODE_BUILTIN_RE = /^(node:)?(url|fs|path)$/;
152
+
153
+ /** Benign browser shim SOURCE per builtin (see header — droppable only when
154
+ * pyric breaks the `firestore/index → rules/tools → modules/resolver` static
155
+ * chain, #553). Exported so the Vite plugin (`./vite`) reuses the SAME shims
156
+ * through its own resolveId/load + optimizeDeps esbuild pass, instead of
157
+ * re-deriving them. */
158
+ export const NODE_BUILTIN_SHIMS: Record<string, string> = {
159
+ fs: `const no = () => { throw new Error('pyric dev: fs is not available in the browser'); };
160
+ export const readFileSync = no; export const existsSync = () => false;
161
+ export const readdirSync = no; export const writeFileSync = no; export const mkdirSync = no;
162
+ export default { readFileSync, existsSync, readdirSync, writeFileSync, mkdirSync };`,
163
+ url: `export const fileURLToPath = (u) => String(u).replace('file://', '');
164
+ export const pathToFileURL = (p) => new URL('file://' + p);
165
+ export default { fileURLToPath, pathToFileURL };`,
166
+ path: `export const join = (...p) => p.join('/');
167
+ export const dirname = (p) => p.split('/').slice(0, -1).join('/') || '/';
168
+ export const resolve = (...p) => p.join('/');
169
+ export const basename = (p) => p.split('/').pop() ?? '';
170
+ export default { join, dirname, resolve, basename };`,
171
+ };
172
+
173
+ function nodeShimPlugin(): esbuild.Plugin {
174
+ return {
175
+ name: 'pyric-serve-node-shims',
176
+ setup(build) {
177
+ build.onResolve({ filter: NODE_BUILTIN_RE }, (args) => ({
178
+ path: args.path.replace(/^node:/, ''),
179
+ namespace: 'pyric-node-shim',
180
+ }));
181
+ build.onLoad({ filter: /.*/, namespace: 'pyric-node-shim' }, (args) => ({
182
+ contents: NODE_BUILTIN_SHIMS[args.path]!,
183
+ loader: 'js',
184
+ }));
185
+ },
186
+ };
187
+ }
188
+
189
+ // ─── cache ────────────────────────────────────────────────────────────
190
+
191
+ export interface BundleOptions {
192
+ /** Entry files (the wrapper modules in `entries/`), keyed by the served
193
+ * basename (`auth` → `/__pyric/sdk/auth.js`). */
194
+ entries: Record<string, string>;
195
+ /** Bypass the cache and build an immutable, process-independent generation. */
196
+ noCache?: boolean;
197
+ /** Override the cache root (tests). Default `~/.pyric/serve-cache`. */
198
+ cacheRoot?: string;
199
+ minify?: boolean;
200
+ }
201
+
202
+ export interface BundleResult {
203
+ outDir: string;
204
+ /** Served basename → absolute file path (entry bundles + shared chunks). */
205
+ files: string[];
206
+ cached: boolean;
207
+ /** Release a bypass generation after its server stops. Cache generations
208
+ * deliberately remain durable, so their disposer is a no-op. */
209
+ dispose(): void;
210
+ }
211
+
212
+ /** Bump when bundler logic changes in a way that must invalidate caches. */
213
+ const BUNDLER_REV = 9; // 9: cache keys also cover dependency-resolution metadata
214
+
215
+ const RESOLUTION_FILES = [
216
+ 'bun.lock',
217
+ 'bun.lockb',
218
+ 'package-lock.json',
219
+ 'pnpm-lock.yaml',
220
+ 'yarn.lock',
221
+ ] as const;
222
+
223
+ function nearestPackageRoot(start: string): string | undefined {
224
+ let dir = start;
225
+ while (true) {
226
+ if (existsSync(join(dir, 'package.json'))) return dir;
227
+ const parent = dirname(dir);
228
+ if (parent === dir) return undefined;
229
+ dir = parent;
230
+ }
231
+ }
232
+
233
+ /**
234
+ * Hash package-manager identity separately from executable source bytes.
235
+ * Workspace dependency upgrades commonly change package.json/bun.lock before
236
+ * the prerelease package version moves; installed packages get the same signal
237
+ * from their consumer lockfile. Package managers own third-party byte
238
+ * integrity, so their manifests/locks are the supported cache boundary.
239
+ */
240
+ function packageResolutionHash(graphRoot: string, pyricRoot: string): string {
241
+ const h = createHash('sha256');
242
+ const cliRoot = nearestPackageRoot(graphRoot);
243
+ for (const [label, root] of [
244
+ ['cli', cliRoot],
245
+ ['pyric', pyricRoot],
246
+ ] as const) {
247
+ if (!root) continue;
248
+ const manifest = join(root, 'package.json');
249
+ if (!existsSync(manifest)) continue;
250
+ h.update(label);
251
+ h.update(readFileSync(manifest));
252
+ }
253
+
254
+ let dir = cliRoot ?? graphRoot;
255
+ while (true) {
256
+ for (const name of RESOLUTION_FILES) {
257
+ const file = join(dir, name);
258
+ if (!existsSync(file)) continue;
259
+ h.update(name);
260
+ h.update(readFileSync(file));
261
+ }
262
+ const parent = dirname(dir);
263
+ if (parent === dir) break;
264
+ dir = parent;
265
+ }
266
+ return h.digest('hex');
267
+ }
268
+
269
+ export function cacheKey(
270
+ opts: BundleOptions,
271
+ version: string,
272
+ graphRoot = dirname(dirname(fileURLToPath(import.meta.url))),
273
+ pyricRoot = pyricPackageRoot(),
274
+ ): string {
275
+ const h = createHash('sha256');
276
+ h.update(`rev${BUNDLER_REV}`);
277
+ h.update(version);
278
+ // Hash the WHOLE local CLI graph, not just entries or entries/ siblings.
279
+ // Page adapters import worker/client, worker/protocol, bridge, and verify
280
+ // leaves outside that directory; missing one can pair a stale page client
281
+ // with a freshly rebuilt worker. The installed pyric implementation is
282
+ // hashed separately: workspace and prerelease builds can change dist bytes
283
+ // without changing package.json's version.
284
+ h.update(sourceTreeHash(graphRoot));
285
+ h.update(sourceTreeHash(join(pyricRoot, 'dist')));
286
+ h.update(packageResolutionHash(graphRoot, pyricRoot));
287
+ for (const [name, file] of Object.entries(opts.entries).sort()) {
288
+ h.update(name);
289
+ h.update(readFileSync(file, 'utf8'));
290
+ }
291
+ return `${version}-${h.digest('hex').slice(0, 12)}`;
292
+ }
293
+
294
+ // ─── the bundle step ──────────────────────────────────────────────────
295
+
296
+ export async function bundleSdk(opts: BundleOptions): Promise<BundleResult> {
297
+ const root = pyricPackageRoot();
298
+ const version = pyricVersion(root);
299
+ const key = cacheKey(opts, version);
300
+ const cacheRoot = opts.cacheRoot ?? join(homedir(), '.pyric', 'serve-cache');
301
+ mkdirSync(cacheRoot, { recursive: true });
302
+ // A running server keeps serving this directory after bundleSdk returns.
303
+ // Therefore `--no-cache` must never overwrite the deterministic warm-cache
304
+ // path: another concurrent `pyric dev --no-cache` could otherwise replace
305
+ // an entry and its shared chunks between browser requests, splitting
306
+ // singleton module state (notably the app registry). Each bypass build gets
307
+ // an immutable generation; the OS-level mkdir primitive makes concurrent
308
+ // callers distinct without coordination.
309
+ const generationRoot = opts.noCache
310
+ ? mkdtempSync(join(cacheRoot, `.no-cache-${key}-`))
311
+ : join(cacheRoot, key);
312
+ const outDir = join(generationRoot, 'sdk');
313
+ const dispose = opts.noCache
314
+ ? () => rmSync(generationRoot, { recursive: true, force: true })
315
+ : () => {};
316
+
317
+ if (!opts.noCache && existsSync(join(outDir, '.complete'))) {
318
+ const files = (readdirSync(outDir) as string[])
319
+ .filter((f) => f.endsWith('.js'))
320
+ .map((f) => join(outDir, f));
321
+ return { outDir, files, cached: true, dispose };
322
+ }
323
+
324
+ mkdirSync(outDir, { recursive: true });
325
+ try {
326
+ const result = await esbuild.build({
327
+ entryPoints: Object.fromEntries(
328
+ Object.entries(opts.entries).map(([name, file]) => [name, file]),
329
+ ),
330
+ outdir: outDir,
331
+ bundle: true,
332
+ format: 'esm',
333
+ platform: 'browser',
334
+ splitting: true,
335
+ sourcemap: 'linked',
336
+ minify: opts.minify ?? true,
337
+ logLevel: 'silent',
338
+ // Provenance: any stack frame or "view source" into these bundles must
339
+ // self-identify as the sandbox shim, not the real Firebase SDK.
340
+ chunkNames: 'pyric-sandbox-[hash]',
341
+ banner: {
342
+ js: '/* pyric dev: pyric sandbox shim serving firebase/* — NOT the real Firebase SDK */',
343
+ },
344
+ plugins: [pyricResolvePlugin(), nodeShimPlugin()],
345
+ });
346
+ if (result.errors.length > 0) {
347
+ throw new Error(
348
+ `pyric dev: SDK bundle failed:\n${result.errors.map((e) => e.text).join('\n')}`,
349
+ );
350
+ }
351
+ writeFileSync(join(outDir, '.complete'), new Date().toISOString());
352
+ const files = (readdirSync(outDir) as string[])
353
+ .filter((f) => f.endsWith('.js'))
354
+ .map((f) => join(outDir, f));
355
+ return { outDir, files, cached: false, dispose };
356
+ } catch (error) {
357
+ dispose();
358
+ throw error;
359
+ }
360
+ }
361
+
362
+ // ─── the SharedWorker bundle (Phase 3c.A) ─────────────────────────────────
363
+
364
+ /**
365
+ * Locate the SharedWorker entry (`worker/entry.ts`) — compiled `.js` sibling
366
+ * from dist, `.ts` source in the workspace. Same dual-resolution as
367
+ * `defaultSdkEntries`.
368
+ */
369
+ export function workerEntryPath(): string {
370
+ const here = dirname(fileURLToPath(import.meta.url));
371
+ const js = join(here, 'worker', 'entry.js');
372
+ if (existsSync(js)) return js;
373
+ const ts = join(here, 'worker', 'entry.ts');
374
+ if (existsSync(ts)) return ts;
375
+ throw new Error(`pyric dev: missing SharedWorker entry next to ${here}`);
376
+ }
377
+
378
+ export interface WorkerBundleOptions {
379
+ /** Where to write `worker.js` — normally the SDK bundle's `outDir` so the
380
+ * existing `/__pyric/sdk/<file>` route serves it with no extra wiring. */
381
+ outDir: string;
382
+ /** Bypass + overwrite the cached worker bundle. */
383
+ noCache?: boolean;
384
+ minify?: boolean;
385
+ }
386
+
387
+ /**
388
+ * Content hash of the worker's executable CLI graph + pyric version. The worker bundle
389
+ * shares the SDK `outDir` (keyed by the SDK entries), which does NOT cover the
390
+ * worker graph — so the worker bundle carries its OWN content marker. Hash the
391
+ * full CLI source/dist tree because the worker imports bridge and verify leaves
392
+ * outside `serve/worker`; an allowlist would silently miss future dependencies.
393
+ * Installed pyric dist bytes are also hashed because workspace and prerelease
394
+ * builds can change implementation without a version bump.
395
+ */
396
+ export function workerSourceHash(
397
+ pyricRoot = pyricPackageRoot(),
398
+ graphRoot = dirname(dirname(fileURLToPath(import.meta.url))),
399
+ ): string {
400
+ const h = createHash('sha256');
401
+ h.update(`rev${BUNDLER_REV}`);
402
+ h.update(pyricVersion(pyricRoot));
403
+ h.update(sourceTreeHash(join(pyricRoot, 'dist')));
404
+ h.update(sourceTreeHash(graphRoot));
405
+ h.update(packageResolutionHash(graphRoot, pyricRoot));
406
+ return h.digest('hex').slice(0, 16);
407
+ }
408
+
409
+ /** Hash every JS/TS source below a directory, including split host/client leaves. */
410
+ export function sourceTreeHash(root: string): string {
411
+ const files: string[] = [];
412
+ const visit = (dir: string): void => {
413
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
414
+ const path = join(dir, entry.name);
415
+ if (entry.isDirectory()) visit(path);
416
+ else if (entry.isFile() && (entry.name.endsWith('.ts') || entry.name.endsWith('.js'))) {
417
+ files.push(path);
418
+ }
419
+ }
420
+ };
421
+ visit(root);
422
+ const h = createHash('sha256');
423
+ for (const file of files.sort()) {
424
+ h.update(relative(root, file).split(sep).join('/'));
425
+ h.update(readFileSync(file, 'utf8'));
426
+ }
427
+ return h.digest('hex');
428
+ }
429
+
430
+ /**
431
+ * Bundle the SharedWorker entry into a single classic worker script served at
432
+ * `/__pyric/sdk/worker.js`.
433
+ *
434
+ * WHY A SEPARATE BUILD (not part of `bundleSdk`'s splitting graph): a
435
+ * SharedWorker script is loaded by URL into its OWN realm — it cannot share
436
+ * the page's runtime chunk. So it is bundled standalone: `splitting: false`,
437
+ * `format: 'iife'` (the client opens it with `new SharedWorker(url, { type:
438
+ * 'classic' })`), everything inlined into one file. It reuses the same three
439
+ * browser-standalone plugins as the SDK bundle (pyric-resolve + node-shims +
440
+ * firebase-stub) so `pyric/*`, node builtins, and the `firebase/*` peer stubs
441
+ * resolve identically.
442
+ *
443
+ * NOTE: the worker legitimately holds the whole sandbox engine — it IS the
444
+ * backend — so this bundle is large, but it loads ONCE per origin (not per
445
+ * page). Trimming node/admin dead paths is tracked as Phase 3b.
446
+ *
447
+ * The `.worker-complete` marker is independent of `bundleSdk`'s `.complete`,
448
+ * while `cacheKey()` includes the complete CLI source graph. A worker/client
449
+ * or protocol edit therefore invalidates the shared SDK generation too, so
450
+ * the page and worker halves cannot come from different source generations.
451
+ */
452
+ export async function bundleWorker(opts: WorkerBundleOptions): Promise<string> {
453
+ const outFile = join(opts.outDir, 'worker.js');
454
+ const marker = join(opts.outDir, '.worker-complete');
455
+ const hash = workerSourceHash();
456
+ if (
457
+ !opts.noCache &&
458
+ existsSync(outFile) &&
459
+ existsSync(marker) &&
460
+ readFileSync(marker, 'utf8') === hash
461
+ ) {
462
+ return outFile;
463
+ }
464
+
465
+ mkdirSync(opts.outDir, { recursive: true });
466
+ const root = pyricPackageRoot();
467
+ const result = await esbuild.build({
468
+ entryPoints: [workerEntryPath()],
469
+ outfile: outFile,
470
+ bundle: true,
471
+ format: 'iife',
472
+ platform: 'browser',
473
+ splitting: false,
474
+ sourcemap: 'linked',
475
+ minify: opts.minify ?? true,
476
+ logLevel: 'silent',
477
+ // Bake the build hash so the worker can report its version (staleness
478
+ // guard): the page warns when a still-running OLD worker is older than the
479
+ // served bundle. Matches the `<meta name="pyric-worker-v">` value.
480
+ define: { __PYRIC_WORKER_VERSION__: JSON.stringify(hash) },
481
+ banner: {
482
+ js: '/* pyric dev: SharedWorker sandbox host serving firebase/* — NOT the real Firebase SDK */',
483
+ },
484
+ plugins: [pyricResolvePlugin(), nodeShimPlugin()],
485
+ });
486
+ if (result.errors.length > 0) {
487
+ throw new Error(
488
+ `pyric dev: SharedWorker bundle failed:\n${result.errors.map((e) => e.text).join('\n')}`,
489
+ );
490
+ }
491
+ writeFileSync(marker, hash);
492
+ return outFile;
493
+ }