pyric-tools 0.1.0-alpha.7

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 (1052) hide show
  1. package/README.md +125 -0
  2. package/dist/auth/domains/handler.d.ts +6 -0
  3. package/dist/auth/domains/handler.d.ts.map +1 -0
  4. package/dist/auth/domains/handler.js +75 -0
  5. package/dist/auth/domains/handler.js.map +1 -0
  6. package/dist/auth/domains/spec.d.ts +25 -0
  7. package/dist/auth/domains/spec.d.ts.map +1 -0
  8. package/dist/auth/domains/spec.js +6 -0
  9. package/dist/auth/domains/spec.js.map +1 -0
  10. package/dist/auth/index.d.ts +34 -0
  11. package/dist/auth/index.d.ts.map +1 -0
  12. package/dist/auth/index.js +30 -0
  13. package/dist/auth/index.js.map +1 -0
  14. package/dist/auth/mapper.d.ts +5 -0
  15. package/dist/auth/mapper.d.ts.map +1 -0
  16. package/dist/auth/mapper.js +49 -0
  17. package/dist/auth/mapper.js.map +1 -0
  18. package/dist/auth/provider/handler.d.ts +9 -0
  19. package/dist/auth/provider/handler.d.ts.map +1 -0
  20. package/dist/auth/provider/handler.js +128 -0
  21. package/dist/auth/provider/handler.js.map +1 -0
  22. package/dist/auth/provider/spec.d.ts +29 -0
  23. package/dist/auth/provider/spec.d.ts.map +1 -0
  24. package/dist/auth/provider/spec.js +12 -0
  25. package/dist/auth/provider/spec.js.map +1 -0
  26. package/dist/auth/resolver.d.ts +16 -0
  27. package/dist/auth/resolver.d.ts.map +1 -0
  28. package/dist/auth/resolver.js +32 -0
  29. package/dist/auth/resolver.js.map +1 -0
  30. package/dist/auth/tools.d.ts +23 -0
  31. package/dist/auth/tools.d.ts.map +1 -0
  32. package/dist/auth/tools.js +71 -0
  33. package/dist/auth/tools.js.map +1 -0
  34. package/dist/auth/types.d.ts +37 -0
  35. package/dist/auth/types.d.ts.map +1 -0
  36. package/dist/auth/types.js +17 -0
  37. package/dist/auth/types.js.map +1 -0
  38. package/dist/bridge/client/bridge.d.ts +133 -0
  39. package/dist/bridge/client/bridge.d.ts.map +1 -0
  40. package/dist/bridge/client/bridge.js +430 -0
  41. package/dist/bridge/client/bridge.js.map +1 -0
  42. package/dist/bridge/client/dispatch.d.ts +49 -0
  43. package/dist/bridge/client/dispatch.d.ts.map +1 -0
  44. package/dist/bridge/client/dispatch.js +103 -0
  45. package/dist/bridge/client/dispatch.js.map +1 -0
  46. package/dist/bridge/client.d.ts +24 -0
  47. package/dist/bridge/client.d.ts.map +1 -0
  48. package/dist/bridge/client.js +26 -0
  49. package/dist/bridge/client.js.map +1 -0
  50. package/dist/bridge/protocol.d.ts +228 -0
  51. package/dist/bridge/protocol.d.ts.map +1 -0
  52. package/dist/bridge/protocol.js +131 -0
  53. package/dist/bridge/protocol.js.map +1 -0
  54. package/dist/bridge/server/audit.d.ts +19 -0
  55. package/dist/bridge/server/audit.d.ts.map +1 -0
  56. package/dist/bridge/server/audit.js +46 -0
  57. package/dist/bridge/server/audit.js.map +1 -0
  58. package/dist/bridge/server/bridge.d.ts +165 -0
  59. package/dist/bridge/server/bridge.d.ts.map +1 -0
  60. package/dist/bridge/server/bridge.js +410 -0
  61. package/dist/bridge/server/bridge.js.map +1 -0
  62. package/dist/bridge/server/confirm-policy.d.ts +56 -0
  63. package/dist/bridge/server/confirm-policy.d.ts.map +1 -0
  64. package/dist/bridge/server/confirm-policy.js +107 -0
  65. package/dist/bridge/server/confirm-policy.js.map +1 -0
  66. package/dist/bridge/server/confirm-prompt.d.ts +73 -0
  67. package/dist/bridge/server/confirm-prompt.d.ts.map +1 -0
  68. package/dist/bridge/server/confirm-prompt.js +199 -0
  69. package/dist/bridge/server/confirm-prompt.js.map +1 -0
  70. package/dist/bridge/server/confirm.d.ts +92 -0
  71. package/dist/bridge/server/confirm.d.ts.map +1 -0
  72. package/dist/bridge/server/confirm.js +210 -0
  73. package/dist/bridge/server/confirm.js.map +1 -0
  74. package/dist/bridge/server/headless.d.ts +36 -0
  75. package/dist/bridge/server/headless.d.ts.map +1 -0
  76. package/dist/bridge/server/headless.js +132 -0
  77. package/dist/bridge/server/headless.js.map +1 -0
  78. package/dist/bridge/server/json-schema-to-zod.d.ts +37 -0
  79. package/dist/bridge/server/json-schema-to-zod.d.ts.map +1 -0
  80. package/dist/bridge/server/json-schema-to-zod.js +93 -0
  81. package/dist/bridge/server/json-schema-to-zod.js.map +1 -0
  82. package/dist/bridge/server/local-bridge.d.ts +33 -0
  83. package/dist/bridge/server/local-bridge.d.ts.map +1 -0
  84. package/dist/bridge/server/local-bridge.js +42 -0
  85. package/dist/bridge/server/local-bridge.js.map +1 -0
  86. package/dist/bridge/server/logger.d.ts +28 -0
  87. package/dist/bridge/server/logger.d.ts.map +1 -0
  88. package/dist/bridge/server/logger.js +42 -0
  89. package/dist/bridge/server/logger.js.map +1 -0
  90. package/dist/bridge/server/mcp.d.ts +33 -0
  91. package/dist/bridge/server/mcp.d.ts.map +1 -0
  92. package/dist/bridge/server/mcp.js +159 -0
  93. package/dist/bridge/server/mcp.js.map +1 -0
  94. package/dist/bridge/server/peer.d.ts +39 -0
  95. package/dist/bridge/server/peer.d.ts.map +1 -0
  96. package/dist/bridge/server/peer.js +189 -0
  97. package/dist/bridge/server/peer.js.map +1 -0
  98. package/dist/bridge/server/standalone.d.ts +102 -0
  99. package/dist/bridge/server/standalone.d.ts.map +1 -0
  100. package/dist/bridge/server/standalone.js +352 -0
  101. package/dist/bridge/server/standalone.js.map +1 -0
  102. package/dist/bridge/server/tool-metadata.d.ts +38 -0
  103. package/dist/bridge/server/tool-metadata.d.ts.map +1 -0
  104. package/dist/bridge/server/tool-metadata.js +79 -0
  105. package/dist/bridge/server/tool-metadata.js.map +1 -0
  106. package/dist/bridge/server.d.ts +25 -0
  107. package/dist/bridge/server.d.ts.map +1 -0
  108. package/dist/bridge/server.js +23 -0
  109. package/dist/bridge/server.js.map +1 -0
  110. package/dist/cli/auth.d.ts +37 -0
  111. package/dist/cli/auth.d.ts.map +1 -0
  112. package/dist/cli/auth.js +119 -0
  113. package/dist/cli/auth.js.map +1 -0
  114. package/dist/cli/database-rules.d.ts +25 -0
  115. package/dist/cli/database-rules.d.ts.map +1 -0
  116. package/dist/cli/database-rules.js +215 -0
  117. package/dist/cli/database-rules.js.map +1 -0
  118. package/dist/cli/deploy-progress.d.ts +35 -0
  119. package/dist/cli/deploy-progress.d.ts.map +1 -0
  120. package/dist/cli/deploy-progress.js +69 -0
  121. package/dist/cli/deploy-progress.js.map +1 -0
  122. package/dist/cli/deploy.d.ts +81 -0
  123. package/dist/cli/deploy.d.ts.map +1 -0
  124. package/dist/cli/deploy.js +413 -0
  125. package/dist/cli/deploy.js.map +1 -0
  126. package/dist/cli/dev-runner.d.ts +112 -0
  127. package/dist/cli/dev-runner.d.ts.map +1 -0
  128. package/dist/cli/dev-runner.js +227 -0
  129. package/dist/cli/dev-runner.js.map +1 -0
  130. package/dist/cli/discover.d.ts +47 -0
  131. package/dist/cli/discover.d.ts.map +1 -0
  132. package/dist/cli/discover.js +77 -0
  133. package/dist/cli/discover.js.map +1 -0
  134. package/dist/cli/firebase-json.d.ts +66 -0
  135. package/dist/cli/firebase-json.d.ts.map +1 -0
  136. package/dist/cli/firebase-json.js +60 -0
  137. package/dist/cli/firebase-json.js.map +1 -0
  138. package/dist/cli/index.d.ts +55 -0
  139. package/dist/cli/index.d.ts.map +1 -0
  140. package/dist/cli/index.js +500 -0
  141. package/dist/cli/index.js.map +1 -0
  142. package/dist/cli/init-templates.d.ts +37 -0
  143. package/dist/cli/init-templates.d.ts.map +1 -0
  144. package/dist/cli/init-templates.js +638 -0
  145. package/dist/cli/init-templates.js.map +1 -0
  146. package/dist/cli/init.d.ts +124 -0
  147. package/dist/cli/init.d.ts.map +1 -0
  148. package/dist/cli/init.js +423 -0
  149. package/dist/cli/init.js.map +1 -0
  150. package/dist/cli/login.d.ts +18 -0
  151. package/dist/cli/login.d.ts.map +1 -0
  152. package/dist/cli/login.js +59 -0
  153. package/dist/cli/login.js.map +1 -0
  154. package/dist/cli/mcp-proxy.d.ts +10 -0
  155. package/dist/cli/mcp-proxy.d.ts.map +1 -0
  156. package/dist/cli/mcp-proxy.js +126 -0
  157. package/dist/cli/mcp-proxy.js.map +1 -0
  158. package/dist/cli/parse-args.d.ts +20 -0
  159. package/dist/cli/parse-args.d.ts.map +1 -0
  160. package/dist/cli/parse-args.js +69 -0
  161. package/dist/cli/parse-args.js.map +1 -0
  162. package/dist/cli/rules.d.ts +40 -0
  163. package/dist/cli/rules.d.ts.map +1 -0
  164. package/dist/cli/rules.js +162 -0
  165. package/dist/cli/rules.js.map +1 -0
  166. package/dist/cli/scope.d.ts +38 -0
  167. package/dist/cli/scope.d.ts.map +1 -0
  168. package/dist/cli/scope.js +63 -0
  169. package/dist/cli/scope.js.map +1 -0
  170. package/dist/cli/serve.d.ts +122 -0
  171. package/dist/cli/serve.d.ts.map +1 -0
  172. package/dist/cli/serve.js +685 -0
  173. package/dist/cli/serve.js.map +1 -0
  174. package/dist/cli/snapshot.d.ts +22 -0
  175. package/dist/cli/snapshot.d.ts.map +1 -0
  176. package/dist/cli/snapshot.js +139 -0
  177. package/dist/cli/snapshot.js.map +1 -0
  178. package/dist/cli/verify.d.ts +23 -0
  179. package/dist/cli/verify.d.ts.map +1 -0
  180. package/dist/cli/verify.js +349 -0
  181. package/dist/cli/verify.js.map +1 -0
  182. package/dist/credentials/core/authorize.d.ts +22 -0
  183. package/dist/credentials/core/authorize.d.ts.map +1 -0
  184. package/dist/credentials/core/authorize.js +39 -0
  185. package/dist/credentials/core/authorize.js.map +1 -0
  186. package/dist/credentials/core/client.d.ts +14 -0
  187. package/dist/credentials/core/client.d.ts.map +1 -0
  188. package/dist/credentials/core/client.js +13 -0
  189. package/dist/credentials/core/client.js.map +1 -0
  190. package/dist/credentials/core/exchange.d.ts +24 -0
  191. package/dist/credentials/core/exchange.d.ts.map +1 -0
  192. package/dist/credentials/core/exchange.js +46 -0
  193. package/dist/credentials/core/exchange.js.map +1 -0
  194. package/dist/credentials/core/flow.d.ts +15 -0
  195. package/dist/credentials/core/flow.d.ts.map +1 -0
  196. package/dist/credentials/core/flow.js +40 -0
  197. package/dist/credentials/core/flow.js.map +1 -0
  198. package/dist/credentials/core/from-user-credential.d.ts +3 -0
  199. package/dist/credentials/core/from-user-credential.d.ts.map +1 -0
  200. package/dist/credentials/core/from-user-credential.js +19 -0
  201. package/dist/credentials/core/from-user-credential.js.map +1 -0
  202. package/dist/credentials/core/scopes.d.ts +26 -0
  203. package/dist/credentials/core/scopes.d.ts.map +1 -0
  204. package/dist/credentials/core/scopes.js +32 -0
  205. package/dist/credentials/core/scopes.js.map +1 -0
  206. package/dist/credentials/core/types.d.ts +72 -0
  207. package/dist/credentials/core/types.d.ts.map +1 -0
  208. package/dist/credentials/core/types.js +2 -0
  209. package/dist/credentials/core/types.js.map +1 -0
  210. package/dist/credentials/index.d.ts +14 -0
  211. package/dist/credentials/index.d.ts.map +1 -0
  212. package/dist/credentials/index.js +13 -0
  213. package/dist/credentials/index.js.map +1 -0
  214. package/dist/credentials/node/ensure-scope.d.ts +29 -0
  215. package/dist/credentials/node/ensure-scope.d.ts.map +1 -0
  216. package/dist/credentials/node/ensure-scope.js +42 -0
  217. package/dist/credentials/node/ensure-scope.js.map +1 -0
  218. package/dist/credentials/node/file-store.d.ts +4 -0
  219. package/dist/credentials/node/file-store.d.ts.map +1 -0
  220. package/dist/credentials/node/file-store.js +52 -0
  221. package/dist/credentials/node/file-store.js.map +1 -0
  222. package/dist/credentials/node/from-adc.d.ts +10 -0
  223. package/dist/credentials/node/from-adc.d.ts.map +1 -0
  224. package/dist/credentials/node/from-adc.js +60 -0
  225. package/dist/credentials/node/from-adc.js.map +1 -0
  226. package/dist/credentials/node/index.d.ts +10 -0
  227. package/dist/credentials/node/index.d.ts.map +1 -0
  228. package/dist/credentials/node/index.js +10 -0
  229. package/dist/credentials/node/index.js.map +1 -0
  230. package/dist/credentials/node/loopback-authorizer.d.ts +12 -0
  231. package/dist/credentials/node/loopback-authorizer.d.ts.map +1 -0
  232. package/dist/credentials/node/loopback-authorizer.js +61 -0
  233. package/dist/credentials/node/loopback-authorizer.js.map +1 -0
  234. package/dist/credentials/node/resolve-local-token.d.ts +20 -0
  235. package/dist/credentials/node/resolve-local-token.d.ts.map +1 -0
  236. package/dist/credentials/node/resolve-local-token.js +97 -0
  237. package/dist/credentials/node/resolve-local-token.js.map +1 -0
  238. package/dist/credentials/server/bff.d.ts +44 -0
  239. package/dist/credentials/server/bff.d.ts.map +1 -0
  240. package/dist/credentials/server/bff.js +43 -0
  241. package/dist/credentials/server/bff.js.map +1 -0
  242. package/dist/deploy/api-enablement.d.ts +45 -0
  243. package/dist/deploy/api-enablement.d.ts.map +1 -0
  244. package/dist/deploy/api-enablement.js +125 -0
  245. package/dist/deploy/api-enablement.js.map +1 -0
  246. package/dist/deploy/firestore/databases.d.ts +41 -0
  247. package/dist/deploy/firestore/databases.d.ts.map +1 -0
  248. package/dist/deploy/firestore/databases.js +63 -0
  249. package/dist/deploy/firestore/databases.js.map +1 -0
  250. package/dist/deploy/firestore/indexes.d.ts +120 -0
  251. package/dist/deploy/firestore/indexes.d.ts.map +1 -0
  252. package/dist/deploy/firestore/indexes.js +243 -0
  253. package/dist/deploy/firestore/indexes.js.map +1 -0
  254. package/dist/deploy/firestore/recipes.d.ts +21 -0
  255. package/dist/deploy/firestore/recipes.d.ts.map +1 -0
  256. package/dist/deploy/firestore/recipes.js +34 -0
  257. package/dist/deploy/firestore/recipes.js.map +1 -0
  258. package/dist/deploy/firestore/rules.d.ts +76 -0
  259. package/dist/deploy/firestore/rules.d.ts.map +1 -0
  260. package/dist/deploy/firestore/rules.js +180 -0
  261. package/dist/deploy/firestore/rules.js.map +1 -0
  262. package/dist/deploy/from-admin-app.d.ts +20 -0
  263. package/dist/deploy/from-admin-app.d.ts.map +1 -0
  264. package/dist/deploy/from-admin-app.js +18 -0
  265. package/dist/deploy/from-admin-app.js.map +1 -0
  266. package/dist/deploy/from-service-account.d.ts +29 -0
  267. package/dist/deploy/from-service-account.d.ts.map +1 -0
  268. package/dist/deploy/from-service-account.js +115 -0
  269. package/dist/deploy/from-service-account.js.map +1 -0
  270. package/dist/deploy/functions/bundle.d.ts +37 -0
  271. package/dist/deploy/functions/bundle.d.ts.map +1 -0
  272. package/dist/deploy/functions/bundle.js +181 -0
  273. package/dist/deploy/functions/bundle.js.map +1 -0
  274. package/dist/deploy/functions/core.d.ts +18 -0
  275. package/dist/deploy/functions/core.d.ts.map +1 -0
  276. package/dist/deploy/functions/core.js +211 -0
  277. package/dist/deploy/functions/core.js.map +1 -0
  278. package/dist/deploy/functions/iam.d.ts +17 -0
  279. package/dist/deploy/functions/iam.d.ts.map +1 -0
  280. package/dist/deploy/functions/iam.js +40 -0
  281. package/dist/deploy/functions/iam.js.map +1 -0
  282. package/dist/deploy/functions/operation.d.ts +39 -0
  283. package/dist/deploy/functions/operation.d.ts.map +1 -0
  284. package/dist/deploy/functions/operation.js +37 -0
  285. package/dist/deploy/functions/operation.js.map +1 -0
  286. package/dist/deploy/functions/spec.d.ts +69 -0
  287. package/dist/deploy/functions/spec.d.ts.map +1 -0
  288. package/dist/deploy/functions/spec.js +7 -0
  289. package/dist/deploy/functions/spec.js.map +1 -0
  290. package/dist/deploy/functions/types.d.ts +50 -0
  291. package/dist/deploy/functions/types.d.ts.map +1 -0
  292. package/dist/deploy/functions/types.js +3 -0
  293. package/dist/deploy/functions/types.js.map +1 -0
  294. package/dist/deploy/hosting/channels.d.ts +93 -0
  295. package/dist/deploy/hosting/channels.d.ts.map +1 -0
  296. package/dist/deploy/hosting/channels.js +134 -0
  297. package/dist/deploy/hosting/channels.js.map +1 -0
  298. package/dist/deploy/hosting/config.d.ts +28 -0
  299. package/dist/deploy/hosting/config.d.ts.map +1 -0
  300. package/dist/deploy/hosting/config.js +261 -0
  301. package/dist/deploy/hosting/config.js.map +1 -0
  302. package/dist/deploy/hosting/core.d.ts +38 -0
  303. package/dist/deploy/hosting/core.d.ts.map +1 -0
  304. package/dist/deploy/hosting/core.js +311 -0
  305. package/dist/deploy/hosting/core.js.map +1 -0
  306. package/dist/deploy/hosting/gzip.d.ts +11 -0
  307. package/dist/deploy/hosting/gzip.d.ts.map +1 -0
  308. package/dist/deploy/hosting/gzip.js +20 -0
  309. package/dist/deploy/hosting/gzip.js.map +1 -0
  310. package/dist/deploy/hosting/hash.d.ts +6 -0
  311. package/dist/deploy/hosting/hash.d.ts.map +1 -0
  312. package/dist/deploy/hosting/hash.js +17 -0
  313. package/dist/deploy/hosting/hash.js.map +1 -0
  314. package/dist/deploy/hosting/sites.d.ts +77 -0
  315. package/dist/deploy/hosting/sites.d.ts.map +1 -0
  316. package/dist/deploy/hosting/sites.js +73 -0
  317. package/dist/deploy/hosting/sites.js.map +1 -0
  318. package/dist/deploy/hosting/spec.d.ts +140 -0
  319. package/dist/deploy/hosting/spec.d.ts.map +1 -0
  320. package/dist/deploy/hosting/spec.js +8 -0
  321. package/dist/deploy/hosting/spec.js.map +1 -0
  322. package/dist/deploy/hosting/types.d.ts +71 -0
  323. package/dist/deploy/hosting/types.d.ts.map +1 -0
  324. package/dist/deploy/hosting/types.js +3 -0
  325. package/dist/deploy/hosting/types.js.map +1 -0
  326. package/dist/deploy/hosting/walk.d.ts +31 -0
  327. package/dist/deploy/hosting/walk.d.ts.map +1 -0
  328. package/dist/deploy/hosting/walk.js +167 -0
  329. package/dist/deploy/hosting/walk.js.map +1 -0
  330. package/dist/deploy/index.d.ts +51 -0
  331. package/dist/deploy/index.d.ts.map +1 -0
  332. package/dist/deploy/index.js +50 -0
  333. package/dist/deploy/index.js.map +1 -0
  334. package/dist/deploy/memoize-ttl.d.ts +58 -0
  335. package/dist/deploy/memoize-ttl.d.ts.map +1 -0
  336. package/dist/deploy/memoize-ttl.js +88 -0
  337. package/dist/deploy/memoize-ttl.js.map +1 -0
  338. package/dist/deploy/namespaces.d.ts +119 -0
  339. package/dist/deploy/namespaces.d.ts.map +1 -0
  340. package/dist/deploy/namespaces.js +192 -0
  341. package/dist/deploy/namespaces.js.map +1 -0
  342. package/dist/deploy/preflight.d.ts +119 -0
  343. package/dist/deploy/preflight.d.ts.map +1 -0
  344. package/dist/deploy/preflight.js +440 -0
  345. package/dist/deploy/preflight.js.map +1 -0
  346. package/dist/deploy/provider.d.ts +130 -0
  347. package/dist/deploy/provider.d.ts.map +1 -0
  348. package/dist/deploy/provider.js +21 -0
  349. package/dist/deploy/provider.js.map +1 -0
  350. package/dist/deploy/providers/database.d.ts +8 -0
  351. package/dist/deploy/providers/database.d.ts.map +1 -0
  352. package/dist/deploy/providers/database.js +37 -0
  353. package/dist/deploy/providers/database.js.map +1 -0
  354. package/dist/deploy/providers/firestore.d.ts +11 -0
  355. package/dist/deploy/providers/firestore.d.ts.map +1 -0
  356. package/dist/deploy/providers/firestore.js +43 -0
  357. package/dist/deploy/providers/firestore.js.map +1 -0
  358. package/dist/deploy/providers/functions.d.ts +9 -0
  359. package/dist/deploy/providers/functions.d.ts.map +1 -0
  360. package/dist/deploy/providers/functions.js +49 -0
  361. package/dist/deploy/providers/functions.js.map +1 -0
  362. package/dist/deploy/providers/hosting.d.ts +13 -0
  363. package/dist/deploy/providers/hosting.d.ts.map +1 -0
  364. package/dist/deploy/providers/hosting.js +149 -0
  365. package/dist/deploy/providers/hosting.js.map +1 -0
  366. package/dist/deploy/providers/storage.d.ts +9 -0
  367. package/dist/deploy/providers/storage.d.ts.map +1 -0
  368. package/dist/deploy/providers/storage.js +46 -0
  369. package/dist/deploy/providers/storage.js.map +1 -0
  370. package/dist/deploy/registry.d.ts +17 -0
  371. package/dist/deploy/registry.d.ts.map +1 -0
  372. package/dist/deploy/registry.js +15 -0
  373. package/dist/deploy/registry.js.map +1 -0
  374. package/dist/deploy/rtdb/rules.d.ts +21 -0
  375. package/dist/deploy/rtdb/rules.d.ts.map +1 -0
  376. package/dist/deploy/rtdb/rules.js +104 -0
  377. package/dist/deploy/rtdb/rules.js.map +1 -0
  378. package/dist/deploy/scope.d.ts +81 -0
  379. package/dist/deploy/scope.d.ts.map +1 -0
  380. package/dist/deploy/scope.js +44 -0
  381. package/dist/deploy/scope.js.map +1 -0
  382. package/dist/deploy/tools.d.ts +55 -0
  383. package/dist/deploy/tools.d.ts.map +1 -0
  384. package/dist/deploy/tools.js +329 -0
  385. package/dist/deploy/tools.js.map +1 -0
  386. package/dist/deploy/with-resolved-scope.d.ts +31 -0
  387. package/dist/deploy/with-resolved-scope.d.ts.map +1 -0
  388. package/dist/deploy/with-resolved-scope.js +73 -0
  389. package/dist/deploy/with-resolved-scope.js.map +1 -0
  390. package/dist/discover/concurrency.d.ts +42 -0
  391. package/dist/discover/concurrency.d.ts.map +1 -0
  392. package/dist/discover/concurrency.js +113 -0
  393. package/dist/discover/concurrency.js.map +1 -0
  394. package/dist/discover/crawler-adapter.d.ts +49 -0
  395. package/dist/discover/crawler-adapter.d.ts.map +1 -0
  396. package/dist/discover/crawler-adapter.js +92 -0
  397. package/dist/discover/crawler-adapter.js.map +1 -0
  398. package/dist/discover/crawler.d.ts +333 -0
  399. package/dist/discover/crawler.d.ts.map +1 -0
  400. package/dist/discover/crawler.js +764 -0
  401. package/dist/discover/crawler.js.map +1 -0
  402. package/dist/discover/findCollectionGroup.d.ts +80 -0
  403. package/dist/discover/findCollectionGroup.d.ts.map +1 -0
  404. package/dist/discover/findCollectionGroup.js +81 -0
  405. package/dist/discover/findCollectionGroup.js.map +1 -0
  406. package/dist/discover/index.d.ts +15 -0
  407. package/dist/discover/index.d.ts.map +1 -0
  408. package/dist/discover/index.js +24 -0
  409. package/dist/discover/index.js.map +1 -0
  410. package/dist/discover/merge.d.ts +108 -0
  411. package/dist/discover/merge.d.ts.map +1 -0
  412. package/dist/discover/merge.js +394 -0
  413. package/dist/discover/merge.js.map +1 -0
  414. package/dist/discover/rest-crawler-firestore.d.ts +51 -0
  415. package/dist/discover/rest-crawler-firestore.d.ts.map +1 -0
  416. package/dist/discover/rest-crawler-firestore.js +154 -0
  417. package/dist/discover/rest-crawler-firestore.js.map +1 -0
  418. package/dist/discover/session.d.ts +116 -0
  419. package/dist/discover/session.d.ts.map +1 -0
  420. package/dist/discover/session.js +331 -0
  421. package/dist/discover/session.js.map +1 -0
  422. package/dist/discover/tools.d.ts +59 -0
  423. package/dist/discover/tools.d.ts.map +1 -0
  424. package/dist/discover/tools.js +115 -0
  425. package/dist/discover/tools.js.map +1 -0
  426. package/dist/discover/types.d.ts +191 -0
  427. package/dist/discover/types.d.ts.map +1 -0
  428. package/dist/discover/types.js +12 -0
  429. package/dist/discover/types.js.map +1 -0
  430. package/dist/discover/wire.d.ts +81 -0
  431. package/dist/discover/wire.d.ts.map +1 -0
  432. package/dist/discover/wire.js +308 -0
  433. package/dist/discover/wire.js.map +1 -0
  434. package/dist/pkg-version.d.ts +2 -0
  435. package/dist/pkg-version.d.ts.map +1 -0
  436. package/dist/pkg-version.js +47 -0
  437. package/dist/pkg-version.js.map +1 -0
  438. package/dist/register/esm-exports.d.ts +26 -0
  439. package/dist/register/esm-exports.d.ts.map +1 -0
  440. package/dist/register/esm-exports.js +61 -0
  441. package/dist/register/esm-exports.js.map +1 -0
  442. package/dist/register/exempt.d.ts +11 -0
  443. package/dist/register/exempt.d.ts.map +1 -0
  444. package/dist/register/exempt.js +74 -0
  445. package/dist/register/exempt.js.map +1 -0
  446. package/dist/register/hooks.d.ts +13 -0
  447. package/dist/register/hooks.d.ts.map +1 -0
  448. package/dist/register/hooks.js +15 -0
  449. package/dist/register/hooks.js.map +1 -0
  450. package/dist/register/index.d.ts +5 -0
  451. package/dist/register/index.d.ts.map +1 -0
  452. package/dist/register/index.js +126 -0
  453. package/dist/register/index.js.map +1 -0
  454. package/dist/register/mapping.d.ts +17 -0
  455. package/dist/register/mapping.d.ts.map +1 -0
  456. package/dist/register/mapping.js +31 -0
  457. package/dist/register/mapping.js.map +1 -0
  458. package/dist/registry/admin-deps.d.ts +17 -0
  459. package/dist/registry/admin-deps.d.ts.map +1 -0
  460. package/dist/registry/admin-deps.js +68 -0
  461. package/dist/registry/admin-deps.js.map +1 -0
  462. package/dist/registry/compose.d.ts +68 -0
  463. package/dist/registry/compose.d.ts.map +1 -0
  464. package/dist/registry/compose.js +113 -0
  465. package/dist/registry/compose.js.map +1 -0
  466. package/dist/registry/index.d.ts +11 -0
  467. package/dist/registry/index.d.ts.map +1 -0
  468. package/dist/registry/index.js +11 -0
  469. package/dist/registry/index.js.map +1 -0
  470. package/dist/remote/index.d.ts +232 -0
  471. package/dist/remote/index.d.ts.map +1 -0
  472. package/dist/remote/index.js +719 -0
  473. package/dist/remote/index.js.map +1 -0
  474. package/dist/rtdb/rules-json.d.ts +8 -0
  475. package/dist/rtdb/rules-json.d.ts.map +1 -0
  476. package/dist/rtdb/rules-json.js +17 -0
  477. package/dist/rtdb/rules-json.js.map +1 -0
  478. package/dist/serve/bridge-mount.d.ts +38 -0
  479. package/dist/serve/bridge-mount.d.ts.map +1 -0
  480. package/dist/serve/bridge-mount.js +162 -0
  481. package/dist/serve/bridge-mount.js.map +1 -0
  482. package/dist/serve/bundler.d.ts +134 -0
  483. package/dist/serve/bundler.d.ts.map +1 -0
  484. package/dist/serve/bundler.js +452 -0
  485. package/dist/serve/bundler.js.map +1 -0
  486. package/dist/serve/capture-store.d.ts +27 -0
  487. package/dist/serve/capture-store.d.ts.map +1 -0
  488. package/dist/serve/capture-store.js +47 -0
  489. package/dist/serve/capture-store.js.map +1 -0
  490. package/dist/serve/discovery.d.ts +71 -0
  491. package/dist/serve/discovery.d.ts.map +1 -0
  492. package/dist/serve/discovery.js +158 -0
  493. package/dist/serve/discovery.js.map +1 -0
  494. package/dist/serve/docs-ui/_astro/_slug_.BsN3bPok.css +1 -0
  495. package/dist/serve/docs-ui/docs/_rhythm/index.html +99 -0
  496. package/dist/serve/docs-ui/docs/_rhythm.md +101 -0
  497. package/dist/serve/docs-ui/docs/index.html +1 -0
  498. package/dist/serve/docs-ui/docs/index.json +9914 -0
  499. package/dist/serve/docs-ui/docs/pyric-admin-firestore/index.html +38 -0
  500. package/dist/serve/docs-ui/docs/pyric-admin-firestore-explanation-error-translation/index.html +57 -0
  501. package/dist/serve/docs-ui/docs/pyric-admin-firestore-explanation-error-translation.md +87 -0
  502. package/dist/serve/docs-ui/docs/pyric-admin-firestore-explanation-per-call-delegate/index.html +58 -0
  503. package/dist/serve/docs-ui/docs/pyric-admin-firestore-explanation-per-call-delegate.md +86 -0
  504. package/dist/serve/docs-ui/docs/pyric-admin-firestore-explanation-why-mirror-admin-shape/index.html +59 -0
  505. package/dist/serve/docs-ui/docs/pyric-admin-firestore-explanation-why-mirror-admin-shape.md +82 -0
  506. package/dist/serve/docs-ui/docs/pyric-admin-firestore-how-to-run-a-transaction/index.html +53 -0
  507. package/dist/serve/docs-ui/docs/pyric-admin-firestore-how-to-run-a-transaction.md +70 -0
  508. package/dist/serve/docs-ui/docs/pyric-admin-firestore-how-to-seed-and-set-rules/index.html +62 -0
  509. package/dist/serve/docs-ui/docs/pyric-admin-firestore-how-to-seed-and-set-rules.md +86 -0
  510. package/dist/serve/docs-ui/docs/pyric-admin-firestore-how-to-translate-denials/index.html +80 -0
  511. package/dist/serve/docs-ui/docs/pyric-admin-firestore-how-to-translate-denials.md +92 -0
  512. package/dist/serve/docs-ui/docs/pyric-admin-firestore-how-to-use-onsnapshot/index.html +57 -0
  513. package/dist/serve/docs-ui/docs/pyric-admin-firestore-how-to-use-onsnapshot.md +78 -0
  514. package/dist/serve/docs-ui/docs/pyric-admin-firestore-how-to-write-a-batch/index.html +50 -0
  515. package/dist/serve/docs-ui/docs/pyric-admin-firestore-how-to-write-a-batch.md +67 -0
  516. package/dist/serve/docs-ui/docs/pyric-admin-firestore-reference-api/index.html +53 -0
  517. package/dist/serve/docs-ui/docs/pyric-admin-firestore-reference-api.md +73 -0
  518. package/dist/serve/docs-ui/docs/pyric-admin-firestore-reference-onsnapshot/index.html +68 -0
  519. package/dist/serve/docs-ui/docs/pyric-admin-firestore-reference-onsnapshot.md +75 -0
  520. package/dist/serve/docs-ui/docs/pyric-admin-firestore-reference-re-exported-types/index.html +76 -0
  521. package/dist/serve/docs-ui/docs/pyric-admin-firestore-reference-re-exported-types.md +95 -0
  522. package/dist/serve/docs-ui/docs/pyric-admin-firestore-reference-sandbox-firestore/index.html +40 -0
  523. package/dist/serve/docs-ui/docs/pyric-admin-firestore-reference-sandbox-firestore.md +73 -0
  524. package/dist/serve/docs-ui/docs/pyric-admin-firestore-tutorials-01-first-admin-session/index.html +118 -0
  525. package/dist/serve/docs-ui/docs/pyric-admin-firestore-tutorials-01-first-admin-session.md +146 -0
  526. package/dist/serve/docs-ui/docs/pyric-admin-firestore.md +49 -0
  527. package/dist/serve/docs-ui/docs/pyric-auth/index.html +64 -0
  528. package/dist/serve/docs-ui/docs/pyric-auth-compat/index.html +874 -0
  529. package/dist/serve/docs-ui/docs/pyric-auth-compat.md +265 -0
  530. package/dist/serve/docs-ui/docs/pyric-auth-reference-api/index.html +112 -0
  531. package/dist/serve/docs-ui/docs/pyric-auth-reference-api.md +129 -0
  532. package/dist/serve/docs-ui/docs/pyric-auth-reference-feature-matrix/index.html +781 -0
  533. package/dist/serve/docs-ui/docs/pyric-auth-reference-feature-matrix.md +198 -0
  534. package/dist/serve/docs-ui/docs/pyric-auth-reference-sandbox-test-driver/index.html +80 -0
  535. package/dist/serve/docs-ui/docs/pyric-auth-reference-sandbox-test-driver.md +107 -0
  536. package/dist/serve/docs-ui/docs/pyric-auth.md +84 -0
  537. package/dist/serve/docs-ui/docs/pyric-database/index.html +36 -0
  538. package/dist/serve/docs-ui/docs/pyric-database-compat/index.html +2428 -0
  539. package/dist/serve/docs-ui/docs/pyric-database-compat.md +850 -0
  540. package/dist/serve/docs-ui/docs/pyric-database-explanation-rules-authoring-and-deploy-are-separate/index.html +23 -0
  541. package/dist/serve/docs-ui/docs/pyric-database-explanation-rules-authoring-and-deploy-are-separate.md +29 -0
  542. package/dist/serve/docs-ui/docs/pyric-database-reference-rules-tooling/index.html +263 -0
  543. package/dist/serve/docs-ui/docs/pyric-database-reference-rules-tooling.md +288 -0
  544. package/dist/serve/docs-ui/docs/pyric-database-tutorials-01-author-rtdb-rules-with-constraints/index.html +116 -0
  545. package/dist/serve/docs-ui/docs/pyric-database-tutorials-01-author-rtdb-rules-with-constraints.md +142 -0
  546. package/dist/serve/docs-ui/docs/pyric-database.md +18 -0
  547. package/dist/serve/docs-ui/docs/pyric-firestore/index.html +54 -0
  548. package/dist/serve/docs-ui/docs/pyric-firestore-compat/index.html +1345 -0
  549. package/dist/serve/docs-ui/docs/pyric-firestore-compat.md +366 -0
  550. package/dist/serve/docs-ui/docs/pyric-firestore-explanation-rules-tooling-is-separate/index.html +79 -0
  551. package/dist/serve/docs-ui/docs/pyric-firestore-explanation-rules-tooling-is-separate.md +62 -0
  552. package/dist/serve/docs-ui/docs/pyric-firestore-explanation-target-symbol-opacity/index.html +59 -0
  553. package/dist/serve/docs-ui/docs/pyric-firestore-explanation-target-symbol-opacity.md +85 -0
  554. package/dist/serve/docs-ui/docs/pyric-firestore-explanation-two-backends-one-surface/index.html +61 -0
  555. package/dist/serve/docs-ui/docs/pyric-firestore-explanation-two-backends-one-surface.md +87 -0
  556. package/dist/serve/docs-ui/docs/pyric-firestore-how-to-build-queries/index.html +105 -0
  557. package/dist/serve/docs-ui/docs/pyric-firestore-how-to-build-queries.md +138 -0
  558. package/dist/serve/docs-ui/docs/pyric-firestore-how-to-migrate-from-firebase-firestore/index.html +65 -0
  559. package/dist/serve/docs-ui/docs/pyric-firestore-how-to-migrate-from-firebase-firestore.md +93 -0
  560. package/dist/serve/docs-ui/docs/pyric-firestore-how-to-pick-a-backend/index.html +112 -0
  561. package/dist/serve/docs-ui/docs/pyric-firestore-how-to-pick-a-backend.md +100 -0
  562. package/dist/serve/docs-ui/docs/pyric-firestore-how-to-run-a-transaction/index.html +42 -0
  563. package/dist/serve/docs-ui/docs/pyric-firestore-how-to-run-a-transaction.md +55 -0
  564. package/dist/serve/docs-ui/docs/pyric-firestore-how-to-use-onsnapshot/index.html +61 -0
  565. package/dist/serve/docs-ui/docs/pyric-firestore-how-to-use-onsnapshot.md +83 -0
  566. package/dist/serve/docs-ui/docs/pyric-firestore-how-to-use-sandbox-ops/index.html +52 -0
  567. package/dist/serve/docs-ui/docs/pyric-firestore-how-to-use-sandbox-ops.md +73 -0
  568. package/dist/serve/docs-ui/docs/pyric-firestore-reference-api/index.html +132 -0
  569. package/dist/serve/docs-ui/docs/pyric-firestore-reference-api.md +160 -0
  570. package/dist/serve/docs-ui/docs/pyric-firestore-reference-feature-matrix/index.html +793 -0
  571. package/dist/serve/docs-ui/docs/pyric-firestore-reference-feature-matrix.md +239 -0
  572. package/dist/serve/docs-ui/docs/pyric-firestore-reference-getfirestore/index.html +55 -0
  573. package/dist/serve/docs-ui/docs/pyric-firestore-reference-getfirestore.md +76 -0
  574. package/dist/serve/docs-ui/docs/pyric-firestore-reference-query-constraints/index.html +78 -0
  575. package/dist/serve/docs-ui/docs/pyric-firestore-reference-query-constraints.md +115 -0
  576. package/dist/serve/docs-ui/docs/pyric-firestore-reference-sandbox-ops/index.html +53 -0
  577. package/dist/serve/docs-ui/docs/pyric-firestore-reference-sandbox-ops.md +76 -0
  578. package/dist/serve/docs-ui/docs/pyric-firestore-reference-tool-factories/index.html +51 -0
  579. package/dist/serve/docs-ui/docs/pyric-firestore-reference-tool-factories.md +65 -0
  580. package/dist/serve/docs-ui/docs/pyric-firestore-tutorials-01-write-a-sandbox-demo/index.html +127 -0
  581. package/dist/serve/docs-ui/docs/pyric-firestore-tutorials-01-write-a-sandbox-demo.md +157 -0
  582. package/dist/serve/docs-ui/docs/pyric-firestore-tutorials-02-swap-to-prod-backend/index.html +85 -0
  583. package/dist/serve/docs-ui/docs/pyric-firestore-tutorials-02-swap-to-prod-backend.md +122 -0
  584. package/dist/serve/docs-ui/docs/pyric-firestore.md +69 -0
  585. package/dist/serve/docs-ui/docs/pyric-rules/index.html +93 -0
  586. package/dist/serve/docs-ui/docs/pyric-rules-explanation-agent-failure-modes/index.html +50 -0
  587. package/dist/serve/docs-ui/docs/pyric-rules-explanation-agent-failure-modes.md +74 -0
  588. package/dist/serve/docs-ui/docs/pyric-rules-explanation-lint-vs-validate-vs-simulate-vs-test/index.html +79 -0
  589. package/dist/serve/docs-ui/docs/pyric-rules-explanation-lint-vs-validate-vs-simulate-vs-test.md +69 -0
  590. package/dist/serve/docs-ui/docs/pyric-rules-explanation-runtime-budget-and-shared-gates/index.html +67 -0
  591. package/dist/serve/docs-ui/docs/pyric-rules-explanation-runtime-budget-and-shared-gates.md +71 -0
  592. package/dist/serve/docs-ui/docs/pyric-rules-explanation-sentinel-expression-engine/index.html +49 -0
  593. package/dist/serve/docs-ui/docs/pyric-rules-explanation-sentinel-expression-engine.md +70 -0
  594. package/dist/serve/docs-ui/docs/pyric-rules-explanation-simulator-vs-rules-test-api/index.html +96 -0
  595. package/dist/serve/docs-ui/docs/pyric-rules-explanation-simulator-vs-rules-test-api.md +76 -0
  596. package/dist/serve/docs-ui/docs/pyric-rules-explanation-the-2-plus-modules-extension/index.html +58 -0
  597. package/dist/serve/docs-ui/docs/pyric-rules-explanation-the-2-plus-modules-extension.md +75 -0
  598. package/dist/serve/docs-ui/docs/pyric-rules-explanation-value-wrappers-design/index.html +62 -0
  599. package/dist/serve/docs-ui/docs/pyric-rules-explanation-value-wrappers-design.md +80 -0
  600. package/dist/serve/docs-ui/docs/pyric-rules-explanation-why-this-package-exists/index.html +34 -0
  601. package/dist/serve/docs-ui/docs/pyric-rules-explanation-why-this-package-exists.md +43 -0
  602. package/dist/serve/docs-ui/docs/pyric-rules-how-to-compare-rulesets-for-weakening/index.html +54 -0
  603. package/dist/serve/docs-ui/docs/pyric-rules-how-to-compare-rulesets-for-weakening.md +69 -0
  604. package/dist/serve/docs-ui/docs/pyric-rules-how-to-inspect-rules-via-the-ast/index.html +89 -0
  605. package/dist/serve/docs-ui/docs/pyric-rules-how-to-inspect-rules-via-the-ast.md +113 -0
  606. package/dist/serve/docs-ui/docs/pyric-rules-how-to-lint-rules-source/index.html +50 -0
  607. package/dist/serve/docs-ui/docs/pyric-rules-how-to-lint-rules-source.md +73 -0
  608. package/dist/serve/docs-ui/docs/pyric-rules-how-to-pin-request-time/index.html +47 -0
  609. package/dist/serve/docs-ui/docs/pyric-rules-how-to-pin-request-time.md +58 -0
  610. package/dist/serve/docs-ui/docs/pyric-rules-how-to-register-tools-with-an-agent/index.html +61 -0
  611. package/dist/serve/docs-ui/docs/pyric-rules-how-to-register-tools-with-an-agent.md +78 -0
  612. package/dist/serve/docs-ui/docs/pyric-rules-how-to-resolve-module-imports/index.html +92 -0
  613. package/dist/serve/docs-ui/docs/pyric-rules-how-to-resolve-module-imports.md +93 -0
  614. package/dist/serve/docs-ui/docs/pyric-rules-how-to-simulate-rules-locally/index.html +89 -0
  615. package/dist/serve/docs-ui/docs/pyric-rules-how-to-simulate-rules-locally.md +117 -0
  616. package/dist/serve/docs-ui/docs/pyric-rules-how-to-test-rules-against-firebase/index.html +70 -0
  617. package/dist/serve/docs-ui/docs/pyric-rules-how-to-test-rules-against-firebase.md +93 -0
  618. package/dist/serve/docs-ui/docs/pyric-rules-reference-api/index.html +129 -0
  619. package/dist/serve/docs-ui/docs/pyric-rules-reference-api.md +176 -0
  620. package/dist/serve/docs-ui/docs/pyric-rules-reference-ast/index.html +85 -0
  621. package/dist/serve/docs-ui/docs/pyric-rules-reference-ast.md +105 -0
  622. package/dist/serve/docs-ui/docs/pyric-rules-reference-errors/index.html +63 -0
  623. package/dist/serve/docs-ui/docs/pyric-rules-reference-errors.md +89 -0
  624. package/dist/serve/docs-ui/docs/pyric-rules-reference-lint-rules/index.html +148 -0
  625. package/dist/serve/docs-ui/docs/pyric-rules-reference-lint-rules.md +159 -0
  626. package/dist/serve/docs-ui/docs/pyric-rules-reference-simulator-context/index.html +66 -0
  627. package/dist/serve/docs-ui/docs/pyric-rules-reference-simulator-context.md +98 -0
  628. package/dist/serve/docs-ui/docs/pyric-rules-reference-stdlib-modules/index.html +239 -0
  629. package/dist/serve/docs-ui/docs/pyric-rules-reference-stdlib-modules.md +135 -0
  630. package/dist/serve/docs-ui/docs/pyric-rules-reference-test-case-schema/index.html +93 -0
  631. package/dist/serve/docs-ui/docs/pyric-rules-reference-test-case-schema.md +109 -0
  632. package/dist/serve/docs-ui/docs/pyric-rules-reference-validator-findings/index.html +86 -0
  633. package/dist/serve/docs-ui/docs/pyric-rules-reference-validator-findings.md +113 -0
  634. package/dist/serve/docs-ui/docs/pyric-rules-reference-value-wrappers/index.html +101 -0
  635. package/dist/serve/docs-ui/docs/pyric-rules-reference-value-wrappers.md +134 -0
  636. package/dist/serve/docs-ui/docs/pyric-rules-tutorials-01-lint-your-first-rules-file/index.html +107 -0
  637. package/dist/serve/docs-ui/docs/pyric-rules-tutorials-01-lint-your-first-rules-file.md +142 -0
  638. package/dist/serve/docs-ui/docs/pyric-rules-tutorials-02-write-a-test-suite-for-your-rules/index.html +151 -0
  639. package/dist/serve/docs-ui/docs/pyric-rules-tutorials-02-write-a-test-suite-for-your-rules.md +194 -0
  640. package/dist/serve/docs-ui/docs/pyric-rules.md +88 -0
  641. package/dist/serve/docs-ui/docs/pyric-sandbox/index.html +67 -0
  642. package/dist/serve/docs-ui/docs/pyric-sandbox-explanation-every-op-is-a-request/index.html +141 -0
  643. package/dist/serve/docs-ui/docs/pyric-sandbox-explanation-every-op-is-a-request.md +157 -0
  644. package/dist/serve/docs-ui/docs/pyric-sandbox-explanation-identity-is-a-context/index.html +66 -0
  645. package/dist/serve/docs-ui/docs/pyric-sandbox-explanation-identity-is-a-context.md +98 -0
  646. package/dist/serve/docs-ui/docs/pyric-sandbox-explanation-internal-adapter-protocol/index.html +71 -0
  647. package/dist/serve/docs-ui/docs/pyric-sandbox-explanation-internal-adapter-protocol.md +105 -0
  648. package/dist/serve/docs-ui/docs/pyric-sandbox-explanation-listener-re-evaluation/index.html +56 -0
  649. package/dist/serve/docs-ui/docs/pyric-sandbox-explanation-listener-re-evaluation.md +80 -0
  650. package/dist/serve/docs-ui/docs/pyric-sandbox-explanation-local-backend-vs-firestore-offline/index.html +64 -0
  651. package/dist/serve/docs-ui/docs/pyric-sandbox-explanation-local-backend-vs-firestore-offline.md +56 -0
  652. package/dist/serve/docs-ui/docs/pyric-sandbox-explanation-why-adapters-are-siblings/index.html +88 -0
  653. package/dist/serve/docs-ui/docs/pyric-sandbox-explanation-why-adapters-are-siblings.md +87 -0
  654. package/dist/serve/docs-ui/docs/pyric-sandbox-explanation-why-this-package-exists/index.html +69 -0
  655. package/dist/serve/docs-ui/docs/pyric-sandbox-explanation-why-this-package-exists.md +57 -0
  656. package/dist/serve/docs-ui/docs/pyric-sandbox-how-to-multiple-isolated-sandboxes/index.html +63 -0
  657. package/dist/serve/docs-ui/docs/pyric-sandbox-how-to-multiple-isolated-sandboxes.md +83 -0
  658. package/dist/serve/docs-ui/docs/pyric-sandbox-how-to-observe-events/index.html +159 -0
  659. package/dist/serve/docs-ui/docs/pyric-sandbox-how-to-observe-events.md +154 -0
  660. package/dist/serve/docs-ui/docs/pyric-sandbox-how-to-pick-an-adapter/index.html +79 -0
  661. package/dist/serve/docs-ui/docs/pyric-sandbox-how-to-pick-an-adapter.md +69 -0
  662. package/dist/serve/docs-ui/docs/pyric-sandbox-how-to-replay-events/index.html +89 -0
  663. package/dist/serve/docs-ui/docs/pyric-sandbox-how-to-replay-events.md +87 -0
  664. package/dist/serve/docs-ui/docs/pyric-sandbox-how-to-reset-between-tests/index.html +68 -0
  665. package/dist/serve/docs-ui/docs/pyric-sandbox-how-to-reset-between-tests.md +86 -0
  666. package/dist/serve/docs-ui/docs/pyric-sandbox-how-to-seed-data-and-rules/index.html +91 -0
  667. package/dist/serve/docs-ui/docs/pyric-sandbox-how-to-seed-data-and-rules.md +112 -0
  668. package/dist/serve/docs-ui/docs/pyric-sandbox-how-to-switch-users/index.html +50 -0
  669. package/dist/serve/docs-ui/docs/pyric-sandbox-how-to-switch-users.md +70 -0
  670. package/dist/serve/docs-ui/docs/pyric-sandbox-how-to-use-admin-reads/index.html +54 -0
  671. package/dist/serve/docs-ui/docs/pyric-sandbox-how-to-use-admin-reads.md +73 -0
  672. package/dist/serve/docs-ui/docs/pyric-sandbox-reference-api/index.html +77 -0
  673. package/dist/serve/docs-ui/docs/pyric-sandbox-reference-api.md +117 -0
  674. package/dist/serve/docs-ui/docs/pyric-sandbox-reference-divergences/index.html +125 -0
  675. package/dist/serve/docs-ui/docs/pyric-sandbox-reference-divergences.md +137 -0
  676. package/dist/serve/docs-ui/docs/pyric-sandbox-reference-error-codes/index.html +110 -0
  677. package/dist/serve/docs-ui/docs/pyric-sandbox-reference-error-codes.md +114 -0
  678. package/dist/serve/docs-ui/docs/pyric-sandbox-reference-internal-protocol/index.html +72 -0
  679. package/dist/serve/docs-ui/docs/pyric-sandbox-reference-internal-protocol.md +99 -0
  680. package/dist/serve/docs-ui/docs/pyric-sandbox-reference-sandbox-and-context/index.html +90 -0
  681. package/dist/serve/docs-ui/docs/pyric-sandbox-reference-sandbox-and-context.md +128 -0
  682. package/dist/serve/docs-ui/docs/pyric-sandbox-reference-sandbox-event/index.html +146 -0
  683. package/dist/serve/docs-ui/docs/pyric-sandbox-reference-sandbox-event.md +188 -0
  684. package/dist/serve/docs-ui/docs/pyric-sandbox-reference-snapshot-and-admin/index.html +53 -0
  685. package/dist/serve/docs-ui/docs/pyric-sandbox-reference-snapshot-and-admin.md +77 -0
  686. package/dist/serve/docs-ui/docs/pyric-sandbox-tutorials-01-your-first-sandbox-session/index.html +115 -0
  687. package/dist/serve/docs-ui/docs/pyric-sandbox-tutorials-01-your-first-sandbox-session.md +154 -0
  688. package/dist/serve/docs-ui/docs/pyric-sandbox-tutorials-02-use-the-sandbox-in-a-test-harness/index.html +191 -0
  689. package/dist/serve/docs-ui/docs/pyric-sandbox-tutorials-02-use-the-sandbox-in-a-test-harness.md +222 -0
  690. package/dist/serve/docs-ui/docs/pyric-sandbox-tutorials-03-build-a-traffic-monitor/index.html +163 -0
  691. package/dist/serve/docs-ui/docs/pyric-sandbox-tutorials-03-build-a-traffic-monitor.md +192 -0
  692. package/dist/serve/docs-ui/docs/pyric-sandbox.md +63 -0
  693. package/dist/serve/docs-ui/docs/pyric-storage/index.html +65 -0
  694. package/dist/serve/docs-ui/docs/pyric-storage-compat/index.html +1076 -0
  695. package/dist/serve/docs-ui/docs/pyric-storage-compat.md +337 -0
  696. package/dist/serve/docs-ui/docs/pyric-storage-explanation-implementation-scope/index.html +63 -0
  697. package/dist/serve/docs-ui/docs/pyric-storage-explanation-implementation-scope.md +104 -0
  698. package/dist/serve/docs-ui/docs/pyric-storage-explanation-session-archive-use-case/index.html +58 -0
  699. package/dist/serve/docs-ui/docs/pyric-storage-explanation-session-archive-use-case.md +80 -0
  700. package/dist/serve/docs-ui/docs/pyric-storage-explanation-why-indexeddb/index.html +50 -0
  701. package/dist/serve/docs-ui/docs/pyric-storage-explanation-why-indexeddb.md +82 -0
  702. package/dist/serve/docs-ui/docs/pyric-storage-how-to-enforce-rules/index.html +104 -0
  703. package/dist/serve/docs-ui/docs/pyric-storage-how-to-enforce-rules.md +111 -0
  704. package/dist/serve/docs-ui/docs/pyric-storage-how-to-list-and-delete/index.html +53 -0
  705. package/dist/serve/docs-ui/docs/pyric-storage-how-to-list-and-delete.md +76 -0
  706. package/dist/serve/docs-ui/docs/pyric-storage-how-to-round-trip-metadata/index.html +77 -0
  707. package/dist/serve/docs-ui/docs/pyric-storage-how-to-round-trip-metadata.md +100 -0
  708. package/dist/serve/docs-ui/docs/pyric-storage-how-to-switch-backends/index.html +65 -0
  709. package/dist/serve/docs-ui/docs/pyric-storage-how-to-switch-backends.md +82 -0
  710. package/dist/serve/docs-ui/docs/pyric-storage-how-to-test-rule-expressions/index.html +70 -0
  711. package/dist/serve/docs-ui/docs/pyric-storage-how-to-test-rule-expressions.md +82 -0
  712. package/dist/serve/docs-ui/docs/pyric-storage-reference-api/index.html +92 -0
  713. package/dist/serve/docs-ui/docs/pyric-storage-reference-api.md +152 -0
  714. package/dist/serve/docs-ui/docs/pyric-storage-reference-error-codes/index.html +46 -0
  715. package/dist/serve/docs-ui/docs/pyric-storage-reference-error-codes.md +66 -0
  716. package/dist/serve/docs-ui/docs/pyric-storage-reference-rules-subset/index.html +73 -0
  717. package/dist/serve/docs-ui/docs/pyric-storage-reference-rules-subset.md +90 -0
  718. package/dist/serve/docs-ui/docs/pyric-storage-reference-storage-options/index.html +45 -0
  719. package/dist/serve/docs-ui/docs/pyric-storage-reference-storage-options.md +65 -0
  720. package/dist/serve/docs-ui/docs/pyric-storage-tutorials-01-upload-and-download/index.html +114 -0
  721. package/dist/serve/docs-ui/docs/pyric-storage-tutorials-01-upload-and-download.md +141 -0
  722. package/dist/serve/docs-ui/docs/pyric-storage.md +63 -0
  723. package/dist/serve/docs-ui/docs/pyric-tools/index.html +45 -0
  724. package/dist/serve/docs-ui/docs/pyric-tools-bridge/index.html +71 -0
  725. package/dist/serve/docs-ui/docs/pyric-tools-bridge.md +90 -0
  726. package/dist/serve/docs-ui/docs/pyric-tools-deploy/index.html +59 -0
  727. package/dist/serve/docs-ui/docs/pyric-tools-deploy-explanation-primitives-vs-orchestrators/index.html +63 -0
  728. package/dist/serve/docs-ui/docs/pyric-tools-deploy-explanation-primitives-vs-orchestrators.md +83 -0
  729. package/dist/serve/docs-ui/docs/pyric-tools-deploy-explanation-token-caching/index.html +47 -0
  730. package/dist/serve/docs-ui/docs/pyric-tools-deploy-explanation-token-caching.md +70 -0
  731. package/dist/serve/docs-ui/docs/pyric-tools-deploy-explanation-why-no-firebase-cli/index.html +36 -0
  732. package/dist/serve/docs-ui/docs/pyric-tools-deploy-explanation-why-no-firebase-cli.md +50 -0
  733. package/dist/serve/docs-ui/docs/pyric-tools-deploy-explanation-why-this-package-exists/index.html +47 -0
  734. package/dist/serve/docs-ui/docs/pyric-tools-deploy-explanation-why-this-package-exists.md +60 -0
  735. package/dist/serve/docs-ui/docs/pyric-tools-deploy-how-to-build-projectscope-from-firebase-auth/index.html +55 -0
  736. package/dist/serve/docs-ui/docs/pyric-tools-deploy-how-to-build-projectscope-from-firebase-auth.md +68 -0
  737. package/dist/serve/docs-ui/docs/pyric-tools-deploy-how-to-build-projectscope-from-service-account/index.html +35 -0
  738. package/dist/serve/docs-ui/docs/pyric-tools-deploy-how-to-build-projectscope-from-service-account.md +52 -0
  739. package/dist/serve/docs-ui/docs/pyric-tools-deploy-how-to-bundle-and-deploy-a-function/index.html +81 -0
  740. package/dist/serve/docs-ui/docs/pyric-tools-deploy-how-to-bundle-and-deploy-a-function.md +98 -0
  741. package/dist/serve/docs-ui/docs/pyric-tools-deploy-how-to-deploy-firestore-indexes/index.html +71 -0
  742. package/dist/serve/docs-ui/docs/pyric-tools-deploy-how-to-deploy-firestore-indexes.md +91 -0
  743. package/dist/serve/docs-ui/docs/pyric-tools-deploy-how-to-deploy-firestore-rules/index.html +100 -0
  744. package/dist/serve/docs-ui/docs/pyric-tools-deploy-how-to-deploy-firestore-rules.md +105 -0
  745. package/dist/serve/docs-ui/docs/pyric-tools-deploy-how-to-deploy-hosting-rewrites/index.html +86 -0
  746. package/dist/serve/docs-ui/docs/pyric-tools-deploy-how-to-deploy-hosting-rewrites.md +92 -0
  747. package/dist/serve/docs-ui/docs/pyric-tools-deploy-how-to-deploy-realtime-database-rules/index.html +132 -0
  748. package/dist/serve/docs-ui/docs/pyric-tools-deploy-how-to-deploy-realtime-database-rules.md +165 -0
  749. package/dist/serve/docs-ui/docs/pyric-tools-deploy-how-to-deploy-to-a-preview-channel/index.html +46 -0
  750. package/dist/serve/docs-ui/docs/pyric-tools-deploy-how-to-deploy-to-a-preview-channel.md +62 -0
  751. package/dist/serve/docs-ui/docs/pyric-tools-deploy-how-to-handle-errors-and-outcomes/index.html +75 -0
  752. package/dist/serve/docs-ui/docs/pyric-tools-deploy-how-to-handle-errors-and-outcomes.md +94 -0
  753. package/dist/serve/docs-ui/docs/pyric-tools-deploy-how-to-provision-a-firestore-database/index.html +48 -0
  754. package/dist/serve/docs-ui/docs/pyric-tools-deploy-how-to-provision-a-firestore-database.md +65 -0
  755. package/dist/serve/docs-ui/docs/pyric-tools-deploy-how-to-register-tools-with-an-agent/index.html +65 -0
  756. package/dist/serve/docs-ui/docs/pyric-tools-deploy-how-to-register-tools-with-an-agent.md +82 -0
  757. package/dist/serve/docs-ui/docs/pyric-tools-deploy-reference-api/index.html +155 -0
  758. package/dist/serve/docs-ui/docs/pyric-tools-deploy-reference-api.md +190 -0
  759. package/dist/serve/docs-ui/docs/pyric-tools-deploy-reference-cli-agent-io/index.html +84 -0
  760. package/dist/serve/docs-ui/docs/pyric-tools-deploy-reference-cli-agent-io.md +66 -0
  761. package/dist/serve/docs-ui/docs/pyric-tools-deploy-reference-error-codes/index.html +71 -0
  762. package/dist/serve/docs-ui/docs/pyric-tools-deploy-reference-error-codes.md +102 -0
  763. package/dist/serve/docs-ui/docs/pyric-tools-deploy-reference-firestore-namespace/index.html +138 -0
  764. package/dist/serve/docs-ui/docs/pyric-tools-deploy-reference-firestore-namespace.md +175 -0
  765. package/dist/serve/docs-ui/docs/pyric-tools-deploy-reference-functions-namespace/index.html +105 -0
  766. package/dist/serve/docs-ui/docs/pyric-tools-deploy-reference-functions-namespace.md +133 -0
  767. package/dist/serve/docs-ui/docs/pyric-tools-deploy-reference-hosting-config/index.html +194 -0
  768. package/dist/serve/docs-ui/docs/pyric-tools-deploy-reference-hosting-config.md +95 -0
  769. package/dist/serve/docs-ui/docs/pyric-tools-deploy-reference-hosting-namespace/index.html +99 -0
  770. package/dist/serve/docs-ui/docs/pyric-tools-deploy-reference-hosting-namespace.md +126 -0
  771. package/dist/serve/docs-ui/docs/pyric-tools-deploy-reference-rtdb-namespace/index.html +110 -0
  772. package/dist/serve/docs-ui/docs/pyric-tools-deploy-reference-rtdb-namespace.md +133 -0
  773. package/dist/serve/docs-ui/docs/pyric-tools-deploy-reference-scope-and-outcome/index.html +68 -0
  774. package/dist/serve/docs-ui/docs/pyric-tools-deploy-reference-scope-and-outcome.md +88 -0
  775. package/dist/serve/docs-ui/docs/pyric-tools-deploy-reference-tool-factories/index.html +160 -0
  776. package/dist/serve/docs-ui/docs/pyric-tools-deploy-reference-tool-factories.md +104 -0
  777. package/dist/serve/docs-ui/docs/pyric-tools-deploy-tutorials-01-deploy-a-cloud-function/index.html +88 -0
  778. package/dist/serve/docs-ui/docs/pyric-tools-deploy-tutorials-01-deploy-a-cloud-function.md +119 -0
  779. package/dist/serve/docs-ui/docs/pyric-tools-deploy.md +52 -0
  780. package/dist/serve/docs-ui/docs/pyric-tools-how-to-build-a-standalone-binary/index.html +92 -0
  781. package/dist/serve/docs-ui/docs/pyric-tools-how-to-build-a-standalone-binary.md +95 -0
  782. package/dist/serve/docs-ui/docs/pyric-tools-how-to-configure-auth-providers-and-domains/index.html +101 -0
  783. package/dist/serve/docs-ui/docs/pyric-tools-how-to-configure-auth-providers-and-domains.md +126 -0
  784. package/dist/serve/docs-ui/docs/pyric-tools-how-to-discover-a-schema-from-firestore/index.html +95 -0
  785. package/dist/serve/docs-ui/docs/pyric-tools-how-to-discover-a-schema-from-firestore.md +119 -0
  786. package/dist/serve/docs-ui/docs/pyric-tools-how-to-promote-sandbox-state-to-a-fixture/index.html +52 -0
  787. package/dist/serve/docs-ui/docs/pyric-tools-how-to-promote-sandbox-state-to-a-fixture.md +83 -0
  788. package/dist/serve/docs-ui/docs/pyric-tools-how-to-serve-persistence-and-multi-tab/index.html +232 -0
  789. package/dist/serve/docs-ui/docs/pyric-tools-how-to-serve-persistence-and-multi-tab.md +178 -0
  790. package/dist/serve/docs-ui/docs/pyric-tools-how-to-use-the-vite-plugin/index.html +340 -0
  791. package/dist/serve/docs-ui/docs/pyric-tools-how-to-use-the-vite-plugin.md +288 -0
  792. package/dist/serve/docs-ui/docs/pyric-tools-how-to-verify-against-a-captured-session/index.html +128 -0
  793. package/dist/serve/docs-ui/docs/pyric-tools-how-to-verify-against-a-captured-session.md +173 -0
  794. package/dist/serve/docs-ui/docs/pyric-tools-reference-cli/index.html +433 -0
  795. package/dist/serve/docs-ui/docs/pyric-tools-reference-cli.md +252 -0
  796. package/dist/serve/docs-ui/docs/pyric-tools-reference-verify/index.html +81 -0
  797. package/dist/serve/docs-ui/docs/pyric-tools-reference-verify.md +98 -0
  798. package/dist/serve/docs-ui/docs/pyric-tools-tutorials-getting-started/index.html +92 -0
  799. package/dist/serve/docs-ui/docs/pyric-tools-tutorials-getting-started.md +117 -0
  800. package/dist/serve/docs-ui/docs/pyric-tools-tutorials-server-adoption/index.html +221 -0
  801. package/dist/serve/docs-ui/docs/pyric-tools-tutorials-server-adoption.md +239 -0
  802. package/dist/serve/docs-ui/docs/pyric-tools-tutorials-wire-claude-code/index.html +205 -0
  803. package/dist/serve/docs-ui/docs/pyric-tools-tutorials-wire-claude-code.md +250 -0
  804. package/dist/serve/docs-ui/docs/pyric-tools.md +47 -0
  805. package/dist/serve/docs-ui/docs/ui/index.html +76 -0
  806. package/dist/serve/docs-ui/docs/ui-auth-authsigninhelper/index.html +194 -0
  807. package/dist/serve/docs-ui/docs/ui-auth-authsigninhelper.md +108 -0
  808. package/dist/serve/docs-ui/docs/ui-auth-authusers/index.html +258 -0
  809. package/dist/serve/docs-ui/docs/ui-auth-authusers.md +174 -0
  810. package/dist/serve/docs-ui/docs/ui-firestore-collectionlist/index.html +92 -0
  811. package/dist/serve/docs-ui/docs/ui-firestore-collectionlist.md +71 -0
  812. package/dist/serve/docs-ui/docs/ui-firestore-deletewithconfirm/index.html +128 -0
  813. package/dist/serve/docs-ui/docs/ui-firestore-deletewithconfirm.md +94 -0
  814. package/dist/serve/docs-ui/docs/ui-firestore-documenteditor/index.html +154 -0
  815. package/dist/serve/docs-ui/docs/ui-firestore-documenteditor.md +107 -0
  816. package/dist/serve/docs-ui/docs/ui-firestore-documentlist/index.html +132 -0
  817. package/dist/serve/docs-ui/docs/ui-firestore-documentlist.md +86 -0
  818. package/dist/serve/docs-ui/docs/ui-firestore-documentpreview/index.html +159 -0
  819. package/dist/serve/docs-ui/docs/ui-firestore-documentpreview.md +78 -0
  820. package/dist/serve/docs-ui/docs/ui-firestore-querybuilder/index.html +104 -0
  821. package/dist/serve/docs-ui/docs/ui-firestore-querybuilder.md +95 -0
  822. package/dist/serve/docs-ui/docs/ui-firestore-referencepicker/index.html +113 -0
  823. package/dist/serve/docs-ui/docs/ui-firestore-referencepicker.md +85 -0
  824. package/dist/serve/docs-ui/docs/ui-primitives-badge/index.html +56 -0
  825. package/dist/serve/docs-ui/docs/ui-primitives-badge.md +34 -0
  826. package/dist/serve/docs-ui/docs/ui-primitives-confirmdialog/index.html +140 -0
  827. package/dist/serve/docs-ui/docs/ui-primitives-confirmdialog.md +98 -0
  828. package/dist/serve/docs-ui/docs/ui-primitives-copybutton/index.html +58 -0
  829. package/dist/serve/docs-ui/docs/ui-primitives-copybutton.md +31 -0
  830. package/dist/serve/docs-ui/docs/ui-primitives-jsonview/index.html +58 -0
  831. package/dist/serve/docs-ui/docs/ui-primitives-jsonview.md +41 -0
  832. package/dist/serve/docs-ui/docs/ui-primitives-segmentedcontrol/index.html +70 -0
  833. package/dist/serve/docs-ui/docs/ui-primitives-segmentedcontrol.md +44 -0
  834. package/dist/serve/docs-ui/docs/ui-primitives-toast/index.html +87 -0
  835. package/dist/serve/docs-ui/docs/ui-primitives-toast.md +74 -0
  836. package/dist/serve/docs-ui/docs/ui-primitives-virtuallist/index.html +85 -0
  837. package/dist/serve/docs-ui/docs/ui-primitives-virtuallist.md +48 -0
  838. package/dist/serve/docs-ui/docs/ui-storage/index.html +154 -0
  839. package/dist/serve/docs-ui/docs/ui-storage-deleteselectionwithconfirm/index.html +127 -0
  840. package/dist/serve/docs-ui/docs/ui-storage-deleteselectionwithconfirm.md +83 -0
  841. package/dist/serve/docs-ui/docs/ui-storage-objectbrowser/index.html +137 -0
  842. package/dist/serve/docs-ui/docs/ui-storage-objectbrowser.md +84 -0
  843. package/dist/serve/docs-ui/docs/ui-storage-objectinspector/index.html +139 -0
  844. package/dist/serve/docs-ui/docs/ui-storage-objectinspector.md +87 -0
  845. package/dist/serve/docs-ui/docs/ui-storage-pathbreadcrumb/index.html +72 -0
  846. package/dist/serve/docs-ui/docs/ui-storage-pathbreadcrumb.md +51 -0
  847. package/dist/serve/docs-ui/docs/ui-storage-rules-aware-affordances/index.html +65 -0
  848. package/dist/serve/docs-ui/docs/ui-storage-rules-aware-affordances.md +53 -0
  849. package/dist/serve/docs-ui/docs/ui-storage-uploaddropzone/index.html +93 -0
  850. package/dist/serve/docs-ui/docs/ui-storage-uploaddropzone.md +72 -0
  851. package/dist/serve/docs-ui/docs/ui-storage-usestoragerulesgate/index.html +153 -0
  852. package/dist/serve/docs-ui/docs/ui-storage-usestoragerulesgate.md +105 -0
  853. package/dist/serve/docs-ui/docs/ui-storage.md +165 -0
  854. package/dist/serve/docs-ui/docs/ui-traffic/index.html +88 -0
  855. package/dist/serve/docs-ui/docs/ui-traffic-ruleheatmap/index.html +79 -0
  856. package/dist/serve/docs-ui/docs/ui-traffic-ruleheatmap.md +54 -0
  857. package/dist/serve/docs-ui/docs/ui-traffic-trafficdetail/index.html +78 -0
  858. package/dist/serve/docs-ui/docs/ui-traffic-trafficdetail.md +53 -0
  859. package/dist/serve/docs-ui/docs/ui-traffic-trafficlog/index.html +118 -0
  860. package/dist/serve/docs-ui/docs/ui-traffic-trafficlog.md +63 -0
  861. package/dist/serve/docs-ui/docs/ui-traffic-trafficstats/index.html +51 -0
  862. package/dist/serve/docs-ui/docs/ui-traffic-trafficstats.md +40 -0
  863. package/dist/serve/docs-ui/docs/ui-traffic.md +96 -0
  864. package/dist/serve/docs-ui/docs/ui.md +88 -0
  865. package/dist/serve/docs-ui/index.html +1 -0
  866. package/dist/serve/docs-ui/llms.txt +221 -0
  867. package/dist/serve/entries/app.d.ts +26 -0
  868. package/dist/serve/entries/app.d.ts.map +1 -0
  869. package/dist/serve/entries/app.js +51 -0
  870. package/dist/serve/entries/app.js.map +1 -0
  871. package/dist/serve/entries/auth-helper-core.d.ts +47 -0
  872. package/dist/serve/entries/auth-helper-core.d.ts.map +1 -0
  873. package/dist/serve/entries/auth-helper-core.js +121 -0
  874. package/dist/serve/entries/auth-helper-core.js.map +1 -0
  875. package/dist/serve/entries/auth-helper-dom.d.ts +14 -0
  876. package/dist/serve/entries/auth-helper-dom.d.ts.map +1 -0
  877. package/dist/serve/entries/auth-helper-dom.js +116 -0
  878. package/dist/serve/entries/auth-helper-dom.js.map +1 -0
  879. package/dist/serve/entries/auth.d.ts +62 -0
  880. package/dist/serve/entries/auth.d.ts.map +1 -0
  881. package/dist/serve/entries/auth.js +128 -0
  882. package/dist/serve/entries/auth.js.map +1 -0
  883. package/dist/serve/entries/bridge-url.d.ts +25 -0
  884. package/dist/serve/entries/bridge-url.d.ts.map +1 -0
  885. package/dist/serve/entries/bridge-url.js +30 -0
  886. package/dist/serve/entries/bridge-url.js.map +1 -0
  887. package/dist/serve/entries/database.d.ts +34 -0
  888. package/dist/serve/entries/database.d.ts.map +1 -0
  889. package/dist/serve/entries/database.js +42 -0
  890. package/dist/serve/entries/database.js.map +1 -0
  891. package/dist/serve/entries/firestore.d.ts +68 -0
  892. package/dist/serve/entries/firestore.d.ts.map +1 -0
  893. package/dist/serve/entries/firestore.js +115 -0
  894. package/dist/serve/entries/firestore.js.map +1 -0
  895. package/dist/serve/entries/init.d.ts +2 -0
  896. package/dist/serve/entries/init.d.ts.map +1 -0
  897. package/dist/serve/entries/init.js +14 -0
  898. package/dist/serve/entries/init.js.map +1 -0
  899. package/dist/serve/entries/keepalive.d.ts +20 -0
  900. package/dist/serve/entries/keepalive.d.ts.map +1 -0
  901. package/dist/serve/entries/keepalive.js +38 -0
  902. package/dist/serve/entries/keepalive.js.map +1 -0
  903. package/dist/serve/entries/runtime.d.ts +60 -0
  904. package/dist/serve/entries/runtime.d.ts.map +1 -0
  905. package/dist/serve/entries/runtime.js +555 -0
  906. package/dist/serve/entries/runtime.js.map +1 -0
  907. package/dist/serve/entries/session-store.d.ts +45 -0
  908. package/dist/serve/entries/session-store.d.ts.map +1 -0
  909. package/dist/serve/entries/session-store.js +71 -0
  910. package/dist/serve/entries/session-store.js.map +1 -0
  911. package/dist/serve/entries/storage.d.ts +23 -0
  912. package/dist/serve/entries/storage.d.ts.map +1 -0
  913. package/dist/serve/entries/storage.js +49 -0
  914. package/dist/serve/entries/storage.js.map +1 -0
  915. package/dist/serve/entries/tab-sync-wiring.d.ts +88 -0
  916. package/dist/serve/entries/tab-sync-wiring.d.ts.map +1 -0
  917. package/dist/serve/entries/tab-sync-wiring.js +217 -0
  918. package/dist/serve/entries/tab-sync-wiring.js.map +1 -0
  919. package/dist/serve/namespace.d.ts +93 -0
  920. package/dist/serve/namespace.d.ts.map +1 -0
  921. package/dist/serve/namespace.js +367 -0
  922. package/dist/serve/namespace.js.map +1 -0
  923. package/dist/serve/open-browser.d.ts +23 -0
  924. package/dist/serve/open-browser.d.ts.map +1 -0
  925. package/dist/serve/open-browser.js +58 -0
  926. package/dist/serve/open-browser.js.map +1 -0
  927. package/dist/serve/playground-ui/_astro/HomePage.BFOjZGZp.js +2 -0
  928. package/dist/serve/playground-ui/_astro/PlaygroundPage.3jki8il0.js +3300 -0
  929. package/dist/serve/playground-ui/_astro/PlaygroundPage.MhP_vFvK.css +32 -0
  930. package/dist/serve/playground-ui/_astro/TopBar.ChifCjLD.js +5025 -0
  931. package/dist/serve/playground-ui/_astro/client.D61DpmEZ.js +274 -0
  932. package/dist/serve/playground-ui/_astro/client.GCK88uRs.js +1 -0
  933. package/dist/serve/playground-ui/_astro/esbuild.BHljloGq.wasm +0 -0
  934. package/dist/serve/playground-ui/_astro/index.B9aLOeXL.js +29 -0
  935. package/dist/serve/playground-ui/_astro/index.BTKE7uRF.js +1227 -0
  936. package/dist/serve/playground-ui/_astro/index.BfCthstx.css +1 -0
  937. package/dist/serve/playground-ui/_astro/index.Mr0Dtjih.js +342 -0
  938. package/dist/serve/playground-ui/_astro/ort-wasm-simd-threaded.asyncify.DMmc6YqF.wasm +0 -0
  939. package/dist/serve/playground-ui/favicon.svg +4 -0
  940. package/dist/serve/playground-ui/index.html +2 -0
  941. package/dist/serve/playground-ui/playground/index.html +2 -0
  942. package/dist/serve/rules.d.ts +45 -0
  943. package/dist/serve/rules.d.ts.map +1 -0
  944. package/dist/serve/rules.js +139 -0
  945. package/dist/serve/rules.js.map +1 -0
  946. package/dist/serve/sandbox-marker.d.ts +9 -0
  947. package/dist/serve/sandbox-marker.d.ts.map +1 -0
  948. package/dist/serve/sandbox-marker.js +32 -0
  949. package/dist/serve/sandbox-marker.js.map +1 -0
  950. package/dist/serve/server.d.ts +135 -0
  951. package/dist/serve/server.d.ts.map +1 -0
  952. package/dist/serve/server.js +328 -0
  953. package/dist/serve/server.js.map +1 -0
  954. package/dist/serve/standalone-assets.d.ts +66 -0
  955. package/dist/serve/standalone-assets.d.ts.map +1 -0
  956. package/dist/serve/standalone-assets.js +144 -0
  957. package/dist/serve/standalone-assets.js.map +1 -0
  958. package/dist/serve/state-store.d.ts +45 -0
  959. package/dist/serve/state-store.d.ts.map +1 -0
  960. package/dist/serve/state-store.js +120 -0
  961. package/dist/serve/state-store.js.map +1 -0
  962. package/dist/serve/studio/disk-project-store.d.ts +9 -0
  963. package/dist/serve/studio/disk-project-store.d.ts.map +1 -0
  964. package/dist/serve/studio/disk-project-store.js +173 -0
  965. package/dist/serve/studio/disk-project-store.js.map +1 -0
  966. package/dist/serve/studio/disk-workspace.d.ts +14 -0
  967. package/dist/serve/studio/disk-workspace.d.ts.map +1 -0
  968. package/dist/serve/studio/disk-workspace.js +152 -0
  969. package/dist/serve/studio/disk-workspace.js.map +1 -0
  970. package/dist/serve/studio/index.d.ts +10 -0
  971. package/dist/serve/studio/index.d.ts.map +1 -0
  972. package/dist/serve/studio/index.js +9 -0
  973. package/dist/serve/studio/index.js.map +1 -0
  974. package/dist/serve/studio/routes.d.ts +40 -0
  975. package/dist/serve/studio/routes.d.ts.map +1 -0
  976. package/dist/serve/studio/routes.js +182 -0
  977. package/dist/serve/studio/routes.js.map +1 -0
  978. package/dist/serve/studio/store-types.d.ts +45 -0
  979. package/dist/serve/studio/store-types.d.ts.map +1 -0
  980. package/dist/serve/studio/store-types.js +11 -0
  981. package/dist/serve/studio/store-types.js.map +1 -0
  982. package/dist/serve/studio-ui/assets/RulesCodeEditor-Bncteb8z.js +23 -0
  983. package/dist/serve/studio-ui/assets/index-CmkJxFcG.js +4577 -0
  984. package/dist/serve/studio-ui/assets/index-DNK39P6C.css +1 -0
  985. package/dist/serve/studio-ui/assets/seed-Bab4ArXQ.js +2 -0
  986. package/dist/serve/studio-ui/index.html +32 -0
  987. package/dist/serve/studio-ui/pyric-logo.svg +76 -0
  988. package/dist/serve/vite-plugin.d.ts +57 -0
  989. package/dist/serve/vite-plugin.d.ts.map +1 -0
  990. package/dist/serve/vite-plugin.js +687 -0
  991. package/dist/serve/vite-plugin.js.map +1 -0
  992. package/dist/serve/worker/client.d.ts +668 -0
  993. package/dist/serve/worker/client.d.ts.map +1 -0
  994. package/dist/serve/worker/client.js +1635 -0
  995. package/dist/serve/worker/client.js.map +1 -0
  996. package/dist/serve/worker/entry.d.ts +40 -0
  997. package/dist/serve/worker/entry.d.ts.map +1 -0
  998. package/dist/serve/worker/entry.js +122 -0
  999. package/dist/serve/worker/entry.js.map +1 -0
  1000. package/dist/serve/worker/host-auth.d.ts +47 -0
  1001. package/dist/serve/worker/host-auth.d.ts.map +1 -0
  1002. package/dist/serve/worker/host-auth.js +434 -0
  1003. package/dist/serve/worker/host-auth.js.map +1 -0
  1004. package/dist/serve/worker/host-context.d.ts +180 -0
  1005. package/dist/serve/worker/host-context.d.ts.map +1 -0
  1006. package/dist/serve/worker/host-context.js +45 -0
  1007. package/dist/serve/worker/host-context.js.map +1 -0
  1008. package/dist/serve/worker/host-events.d.ts +21 -0
  1009. package/dist/serve/worker/host-events.d.ts.map +1 -0
  1010. package/dist/serve/worker/host-events.js +87 -0
  1011. package/dist/serve/worker/host-events.js.map +1 -0
  1012. package/dist/serve/worker/host.d.ts +81 -0
  1013. package/dist/serve/worker/host.d.ts.map +1 -0
  1014. package/dist/serve/worker/host.js +1605 -0
  1015. package/dist/serve/worker/host.js.map +1 -0
  1016. package/dist/serve/worker/index.d.ts +22 -0
  1017. package/dist/serve/worker/index.d.ts.map +1 -0
  1018. package/dist/serve/worker/index.js +61 -0
  1019. package/dist/serve/worker/index.js.map +1 -0
  1020. package/dist/serve/worker/protocol.d.ts +955 -0
  1021. package/dist/serve/worker/protocol.d.ts.map +1 -0
  1022. package/dist/serve/worker/protocol.js +197 -0
  1023. package/dist/serve/worker/protocol.js.map +1 -0
  1024. package/dist/serve/worker/serve-init.d.ts +192 -0
  1025. package/dist/serve/worker/serve-init.d.ts.map +1 -0
  1026. package/dist/serve/worker/serve-init.js +555 -0
  1027. package/dist/serve/worker/serve-init.js.map +1 -0
  1028. package/dist/serve/writer-lock.d.ts +32 -0
  1029. package/dist/serve/writer-lock.d.ts.map +1 -0
  1030. package/dist/serve/writer-lock.js +43 -0
  1031. package/dist/serve/writer-lock.js.map +1 -0
  1032. package/dist/verify/cases.d.ts +37 -0
  1033. package/dist/verify/cases.d.ts.map +1 -0
  1034. package/dist/verify/cases.js +173 -0
  1035. package/dist/verify/cases.js.map +1 -0
  1036. package/dist/verify/fixture.d.ts +79 -0
  1037. package/dist/verify/fixture.d.ts.map +1 -0
  1038. package/dist/verify/fixture.js +114 -0
  1039. package/dist/verify/fixture.js.map +1 -0
  1040. package/dist/verify/index.d.ts +102 -0
  1041. package/dist/verify/index.d.ts.map +1 -0
  1042. package/dist/verify/index.js +318 -0
  1043. package/dist/verify/index.js.map +1 -0
  1044. package/dist/verify/tools.d.ts +7 -0
  1045. package/dist/verify/tools.d.ts.map +1 -0
  1046. package/dist/verify/tools.js +83 -0
  1047. package/dist/verify/tools.js.map +1 -0
  1048. package/dist/vite.d.ts +12 -0
  1049. package/dist/vite.d.ts.map +1 -0
  1050. package/dist/vite.js +16 -0
  1051. package/dist/vite.js.map +1 -0
  1052. package/package.json +127 -0
@@ -0,0 +1,1605 @@
1
+ /**
2
+ * SharedWorker host — op handlers and subscription registry.
3
+ *
4
+ * WHY INJECTED DEPS
5
+ * -----------------
6
+ * The host is deliberately decoupled from `SharedWorkerGlobalScope` so
7
+ * unit tests can drive it with a REAL pyric sandbox + fake MessagePort
8
+ * objects — no browser or SharedWorker runtime required. The entry point
9
+ * (`entry.ts`) creates the real sandbox + db and wires the connecting ports
10
+ * to this module.
11
+ *
12
+ * ARCHITECTURE
13
+ * ------------
14
+ * One `HostCtx` is shared across ALL connected ports. It holds:
15
+ * - `db` — the single modular Firestore handle (from pyric/firestore's
16
+ * `getFirestore(sandbox)` — sandbox-live mode so auth changes propagate).
17
+ * - `sandbox` — the underlying Sandbox, needed for `setRules` and for
18
+ * constructing sentinels via FieldValue.
19
+ * - `subs` — per-port subscription registry: Map<PortLike, Map<subId, unsub>>
20
+ *
21
+ * Each connecting port calls `handleMessage(ctx, port, msg)`. The function
22
+ * is exported so tests can call it directly.
23
+ *
24
+ * SENTINEL RESOLUTION
25
+ * -------------------
26
+ * Write data crossing the port may contain `SentinelMarker` objects
27
+ * (`{ __sentinel: 'serverTimestamp' }` etc.). Before passing data to the
28
+ * sandbox we walk the payload and replace each marker with the real
29
+ * FieldValue object from `pyric/firestore`'s sentinel factories. The
30
+ * sandbox's value-resolver then executes them as usual.
31
+ *
32
+ * SUBSCRIPTION FAN-OUT
33
+ * --------------------
34
+ * Because all ports share ONE sandbox, an onSnapshot listener registered
35
+ * via the sandbox automatically fires for writes from ANY port. We just
36
+ * need to forward the snapshot to the correct originating port(s).
37
+ *
38
+ * TRANSACTIONS + READ-SET VALIDATION
39
+ * ------------------------------------
40
+ * `runTransaction` on the worker is the full-fidelity path: the host calls
41
+ * the sandbox's `runTransaction`, which runs the update function, handles
42
+ * optimistic-concurrency retries, and commits atomically. The client now
43
+ * sends a `reads` array alongside `writes`; the worker re-reads each doc
44
+ * inside the sandbox transaction and validates that no concurrent write
45
+ * changed any of them between the client's read and this commit. A mismatch
46
+ * signals `{ code: 'aborted' }` on the wire so the client can re-run
47
+ * `updateFn` — see `txnCommit` handler for full details.
48
+ */
49
+ import { getFirestore as pyricGetFirestore, getAdminFirestore as pyricGetAdminFirestore, doc as pyricDoc, collection as pyricCollection, collectionGroup as pyricCollectionGroup, query as pyricQuery, where as pyricWhere, and as pyricAnd, or as pyricOr, orderBy as pyricOrderBy, limit as pyricLimit, limitToLast as pyricLimitToLast, startAt as pyricStartAt, startAfter as pyricStartAfter, endAt as pyricEndAt, endBefore as pyricEndBefore, getDoc, getDocs, setDoc, updateDoc, deleteDoc, addDoc, onSnapshot, getCountFromServer, getAggregateFromServer, runTransaction, writeBatch, serverTimestamp, increment, arrayUnion, arrayRemove, deleteField, } from 'pyric/firestore';
50
+ // The canonical value codec (same module the protocol's read path uses) —
51
+ // write payloads rehydrate host-side so marker-shaped scalars from the JSON
52
+ // relay legs are STORED as real wrapper instances (see prepareWriteData).
53
+ import { rehydrateDocValue } from 'pyric/firestore-values';
54
+ import { serializeToBuckets, bundleRecords, parseBundle, deserializeFromBuckets } from 'pyric/sandbox';
55
+ import { sandbox as sandboxOps } from 'pyric/firestore';
56
+ import { getInternalEnv } from 'pyric/sandbox/internal';
57
+ import { initializeApp } from 'pyric/app';
58
+ import { getStorage, getStorageSandbox, ref as storageRef, listAll as storageListAll, getMetadata as storageGetMetadata, getBlob as storageGetBlob, getBytes as storageGetBytes, uploadBytes as storageUploadBytes, deleteObject as storageDeleteObject, } from 'pyric/storage';
59
+ // Host-only rules-bypass admin plane — the storage mirror of
60
+ // `getAdminFirestore`/`getAdminDatabase`, resolved for `actAs: { mode: 'admin' }`.
61
+ import { getAdminStorageSandbox } from 'pyric/storage/internal';
62
+ import { getDatabase as pyricGetDatabase, getAdminDatabase as pyricGetAdminDatabase, ref as rtdbRef, get as rtdbGet, set as rtdbSet, update as rtdbUpdate, remove as rtdbRemove, onValue as rtdbOnValue, serverTimestamp as rtdbServerTimestamp, sandbox as rtdbSandbox, } from 'pyric/database/modular';
63
+ import { serializeError, serializeDocData, isSentinelMarker, isAuthSub, isEventSub, isRtdbSub, bytesToBase64, base64ToBytes, storagePayloadTooLarge, MAX_STORAGE_OP_BYTES, MAX_STORAGE_OP_B64_LENGTH, } from './protocol.js';
64
+ // The canonical agent tool dispatcher — reused on the worker so a bridged agent
65
+ // executes against THIS sandbox (one backend for app + Studio + agent), instead
66
+ // of a separate in-page sandbox.
67
+ import { buildSandboxDispatcher } from '../../bridge/client/dispatch.js';
68
+ import { post, ok, fail, bestEffortFlush } from './host-context.js';
69
+ import { authSubsFor, isAuthOp, handleAuthOp, handleAuthSub, handleAuthUnsub, portSession, cleanupPortSession, } from './host-auth.js';
70
+ import { eventSubsFor, handleEventSub, handleEventUnsub, } from './host-events.js';
71
+ // Re-export so host.ts's public surface is unchanged after the decomposition.
72
+ export { ensureAuth, portSession } from './host-auth.js';
73
+ /**
74
+ * Per-SharedWorker instance id — generated once and persisted to the RAW idb
75
+ * (local-only, like the session record above; it must NEVER reach the
76
+ * committable server file). Because IndexedDB is per (origin + browser profile),
77
+ * two profiles on the same `localhost:<port>` get two distinct ids — which is
78
+ * exactly how the UI tells same-port-different-profile sandboxes apart.
79
+ */
80
+ export const INSTANCE_ID_KEY = 'pyric:worker:instance';
81
+ /**
82
+ * `crypto.randomUUID()` is secure-context-only (https or localhost), so it is
83
+ * `undefined` over plain http on a non-localhost host (a Tailscale or LAN
84
+ * hostname). `crypto.getRandomValues` is NOT gated, so build a v4 UUID from it as
85
+ * the fallback. Without this the worker throws on init over Tailscale and the
86
+ * whole sandbox (auth, firestore, bridge) silently fails to come up.
87
+ */
88
+ export function randomUuid() {
89
+ if (typeof crypto.randomUUID === 'function')
90
+ return crypto.randomUUID();
91
+ const b = crypto.getRandomValues(new Uint8Array(16));
92
+ b[6] = (b[6] & 0x0f) | 0x40;
93
+ b[8] = (b[8] & 0x3f) | 0x80;
94
+ const h = Array.from(b, (x) => x.toString(16).padStart(2, '0'));
95
+ return `${h.slice(0, 4).join('')}-${h.slice(4, 6).join('')}-${h.slice(6, 8).join('')}-${h.slice(8, 10).join('')}-${h.slice(10, 16).join('')}`;
96
+ }
97
+ export async function getOrCreateInstanceId(idb) {
98
+ const rec = (await idb.getRecord(INSTANCE_ID_KEY, 'id'));
99
+ if (rec && typeof rec.value === 'string')
100
+ return rec.value;
101
+ const id = randomUuid();
102
+ await idb.putRecords(INSTANCE_ID_KEY, new Map([['id', { value: id }]]));
103
+ return id;
104
+ }
105
+ // ── Phase 3: named branches ─────────────────────────────────────────────────
106
+ // A branch is a named saved state bundle in the RAW idb (local-only, like the
107
+ // instance id + session; it must NEVER reach the committable server file). They
108
+ // let one instance keep several named states it can switch between
109
+ // (switchBranch = loadSnapshot the bundle, a clobber). A registry record holds
110
+ // the ordered name list, since the backend lists records WITHIN a key, not keys.
111
+ export const BRANCH_PREFIX = 'pyric:worker:branch:';
112
+ export const BRANCH_REGISTRY_KEY = 'pyric:worker:branches';
113
+ export async function listBranchNames(idb) {
114
+ if (!idb)
115
+ return [];
116
+ const rec = (await idb.getRecord(BRANCH_REGISTRY_KEY, 'names'));
117
+ return Array.isArray(rec?.value) ? rec.value : [];
118
+ }
119
+ async function writeBranchRegistry(idb, names) {
120
+ await idb.putRecords(BRANCH_REGISTRY_KEY, new Map([['names', { value: names }]]));
121
+ }
122
+ // ─── Descriptor → live ref resolution ───────────────────────────────────
123
+ /**
124
+ * Resolve a TargetDescriptor into a live pyric ref or query.
125
+ *
126
+ * WHY WE RESOLVE ON THE WORKER
127
+ * The client never holds live pyric refs — it holds plain `DocRef`/
128
+ * `CollRef`/`GroupRef`/`QueryDescriptor` objects that serialize cleanly
129
+ * across the MessagePort. On every op the worker rebuilds the live ref
130
+ * from these descriptors. This keeps the client completely stateless with
131
+ * respect to sandbox internals and makes retries trivially correct.
132
+ */
133
+ function resolveTarget(db, target) {
134
+ if (target.__ref === 'doc') {
135
+ return pyricDoc(db, target.path);
136
+ }
137
+ if (target.__ref === 'collection') {
138
+ return pyricCollection(db, target.path);
139
+ }
140
+ if (target.__ref === 'group') {
141
+ return pyricCollectionGroup(db, target.collectionId);
142
+ }
143
+ // query descriptor
144
+ const source = resolveTarget(db, target.source);
145
+ const constraints = target.constraints.map((c) => resolveConstraint(c));
146
+ return pyricQuery(source, ...constraints);
147
+ }
148
+ function resolveConstraint(c) {
149
+ switch (c.kind) {
150
+ case 'where':
151
+ // Rehydrate the comparison value (same rationale as prepareWriteData,
152
+ // spike gap 4, applied to READ inputs): over the JSON relay legs a
153
+ // Node-side Timestamp/Bytes/GeoPoint arrives as its marker shape;
154
+ // without rehydration the comparison would see a plain map and the
155
+ // filter would silently mismatch typed stored values.
156
+ return pyricWhere(c.field, c.op, rehydrateDocValue(c.value));
157
+ // Composite filters rebuild through the modular `and`/`or` factories,
158
+ // which validate operands: an empty composite or a nested non-filter
159
+ // throws the same TypeError the in-page SDK raises (surfaces as an
160
+ // error res / snap-error, never a crash).
161
+ case 'and':
162
+ return pyricAnd(...c.filters.map(resolveConstraint));
163
+ case 'or':
164
+ return pyricOr(...c.filters.map(resolveConstraint));
165
+ case 'orderBy':
166
+ return pyricOrderBy(c.field, c.direction);
167
+ case 'limit':
168
+ return pyricLimit(c.n);
169
+ case 'limitToLast':
170
+ return pyricLimitToLast(c.n);
171
+ // Cursor values rehydrate for the same reason as `where` values —
172
+ // `startAfter(<timestamp>)` must position against real Timestamps.
173
+ case 'startAt':
174
+ return pyricStartAt(...c.values.map(rehydrateDocValue));
175
+ case 'startAfter':
176
+ return pyricStartAfter(...c.values.map(rehydrateDocValue));
177
+ case 'endAt':
178
+ return pyricEndAt(...c.values.map(rehydrateDocValue));
179
+ case 'endBefore':
180
+ return pyricEndBefore(...c.values.map(rehydrateDocValue));
181
+ }
182
+ }
183
+ // ─── Sentinel resolution ──────────────────────────────────────────────────
184
+ /**
185
+ * Walk a write payload and replace every `SentinelMarker` with the
186
+ * corresponding pyric/firestore FieldValue sentinel object.
187
+ *
188
+ * WHY: FieldValue class instances don't survive structured clone — they
189
+ * arrive as plain objects and lose their prototype, breaking sandbox
190
+ * sentinel detection. The client sends `{ __sentinel: 'serverTimestamp' }`
191
+ * etc. and we reconstruct the real FieldValue here before the sandbox sees
192
+ * the data.
193
+ *
194
+ * The sandbox's sentinel-capture code (sentinel-capture.ts) recognizes
195
+ * FieldValue objects by their internal `__type` property, which the
196
+ * factories produce correctly.
197
+ */
198
+ function resolveSentinels(value) {
199
+ if (isSentinelMarker(value)) {
200
+ return resolveSentinel(value);
201
+ }
202
+ if (Array.isArray(value)) {
203
+ return value.map(resolveSentinels);
204
+ }
205
+ if (value !== null && typeof value === 'object') {
206
+ // Leave CLASS instances intact (Timestamp/Bytes/LatLng rehydrated by
207
+ // prepareWriteData below, or FieldValue objects): walking their entries
208
+ // into a plain object would strip the prototype the sandbox keys on.
209
+ // Sentinel markers only ever live in plain JSON containers.
210
+ const proto = Object.getPrototypeOf(value);
211
+ if (proto !== Object.prototype && proto !== null) {
212
+ return value;
213
+ }
214
+ const obj = value;
215
+ const out = {};
216
+ for (const [k, v] of Object.entries(obj)) {
217
+ out[k] = resolveSentinels(v);
218
+ }
219
+ return out;
220
+ }
221
+ return value;
222
+ }
223
+ /**
224
+ * Prepare an incoming WRITE payload for the sandbox: rehydrate marker-shaped
225
+ * scalars into REAL wrapper instances, then rebuild FieldValue sentinels.
226
+ *
227
+ * WHY REHYDRATE WRITES (spike gap 4): over the JSON relay legs a Node-side
228
+ * `Timestamp`/`Bytes`/`GeoPoint` arrives as its `toJSON()` marker
229
+ * (`{ type: 'firestore/timestamp/1.0', … }` or `{ __type: 'timestamp', … }`).
230
+ * Without rehydration the worker STORES the marker as a plain map — reads
231
+ * mask the bug (the read path rehydrates), but in-worker rules comparisons
232
+ * and `orderBy` over that field see a map, not a timestamp. `rehydrateDocValue`
233
+ * is the same canonical codec the read path / persistence uses, so the wire,
234
+ * store, and IDB formats stay one format.
235
+ *
236
+ * Order matters: rehydration first (it passes `__sentinel` markers through
237
+ * as plain objects, rehydrating any marker-shaped values nested inside
238
+ * arrayUnion/arrayRemove), then sentinel resolution (which now skips the
239
+ * freshly rehydrated class instances — see resolveSentinels).
240
+ */
241
+ function prepareWriteData(value) {
242
+ return resolveSentinels(rehydrateDocValue(value));
243
+ }
244
+ function resolveRtdbSentinels(value) {
245
+ if (value && typeof value === 'object') {
246
+ const marker = value;
247
+ if (marker.__rtdbSentinel === 'serverTimestamp')
248
+ return rtdbServerTimestamp();
249
+ if (Array.isArray(value))
250
+ return value.map(resolveRtdbSentinels);
251
+ const out = {};
252
+ for (const [k, v] of Object.entries(value))
253
+ out[k] = resolveRtdbSentinels(v);
254
+ return out;
255
+ }
256
+ return value;
257
+ }
258
+ function resolveSentinel(marker) {
259
+ switch (marker.__sentinel) {
260
+ case 'serverTimestamp': return serverTimestamp();
261
+ case 'increment': return increment(marker.n);
262
+ case 'arrayUnion': return arrayUnion(...marker.values);
263
+ case 'arrayRemove': return arrayRemove(...marker.values);
264
+ case 'deleteField': return deleteField();
265
+ }
266
+ }
267
+ // ─── Snapshot serialization ───────────────────────────────────────────────
268
+ /**
269
+ * Rebuild a JSON-flattened `Uint8Array` (an index-keyed `{ "0": n, … }`
270
+ * map) and encode it as base64url without padding — the exact form
271
+ * `pyric/rules`' `Bytes.toBase64()` emits. Backs the transaction read-set
272
+ * canonicalizer's handling of prototype-stripped `Bytes` clones.
273
+ */
274
+ /** Timestamp/Duration stripped-clone key set (they share field names). */
275
+ const TS_CLONE_KEYS = ['typeName', 'seconds', 'nanos'];
276
+ /** Does `o` carry EXACTLY these own enumerable keys (no more, no fewer)?
277
+ * Backs the transaction canonicalizer's strict clone-shape matching. */
278
+ function hasExactKeys(o, keys) {
279
+ const own = Object.keys(o);
280
+ return own.length === keys.length && keys.every((k) => k in o);
281
+ }
282
+ function indexMapToBase64Url(data) {
283
+ const map = (data ?? {});
284
+ const keys = Object.keys(map);
285
+ const bytes = new Uint8Array(keys.length);
286
+ for (let i = 0; i < keys.length; i++)
287
+ bytes[i] = map[String(i)] ?? 0;
288
+ let binary = '';
289
+ for (let i = 0; i < bytes.length; i++)
290
+ binary += String.fromCharCode(bytes[i]);
291
+ return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
292
+ }
293
+ /**
294
+ * Serialize a document snapshot to cross-port form.
295
+ * Uses JSON-via-serializeDocData so Timestamp/Bytes/LatLng survive.
296
+ */
297
+ function serializeDocSnap(snap) {
298
+ const existsBool = typeof snap.exists === 'function' ? snap.exists() : snap.exists;
299
+ const data = existsBool ? snap.data() : undefined;
300
+ return {
301
+ id: snap.id,
302
+ // Doc snapshots carry a top-level `path`; QUERY doc snapshots carry it on
303
+ // `.ref.path`. Read both so query rows serialize their FULL path (Studio's
304
+ // browse resolves the document detail from it).
305
+ path: snap.path ?? snap.ref?.path,
306
+ exists: existsBool,
307
+ data: data ? serializeDocData(data) : undefined,
308
+ };
309
+ }
310
+ // ─── Auth lens (Pyric Studio) ─────────────────────────────────────────────
311
+ /**
312
+ * Resolve the Firestore handle an op runs against, given its `actAs` lens
313
+ * (Pyric Studio auth lens, T2 — implements the C2 protocol seam).
314
+ *
315
+ * Resolution by `actAs.mode` (absent ⇒ the app's session):
316
+ *
317
+ * - `app-session` (and `actAs` absent): `ctx.db` — the shared sandbox-live
318
+ * handle. Reads `sandbox.currentUser` per op, so rules evaluate under
319
+ * whoever the served app is signed in as. This is the unchanged default
320
+ * and the ONLY lens the app itself ever uses.
321
+ *
322
+ * - `{ mode: 'as', uid }` (impersonation — the rules-debugging primitive):
323
+ * a FROZEN-identity `getFirestore(sandbox.withAuth({ uid }))` handle.
324
+ * Security rules APPLY and evaluate as that user — `request.auth.uid`
325
+ * resolves to `uid`. This lets Studio "re-run this denied op as the user
326
+ * who attempted it" (Wave-2 rules-debugging, F4). Handles are cached
327
+ * per-uid on `ctx.lensHandles`.
328
+ *
329
+ * - `{ mode: 'admin' }` (rule bypass): a modular `getAdminFirestore(sandbox)`
330
+ * handle (pyric Gap #2) whose ops skip security-rule evaluation while still
331
+ * reading/writing the same store and emitting events. This is Studio's
332
+ * "edit anything as admin" surface (F2). Cached on `ctx.adminDb`.
333
+ *
334
+ * - `{ mode: 'anon' }` (explicitly unauthenticated): a frozen
335
+ * `getFirestore(sandbox.withAuth(null))` handle — rules apply with
336
+ * `request.auth == null`. The remote arm's `withAuth(null)`; distinct from
337
+ * an ABSENT lens, which resolves to the port's session. Cached on
338
+ * `ctx.anonDb`.
339
+ *
340
+ * WRITE-IMPERSONATION GATING (open micro-decision #1, honoured): the resolver
341
+ * itself is symmetric — an `{ mode: 'as', uid }` lens applies to BOTH reads and
342
+ * writes (a write-as-user is denied/allowed exactly as that user's rules say).
343
+ * The POLICY decision — "read-as-user always; write-as-user only on an explicit
344
+ * reproduce path" — is enforced at the CALLER (the Studio client / UI chooses
345
+ * when to attach a write op's `actAs`), NOT here. Default behaviour stays admin/
346
+ * app-session unless a caller sets `actAs`; no UI is added by T2.
347
+ */
348
+ function lensDb(ctx, actAs) {
349
+ // Absent / app-session → the app's live session handle.
350
+ if (!actAs || actAs.mode === 'app-session') {
351
+ return ctx.db;
352
+ }
353
+ // { mode: 'admin' } → a modular rules-bypass handle (cached per ctx).
354
+ if (actAs.mode === 'admin') {
355
+ return (ctx.adminDb ??= pyricGetAdminFirestore(ctx.sandbox));
356
+ }
357
+ // { mode: 'anon' } → a genuinely UNAUTHENTICATED handle
358
+ // (`withAuth(null)` — `request.auth == null` in rules). NOT the same as an
359
+ // absent lens, which resolves to the PORT'S SESSION: a relayed op that
360
+ // means "no auth" must pin this lens or it silently runs as whoever the
361
+ // browser tab is signed in as.
362
+ if (actAs.mode === 'anon') {
363
+ return (ctx.anonDb ??= pyricGetFirestore(ctx.sandbox.withAuth(null)));
364
+ }
365
+ // { mode: 'as', uid } → a frozen-identity handle; rules evaluate as `uid`.
366
+ const handles = (ctx.lensHandles ??= new Map());
367
+ const key = lensCacheKey(actAs);
368
+ let handle = handles.get(key);
369
+ if (!handle) {
370
+ handle = pyricGetFirestore(ctx.sandbox.withAuth(authStateForLens(actAs)));
371
+ handles.set(key, handle);
372
+ }
373
+ return handle;
374
+ }
375
+ function authStateForLens(actAs) {
376
+ return actAs.token === undefined
377
+ ? { uid: actAs.uid }
378
+ : { uid: actAs.uid, token: actAs.token };
379
+ }
380
+ function lensCacheKey(actAs) {
381
+ return actAs.token === undefined
382
+ ? actAs.uid
383
+ : `${actAs.uid}:${JSON.stringify(actAs.token)}`;
384
+ }
385
+ /**
386
+ * Normalise a per-op `actAs` lens to the {@link AuthLens} provenance shape that
387
+ * the unified sandbox event stream stamps on each event's `authLens` field.
388
+ *
389
+ * Absent ⇒ `{ mode: 'app-session' }` (the app's own session) — matching how
390
+ * `EventProvenance.authLens` reads when omitted. C1 added the field to events;
391
+ * T1 owns the EMIT path that actually writes it. T2's job is to thread the lens
392
+ * THROUGH the host so T1 has the value at emit time: see the call in `handleOp`
393
+ * where the resolved lens is passed to the sandbox via `withLens` when that
394
+ * emit seam exists. Today the sandbox event emitters infer `authLens` from the
395
+ * acting identity (the impersonation handle's frozen `auth` is `{ uid }`, so a
396
+ * rules eval already carries that uid); this helper exists so the host has a
397
+ * single canonical normaliser when the explicit emit-time stamp lands.
398
+ */
399
+ function lensProvenance(actAs) {
400
+ return actAs ?? { mode: 'app-session' };
401
+ }
402
+ /**
403
+ * Resolve the data handle for an op/sub carrying NO explicit lens: the
404
+ * PORT'S SESSION (#754). A signed-in port gets a per-uid cached
405
+ * `getFirestore(sandbox.withAuth(session.state))` handle — rules evaluate
406
+ * under its uid + custom claims, exactly like a globally signed-in user. A
407
+ * signed-out port falls back to `ctx.db` (sandbox-live; `currentUser` is
408
+ * never set in served mode, so that is the unauthenticated view).
409
+ *
410
+ * This is NOT the impersonation lens: the session was minted by validated
411
+ * sign-in on this port (`sandbox.mintSession`), so both reads and writes
412
+ * legitimately run as that user — the write-impersonation gate on the
413
+ * Studio `as` lens does not apply to a port's own session.
414
+ *
415
+ * Known staleness (same class as `lensHandles`): a claims change via
416
+ * `auth.adminUpdateUser` is not reflected in an existing session's handle
417
+ * until that user session is re-established (real Firebase behaves the same
418
+ * until token refresh).
419
+ */
420
+ function sessionDb(ctx, port) {
421
+ const session = portSession(ctx, port);
422
+ if (!session)
423
+ return ctx.db;
424
+ const cache = (ctx.sessionDbs ??= new Map());
425
+ let handle = cache.get(session.user.uid);
426
+ if (!handle) {
427
+ handle = pyricGetFirestore(ctx.sandbox.withAuth(session.state));
428
+ cache.set(session.user.uid, handle);
429
+ }
430
+ return handle;
431
+ }
432
+ function sessionRtdb(ctx, port) {
433
+ const session = portSession(ctx, port);
434
+ if (!session)
435
+ return ensureRtdb(ctx);
436
+ const cache = (ctx.sessionRtdbs ??= new Map());
437
+ let handle = cache.get(session.user.uid);
438
+ if (!handle) {
439
+ handle = pyricGetDatabase(ctx.sandbox.withAuth(session.state));
440
+ cache.set(session.user.uid, handle);
441
+ }
442
+ return handle;
443
+ }
444
+ function lensRtdb(ctx, actAs, port) {
445
+ if (!actAs || actAs.mode === 'app-session') {
446
+ return sessionRtdb(ctx, port);
447
+ }
448
+ if (actAs.mode === 'admin') {
449
+ return (ctx.adminRtdb ??= pyricGetAdminDatabase(ctx.sandbox));
450
+ }
451
+ // Genuinely unauthenticated — see the `anon` note on lensDb.
452
+ if (actAs.mode === 'anon') {
453
+ return (ctx.anonRtdb ??= pyricGetDatabase(ctx.sandbox.withAuth(null)));
454
+ }
455
+ const handles = (ctx.lensRtdbs ??= new Map());
456
+ const key = lensCacheKey(actAs);
457
+ let handle = handles.get(key);
458
+ if (!handle) {
459
+ handle = pyricGetDatabase(ctx.sandbox.withAuth(authStateForLens(actAs)));
460
+ handles.set(key, handle);
461
+ }
462
+ return handle;
463
+ }
464
+ // ─── Op handlers ──────────────────────────────────────────────────────────
465
+ /** The shared Storage handle, lazily created (Pyric Studio data browse): one per
466
+ * worker, over an app bound to the shared sandbox. The high-level
467
+ * `pyric/storage` ops enforce rules, so the host reads through them. */
468
+ function ensureStorage(ctx) {
469
+ return (ctx.storage ??= getStorage(initializeApp({ sandbox: ctx.sandbox })));
470
+ }
471
+ /**
472
+ * Resolve the Storage handle a storage op runs against, given its `actAs`
473
+ * lens — the storage mirror of {@link lensRtdb}:
474
+ *
475
+ * - absent / `app-session` → the shared anonymous page handle
476
+ * ({@link ensureStorage}). Storage rules apply only when the HOST
477
+ * configured them on this sandbox's storage service (first call per
478
+ * sandbox wins) — the SERVED worker currently configures none
479
+ * (`setRules`/`setDatabaseRules` cover Firestore/RTDB only), so
480
+ * worker-mode storage is effectively open today and all lenses behave
481
+ * alike there. The lens split matters for embedding/test hosts that
482
+ * pre-open the service with rules. (Storage also has no per-port
483
+ * session plumbing — reads always ran anonymous; writes keep that.)
484
+ * - `{ mode: 'admin' }` → the rules-BYPASS handle from
485
+ * `pyric/storage/internal`'s admin plane — same per-sandbox store +
486
+ * ruleset, rule evaluation skipped (firebase-admin semantics for the
487
+ * `pyric-admin` remote arm / Studio admin lens).
488
+ * - `{ mode: 'as', uid }` → a frozen-identity `getStorageSandbox(
489
+ * sandbox.withAuth({ uid, token? }))` handle; rules evaluate AS that
490
+ * user. Cached per uid/token key on `ctx.lensStorages`.
491
+ */
492
+ function lensStorage(ctx, actAs) {
493
+ if (!actAs || actAs.mode === 'app-session') {
494
+ return ensureStorage(ctx);
495
+ }
496
+ if (actAs.mode === 'admin') {
497
+ return (ctx.adminStorage ??= getAdminStorageSandbox(ctx.sandbox));
498
+ }
499
+ // Genuinely unauthenticated — see the `anon` note on lensDb. Distinct from
500
+ // the shared page handle only when the host configured storage rules, but
501
+ // pinning it keeps remote `withAuth(null)` semantics uniform across services.
502
+ if (actAs.mode === 'anon') {
503
+ return (ctx.anonStorage ??= getStorageSandbox(ctx.sandbox.withAuth(null)));
504
+ }
505
+ const handles = (ctx.lensStorages ??= new Map());
506
+ const key = lensCacheKey(actAs);
507
+ let handle = handles.get(key);
508
+ if (!handle) {
509
+ handle = getStorageSandbox(ctx.sandbox.withAuth(authStateForLens(actAs)));
510
+ handles.set(key, handle);
511
+ }
512
+ return handle;
513
+ }
514
+ /**
515
+ * Map a wire `storage.putBytes` payload to `pyric/storage`'s
516
+ * `SettableMetadata`. The explicit `contentType` field wins; recognized
517
+ * settable fields are lifted from `metadata`; a GCS-style nested custom map
518
+ * (`metadata.metadata`, as `@google-cloud/storage`'s `save` spells it) or a
519
+ * pyric-style `metadata.customMetadata` becomes `customMetadata` with
520
+ * values coerced to strings (the storage-rules `metadata` model).
521
+ */
522
+ function toSettableMetadata(msg) {
523
+ const md = msg.metadata ?? {};
524
+ const str = (key) => typeof md[key] === 'string' ? md[key] : undefined;
525
+ const customSource = md['customMetadata'] ?? md['metadata'];
526
+ let customMetadata;
527
+ if (customSource !== null && typeof customSource === 'object' && !Array.isArray(customSource)) {
528
+ customMetadata = {};
529
+ for (const [k, v] of Object.entries(customSource)) {
530
+ customMetadata[k] = String(v);
531
+ }
532
+ }
533
+ const settable = {};
534
+ const contentType = msg.contentType ?? str('contentType');
535
+ if (contentType !== undefined)
536
+ settable.contentType = contentType;
537
+ const cacheControl = str('cacheControl');
538
+ if (cacheControl !== undefined)
539
+ settable.cacheControl = cacheControl;
540
+ const contentDisposition = str('contentDisposition');
541
+ if (contentDisposition !== undefined)
542
+ settable.contentDisposition = contentDisposition;
543
+ const contentEncoding = str('contentEncoding');
544
+ if (contentEncoding !== undefined)
545
+ settable.contentEncoding = contentEncoding;
546
+ const contentLanguage = str('contentLanguage');
547
+ if (contentLanguage !== undefined)
548
+ settable.contentLanguage = contentLanguage;
549
+ if (customMetadata !== undefined)
550
+ settable.customMetadata = customMetadata;
551
+ return settable;
552
+ }
553
+ function ensureRtdb(ctx) {
554
+ return (ctx.rtdb ??= pyricGetDatabase(ctx.sandbox));
555
+ }
556
+ function rtdbSnapToWire(snap) {
557
+ return {
558
+ key: snap.key,
559
+ exists: snap.exists(),
560
+ value: snap.val(),
561
+ size: snap.size,
562
+ };
563
+ }
564
+ function normalizeDatabaseRules(source) {
565
+ if (source === null)
566
+ return null;
567
+ if (typeof source === 'string') {
568
+ return JSON.parse(source);
569
+ }
570
+ if (typeof source === 'object' && source !== null) {
571
+ return source;
572
+ }
573
+ throw new Error('RTDB rules must be a rules JSON object or JSON string.');
574
+ }
575
+ function firestoreRuleMessages(result) {
576
+ const messages = [];
577
+ if (result.parseError) {
578
+ messages.push({
579
+ severity: 'error',
580
+ text: `PARSE ERROR: expected ${String(result.parseError.expected ?? 'valid rules')}`,
581
+ line: result.parseError.line,
582
+ column: result.parseError.column,
583
+ });
584
+ }
585
+ for (const warning of result.warnings ?? []) {
586
+ messages.push({
587
+ severity: warning.severity === 'error' ? 'error' : warning.severity === 'warning' ? 'warn' : 'info',
588
+ text: String(warning.message ?? warning),
589
+ });
590
+ }
591
+ return messages;
592
+ }
593
+ async function handleOp(ctx, port, msg) {
594
+ const { sandbox } = ctx;
595
+ // Explicit lens (Studio admin / as / app-session) → lensDb; no lens → the
596
+ // PORT'S SESSION (#754), so app ops run as whoever this tab signed in as.
597
+ const db = msg.actAs ? lensDb(ctx, msg.actAs) : sessionDb(ctx, port);
598
+ // Provenance the op runs under. Stamped onto the unified event stream's
599
+ // `authLens` by the emit path (C1 field / T1 emit). For `{ mode: 'as', uid }`
600
+ // the resolved `db` already carries `auth: { uid }`, so a rules eval emits
601
+ // under that identity; `lens` is the canonical normalised value the host
602
+ // hands forward when the explicit emit-time stamp seam exists (see lensProvenance).
603
+ const lens = lensProvenance(msg.actAs);
604
+ void lens;
605
+ switch (msg.method) {
606
+ case 'getDoc': {
607
+ try {
608
+ const ref = pyricDoc(db, msg.path);
609
+ const snap = await getDoc(ref);
610
+ ok(port, msg.id, serializeDocSnap(snap));
611
+ }
612
+ catch (e) {
613
+ fail(port, msg.id, e);
614
+ }
615
+ break;
616
+ }
617
+ case 'getDocs': {
618
+ try {
619
+ const source = resolveTarget(db, msg.source);
620
+ // CollectionReference is always also queryable — getDocs accepts Query<T>
621
+ // and CollectionReference is structurally compatible at runtime even though
622
+ // the type system doesn't know that (CollectionReference has no `_isQuery`
623
+ // brand). Cast through Query to satisfy the type checker.
624
+ const snap = await getDocs(source);
625
+ const docs = snap.docs.map((d) => serializeDocSnap(d));
626
+ ok(port, msg.id, { docs });
627
+ }
628
+ catch (e) {
629
+ fail(port, msg.id, e);
630
+ }
631
+ break;
632
+ }
633
+ case 'setDoc': {
634
+ try {
635
+ const ref = pyricDoc(db, msg.path);
636
+ const data = prepareWriteData(msg.data);
637
+ await setDoc(ref, data, msg.options);
638
+ await bestEffortFlush(ctx);
639
+ ok(port, msg.id, null);
640
+ }
641
+ catch (e) {
642
+ fail(port, msg.id, e);
643
+ }
644
+ break;
645
+ }
646
+ case 'updateDoc': {
647
+ try {
648
+ const ref = pyricDoc(db, msg.path);
649
+ const data = prepareWriteData(msg.data);
650
+ await updateDoc(ref, data);
651
+ await bestEffortFlush(ctx);
652
+ ok(port, msg.id, null);
653
+ }
654
+ catch (e) {
655
+ fail(port, msg.id, e);
656
+ }
657
+ break;
658
+ }
659
+ case 'deleteDoc': {
660
+ try {
661
+ const ref = pyricDoc(db, msg.path);
662
+ await deleteDoc(ref);
663
+ await bestEffortFlush(ctx);
664
+ ok(port, msg.id, null);
665
+ }
666
+ catch (e) {
667
+ fail(port, msg.id, e);
668
+ }
669
+ break;
670
+ }
671
+ case 'addDoc': {
672
+ try {
673
+ const coll = pyricCollection(db, msg.collectionPath);
674
+ const data = prepareWriteData(msg.data);
675
+ const ref = await addDoc(coll, data);
676
+ await bestEffortFlush(ctx);
677
+ ok(port, msg.id, { id: ref.id, path: ref.path });
678
+ }
679
+ catch (e) {
680
+ fail(port, msg.id, e);
681
+ }
682
+ break;
683
+ }
684
+ case 'count': {
685
+ try {
686
+ const source = resolveTarget(db, msg.source);
687
+ const snap = await getCountFromServer(source);
688
+ ok(port, msg.id, { count: snap.data().count });
689
+ }
690
+ catch (e) {
691
+ fail(port, msg.id, e);
692
+ }
693
+ break;
694
+ }
695
+ case 'aggregate': {
696
+ // Multi-field aggregates (count/sum/average). The wire spec is
697
+ // structurally pyric/firestore's AggregateSpec, so it passes straight
698
+ // through; the reply data is plain numbers / null (empty-input average).
699
+ try {
700
+ const source = resolveTarget(db, msg.source);
701
+ const snap = await getAggregateFromServer(source, msg.spec);
702
+ ok(port, msg.id, { data: snap.data() });
703
+ }
704
+ catch (e) {
705
+ fail(port, msg.id, e);
706
+ }
707
+ break;
708
+ }
709
+ case 'listRootCollections': {
710
+ // Keyspace enumeration (Pyric Studio data browse). Lens-independent: it
711
+ // lists the collection ids present, not rule-gated reads.
712
+ try {
713
+ ok(port, msg.id, { ids: getInternalEnv(sandbox).listRootCollections() });
714
+ }
715
+ catch (e) {
716
+ fail(port, msg.id, e);
717
+ }
718
+ break;
719
+ }
720
+ case 'listSubcollections': {
721
+ try {
722
+ ok(port, msg.id, { ids: getInternalEnv(sandbox).listSubcollections(msg.docPath) });
723
+ }
724
+ catch (e) {
725
+ fail(port, msg.id, e);
726
+ }
727
+ break;
728
+ }
729
+ case 'batchCommit': {
730
+ /**
731
+ * WHY: writeBatch in pyric/firestore buffers set/update/delete calls
732
+ * and commits them as one unit through the sandbox backend. We
733
+ * reconstruct the batch here from the wire write descriptors.
734
+ * Atomicity guarantee: the sandbox backend applies all writes or none
735
+ * (per-collection lock semantics of LocalEnvironment.batch()).
736
+ */
737
+ try {
738
+ const batch = writeBatch(db);
739
+ for (const w of msg.writes) {
740
+ applyWriteToBatch(db, batch, w);
741
+ }
742
+ await batch.commit();
743
+ await bestEffortFlush(ctx);
744
+ ok(port, msg.id, null);
745
+ }
746
+ catch (e) {
747
+ fail(port, msg.id, e);
748
+ }
749
+ break;
750
+ }
751
+ case 'txnCommit': {
752
+ /**
753
+ * READ-SET VALIDATION FOR MULTI-TAB CORRECTNESS
754
+ * -----------------------------------------------
755
+ * The client sends `{ method: 'txnCommit', reads, writes }` after its
756
+ * `updateFn` callback has resolved. `reads` is the set of docs the
757
+ * client read during `updateFn` (each entry is `{ path, data }` where
758
+ * `data` is the serialized snapshot the client saw, or null if the doc
759
+ * was missing).
760
+ *
761
+ * WHY READ-SET VALIDATION IS NECESSARY
762
+ * -------------------------------------
763
+ * A transaction spans two messages: the client's `txn.get` RPC (sent
764
+ * during `updateFn`) and this `txnCommit` RPC. Between those messages
765
+ * the event loop is free — another tab's write to any of the read docs
766
+ * can land in that gap via a separate `setDoc` message. Without
767
+ * validation that would be a silent lost update: the client computed
768
+ * its writes using stale data, committed them, and the other tab's
769
+ * changes were silently overwritten.
770
+ *
771
+ * HOW WE VALIDATE (no per-doc version/updateTime in the sandbox)
772
+ * ---------------------------------------------------------------
773
+ * Inside a real `runTransaction(db, callback)`, the callback re-reads
774
+ * every doc from the read-set via `tx.get(ref)`, serializes its current
775
+ * data to the same JSON form the client produced (using `serializeDocData`
776
+ * on both sides), and compares the JSON strings. If any doc's current
777
+ * serialized form differs from what the client recorded, another tab
778
+ * wrote it between the client's read and this commit — we throw an abort
779
+ * sentinel so the sandbox rolls back the transaction, and we return
780
+ * `{ ok: false, error: { code: 'aborted' } }` on the wire. The client's
781
+ * retry loop then re-runs `updateFn` with fresh reads.
782
+ *
783
+ * SERIALIZED-FORM EQUALITY — VIA THE CANONICAL CODEC
784
+ * ---------------------------------------------------
785
+ * Both JSON strings are CANONICALIZED before comparison:
786
+ * `JSON.stringify(rehydrateDocValue(JSON.parse(json)))`. Raw string
787
+ * equality is NOT safe here even within one process, because the two
788
+ * read paths yield DIFFERENT wrapper classes for the same stored
789
+ * value: `getDoc` (what the client's read-set echoes) returns
790
+ * `firebase/firestore` classes whose `Timestamp.toJSON()` emits
791
+ * `{ seconds, nanoseconds, type }`, while the transaction's
792
+ * validation re-read comes through the admin-compat wrapper whose
793
+ * `Timestamp.toJSON()` emits `{ type, seconds, nanoseconds }` — same
794
+ * value, different key order, different string. Rehydrating both
795
+ * sides through the ONE shared codec (`pyric/firestore-values`)
796
+ * collapses every marker family into the same wrapper classes with
797
+ * deterministic `toJSON()` key order, so string equality ↔ value
798
+ * equality again — an unmodified doc can never phantom-abort (which
799
+ * would livelock the client's retry loop), while a real concurrent
800
+ * write still mismatches. Plain-map key order is preserved by both
801
+ * paths from the same stored object, so it stays comparable.
802
+ *
803
+ * NOTE ON tx.set/update/delete SIGNATURE
804
+ * ---------------------------------------
805
+ * The modular pyric/firestore Transaction (wrapping the admin-compat
806
+ * TransactionWrapper) takes `tx.set(docRef, data)` where `docRef` is a
807
+ * real `DocumentReference` — NOT a path string. We build refs via
808
+ * `pyricDoc(db, path)` for each write.
809
+ */
810
+ /** Sentinel thrown inside the transaction callback to signal an abort. */
811
+ const TXN_ABORT = Symbol('txn-abort');
812
+ /**
813
+ * Canonicalize a serialized doc-data JSON string (see
814
+ * SERIALIZED-FORM EQUALITY above). Two normalization passes:
815
+ *
816
+ * 1. `rehydrateDocValue` collapses the marker families (`__type`
817
+ * persistence markers and `firebase/firestore` `toJSON()`
818
+ * markers) into the one set of wrapper classes, whose
819
+ * `toJSON()` re-emits a single deterministic form.
820
+ * 2. The stringify replacer additionally normalizes PROTOTYPE-
821
+ * STRIPPED wrapper clones — the sandbox transaction's
822
+ * capture-by-value `structuredClone` turns a stored rules
823
+ * wrapper into a plain `{ typeName, … }` object that neither
824
+ * marker family matches, so without this pass an unmodified
825
+ * typed doc would never compare equal to the client's getDoc
826
+ * echo (a guaranteed phantom abort → retry livelock).
827
+ */
828
+ const canonicalDocJson = (json) => JSON.stringify(rehydrateDocValue(JSON.parse(json)), (_key, v) => {
829
+ if (v === null || typeof v !== 'object' || Array.isArray(v))
830
+ return v;
831
+ const o = v;
832
+ if (typeof o.typeName !== 'string' || o.__type !== undefined)
833
+ return v;
834
+ // Re-shape a stripped rules-wrapper clone into the wrapper's own
835
+ // canonical toJSON marker form (kept in sync with pyric/rules'
836
+ // simulator/wrappers/* instance fields + toJSON()) — but ONLY
837
+ // when the key set EXACTLY matches that wrapper's own-field
838
+ // shape. A looser match would silently DROP extra keys from
839
+ // user data that merely resembles a clone, collapsing two
840
+ // genuinely different docs into one canonical form and letting
841
+ // a concurrent write commit undetected (false equality). A
842
+ // near-miss map passes through unchanged — worst case is a
843
+ // spurious abort + retry, never a lost update.
844
+ switch (o.typeName) {
845
+ case 'timestamp':
846
+ return hasExactKeys(o, TS_CLONE_KEYS)
847
+ ? { __type: 'timestamp', seconds: o.seconds, nanos: o.nanos }
848
+ : v;
849
+ case 'duration':
850
+ return hasExactKeys(o, TS_CLONE_KEYS)
851
+ ? { __type: 'duration', seconds: o.seconds, nanos: o.nanos }
852
+ : v;
853
+ case 'latlng':
854
+ return hasExactKeys(o, ['typeName', 'lat', 'lng'])
855
+ ? { __type: 'latlng', lat: o.lat, lng: o.lng }
856
+ : v;
857
+ case 'reference':
858
+ return hasExactKeys(o, ['typeName', 'path'])
859
+ ? { __type: 'reference', path: o.path }
860
+ : v;
861
+ case 'path':
862
+ return hasExactKeys(o, ['typeName', 'segments', 'bindings'])
863
+ ? { __type: 'path', segments: o.segments }
864
+ : v;
865
+ case 'bytes':
866
+ // The Uint8Array field serialized as an index-keyed map;
867
+ // rebuild and emit Bytes.toJSON()'s base64url form.
868
+ return hasExactKeys(o, ['typeName', 'data'])
869
+ ? { __type: 'bytes', base64: indexMapToBase64Url(o.data) }
870
+ : v;
871
+ default:
872
+ return v;
873
+ }
874
+ });
875
+ try {
876
+ await runTransaction(db, async (tx) => {
877
+ // ── Step 1: validate the read-set ──────────────────────────────
878
+ // Re-read each doc the client touched and compare its current
879
+ // serialized form against what the client recorded at read time.
880
+ const modularTx = tx;
881
+ for (const r of msg.reads) {
882
+ const ref = pyricDoc(db, r.path);
883
+ const currentSnap = await modularTx.get(ref);
884
+ const existsBool = typeof currentSnap.exists === 'function'
885
+ ? currentSnap.exists()
886
+ : currentSnap.exists;
887
+ // Compute the serialized form of the current doc state.
888
+ const currentSerialized = existsBool && currentSnap.data()
889
+ ? serializeDocData(currentSnap.data())
890
+ : null;
891
+ // Compare against what the client recorded:
892
+ // both null → ok (doc still doesn't exist)
893
+ // both present + same JSON → ok
894
+ // anything else → conflict
895
+ const clientHadNull = r.data === null;
896
+ const workerHasNull = currentSerialized === null;
897
+ if (clientHadNull !== workerHasNull) {
898
+ // Existence changed (created or deleted by another tab).
899
+ throw TXN_ABORT;
900
+ }
901
+ if (!clientHadNull &&
902
+ !workerHasNull &&
903
+ canonicalDocJson(r.data.json) !== canonicalDocJson(currentSerialized.json)) {
904
+ // Data changed by another tab.
905
+ throw TXN_ABORT;
906
+ }
907
+ }
908
+ // ── Step 2: apply the queued writes ────────────────────────────
909
+ for (const w of msg.writes) {
910
+ const ref = pyricDoc(db, w.path);
911
+ if (w.method === 'set') {
912
+ const data = prepareWriteData(w.data);
913
+ modularTx.set(ref, data, w.options);
914
+ }
915
+ else if (w.method === 'update') {
916
+ const data = prepareWriteData(w.data);
917
+ modularTx.update(ref, data);
918
+ }
919
+ else if (w.method === 'delete') {
920
+ modularTx.delete(ref);
921
+ }
922
+ }
923
+ });
924
+ await bestEffortFlush(ctx);
925
+ ok(port, msg.id, null);
926
+ }
927
+ catch (e) {
928
+ if (e === TXN_ABORT) {
929
+ // Read-set conflict — signal the client to retry its updateFn.
930
+ const abortErr = { code: 'aborted', message: 'Transaction read-set conflict: a concurrent write invalidated the read snapshot.' };
931
+ post(port, { t: 'res', id: msg.id, ok: false, error: abortErr });
932
+ }
933
+ else {
934
+ fail(port, msg.id, e);
935
+ }
936
+ }
937
+ break;
938
+ }
939
+ case 'setRules':
940
+ case 'setFirestoreRules': {
941
+ try {
942
+ const result = sandboxOps.setRules(db, msg.source);
943
+ const messages = firestoreRuleMessages(result);
944
+ const okDeploy = !messages.some((m) => m.severity === 'error');
945
+ ctx.activeRules ??= {};
946
+ const previous = ctx.activeRules.firestore?.status === 'active'
947
+ ? ctx.activeRules.firestore.source
948
+ : ctx.activeRules.firestore?.lastKnownGood;
949
+ ctx.activeRules.firestore = {
950
+ source: okDeploy ? msg.source : ctx.activeRules.firestore?.source ?? msg.source,
951
+ updatedAt: Date.now(),
952
+ status: okDeploy ? 'active' : 'error',
953
+ messages,
954
+ ...(previous ? { lastKnownGood: previous } : {}),
955
+ };
956
+ ok(port, msg.id, { warnings: result.warnings, messages, ok: okDeploy });
957
+ }
958
+ catch (e) {
959
+ fail(port, msg.id, e);
960
+ }
961
+ break;
962
+ }
963
+ case 'setDatabaseRules': {
964
+ try {
965
+ const db = ensureRtdb(ctx);
966
+ const rules = normalizeDatabaseRules(msg.source);
967
+ const previous = ctx.activeRules?.database?.status === 'active'
968
+ ? ctx.activeRules.database.source
969
+ : ctx.activeRules?.database?.lastKnownGood;
970
+ rtdbSandbox.setRules(db, rules);
971
+ ctx.activeRules ??= {};
972
+ ctx.activeRules.database = {
973
+ source: rules,
974
+ updatedAt: Date.now(),
975
+ status: 'active',
976
+ messages: [],
977
+ ...(previous ? { lastKnownGood: previous } : {}),
978
+ };
979
+ ok(port, msg.id, { ok: true, messages: [] });
980
+ }
981
+ catch (e) {
982
+ fail(port, msg.id, e);
983
+ }
984
+ break;
985
+ }
986
+ case 'getActiveRules': {
987
+ ok(port, msg.id, msg.service ? ctx.activeRules?.[msg.service] ?? null : ctx.activeRules ?? {});
988
+ break;
989
+ }
990
+ case 'getRulesStatus': {
991
+ ok(port, msg.id, msg.service ? ctx.activeRules?.[msg.service] ?? null : ctx.activeRules ?? {});
992
+ break;
993
+ }
994
+ case 'admin.getDocument': {
995
+ try {
996
+ ok(port, msg.id, ctx.sandbox.admin.getDocument(msg.path));
997
+ }
998
+ catch (e) {
999
+ fail(port, msg.id, e);
1000
+ }
1001
+ break;
1002
+ }
1003
+ case 'admin.listDocuments': {
1004
+ try {
1005
+ ok(port, msg.id, ctx.sandbox.admin.listDocuments(msg.path));
1006
+ }
1007
+ catch (e) {
1008
+ fail(port, msg.id, e);
1009
+ }
1010
+ break;
1011
+ }
1012
+ case 'admin.setDocument': {
1013
+ try {
1014
+ ctx.sandbox.admin.setDocument(msg.path, msg.data);
1015
+ await bestEffortFlush(ctx);
1016
+ ok(port, msg.id, null);
1017
+ }
1018
+ catch (e) {
1019
+ fail(port, msg.id, e);
1020
+ }
1021
+ break;
1022
+ }
1023
+ case 'admin.deleteDocument': {
1024
+ try {
1025
+ const deleted = ctx.sandbox.admin.deleteDocument(msg.path);
1026
+ await bestEffortFlush(ctx);
1027
+ ok(port, msg.id, deleted);
1028
+ }
1029
+ catch (e) {
1030
+ fail(port, msg.id, e);
1031
+ }
1032
+ break;
1033
+ }
1034
+ case 'admin.readState': {
1035
+ try {
1036
+ const snap = sandboxOps.snapshotState(ctx.adminDb ?? lensDb(ctx, { mode: 'admin' }));
1037
+ const out = {};
1038
+ const prefix = msg.path ?? '';
1039
+ for (const [path, data] of Object.entries(snap)) {
1040
+ if (prefix && !path.startsWith(prefix))
1041
+ continue;
1042
+ if (msg.maxDepth !== undefined && path.split('/').length > msg.maxDepth)
1043
+ continue;
1044
+ out[path] = data;
1045
+ }
1046
+ ok(port, msg.id, out);
1047
+ }
1048
+ catch (e) {
1049
+ fail(port, msg.id, e);
1050
+ }
1051
+ break;
1052
+ }
1053
+ case 'rtdb.get': {
1054
+ try {
1055
+ const db = lensRtdb(ctx, msg.actAs, port);
1056
+ ok(port, msg.id, rtdbSnapToWire(await rtdbGet(rtdbRef(db, msg.path))));
1057
+ }
1058
+ catch (e) {
1059
+ fail(port, msg.id, e);
1060
+ }
1061
+ break;
1062
+ }
1063
+ case 'rtdb.set': {
1064
+ try {
1065
+ const db = lensRtdb(ctx, msg.actAs, port);
1066
+ const value = resolveRtdbSentinels(msg.value);
1067
+ await rtdbSet(rtdbRef(db, msg.path), value);
1068
+ await bestEffortFlush(ctx);
1069
+ ok(port, msg.id, null);
1070
+ }
1071
+ catch (e) {
1072
+ fail(port, msg.id, e);
1073
+ }
1074
+ break;
1075
+ }
1076
+ case 'rtdb.update': {
1077
+ try {
1078
+ const db = lensRtdb(ctx, msg.actAs, port);
1079
+ await rtdbUpdate(rtdbRef(db, msg.path), resolveRtdbSentinels(msg.values));
1080
+ await bestEffortFlush(ctx);
1081
+ ok(port, msg.id, null);
1082
+ }
1083
+ catch (e) {
1084
+ fail(port, msg.id, e);
1085
+ }
1086
+ break;
1087
+ }
1088
+ case 'rtdb.remove': {
1089
+ try {
1090
+ const db = lensRtdb(ctx, msg.actAs, port);
1091
+ await rtdbRemove(rtdbRef(db, msg.path));
1092
+ await bestEffortFlush(ctx);
1093
+ ok(port, msg.id, null);
1094
+ }
1095
+ catch (e) {
1096
+ fail(port, msg.id, e);
1097
+ }
1098
+ break;
1099
+ }
1100
+ case 'rtdb.push': {
1101
+ try {
1102
+ const db = lensRtdb(ctx, msg.actAs, port);
1103
+ const childPath = `${msg.path}/${msg.key}`;
1104
+ if (msg.value !== undefined) {
1105
+ await rtdbSet(rtdbRef(db, childPath), resolveRtdbSentinels(msg.value));
1106
+ await bestEffortFlush(ctx);
1107
+ }
1108
+ const normalizedPath = `/${childPath.split('/').filter(Boolean).join('/')}`;
1109
+ ok(port, msg.id, { key: msg.key, path: normalizedPath });
1110
+ }
1111
+ catch (e) {
1112
+ fail(port, msg.id, e);
1113
+ }
1114
+ break;
1115
+ }
1116
+ case 'rtdb.adminSnapshot': {
1117
+ try {
1118
+ ok(port, msg.id, rtdbSandbox.snapshotState(lensRtdb(ctx, { mode: 'admin' }, port)));
1119
+ }
1120
+ catch (e) {
1121
+ fail(port, msg.id, e);
1122
+ }
1123
+ break;
1124
+ }
1125
+ case 'getVersion': {
1126
+ // The build hash is injected by the bundler (esbuild `define`). `typeof`
1127
+ // guards the non-bundled path (tests import the compiled host directly,
1128
+ // where the global is undefined) → reports 'dev'.
1129
+ ok(port, msg.id, {
1130
+ version: typeof __PYRIC_WORKER_VERSION__ !== 'undefined' ? __PYRIC_WORKER_VERSION__ : 'dev',
1131
+ instanceId: ctx.instanceId,
1132
+ });
1133
+ break;
1134
+ }
1135
+ case 'exportState': {
1136
+ // Phase 2 (transfer): serialize the FULL sandbox state to a portable bundle
1137
+ // string using the SAME chunk format the persist layer uses, so wrapper
1138
+ // types (Timestamp / Bytes / GeoPoint / VectorValue) round-trip. The string
1139
+ // crosses the MessagePort cleanly (unlike the raw snapshot object).
1140
+ const snap = ctx.sandbox.snapshot();
1141
+ ok(port, msg.id, { bundle: bundleRecords(serializeToBuckets(snap.firestore, snap.services, 0)) });
1142
+ break;
1143
+ }
1144
+ case 'importState': {
1145
+ // Phase 2 (clobber): replace this sandbox's ENTIRE state with the imported
1146
+ // bundle via the public loadSnapshot() (reset + rebuild firestore + restore
1147
+ // services; listeners re-evaluate, persist re-flushes).
1148
+ ctx.sandbox.loadSnapshot(deserializeFromBuckets(parseBundle(msg.bundle)));
1149
+ ok(port, msg.id, { ok: true });
1150
+ break;
1151
+ }
1152
+ case 'saveBranch': {
1153
+ // Phase 3: snapshot the live sandbox into a named branch bundle (raw idb).
1154
+ if (!ctx.sessionBackend) {
1155
+ ok(port, msg.id, { ok: false, error: 'no persistence backend' });
1156
+ break;
1157
+ }
1158
+ const snap = ctx.sandbox.snapshot();
1159
+ const bundle = bundleRecords(serializeToBuckets(snap.firestore, snap.services, 0));
1160
+ await ctx.sessionBackend.putRecords(BRANCH_PREFIX + msg.name, new Map([['bundle', { value: bundle }]]));
1161
+ const names = await listBranchNames(ctx.sessionBackend);
1162
+ if (!names.includes(msg.name))
1163
+ await writeBranchRegistry(ctx.sessionBackend, [...names, msg.name]);
1164
+ ok(port, msg.id, { ok: true });
1165
+ break;
1166
+ }
1167
+ case 'listBranches': {
1168
+ ok(port, msg.id, { branches: await listBranchNames(ctx.sessionBackend) });
1169
+ break;
1170
+ }
1171
+ case 'switchBranch': {
1172
+ // Phase 3: loadSnapshot the named branch bundle (a clobber).
1173
+ const rec = ctx.sessionBackend
1174
+ ? (await ctx.sessionBackend.getRecord(BRANCH_PREFIX + msg.name, 'bundle'))
1175
+ : undefined;
1176
+ if (!rec?.value) {
1177
+ ok(port, msg.id, { ok: false, error: `no such branch: ${msg.name}` });
1178
+ break;
1179
+ }
1180
+ ctx.sandbox.loadSnapshot(deserializeFromBuckets(parseBundle(rec.value)));
1181
+ ok(port, msg.id, { ok: true });
1182
+ break;
1183
+ }
1184
+ case 'deleteBranch': {
1185
+ if (ctx.sessionBackend) {
1186
+ await ctx.sessionBackend.clear(BRANCH_PREFIX + msg.name);
1187
+ await writeBranchRegistry(ctx.sessionBackend, (await listBranchNames(ctx.sessionBackend)).filter((n) => n !== msg.name));
1188
+ }
1189
+ ok(port, msg.id, { ok: true });
1190
+ break;
1191
+ }
1192
+ case 'setPolicy': {
1193
+ // Store the dial's PolicyRequest as the worker-side runtime governance
1194
+ // (Pyric Studio F3). This is the source of truth Studio reflects + a
1195
+ // future in-worker agent runtime consults. It does NOT push into a
1196
+ // running bridge process — see the limitation note on `ctx.policy` /
1197
+ // `PolicyRequest`. Additive + idempotent: last write wins.
1198
+ ctx.policy = msg.policy;
1199
+ ok(port, msg.id, null);
1200
+ break;
1201
+ }
1202
+ case 'getPolicy': {
1203
+ // Read back the active runtime policy (null until the dial set one), so
1204
+ // Studio can reflect persisted state across reconnects within a worker
1205
+ // lifetime and a freshly-connecting port can hydrate the dial.
1206
+ ok(port, msg.id, ctx.policy ?? null);
1207
+ break;
1208
+ }
1209
+ case 'getSnapshot': {
1210
+ // Export the current sandbox snapshot (Pyric Studio rules re-run): Studio
1211
+ // forks it locally to test edited rules / re-issue as the user on a branch.
1212
+ ok(port, msg.id, ctx.sandbox.snapshot());
1213
+ break;
1214
+ }
1215
+ case 'storage.listAll': {
1216
+ // Object browse. `listAll` enforces `read` rules on the scanned prefix
1217
+ // under the op's lens (admin lens bypasses — see lensStorage).
1218
+ try {
1219
+ const storage = lensStorage(ctx, msg.actAs);
1220
+ const result = await storageListAll(storageRef(storage, msg.path));
1221
+ ok(port, msg.id, {
1222
+ items: result.items.map((r) => ({ fullPath: r.fullPath, name: r.name })),
1223
+ prefixes: result.prefixes.map((r) => ({ fullPath: r.fullPath, name: r.name })),
1224
+ });
1225
+ }
1226
+ catch (e) {
1227
+ fail(port, msg.id, e);
1228
+ }
1229
+ break;
1230
+ }
1231
+ case 'storage.getMetadata': {
1232
+ try {
1233
+ const storage = lensStorage(ctx, msg.actAs);
1234
+ // FullMetadata is plain JSON (bucket/fullPath/name/size/contentType/...).
1235
+ ok(port, msg.id, await storageGetMetadata(storageRef(storage, msg.path)));
1236
+ }
1237
+ catch (e) {
1238
+ fail(port, msg.id, e);
1239
+ }
1240
+ break;
1241
+ }
1242
+ case 'storage.getBlob': {
1243
+ // MessagePort-ONLY: the Blob structured-clones to in-page callers
1244
+ // (Studio previews) but silently corrupts under the JSON WS relay — the
1245
+ // bridge client rejects relaying it (binary-payload guard). Remote
1246
+ // callers use `storage.getBytes` (base64) instead.
1247
+ try {
1248
+ const storage = lensStorage(ctx, msg.actAs);
1249
+ ok(port, msg.id, await storageGetBlob(storageRef(storage, msg.path)));
1250
+ }
1251
+ catch (e) {
1252
+ fail(port, msg.id, e);
1253
+ }
1254
+ break;
1255
+ }
1256
+ case 'storage.putBytes': {
1257
+ // Byte upload (remote sandbox, slice 2). Decode-end size cap: reject an
1258
+ // oversized base64 string BEFORE materializing its bytes, then re-check
1259
+ // the exact decoded length. Rules enforce under the op's lens; the
1260
+ // upload emits `service_mutation` events, so server writes get Studio
1261
+ // provenance for free.
1262
+ try {
1263
+ if (msg.dataB64.length > MAX_STORAGE_OP_B64_LENGTH) {
1264
+ throw storagePayloadTooLarge(Math.floor(msg.dataB64.length * 0.75), `storage.putBytes payload for '${msg.path}'`);
1265
+ }
1266
+ const bytes = base64ToBytes(msg.dataB64);
1267
+ if (bytes.byteLength > MAX_STORAGE_OP_BYTES) {
1268
+ throw storagePayloadTooLarge(bytes.byteLength, `storage.putBytes payload for '${msg.path}'`);
1269
+ }
1270
+ const storage = lensStorage(ctx, msg.actAs);
1271
+ const result = await storageUploadBytes(storageRef(storage, msg.path), bytes, toSettableMetadata(msg));
1272
+ // FullMetadata — plain JSON, relay-safe.
1273
+ ok(port, msg.id, result.metadata);
1274
+ }
1275
+ catch (e) {
1276
+ fail(port, msg.id, e);
1277
+ }
1278
+ break;
1279
+ }
1280
+ case 'storage.getBytes': {
1281
+ // JSON-safe byte download (remote sandbox, slice 2): base64 in the
1282
+ // result. Encode-end size cap so a big browser-side object can't blow
1283
+ // up the relay. Metadata is read alongside for contentType (both reads
1284
+ // run under the same lens; rule-eval order keeps `unauthorized`
1285
+ // superseding `not-found`, matching pyric/storage).
1286
+ try {
1287
+ const storage = lensStorage(ctx, msg.actAs);
1288
+ const r = storageRef(storage, msg.path);
1289
+ const meta = await storageGetMetadata(r);
1290
+ if (meta.size > MAX_STORAGE_OP_BYTES) {
1291
+ throw storagePayloadTooLarge(meta.size, `object '${msg.path}'`);
1292
+ }
1293
+ const buf = await storageGetBytes(r);
1294
+ if (buf.byteLength > MAX_STORAGE_OP_BYTES) {
1295
+ throw storagePayloadTooLarge(buf.byteLength, `object '${msg.path}'`);
1296
+ }
1297
+ ok(port, msg.id, {
1298
+ dataB64: bytesToBase64(new Uint8Array(buf)),
1299
+ contentType: meta.contentType,
1300
+ size: buf.byteLength,
1301
+ });
1302
+ }
1303
+ catch (e) {
1304
+ fail(port, msg.id, e);
1305
+ }
1306
+ break;
1307
+ }
1308
+ case 'storage.deleteObject': {
1309
+ // `pyric/storage`'s sandbox delete is idempotent (no-op on missing) —
1310
+ // matching the pyric-admin local arm's delete semantics. Rules enforce
1311
+ // `write` under the op's lens; deletes emit `service_mutation` events.
1312
+ try {
1313
+ const storage = lensStorage(ctx, msg.actAs);
1314
+ await storageDeleteObject(storageRef(storage, msg.path));
1315
+ ok(port, msg.id, null);
1316
+ }
1317
+ catch (e) {
1318
+ fail(port, msg.id, e);
1319
+ }
1320
+ break;
1321
+ }
1322
+ default: {
1323
+ // Auth ops (`auth.*`) are routed to handleAuthOp by handleMessage before
1324
+ // reaching here, so any method landing in this default is genuinely
1325
+ // unknown. (We can't use a `never` exhaustiveness check anymore because
1326
+ // OpMessage now includes the auth variants this switch deliberately skips.)
1327
+ fail(port, msg.id, new Error(`Unknown method: ${String(msg.method)}`));
1328
+ }
1329
+ }
1330
+ }
1331
+ function applyWriteToBatch(db, batch, w) {
1332
+ const b = batch;
1333
+ const ref = pyricDoc(db, w.path);
1334
+ if (w.method === 'set') {
1335
+ const data = prepareWriteData(w.data);
1336
+ b.set(ref, data, w.options);
1337
+ }
1338
+ else if (w.method === 'update') {
1339
+ const data = prepareWriteData(w.data);
1340
+ b.update(ref, data);
1341
+ }
1342
+ else if (w.method === 'delete') {
1343
+ b.delete(ref);
1344
+ }
1345
+ }
1346
+ const _sessionSubs = new WeakMap();
1347
+ function sessionSubsFor(ctx, port) {
1348
+ let byPort = _sessionSubs.get(ctx);
1349
+ if (!byPort) {
1350
+ byPort = new Map();
1351
+ _sessionSubs.set(ctx, byPort);
1352
+ }
1353
+ let bySubId = byPort.get(port);
1354
+ if (!bySubId) {
1355
+ bySubId = new Map();
1356
+ byPort.set(port, bySubId);
1357
+ }
1358
+ return bySubId;
1359
+ }
1360
+ /**
1361
+ * Re-establish a port's session-bound listeners under its CURRENT session —
1362
+ * invoked (via the ctx hook) on every port session change. Mirrors prod's
1363
+ * stream re-establishment on auth transitions: each listener is torn down and
1364
+ * re-registered through `sessionDb`, so the fresh evaluation either delivers
1365
+ * a snapshot (allowed) or a `permission-denied` snap-error (revoked). A
1366
+ * signed-out page no longer keeps receiving auth-gated data.
1367
+ */
1368
+ function resubscribeSessionSubs(ctx, port) {
1369
+ const bound = _sessionSubs.get(ctx)?.get(port);
1370
+ if (!bound || bound.size === 0)
1371
+ return;
1372
+ const portSubs = ctx.subs.get(port);
1373
+ for (const [subId, msg] of [...bound]) {
1374
+ const unsub = portSubs?.get(subId);
1375
+ if (unsub)
1376
+ unsub();
1377
+ portSubs?.delete(subId);
1378
+ bound.delete(subId); // handleSub/handleRtdbSub re-records it
1379
+ if (isRtdbSub(msg)) {
1380
+ handleRtdbSub(ctx, port, msg);
1381
+ }
1382
+ else {
1383
+ handleSub(ctx, port, msg);
1384
+ }
1385
+ }
1386
+ }
1387
+ function handleSub(ctx, port, msg) {
1388
+ // Resolve the listener's data handle through the SAME lens path ops use
1389
+ // (Pyric Studio F4 "watch as user"): `{ mode: 'as', uid }` registers the
1390
+ // listener as that user so its rule evals impersonate, `{ mode: 'admin' }`
1391
+ // bypasses rules. Absent ⇒ the PORT'S SESSION (#754), so an app listener
1392
+ // evaluates rules as whoever this tab signed in as.
1393
+ const db = msg.actAs ? lensDb(ctx, msg.actAs) : sessionDb(ctx, port);
1394
+ ensurePortSubs(ctx, port);
1395
+ const portSubs = ctx.subs.get(port);
1396
+ if (portSubs.has(msg.subId))
1397
+ return; // idempotent
1398
+ // Session-bound listeners re-establish on this port's auth transitions.
1399
+ if (!msg.actAs) {
1400
+ ctx.resubscribePortSubs ??= (p) => resubscribeSessionSubs(ctx, p);
1401
+ sessionSubsFor(ctx, port).set(msg.subId, msg);
1402
+ }
1403
+ let target;
1404
+ let unsub;
1405
+ try {
1406
+ target = resolveTarget(db, msg.target);
1407
+ unsub = registerListener(ctx, port, msg, target);
1408
+ }
1409
+ catch (e) {
1410
+ // resolveTarget / onSnapshot can throw synchronously (e.g. an invalid
1411
+ // query or a rules-rejected target). Deliver it to the client's onSnapshot
1412
+ // error callback as a snap-error instead of letting it escape handleMessage
1413
+ // as an unhandled rejection (which would silently deliver NOTHING).
1414
+ post(port, { t: 'snap', subId: msg.subId, value: { __error: serializeError(e) } });
1415
+ return;
1416
+ }
1417
+ portSubs.set(msg.subId, unsub);
1418
+ }
1419
+ function handleRtdbSub(ctx, port, msg) {
1420
+ ensurePortSubs(ctx, port);
1421
+ const portSubs = ctx.subs.get(port);
1422
+ if (portSubs.has(msg.subId))
1423
+ return;
1424
+ if (!msg.actAs) {
1425
+ ctx.resubscribePortSubs ??= (p) => resubscribeSessionSubs(ctx, p);
1426
+ sessionSubsFor(ctx, port).set(msg.subId, msg);
1427
+ }
1428
+ try {
1429
+ const ref = rtdbRef(lensRtdb(ctx, msg.actAs, port), msg.target.path);
1430
+ const unsub = rtdbOnValue(ref, (snap) => post(port, { t: 'snap', subId: msg.subId, value: rtdbSnapToWire(snap) }));
1431
+ portSubs.set(msg.subId, unsub);
1432
+ }
1433
+ catch (e) {
1434
+ post(port, { t: 'snap', subId: msg.subId, value: { __error: serializeError(e) } });
1435
+ }
1436
+ }
1437
+ /** Register the real sandbox listener for a resolved target; returns its unsub.
1438
+ * Split out of handleSub so the throwing surface (resolveTarget + onSnapshot)
1439
+ * is inside handleSub's try/catch. */
1440
+ function registerListener(_ctx, port, msg, target) {
1441
+ return onSnapshot(target, (snap) => {
1442
+ // Detect doc vs query snapshot by shape.
1443
+ const snapAny = snap;
1444
+ if (Array.isArray(snapAny.docs)) {
1445
+ // Query snapshot
1446
+ const docs = snapAny.docs.map((d) => serializeDocSnap(d));
1447
+ post(port, { t: 'snap', subId: msg.subId, value: { docs } });
1448
+ }
1449
+ else if (snapAny.id !== undefined) {
1450
+ // Doc snapshot
1451
+ post(port, {
1452
+ t: 'snap',
1453
+ subId: msg.subId,
1454
+ value: serializeDocSnap(snapAny),
1455
+ });
1456
+ }
1457
+ }, (err) => {
1458
+ // Snapshot listener error (e.g. rules changed to deny).
1459
+ // We forward as a snap with an __error field so the client can
1460
+ // surface it to the original onSnapshot error callback.
1461
+ post(port, { t: 'snap', subId: msg.subId, value: { __error: serializeError(err) } });
1462
+ });
1463
+ }
1464
+ function handleUnsub(ctx, port, msg) {
1465
+ // Drop the session-bound record first — even when the live listener never
1466
+ // registered (it errored at sub time), the record must not resurrect the
1467
+ // sub on a later session change.
1468
+ _sessionSubs.get(ctx)?.get(port)?.delete(msg.subId);
1469
+ const portSubs = ctx.subs.get(port);
1470
+ if (!portSubs)
1471
+ return;
1472
+ const unsub = portSubs.get(msg.subId);
1473
+ if (!unsub)
1474
+ return;
1475
+ unsub();
1476
+ portSubs.delete(msg.subId);
1477
+ }
1478
+ // ─── Main dispatch ────────────────────────────────────────────────────────
1479
+ /**
1480
+ * Handle one inbound message from a port.
1481
+ *
1482
+ * This is the primary unit-testable seam. Tests create a real `HostCtx`
1483
+ * backed by an in-memory pyric sandbox and call this function directly
1484
+ * with fake port objects, exercising the full op+subscription lifecycle
1485
+ * without a real SharedWorker.
1486
+ */
1487
+ /**
1488
+ * Agent tool-call dispatch. The bridge peer forwards `tool` messages so the
1489
+ * agent runs the canonical sandbox tool set against THIS worker's sandbox (the
1490
+ * same instance the app + Studio use) instead of a separate in-page backend.
1491
+ * Replies with a `res` whose value is the `{ ok, summary, data }` result.
1492
+ */
1493
+ async function handleTool(ctx, port, msg) {
1494
+ try {
1495
+ ctx.toolDispatch ??= buildSandboxDispatcher(ctx.sandbox);
1496
+ const result = await ctx.toolDispatch(msg.name, msg.args ?? {});
1497
+ // Pre-serialize via JSON BEFORE the structured-clone hop over the port. Read
1498
+ // results carry real firebase wrapper instances (Timestamp/GeoPoint/Bytes/
1499
+ // VectorValue) whose toJSON() produces the canonical agent-facing shapes.
1500
+ // structuredClone would strip those prototypes (losing toJSON) and post
1501
+ // mangled internals, and would throw DataCloneError on any non-cloneable
1502
+ // field. JSON.stringify here runs toJSON() while the instances are intact and
1503
+ // yields a plain, clone-safe object — matching the in-page path, which
1504
+ // JSON-stringified at the bridge. A serialization error lands in the catch
1505
+ // below, never in postMessage.
1506
+ ok(port, msg.id, JSON.parse(JSON.stringify(result)));
1507
+ }
1508
+ catch (e) {
1509
+ fail(port, msg.id, e);
1510
+ }
1511
+ }
1512
+ export async function handleMessage(ctx, port, msg) {
1513
+ // Op provenance, stamped MECHANICALLY at dispatch:
1514
+ // - `actor`: a client that DECLARED itself the issuer (`issuer: 'studio'`,
1515
+ // stamped by Studio's worker client — see `setOpIssuer` in client.ts)
1516
+ // gets `actor: { kind: 'studio' }`. Bridge-relayed frames are forwarded
1517
+ // verbatim without the stamp (client.ts `relayWorkerOp`), so remote
1518
+ // admin/agent traffic is never mislabeled as Studio's.
1519
+ // - `authLens`: the op's EFFECTIVE lens (`msg.actAs`) is stamped whenever
1520
+ // present — independent of issuer declaration, because admin is admin
1521
+ // regardless of who asked (an agent tool relay's admin op must classify
1522
+ // as a rules BYPASS in Traffic, not as a rules allow). Without this,
1523
+ // Firestore admin-lens ops carried no lens on their events and
1524
+ // `verdictFor` mislabeled the bypass as ALLOW (the RTDB/Firestore
1525
+ // asymmetry the traffic-metrics work flagged).
1526
+ // Events an emitter already stamped win over the window (stampProvenance
1527
+ // semantics: the event's own field always beats the ambient default).
1528
+ const issuer = msg.issuer;
1529
+ const actAs = msg.actAs;
1530
+ if ((issuer === 'studio' || actAs) && ctx.sandbox.runWithProvenance) {
1531
+ return ctx.sandbox.runWithProvenance({
1532
+ ...(issuer === 'studio' ? { actor: { kind: 'studio' } } : {}),
1533
+ ...(actAs ? { authLens: actAs } : {}),
1534
+ }, () => dispatchMessage(ctx, port, msg));
1535
+ }
1536
+ return dispatchMessage(ctx, port, msg);
1537
+ }
1538
+ async function dispatchMessage(ctx, port, msg) {
1539
+ if (msg.t === 'op') {
1540
+ if (isAuthOp(msg.method)) {
1541
+ await handleAuthOp(ctx, port, msg);
1542
+ }
1543
+ else {
1544
+ await handleOp(ctx, port, msg);
1545
+ }
1546
+ }
1547
+ else if (msg.t === 'sub') {
1548
+ if (isAuthSub(msg)) {
1549
+ handleAuthSub(ctx, port, msg);
1550
+ }
1551
+ else if (isEventSub(msg)) {
1552
+ handleEventSub(ctx, port, msg);
1553
+ }
1554
+ else if (isRtdbSub(msg)) {
1555
+ handleRtdbSub(ctx, port, msg);
1556
+ }
1557
+ else {
1558
+ handleSub(ctx, port, msg);
1559
+ }
1560
+ }
1561
+ else if (msg.t === 'unsub') {
1562
+ // An unsub may target an auth sub, an event-stream sub, or a Firestore
1563
+ // listener — try the cheap routing registries first, then fall through to
1564
+ // the Firestore listener teardown.
1565
+ if (!handleAuthUnsub(ctx, port, msg.subId) &&
1566
+ !handleEventUnsub(ctx, port, msg.subId)) {
1567
+ handleUnsub(ctx, port, msg);
1568
+ }
1569
+ }
1570
+ else if (msg.t === 'tool') {
1571
+ await handleTool(ctx, port, msg);
1572
+ }
1573
+ }
1574
+ // ─── Port cleanup ─────────────────────────────────────────────────────────
1575
+ /**
1576
+ * Tear down all subscriptions for a disconnected port.
1577
+ * Called when a port's `close` event fires (browser best-effort) or
1578
+ * when the entry point explicitly cleans up a port.
1579
+ */
1580
+ export function cleanupPort(ctx, port) {
1581
+ // Drop the port's auth subscriptions (routing entries — no real listener
1582
+ // to tear down), its per-port session, and its session-bound sub records
1583
+ // (#754).
1584
+ authSubsFor(ctx).delete(port);
1585
+ cleanupPortSession(ctx, port);
1586
+ _sessionSubs.get(ctx)?.delete(port);
1587
+ // Drop the port's event-stream subscriptions too (also routing entries off
1588
+ // the single shared `sandbox.onEvent` subscription — nothing to unsubscribe,
1589
+ // just stop fanning out to a dead port).
1590
+ eventSubsFor(ctx).delete(port);
1591
+ const portSubs = ctx.subs.get(port);
1592
+ if (!portSubs)
1593
+ return;
1594
+ for (const unsub of portSubs.values()) {
1595
+ unsub();
1596
+ }
1597
+ ctx.subs.delete(port);
1598
+ }
1599
+ // ─── Internal helpers ─────────────────────────────────────────────────────
1600
+ function ensurePortSubs(ctx, port) {
1601
+ if (!ctx.subs.has(port)) {
1602
+ ctx.subs.set(port, new Map());
1603
+ }
1604
+ }
1605
+ //# sourceMappingURL=host.js.map