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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (498) hide show
  1. package/README.md.orig +2 -2
  2. package/dist/assurance/.generated/conformance-verdicts.d.ts +16 -0
  3. package/dist/assurance/.generated/conformance-verdicts.d.ts.map +1 -1
  4. package/dist/assurance/.generated/conformance-verdicts.js +16 -0
  5. package/dist/assurance/.generated/conformance-verdicts.js.map +1 -1
  6. package/dist/bridge/server/peer.d.ts +1 -1
  7. package/dist/bridge/server.d.ts +1 -1
  8. package/dist/bridge/server.js +1 -1
  9. package/dist/cli/index.d.ts +11 -0
  10. package/dist/cli/index.d.ts.map +1 -1
  11. package/dist/cli/index.js +13 -1
  12. package/dist/cli/index.js.map +1 -1
  13. package/dist/cli/init.d.ts +3 -3
  14. package/dist/cli/init.d.ts.map +1 -1
  15. package/dist/cli/init.js +5 -5
  16. package/dist/cli/init.js.map +1 -1
  17. package/dist/cli/serve.d.ts.map +1 -1
  18. package/dist/cli/serve.js +8 -2
  19. package/dist/cli/serve.js.map +1 -1
  20. package/dist/conformance/.generated/can-i-use-browser.d.ts.map +1 -1
  21. package/dist/conformance/.generated/can-i-use-browser.js +1 -1
  22. package/dist/conformance/.generated/can-i-use-browser.js.map +1 -1
  23. package/dist/conformance/.generated/can-i-use.d.ts.map +1 -1
  24. package/dist/conformance/.generated/can-i-use.js +2 -2
  25. package/dist/conformance/.generated/can-i-use.js.map +1 -1
  26. package/dist/conformance/.generated/conformance-docs.d.ts +12 -0
  27. package/dist/conformance/.generated/conformance-docs.d.ts.map +1 -0
  28. package/dist/conformance/.generated/conformance-docs.js +2 -0
  29. package/dist/conformance/.generated/conformance-docs.js.map +1 -0
  30. package/dist/conformance/docs.d.ts +6 -0
  31. package/dist/conformance/docs.d.ts.map +1 -0
  32. package/dist/conformance/docs.js +6 -0
  33. package/dist/conformance/docs.js.map +1 -0
  34. package/dist/remote/index.d.ts.map +1 -1
  35. package/dist/remote/index.js +1 -0
  36. package/dist/remote/index.js.map +1 -1
  37. package/dist/serve/activity-guard.d.ts +4 -0
  38. package/dist/serve/activity-guard.d.ts.map +1 -0
  39. package/dist/serve/activity-guard.js +22 -0
  40. package/dist/serve/activity-guard.js.map +1 -0
  41. package/dist/serve/activity-route.d.ts +4 -0
  42. package/dist/serve/activity-route.d.ts.map +1 -0
  43. package/dist/serve/activity-route.js +157 -0
  44. package/dist/serve/activity-route.js.map +1 -0
  45. package/dist/serve/activity-warning.d.ts +4 -0
  46. package/dist/serve/activity-warning.d.ts.map +1 -0
  47. package/dist/serve/activity-warning.js +21 -0
  48. package/dist/serve/activity-warning.js.map +1 -0
  49. package/dist/serve/bridge-mount.d.ts +5 -0
  50. package/dist/serve/bridge-mount.d.ts.map +1 -1
  51. package/dist/serve/bridge-mount.js +1 -0
  52. package/dist/serve/bridge-mount.js.map +1 -1
  53. package/dist/serve/docs-ui/404.html +3 -0
  54. package/dist/serve/docs-ui/_astro/{_slug_.DPUwShm2.css → _slug_.CXh9yTP1.css} +1 -1
  55. package/dist/serve/docs-ui/_astro/_slug_.DUci-duz.css +1 -0
  56. package/dist/serve/docs-ui/docs/_rhythm/index.html +2 -1
  57. package/dist/serve/docs-ui/docs/agent/set-up-your-agent/index.html +3 -2
  58. package/dist/serve/docs-ui/docs/agent/set-up-your-agent.md +1 -1
  59. package/dist/serve/docs-ui/docs/agent/watch-and-review/index.html +2 -1
  60. package/dist/serve/docs-ui/docs/agent/work-with-an-agent/index.html +2 -1
  61. package/dist/serve/docs-ui/docs/ai-compat/index.html +2 -1
  62. package/dist/serve/docs-ui/docs/api-reference/index.html +51 -49
  63. package/dist/serve/docs-ui/docs/api-reference.md +3 -3
  64. package/dist/serve/docs-ui/docs/app-compat/index.html +2 -1
  65. package/dist/serve/docs-ui/docs/auth-compat/index.html +2 -1
  66. package/dist/serve/docs-ui/docs/build/ai-logic/index.html +18 -3
  67. package/dist/serve/docs-ui/docs/build/ai-logic.md +30 -2
  68. package/dist/serve/docs-ui/docs/build/authentication/index.html +2 -1
  69. package/dist/serve/docs-ui/docs/build/authentication.md +1 -1
  70. package/dist/serve/docs-ui/docs/build/cloud-firestore/index.html +2 -1
  71. package/dist/serve/docs-ui/docs/build/cloud-messaging/index.html +46 -2
  72. package/dist/serve/docs-ui/docs/build/cloud-messaging.md +56 -1
  73. package/dist/serve/docs-ui/docs/build/cloud-storage/index.html +2 -1
  74. package/dist/serve/docs-ui/docs/build/cloud-storage.md +1 -1
  75. package/dist/serve/docs-ui/docs/build/realtime-database/index.html +21 -3
  76. package/dist/serve/docs-ui/docs/build/realtime-database.md +25 -1
  77. package/dist/serve/docs-ui/docs/conformance-scores/index.html +2 -1
  78. package/dist/serve/docs-ui/docs/create-pyric-reference-api/index.html +61 -28
  79. package/dist/serve/docs-ui/docs/create-pyric-reference-api.md +65 -20
  80. package/dist/serve/docs-ui/docs/database-compat/index.html +2 -1
  81. package/dist/serve/docs-ui/docs/firestore-compat/index.html +6 -5
  82. package/dist/serve/docs-ui/docs/firestore-compat.md +4 -4
  83. package/dist/serve/docs-ui/docs/functions-rtdb-compat/index.html +2 -1
  84. package/dist/serve/docs-ui/docs/get-started/how-the-swap-works/index.html +2 -1
  85. package/dist/serve/docs-ui/docs/get-started/start-building/index.html +4 -3
  86. package/dist/serve/docs-ui/docs/get-started/start-building.md +2 -2
  87. package/dist/serve/docs-ui/docs/index.json +979 -579
  88. package/dist/serve/docs-ui/docs/messaging-compat/index.html +2 -1
  89. package/dist/serve/docs-ui/docs/observe/see-whats-happening/index.html +2 -1
  90. package/dist/serve/docs-ui/docs/observe/shape-your-data/index.html +2 -1
  91. package/dist/serve/docs-ui/docs/overview/index.html +2 -1
  92. package/dist/serve/docs-ui/docs/pyric-admin-app-reference-api/index.html +5 -4
  93. package/dist/serve/docs-ui/docs/pyric-admin-app-reference-api.md +3 -4
  94. package/dist/serve/docs-ui/docs/pyric-admin-auth-reference-api/index.html +5 -4
  95. package/dist/serve/docs-ui/docs/pyric-admin-auth-reference-api.md +3 -4
  96. package/dist/serve/docs-ui/docs/pyric-admin-database-reference-api/index.html +5 -4
  97. package/dist/serve/docs-ui/docs/pyric-admin-database-reference-api.md +3 -4
  98. package/dist/serve/docs-ui/docs/pyric-admin-firestore-reference-api/index.html +2503 -12
  99. package/dist/serve/docs-ui/docs/pyric-admin-firestore-reference-api.md +2219 -8
  100. package/dist/serve/docs-ui/docs/pyric-admin-messaging-reference-api/index.html +916 -0
  101. package/dist/serve/docs-ui/docs/pyric-admin-messaging-reference-api.md +706 -0
  102. package/dist/serve/docs-ui/docs/pyric-admin-storage-reference-api/index.html +4 -3
  103. package/dist/serve/docs-ui/docs/pyric-admin-storage-reference-api.md +2 -3
  104. package/dist/serve/docs-ui/docs/pyric-ai-reference-api/index.html +16 -11
  105. package/dist/serve/docs-ui/docs/pyric-ai-reference-api.md +14 -13
  106. package/dist/serve/docs-ui/docs/pyric-ai-scripting-reference-api/index.html +4 -3
  107. package/dist/serve/docs-ui/docs/pyric-ai-scripting-reference-api.md +2 -3
  108. package/dist/serve/docs-ui/docs/pyric-app-reference-api/index.html +7 -2
  109. package/dist/serve/docs-ui/docs/pyric-app-reference-api.md +5 -2
  110. package/dist/serve/docs-ui/docs/pyric-auth-reference-api/index.html +23 -12
  111. package/dist/serve/docs-ui/docs/pyric-auth-reference-api.md +15 -12
  112. package/dist/serve/docs-ui/docs/pyric-cli-assurance-browser-reference-api/index.html +10 -48
  113. package/dist/serve/docs-ui/docs/pyric-cli-assurance-browser-reference-api.md +11 -24
  114. package/dist/serve/docs-ui/docs/pyric-cli-assurance-reference-api/index.html +87 -97
  115. package/dist/serve/docs-ui/docs/pyric-cli-assurance-reference-api.md +53 -65
  116. package/dist/serve/docs-ui/docs/pyric-cli-bridge-client-reference-api/index.html +14 -15
  117. package/dist/serve/docs-ui/docs/pyric-cli-bridge-client-reference-api.md +19 -28
  118. package/dist/serve/docs-ui/docs/pyric-cli-bridge-reference-api/index.html +4 -3
  119. package/dist/serve/docs-ui/docs/pyric-cli-bridge-reference-api.md +2 -3
  120. package/dist/serve/docs-ui/docs/pyric-cli-conformance-browser-reference-api/index.html +3 -2
  121. package/dist/serve/docs-ui/docs/pyric-cli-conformance-browser-reference-api.md +1 -2
  122. package/dist/serve/docs-ui/docs/pyric-cli-conformance-docs-reference-api/index.html +51 -0
  123. package/dist/serve/docs-ui/docs/pyric-cli-conformance-docs-reference-api.md +36 -0
  124. package/dist/serve/docs-ui/docs/pyric-cli-conformance-reference-api/index.html +3 -2
  125. package/dist/serve/docs-ui/docs/pyric-cli-conformance-reference-api.md +1 -2
  126. package/dist/serve/docs-ui/docs/pyric-cli-credentials-node-reference-api/index.html +3 -2
  127. package/dist/serve/docs-ui/docs/pyric-cli-credentials-node-reference-api.md +1 -2
  128. package/dist/serve/docs-ui/docs/pyric-cli-discover-reference-api/index.html +3 -2
  129. package/dist/serve/docs-ui/docs/pyric-cli-discover-reference-api.md +1 -2
  130. package/dist/serve/docs-ui/docs/pyric-cli-register-reference-api/index.html +3 -2
  131. package/dist/serve/docs-ui/docs/pyric-cli-register-reference-api.md +1 -2
  132. package/dist/serve/docs-ui/docs/pyric-cli-remote-reference-api/index.html +1067 -83
  133. package/dist/serve/docs-ui/docs/pyric-cli-remote-reference-api.md +1291 -160
  134. package/dist/serve/docs-ui/docs/pyric-cli-serve-worker-reference-api/index.html +253 -157
  135. package/dist/serve/docs-ui/docs/pyric-cli-serve-worker-reference-api.md +179 -85
  136. package/dist/serve/docs-ui/docs/pyric-cli-verify-reference-api/index.html +72 -7
  137. package/dist/serve/docs-ui/docs/pyric-cli-verify-reference-api.md +22 -7
  138. package/dist/serve/docs-ui/docs/pyric-cli-vite-reference-api/index.html +33 -12
  139. package/dist/serve/docs-ui/docs/pyric-cli-vite-reference-api.md +15 -12
  140. package/dist/serve/docs-ui/docs/pyric-database-reference-api/index.html +18 -17
  141. package/dist/serve/docs-ui/docs/pyric-database-reference-api.md +19 -22
  142. package/dist/serve/docs-ui/docs/pyric-firestore-reference-api/index.html +1512 -545
  143. package/dist/serve/docs-ui/docs/pyric-firestore-reference-api.md +1311 -316
  144. package/dist/serve/docs-ui/docs/pyric-messaging-reference-api/index.html +5 -4
  145. package/dist/serve/docs-ui/docs/pyric-messaging-reference-api.md +3 -4
  146. package/dist/serve/docs-ui/docs/pyric-messaging-sw-reference-api/index.html +69 -369
  147. package/dist/serve/docs-ui/docs/pyric-messaging-sw-reference-api.md +103 -216
  148. package/dist/serve/docs-ui/docs/pyric-rules-reference-api/index.html +10 -9
  149. package/dist/serve/docs-ui/docs/pyric-rules-reference-api.md +9 -10
  150. package/dist/serve/docs-ui/docs/pyric-sandbox-database-reference-api/index.html +8 -7
  151. package/dist/serve/docs-ui/docs/pyric-sandbox-database-reference-api.md +6 -7
  152. package/dist/serve/docs-ui/docs/pyric-sandbox-firestore-reference-api/index.html +10 -9
  153. package/dist/serve/docs-ui/docs/pyric-sandbox-firestore-reference-api.md +9 -10
  154. package/dist/serve/docs-ui/docs/pyric-sandbox-reference-api/index.html +371 -884
  155. package/dist/serve/docs-ui/docs/pyric-sandbox-reference-api.md +369 -883
  156. package/dist/serve/docs-ui/docs/pyric-storage-reference-api/index.html +25 -17
  157. package/dist/serve/docs-ui/docs/pyric-storage-reference-api.md +21 -19
  158. package/dist/serve/docs-ui/docs/pyric-ui-agents-reference-api/index.html +3 -2
  159. package/dist/serve/docs-ui/docs/pyric-ui-agents-reference-api.md +1 -2
  160. package/dist/serve/docs-ui/docs/pyric-ui-auth-hooks-reference-api/index.html +63 -776
  161. package/dist/serve/docs-ui/docs/pyric-ui-auth-hooks-reference-api.md +69 -510
  162. package/dist/serve/docs-ui/docs/pyric-ui-auth-reference-api/index.html +22 -21
  163. package/dist/serve/docs-ui/docs/pyric-ui-auth-reference-api.md +29 -30
  164. package/dist/serve/docs-ui/docs/pyric-ui-events-hooks-reference-api/index.html +15 -168
  165. package/dist/serve/docs-ui/docs/pyric-ui-events-hooks-reference-api.md +17 -87
  166. package/dist/serve/docs-ui/docs/pyric-ui-events-reference-api/index.html +6 -5
  167. package/dist/serve/docs-ui/docs/pyric-ui-events-reference-api.md +5 -6
  168. package/dist/serve/docs-ui/docs/pyric-ui-firestore-hooks-reference-api/index.html +82 -970
  169. package/dist/serve/docs-ui/docs/pyric-ui-firestore-hooks-reference-api.md +104 -479
  170. package/dist/serve/docs-ui/docs/pyric-ui-firestore-reference-api/index.html +30 -29
  171. package/dist/serve/docs-ui/docs/pyric-ui-firestore-reference-api.md +54 -55
  172. package/dist/serve/docs-ui/docs/pyric-ui-primitives-reference-api/index.html +3 -2
  173. package/dist/serve/docs-ui/docs/pyric-ui-primitives-reference-api.md +1 -2
  174. package/dist/serve/docs-ui/docs/pyric-ui-rtdb-reference-api/index.html +5 -4
  175. package/dist/serve/docs-ui/docs/pyric-ui-rtdb-reference-api.md +3 -4
  176. package/dist/serve/docs-ui/docs/pyric-ui-rules-hooks-reference-api/index.html +9 -124
  177. package/dist/serve/docs-ui/docs/pyric-ui-rules-hooks-reference-api.md +7 -58
  178. package/dist/serve/docs-ui/docs/pyric-ui-rules-reference-api/index.html +24 -23
  179. package/dist/serve/docs-ui/docs/pyric-ui-rules-reference-api.md +31 -34
  180. package/dist/serve/docs-ui/docs/pyric-ui-storage-hooks-reference-api/index.html +104 -1385
  181. package/dist/serve/docs-ui/docs/pyric-ui-storage-hooks-reference-api.md +133 -817
  182. package/dist/serve/docs-ui/docs/pyric-ui-storage-reference-api/index.html +34 -33
  183. package/dist/serve/docs-ui/docs/pyric-ui-storage-reference-api.md +37 -38
  184. package/dist/serve/docs-ui/docs/pyric-ui-traffic-hooks-reference-api/index.html +116 -1169
  185. package/dist/serve/docs-ui/docs/pyric-ui-traffic-hooks-reference-api.md +142 -590
  186. package/dist/serve/docs-ui/docs/pyric-ui-traffic-reference-api/index.html +4 -3
  187. package/dist/serve/docs-ui/docs/pyric-ui-traffic-reference-api.md +3 -4
  188. package/dist/serve/docs-ui/docs/rules-compat/index.html +34 -1
  189. package/dist/serve/docs-ui/docs/rules-compat.md +32 -0
  190. package/dist/serve/docs-ui/docs/secure/audit-your-rules/index.html +19 -2
  191. package/dist/serve/docs-ui/docs/secure/audit-your-rules.md +32 -0
  192. package/dist/serve/docs-ui/docs/secure/firestore-rules-limits/index.html +2 -1
  193. package/dist/serve/docs-ui/docs/secure/read-a-denial/index.html +2 -1
  194. package/dist/serve/docs-ui/docs/secure/rtdb-rules-in-typescript/index.html +2 -1
  195. package/dist/serve/docs-ui/docs/secure/rtdb-rules-in-typescript.md +1 -1
  196. package/dist/serve/docs-ui/docs/secure/rules-standard-library/index.html +2 -1
  197. package/dist/serve/docs-ui/docs/secure/secure-it-with-rules/index.html +2 -1
  198. package/dist/serve/docs-ui/docs/secure/simulate-and-lint/index.html +2 -1
  199. package/dist/serve/docs-ui/docs/secure/write-a-rules-test-suite/index.html +2 -1
  200. package/dist/serve/docs-ui/docs/ship/ship-to-production/index.html +2 -1
  201. package/dist/serve/docs-ui/docs/ship/test-in-node/index.html +2 -1
  202. package/dist/serve/docs-ui/docs/storage-compat/index.html +2 -1
  203. package/dist/serve/docs-ui/docs/trust/how-we-know-it-matches-firebase/index.html +2 -1
  204. package/dist/serve/docs-ui/docs/trust/versioning-and-compatibility/index.html +2 -1
  205. package/dist/serve/docs-ui/llms.txt +2 -1
  206. package/dist/serve/entries/ai.d.ts.map +1 -1
  207. package/dist/serve/entries/ai.js +39 -1
  208. package/dist/serve/entries/ai.js.map +1 -1
  209. package/dist/serve/entries/messaging-sw.d.ts.map +1 -1
  210. package/dist/serve/entries/messaging-sw.js +5 -1
  211. package/dist/serve/entries/messaging-sw.js.map +1 -1
  212. package/dist/serve/entries/messaging.d.ts.map +1 -1
  213. package/dist/serve/entries/messaging.js +7 -1
  214. package/dist/serve/entries/messaging.js.map +1 -1
  215. package/dist/serve/entries/runtime.d.ts.map +1 -1
  216. package/dist/serve/entries/runtime.js +20 -1
  217. package/dist/serve/entries/runtime.js.map +1 -1
  218. package/dist/serve/init-payload.d.ts +25 -0
  219. package/dist/serve/init-payload.d.ts.map +1 -1
  220. package/dist/serve/namespace.d.ts +39 -0
  221. package/dist/serve/namespace.d.ts.map +1 -1
  222. package/dist/serve/namespace.js +80 -1
  223. package/dist/serve/namespace.js.map +1 -1
  224. package/dist/serve/standalone-assets.d.ts +11 -9
  225. package/dist/serve/standalone-assets.d.ts.map +1 -1
  226. package/dist/serve/standalone-assets.js +6 -5
  227. package/dist/serve/standalone-assets.js.map +1 -1
  228. package/dist/serve/studio-ui/assets/{RulesCodeEditor-CXdSLKhO.js → RulesCodeEditor-CxVYcZXw.js} +1 -1
  229. package/dist/serve/studio-ui/assets/index-cpSfgQ7H.js +1218 -0
  230. package/dist/serve/studio-ui/assets/seed-DeuFKi4J.js +20 -0
  231. package/dist/serve/studio-ui/index.html +1 -1
  232. package/dist/serve/vite-plugin.d.ts +92 -15
  233. package/dist/serve/vite-plugin.d.ts.map +1 -1
  234. package/dist/serve/vite-plugin.js +281 -11
  235. package/dist/serve/vite-plugin.js.map +1 -1
  236. package/dist/serve/worker/activity-bootstrap.d.ts +8 -0
  237. package/dist/serve/worker/activity-bootstrap.d.ts.map +1 -0
  238. package/dist/serve/worker/activity-bootstrap.js +11 -0
  239. package/dist/serve/worker/activity-bootstrap.js.map +1 -0
  240. package/dist/serve/worker/client/connection.d.ts.map +1 -1
  241. package/dist/serve/worker/client/connection.js +17 -5
  242. package/dist/serve/worker/client/connection.js.map +1 -1
  243. package/dist/serve/worker/client/core.d.ts +2 -2
  244. package/dist/serve/worker/client/core.d.ts.map +1 -1
  245. package/dist/serve/worker/client/core.js +33 -5
  246. package/dist/serve/worker/client/core.js.map +1 -1
  247. package/dist/serve/worker/client/firestore-writes.d.ts.map +1 -1
  248. package/dist/serve/worker/client/firestore-writes.js +1 -0
  249. package/dist/serve/worker/client/firestore-writes.js.map +1 -1
  250. package/dist/serve/worker/client/messaging.d.ts +9 -1
  251. package/dist/serve/worker/client/messaging.d.ts.map +1 -1
  252. package/dist/serve/worker/client/messaging.js +15 -0
  253. package/dist/serve/worker/client/messaging.js.map +1 -1
  254. package/dist/serve/worker/client/studio.d.ts +10 -0
  255. package/dist/serve/worker/client/studio.d.ts.map +1 -1
  256. package/dist/serve/worker/client/studio.js +11 -0
  257. package/dist/serve/worker/client/studio.js.map +1 -1
  258. package/dist/serve/worker/durable-persistence.d.ts +12 -0
  259. package/dist/serve/worker/durable-persistence.d.ts.map +1 -0
  260. package/dist/serve/worker/durable-persistence.js +118 -0
  261. package/dist/serve/worker/durable-persistence.js.map +1 -0
  262. package/dist/serve/worker/entry.d.ts +7 -3
  263. package/dist/serve/worker/entry.d.ts.map +1 -1
  264. package/dist/serve/worker/entry.js +7 -3
  265. package/dist/serve/worker/entry.js.map +1 -1
  266. package/dist/serve/worker/host/core.d.ts +9 -5
  267. package/dist/serve/worker/host/core.d.ts.map +1 -1
  268. package/dist/serve/worker/host/core.js +34 -9
  269. package/dist/serve/worker/host/core.js.map +1 -1
  270. package/dist/serve/worker/host/dispatch.d.ts.map +1 -1
  271. package/dist/serve/worker/host/dispatch.js +9 -2
  272. package/dist/serve/worker/host/dispatch.js.map +1 -1
  273. package/dist/serve/worker/host/firestore-writes.d.ts.map +1 -1
  274. package/dist/serve/worker/host/firestore-writes.js +3 -2
  275. package/dist/serve/worker/host/firestore-writes.js.map +1 -1
  276. package/dist/serve/worker/host/studio.d.ts +1 -1
  277. package/dist/serve/worker/host/studio.d.ts.map +1 -1
  278. package/dist/serve/worker/host/studio.js +36 -1
  279. package/dist/serve/worker/host/studio.js.map +1 -1
  280. package/dist/serve/worker/host/subscriptions.d.ts.map +1 -1
  281. package/dist/serve/worker/host/subscriptions.js +27 -7
  282. package/dist/serve/worker/host/subscriptions.js.map +1 -1
  283. package/dist/serve/worker/host-context.d.ts +15 -0
  284. package/dist/serve/worker/host-context.d.ts.map +1 -1
  285. package/dist/serve/worker/host-context.js +10 -0
  286. package/dist/serve/worker/host-context.js.map +1 -1
  287. package/dist/serve/worker/host-messaging.d.ts.map +1 -1
  288. package/dist/serve/worker/host-messaging.js +9 -2
  289. package/dist/serve/worker/host-messaging.js.map +1 -1
  290. package/dist/serve/worker/index.d.ts +3 -2
  291. package/dist/serve/worker/index.d.ts.map +1 -1
  292. package/dist/serve/worker/index.js +4 -1
  293. package/dist/serve/worker/index.js.map +1 -1
  294. package/dist/serve/worker/protocol.d.ts +25 -3
  295. package/dist/serve/worker/protocol.d.ts.map +1 -1
  296. package/dist/serve/worker/protocol.js +2 -2
  297. package/dist/serve/worker/protocol.js.map +1 -1
  298. package/dist/serve/worker/serve-init.d.ts +23 -37
  299. package/dist/serve/worker/serve-init.d.ts.map +1 -1
  300. package/dist/serve/worker/serve-init.js +95 -172
  301. package/dist/serve/worker/serve-init.js.map +1 -1
  302. package/dist/vite.d.ts +4 -4
  303. package/dist/vite.d.ts.map +1 -1
  304. package/dist/vite.js +3 -3
  305. package/dist/vite.js.map +1 -1
  306. package/package.json +9 -4
  307. package/src/assurance/.generated/conformance-verdicts.ts +1090 -0
  308. package/src/assurance/attachment.ts +211 -0
  309. package/src/assurance/browser.ts +80 -0
  310. package/src/assurance/campaign.ts +496 -0
  311. package/src/assurance/capabilities.ts +446 -0
  312. package/src/assurance/cases.ts +43 -0
  313. package/src/assurance/index.ts +75 -0
  314. package/src/assurance/runner.ts +885 -0
  315. package/src/assurance/tool-names.ts +15 -0
  316. package/src/assurance/tools.ts +830 -0
  317. package/src/assurance/types.ts +352 -0
  318. package/src/assurance/validation.ts +434 -0
  319. package/src/bridge/client/bridge.ts +550 -0
  320. package/src/bridge/client/dispatch.ts +137 -0
  321. package/src/bridge/client.ts +45 -0
  322. package/src/bridge/protocol.ts +351 -0
  323. package/src/bridge/server/audit.ts +53 -0
  324. package/src/bridge/server/bridge.ts +597 -0
  325. package/src/bridge/server/headless.ts +160 -0
  326. package/src/bridge/server/json-schema-to-zod.ts +108 -0
  327. package/src/bridge/server/local-bridge.ts +66 -0
  328. package/src/bridge/server/logger.ts +50 -0
  329. package/src/bridge/server/mcp-contract.ts +93 -0
  330. package/src/bridge/server/mcp.ts +129 -0
  331. package/src/bridge/server/peer.ts +232 -0
  332. package/src/bridge/server/standalone.ts +347 -0
  333. package/src/bridge/server/tool-metadata.ts +99 -0
  334. package/src/bridge/server.ts +28 -0
  335. package/src/cli/can-i-use.ts +50 -0
  336. package/src/cli/cli.test.ts +493 -0
  337. package/src/cli/database-rules.ts +329 -0
  338. package/src/cli/dev-runner.ts +282 -0
  339. package/src/cli/firebase-json.ts +100 -0
  340. package/src/cli/firestore-indexes.ts +70 -0
  341. package/src/cli/index.ts +425 -0
  342. package/src/cli/init.ts +260 -0
  343. package/src/cli/mcp-proxy.ts +196 -0
  344. package/src/cli/parse-args.ts +80 -0
  345. package/src/cli/rules.ts +259 -0
  346. package/src/cli/scope.ts +67 -0
  347. package/src/cli/serve.ts +996 -0
  348. package/src/cli/service-commands.ts +73 -0
  349. package/src/cli/snapshot.ts +164 -0
  350. package/src/cli/storage-rules.ts +172 -0
  351. package/src/cli/verify.ts +416 -0
  352. package/src/conformance/.generated/can-i-use-browser.ts +113 -0
  353. package/src/conformance/.generated/can-i-use.ts +117 -0
  354. package/src/conformance/.generated/conformance-docs.ts +14 -0
  355. package/src/conformance/browser.ts +24 -0
  356. package/src/conformance/can-i-use-tool.ts +51 -0
  357. package/src/conformance/can-i-use.ts +18 -0
  358. package/src/conformance/docs.ts +8 -0
  359. package/src/conformance/index.ts +20 -0
  360. package/src/conformance/tools.ts +16 -0
  361. package/src/credentials/core/memoize-ttl.ts +142 -0
  362. package/src/credentials/core/types.ts +10 -0
  363. package/src/credentials/node/from-adc.ts +96 -0
  364. package/src/credentials/node/from-service-account.ts +133 -0
  365. package/src/credentials/node/index.ts +6 -0
  366. package/src/discover/concurrency.ts +127 -0
  367. package/src/discover/crawler-adapter.ts +142 -0
  368. package/src/discover/crawler.ts +1127 -0
  369. package/src/discover/credential-free.ts +27 -0
  370. package/src/discover/findCollectionGroup.ts +131 -0
  371. package/src/discover/firestore-source.ts +59 -0
  372. package/src/discover/index.ts +5 -0
  373. package/src/discover/merge.ts +523 -0
  374. package/src/discover/session.ts +402 -0
  375. package/src/discover/tools.ts +201 -0
  376. package/src/discover/types.ts +187 -0
  377. package/src/discover/wire.ts +324 -0
  378. package/src/functions-rtdb/child.ts +382 -0
  379. package/src/functions-rtdb/delivery.ts +7 -0
  380. package/src/functions-rtdb/discovery.ts +136 -0
  381. package/src/functions-rtdb/event.ts +64 -0
  382. package/src/functions-rtdb/execution.ts +111 -0
  383. package/src/functions-rtdb/in-memory-delivery.ts +40 -0
  384. package/src/functions-rtdb/project.ts +74 -0
  385. package/src/functions-rtdb/projection.ts +104 -0
  386. package/src/functions-rtdb/reference-pattern.ts +20 -0
  387. package/src/functions-rtdb/remote-delivery.ts +23 -0
  388. package/src/pkg-version.ts +49 -0
  389. package/src/register/esm-exports.ts +63 -0
  390. package/src/register/hooks.ts +30 -0
  391. package/src/register/index.ts +150 -0
  392. package/src/register/mapping.ts +33 -0
  393. package/src/remote/index.ts +1050 -0
  394. package/src/rtdb/crawl-snapshot.ts +118 -0
  395. package/src/rtdb/inspection.ts +137 -0
  396. package/src/rtdb/load-rules-document.ts +56 -0
  397. package/src/rtdb/rules-generation-tool.ts +38 -0
  398. package/src/rtdb/rules-json.ts +29 -0
  399. package/src/serve/activity-guard.ts +29 -0
  400. package/src/serve/activity-route.ts +164 -0
  401. package/src/serve/activity-warning.ts +26 -0
  402. package/src/serve/bridge-mount.ts +200 -0
  403. package/src/serve/bundler.ts +493 -0
  404. package/src/serve/capture-store.ts +65 -0
  405. package/src/serve/discovery.ts +199 -0
  406. package/src/serve/entries/ai.ts +292 -0
  407. package/src/serve/entries/app-backend.ts +4 -0
  408. package/src/serve/entries/app-client.ts +26 -0
  409. package/src/serve/entries/app-session-store.ts +47 -0
  410. package/src/serve/entries/app.ts +11 -0
  411. package/src/serve/entries/auth-helper-core.ts +181 -0
  412. package/src/serve/entries/auth-helper-dom.ts +134 -0
  413. package/src/serve/entries/auth-helper-runtime.ts +20 -0
  414. package/src/serve/entries/auth.ts +255 -0
  415. package/src/serve/entries/bridge-url.ts +31 -0
  416. package/src/serve/entries/database.ts +129 -0
  417. package/src/serve/entries/firestore.ts +232 -0
  418. package/src/serve/entries/init.ts +46 -0
  419. package/src/serve/entries/keepalive.ts +46 -0
  420. package/src/serve/entries/messaging-sw.ts +75 -0
  421. package/src/serve/entries/messaging.ts +96 -0
  422. package/src/serve/entries/runtime.ts +540 -0
  423. package/src/serve/entries/session-store.ts +97 -0
  424. package/src/serve/entries/storage.ts +92 -0
  425. package/src/serve/entries/tab-sync-wiring.ts +274 -0
  426. package/src/serve/entries/worker-runtime.ts +75 -0
  427. package/src/serve/init-payload.ts +42 -0
  428. package/src/serve/namespace.ts +675 -0
  429. package/src/serve/open-browser.ts +68 -0
  430. package/src/serve/rules.ts +272 -0
  431. package/src/serve/sandbox-marker.ts +31 -0
  432. package/src/serve/server.ts +428 -0
  433. package/src/serve/standalone-assets.ts +180 -0
  434. package/src/serve/state-store.ts +166 -0
  435. package/src/serve/studio/disk-project-store.ts +185 -0
  436. package/src/serve/studio/disk-workspace.ts +162 -0
  437. package/src/serve/studio/index.ts +20 -0
  438. package/src/serve/studio/routes.ts +237 -0
  439. package/src/serve/studio/store-types.ts +48 -0
  440. package/src/serve/studio/studio-storage.test.ts +289 -0
  441. package/src/serve/vite-plugin.ts +1102 -0
  442. package/src/serve/worker/activity-bootstrap.ts +24 -0
  443. package/src/serve/worker/client/admin-firestore.ts +38 -0
  444. package/src/serve/worker/client/ai.ts +124 -0
  445. package/src/serve/worker/client/auth.ts +489 -0
  446. package/src/serve/worker/client/connection.ts +202 -0
  447. package/src/serve/worker/client/core.ts +350 -0
  448. package/src/serve/worker/client/disconnect.ts +45 -0
  449. package/src/serve/worker/client/firestore-reads.ts +181 -0
  450. package/src/serve/worker/client/firestore-refs.ts +226 -0
  451. package/src/serve/worker/client/firestore-writes.ts +232 -0
  452. package/src/serve/worker/client/handles.ts +91 -0
  453. package/src/serve/worker/client/messaging.ts +119 -0
  454. package/src/serve/worker/client/presence.ts +174 -0
  455. package/src/serve/worker/client/rtdb.ts +311 -0
  456. package/src/serve/worker/client/rules.ts +63 -0
  457. package/src/serve/worker/client/service-worker-connection.ts +59 -0
  458. package/src/serve/worker/client/snapshots.ts +86 -0
  459. package/src/serve/worker/client/storage.ts +195 -0
  460. package/src/serve/worker/client/studio.ts +88 -0
  461. package/src/serve/worker/client.ts +57 -0
  462. package/src/serve/worker/durable-persistence.ts +137 -0
  463. package/src/serve/worker/entry.ts +179 -0
  464. package/src/serve/worker/host/admin-firestore.ts +88 -0
  465. package/src/serve/worker/host/connection.ts +179 -0
  466. package/src/serve/worker/host/core.ts +387 -0
  467. package/src/serve/worker/host/dispatch.ts +316 -0
  468. package/src/serve/worker/host/firestore-reads.ts +117 -0
  469. package/src/serve/worker/host/firestore-writes.ts +456 -0
  470. package/src/serve/worker/host/presence.ts +312 -0
  471. package/src/serve/worker/host/rtdb.ts +136 -0
  472. package/src/serve/worker/host/rules.ts +128 -0
  473. package/src/serve/worker/host/storage.ts +312 -0
  474. package/src/serve/worker/host/studio.ts +75 -0
  475. package/src/serve/worker/host/subscriptions.ts +238 -0
  476. package/src/serve/worker/host-ai.ts +165 -0
  477. package/src/serve/worker/host-auth.ts +469 -0
  478. package/src/serve/worker/host-context.ts +273 -0
  479. package/src/serve/worker/host-events.ts +94 -0
  480. package/src/serve/worker/host-messaging.ts +240 -0
  481. package/src/serve/worker/host.ts +47 -0
  482. package/src/serve/worker/index.ts +196 -0
  483. package/src/serve/worker/presence-timing.ts +15 -0
  484. package/src/serve/worker/protocol.ts +1112 -0
  485. package/src/serve/worker/serve-init.ts +606 -0
  486. package/src/serve/worker/service-worker-channel.ts +33 -0
  487. package/src/serve/worker/service-worker-relay.ts +86 -0
  488. package/src/serve/writer-lock.ts +53 -0
  489. package/src/verify/cases.ts +233 -0
  490. package/src/verify/fixture.ts +258 -0
  491. package/src/verify/index.ts +519 -0
  492. package/src/verify/tools.ts +108 -0
  493. package/src/version/compat-target.ts +17 -0
  494. package/src/vite.ts +17 -0
  495. package/dist/serve/docs-ui/docs/pyric-firestore-values-reference-api/index.html +0 -30
  496. package/dist/serve/docs-ui/docs/pyric-firestore-values-reference-api.md +0 -30
  497. package/dist/serve/studio-ui/assets/index-BvtQIpGn.js +0 -1211
  498. package/dist/serve/studio-ui/assets/seed-3DMedLL3.js +0 -2
@@ -0,0 +1,166 @@
1
+ /**
2
+ * On-disk state store for `pyric dev --persist` — the substrate side of
3
+ * flow doc section 3c ("persistence = an autosaved seed").
4
+ *
5
+ * The file is an ENVELOPE around two sections:
6
+ * - `firestore`: the `pyric/sandbox` persistence controller's own blob
7
+ * (`{version, savedAt, firestore: {path: fields}}`), stored verbatim as
8
+ * parsed JSON. The page's controller wrote it and will read it back
9
+ * through its own (de)serializer — wrapper types (Timestamp, Bytes, …)
10
+ * live as marker shapes and re-hydrate to class instances on restore.
11
+ * This store never interprets it.
12
+ * - `auth`: `{users: SeedUser[]}` — what `sandbox.exportUsers` emits and
13
+ * `sandbox.seedUsers` accepts.
14
+ *
15
+ * Everything is plain, diffable JSON on purpose: the persisted state
16
+ * doubles as a context artifact agents can read and promote to a fixture
17
+ * (`pyric snapshot`, plan P3).
18
+ *
19
+ * Durability stance (judgment zone 6, recorded): writes are atomic
20
+ * (tmp + rename, same volume) so a crash never truncates the file. A
21
+ * CORRUPT or version-mismatched file makes `load()` throw rather than
22
+ * silently discarding user data — serve fails fast and tells the user to
23
+ * inspect or delete it. Last-writer-wins between section writes is
24
+ * accepted (single serve process, sync fs — effectively serialized).
25
+ */
26
+ import { copyFileSync, mkdirSync, readFileSync, renameSync, writeFileSync, existsSync } from 'node:fs';
27
+ import { dirname, join } from 'node:path';
28
+ import type { sandbox as authSandbox } from 'pyric/auth';
29
+
30
+ export type ExportedUsers = ReturnType<typeof authSandbox.exportUsers>;
31
+
32
+ export const STATE_FILE_VERSION = 1 as const;
33
+
34
+ /** Expected inner version of the sandbox persistence controller's blob.
35
+ * MIRRORS pyric's `serialize.ts` `SCHEMA_VERSION` — bump in lockstep when
36
+ * pyric does (they ship together). Drift-risk noted: not importable
37
+ * (pyric doesn't export it), so a check, not a re-use. */
38
+ export const EXPECTED_CONTROLLER_BLOB_VERSION = 1;
39
+
40
+ export interface PyricStateFile {
41
+ version: typeof STATE_FILE_VERSION;
42
+ /** The sandbox persistence controller's blob, verbatim. Null = never flushed. */
43
+ firestore: unknown | null;
44
+ auth: { users: ExportedUsers } | null;
45
+ }
46
+
47
+ export type StateSection = 'firestore' | 'auth';
48
+
49
+ export class StateFileError extends Error {
50
+ constructor(message: string) {
51
+ super(message);
52
+ this.name = 'StateFileError';
53
+ }
54
+ }
55
+
56
+ export interface StateStore {
57
+ /** The project directory this store persists for (for the
58
+ * promote-the-wrong-project guard). */
59
+ projectDir: string;
60
+ /** Absolute path of the state file. */
61
+ path: string;
62
+ /** Absolute path of the one-deep recovery backup (`state.json.bak`). */
63
+ backupPath: string;
64
+ /** Parsed envelope, or null when the file doesn't exist. Throws
65
+ * {@link StateFileError} on corrupt JSON or version mismatch —
66
+ * fail fast over silent data loss. */
67
+ load(): PyricStateFile | null;
68
+ readSection(section: StateSection): unknown | null;
69
+ /** Read-modify-write the envelope; atomic tmp+rename. A firestore write
70
+ * that would collapse a non-empty doc set to empty first preserves the
71
+ * prior file as `.bak` (pre-mortem #2). */
72
+ writeSection(section: StateSection, value: unknown): void;
73
+ exists(): boolean;
74
+ }
75
+
76
+ export const STATE_RELATIVE_PATH = join('.pyric', 'state', 'state.json');
77
+
78
+ export function createStateStore(projectDir: string): StateStore {
79
+ const path = join(projectDir, STATE_RELATIVE_PATH);
80
+ const backupPath = `${path}.bak`;
81
+
82
+ const load = (): PyricStateFile | null => {
83
+ if (!existsSync(path)) return null;
84
+ let parsed: unknown;
85
+ try {
86
+ parsed = JSON.parse(readFileSync(path, 'utf8'));
87
+ } catch (e) {
88
+ throw new StateFileError(
89
+ `state file at ${path} is not valid JSON (${e instanceof Error ? e.message : String(e)}). ` +
90
+ 'Inspect or delete it to continue — pyric will not overwrite it silently.',
91
+ );
92
+ }
93
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
94
+ throw new StateFileError(`state file at ${path} is not an object.`);
95
+ }
96
+ const file = parsed as PyricStateFile;
97
+ if (file.version !== STATE_FILE_VERSION) {
98
+ throw new StateFileError(
99
+ `state file at ${path} has version ${String(file.version)}; this @pyric/cli expects ` +
100
+ `${STATE_FILE_VERSION}. Delete it (or promote it with a matching @pyric/cli) to continue.`,
101
+ );
102
+ }
103
+ // Inner controller-blob version (pre-mortem #6): the page's
104
+ // `deserializeSnapshot` validates this at restore — so a pyric upgrade
105
+ // that bumps its SCHEMA_VERSION would otherwise fail IN THE PAGE while
106
+ // the banner cheerfully reports "N docs restored". Peek it server-side
107
+ // and fail fast here instead. (Read-only peek; the section is still
108
+ // never re-encoded.) EXPECTED_CONTROLLER_BLOB_VERSION mirrors pyric's
109
+ // serialize `SCHEMA_VERSION` and must bump in lockstep — they ship
110
+ // together in this monorepo.
111
+ const inner = (file.firestore as { version?: unknown } | null)?.version;
112
+ if (inner !== undefined && inner !== EXPECTED_CONTROLLER_BLOB_VERSION) {
113
+ throw new StateFileError(
114
+ `state file at ${path} holds a firestore blob of version ${String(inner)}; this ` +
115
+ `pyric expects ${EXPECTED_CONTROLLER_BLOB_VERSION} (pyric was likely upgraded). ` +
116
+ 'Delete the state file or re-promote it with a matching pyric to continue.',
117
+ );
118
+ }
119
+ return file;
120
+ };
121
+
122
+ const writeAtomic = (file: PyricStateFile): void => {
123
+ mkdirSync(dirname(path), { recursive: true });
124
+ const tmp = `${path}.tmp-${process.pid}`;
125
+ writeFileSync(tmp, JSON.stringify(file, null, 2) + '\n', 'utf8');
126
+ renameSync(tmp, path); // same volume — atomic replace
127
+ };
128
+
129
+ return {
130
+ projectDir,
131
+ path,
132
+ backupPath,
133
+ load,
134
+ exists: () => existsSync(path),
135
+ readSection(section) {
136
+ return load()?.[section] ?? null;
137
+ },
138
+ writeSection(section, value) {
139
+ const current = load() ?? { version: STATE_FILE_VERSION, firestore: null, auth: null };
140
+ // Data-loss guard (flow doc section 3c / pre-mortem #2): a `sandbox.reset()`
141
+ // makes the persistence controller flush EMPTY firestore over the
142
+ // live file (pyric flushes on session_boundary by design), and the
143
+ // backend write can't tell that from a legitimate delete-all. Before
144
+ // a firestore write would collapse a NON-empty doc set to empty, keep
145
+ // the prior file as `.bak` (one-deep) so it's recoverable. Intent-
146
+ // agnostic on purpose — covers reset, agent error, and accidental
147
+ // delete-all uniformly.
148
+ if (
149
+ section === 'firestore' &&
150
+ firestoreDocCount(value) === 0 &&
151
+ firestoreDocCount(current.firestore) > 0 &&
152
+ existsSync(path)
153
+ ) {
154
+ copyFileSync(path, backupPath);
155
+ }
156
+ writeAtomic({ ...current, [section]: value } as PyricStateFile);
157
+ },
158
+ };
159
+ }
160
+
161
+ /** Doc count inside a controller firestore blob (`{…, firestore: {path:
162
+ * fields}}`), 0 for null/empty/malformed. */
163
+ export function firestoreDocCount(section: unknown): number {
164
+ const docs = (section as { firestore?: Record<string, unknown> } | null)?.firestore;
165
+ return docs && typeof docs === 'object' ? Object.keys(docs).length : 0;
166
+ }
@@ -0,0 +1,185 @@
1
+ /**
2
+ * `diskProjectStore(root)` — a {@link ProjectStore} where each project is a
3
+ * subdirectory of `root` carrying a small metadata file (`.pyric-project.json`,
4
+ * shape {@link ProjectMeta}). `open(id)` hands back a {@link ProjectHandle}
5
+ * whose `workspace` is a {@link diskWorkspace} rooted at that subdirectory.
6
+ *
7
+ * Project ids ARE the directory names; they're slugged from the title (or
8
+ * generated) and validated to a safe charset so an id can never traverse out of
9
+ * `root`. The metadata file lives alongside the project's working tree — it's
10
+ * skipped by `diskWorkspace.list` callers only by convention (the file tree
11
+ * shows it; that's acceptable for a `.pyric-*` dotfile).
12
+ */
13
+ import { mkdir, readFile, readdir, rm, stat, writeFile } from 'node:fs/promises';
14
+ import { join, resolve } from 'node:path';
15
+
16
+ import { diskWorkspace } from './disk-workspace.js';
17
+ import type {
18
+ ProjectHandle,
19
+ ProjectMeta,
20
+ ProjectStore,
21
+ } from './store-types.js';
22
+
23
+ const META_FILE = '.pyric-project.json';
24
+
25
+ /** Thrown when an id isn't a safe single path segment. */
26
+ export class ProjectIdError extends Error {
27
+ constructor(id: string) {
28
+ super(`invalid project id: '${id}'`);
29
+ this.name = 'ProjectIdError';
30
+ }
31
+ }
32
+
33
+ /** Slug a title into a filesystem-safe id segment. */
34
+ export function slugifyProjectId(title: string): string {
35
+ const slug = title
36
+ .toLowerCase()
37
+ .trim()
38
+ .replace(/[^a-z0-9]+/g, '-')
39
+ .replace(/^-+|-+$/g, '')
40
+ .slice(0, 48);
41
+ return slug;
42
+ }
43
+
44
+ /** A safe id is a single non-dot path segment of `[a-z0-9-_]`. */
45
+ function assertSafeId(id: string): void {
46
+ if (!/^[a-z0-9][a-z0-9_-]*$/.test(id) || id.includes('/') || id.includes('..')) {
47
+ throw new ProjectIdError(id);
48
+ }
49
+ }
50
+
51
+ export function diskProjectStore(rootDir: string): ProjectStore {
52
+ const root = resolve(rootDir);
53
+
54
+ function projectDir(id: string): string {
55
+ assertSafeId(id);
56
+ return join(root, id);
57
+ }
58
+
59
+ async function readMeta(id: string): Promise<ProjectMeta | null> {
60
+ try {
61
+ const raw = await readFile(join(projectDir(id), META_FILE), 'utf8');
62
+ const parsed = JSON.parse(raw) as Partial<ProjectMeta>;
63
+ const now = Date.now();
64
+ return {
65
+ id,
66
+ title: typeof parsed.title === 'string' ? parsed.title : id,
67
+ createdAt: typeof parsed.createdAt === 'number' ? parsed.createdAt : now,
68
+ updatedAt: typeof parsed.updatedAt === 'number' ? parsed.updatedAt : now,
69
+ };
70
+ } catch (e) {
71
+ if ((e as NodeJS.ErrnoException).code === 'ENOENT') return null;
72
+ throw e;
73
+ }
74
+ }
75
+
76
+ async function writeMeta(meta: ProjectMeta): Promise<void> {
77
+ const dir = projectDir(meta.id);
78
+ await mkdir(dir, { recursive: true });
79
+ await writeFile(
80
+ join(dir, META_FILE),
81
+ JSON.stringify(meta, null, 2) + '\n',
82
+ 'utf8',
83
+ );
84
+ }
85
+
86
+ return {
87
+ async list() {
88
+ let names: string[];
89
+ try {
90
+ names = await readdir(root);
91
+ } catch (e) {
92
+ if ((e as NodeJS.ErrnoException).code === 'ENOENT') return [];
93
+ throw e;
94
+ }
95
+ const metas: ProjectMeta[] = [];
96
+ for (const name of names) {
97
+ if (name.startsWith('.')) continue;
98
+ let isDir = false;
99
+ try {
100
+ isDir = (await stat(join(root, name))).isDirectory();
101
+ } catch {
102
+ continue;
103
+ }
104
+ if (!isDir) continue;
105
+ try {
106
+ const meta = await readMeta(name);
107
+ // A directory without a meta file is still a project — synthesize.
108
+ metas.push(
109
+ meta ?? {
110
+ id: name,
111
+ title: name,
112
+ createdAt: 0,
113
+ updatedAt: 0,
114
+ },
115
+ );
116
+ } catch (e) {
117
+ if (e instanceof ProjectIdError) continue; // not a valid project dir
118
+ throw e;
119
+ }
120
+ }
121
+ metas.sort((a, b) => b.updatedAt - a.updatedAt);
122
+ return metas;
123
+ },
124
+
125
+ async open(id) {
126
+ const dir = projectDir(id);
127
+ let meta = await readMeta(id);
128
+ if (meta === null) {
129
+ // Allow opening a bare directory that exists but lacks metadata.
130
+ try {
131
+ if ((await stat(dir)).isDirectory()) {
132
+ meta = { id, title: id, createdAt: Date.now(), updatedAt: Date.now() };
133
+ }
134
+ } catch {
135
+ /* fall through to throw */
136
+ }
137
+ }
138
+ if (meta === null) {
139
+ throw new Error(`project not found: '${id}'`);
140
+ }
141
+ const handle: ProjectHandle = {
142
+ meta,
143
+ workspace: diskWorkspace(dir),
144
+ };
145
+ return handle;
146
+ },
147
+
148
+ async create(input) {
149
+ const title = input.title?.trim() || 'untitled';
150
+ const base = slugifyProjectId(title) || 'project';
151
+ // Disambiguate against existing dirs.
152
+ let id = base;
153
+ let n = 1;
154
+ while (true) {
155
+ try {
156
+ await stat(join(root, id));
157
+ id = `${base}-${++n}`;
158
+ } catch {
159
+ break; // free
160
+ }
161
+ }
162
+ const now = Date.now();
163
+ const meta: ProjectMeta = { id, title, createdAt: now, updatedAt: now };
164
+ await writeMeta(meta);
165
+ return meta;
166
+ },
167
+
168
+ async update(id, patch) {
169
+ const existing = await readMeta(id);
170
+ if (existing === null) throw new Error(`project not found: '${id}'`);
171
+ const next: ProjectMeta = {
172
+ ...existing,
173
+ ...patch,
174
+ id, // id is immutable
175
+ updatedAt: Date.now(),
176
+ };
177
+ await writeMeta(next);
178
+ },
179
+
180
+ async remove(id) {
181
+ const dir = projectDir(id);
182
+ await rm(dir, { recursive: true, force: true });
183
+ },
184
+ };
185
+ }
@@ -0,0 +1,162 @@
1
+ /**
2
+ * `diskWorkspace(dir)` — a {@link WorkspaceStore} over a real filesystem
3
+ * directory (the Studio `local` mode's project file tree, served at
4
+ * `/__pyric/workspace`).
5
+ *
6
+ * Paths are project-relative POSIX strings (`src/app.tsx`, `firestore.rules`).
7
+ * Every op resolves the path inside `dir` and REFUSES anything that escapes the
8
+ * root (`..` traversal, absolute paths) — the same posture the static server
9
+ * takes for `publicDir`. `watch(cb)` uses a recursive `fs.watch` and reports
10
+ * create/update/delete as {@link WorkspaceChange} events.
11
+ */
12
+ import {
13
+ mkdir,
14
+ readFile,
15
+ readdir,
16
+ rm,
17
+ stat,
18
+ writeFile,
19
+ } from 'node:fs/promises';
20
+ import { existsSync, mkdirSync, watch as fsWatch, type FSWatcher } from 'node:fs';
21
+ import { dirname, join, posix, relative, resolve, sep } from 'node:path';
22
+
23
+ import type {
24
+ WorkspaceChange,
25
+ WorkspaceEntry,
26
+ WorkspaceStore,
27
+ } from './store-types.js';
28
+
29
+ /** Thrown when a request path escapes the workspace root. */
30
+ export class WorkspacePathError extends Error {
31
+ constructor(path: string) {
32
+ super(`workspace path escapes root: '${path}'`);
33
+ this.name = 'WorkspacePathError';
34
+ }
35
+ }
36
+
37
+ /**
38
+ * Resolve a project-relative POSIX path to an absolute OS path inside `root`,
39
+ * or throw {@link WorkspacePathError} if it would escape. Leading slashes are
40
+ * stripped (treated as project-root-relative); empty / `.` resolves to the
41
+ * root itself.
42
+ */
43
+ export function resolveWorkspacePath(root: string, rel: string): string {
44
+ const cleaned = rel.replace(/^\/+/, '');
45
+ // Normalize via POSIX semantics first so `a/../b` collapses predictably,
46
+ // then map separators to the host OS.
47
+ const normalized = posix.normalize(cleaned === '' ? '.' : cleaned);
48
+ if (normalized === '..' || normalized.startsWith('../')) {
49
+ throw new WorkspacePathError(rel);
50
+ }
51
+ const abs = resolve(root, normalized.split('/').join(sep));
52
+ if (abs !== root && !abs.startsWith(root + sep)) {
53
+ throw new WorkspacePathError(rel);
54
+ }
55
+ return abs;
56
+ }
57
+
58
+ /** Convert an absolute OS path under `root` to a project-relative POSIX path. */
59
+ function toPosixRel(root: string, abs: string): string {
60
+ return relative(root, abs).split(sep).join('/');
61
+ }
62
+
63
+ export function diskWorkspace(dir: string): WorkspaceStore {
64
+ const root = resolve(dir);
65
+
66
+ return {
67
+ async read(path) {
68
+ const abs = resolveWorkspacePath(root, path);
69
+ try {
70
+ return await readFile(abs, 'utf8');
71
+ } catch (e) {
72
+ if ((e as NodeJS.ErrnoException).code === 'ENOENT') return null;
73
+ // A directory read errors as EISDIR — treat as "no file content".
74
+ if ((e as NodeJS.ErrnoException).code === 'EISDIR') return null;
75
+ throw e;
76
+ }
77
+ },
78
+
79
+ async write(path, content) {
80
+ const abs = resolveWorkspacePath(root, path);
81
+ await mkdir(dirname(abs), { recursive: true });
82
+ await writeFile(abs, content, 'utf8');
83
+ },
84
+
85
+ async list(dir) {
86
+ const abs = resolveWorkspacePath(root, dir ?? '');
87
+ let names: string[];
88
+ try {
89
+ names = await readdir(abs);
90
+ } catch (e) {
91
+ if ((e as NodeJS.ErrnoException).code === 'ENOENT') return [];
92
+ throw e;
93
+ }
94
+ const entries: WorkspaceEntry[] = [];
95
+ for (const name of names) {
96
+ const childAbs = join(abs, name);
97
+ let isDir = false;
98
+ try {
99
+ isDir = (await stat(childAbs)).isDirectory();
100
+ } catch {
101
+ continue; // raced away between readdir and stat
102
+ }
103
+ entries.push({
104
+ path: toPosixRel(root, childAbs),
105
+ kind: isDir ? 'dir' : 'file',
106
+ });
107
+ }
108
+ entries.sort((a, b) => a.path.localeCompare(b.path));
109
+ return entries;
110
+ },
111
+
112
+ async remove(path) {
113
+ const abs = resolveWorkspacePath(root, path);
114
+ if (abs === root) throw new WorkspacePathError(path); // never nuke the root
115
+ await rm(abs, { recursive: true, force: true });
116
+ },
117
+
118
+ watch(cb) {
119
+ if (!existsSync(root)) {
120
+ try {
121
+ // best-effort: ensure the dir exists so the watcher can attach.
122
+ mkdirSync(root, { recursive: true });
123
+ } catch {
124
+ /* watcher just won't fire */
125
+ }
126
+ }
127
+ let watcher: FSWatcher | null = null;
128
+ try {
129
+ watcher = fsWatch(root, { recursive: true }, (_event, filename) => {
130
+ if (filename == null) return;
131
+ const relPath = String(filename).split(sep).join('/');
132
+ // fs.watch doesn't tell us create vs update vs delete reliably across
133
+ // platforms; derive it from current existence.
134
+ let abs: string;
135
+ try {
136
+ abs = resolveWorkspacePath(root, relPath);
137
+ } catch {
138
+ return; // ignore anything outside the root
139
+ }
140
+ const type: WorkspaceChange['type'] = existsSync(abs)
141
+ ? 'update'
142
+ : 'delete';
143
+ cb({ path: relPath, type });
144
+ });
145
+ } catch {
146
+ // Recursive watch unsupported here — degrade to no live updates.
147
+ return () => {};
148
+ }
149
+ // Watcher failure (EMFILE, root renamed) must not surface as an
150
+ // unhandled 'error' event — that would kill the serve process.
151
+ // Degrade to no live updates instead.
152
+ watcher.on('error', () => {
153
+ watcher?.close();
154
+ watcher = null;
155
+ });
156
+ return () => {
157
+ watcher?.close();
158
+ watcher = null;
159
+ };
160
+ },
161
+ };
162
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Studio storage (Track T3) — disk-backed `WorkspaceStore` / `ProjectStore`
3
+ * impls + the `/__pyric/workspace` and `/__pyric/projects` serve routes that
4
+ * `@pyric/studio`'s `local` mode talks to. See the design rationale.
5
+ */
6
+ export { diskWorkspace, resolveWorkspacePath, WorkspacePathError } from './disk-workspace.js';
7
+ export {
8
+ diskProjectStore,
9
+ slugifyProjectId,
10
+ ProjectIdError,
11
+ } from './disk-project-store.js';
12
+ export { createStudioRoutes, type StudioRouteOptions } from './routes.js';
13
+ export type {
14
+ ProjectHandle,
15
+ ProjectMeta,
16
+ ProjectStore,
17
+ WorkspaceChange,
18
+ WorkspaceEntry,
19
+ WorkspaceStore,
20
+ } from './store-types.js';