@pyric/cli 0.1.0-alpha.11 → 0.1.0-alpha.13

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 (424) hide show
  1. package/dist/cli/serve.js +4 -4
  2. package/dist/cli/serve.js.map +1 -1
  3. package/dist/conformance/.generated/can-i-use-browser.d.ts.map +1 -1
  4. package/dist/conformance/.generated/can-i-use-browser.js +1 -1
  5. package/dist/conformance/.generated/can-i-use-browser.js.map +1 -1
  6. package/dist/conformance/.generated/can-i-use.d.ts.map +1 -1
  7. package/dist/conformance/.generated/can-i-use.js +2 -2
  8. package/dist/conformance/.generated/can-i-use.js.map +1 -1
  9. package/dist/serve/bundler.d.ts +13 -8
  10. package/dist/serve/bundler.d.ts.map +1 -1
  11. package/dist/serve/bundler.js +31 -36
  12. package/dist/serve/bundler.js.map +1 -1
  13. package/dist/serve/docs-ui/docs/_rhythm/index.html +1 -1
  14. package/dist/serve/docs-ui/docs/agent/set-up-your-agent/index.html +1 -1
  15. package/dist/serve/docs-ui/docs/agent/watch-and-review/index.html +1 -1
  16. package/dist/serve/docs-ui/docs/agent/work-with-an-agent/index.html +1 -1
  17. package/dist/serve/docs-ui/docs/ai-compat/index.html +1 -1
  18. package/dist/serve/docs-ui/docs/api-reference/index.html +2 -2
  19. package/dist/serve/docs-ui/docs/api-reference.md +1 -1
  20. package/dist/serve/docs-ui/docs/app-compat/index.html +1 -1
  21. package/dist/serve/docs-ui/docs/auth-compat/index.html +1 -1
  22. package/dist/serve/docs-ui/docs/build/ai-logic/index.html +44 -22
  23. package/dist/serve/docs-ui/docs/build/ai-logic.md +69 -21
  24. package/dist/serve/docs-ui/docs/build/authentication/index.html +1 -1
  25. package/dist/serve/docs-ui/docs/build/cloud-firestore/index.html +1 -1
  26. package/dist/serve/docs-ui/docs/build/cloud-messaging/index.html +1 -1
  27. package/dist/serve/docs-ui/docs/build/cloud-storage/index.html +1 -1
  28. package/dist/serve/docs-ui/docs/build/realtime-database/index.html +1 -1
  29. package/dist/serve/docs-ui/docs/conformance-scores/index.html +1 -1
  30. package/dist/serve/docs-ui/docs/create-pyric-reference-api/index.html +1 -1
  31. package/dist/serve/docs-ui/docs/database-compat/index.html +1 -1
  32. package/dist/serve/docs-ui/docs/firestore-compat/index.html +1 -1
  33. package/dist/serve/docs-ui/docs/functions-rtdb-compat/index.html +1 -1
  34. package/dist/serve/docs-ui/docs/get-started/how-the-swap-works/index.html +2 -1
  35. package/dist/serve/docs-ui/docs/get-started/how-the-swap-works.md +2 -0
  36. package/dist/serve/docs-ui/docs/get-started/start-building/index.html +3 -2
  37. package/dist/serve/docs-ui/docs/get-started/start-building.md +3 -1
  38. package/dist/serve/docs-ui/docs/index.json +349 -29
  39. package/dist/serve/docs-ui/docs/messaging-compat/index.html +1 -1
  40. package/dist/serve/docs-ui/docs/observe/resolve-runtime-status/index.html +28 -0
  41. package/dist/serve/docs-ui/docs/observe/resolve-runtime-status.md +47 -0
  42. package/dist/serve/docs-ui/docs/observe/see-whats-happening/index.html +1 -1
  43. package/dist/serve/docs-ui/docs/observe/shape-your-data/index.html +1 -1
  44. package/dist/serve/docs-ui/docs/overview/index.html +1 -1
  45. package/dist/serve/docs-ui/docs/pyric-admin-app-reference-api/index.html +1 -1
  46. package/dist/serve/docs-ui/docs/pyric-admin-auth-reference-api/index.html +1 -1
  47. package/dist/serve/docs-ui/docs/pyric-admin-database-reference-api/index.html +1 -1
  48. package/dist/serve/docs-ui/docs/pyric-admin-firestore-reference-api/index.html +1 -1
  49. package/dist/serve/docs-ui/docs/pyric-admin-messaging-reference-api/index.html +916 -0
  50. package/dist/serve/docs-ui/docs/pyric-admin-messaging-reference-api.md +706 -0
  51. package/dist/serve/docs-ui/docs/pyric-admin-storage-reference-api/index.html +1 -1
  52. package/dist/serve/docs-ui/docs/pyric-ai-reference-api/index.html +1 -1
  53. package/dist/serve/docs-ui/docs/pyric-ai-scripting-reference-api/index.html +1 -1
  54. package/dist/serve/docs-ui/docs/pyric-app-reference-api/index.html +1 -1
  55. package/dist/serve/docs-ui/docs/pyric-auth-reference-api/index.html +1 -1
  56. package/dist/serve/docs-ui/docs/pyric-cli-assurance-browser-reference-api/index.html +1 -1
  57. package/dist/serve/docs-ui/docs/pyric-cli-assurance-reference-api/index.html +1 -1
  58. package/dist/serve/docs-ui/docs/pyric-cli-bridge-client-reference-api/index.html +1 -1
  59. package/dist/serve/docs-ui/docs/pyric-cli-bridge-reference-api/index.html +1 -1
  60. package/dist/serve/docs-ui/docs/pyric-cli-conformance-browser-reference-api/index.html +1 -1
  61. package/dist/serve/docs-ui/docs/pyric-cli-conformance-docs-reference-api/index.html +1 -1
  62. package/dist/serve/docs-ui/docs/pyric-cli-conformance-reference-api/index.html +1 -1
  63. package/dist/serve/docs-ui/docs/pyric-cli-credentials-node-reference-api/index.html +1 -1
  64. package/dist/serve/docs-ui/docs/pyric-cli-discover-reference-api/index.html +1 -1
  65. package/dist/serve/docs-ui/docs/pyric-cli-register-reference-api/index.html +1 -1
  66. package/dist/serve/docs-ui/docs/pyric-cli-remote-reference-api/index.html +1 -1
  67. package/dist/serve/docs-ui/docs/pyric-cli-serve-worker-reference-api/index.html +168 -76
  68. package/dist/serve/docs-ui/docs/pyric-cli-serve-worker-reference-api.md +156 -73
  69. package/dist/serve/docs-ui/docs/pyric-cli-verify-reference-api/index.html +1 -1
  70. package/dist/serve/docs-ui/docs/pyric-cli-vite-reference-api/index.html +11 -8
  71. package/dist/serve/docs-ui/docs/pyric-cli-vite-reference-api.md +17 -4
  72. package/dist/serve/docs-ui/docs/pyric-database-reference-api/index.html +1 -1
  73. package/dist/serve/docs-ui/docs/pyric-firestore-reference-api/index.html +1 -1
  74. package/dist/serve/docs-ui/docs/pyric-messaging-reference-api/index.html +1 -1
  75. package/dist/serve/docs-ui/docs/pyric-messaging-sw-reference-api/index.html +1 -1
  76. package/dist/serve/docs-ui/docs/pyric-rules-reference-api/index.html +1 -1
  77. package/dist/serve/docs-ui/docs/pyric-sandbox-database-reference-api/index.html +1 -1
  78. package/dist/serve/docs-ui/docs/pyric-sandbox-firestore-reference-api/index.html +1 -1
  79. package/dist/serve/docs-ui/docs/pyric-sandbox-reference-api/index.html +37 -14
  80. package/dist/serve/docs-ui/docs/pyric-sandbox-reference-api.md +28 -8
  81. package/dist/serve/docs-ui/docs/pyric-storage-reference-api/index.html +1 -1
  82. package/dist/serve/docs-ui/docs/pyric-ui-agents-reference-api/index.html +1 -1
  83. package/dist/serve/docs-ui/docs/pyric-ui-auth-hooks-reference-api/index.html +1 -1
  84. package/dist/serve/docs-ui/docs/pyric-ui-auth-reference-api/index.html +1 -1
  85. package/dist/serve/docs-ui/docs/pyric-ui-events-hooks-reference-api/index.html +1 -1
  86. package/dist/serve/docs-ui/docs/pyric-ui-events-reference-api/index.html +1 -1
  87. package/dist/serve/docs-ui/docs/pyric-ui-firestore-hooks-reference-api/index.html +1 -1
  88. package/dist/serve/docs-ui/docs/pyric-ui-firestore-reference-api/index.html +1 -1
  89. package/dist/serve/docs-ui/docs/pyric-ui-primitives-reference-api/index.html +1 -1
  90. package/dist/serve/docs-ui/docs/pyric-ui-rtdb-reference-api/index.html +1 -1
  91. package/dist/serve/docs-ui/docs/pyric-ui-rules-hooks-reference-api/index.html +1 -1
  92. package/dist/serve/docs-ui/docs/pyric-ui-rules-reference-api/index.html +1 -1
  93. package/dist/serve/docs-ui/docs/pyric-ui-storage-hooks-reference-api/index.html +1 -1
  94. package/dist/serve/docs-ui/docs/pyric-ui-storage-reference-api/index.html +1 -1
  95. package/dist/serve/docs-ui/docs/pyric-ui-traffic-hooks-reference-api/index.html +1 -1
  96. package/dist/serve/docs-ui/docs/pyric-ui-traffic-reference-api/index.html +1 -1
  97. package/dist/serve/docs-ui/docs/rules-compat/index.html +1 -1
  98. package/dist/serve/docs-ui/docs/secure/audit-your-rules/index.html +1 -1
  99. package/dist/serve/docs-ui/docs/secure/firestore-rules-limits/index.html +1 -1
  100. package/dist/serve/docs-ui/docs/secure/read-a-denial/index.html +1 -1
  101. package/dist/serve/docs-ui/docs/secure/rtdb-rules-in-typescript/index.html +1 -1
  102. package/dist/serve/docs-ui/docs/secure/rules-standard-library/index.html +1 -1
  103. package/dist/serve/docs-ui/docs/secure/secure-it-with-rules/index.html +1 -1
  104. package/dist/serve/docs-ui/docs/secure/simulate-and-lint/index.html +1 -1
  105. package/dist/serve/docs-ui/docs/secure/write-a-rules-test-suite/index.html +1 -1
  106. package/dist/serve/docs-ui/docs/ship/ship-to-production/index.html +1 -1
  107. package/dist/serve/docs-ui/docs/ship/test-in-node/index.html +1 -1
  108. package/dist/serve/docs-ui/docs/storage-compat/index.html +1 -1
  109. package/dist/serve/docs-ui/docs/trust/how-we-know-it-matches-firebase/index.html +1 -1
  110. package/dist/serve/docs-ui/docs/trust/versioning-and-compatibility/index.html +1 -1
  111. package/dist/serve/docs-ui/docs/tutorial/index.html +429 -0
  112. package/dist/serve/docs-ui/docs/tutorial.md +701 -0
  113. package/dist/serve/docs-ui/llms.txt +3 -1
  114. package/dist/serve/entries/init.d.ts.map +1 -1
  115. package/dist/serve/entries/init.js +3 -0
  116. package/dist/serve/entries/init.js.map +1 -1
  117. package/dist/serve/entries/runtime.d.ts.map +1 -1
  118. package/dist/serve/entries/runtime.js +20 -5
  119. package/dist/serve/entries/runtime.js.map +1 -1
  120. package/dist/serve/entries/worker-runtime.d.ts +2 -2
  121. package/dist/serve/entries/worker-runtime.d.ts.map +1 -1
  122. package/dist/serve/entries/worker-runtime.js +45 -10
  123. package/dist/serve/entries/worker-runtime.js.map +1 -1
  124. package/dist/serve/runtime/chip-config.d.ts +19 -0
  125. package/dist/serve/runtime/chip-config.d.ts.map +1 -0
  126. package/dist/serve/runtime/chip-config.js +19 -0
  127. package/dist/serve/runtime/chip-config.js.map +1 -0
  128. package/dist/serve/runtime/chip-install.d.ts +10 -0
  129. package/dist/serve/runtime/chip-install.d.ts.map +1 -0
  130. package/dist/serve/runtime/chip-install.js +15 -0
  131. package/dist/serve/runtime/chip-install.js.map +1 -0
  132. package/dist/serve/runtime/chip.d.ts +17 -0
  133. package/dist/serve/runtime/chip.d.ts.map +1 -0
  134. package/dist/serve/runtime/chip.js +257 -0
  135. package/dist/serve/runtime/chip.js.map +1 -0
  136. package/dist/serve/runtime/manifest.d.ts +22 -0
  137. package/dist/serve/runtime/manifest.d.ts.map +1 -0
  138. package/dist/serve/runtime/manifest.js +20 -0
  139. package/dist/serve/runtime/manifest.js.map +1 -0
  140. package/dist/serve/runtime/status.d.ts +49 -0
  141. package/dist/serve/runtime/status.d.ts.map +1 -0
  142. package/dist/serve/runtime/status.js +164 -0
  143. package/dist/serve/runtime/status.js.map +1 -0
  144. package/dist/serve/runtime/worker-connection.d.ts +4 -0
  145. package/dist/serve/runtime/worker-connection.d.ts.map +1 -0
  146. package/dist/serve/runtime/worker-connection.js +11 -0
  147. package/dist/serve/runtime/worker-connection.js.map +1 -0
  148. package/dist/serve/runtime/worker-generation.d.ts +14 -0
  149. package/dist/serve/runtime/worker-generation.d.ts.map +1 -0
  150. package/dist/serve/runtime/worker-generation.js +56 -0
  151. package/dist/serve/runtime/worker-generation.js.map +1 -0
  152. package/dist/serve/runtime/worker-replacement.d.ts +18 -0
  153. package/dist/serve/runtime/worker-replacement.d.ts.map +1 -0
  154. package/dist/serve/runtime/worker-replacement.js +29 -0
  155. package/dist/serve/runtime/worker-replacement.js.map +1 -0
  156. package/dist/serve/standalone-assets.d.ts +2 -2
  157. package/dist/serve/standalone-assets.d.ts.map +1 -1
  158. package/dist/serve/standalone-assets.js +1 -1
  159. package/dist/serve/standalone-assets.js.map +1 -1
  160. package/dist/serve/studio-ui/assets/{RulesCodeEditor-CxVYcZXw.js → RulesCodeEditor-BDlH-5lp.js} +1 -1
  161. package/dist/serve/studio-ui/assets/{index-cpSfgQ7H.js → index-Bf9SSE8L.js} +5 -5
  162. package/dist/serve/studio-ui/assets/{seed-DeuFKi4J.js → seed-D_UjxfOU.js} +1 -1
  163. package/dist/serve/studio-ui/index.html +1 -1
  164. package/dist/serve/vite-ai-config.d.ts +25 -0
  165. package/dist/serve/vite-ai-config.d.ts.map +1 -0
  166. package/dist/serve/vite-ai-config.js +45 -0
  167. package/dist/serve/vite-ai-config.js.map +1 -0
  168. package/dist/serve/vite-plugin.d.ts +17 -36
  169. package/dist/serve/vite-plugin.d.ts.map +1 -1
  170. package/dist/serve/vite-plugin.js +38 -57
  171. package/dist/serve/vite-plugin.js.map +1 -1
  172. package/dist/serve/vite-rules-source.d.ts +8 -0
  173. package/dist/serve/vite-rules-source.d.ts.map +1 -0
  174. package/dist/serve/vite-rules-source.js +23 -0
  175. package/dist/serve/vite-rules-source.js.map +1 -0
  176. package/dist/serve/vite-worker-runtime.d.ts +20 -0
  177. package/dist/serve/vite-worker-runtime.d.ts.map +1 -0
  178. package/dist/serve/vite-worker-runtime.js +25 -0
  179. package/dist/serve/vite-worker-runtime.js.map +1 -0
  180. package/dist/serve/worker/client/connection.d.ts +7 -2
  181. package/dist/serve/worker/client/connection.d.ts.map +1 -1
  182. package/dist/serve/worker/client/connection.js +13 -16
  183. package/dist/serve/worker/client/connection.js.map +1 -1
  184. package/dist/serve/worker/client/core.d.ts +7 -0
  185. package/dist/serve/worker/client/core.d.ts.map +1 -1
  186. package/dist/serve/worker/client/core.js +28 -0
  187. package/dist/serve/worker/client/core.js.map +1 -1
  188. package/dist/serve/worker/client/runtime-control.d.ts +8 -0
  189. package/dist/serve/worker/client/runtime-control.d.ts.map +1 -0
  190. package/dist/serve/worker/client/runtime-control.js +11 -0
  191. package/dist/serve/worker/client/runtime-control.js.map +1 -0
  192. package/dist/serve/worker/entry.js +44 -2
  193. package/dist/serve/worker/entry.js.map +1 -1
  194. package/dist/serve/worker/host/core.js +1 -1
  195. package/dist/serve/worker/host/core.js.map +1 -1
  196. package/dist/serve/worker/host/firestore-writes.d.ts.map +1 -1
  197. package/dist/serve/worker/host/firestore-writes.js +3 -2
  198. package/dist/serve/worker/host/firestore-writes.js.map +1 -1
  199. package/dist/serve/worker/host/studio.js +1 -1
  200. package/dist/serve/worker/host/studio.js.map +1 -1
  201. package/dist/serve/worker/host-context.d.ts +1 -1
  202. package/dist/serve/worker/host-context.d.ts.map +1 -1
  203. package/dist/serve/worker/index.d.ts +2 -1
  204. package/dist/serve/worker/index.d.ts.map +1 -1
  205. package/dist/serve/worker/index.js +2 -1
  206. package/dist/serve/worker/index.js.map +1 -1
  207. package/dist/serve/worker/protocol.d.ts +15 -1
  208. package/dist/serve/worker/protocol.d.ts.map +1 -1
  209. package/dist/serve/worker/protocol.js +2 -2
  210. package/dist/serve/worker/protocol.js.map +1 -1
  211. package/dist/serve/worker/retirement.d.ts +21 -0
  212. package/dist/serve/worker/retirement.d.ts.map +1 -0
  213. package/dist/serve/worker/retirement.js +97 -0
  214. package/dist/serve/worker/retirement.js.map +1 -0
  215. package/dist/serve/worker/serve-init.d.ts.map +1 -1
  216. package/dist/serve/worker/serve-init.js +5 -6
  217. package/dist/serve/worker/serve-init.js.map +1 -1
  218. package/dist/vite.d.ts +1 -0
  219. package/dist/vite.d.ts.map +1 -1
  220. package/dist/vite.js.map +1 -1
  221. package/package.json +6 -4
  222. package/src/assurance/.generated/conformance-verdicts.ts +1090 -0
  223. package/src/assurance/attachment.ts +211 -0
  224. package/src/assurance/browser.ts +80 -0
  225. package/src/assurance/campaign.ts +496 -0
  226. package/src/assurance/capabilities.ts +446 -0
  227. package/src/assurance/cases.ts +43 -0
  228. package/src/assurance/index.ts +75 -0
  229. package/src/assurance/runner.ts +885 -0
  230. package/src/assurance/tool-names.ts +15 -0
  231. package/src/assurance/tools.ts +830 -0
  232. package/src/assurance/types.ts +352 -0
  233. package/src/assurance/validation.ts +434 -0
  234. package/src/bridge/client/bridge.ts +550 -0
  235. package/src/bridge/client/dispatch.ts +137 -0
  236. package/src/bridge/client.ts +45 -0
  237. package/src/bridge/protocol.ts +351 -0
  238. package/src/bridge/server/audit.ts +53 -0
  239. package/src/bridge/server/bridge.ts +597 -0
  240. package/src/bridge/server/headless.ts +160 -0
  241. package/src/bridge/server/json-schema-to-zod.ts +108 -0
  242. package/src/bridge/server/local-bridge.ts +66 -0
  243. package/src/bridge/server/logger.ts +50 -0
  244. package/src/bridge/server/mcp-contract.ts +93 -0
  245. package/src/bridge/server/mcp.ts +129 -0
  246. package/src/bridge/server/peer.ts +232 -0
  247. package/src/bridge/server/standalone.ts +347 -0
  248. package/src/bridge/server/tool-metadata.ts +99 -0
  249. package/src/bridge/server.ts +28 -0
  250. package/src/cli/can-i-use.ts +50 -0
  251. package/src/cli/cli.test.ts +493 -0
  252. package/src/cli/database-rules.ts +329 -0
  253. package/src/cli/dev-runner.ts +282 -0
  254. package/src/cli/firebase-json.ts +100 -0
  255. package/src/cli/firestore-indexes.ts +70 -0
  256. package/src/cli/index.ts +425 -0
  257. package/src/cli/init.ts +260 -0
  258. package/src/cli/mcp-proxy.ts +196 -0
  259. package/src/cli/parse-args.ts +80 -0
  260. package/src/cli/rules.ts +259 -0
  261. package/src/cli/scope.ts +67 -0
  262. package/src/cli/serve.ts +996 -0
  263. package/src/cli/service-commands.ts +73 -0
  264. package/src/cli/snapshot.ts +164 -0
  265. package/src/cli/storage-rules.ts +172 -0
  266. package/src/cli/verify.ts +416 -0
  267. package/src/conformance/.generated/can-i-use-browser.ts +113 -0
  268. package/src/conformance/.generated/can-i-use.ts +117 -0
  269. package/src/conformance/.generated/conformance-docs.ts +14 -0
  270. package/src/conformance/browser.ts +24 -0
  271. package/src/conformance/can-i-use-tool.ts +51 -0
  272. package/src/conformance/can-i-use.ts +18 -0
  273. package/src/conformance/docs.ts +8 -0
  274. package/src/conformance/index.ts +20 -0
  275. package/src/conformance/tools.ts +16 -0
  276. package/src/credentials/core/memoize-ttl.ts +142 -0
  277. package/src/credentials/core/types.ts +10 -0
  278. package/src/credentials/node/from-adc.ts +96 -0
  279. package/src/credentials/node/from-service-account.ts +133 -0
  280. package/src/credentials/node/index.ts +6 -0
  281. package/src/discover/concurrency.ts +127 -0
  282. package/src/discover/crawler-adapter.ts +142 -0
  283. package/src/discover/crawler.ts +1127 -0
  284. package/src/discover/credential-free.ts +27 -0
  285. package/src/discover/findCollectionGroup.ts +131 -0
  286. package/src/discover/firestore-source.ts +59 -0
  287. package/src/discover/index.ts +5 -0
  288. package/src/discover/merge.ts +523 -0
  289. package/src/discover/session.ts +402 -0
  290. package/src/discover/tools.ts +201 -0
  291. package/src/discover/types.ts +187 -0
  292. package/src/discover/wire.ts +324 -0
  293. package/src/functions-rtdb/child.ts +382 -0
  294. package/src/functions-rtdb/delivery.ts +7 -0
  295. package/src/functions-rtdb/discovery.ts +136 -0
  296. package/src/functions-rtdb/event.ts +64 -0
  297. package/src/functions-rtdb/execution.ts +111 -0
  298. package/src/functions-rtdb/in-memory-delivery.ts +40 -0
  299. package/src/functions-rtdb/project.ts +74 -0
  300. package/src/functions-rtdb/projection.ts +104 -0
  301. package/src/functions-rtdb/reference-pattern.ts +20 -0
  302. package/src/functions-rtdb/remote-delivery.ts +23 -0
  303. package/src/pkg-version.ts +49 -0
  304. package/src/register/esm-exports.ts +63 -0
  305. package/src/register/hooks.ts +30 -0
  306. package/src/register/index.ts +150 -0
  307. package/src/register/mapping.ts +33 -0
  308. package/src/remote/index.ts +1050 -0
  309. package/src/rtdb/crawl-snapshot.ts +118 -0
  310. package/src/rtdb/inspection.ts +137 -0
  311. package/src/rtdb/load-rules-document.ts +56 -0
  312. package/src/rtdb/rules-generation-tool.ts +38 -0
  313. package/src/rtdb/rules-json.ts +29 -0
  314. package/src/serve/activity-guard.ts +29 -0
  315. package/src/serve/activity-route.ts +164 -0
  316. package/src/serve/activity-warning.ts +26 -0
  317. package/src/serve/bridge-mount.ts +200 -0
  318. package/src/serve/bundler.ts +519 -0
  319. package/src/serve/capture-store.ts +65 -0
  320. package/src/serve/discovery.ts +199 -0
  321. package/src/serve/entries/ai.ts +292 -0
  322. package/src/serve/entries/app-backend.ts +4 -0
  323. package/src/serve/entries/app-client.ts +26 -0
  324. package/src/serve/entries/app-session-store.ts +47 -0
  325. package/src/serve/entries/app.ts +11 -0
  326. package/src/serve/entries/auth-helper-core.ts +181 -0
  327. package/src/serve/entries/auth-helper-dom.ts +134 -0
  328. package/src/serve/entries/auth-helper-runtime.ts +20 -0
  329. package/src/serve/entries/auth.ts +255 -0
  330. package/src/serve/entries/bridge-url.ts +31 -0
  331. package/src/serve/entries/database.ts +129 -0
  332. package/src/serve/entries/firestore.ts +232 -0
  333. package/src/serve/entries/init.ts +49 -0
  334. package/src/serve/entries/keepalive.ts +46 -0
  335. package/src/serve/entries/messaging-sw.ts +75 -0
  336. package/src/serve/entries/messaging.ts +96 -0
  337. package/src/serve/entries/runtime.ts +556 -0
  338. package/src/serve/entries/session-store.ts +97 -0
  339. package/src/serve/entries/storage.ts +92 -0
  340. package/src/serve/entries/tab-sync-wiring.ts +274 -0
  341. package/src/serve/entries/worker-runtime.ts +135 -0
  342. package/src/serve/init-payload.ts +42 -0
  343. package/src/serve/namespace.ts +675 -0
  344. package/src/serve/open-browser.ts +68 -0
  345. package/src/serve/rules.ts +272 -0
  346. package/src/serve/runtime/chip-config.ts +30 -0
  347. package/src/serve/runtime/chip-install.ts +27 -0
  348. package/src/serve/runtime/chip.ts +282 -0
  349. package/src/serve/runtime/manifest.ts +37 -0
  350. package/src/serve/runtime/status.ts +216 -0
  351. package/src/serve/runtime/worker-connection.ts +14 -0
  352. package/src/serve/runtime/worker-generation.ts +75 -0
  353. package/src/serve/runtime/worker-replacement.ts +42 -0
  354. package/src/serve/sandbox-marker.ts +31 -0
  355. package/src/serve/server.ts +428 -0
  356. package/src/serve/standalone-assets.ts +180 -0
  357. package/src/serve/state-store.ts +166 -0
  358. package/src/serve/studio/disk-project-store.ts +185 -0
  359. package/src/serve/studio/disk-workspace.ts +162 -0
  360. package/src/serve/studio/index.ts +20 -0
  361. package/src/serve/studio/routes.ts +237 -0
  362. package/src/serve/studio/store-types.ts +48 -0
  363. package/src/serve/studio/studio-storage.test.ts +289 -0
  364. package/src/serve/vite-ai-config.ts +77 -0
  365. package/src/serve/vite-plugin.ts +1075 -0
  366. package/src/serve/vite-rules-source.ts +28 -0
  367. package/src/serve/vite-worker-runtime.ts +51 -0
  368. package/src/serve/worker/activity-bootstrap.ts +24 -0
  369. package/src/serve/worker/client/admin-firestore.ts +38 -0
  370. package/src/serve/worker/client/ai.ts +124 -0
  371. package/src/serve/worker/client/auth.ts +489 -0
  372. package/src/serve/worker/client/connection.ts +227 -0
  373. package/src/serve/worker/client/core.ts +384 -0
  374. package/src/serve/worker/client/disconnect.ts +45 -0
  375. package/src/serve/worker/client/firestore-reads.ts +181 -0
  376. package/src/serve/worker/client/firestore-refs.ts +226 -0
  377. package/src/serve/worker/client/firestore-writes.ts +232 -0
  378. package/src/serve/worker/client/handles.ts +91 -0
  379. package/src/serve/worker/client/messaging.ts +119 -0
  380. package/src/serve/worker/client/presence.ts +174 -0
  381. package/src/serve/worker/client/rtdb.ts +311 -0
  382. package/src/serve/worker/client/rules.ts +63 -0
  383. package/src/serve/worker/client/runtime-control.ts +22 -0
  384. package/src/serve/worker/client/service-worker-connection.ts +59 -0
  385. package/src/serve/worker/client/snapshots.ts +86 -0
  386. package/src/serve/worker/client/storage.ts +195 -0
  387. package/src/serve/worker/client/studio.ts +88 -0
  388. package/src/serve/worker/client.ts +57 -0
  389. package/src/serve/worker/durable-persistence.ts +137 -0
  390. package/src/serve/worker/entry.ts +223 -0
  391. package/src/serve/worker/host/admin-firestore.ts +88 -0
  392. package/src/serve/worker/host/connection.ts +179 -0
  393. package/src/serve/worker/host/core.ts +387 -0
  394. package/src/serve/worker/host/dispatch.ts +316 -0
  395. package/src/serve/worker/host/firestore-reads.ts +117 -0
  396. package/src/serve/worker/host/firestore-writes.ts +456 -0
  397. package/src/serve/worker/host/presence.ts +312 -0
  398. package/src/serve/worker/host/rtdb.ts +136 -0
  399. package/src/serve/worker/host/rules.ts +128 -0
  400. package/src/serve/worker/host/storage.ts +312 -0
  401. package/src/serve/worker/host/studio.ts +75 -0
  402. package/src/serve/worker/host/subscriptions.ts +238 -0
  403. package/src/serve/worker/host-ai.ts +165 -0
  404. package/src/serve/worker/host-auth.ts +469 -0
  405. package/src/serve/worker/host-context.ts +273 -0
  406. package/src/serve/worker/host-events.ts +94 -0
  407. package/src/serve/worker/host-messaging.ts +240 -0
  408. package/src/serve/worker/host.ts +47 -0
  409. package/src/serve/worker/index.ts +196 -0
  410. package/src/serve/worker/presence-timing.ts +15 -0
  411. package/src/serve/worker/protocol.ts +1124 -0
  412. package/src/serve/worker/retirement.ts +125 -0
  413. package/src/serve/worker/serve-init.ts +605 -0
  414. package/src/serve/worker/service-worker-channel.ts +33 -0
  415. package/src/serve/worker/service-worker-relay.ts +86 -0
  416. package/src/serve/writer-lock.ts +53 -0
  417. package/src/verify/cases.ts +233 -0
  418. package/src/verify/fixture.ts +258 -0
  419. package/src/verify/index.ts +519 -0
  420. package/src/verify/tools.ts +108 -0
  421. package/src/version/compat-target.ts +17 -0
  422. package/src/vite.ts +18 -0
  423. package/dist/serve/docs-ui/docs/pyric-firestore-values-reference-api/index.html +0 -31
  424. package/dist/serve/docs-ui/docs/pyric-firestore-values-reference-api.md +0 -29
@@ -0,0 +1,92 @@
1
+ /**
2
+ * The bundle the import map serves for `firebase/storage`.
3
+ *
4
+ * Served apps keep their normal Firebase Storage imports. In worker mode, browse
5
+ * and metadata reads use the shared worker-hosted object store; operations that
6
+ * still lack a worker protocol fail loudly. In in-page fallback mode, the entry
7
+ * delegates to `pyric/storage` against the page sandbox.
8
+ */
9
+ import * as ip from 'pyric/storage';
10
+ import {
11
+ getStorage as pyricGetStorage,
12
+ type FirebaseStorage,
13
+ } from 'pyric/storage';
14
+ import {
15
+ getStorage as workerGetStorage,
16
+ getBlob as workerGetBlob,
17
+ getBytes as workerGetBytes,
18
+ getDownloadURL as workerGetDownloadURL,
19
+ getMetadata as workerGetMetadata,
20
+ listAll as workerListAll,
21
+ ref as workerRef,
22
+ uploadBytes as workerUploadBytes,
23
+ deleteObject as workerDeleteObject,
24
+ } from '../worker/client.js';
25
+ import { useWorker } from './worker-runtime.js';
26
+ import { getApp, type FirebaseApp } from 'pyric/app';
27
+ import { workerClientForApp } from './app-client.js';
28
+
29
+ const workerStorageByApp = new WeakMap<FirebaseApp, FirebaseStorage>();
30
+
31
+ export function getStorage(app?: FirebaseApp, _bucketUrl?: string): FirebaseStorage {
32
+ const resolved = app ?? getApp();
33
+ if (!useWorker) return pyricGetStorage(resolved, _bucketUrl);
34
+ const existing = workerStorageByApp.get(resolved);
35
+ if (existing) return existing;
36
+ const client = workerClientForApp(resolved);
37
+ const handle = Object.assign(workerGetStorage(client), { app: resolved }) as unknown as FirebaseStorage;
38
+ workerStorageByApp.set(resolved, handle);
39
+ return handle;
40
+ }
41
+
42
+ export const ref = (useWorker ? workerRef : ip.ref) as typeof ip.ref;
43
+ export const listAll = (useWorker ? workerListAll : ip.listAll) as typeof ip.listAll;
44
+ export const getMetadata = (
45
+ useWorker ? workerGetMetadata : ip.getMetadata
46
+ ) as typeof ip.getMetadata;
47
+ export const getBlob = (useWorker ? workerGetBlob : ip.getBlob) as typeof ip.getBlob;
48
+ export const getDownloadURL = (
49
+ useWorker ? workerGetDownloadURL : ip.getDownloadURL
50
+ ) as typeof ip.getDownloadURL;
51
+
52
+ export const StorageError = ip.StorageError;
53
+
54
+ // pyric replaces the Firebase emulator; connectStorageEmulator is a
55
+ // no-op. Served apps are already talking to the pyric sandbox (or the
56
+ // SharedWorker's shared store), which IS the local emulator — pointing
57
+ // it at a `firebase-tools` emulator host would be a step backward, not
58
+ // forward, so the call is logged and swallowed rather than forwarded.
59
+ export function connectStorageEmulator(
60
+ storage: unknown,
61
+ _host: string,
62
+ _port: number,
63
+ _options?: unknown,
64
+ ): void {
65
+ console.info(
66
+ '[pyric dev] pyric replaces the Firebase emulator; connectStorageEmulator is a no-op.',
67
+ );
68
+ if (!useWorker) ip.connectStorageEmulator(storage as FirebaseStorage, _host, _port, _options as never);
69
+ }
70
+
71
+ function unsupportedWorkerApi(name: string): never {
72
+ throw new Error(
73
+ `firebase/storage ${name}() is not supported over the pyric SharedWorker yet. ` +
74
+ 'Use the in-page fallback for this operation.',
75
+ );
76
+ }
77
+
78
+ function workerOrInPage<T extends (...args: any[]) => unknown>(name: string, fn: T): T {
79
+ return (useWorker ? (() => unsupportedWorkerApi(name)) : fn) as T;
80
+ }
81
+
82
+ // Byte ops use the worker protocol (base64 `storage.putBytes` /
83
+ // `storage.getBytes` / `storage.deleteObject`) and the initiating app port's
84
+ // authenticated session. All app ports reach the same object store and
85
+ // ruleset; only their active Auth sessions differ. Payloads are capped at
86
+ // 8 MiB per op.
87
+ export const uploadBytes = (useWorker ? workerUploadBytes : ip.uploadBytes) as typeof ip.uploadBytes;
88
+ export const getBytes = (useWorker ? workerGetBytes : ip.getBytes) as typeof ip.getBytes;
89
+ export const deleteObject = (useWorker ? workerDeleteObject : ip.deleteObject) as typeof ip.deleteObject;
90
+
91
+ export const uploadString = workerOrInPage('uploadString', ip.uploadString);
92
+ export const updateMetadata = workerOrInPage('updateMetadata', ip.updateMetadata);
@@ -0,0 +1,274 @@
1
+ /**
2
+ * Cross-tab realtime sync wiring for `pyric dev`.
3
+ *
4
+ * Two independent channels carry two independent kinds of state:
5
+ *
6
+ * 1. Firestore data — delegated entirely to `sandbox.enableTabSync()`.
7
+ * That call attaches a BroadcastChannel-based write-propagation and
8
+ * late-join handshake (hello/state) internally; we just supply the
9
+ * channel so the name is under our control.
10
+ *
11
+ * 2. Auth state — `enableTabSync` only carries Firestore write events;
12
+ * auth sign-in / sign-out lives entirely outside the Firestore environment.
13
+ * We bridge it on a SECOND channel using a simple full-state protocol:
14
+ * any auth change broadcasts the complete user DB + current UID; a new tab
15
+ * announces itself with `hello` and receives the current state in reply.
16
+ *
17
+ * Why full-state instead of deltas?
18
+ * Auth payloads are tiny (a handful of users). Broadcasting the entire user DB
19
+ * + current UID on every change avoids ordering bugs where a `seedUsers` arrives
20
+ * before a `restoreSession` (or vice versa) and leaves the receiving tab in an
21
+ * inconsistent partial state. One atomic message = one safe apply.
22
+ *
23
+ * Echo-loop prevention (load-bearing):
24
+ * Applying a received auth state calls `seedUsers` + `restoreSession`, which
25
+ * fire `subscribeUsers` / `onAuthStateChanged` callbacks — the same callbacks
26
+ * that trigger outbound broadcasts. Without a guard this creates an infinite
27
+ * cross-tab loop. The `applyingRemoteAuth` boolean breaks it: set to `true`
28
+ * immediately before the apply and `false` in the `finally` block; both
29
+ * broadcast callbacks check the flag and no-op while it is set.
30
+ * JavaScript is single-threaded, and `seedUsers` + `restoreSession` are
31
+ * synchronous (they resolve before any pending microtask can enter), so the
32
+ * boolean is collision-free.
33
+ *
34
+ * Late-join (hello/state):
35
+ * A tab that opens AFTER others have already signed in broadcasts `hello` on
36
+ * init. Any peer that receives it replies with `state` (full user DB +
37
+ * currentUid). The joining tab receives that `state` and calls
38
+ * `seedUsers` + `restoreSession` to catch up — same as if those ops had
39
+ * happened locally. The existing tabs' `onAuthStateChanged` listeners in the
40
+ * joining tab fire as a result, exactly as they would in production.
41
+ *
42
+ * Origin tagging:
43
+ * Every message carries an `origin` equal to the sending tab's random ID.
44
+ * Receiving tabs drop messages whose `origin` equals their own ID — both to
45
+ * comply with the echo guard and to be correct on injected test channels that
46
+ * may deliver messages back to the sender (the real BroadcastChannel spec does
47
+ * NOT deliver to the sender, but test stubs may differ).
48
+ */
49
+
50
+ import type { Auth, SeedUser } from 'pyric/auth';
51
+
52
+ // ─── Auth channel message shapes ─────────────────────────────────────────────
53
+
54
+ /** Full auth state broadcast whenever auth changes or in response to a hello. */
55
+ interface AuthStateMessage {
56
+ kind: 'state';
57
+ /** Sending tab's random ID — used for echo suppression. */
58
+ origin: string;
59
+ /** Complete user DB snapshot in the shape `seedUsers` accepts. */
60
+ users: SeedUser[];
61
+ /** UID of the signed-in user, or null when signed out. */
62
+ currentUid: string | null;
63
+ }
64
+
65
+ /** Sent by a new tab on init to request the current auth state from peers. */
66
+ interface AuthHelloMessage {
67
+ kind: 'hello';
68
+ /** Sending tab's random ID. */
69
+ origin: string;
70
+ }
71
+
72
+ type AuthSyncMessage = AuthStateMessage | AuthHelloMessage;
73
+
74
+ /** Loose type guard — checks the discriminant fields only. */
75
+ function isAuthSyncMessage(v: unknown): v is AuthSyncMessage {
76
+ return (
77
+ typeof v === 'object' &&
78
+ v !== null &&
79
+ typeof (v as Record<string, unknown>).kind === 'string' &&
80
+ typeof (v as Record<string, unknown>).origin === 'string'
81
+ );
82
+ }
83
+
84
+ // ─── Auth ops interface (injected to keep this module testable) ───────────────
85
+
86
+ /**
87
+ * The subset of `pyric/auth`'s `sandbox` namespace that we need.
88
+ * Injected so tests can stub without a real sandbox.
89
+ */
90
+ export interface AuthOps {
91
+ exportUsers(auth: Auth): SeedUser[];
92
+ seedUsers(auth: Auth, users: ReadonlyArray<SeedUser>): void;
93
+ restoreSession(auth: Auth, uid: string): void;
94
+ subscribeUsers(auth: Auth, callback: () => void): () => void;
95
+ }
96
+
97
+ // ─── Public entry point ───────────────────────────────────────────────────────
98
+
99
+ /**
100
+ * Wire cross-tab auth synchronization.
101
+ *
102
+ * Call once, after the sandbox and auth are initialised, near the END of
103
+ * `runtime.ts` init. The function is a no-op when `BroadcastChannel` is
104
+ * not available (SSR / old Node environments stay inert).
105
+ *
106
+ * @param auth The `Auth` handle for this page (from `getAuth(sandbox)`).
107
+ * @param authOps The `sandbox` namespace from `pyric/auth`.
108
+ * @param onAuthStateChanged `onAuthStateChanged` from `pyric/auth`.
109
+ * @param signOut `signOut` from `pyric/auth`.
110
+ * @param channel Optional injected channel (omit in production — a real
111
+ * `BroadcastChannel('pyric:serve:auth-sync')` is created).
112
+ * @param originId Optional injected origin ID (omit in production).
113
+ *
114
+ * @returns A disable function that removes all listeners and closes the channel.
115
+ * Safe to ignore in `pyric dev` — the tab is the lifetime.
116
+ */
117
+ export function wireAuthTabSync(
118
+ auth: Auth,
119
+ authOps: AuthOps,
120
+ onAuthStateChanged: (auth: Auth, cb: (user: unknown) => void) => () => void,
121
+ signOut: (auth: Auth) => Promise<void>,
122
+ channel?: { postMessage(msg: unknown): void; addEventListener(type: 'message', l: (ev: { data: unknown }) => void): void; removeEventListener(type: 'message', l: (ev: { data: unknown }) => void): void; close(): void },
123
+ originId?: string,
124
+ ): () => void {
125
+ // Guard: BroadcastChannel is browser-only. Non-browser imports (SSR, bundler
126
+ // tree-shaking analysis) must stay inert.
127
+ if (!channel && typeof BroadcastChannel === 'undefined') {
128
+ return () => { /* no-op: BroadcastChannel unavailable */ };
129
+ }
130
+
131
+ const bc = channel ?? new BroadcastChannel('pyric:serve:auth-sync');
132
+ const ownedByUs = !channel;
133
+
134
+ const origin: string =
135
+ originId ??
136
+ (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function'
137
+ ? crypto.randomUUID()
138
+ : `auth-tab-${Date.now()}-${Math.random().toString(36).slice(2)}`);
139
+
140
+ // ── Echo / loop guard ────────────────────────────────────────────────────
141
+ //
142
+ // WHY: applying a received `state` calls `seedUsers` (fires `subscribeUsers`
143
+ // callbacks) and `restoreSession` (fires `onAuthStateChanged` callbacks).
144
+ // Both of those callbacks are hooked to broadcast the current state outward.
145
+ // Without this guard, the sequence would loop: Tab A signs in → broadcasts →
146
+ // Tab B receives → applies → subscribeUsers/onAuthStateChanged fire in Tab B
147
+ // → Tab B broadcasts → Tab A receives → applies → loops.
148
+ //
149
+ // Setting this flag to `true` around the synchronous apply block prevents
150
+ // the outbound broadcast callbacks from posting. Both `seedUsers` and
151
+ // `restoreSession` are synchronous; JS is single-threaded — the flag is
152
+ // reset to `false` before any micro/macrotask can re-enter.
153
+ let applyingRemoteAuth = false;
154
+
155
+ // ── Debounced outbound broadcast ─────────────────────────────────────────
156
+ //
157
+ // We debounce (~100ms) to coalesce a `seedUsers` + `restoreSession` pair
158
+ // (which fires both subscribeUsers AND onAuthStateChanged in quick succession)
159
+ // into a single outbound message. Without debounce the two callbacks could
160
+ // send two rapid-fire broadcasts to peers, causing redundant double-applies.
161
+ let broadcastTimer: ReturnType<typeof setTimeout> | null = null;
162
+
163
+ function scheduleBroadcast(): void {
164
+ // Skip if we're currently applying a remote state — that would echo.
165
+ if (applyingRemoteAuth) return;
166
+ if (broadcastTimer !== null) clearTimeout(broadcastTimer);
167
+ broadcastTimer = setTimeout(() => {
168
+ broadcastTimer = null;
169
+ if (applyingRemoteAuth) return; // double-check after the debounce delay
170
+ broadcastCurrentState();
171
+ }, 100);
172
+ }
173
+
174
+ function broadcastCurrentState(): void {
175
+ const msg: AuthStateMessage = {
176
+ kind: 'state',
177
+ origin,
178
+ users: authOps.exportUsers(auth),
179
+ currentUid: auth.currentUser?.uid ?? null,
180
+ };
181
+ bc.postMessage(msg);
182
+ }
183
+
184
+ // ── Subscribe to local auth changes ──────────────────────────────────────
185
+ //
186
+ // Two hooks cover the two dimensions of auth state:
187
+ // - `subscribeUsers` fires when the user DB changes (new user created,
188
+ // user properties changed, user deleted). This carries the FULL user DB.
189
+ // - `onAuthStateChanged` fires when the signed-in session changes (sign in,
190
+ // sign out, session swap). This carries `currentUid`.
191
+ //
192
+ // Broadcasting on BOTH ensures sign-in (changes currentUid without touching
193
+ // users DB), sign-up (creates a user AND changes currentUid), and user-DB
194
+ // edits all propagate. They share the debounce so a sign-up that fires
195
+ // subscribeUsers first and onAuthStateChanged second sends one message, not two.
196
+
197
+ const unsubUsers = authOps.subscribeUsers(auth, scheduleBroadcast);
198
+ const unsubAuthState = onAuthStateChanged(auth, scheduleBroadcast);
199
+
200
+ // ── Receive remote auth state ─────────────────────────────────────────────
201
+
202
+ const handleMessage = async (ev: { data: unknown }): Promise<void> => {
203
+ if (!isAuthSyncMessage(ev.data)) return;
204
+ const msg = ev.data;
205
+
206
+ // Echo suppression: ignore messages from ourselves.
207
+ if (msg.origin === origin) return;
208
+
209
+ if (msg.kind === 'hello') {
210
+ // A new tab joined and wants the current state. Reply immediately (no
211
+ // debounce — this is a direct reply to a single event, not a user write).
212
+ broadcastCurrentState();
213
+ return;
214
+ }
215
+
216
+ if (msg.kind === 'state') {
217
+ // Apply received full auth state under the echo guard.
218
+ //
219
+ // Order is critical:
220
+ // 1. seedUsers FIRST — ensures the uid in `currentUid` exists in the DB.
221
+ // restoreSession throws 'user-not-found' if the uid is absent, which
222
+ // would leave the tab signed out even though the peer is signed in.
223
+ // 2. restoreSession SECOND — re-establishes the signed-in session.
224
+ // If `currentUid` is null, sign out instead (sign-out in Tab A must
225
+ // flow through to Tab B).
226
+ applyingRemoteAuth = true;
227
+ try {
228
+ authOps.seedUsers(auth, msg.users);
229
+ if (msg.currentUid) {
230
+ try {
231
+ authOps.restoreSession(auth, msg.currentUid);
232
+ } catch {
233
+ // uid was in the exported list but restoreSession still failed
234
+ // (e.g. user disabled between export and apply). Sign out to stay
235
+ // consistent with the peer's next broadcast rather than silently
236
+ // leaving the tab in a stale signed-in state.
237
+ await signOut(auth).catch(() => {});
238
+ }
239
+ } else {
240
+ // Peer signed out — mirror that here.
241
+ await signOut(auth).catch(() => {});
242
+ }
243
+ } finally {
244
+ applyingRemoteAuth = false;
245
+ }
246
+ }
247
+ };
248
+
249
+ bc.addEventListener('message', handleMessage);
250
+
251
+ // ── Late-join hello ───────────────────────────────────────────────────────
252
+ //
253
+ // Post a `hello` AFTER wiring the message listener so we don't miss a `state`
254
+ // reply that arrives while still setting up (relevant on injected synchronous
255
+ // test channels). Peers reply with their current auth state; we apply it in
256
+ // `handleMessage` above. This lets a freshly-opened tab immediately inherit
257
+ // the signed-in user from an already-open tab without waiting for the next
258
+ // user-driven auth change.
259
+ const helloMsg: AuthHelloMessage = { kind: 'hello', origin };
260
+ bc.postMessage(helloMsg);
261
+
262
+ // ── Disable ───────────────────────────────────────────────────────────────
263
+
264
+ return () => {
265
+ if (broadcastTimer !== null) {
266
+ clearTimeout(broadcastTimer);
267
+ broadcastTimer = null;
268
+ }
269
+ unsubUsers();
270
+ unsubAuthState();
271
+ bc.removeEventListener('message', handleMessage);
272
+ if (ownedByUs) bc.close();
273
+ };
274
+ }
@@ -0,0 +1,135 @@
1
+ /** SharedWorker availability, control-port ownership, presence, and staleness. */
2
+ import {
3
+ getFirestore,
4
+ getWorkerVersion,
5
+ startPresence,
6
+ subscribeEvents,
7
+ subscribePresence,
8
+ type ClientDb,
9
+ } from '../worker/client.js';
10
+ import { getServiceWorkerFirestore } from '../worker/client/service-worker-connection.js';
11
+ import { isServiceWorkerRealm } from '../worker/service-worker-channel.js';
12
+ import {
13
+ PYRIC_WORKER_NAME,
14
+ PYRIC_WORKER_URL,
15
+ } from '../runtime/manifest.js';
16
+ import { getPyricRuntimeStatus } from '../runtime/status.js';
17
+ import { connectRuntimeWorker } from '../runtime/worker-connection.js';
18
+ import { createWorkerReplacement } from '../runtime/worker-replacement.js';
19
+ import {
20
+ preflightWorkerEpochStorage,
21
+ rememberWorkerEpoch,
22
+ workerNameForEpoch,
23
+ } from '../runtime/worker-generation.js';
24
+ import {
25
+ onWorkerRuntimeReload,
26
+ retireWorkerRuntime,
27
+ } from '../worker/client/runtime-control.js';
28
+
29
+ const hasSharedWorker = typeof SharedWorker !== 'undefined';
30
+ const runtimeStatus = getPyricRuntimeStatus();
31
+
32
+ const workerRequested =
33
+ (hasSharedWorker || (isServiceWorkerRealm() && typeof BroadcastChannel !== 'undefined'))
34
+ && !(globalThis as { __PYRIC_FORCE_INPAGE__?: boolean }).__PYRIC_FORCE_INPAGE__;
35
+
36
+ export const WORKER_URL = PYRIC_WORKER_URL;
37
+ let epochStorage: Storage | undefined;
38
+ try {
39
+ epochStorage = typeof localStorage === 'undefined' ? undefined : localStorage;
40
+ } catch {
41
+ epochStorage = undefined;
42
+ }
43
+ export const WORKER_NAME = workerNameForEpoch(
44
+ runtimeStatus.getSnapshot().servedEpoch,
45
+ epochStorage,
46
+ );
47
+
48
+ /** Control traffic only; Firebase apps receive independent app-owned ports. */
49
+ export const workerDb: ClientDb | null = workerRequested
50
+ ? hasSharedWorker
51
+ ? connectRuntimeWorker(
52
+ () => getFirestore(WORKER_URL, WORKER_NAME, {
53
+ onError: (error) => runtimeStatus.reportError(error, 'worker'),
54
+ }),
55
+ (error) => runtimeStatus.reportError(error, 'worker'),
56
+ )
57
+ : null
58
+ : null;
59
+
60
+ export const useWorker = workerRequested && (!hasSharedWorker || workerDb !== null);
61
+
62
+ export function openWorkerDb(appName: string): ClientDb {
63
+ if (hasSharedWorker) return getFirestore(WORKER_URL, WORKER_NAME);
64
+ if (isServiceWorkerRealm()) return getServiceWorkerFirestore(appName);
65
+ throw new Error('No Pyric worker transport is available in this browser context.');
66
+ }
67
+
68
+ export const presenceSession = useWorker && workerDb
69
+ ? startPresence({ db: workerDb, kind: 'app' })
70
+ : null;
71
+
72
+ runtimeStatus.setWorker({
73
+ mode: useWorker ? 'shared-worker' : 'in-page',
74
+ runningEpoch: null,
75
+ });
76
+
77
+ const workerReplacement = useWorker && workerDb && typeof window !== 'undefined'
78
+ ? createWorkerReplacement({
79
+ targetEpoch: runtimeStatus.getSnapshot().servedEpoch!,
80
+ retire: () => retireWorkerRuntime(
81
+ workerDb,
82
+ runtimeStatus.getSnapshot().servedEpoch!,
83
+ ),
84
+ subscribeReload: onWorkerRuntimeReload,
85
+ preflight: () => preflightWorkerEpochStorage(epochStorage),
86
+ commitGeneration: (epoch) => rememberWorkerEpoch(epoch, epochStorage),
87
+ onPreparationError: (error) => runtimeStatus.reportError(error, 'worker'),
88
+ reload: () => window.location.reload(),
89
+ })
90
+ : null;
91
+ runtimeStatus.setWorkerUpdater(
92
+ workerReplacement ? () => workerReplacement.request() : null,
93
+ );
94
+
95
+ if (useWorker && workerDb) {
96
+ subscribeEvents(workerDb, (events) => runtimeStatus.recordSandboxEvents(events));
97
+ }
98
+
99
+ if (useWorker && typeof document !== 'undefined') {
100
+ const servedVersion = runtimeStatus.getSnapshot().servedEpoch;
101
+ void getWorkerVersion(workerDb!)
102
+ .then(async (runningVersion) => {
103
+ runtimeStatus.setWorker({ mode: 'shared-worker', runningEpoch: runningVersion });
104
+ if (
105
+ !servedVersion
106
+ || !runningVersion
107
+ || runningVersion === 'dev'
108
+ || servedVersion === runningVersion
109
+ ) return;
110
+ const otherPages = await new Promise<number>((resolve) => {
111
+ const unsubscribe = subscribePresence(workerDb!, (snapshot) => {
112
+ unsubscribe();
113
+ resolve(snapshot.clients.filter(
114
+ (client) => client.clientId !== presenceSession?.clientId,
115
+ ).length);
116
+ });
117
+ setTimeout(() => {
118
+ unsubscribe();
119
+ resolve(-1);
120
+ }, 2_000);
121
+ });
122
+ const othersHint = otherPages > 0
123
+ ? ` ${otherPages} other page${otherPages === 1 ? '' : 's'} must disconnect before the worker can restart.`
124
+ : otherPages === 0
125
+ ? ' This is the only connected page — reload it to pick up the new worker.'
126
+ : '';
127
+ console.warn(
128
+ `[pyric dev] the SharedWorker is running OLDER code (build ${runningVersion}) than what is now `
129
+ + `served (build ${servedVersion}). A SharedWorker can't hot-update — CLOSE ALL TABS of this origin `
130
+ + 'and reopen to load the new worker. (All tabs share one worker, so a partial reload leaves '
131
+ + `the old code running for everyone.)${othersHint}`,
132
+ );
133
+ })
134
+ .catch((error) => runtimeStatus.reportError(error, 'worker'));
135
+ }
@@ -0,0 +1,42 @@
1
+ import type { AiEngineConfigWire } from './worker/protocol.js';
2
+
3
+ /** Browser-safe wire contract served by `/__pyric/init.json`. */
4
+ export interface InitPayload {
5
+ /** Per-server capability for the warning-only activity report endpoint. */
6
+ activityToken?: string;
7
+ rules: string | null;
8
+ rulesHash: string | null;
9
+ databaseRules?: { rules: Record<string, unknown> } | null;
10
+ databaseRulesHash?: string | null;
11
+ databaseUrl?: string | null;
12
+ /** Storage rules are installed once, before the first Storage operation. */
13
+ storageRules: string | null;
14
+ storageRulesHash: string | null;
15
+ /**
16
+ * Local project identity — the served project directory. Scopes the
17
+ * storage IndexedDB database name (`pyric-storage:<projectKey>`, issue
18
+ * #359): IndexedDB is origin-scoped, so without this every project served
19
+ * on one localhost port shared one storage database. Local-only (a dev
20
+ * server path never leaves the machine). Absent/null on older servers —
21
+ * consumers fall back to the legacy shared name.
22
+ */
23
+ projectKey?: string | null;
24
+ bridgeUrl: string | null;
25
+ seed: Record<string, Record<string, unknown>> | null;
26
+ persist?: boolean;
27
+ seedState?: unknown | null;
28
+ authUsers?: ReadonlyArray<Record<string, unknown>> | null;
29
+ capture?: boolean;
30
+ messaging?: boolean;
31
+ /**
32
+ * Plugin-level AI config (`@pyric/cli/vite`'s `ai.engine`). Only the ENGINE
33
+ * travels here — the OpenAI proxy upstream is a server-side namespace option
34
+ * that never reaches the page. The worker host reads `ai.engine` into
35
+ * `ctx.aiEngine`, which wins over any op-carried `engine` (see host-ai.ts).
36
+ * Absent under `pyric dev` (no CLI surface) and whenever the plugin sets no
37
+ * engine. The in-page fallback path receives the same engine synchronously
38
+ * via the injected `globalThis.__PYRIC_AI_ENGINE__` (init.json can't be read
39
+ * synchronously by the served `getAI`).
40
+ */
41
+ ai?: { engine?: AiEngineConfigWire } | null;
42
+ }