@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,273 @@
1
+ /**
2
+ * SharedWorker host — shared foundation (context + reply helpers).
3
+ *
4
+ * The `HostCtx` is the single per-worker context shared across ALL connected
5
+ * ports and across the auth + event-stream subsystems. `PortLike`, `post`,
6
+ * `ok`, and `fail` are the minimal port + reply primitives every subsystem
7
+ * uses. This module imports NOTHING from the sibling `host-*` files, so it is
8
+ * the dependency root (no circular imports).
9
+ */
10
+
11
+ import type { Firestore } from 'pyric/firestore';
12
+ import type { Database } from 'pyric/database';
13
+ import type { LocalSandbox, PersistenceBackend } from 'pyric/sandbox';
14
+ import type { Auth, MintedSession } from 'pyric/auth';
15
+ import type { FirebaseStorage } from 'pyric/storage';
16
+ import {
17
+ serializeError,
18
+ type OutboundMessage,
19
+ type AuthPersistenceMode,
20
+ type AiEngineConfigWire,
21
+ } from './protocol.js';
22
+
23
+ // ─── AI broker (structural view) ──────────────────────────────────────────
24
+
25
+ /**
26
+ * The worker host's view of `pyric/ai`'s `AiBroker` — the in-process
27
+ * Gemini-wire answer seam (cdd-deltas #98.1: the broker lives with the
28
+ * sandbox, i.e. HERE in the serve worker host). Structural because the
29
+ * `AiBroker` class is not exported from `pyric/ai`'s public surface; the
30
+ * host recovers the instance from a `getAI(sandbox)` handle via
31
+ * `TARGET_SYMBOL` (see host-ai.ts). Requests/responses are the plain
32
+ * Gemini-wire JSON the protocol's ai ops carry.
33
+ */
34
+ export interface AiBrokerLike {
35
+ generateContent(req: Record<string, unknown>, model: string): Promise<Record<string, unknown>>;
36
+ streamGenerateContent(req: Record<string, unknown>, model: string): AsyncIterable<Record<string, unknown>>;
37
+ countTokens(req: Record<string, unknown>, model: string): Promise<Record<string, unknown>>;
38
+ }
39
+
40
+ // ─── Port interface ───────────────────────────────────────────────────────
41
+
42
+ /** Minimal postMessage interface. Fake ports in tests implement this. */
43
+ export interface PortLike {
44
+ postMessage(msg: OutboundMessage): void;
45
+ }
46
+
47
+ /** Stable worker-lifetime activity identity for an app page/port. */
48
+ export function activityJourneyId(ctx: HostCtx, port: PortLike): string {
49
+ const ids = (ctx.activityJourneyIds ??= new WeakMap());
50
+ const existing = ids.get(port);
51
+ if (existing) return existing;
52
+ const id = `page-${(ctx.activityJourneySequence = (ctx.activityJourneySequence ?? 0) + 1)}`;
53
+ ids.set(port, id);
54
+ return id;
55
+ }
56
+
57
+ // ─── Host context ─────────────────────────────────────────────────────────
58
+
59
+ export interface HostCtx {
60
+ /** The single shared Firestore handle (sandbox-live — reads currentUser per op). */
61
+ db: Firestore;
62
+ /** The underlying Sandbox, for setRules + direct sandbox ops. */
63
+ sandbox: LocalSandbox;
64
+ /** Stable worker-lifetime activity identity for each page/app port. */
65
+ activityJourneyIds?: WeakMap<PortLike, string>;
66
+ activityJourneySequence?: number;
67
+ /** Stable per-SharedWorker instance id (see {@link INSTANCE_ID_KEY}). Reported
68
+ * via `getVersion` so the UI can identify which sandbox instance this is. */
69
+ instanceId: string;
70
+ /** The shared Storage handle (Pyric Studio data browse). Lazily created on
71
+ * the first storage op via `getStorageSandbox(sandbox)`. */
72
+ storage?: FirebaseStorage;
73
+ /** Shared RTDB handle. Lazily created for the playground shared-runtime path. */
74
+ rtdb?: Database;
75
+ /** Cached admin (rules-bypass) RTDB handle for Studio/Playground data inspection. */
76
+ adminRtdb?: Database;
77
+ /** Cached admin (rules-bypass) Storage handle for the `{ mode: 'admin' }`
78
+ * lens — `pyric/storage/internal`'s `getAdminStorageSandbox`, the handle
79
+ * the pyric-admin remote arm's storage ops resolve to. */
80
+ adminStorage?: FirebaseStorage;
81
+ /** Per-uid/token Storage handles for the impersonation lens (rules evaluate
82
+ * as that user). Mirrors {@link HostCtx.lensRtdbs}. */
83
+ lensStorages?: Map<string, FirebaseStorage>;
84
+ /** Per-session Storage handles so app-session rules see the initiating
85
+ * port's authenticated identity. */
86
+ sessionStorages?: Map<string, FirebaseStorage>;
87
+ /** Cached genuinely-UNAUTHENTICATED Firestore handle for the
88
+ * `{ mode: 'anon' }` lens — `getFirestore(sandbox.withAuth(null))`, so
89
+ * rules evaluate with `request.auth == null`. The remote arm's
90
+ * `withAuth(null)`; distinct from an absent lens (the port's session). */
91
+ anonDb?: Firestore;
92
+ /** Cached unauthenticated RTDB handle for the `{ mode: 'anon' }` lens. */
93
+ anonRtdb?: Database;
94
+ /** Cached unauthenticated Storage handle for the `{ mode: 'anon' }` lens. */
95
+ anonStorage?: FirebaseStorage;
96
+ /** Per-uid RTDB handles carrying a port session's real identity. */
97
+ sessionRtdbs?: Map<string, Database>;
98
+ /** Per-uid/token RTDB handles for the Studio impersonation lens. */
99
+ lensRtdbs?: Map<string, Database>;
100
+ /** Current active rules metadata for shared-runtime diagnostics and revert. */
101
+ activeRules?: {
102
+ firestore?: ActiveRulesState;
103
+ database?: ActiveRulesState;
104
+ };
105
+ /** Per-port subscription registry. Map<port, Map<subId, unsub>>. */
106
+ subs: Map<PortLike, Map<string, () => void>>;
107
+ /** Ports whose app containers completed explicit deletion. */
108
+ disconnectedPorts?: WeakSet<PortLike>;
109
+ /** Firebase options accepted by the first app-owned port. All named apps
110
+ * and tabs share this one backend, so later app ports must match it. */
111
+ appOptions?: Record<string, unknown>;
112
+ /** App ports that attempted to attach a different Firebase configuration. */
113
+ rejectedConfigPorts?: WeakSet<PortLike>;
114
+ /**
115
+ * The ONE shared auth handle for the worker — the USER POOL (who exists)
116
+ * and the admin surface. Lazily created on first auth op or auth
117
+ * subscription. Sessions (who a connection IS) are per-port: see
118
+ * {@link HostCtx.portSessions} (#754). The handle's own `currentUser`
119
+ * stays null in served mode.
120
+ */
121
+ auth?: Auth;
122
+ /**
123
+ * Per-connection identity (#754): each port's signed-in session, minted
124
+ * via `sandbox.mintSession` (an authentic session — validated
125
+ * credentials / a really-minted identity, NOT the impersonation lens).
126
+ * A port absent from the map, or mapped to null, is signed out. The
127
+ * user pool + data + rules stay shared across ports.
128
+ */
129
+ portSessions?: Map<PortLike, MintedSession | null>;
130
+ /**
131
+ * Re-establish a port's session-bound Firestore listeners under its NEW
132
+ * session (#754) — set lazily by host.ts (which owns subs) and invoked by
133
+ * host-auth.ts on every port session change, mirroring prod's stream
134
+ * re-establishment on auth transitions: a sign-out re-evaluates live
135
+ * listeners so an auth-gated stream loses access instead of leaking the
136
+ * previous user's data. (A ctx hook, not an import — host-auth must not
137
+ * import host.ts.)
138
+ */
139
+ resubscribePortSubs?: (port: PortLike) => void;
140
+ /**
141
+ * Per-session-token Firestore handles carrying a PORT SESSION's identity —
142
+ * `getFirestore(sandbox.withAuth(session.state))`, so rules see the
143
+ * session's uid AND its custom claims on `request.auth.token`. Keyed by
144
+ * uid + token so distinct claim snapshots never share a handle; forced
145
+ * refresh also clears the cache. Distinct from
146
+ * {@link HostCtx.lensHandles}, which is the Studio debugging lens
147
+ * (uid-only, caller-gated writes).
148
+ */
149
+ sessionDbs?: Map<string, Firestore>;
150
+ /**
151
+ * The backend used for named state branches (save/switch). Session
152
+ * persistence is NOT here anymore: per-tab sessions persist client-side
153
+ * in web storage (the page restores via `auth.restorePortSession`),
154
+ * mirroring how the real SDK owns persistence in the client (#754).
155
+ */
156
+ sessionBackend?: PersistenceBackend;
157
+ /**
158
+ * Accepted `auth.setPersistence` mode, kept for surface parity. The
159
+ * worker itself no longer writes a session record — the CLIENT's
160
+ * SessionStore honors the mode (which web storage slot, or none).
161
+ */
162
+ sessionMode?: AuthPersistenceMode;
163
+ /**
164
+ * Force an IMMEDIATE session-capture flush, bypassing the debounce — set
165
+ * by `applyServeInit` when `--capture` is on (see the capture block in
166
+ * serve-init.ts). The `resetAll` op invokes it so the server-persisted
167
+ * capture (`.pyric/last-session.json`) records the post-reset (empty)
168
+ * event history instead of the wiped session's traffic, which a rebooting
169
+ * worker would otherwise re-prime via `hydrateEventHistory` (issue #359
170
+ * extension). Absent when capture is off.
171
+ */
172
+ captureFlush?: () => Promise<void>;
173
+ /**
174
+ * Per-uid impersonation Firestore handles (Pyric Studio auth lens, T2).
175
+ * Keyed by the impersonated uid. Each is a FROZEN-identity
176
+ * `getFirestore(sandbox.withAuth({ uid }))` handle whose ops evaluate
177
+ * security rules AS that user — the rules-debugging primitive. Cached so
178
+ * repeated `{ mode: 'as', uid }` ops reuse one handle (and so the modular
179
+ * layer's per-ctx ref cache stays warm). Lazily populated by `lensDb`.
180
+ */
181
+ lensHandles?: Map<string, Firestore>;
182
+ /**
183
+ * Cached admin (rules-bypass) Firestore handle for the `{ mode: 'admin' }`
184
+ * lens (Pyric Studio, Gap #2). A modular `getAdminFirestore(sandbox)` handle
185
+ * whose ops skip security-rule evaluation — Studio's "edit anything as admin"
186
+ * (F2). Lazily populated by `lensDb`.
187
+ */
188
+ adminDb?: Firestore;
189
+ /**
190
+ * The sandbox's AiBroker (pyric/ai), lazily created on the first ai op /
191
+ * ai stream sub via `getAI(ctx.sandbox, …)` — so the worker's broker IS the
192
+ * mirror's per-sandbox broker (one instance, events land on the shared
193
+ * sandbox's unified stream). See `ensureAiBroker` in host-ai.ts.
194
+ */
195
+ aiBroker?: AiBrokerLike;
196
+ /**
197
+ * Host-construction engine config for the AI broker (cdd-deltas #98.4).
198
+ * When set (embedding/test hosts, or serve wiring), it WINS over any
199
+ * op-carried `engine` field; absent, the first ai op's `engine` field is
200
+ * honored; both absent ⇒ the zero-config scripted default.
201
+ */
202
+ aiEngine?: AiEngineConfigWire;
203
+ /**
204
+ * Lazily-built agent tool dispatcher (the canonical sandbox tool set) bound to
205
+ * THIS worker's sandbox. The bridge peer forwards `tool` messages here so the
206
+ * agent executes against the one shared sandbox, not a separate in-page
207
+ * backend. Cached so the handler array is built once.
208
+ */
209
+ toolDispatch?: (
210
+ name: string,
211
+ args: Record<string, unknown>,
212
+ ) => Promise<{ ok: boolean; summary: string; data?: unknown }>;
213
+ /**
214
+ * Messaging host capability. Normal serve producers enable it because
215
+ * Messaging is in the canonical SDK swap. Absent/false answers
216
+ * `messaging/disabled` for standalone host construction and focused tests.
217
+ */
218
+ messagingEnabled?: boolean;
219
+ /**
220
+ * Per-port broker client ids (the worker-host seam's visibility mapping):
221
+ * a port that reports `messaging.setVisibility` becomes ONE window client
222
+ * in the broker, so the captured routing rule — foreground iff ANY visible
223
+ * client — spans tabs. `cleanupPort` removes the client so a closed tab
224
+ * stops counting as visible. Lazily populated by host-messaging.ts.
225
+ */
226
+ messagingClients?: Map<PortLike, string>;
227
+ }
228
+
229
+ export interface ActiveRulesState {
230
+ source: unknown;
231
+ updatedAt: number;
232
+ status: 'active' | 'error';
233
+ messages: Array<{ severity: 'info' | 'warn' | 'error'; text: string; line?: number; column?: number }>;
234
+ lastKnownGood?: unknown;
235
+ }
236
+
237
+ // ─── Utility: post a typed reply to a port ───────────────────────────────
238
+
239
+ export function post(port: PortLike, msg: OutboundMessage): void {
240
+ port.postMessage(msg);
241
+ }
242
+
243
+ export function ok(port: PortLike, id: string, value: unknown): void {
244
+ post(port, { t: 'res', id, ok: true, value });
245
+ }
246
+
247
+ export function fail(port: PortLike, id: string, err: unknown): void {
248
+ // The serialized error carries `denialContext` when present (spike gap 6) —
249
+ // post it whole so the structured denial frame reaches remote consumers.
250
+ post(port, { t: 'res', id, ok: false, error: serializeError(err) });
251
+ }
252
+
253
+ /**
254
+ * Await a persistence flush BEFORE acking a mutating op, so an acknowledged
255
+ * write is durable in IndexedDB when the reply reaches the client. This is
256
+ * the worker's ONLY durability mechanism: there is no beforeterminate-style
257
+ * event for a SharedWorker, so a teardown on last-tab close/reload can kill
258
+ * the worker inside the controller's debounce window — an un-flushed write
259
+ * would be silently lost (the reload-durability bug).
260
+ *
261
+ * "Best-effort" refers to the REPLY, not the flush: a flush failure (e.g.
262
+ * storage quota) must not turn a successful in-memory mutation into a wire
263
+ * error, but it IS logged — a silently swallowed flush failure is exactly
264
+ * how a broken flush contract stays invisible.
265
+ */
266
+ export async function bestEffortFlush(ctx: HostCtx): Promise<void> {
267
+ try {
268
+ await ctx.sandbox.flush?.();
269
+ } catch (e) {
270
+ // eslint-disable-next-line no-console
271
+ console.warn('[pyric worker] persistence flush after acked write failed:', e);
272
+ }
273
+ }
@@ -0,0 +1,94 @@
1
+ /**
2
+ * SharedWorker host — event-stream subsystem (Pyric Studio keystone).
3
+ *
4
+ * Surfaces the sandbox's unified cross-service `onEvent`/`history()` stream over
5
+ * the port so Studio's Action Center / traffic / rules-debug denial feed see
6
+ * real activity. Imports only from `./host-context.js` + external packages (no
7
+ * circular imports).
8
+ */
9
+
10
+ import { type HostCtx, type PortLike, post } from './host-context.js';
11
+ import type { SandboxEvent } from 'pyric/sandbox';
12
+ import type { EventSubMessage } from './protocol.js';
13
+
14
+ // ─── Event-stream subscription (Pyric Studio keystone) ─────────────────────
15
+ //
16
+ // Surfaces the sandbox's unified cross-service `onEvent`/`history()` stream over
17
+ // the port so Studio's Action Center / traffic / rules-debug denial feed see
18
+ // real activity. Architecture mirrors the AUTH fan-out: ONE real
19
+ // `sandbox.onEvent` subscription per worker (lazily wired in {@link ensureEventStream}),
20
+ // and a per-port routing registry telling the host which ports want events. On
21
+ // any event the single sandbox subscription fires once and we fan it out to
22
+ // EVERY subscribed port — so multiple Studio/app tabs share the one backend
23
+ // stream. Events are plain JSON (no codec round-trip; see EventSubMessage).
24
+
25
+ /** Per-port event-stream subscription registry. Map<port, Set<subId>>. The
26
+ * WeakMap is keyed by ctx so multiple in-process hosts (tests) don't collide. */
27
+ const _eventSubs = new WeakMap<HostCtx, Map<PortLike, Set<string>>>();
28
+
29
+ /** Tracks whether the single real `sandbox.onEvent` subscription is wired. */
30
+ const _eventStreamWired = new WeakSet<HostCtx>();
31
+
32
+ export function eventSubsFor(ctx: HostCtx): Map<PortLike, Set<string>> {
33
+ let m = _eventSubs.get(ctx);
34
+ if (!m) {
35
+ m = new Map();
36
+ _eventSubs.set(ctx, m);
37
+ }
38
+ return m;
39
+ }
40
+
41
+ /**
42
+ * Lazily wire the ONE real `sandbox.onEvent` subscription for this ctx. On every
43
+ * sandbox event it fans the event out (as a single-element batch) to every port
44
+ * subscribed to the stream. Idempotent — only the first event sub wires it; the
45
+ * subscription then lives for the worker's lifetime (cheap, observational).
46
+ */
47
+ function ensureEventStream(ctx: HostCtx): void {
48
+ if (_eventStreamWired.has(ctx)) return;
49
+ _eventStreamWired.add(ctx);
50
+ ctx.sandbox.onEvent((event) => {
51
+ broadcastEvents(ctx, [event]);
52
+ });
53
+ }
54
+
55
+ /** Fan a batch of events out to every port subscribed to the event stream. */
56
+ function broadcastEvents(ctx: HostCtx, events: readonly SandboxEvent[]): void {
57
+ const subs = eventSubsFor(ctx);
58
+ for (const [port, subIds] of subs) {
59
+ for (const subId of subIds) {
60
+ post(port, { t: 'event', subId, events });
61
+ }
62
+ }
63
+ }
64
+
65
+ /**
66
+ * Register an event-stream subscription for a port (Pyric Studio keystone).
67
+ * Records the (port, subId) so live events reach it, ensures the single real
68
+ * sandbox subscription is wired, and immediately delivers `sandbox.history()`
69
+ * (every event so far) as the initial batch — so a late subscriber folds the
70
+ * full backlog then streams uniformly (mirrors `onEvent` + `history`).
71
+ */
72
+ export function handleEventSub(ctx: HostCtx, port: PortLike, msg: EventSubMessage): void {
73
+ ensureEventStream(ctx);
74
+ const subs = eventSubsFor(ctx);
75
+ let subIds = subs.get(port);
76
+ if (!subIds) {
77
+ subIds = new Set();
78
+ subs.set(port, subIds);
79
+ }
80
+ if (subIds.has(msg.subId)) return; // idempotent
81
+ subIds.add(msg.subId);
82
+
83
+ // Initial fire: the full history snapshot (a defensive copy from the sandbox).
84
+ post(port, { t: 'event', subId: msg.subId, events: ctx.sandbox.history() });
85
+ }
86
+
87
+ export function handleEventUnsub(ctx: HostCtx, port: PortLike, subId: string): boolean {
88
+ const subs = eventSubsFor(ctx);
89
+ const subIds = subs.get(port);
90
+ if (!subIds || !subIds.has(subId)) return false;
91
+ subIds.delete(subId);
92
+ if (subIds.size === 0) subs.delete(port);
93
+ return true;
94
+ }
@@ -0,0 +1,240 @@
1
+ /**
2
+ * SharedWorker host — messaging subsystem: the broker's documented
3
+ * worker-host seam, wired (see `pyric/src/messaging/broker/broker.ts`
4
+ * header — each public broker method is one `messaging.*` op here).
5
+ *
6
+ * ONE broker per sandbox (`getMessagingBroker`), shared by every port —
7
+ * production's one-service-worker-per-origin model. Two seams cross the
8
+ * transport:
9
+ *
10
+ * OPS token lifecycle (`getToken`/`deleteToken`), the send plane
11
+ * (`send`, topic management), the delivery driver (`deliver`), and
12
+ * visibility (`setVisibility`). Send-plane rejections carry the
13
+ * broker's captured google.rpc envelope VERBATIM on
14
+ * `error.envelope` (plain JSON, structured-clone-safe).
15
+ *
16
+ * SUBS `messaging.foreground` / `messaging.background` register real
17
+ * broker handlers whose payloads forward to the subscribing port as
18
+ * snaps. Unsubs ride the ordinary `ctx.subs` registry, so
19
+ * `cleanupPort` tears them down like Firestore listeners.
20
+ *
21
+ * PER-PORT VISIBILITY — the captured routing rule crossing the transport:
22
+ * each port that reports `messaging.setVisibility` is ONE window client in
23
+ * the broker (`setClientVisibility(portId, state)` on the page's
24
+ * `visibilitychange`). A hidden tab's port marks its client not-visible;
25
+ * routing is foreground iff ANY visible client (oracle:
26
+ * `messaging-web-visibility-routing` — visibility, never focus). Port close
27
+ * removes the client so a dead tab cannot pin foreground routing.
28
+ *
29
+ * HOST CAPABILITY GATE: ops exist only when the ctx was built with
30
+ * `messagingEnabled: true`. Both serve producers enable it because Messaging
31
+ * is part of the canonical Firebase SDK swap. The disabled path remains for
32
+ * standalone host construction and focused tests.
33
+ *
34
+ * Imports only from `./host-context.js` + external packages (host-auth
35
+ * precedent — no circular imports).
36
+ */
37
+
38
+ import { getMessagingBroker, BrokerSendError } from 'pyric/messaging/internal';
39
+ import type { MessagingBroker } from 'pyric/messaging/internal';
40
+
41
+ import { type HostCtx, type PortLike, post, ok, fail } from './host-context.js';
42
+ import type { OpMessage, MessagingSubMessage } from './protocol.js';
43
+
44
+ /** Registration id used when a `messaging.getToken`/`deleteToken` op names
45
+ * none — the transport twin of the mirrors' module-default registration. */
46
+ export const DEFAULT_WIRE_REGISTRATION_ID = 'swreg-port-default';
47
+
48
+ const MESSAGING_METHODS = new Set([
49
+ 'messaging.getToken',
50
+ 'messaging.deleteToken',
51
+ 'messaging.send',
52
+ 'messaging.subscribeToTopic',
53
+ 'messaging.unsubscribeFromTopic',
54
+ 'messaging.deliver',
55
+ 'messaging.setVisibility',
56
+ ]);
57
+
58
+ /** Is this op method part of the messaging surface? (Routing predicate for
59
+ * handleMessage — the auth-op precedent.) */
60
+ export function isMessagingOp(method: string): boolean {
61
+ return MESSAGING_METHODS.has(method);
62
+ }
63
+
64
+ /** The error every messaging op/sub answers on a host without the capability. */
65
+ function disabledError(): { code: string; message: string } {
66
+ return {
67
+ code: 'messaging/disabled',
68
+ message:
69
+ 'pyric worker: this host was created without Messaging enabled. ' +
70
+ 'Build the worker host ctx with `messagingEnabled: true`.',
71
+ };
72
+ }
73
+
74
+ function broker(ctx: HostCtx): MessagingBroker {
75
+ return getMessagingBroker(ctx.sandbox);
76
+ }
77
+
78
+ // Module-level mint so client ids never collide across delete/re-add on one
79
+ // broker (a ctx-map-size mint would reuse ids after removal).
80
+ let clientSeq = 0;
81
+
82
+ /** The port's broker client id, minted on first visibility report. */
83
+ function clientIdFor(ctx: HostCtx, port: PortLike): string {
84
+ const clients = (ctx.messagingClients ??= new Map());
85
+ let id = clients.get(port);
86
+ if (id === undefined) {
87
+ id = `port-client-${++clientSeq}`;
88
+ clients.set(port, id);
89
+ }
90
+ return id;
91
+ }
92
+
93
+ /**
94
+ * Fail a messaging op. A `BrokerSendError` crosses the wire as its captured
95
+ * envelope (the seam contract): `code` is the envelope's google.rpc status
96
+ * (`INVALID_ARGUMENT`, `NOT_FOUND`, …), `envelope` the full pinned value.
97
+ */
98
+ function failMessaging(port: PortLike, id: string, err: unknown): void {
99
+ if (err instanceof BrokerSendError) {
100
+ post(port, {
101
+ t: 'res',
102
+ id,
103
+ ok: false,
104
+ error: {
105
+ code: err.envelope.error.status,
106
+ message: err.envelope.error.message,
107
+ envelope: err.envelope,
108
+ },
109
+ });
110
+ return;
111
+ }
112
+ fail(port, id, err);
113
+ }
114
+
115
+ export async function handleMessagingOp(
116
+ ctx: HostCtx,
117
+ port: PortLike,
118
+ msg: OpMessage,
119
+ ): Promise<void> {
120
+ if (ctx.messagingEnabled !== true) {
121
+ post(port, { t: 'res', id: msg.id, ok: false, error: disabledError() });
122
+ return;
123
+ }
124
+
125
+ switch (msg.method) {
126
+ case 'messaging.getToken': {
127
+ // Stable per registration (oracle: `messaging-web-token-stability`).
128
+ try {
129
+ const token = broker(ctx).getTokenFor(msg.registrationId ?? DEFAULT_WIRE_REGISTRATION_ID);
130
+ ok(port, msg.id, { token });
131
+ } catch (e) { failMessaging(port, msg.id, e); }
132
+ break;
133
+ }
134
+
135
+ case 'messaging.deleteToken': {
136
+ // Resolves truthy either way (oracle: `messaging-web-deletetoken-unregistered`).
137
+ try {
138
+ ok(port, msg.id, broker(ctx).deleteTokenFor(msg.registrationId ?? DEFAULT_WIRE_REGISTRATION_ID));
139
+ } catch (e) { failMessaging(port, msg.id, e); }
140
+ break;
141
+ }
142
+
143
+ case 'messaging.send': {
144
+ // AcceptedSend is plain JSON — replies verbatim. Rejections carry the
145
+ // captured envelope (see failMessaging).
146
+ try {
147
+ const accepted = broker(ctx).send(msg.message, {
148
+ ...(msg.validateOnly !== undefined ? { validateOnly: msg.validateOnly } : {}),
149
+ });
150
+ ok(port, msg.id, accepted);
151
+ } catch (e) { failMessaging(port, msg.id, e); }
152
+ break;
153
+ }
154
+
155
+ case 'messaging.subscribeToTopic': {
156
+ try {
157
+ ok(port, msg.id, broker(ctx).subscribeToTopic(msg.tokens, msg.topic));
158
+ } catch (e) { failMessaging(port, msg.id, e); }
159
+ break;
160
+ }
161
+
162
+ case 'messaging.unsubscribeFromTopic': {
163
+ try {
164
+ ok(port, msg.id, broker(ctx).unsubscribeFromTopic(msg.tokens, msg.topic));
165
+ } catch (e) { failMessaging(port, msg.id, e); }
166
+ break;
167
+ }
168
+
169
+ case 'messaging.deliver': {
170
+ // Test/Studio driver — DeliveryResult { route, handlerCount, payload }
171
+ // is plain JSON. `visibilityState` (the in-page driver's twin) sets THIS
172
+ // port's client visibility before routing, so `pyric/messaging`'s
173
+ // `sandbox.deliver(spec)` picks foreground/background over the transport
174
+ // exactly as in-page: visible → onMessage, hidden → onBackgroundMessage.
175
+ try {
176
+ const { visibilityState, ...payload } = msg.spec;
177
+ if (visibilityState !== undefined) {
178
+ broker(ctx).setClientVisibility(clientIdFor(ctx, port), visibilityState);
179
+ }
180
+ ok(port, msg.id, broker(ctx).deliver(payload));
181
+ } catch (e) { failMessaging(port, msg.id, e); }
182
+ break;
183
+ }
184
+
185
+ case 'messaging.setVisibility': {
186
+ // THE routing input crossing the transport: this port's tab is one
187
+ // window client; its visibility report updates the broker state the
188
+ // captured rule routes on.
189
+ try {
190
+ broker(ctx).setClientVisibility(clientIdFor(ctx, port), msg.state);
191
+ ok(port, msg.id, null);
192
+ } catch (e) { failMessaging(port, msg.id, e); }
193
+ break;
194
+ }
195
+
196
+ default:
197
+ fail(port, msg.id, new Error(`Unknown messaging method: ${String(msg.method)}`));
198
+ }
199
+ }
200
+
201
+ /**
202
+ * Register a foreground/background delivery listener for this port. The
203
+ * broker handler's unsub goes into the ordinary `ctx.subs` registry, so the
204
+ * generic unsub path and `cleanupPort` both tear it down. Payloads are plain
205
+ * JSON (`DeliveredPayload`) — no codec round-trip.
206
+ */
207
+ export function handleMessagingSub(ctx: HostCtx, port: PortLike, msg: MessagingSubMessage): void {
208
+ if (ctx.messagingEnabled !== true) {
209
+ // Mirror handleSub's error path: deliver the gate as a snap-error so the
210
+ // client's error callback fires instead of the sub silently dying.
211
+ post(port, { t: 'snap', subId: msg.subId, value: { __error: disabledError() } });
212
+ return;
213
+ }
214
+
215
+ if (!ctx.subs.has(port)) ctx.subs.set(port, new Map());
216
+ const portSubs = ctx.subs.get(port)!;
217
+ if (portSubs.has(msg.subId)) return; // idempotent
218
+
219
+ const b = broker(ctx);
220
+ const forward = (payload: unknown): void =>
221
+ post(port, { t: 'snap', subId: msg.subId, value: payload });
222
+ const unsub =
223
+ msg.target === 'messaging.foreground'
224
+ ? b.onForegroundMessage(forward)
225
+ : b.onBackgroundMessage(forward);
226
+ portSubs.set(msg.subId, unsub);
227
+ }
228
+
229
+ /**
230
+ * Drop a disconnected port's broker client so its last-reported visibility
231
+ * stops feeding the routing rule (a closed visible tab must not pin
232
+ * foreground routing forever). Handler subs are NOT touched here — they
233
+ * live in `ctx.subs`, which `cleanupPort` already tears down.
234
+ */
235
+ export function cleanupPortMessaging(ctx: HostCtx, port: PortLike): void {
236
+ const id = ctx.messagingClients?.get(port);
237
+ if (id === undefined) return;
238
+ ctx.messagingClients!.delete(port);
239
+ broker(ctx).removeClient(id);
240
+ }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * SharedWorker host — public barrel over the per-family modules under `host/`.
3
+ *
4
+ * The host is the SharedWorker's data plane: one `HostCtx` shared across ALL
5
+ * connected ports, driven per message by `handleMessage(ctx, port, msg)`. It is
6
+ * deliberately decoupled from `SharedWorkerGlobalScope` so unit tests can drive
7
+ * it with a REAL pyric sandbox + fake MessagePort objects — no browser or
8
+ * SharedWorker runtime required. The entry point (`entry.ts`) creates the real
9
+ * sandbox + db and wires the connecting ports to this module.
10
+ *
11
+ * This file re-exports only the host's PUBLIC surface; the implementation lives
12
+ * in the per-family modules (mirroring the client split under `client/`):
13
+ * - `host/core` shared lens/handle resolution, descriptor → ref
14
+ * resolution, snapshot serialization, op provenance
15
+ * - `host/dispatch` `handleMessage` seam + family routing + `cleanupPort`
16
+ * - `host/firestore-reads` getDoc/getDocs/count/aggregate/keyspace ops
17
+ * - `host/firestore-writes` set/update/delete/add/batch/txn ops (+ sentinels)
18
+ * - `host/rules` firestore/rtdb rules deploy + status
19
+ * - `host/admin-firestore` admin-lens document ops (rules bypass)
20
+ * - `host/rtdb` RTDB modular ops + snapshot→wire shaping
21
+ * - `host/storage` worker-backed Cloud Storage ops + lens resolver
22
+ * - `host/connection` version / state transfer / branches + instance id
23
+ * - `host/studio` runtime confirm-policy + sandbox snapshot
24
+ * - `host/subscriptions` Firestore/RTDB value-subscription registry (#754)
25
+ *
26
+ * The already-extracted auth / AI / events / messaging families keep their
27
+ * sibling `host-*.ts` modules; `host/dispatch` routes to them.
28
+ *
29
+ * `HostCtx` + the shared reply/port primitives live in `host-context.ts` (the
30
+ * engine-free dependency root).
31
+ */
32
+
33
+ // Auth surface used by serve-init (ensureAuth) + the per-port session accessor.
34
+ export { ensureAuth, portSession } from './host-auth.js';
35
+ export type { HostCtx, PortLike } from './host-context.js';
36
+ // Instance-id + named-branch surface (part of the host's public shape:
37
+ // serve-init imports getOrCreateInstanceId; tests import the rest).
38
+ export {
39
+ INSTANCE_ID_KEY,
40
+ randomUuid,
41
+ getOrCreateInstanceId,
42
+ BRANCH_PREFIX,
43
+ BRANCH_REGISTRY_KEY,
44
+ listBranchNames,
45
+ } from './host/connection.js';
46
+ // The message dispatcher seam (entry.ts + tests) and per-port teardown.
47
+ export { handleMessage, cleanupPort } from './host/dispatch.js';