@pyric/cli 0.1.0-alpha.27 → 0.1.0-alpha.28

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 (253) hide show
  1. package/dist/bridge/surface/arguments/assurance.d.ts +6 -6
  2. package/dist/bridge/surface/arguments/messaging.d.ts +2 -2
  3. package/dist/bridge/surface/methods/assurance/map.d.ts +6 -6
  4. package/dist/bridge/surface/methods/messaging/send.d.ts +4 -4
  5. package/dist/bridge/surface/render/discriminator-schemas.d.ts +4 -4
  6. package/dist/cli/index.d.ts +1 -0
  7. package/dist/cli/index.d.ts.map +1 -1
  8. package/dist/cli/index.js +15 -1
  9. package/dist/cli/index.js.map +1 -1
  10. package/dist/cli/run.d.ts +42 -0
  11. package/dist/cli/run.d.ts.map +1 -0
  12. package/dist/cli/run.js +115 -0
  13. package/dist/cli/run.js.map +1 -0
  14. package/dist/conformance/.generated/can-i-use.d.ts.map +1 -1
  15. package/dist/conformance/.generated/can-i-use.js +1 -1
  16. package/dist/conformance/.generated/can-i-use.js.map +1 -1
  17. package/dist/conformance/.generated/conformance-docs.d.ts.map +1 -1
  18. package/dist/conformance/.generated/conformance-docs.js +1 -1
  19. package/dist/conformance/.generated/conformance-docs.js.map +1 -1
  20. package/dist/remote/local-storage-url.d.ts +2 -15
  21. package/dist/remote/local-storage-url.d.ts.map +1 -1
  22. package/dist/remote/local-storage-url.js +1 -60
  23. package/dist/remote/local-storage-url.js.map +1 -1
  24. package/dist/serve/assets/avatar-route.d.ts.map +1 -1
  25. package/dist/serve/assets/avatar-route.js +6 -6
  26. package/dist/serve/assets/avatar-route.js.map +1 -1
  27. package/dist/serve/assets/resolver.d.ts +3 -0
  28. package/dist/serve/assets/resolver.d.ts.map +1 -1
  29. package/dist/serve/assets/resolver.js +1 -1
  30. package/dist/serve/assets/resolver.js.map +1 -1
  31. package/dist/serve/entries/app-client.d.ts.map +1 -1
  32. package/dist/serve/entries/app-client.js +6 -0
  33. package/dist/serve/entries/app-client.js.map +1 -1
  34. package/dist/serve/site-ui/_astro/{app.DpzsBfos.js → app.BCNb6gMq.js} +1 -1
  35. package/dist/serve/site-ui/_astro/{app.CiMHEvEW.js → app.DhoRPDdN.js} +20 -20
  36. package/dist/serve/site-ui/_astro/{seed.BpXSRzLo.js → seed.BO5CXcqE.js} +1 -1
  37. package/dist/serve/site-ui/auth/index.html +1 -1
  38. package/dist/serve/site-ui/docs/_rhythm/index.html +1 -1
  39. package/dist/serve/site-ui/docs/agent/set-up-your-agent/index.html +1 -1
  40. package/dist/serve/site-ui/docs/agent/watch-and-review/index.html +1 -1
  41. package/dist/serve/site-ui/docs/agent/work-with-an-agent/index.html +1 -1
  42. package/dist/serve/site-ui/docs/ai-compat/index.html +1 -1
  43. package/dist/serve/site-ui/docs/api-reference/index.html +2 -1
  44. package/dist/serve/site-ui/docs/api-reference.md +1 -0
  45. package/dist/serve/site-ui/docs/app-compat/index.html +1 -1
  46. package/dist/serve/site-ui/docs/auth-compat/index.html +2 -2
  47. package/dist/serve/site-ui/docs/auth-compat.md +1 -1
  48. package/dist/serve/site-ui/docs/auth-flutter-compat/index.html +1 -1
  49. package/dist/serve/site-ui/docs/auth-kotlin-compat/index.html +1 -1
  50. package/dist/serve/site-ui/docs/auth-swift-compat/index.html +1 -1
  51. package/dist/serve/site-ui/docs/build/ai-logic/index.html +1 -1
  52. package/dist/serve/site-ui/docs/build/authentication/index.html +1 -1
  53. package/dist/serve/site-ui/docs/build/cloud-firestore/index.html +1 -1
  54. package/dist/serve/site-ui/docs/build/cloud-messaging/index.html +1 -1
  55. package/dist/serve/site-ui/docs/build/cloud-storage/index.html +13 -2
  56. package/dist/serve/site-ui/docs/build/cloud-storage.md +23 -0
  57. package/dist/serve/site-ui/docs/build/default-avatars/index.html +1 -1
  58. package/dist/serve/site-ui/docs/build/inspect-activity/index.html +1 -1
  59. package/dist/serve/site-ui/docs/build/realtime-database/index.html +1 -1
  60. package/dist/serve/site-ui/docs/conformance-scores/index.html +1 -1
  61. package/dist/serve/site-ui/docs/create-pyric-reference-api/index.html +1 -1
  62. package/dist/serve/site-ui/docs/database-compat/index.html +1 -1
  63. package/dist/serve/site-ui/docs/examples/chess/index.html +1 -1
  64. package/dist/serve/site-ui/docs/examples/index.html +1 -1
  65. package/dist/serve/site-ui/docs/firestore-compat/index.html +1 -1
  66. package/dist/serve/site-ui/docs/firestore-flutter-compat/index.html +1 -1
  67. package/dist/serve/site-ui/docs/firestore-kotlin-compat/index.html +1 -1
  68. package/dist/serve/site-ui/docs/firestore-swift-compat/index.html +1 -1
  69. package/dist/serve/site-ui/docs/functions-rtdb-compat/index.html +1 -1
  70. package/dist/serve/site-ui/docs/get-started/backend-bundlers/index.html +1 -1
  71. package/dist/serve/site-ui/docs/get-started/flutter/index.html +1 -1
  72. package/dist/serve/site-ui/docs/get-started/how-the-swap-works/index.html +1 -1
  73. package/dist/serve/site-ui/docs/get-started/nextjs/index.html +1 -1
  74. package/dist/serve/site-ui/docs/get-started/node-host/index.html +1 -1
  75. package/dist/serve/site-ui/docs/get-started/start-building/index.html +1 -1
  76. package/dist/serve/site-ui/docs/get-started/vite/index.html +4 -2
  77. package/dist/serve/site-ui/docs/get-started/vite.md +4 -0
  78. package/dist/serve/site-ui/docs/index.json +50 -0
  79. package/dist/serve/site-ui/docs/messaging-compat/index.html +1 -1
  80. package/dist/serve/site-ui/docs/observe/resolve-runtime-status/index.html +1 -1
  81. package/dist/serve/site-ui/docs/observe/see-whats-happening/index.html +1 -1
  82. package/dist/serve/site-ui/docs/observe/shape-your-data/index.html +1 -1
  83. package/dist/serve/site-ui/docs/observe/switch-and-impersonate-identities/index.html +1 -1
  84. package/dist/serve/site-ui/docs/overview/index.html +1 -1
  85. package/dist/serve/site-ui/docs/pyric-admin-app-check-reference-api/index.html +1 -1
  86. package/dist/serve/site-ui/docs/pyric-admin-app-reference-api/index.html +1 -1
  87. package/dist/serve/site-ui/docs/pyric-admin-auth-reference-api/index.html +1 -1
  88. package/dist/serve/site-ui/docs/pyric-admin-data-connect-reference-api/index.html +1 -1
  89. package/dist/serve/site-ui/docs/pyric-admin-database-reference-api/index.html +1 -1
  90. package/dist/serve/site-ui/docs/pyric-admin-eventarc-reference-api/index.html +1 -1
  91. package/dist/serve/site-ui/docs/pyric-admin-extensions-reference-api/index.html +1 -1
  92. package/dist/serve/site-ui/docs/pyric-admin-firestore-reference-api/index.html +1 -1
  93. package/dist/serve/site-ui/docs/pyric-admin-functions-reference-api/index.html +1 -1
  94. package/dist/serve/site-ui/docs/pyric-admin-installations-reference-api/index.html +1 -1
  95. package/dist/serve/site-ui/docs/pyric-admin-instance-id-reference-api/index.html +1 -1
  96. package/dist/serve/site-ui/docs/pyric-admin-machine-learning-reference-api/index.html +1 -1
  97. package/dist/serve/site-ui/docs/pyric-admin-messaging-reference-api/index.html +1 -1
  98. package/dist/serve/site-ui/docs/pyric-admin-phone-number-verification-reference-api/index.html +1 -1
  99. package/dist/serve/site-ui/docs/pyric-admin-project-management-reference-api/index.html +1 -1
  100. package/dist/serve/site-ui/docs/pyric-admin-reference-api/index.html +1 -1
  101. package/dist/serve/site-ui/docs/pyric-admin-remote-config-reference-api/index.html +1 -1
  102. package/dist/serve/site-ui/docs/pyric-admin-security-rules-reference-api/index.html +1 -1
  103. package/dist/serve/site-ui/docs/pyric-admin-storage-reference-api/index.html +1 -1
  104. package/dist/serve/site-ui/docs/pyric-ai-reference-api/index.html +1 -1
  105. package/dist/serve/site-ui/docs/pyric-ai-scripting-reference-api/index.html +1 -1
  106. package/dist/serve/site-ui/docs/pyric-analytics-reference-api/index.html +1 -1
  107. package/dist/serve/site-ui/docs/pyric-app-check-reference-api/index.html +1 -1
  108. package/dist/serve/site-ui/docs/pyric-app-reference-api/index.html +1 -1
  109. package/dist/serve/site-ui/docs/pyric-auth-reference-api/index.html +2 -2
  110. package/dist/serve/site-ui/docs/pyric-auth-reference-api.md +1 -1
  111. package/dist/serve/site-ui/docs/pyric-cli-assurance-browser-reference-api/index.html +1 -1
  112. package/dist/serve/site-ui/docs/pyric-cli-assurance-reference-api/index.html +1 -1
  113. package/dist/serve/site-ui/docs/pyric-cli-bridge-client-reference-api/index.html +1 -1
  114. package/dist/serve/site-ui/docs/pyric-cli-bridge-reference-api/index.html +1 -1
  115. package/dist/serve/site-ui/docs/pyric-cli-bundler-reference-api/index.html +1 -1
  116. package/dist/serve/site-ui/docs/pyric-cli-conformance-browser-reference-api/index.html +1 -1
  117. package/dist/serve/site-ui/docs/pyric-cli-conformance-docs-reference-api/index.html +1 -1
  118. package/dist/serve/site-ui/docs/pyric-cli-conformance-reference-api/index.html +1 -1
  119. package/dist/serve/site-ui/docs/pyric-cli-credentials-node-reference-api/index.html +1 -1
  120. package/dist/serve/site-ui/docs/pyric-cli-discover-reference-api/index.html +1 -1
  121. package/dist/serve/site-ui/docs/pyric-cli-flow-reference-api/index.html +1 -1
  122. package/dist/serve/site-ui/docs/pyric-cli-next-reference-api/index.html +1 -1
  123. package/dist/serve/site-ui/docs/pyric-cli-register-app-bridge-reference-api/index.html +1 -1
  124. package/dist/serve/site-ui/docs/pyric-cli-register-client-app-bridge-reference-api/index.html +1 -1
  125. package/dist/serve/site-ui/docs/pyric-cli-register-client-app-reference-api/index.html +1 -1
  126. package/dist/serve/site-ui/docs/pyric-cli-register-client-auth-reference-api/index.html +1 -1
  127. package/dist/serve/site-ui/docs/pyric-cli-register-client-database-reference-api/index.html +1 -1
  128. package/dist/serve/site-ui/docs/pyric-cli-register-client-firestore-reference-api/index.html +1 -1
  129. package/dist/serve/site-ui/docs/pyric-cli-register-client-storage-reference-api/index.html +1 -1
  130. package/dist/serve/site-ui/docs/pyric-cli-register-reference-api/index.html +1 -1
  131. package/dist/serve/site-ui/docs/pyric-cli-remote-reference-api/index.html +1 -1
  132. package/dist/serve/site-ui/docs/pyric-cli-repro-reference-api/index.html +1 -1
  133. package/dist/serve/site-ui/docs/pyric-cli-serve-worker-reference-api/index.html +1 -1
  134. package/dist/serve/site-ui/docs/pyric-cli-verify-reference-api/index.html +1 -1
  135. package/dist/serve/site-ui/docs/pyric-cli-vite-reference-api/index.html +17 -3
  136. package/dist/serve/site-ui/docs/pyric-cli-vite-reference-api.md +7 -5
  137. package/dist/serve/site-ui/docs/pyric-cli-vite-ssr-loader-reference-api/index.html +1 -1
  138. package/dist/serve/site-ui/docs/pyric-database-reference-api/index.html +1 -1
  139. package/dist/serve/site-ui/docs/pyric-firestore-lite-reference-api/index.html +1 -1
  140. package/dist/serve/site-ui/docs/pyric-firestore-reference-api/index.html +1 -1
  141. package/dist/serve/site-ui/docs/pyric-functions-reference-api/index.html +1 -1
  142. package/dist/serve/site-ui/docs/pyric-messaging-reference-api/index.html +1 -1
  143. package/dist/serve/site-ui/docs/pyric-messaging-sw-reference-api/index.html +1 -1
  144. package/dist/serve/site-ui/docs/pyric-performance-reference-api/index.html +1 -1
  145. package/dist/serve/site-ui/docs/pyric-remote-config-reference-api/index.html +1 -1
  146. package/dist/serve/site-ui/docs/pyric-rules-reference-api/index.html +1 -1
  147. package/dist/serve/site-ui/docs/pyric-sandbox-branches-store-reference-api/index.html +1 -1
  148. package/dist/serve/site-ui/docs/pyric-sandbox-checkpoints-directory-reference-api/index.html +1 -1
  149. package/dist/serve/site-ui/docs/pyric-sandbox-checkpoints-reference-api/index.html +1 -1
  150. package/dist/serve/site-ui/docs/pyric-sandbox-database-reference-api/index.html +1 -1
  151. package/dist/serve/site-ui/docs/pyric-sandbox-firestore-reference-api/index.html +1 -1
  152. package/dist/serve/site-ui/docs/pyric-sandbox-reference-api/index.html +1 -1
  153. package/dist/serve/site-ui/docs/pyric-storage-reference-api/index.html +1 -1
  154. package/dist/serve/site-ui/docs/pyric-storage-sw-reference-api/index.html +102 -0
  155. package/dist/serve/site-ui/docs/pyric-storage-sw-reference-api.md +76 -0
  156. package/dist/serve/site-ui/docs/pyric-ui-agents-reference-api/index.html +1 -1
  157. package/dist/serve/site-ui/docs/pyric-ui-auth-hooks-reference-api/index.html +1 -1
  158. package/dist/serve/site-ui/docs/pyric-ui-auth-reference-api/index.html +1 -1
  159. package/dist/serve/site-ui/docs/pyric-ui-events-hooks-reference-api/index.html +1 -1
  160. package/dist/serve/site-ui/docs/pyric-ui-events-reference-api/index.html +1 -1
  161. package/dist/serve/site-ui/docs/pyric-ui-firestore-hooks-reference-api/index.html +1 -1
  162. package/dist/serve/site-ui/docs/pyric-ui-firestore-reference-api/index.html +1 -1
  163. package/dist/serve/site-ui/docs/pyric-ui-listener-owner-reference-api/index.html +1 -1
  164. package/dist/serve/site-ui/docs/pyric-ui-primitives-reference-api/index.html +1 -1
  165. package/dist/serve/site-ui/docs/pyric-ui-rtdb-reference-api/index.html +1 -1
  166. package/dist/serve/site-ui/docs/pyric-ui-rules-hooks-reference-api/index.html +1 -1
  167. package/dist/serve/site-ui/docs/pyric-ui-rules-reference-api/index.html +1 -1
  168. package/dist/serve/site-ui/docs/pyric-ui-storage-hooks-reference-api/index.html +1 -1
  169. package/dist/serve/site-ui/docs/pyric-ui-storage-reference-api/index.html +1 -1
  170. package/dist/serve/site-ui/docs/pyric-ui-traffic-hooks-reference-api/index.html +1 -1
  171. package/dist/serve/site-ui/docs/pyric-ui-traffic-reference-api/index.html +1 -1
  172. package/dist/serve/site-ui/docs/reference/cli/index.html +12 -2
  173. package/dist/serve/site-ui/docs/reference/cli.md +24 -0
  174. package/dist/serve/site-ui/docs/rtdb-flutter-compat/index.html +1 -1
  175. package/dist/serve/site-ui/docs/rtdb-kotlin-compat/index.html +1 -1
  176. package/dist/serve/site-ui/docs/rtdb-swift-compat/index.html +1 -1
  177. package/dist/serve/site-ui/docs/rules-compat/index.html +1 -1
  178. package/dist/serve/site-ui/docs/secure/audit-your-rules/index.html +1 -1
  179. package/dist/serve/site-ui/docs/secure/firestore-rules-limits/index.html +1 -1
  180. package/dist/serve/site-ui/docs/secure/read-a-denial/index.html +1 -1
  181. package/dist/serve/site-ui/docs/secure/rtdb-rules-in-typescript/index.html +1 -1
  182. package/dist/serve/site-ui/docs/secure/rtdb-rules-standard-library/index.html +1 -1
  183. package/dist/serve/site-ui/docs/secure/rules-standard-library/index.html +1 -1
  184. package/dist/serve/site-ui/docs/secure/secure-it-with-rules/index.html +1 -1
  185. package/dist/serve/site-ui/docs/secure/simulate-and-lint/index.html +1 -1
  186. package/dist/serve/site-ui/docs/secure/write-a-rules-test-suite/index.html +1 -1
  187. package/dist/serve/site-ui/docs/ship/ship-to-production/index.html +1 -1
  188. package/dist/serve/site-ui/docs/ship/test-in-node/index.html +1 -1
  189. package/dist/serve/site-ui/docs/storage-compat/index.html +1 -1
  190. package/dist/serve/site-ui/docs/trust/how-we-know-it-matches-firebase/index.html +1 -1
  191. package/dist/serve/site-ui/docs/trust/rules-standard-library/index.html +1 -1
  192. package/dist/serve/site-ui/docs/trust/versioning-and-compatibility/index.html +1 -1
  193. package/dist/serve/site-ui/docs/tutorial/index.html +1 -1
  194. package/dist/serve/site-ui/examples/chess/index.html +1 -1
  195. package/dist/serve/site-ui/firestore/index.html +1 -1
  196. package/dist/serve/site-ui/llms.txt +1 -0
  197. package/dist/serve/site-ui/rtdb/index.html +1 -1
  198. package/dist/serve/site-ui/settings/index.html +1 -1
  199. package/dist/serve/site-ui/storage/index.html +1 -1
  200. package/dist/serve/site-ui/studio/index.html +1 -1
  201. package/dist/serve/site-ui/traffic/index.html +1 -1
  202. package/dist/serve/vite-plugin.d.ts +6 -0
  203. package/dist/serve/vite-plugin.d.ts.map +1 -1
  204. package/dist/serve/vite-plugin.js +11 -1
  205. package/dist/serve/vite-plugin.js.map +1 -1
  206. package/dist/serve/vite-service-worker.d.ts +12 -0
  207. package/dist/serve/vite-service-worker.d.ts.map +1 -0
  208. package/dist/serve/vite-service-worker.js +33 -0
  209. package/dist/serve/vite-service-worker.js.map +1 -0
  210. package/dist/serve/worker/client/auth.d.ts.map +1 -1
  211. package/dist/serve/worker/client/auth.js +4 -3
  212. package/dist/serve/worker/client/auth.js.map +1 -1
  213. package/dist/serve/worker/host/auth-action-codes.js +2 -2
  214. package/dist/serve/worker/host/auth-action-codes.js.map +1 -1
  215. package/dist/serve/worker/host/auth-custom-token.d.ts.map +1 -1
  216. package/dist/serve/worker/host/auth-custom-token.js +4 -2
  217. package/dist/serve/worker/host/auth-custom-token.js.map +1 -1
  218. package/dist/serve/worker/host/dispatch.d.ts.map +1 -1
  219. package/dist/serve/worker/host/dispatch.js +3 -1
  220. package/dist/serve/worker/host/dispatch.js.map +1 -1
  221. package/dist/serve/worker/host-auth.d.ts +6 -0
  222. package/dist/serve/worker/host-auth.d.ts.map +1 -1
  223. package/dist/serve/worker/host-auth.js +16 -5
  224. package/dist/serve/worker/host-auth.js.map +1 -1
  225. package/dist/serve/worker/inbound-validation.d.ts.map +1 -1
  226. package/dist/serve/worker/inbound-validation.js +1 -0
  227. package/dist/serve/worker/inbound-validation.js.map +1 -1
  228. package/dist/serve/worker/protocol/storage.d.ts +1 -8
  229. package/dist/serve/worker/protocol/storage.d.ts.map +1 -1
  230. package/dist/serve/worker/protocol/storage.js +2 -14
  231. package/dist/serve/worker/protocol/storage.js.map +1 -1
  232. package/dist/serve/worker/protocol.d.ts +2 -0
  233. package/dist/serve/worker/protocol.d.ts.map +1 -1
  234. package/dist/serve/worker/protocol.js.map +1 -1
  235. package/package.json +12 -8
  236. package/src/cli/index.ts +15 -1
  237. package/src/cli/run.ts +139 -0
  238. package/src/conformance/.generated/can-i-use.ts +1 -1
  239. package/src/conformance/.generated/conformance-docs.ts +1 -1
  240. package/src/remote/local-storage-url.ts +1 -58
  241. package/src/serve/assets/avatar-route.ts +6 -7
  242. package/src/serve/assets/resolver.ts +4 -1
  243. package/src/serve/entries/app-client.ts +7 -0
  244. package/src/serve/vite-plugin.ts +15 -1
  245. package/src/serve/vite-service-worker.ts +44 -0
  246. package/src/serve/worker/client/auth.ts +4 -3
  247. package/src/serve/worker/host/auth-action-codes.ts +2 -2
  248. package/src/serve/worker/host/auth-custom-token.ts +4 -2
  249. package/src/serve/worker/host/dispatch.ts +2 -0
  250. package/src/serve/worker/host-auth.ts +18 -5
  251. package/src/serve/worker/inbound-validation.ts +1 -0
  252. package/src/serve/worker/protocol/storage.ts +2 -14
  253. package/src/serve/worker/protocol.ts +2 -0
@@ -1,61 +1,4 @@
1
1
  /**
2
2
  * The host's URL for an object an app server holds a production download URL for.
3
3
  */
4
- import { parseStorageUrl, type StorageLocation } from 'pyric/storage/internal';
5
- import { storageObjectPath } from '../serve/worker/protocol.js';
6
-
7
- /** The hosts production serves Storage objects from. */
8
- const PRODUCTION_STORAGE_HOSTS = new Set([
9
- 'firebasestorage.googleapis.com',
10
- 'storage.googleapis.com',
11
- 'storage.cloud.google.com',
12
- ]);
13
-
14
- /** Cloud Storage's JSON API paths, which share `storage.googleapis.com` but do not start with a bucket. */
15
- const JSON_API_PATH = /^\/(?:download\/|upload\/)?storage\/v1\//;
16
-
17
- /** The bucket and object a production URL names, or undefined when it names none. */
18
- function productionLocationOf(url: URL): StorageLocation | undefined {
19
- const isProductionHost = PRODUCTION_STORAGE_HOSTS.has(url.hostname);
20
- if (!isProductionHost) return undefined;
21
- const isJsonApi = JSON_API_PATH.test(url.pathname);
22
- if (isJsonApi) return undefined;
23
- let location: StorageLocation;
24
- try {
25
- location = parseStorageUrl(url.href);
26
- } catch {
27
- return undefined;
28
- }
29
- const namesAnObject = location.path !== '';
30
- if (!namesAnObject) return undefined;
31
- return location;
32
- }
33
-
34
- /**
35
- * The byte route URL, on the host at `hostOrigin`, for the object a production
36
- * download URL names.
37
- *
38
- * It takes the download URL form
39
- * (`https://firebasestorage.googleapis.com/v0/b/<bucket>/o/<path>?alt=media&token=…`)
40
- * and the Cloud Storage forms (`https://storage.googleapis.com/<bucket>/<path>`,
41
- * `storage.cloud.google.com`), names the same bucket and object on the host's
42
- * byte route, and keeps the URL's `token`. A token set through
43
- * `firebaseStorageDownloadTokens` therefore grants the local URL as it granted
44
- * the production one. Other query parameters, such as `generation`, name
45
- * production state and are dropped. A Cloud Storage URL carries no token, so
46
- * the host grants its local URL only once a token is added to it.
47
- *
48
- * Returns undefined for a URL that names no object on those hosts.
49
- */
50
- export function localStorageUrl(productionUrl: string, hostOrigin: string): string | undefined {
51
- let url: URL;
52
- try {
53
- url = new URL(productionUrl);
54
- } catch {
55
- return undefined;
56
- }
57
- const location = productionLocationOf(url);
58
- if (location === undefined) return undefined;
59
- const token = url.searchParams.get('token') ?? undefined;
60
- return new URL(storageObjectPath(location.bucket, location.path, token), hostOrigin).href;
61
- }
4
+ export { localStorageUrl } from 'pyric/storage/internal';
@@ -76,7 +76,11 @@ export function avatarUidFromPath(pathname: string): string | null {
76
76
  * invokes the configured source, which may cost money and writes a cache
77
77
  * file. `Sec-Fetch-Site` is set by the browser, not the page, so a
78
78
  * `cross-site` value is a reliable statement that the application under
79
- * development did not make this request.
79
+ * development did not start the source. Such a request is still answered
80
+ * from the cache, the pool, or the generated fallback, because the
81
+ * application's own opaque-origin documents (a sandboxed blob-URL or srcdoc
82
+ * iframe showing `user.photoURL`) are cross-site too, and a production photo
83
+ * URL loads anywhere.
80
84
  *
81
85
  * Absence means allow: native clients, `curl`, and older browsers send no
82
86
  * such header, and this route is not the place to break them.
@@ -123,15 +127,10 @@ export async function handleAvatar(
123
127
  .end('method not allowed');
124
128
  return;
125
129
  }
126
- if (isCrossSiteRequest(req)) {
127
- res.writeHead(403, { 'content-type': 'text/plain; charset=utf-8' })
128
- .end('cross-site avatar requests are refused');
129
- return;
130
- }
131
-
132
130
  const assetRequest: AssetRequest = {
133
131
  key: uid,
134
132
  seed: seedFor(uid, url.searchParams.get('d')),
133
+ ...(isCrossSiteRequest(req) ? { sourceAllowed: false } : {}),
135
134
  context: {
136
135
  uid,
137
136
  displayName: url.searchParams.get('n'),
@@ -22,6 +22,9 @@ export interface AssetRequest {
22
22
  key: string;
23
23
  seed: string;
24
24
  context: Record<string, unknown>;
25
+ /** `false` answers from the cache, the pool, or the fallback and never
26
+ * starts the source. Absent means the source may run. */
27
+ sourceAllowed?: boolean;
25
28
  }
26
29
 
27
30
  export type AssetResult = { url: string } | { data: Uint8Array; contentType: string };
@@ -259,7 +262,7 @@ export function createAssetResolver(opts: AssetResolverOptions): AssetResolver {
259
262
  // Missing/unreadable file: fall through as a miss, same as no entry.
260
263
  }
261
264
 
262
- if (opts.source) {
265
+ if (opts.source && req.sourceAllowed !== false) {
263
266
  let pending = inFlight.get(req.key);
264
267
  if (!pending) {
265
268
  // Counted per new invocation, not per request: joining an in-flight
@@ -5,6 +5,7 @@ import type { ClientDb } from '../worker/client.js';
5
5
  import type { InboundMessage } from '../worker/protocol.js';
6
6
  import { ownClientUntilPagehide } from '../worker/client/pagehide.js';
7
7
  import { openWorkerDb } from './worker-runtime.js';
8
+ import { realmOrigin } from 'pyric/sandbox/internal';
8
9
 
9
10
  const clients = new WeakMap<FirebaseApp, ClientDb>();
10
11
 
@@ -21,7 +22,13 @@ export function workerClientForApp(app: FirebaseApp): ClientDb {
21
22
  client.port.postMessage({
22
23
  t: 'appConfig',
23
24
  options: app.options as Record<string, unknown>,
25
+ ...pageOriginScope(),
24
26
  } satisfies InboundMessage);
25
27
  clients.set(app, client);
26
28
  return client;
27
29
  }
30
+
31
+ function pageOriginScope(): { origin?: string } {
32
+ const origin = realmOrigin();
33
+ return origin === null ? {} : { origin };
34
+ }
@@ -60,6 +60,7 @@ import { createVitePageRuntime } from './vite-page-runtime.js';
60
60
  import { stampHostedTarget } from './runtime/hosted-target.js';
61
61
  import { installViteNetGuard } from './vite-net-guard.js';
62
62
  import { attachViteSsrActivation } from './vite-ssr-activation.js';
63
+ import { createServiceWorkerWiring, type ServiceWorkerWiringContext } from './vite-service-worker.js';
63
64
  import type { SwapEnvironment, SwapResolveContext } from './vite-module-swap.js';
64
65
 
65
66
  /** Vite 6's `configEnvironment` hook, which the Vite 5 `Plugin` type does not declare. */
@@ -199,6 +200,12 @@ export interface PyricOptions {
199
200
  * the avatar route entirely. An object supplies a custom source that
200
201
  * runs in the dev server, never the browser. */
201
202
  avatars?: PyricAvatarsOptions;
203
+ /** The app's own service worker module (path from the Vite root), such as a
204
+ * `vite-plugin-pwa` `injectManifest` source. In dev, the plugin adds a fetch
205
+ * listener to it that answers production Storage download URLs from the
206
+ * sandbox (`sandboxStorageFetch` from `pyric/storage/sw`), so the file needs
207
+ * no Pyric import. Builds are untouched. */
208
+ serviceWorker?: string;
202
209
  }
203
210
 
204
211
  function resolveFunctionsOptions(
@@ -255,6 +262,8 @@ export function pyric(options: PyricOptions = {}): Plugin & EnvironmentHooks {
255
262
  workerRuntime,
256
263
  };
257
264
  const pageRuntime = createVitePageRuntime(pageRuntimeInput);
265
+ const serviceWorkerWiring = createServiceWorkerWiring(options.serviceWorker);
266
+ let serviceWorkerContext: ServiceWorkerWiringContext = { root: process.cwd(), devServer: false };
258
267
  const moduleSwap = createViteModuleSwap(moduleContext, {
259
268
  getAiMode: () => pageRuntime.ai().mode,
260
269
  });
@@ -290,6 +299,7 @@ export function pyric(options: PyricOptions = {}): Plugin & EnvironmentHooks {
290
299
  // the dev server: a build or a preview runs no server-rendered app code.
291
300
  const isDevServer = env.command === 'serve' && env.isPreview !== true;
292
301
  if (isDevServer) installViteNetGuard();
302
+ serviceWorkerContext = { ...serviceWorkerContext, devServer: isDevServer };
293
303
  const moduleConfig = moduleSwap.config({ devServer: isDevServer });
294
304
  const pageConfig = pageRuntime.config(config, env);
295
305
  const serverWatchConfig: UserConfig = {
@@ -309,6 +319,7 @@ export function pyric(options: PyricOptions = {}): Plugin & EnvironmentHooks {
309
319
 
310
320
  async configResolved(resolved) {
311
321
  moduleSwap.configResolved(resolved);
322
+ serviceWorkerContext = { ...serviceWorkerContext, root: resolved.root };
312
323
  // A sandbox build stamps pages without a dev server, so configureServer
313
324
  // never reads pyric.json for it.
314
325
  const isBuild = resolved.command === 'build';
@@ -332,7 +343,10 @@ export function pyric(options: PyricOptions = {}): Plugin & EnvironmentHooks {
332
343
  },
333
344
 
334
345
  transform(code, id, transformOptions) {
335
- if (hostedInstanceId === undefined || transformOptions?.ssr === true) return null;
346
+ if (transformOptions?.ssr === true) return null;
347
+ const wiredServiceWorker = serviceWorkerWiring.transform(code, id, serviceWorkerContext);
348
+ if (wiredServiceWorker !== null) return wiredServiceWorker;
349
+ if (hostedInstanceId === undefined) return null;
336
350
  const cleanId = id.split('?')[0];
337
351
  if (cleanId === entries.app || cleanId === entries['messaging-sw']) {
338
352
  return `globalThis.__PYRIC_HOST_INSTANCE_ID__=${JSON.stringify(hostedInstanceId)};\n${code}`;
@@ -0,0 +1,44 @@
1
+ /**
2
+ * The Vite plugin's `serviceWorker` option. In dev, the app's own service
3
+ * worker module gets a fetch listener, ahead of the app's code, that answers
4
+ * production Storage URLs from the sandbox with `pyric/storage/sw`. The helper
5
+ * is imported by an absolute path from Pyric's own install, so the app needs
6
+ * no Pyric import and its production build is untouched.
7
+ */
8
+ import { isAbsolute, resolve } from 'node:path';
9
+ import { fileURLToPath } from 'node:url';
10
+
11
+ export interface ServiceWorkerWiringContext {
12
+ /** Vite's resolved root, which a relative `serviceWorker` path is read from. */
13
+ readonly root: string;
14
+ /** Whether this is the dev server; builds and previews are never wired. */
15
+ readonly devServer: boolean;
16
+ }
17
+
18
+ export interface ServiceWorkerWiring {
19
+ /** The wired source of `id`, or null when `id` is not the app's service worker in dev. */
20
+ transform(code: string, id: string, context: ServiceWorkerWiringContext): string | null;
21
+ }
22
+
23
+ function storageFetchListener(): string {
24
+ const helper = fileURLToPath(import.meta.resolve('pyric/storage/sw'));
25
+ return [
26
+ `import { sandboxStorageFetch as __pyricSandboxStorageFetch } from ${JSON.stringify(helper)};`,
27
+ "self.addEventListener('fetch', (event) => {",
28
+ ' if (__pyricSandboxStorageFetch(event)) event.stopImmediatePropagation();',
29
+ '});',
30
+ '',
31
+ ].join('\n');
32
+ }
33
+
34
+ export function createServiceWorkerWiring(serviceWorker: string | undefined): ServiceWorkerWiring {
35
+ return {
36
+ transform(code, id, { root, devServer }) {
37
+ if (serviceWorker === undefined || !devServer) return null;
38
+ const file = (isAbsolute(serviceWorker) ? serviceWorker : resolve(root, serviceWorker)).replaceAll('\\', '/');
39
+ const isServiceWorker = id.split('?')[0] === file;
40
+ if (!isServiceWorker) return null;
41
+ return storageFetchListener() + code;
42
+ },
43
+ };
44
+ }
@@ -14,6 +14,7 @@ import type {
14
14
  ResolvedIdentity,
15
15
  OpMessage,
16
16
  } from '../protocol.js';
17
+ import { absolutePhotoUrl, realmOrigin } from 'pyric/sandbox/internal';
17
18
  import { closeSubscription, isDisconnectedPort, nextId, nextSubId, openSnapshotSubscription, rpc, wirePort } from './core.js';
18
19
  import type { ClientDb, ClientPort, Unsubscribe } from './handles.js';
19
20
 
@@ -94,7 +95,7 @@ function makeClientUser(port: ClientPort, raw: SerializedUser): ClientUser {
94
95
  email: raw.email,
95
96
  emailVerified: raw.emailVerified,
96
97
  displayName: raw.displayName,
97
- photoURL: raw.photoURL,
98
+ photoURL: absolutePhotoUrl(raw.photoURL, realmOrigin()),
98
99
  phoneNumber: raw.phoneNumber,
99
100
  isAnonymous: raw.isAnonymous,
100
101
  tenantId: raw.tenantId ?? null,
@@ -373,7 +374,7 @@ export async function updateProfile(
373
374
  })) as SerializedUser;
374
375
  const mutable = user as { -readonly [K in keyof ClientUser]: ClientUser[K] };
375
376
  mutable.displayName = raw.displayName;
376
- mutable.photoURL = raw.photoURL;
377
+ mutable.photoURL = absolutePhotoUrl(raw.photoURL, realmOrigin());
377
378
  mutable.providerData = raw.providerData;
378
379
  }
379
380
 
@@ -384,7 +385,7 @@ export async function reload(user: ClientUser): Promise<void> {
384
385
  mutable.email = raw.email;
385
386
  mutable.emailVerified = raw.emailVerified;
386
387
  mutable.displayName = raw.displayName;
387
- mutable.photoURL = raw.photoURL;
388
+ mutable.photoURL = absolutePhotoUrl(raw.photoURL, realmOrigin());
388
389
  mutable.phoneNumber = raw.phoneNumber;
389
390
  mutable.providerData = raw.providerData;
390
391
  mutable.tenantId = raw.tenantId ?? null;
@@ -2,7 +2,7 @@ import { applyActionCode, checkActionCode, confirmPasswordReset, sendEmailVerifi
2
2
  sendPasswordResetEmail, sendSignInLinkToEmail, sandbox, verifyBeforeUpdateEmail, verifyPasswordResetCode } from 'pyric/auth';
3
3
  import { resolveEmailLinkIdentity } from 'pyric/auth/internal';
4
4
  import type { OpMessage } from '../protocol.js';
5
- import { ensureAuth, portSession, setPortSession } from '../host-auth.js';
5
+ import { ensureAuth, portOriginScope, portSession, setPortSession } from '../host-auth.js';
6
6
  import { bestEffortFlush, fail, ok, type HostCtx, type PortLike } from '../host-context.js';
7
7
  import { credReply, requireMatchingPortSession } from './auth-session-seeder.js';
8
8
 
@@ -32,7 +32,7 @@ export async function handleAuthActionCodeOp(ctx: HostCtx, port: PortLike, messa
32
32
  case 'auth.signInWithEmailLink': {
33
33
  const { user, isNewUser } = resolveEmailLinkIdentity(auth, message.email, message.link);
34
34
  const session = sandbox.mintSession(auth, { kind: 'provider', uid: user.uid,
35
- providerId: 'password', tenantId: message.tenantId ?? null });
35
+ providerId: 'password', tenantId: message.tenantId ?? null, ...portOriginScope(port) });
36
36
  await bestEffortFlush(ctx, message.method);
37
37
  setPortSession(ctx, port, session);
38
38
  ok(port, message.id, credReply(session, null, isNewUser));
@@ -1,7 +1,7 @@
1
1
  import { sandbox } from 'pyric/auth';
2
2
  import { resolveCustomTokenIdentity } from 'pyric/auth/internal';
3
3
  import type { OpMessage } from '../protocol.js';
4
- import { ensureAuth, setPortSession } from '../host-auth.js';
4
+ import { ensureAuth, portOriginScope, setPortSession } from '../host-auth.js';
5
5
  import { bestEffortFlush, fail, ok, type HostCtx, type PortLike } from '../host-context.js';
6
6
  import { credReply } from './auth-session-seeder.js';
7
7
 
@@ -13,7 +13,9 @@ export async function handleCustomTokenSignIn(
13
13
  try {
14
14
  const auth = ensureAuth(ctx);
15
15
  const { user, isNewUser } = resolveCustomTokenIdentity(auth, message.customToken);
16
- const session = sandbox.mintSession(auth, { kind: 'custom', uid: user.uid, tenantId: message.tenantId ?? null });
16
+ const session = sandbox.mintSession(auth, {
17
+ kind: 'custom', uid: user.uid, tenantId: message.tenantId ?? null, ...portOriginScope(port),
18
+ });
17
19
  await bestEffortFlush(ctx, message.method);
18
20
  setPortSession(ctx, port, session);
19
21
  ok(port, message.id, credReply(session, null, isNewUser));
@@ -39,6 +39,7 @@ import {
39
39
  handleAuthSub,
40
40
  handleAuthUnsub,
41
41
  cleanupPortSession,
42
+ setPortOrigin,
42
43
  } from '../host-auth.js';
43
44
  import {
44
45
  eventSubsFor,
@@ -218,6 +219,7 @@ export async function handleMessage(
218
219
 
219
220
  const configuresApp = msg.t === 'appConfig';
220
221
  if (configuresApp) {
222
+ if (msg.origin !== undefined) setPortOrigin(targetPort, msg.origin);
221
223
  handleAppConfig(ctx, targetPort, msg, stalePort => cleanupPort(ctx, stalePort));
222
224
  return;
223
225
  }
@@ -60,6 +60,7 @@ import { reauthenticateSession } from './host/auth-reauthentication.js';
60
60
  const _authSubs = new WeakMap<HostCtx, Map<PortLike, Map<string, 'authState' | 'idToken'>>>();
61
61
  const _portTenants = new WeakMap<HostCtx, Map<PortLike, string | null>>();
62
62
  const _lastAuthStateUid = new WeakMap<PortLike, string | null>();
63
+ const _portOrigins = new WeakMap<PortLike, string>();
63
64
 
64
65
  export function authSubsFor(ctx: HostCtx): Map<PortLike, Map<string, 'authState' | 'idToken'>> {
65
66
  let m = _authSubs.get(ctx);
@@ -83,6 +84,17 @@ export function portTenant(ctx: HostCtx, port: PortLike): string | null {
83
84
  return portTenantsFor(ctx).get(port) ?? null;
84
85
  }
85
86
 
87
+ /** Record the page origin an app port's client stated in its `appConfig`. */
88
+ export function setPortOrigin(port: PortLike, origin: string): void {
89
+ _portOrigins.set(port, origin);
90
+ }
91
+
92
+ /** The port's page origin as a mint-request scope, empty when unknown. */
93
+ export function portOriginScope(port: PortLike): { origin?: string } {
94
+ const origin = _portOrigins.get(port);
95
+ return origin === undefined ? {} : { origin };
96
+ }
97
+
86
98
  /**
87
99
  * Lazily create the ONE shared auth handle — the user pool + admin surface.
88
100
  * Calling `getAuth(sandbox)` also (re)registers the auth service with the
@@ -258,7 +270,7 @@ export async function handleAuthOp(ctx: HostCtx, port: PortLike, msg: OpMessage)
258
270
  const tenantId = resolveSignInTenant(ctx, port, msg.tenantId);
259
271
  const session = authSandboxOps.mintSession(auth, {
260
272
  kind: 'createPassword', email: msg.email, password: msg.password,
261
- tenantId,
273
+ tenantId, ...portOriginScope(port),
262
274
  });
263
275
  setPortSession(ctx, port, session);
264
276
  await bestEffortFlush(ctx, msg.method); // new user record must be durable at ack
@@ -272,7 +284,7 @@ export async function handleAuthOp(ctx: HostCtx, port: PortLike, msg: OpMessage)
272
284
  const tenantId = resolveSignInTenant(ctx, port, msg.tenantId);
273
285
  const session = authSandboxOps.mintSession(auth, {
274
286
  kind: 'password', email: msg.email, password: msg.password,
275
- tenantId,
287
+ tenantId, ...portOriginScope(port),
276
288
  });
277
289
  setPortSession(ctx, port, session);
278
290
  await bestEffortFlush(ctx, msg.method);
@@ -294,7 +306,7 @@ export async function handleAuthOp(ctx: HostCtx, port: PortLike, msg: OpMessage)
294
306
  break;
295
307
  }
296
308
  const session = authSandboxOps.mintSession(auth, {
297
- kind: 'anonymous', tenantId,
309
+ kind: 'anonymous', tenantId, ...portOriginScope(port),
298
310
  });
299
311
  setPortSession(ctx, port, session);
300
312
  await bestEffortFlush(ctx, msg.method);
@@ -317,7 +329,7 @@ export async function handleAuthOp(ctx: HostCtx, port: PortLike, msg: OpMessage)
317
329
  let session: MintedSession | null = null;
318
330
  try {
319
331
  session = authSandboxOps.mintSession(auth, {
320
- kind: 'uid', uid: msg.uid, tenantId,
332
+ kind: 'uid', uid: msg.uid, tenantId, ...portOriginScope(port),
321
333
  });
322
334
  } catch {
323
335
  session = null;
@@ -534,6 +546,7 @@ export async function handleAuthOp(ctx: HostCtx, port: PortLike, msg: OpMessage)
534
546
  uid,
535
547
  providerId: cred.providerId,
536
548
  tenantId,
549
+ ...portOriginScope(port),
537
550
  });
538
551
  setPortSession(ctx, port, session);
539
552
  await bestEffortFlush(ctx, msg.method);
@@ -550,7 +563,7 @@ export async function handleAuthOp(ctx: HostCtx, port: PortLike, msg: OpMessage)
550
563
  ...identity, providerId, photoURL: photoURL ?? undefined,
551
564
  });
552
565
  const session = authSandboxOps.mintSession(auth, {
553
- kind: 'provider', uid, providerId, tenantId,
566
+ kind: 'provider', uid, providerId, tenantId, ...portOriginScope(port),
554
567
  });
555
568
  setPortSession(ctx, port, session);
556
569
  await bestEffortFlush(ctx, msg.method);
@@ -43,6 +43,7 @@ function assertInboundFields(message: Record<string, unknown>): void {
43
43
  requireRecord(message.options, 'options');
44
44
  requireOptionalBoolean(message.fromPriorHost, 'fromPriorHost');
45
45
  requireOptionalBoolean(message.hostVerified, 'hostVerified');
46
+ requireOptionalString(message.origin, 'origin');
46
47
  return;
47
48
  case 'clock-subscribe':
48
49
  return;
@@ -34,20 +34,8 @@ export const MAX_STORAGE_PART_B64_LENGTH = Math.ceil(MAX_STORAGE_PART_BYTES / 3)
34
34
  */
35
35
  export const MAX_STORAGE_OBJECT_BYTES = 512 * 1024 * 1024;
36
36
 
37
- /**
38
- * The Node host's HTTP byte route. Its paths follow Firebase's download paths
39
- * under the pyric namespace: `<prefix><bucket>/o/<encoded path>` for an object,
40
- * `<prefix><bucket>/o?name=…` for an upload.
41
- */
42
- export const STORAGE_ROUTE_PREFIX = '/__pyric/storage/v0/b/';
43
-
44
- /** An object's path on the byte route, with a token when the URL must carry its own authority. */
45
- export function storageObjectPath(bucket: string, path: string, token?: string): string {
46
- const query = new URLSearchParams({ alt: 'media' });
47
- const hasToken = token !== undefined;
48
- if (hasToken) query.set('token', token);
49
- return `${STORAGE_ROUTE_PREFIX}${encodeURIComponent(bucket)}/o/${encodeURIComponent(path)}?${query}`;
50
- }
37
+ export { STORAGE_ROUTE_PREFIX, storageObjectPath } from 'pyric/storage/internal';
38
+ import { STORAGE_ROUTE_PREFIX } from 'pyric/storage/internal';
51
39
 
52
40
  /** Where one upload's bytes are sent, carrying the token bound to that upload. */
53
41
  export function storageUploadPath(bucket: string, path: string, uploadId: string, token: string): string {
@@ -471,6 +471,8 @@ export interface AppConfigMessage {
471
471
  fromPriorHost?: boolean;
472
472
  /** Set when the attaching client proved compile-time/HTML origin from this running host instance. */
473
473
  hostVerified?: boolean;
474
+ /** The page's `location.origin`; the host resolves relative photo URLs against it. */
475
+ origin?: string;
474
476
  }
475
477
 
476
478
  /**