@pyric/cli 0.1.0-alpha.27 → 0.1.0-alpha.29
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.
- package/dist/assurance/.generated/conformance-verdicts.d.ts +16 -0
- package/dist/assurance/.generated/conformance-verdicts.d.ts.map +1 -1
- package/dist/assurance/.generated/conformance-verdicts.js +16 -0
- package/dist/assurance/.generated/conformance-verdicts.js.map +1 -1
- package/dist/bridge/surface/arguments/assurance.d.ts +6 -6
- package/dist/bridge/surface/arguments/messaging.d.ts +2 -2
- package/dist/bridge/surface/methods/assurance/map.d.ts +6 -6
- package/dist/bridge/surface/methods/assurance/replaySession.d.ts.map +1 -1
- package/dist/bridge/surface/methods/assurance/replaySession.js +18 -6
- package/dist/bridge/surface/methods/assurance/replaySession.js.map +1 -1
- package/dist/bridge/surface/methods/messaging/send.d.ts +4 -4
- package/dist/bridge/surface/render/discriminator-schemas.d.ts +4 -4
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +15 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/run.d.ts +42 -0
- package/dist/cli/run.d.ts.map +1 -0
- package/dist/cli/run.js +115 -0
- package/dist/cli/run.js.map +1 -0
- package/dist/conformance/.generated/can-i-use.d.ts.map +1 -1
- package/dist/conformance/.generated/can-i-use.js +1 -1
- package/dist/conformance/.generated/can-i-use.js.map +1 -1
- package/dist/conformance/.generated/conformance-docs.d.ts.map +1 -1
- package/dist/conformance/.generated/conformance-docs.js +1 -1
- package/dist/conformance/.generated/conformance-docs.js.map +1 -1
- package/dist/remote/local-storage-url.d.ts +2 -15
- package/dist/remote/local-storage-url.d.ts.map +1 -1
- package/dist/remote/local-storage-url.js +1 -60
- package/dist/remote/local-storage-url.js.map +1 -1
- package/dist/serve/assets/avatar-route.d.ts.map +1 -1
- package/dist/serve/assets/avatar-route.js +6 -6
- package/dist/serve/assets/avatar-route.js.map +1 -1
- package/dist/serve/assets/resolver.d.ts +3 -0
- package/dist/serve/assets/resolver.d.ts.map +1 -1
- package/dist/serve/assets/resolver.js +1 -1
- package/dist/serve/assets/resolver.js.map +1 -1
- package/dist/serve/entries/app-client.d.ts.map +1 -1
- package/dist/serve/entries/app-client.js +6 -0
- package/dist/serve/entries/app-client.js.map +1 -1
- package/dist/serve/site-ui/_astro/{RulesCodeEditor.kBYcclwV.js → RulesCodeEditor.Cp_-FK3b.js} +1 -1
- package/dist/serve/site-ui/_astro/app.C--T2qDK.js +151 -0
- package/dist/serve/site-ui/_astro/app.DqyhRyrU.js +1 -0
- package/dist/serve/site-ui/_astro/{chess-showcase.CZe_3fJZ.js → chess-showcase.CVhCNUi_.js} +1 -1
- package/dist/serve/site-ui/_astro/{example-runtime.CXSZXHZF.js → example-runtime.zC1l-Yka.js} +1 -1
- package/dist/serve/site-ui/_astro/{sandbox-controls.Bp5PoonS.js → sandbox-controls.Bv6lfpIe.js} +39 -39
- package/dist/serve/site-ui/_astro/{seed.BpXSRzLo.js → seed.B4vza_Bv.js} +1 -1
- package/dist/serve/site-ui/_astro/{writes.DHSW22XD.js → writes.Bg0YEEA7.js} +1 -1
- package/dist/serve/site-ui/auth/index.html +1 -1
- package/dist/serve/site-ui/docs/_rhythm/index.html +1 -1
- package/dist/serve/site-ui/docs/agent/set-up-your-agent/index.html +1 -1
- package/dist/serve/site-ui/docs/agent/watch-and-review/index.html +1 -1
- package/dist/serve/site-ui/docs/agent/work-with-an-agent/index.html +1 -1
- package/dist/serve/site-ui/docs/ai-compat/index.html +1 -1
- package/dist/serve/site-ui/docs/api-reference/index.html +2 -1
- package/dist/serve/site-ui/docs/api-reference.md +1 -0
- package/dist/serve/site-ui/docs/app-compat/index.html +1 -1
- package/dist/serve/site-ui/docs/auth-compat/index.html +2 -2
- package/dist/serve/site-ui/docs/auth-compat.md +1 -1
- package/dist/serve/site-ui/docs/auth-flutter-compat/index.html +1 -1
- package/dist/serve/site-ui/docs/auth-kotlin-compat/index.html +1 -1
- package/dist/serve/site-ui/docs/auth-swift-compat/index.html +1 -1
- package/dist/serve/site-ui/docs/build/ai-logic/index.html +1 -1
- package/dist/serve/site-ui/docs/build/authentication/index.html +1 -1
- package/dist/serve/site-ui/docs/build/cloud-firestore/index.html +1 -1
- package/dist/serve/site-ui/docs/build/cloud-messaging/index.html +1 -1
- package/dist/serve/site-ui/docs/build/cloud-storage/index.html +13 -2
- package/dist/serve/site-ui/docs/build/cloud-storage.md +23 -0
- package/dist/serve/site-ui/docs/build/default-avatars/index.html +1 -1
- package/dist/serve/site-ui/docs/build/inspect-activity/index.html +1 -1
- package/dist/serve/site-ui/docs/build/realtime-database/index.html +1 -1
- package/dist/serve/site-ui/docs/conformance-scores/index.html +1 -1
- package/dist/serve/site-ui/docs/create-pyric-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/database-compat/index.html +18 -18
- package/dist/serve/site-ui/docs/database-compat.md +17 -17
- package/dist/serve/site-ui/docs/examples/chess/index.html +1 -1
- package/dist/serve/site-ui/docs/examples/index.html +1 -1
- package/dist/serve/site-ui/docs/firestore-compat/index.html +1 -1
- package/dist/serve/site-ui/docs/firestore-flutter-compat/index.html +1 -1
- package/dist/serve/site-ui/docs/firestore-kotlin-compat/index.html +1 -1
- package/dist/serve/site-ui/docs/firestore-swift-compat/index.html +1 -1
- package/dist/serve/site-ui/docs/functions-rtdb-compat/index.html +1 -1
- package/dist/serve/site-ui/docs/get-started/backend-bundlers/index.html +1 -1
- package/dist/serve/site-ui/docs/get-started/flutter/index.html +1 -1
- package/dist/serve/site-ui/docs/get-started/how-the-swap-works/index.html +1 -1
- package/dist/serve/site-ui/docs/get-started/nextjs/index.html +1 -1
- package/dist/serve/site-ui/docs/get-started/node-host/index.html +1 -1
- package/dist/serve/site-ui/docs/get-started/start-building/index.html +1 -1
- package/dist/serve/site-ui/docs/get-started/vite/index.html +4 -2
- package/dist/serve/site-ui/docs/get-started/vite.md +4 -0
- package/dist/serve/site-ui/docs/index.json +60 -10
- package/dist/serve/site-ui/docs/messaging-compat/index.html +1 -1
- package/dist/serve/site-ui/docs/observe/resolve-runtime-status/index.html +1 -1
- package/dist/serve/site-ui/docs/observe/see-whats-happening/index.html +1 -1
- package/dist/serve/site-ui/docs/observe/shape-your-data/index.html +1 -1
- package/dist/serve/site-ui/docs/observe/switch-and-impersonate-identities/index.html +1 -1
- package/dist/serve/site-ui/docs/overview/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-admin-app-check-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-admin-app-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-admin-auth-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-admin-data-connect-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-admin-database-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-admin-eventarc-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-admin-extensions-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-admin-firestore-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-admin-functions-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-admin-installations-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-admin-instance-id-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-admin-machine-learning-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-admin-messaging-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-admin-phone-number-verification-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-admin-project-management-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-admin-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-admin-remote-config-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-admin-security-rules-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-admin-storage-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-ai-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-ai-scripting-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-analytics-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-app-check-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-app-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-auth-reference-api/index.html +2 -2
- package/dist/serve/site-ui/docs/pyric-auth-reference-api.md +1 -1
- package/dist/serve/site-ui/docs/pyric-cli-assurance-browser-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-cli-assurance-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-cli-bridge-client-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-cli-bridge-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-cli-bundler-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-cli-conformance-browser-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-cli-conformance-docs-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-cli-conformance-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-cli-credentials-node-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-cli-discover-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-cli-flow-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-cli-next-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-cli-register-app-bridge-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-cli-register-client-app-bridge-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-cli-register-client-app-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-cli-register-client-auth-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-cli-register-client-database-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-cli-register-client-firestore-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-cli-register-client-storage-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-cli-register-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-cli-remote-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-cli-repro-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-cli-serve-worker-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-cli-verify-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-cli-vite-reference-api/index.html +17 -3
- package/dist/serve/site-ui/docs/pyric-cli-vite-reference-api.md +7 -5
- package/dist/serve/site-ui/docs/pyric-cli-vite-ssr-loader-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-database-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-firestore-lite-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-firestore-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-functions-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-messaging-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-messaging-sw-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-performance-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-remote-config-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-rules-reference-api/index.html +120 -120
- package/dist/serve/site-ui/docs/pyric-rules-reference-api.md +53 -53
- package/dist/serve/site-ui/docs/pyric-sandbox-branches-store-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-sandbox-checkpoints-directory-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-sandbox-checkpoints-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-sandbox-database-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-sandbox-firestore-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-sandbox-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-storage-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-storage-sw-reference-api/index.html +102 -0
- package/dist/serve/site-ui/docs/pyric-storage-sw-reference-api.md +76 -0
- package/dist/serve/site-ui/docs/pyric-ui-agents-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-ui-auth-hooks-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-ui-auth-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-ui-events-hooks-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-ui-events-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-ui-firestore-hooks-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-ui-firestore-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-ui-listener-owner-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-ui-primitives-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-ui-rtdb-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-ui-rules-hooks-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-ui-rules-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-ui-storage-hooks-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-ui-storage-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-ui-traffic-hooks-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-ui-traffic-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/reference/cli/index.html +12 -2
- package/dist/serve/site-ui/docs/reference/cli.md +24 -0
- package/dist/serve/site-ui/docs/rtdb-flutter-compat/index.html +1 -1
- package/dist/serve/site-ui/docs/rtdb-kotlin-compat/index.html +1 -1
- package/dist/serve/site-ui/docs/rtdb-swift-compat/index.html +1 -1
- package/dist/serve/site-ui/docs/rules-compat/index.html +68 -4
- package/dist/serve/site-ui/docs/rules-compat.md +67 -3
- package/dist/serve/site-ui/docs/secure/audit-your-rules/index.html +1 -1
- package/dist/serve/site-ui/docs/secure/firestore-rules-limits/index.html +1 -1
- package/dist/serve/site-ui/docs/secure/read-a-denial/index.html +1 -1
- package/dist/serve/site-ui/docs/secure/rtdb-rules-in-typescript/index.html +1 -1
- package/dist/serve/site-ui/docs/secure/rtdb-rules-standard-library/index.html +18 -5
- package/dist/serve/site-ui/docs/secure/rtdb-rules-standard-library.md +19 -4
- package/dist/serve/site-ui/docs/secure/rules-standard-library/index.html +1 -1
- package/dist/serve/site-ui/docs/secure/secure-it-with-rules/index.html +1 -1
- package/dist/serve/site-ui/docs/secure/simulate-and-lint/index.html +5 -5
- package/dist/serve/site-ui/docs/secure/simulate-and-lint.md +8 -8
- package/dist/serve/site-ui/docs/secure/write-a-rules-test-suite/index.html +1 -1
- package/dist/serve/site-ui/docs/ship/ship-to-production/index.html +1 -1
- package/dist/serve/site-ui/docs/ship/test-in-node/index.html +1 -1
- package/dist/serve/site-ui/docs/storage-compat/index.html +1 -1
- package/dist/serve/site-ui/docs/trust/how-we-know-it-matches-firebase/index.html +1 -1
- package/dist/serve/site-ui/docs/trust/rules-standard-library/index.html +1 -1
- package/dist/serve/site-ui/docs/trust/versioning-and-compatibility/index.html +1 -1
- package/dist/serve/site-ui/docs/tutorial/index.html +1 -1
- package/dist/serve/site-ui/examples/chess/index.html +1 -1
- package/dist/serve/site-ui/firestore/index.html +1 -1
- package/dist/serve/site-ui/llms.txt +1 -0
- package/dist/serve/site-ui/rtdb/index.html +1 -1
- package/dist/serve/site-ui/settings/index.html +1 -1
- package/dist/serve/site-ui/storage/index.html +1 -1
- package/dist/serve/site-ui/studio/index.html +1 -1
- package/dist/serve/site-ui/traffic/index.html +1 -1
- package/dist/serve/vite-plugin.d.ts +6 -0
- package/dist/serve/vite-plugin.d.ts.map +1 -1
- package/dist/serve/vite-plugin.js +11 -1
- package/dist/serve/vite-plugin.js.map +1 -1
- package/dist/serve/vite-service-worker.d.ts +12 -0
- package/dist/serve/vite-service-worker.d.ts.map +1 -0
- package/dist/serve/vite-service-worker.js +33 -0
- package/dist/serve/vite-service-worker.js.map +1 -0
- package/dist/serve/worker/client/auth.d.ts.map +1 -1
- package/dist/serve/worker/client/auth.js +4 -3
- package/dist/serve/worker/client/auth.js.map +1 -1
- package/dist/serve/worker/client/rtdb-info.d.ts +0 -2
- package/dist/serve/worker/client/rtdb-info.d.ts.map +1 -1
- package/dist/serve/worker/client/rtdb-info.js +4 -17
- package/dist/serve/worker/client/rtdb-info.js.map +1 -1
- package/dist/serve/worker/client/rtdb-reads.d.ts.map +1 -1
- package/dist/serve/worker/client/rtdb-reads.js +3 -5
- package/dist/serve/worker/client/rtdb-reads.js.map +1 -1
- package/dist/serve/worker/host/auth-action-codes.js +2 -2
- package/dist/serve/worker/host/auth-action-codes.js.map +1 -1
- package/dist/serve/worker/host/auth-custom-token.d.ts.map +1 -1
- package/dist/serve/worker/host/auth-custom-token.js +4 -2
- package/dist/serve/worker/host/auth-custom-token.js.map +1 -1
- package/dist/serve/worker/host/dispatch.d.ts.map +1 -1
- package/dist/serve/worker/host/dispatch.js +3 -1
- package/dist/serve/worker/host/dispatch.js.map +1 -1
- package/dist/serve/worker/host-auth.d.ts +6 -0
- package/dist/serve/worker/host-auth.d.ts.map +1 -1
- package/dist/serve/worker/host-auth.js +16 -5
- package/dist/serve/worker/host-auth.js.map +1 -1
- package/dist/serve/worker/inbound-validation.d.ts.map +1 -1
- package/dist/serve/worker/inbound-validation.js +1 -0
- package/dist/serve/worker/inbound-validation.js.map +1 -1
- package/dist/serve/worker/protocol/storage.d.ts +1 -8
- package/dist/serve/worker/protocol/storage.d.ts.map +1 -1
- package/dist/serve/worker/protocol/storage.js +2 -14
- package/dist/serve/worker/protocol/storage.js.map +1 -1
- package/dist/serve/worker/protocol.d.ts +2 -0
- package/dist/serve/worker/protocol.d.ts.map +1 -1
- package/dist/serve/worker/protocol.js.map +1 -1
- package/package.json +12 -8
- package/src/assurance/.generated/conformance-verdicts.ts +16 -0
- package/src/bridge/surface/methods/assurance/replaySession.ts +19 -6
- package/src/cli/index.ts +15 -1
- package/src/cli/run.ts +139 -0
- package/src/conformance/.generated/can-i-use.ts +1 -1
- package/src/conformance/.generated/conformance-docs.ts +1 -1
- package/src/remote/local-storage-url.ts +1 -58
- package/src/serve/assets/avatar-route.ts +6 -7
- package/src/serve/assets/resolver.ts +4 -1
- package/src/serve/entries/app-client.ts +7 -0
- package/src/serve/vite-plugin.ts +15 -1
- package/src/serve/vite-service-worker.ts +44 -0
- package/src/serve/worker/client/auth.ts +4 -3
- package/src/serve/worker/client/rtdb-info.ts +4 -17
- package/src/serve/worker/client/rtdb-reads.ts +3 -4
- package/src/serve/worker/host/auth-action-codes.ts +2 -2
- package/src/serve/worker/host/auth-custom-token.ts +4 -2
- package/src/serve/worker/host/dispatch.ts +2 -0
- package/src/serve/worker/host-auth.ts +18 -5
- package/src/serve/worker/inbound-validation.ts +1 -0
- package/src/serve/worker/protocol/storage.ts +2 -14
- package/src/serve/worker/protocol.ts +2 -0
- package/dist/serve/site-ui/_astro/app.CiMHEvEW.js +0 -147
- package/dist/serve/site-ui/_astro/app.DpzsBfos.js +0 -1
|
@@ -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
|
-
|
|
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
|
|
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
|
+
}
|
package/src/serve/vite-plugin.ts
CHANGED
|
@@ -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 (
|
|
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;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The `.info` tree the web hosted client serves itself (ADR-0019, 2026-10-16
|
|
3
|
-
* amendment). Value, single-event and child listeners
|
|
4
|
-
*
|
|
5
|
-
*
|
|
3
|
+
* amendment). Value, single-event and child listeners on a path under `/.info`
|
|
4
|
+
* are answered here; they never reach the host and are not replayed on
|
|
5
|
+
* re-attach. A one-time `get()` there is refused as production refuses it
|
|
6
|
+
* (`assertGettablePath` in `pyric/database/internal`).
|
|
6
7
|
*
|
|
7
8
|
* | Path | Value |
|
|
8
9
|
* | ------------------------ | ---------------------------------- |
|
|
@@ -67,17 +68,3 @@ export function observeInfo(target: RtdbTarget, next: (snapshot: RtdbDataSnapsho
|
|
|
67
68
|
next(snapshot);
|
|
68
69
|
});
|
|
69
70
|
}
|
|
70
|
-
|
|
71
|
-
/** A one-time read of `target`'s `.info` path, answered from the client's own state. */
|
|
72
|
-
export function readInfo(target: RtdbTarget): Promise<RtdbDataSnapshot> {
|
|
73
|
-
return new Promise((resolve) => {
|
|
74
|
-
let answered = false;
|
|
75
|
-
const stop = observeInfo(target, (snapshot) => {
|
|
76
|
-
if (answered) return;
|
|
77
|
-
answered = true;
|
|
78
|
-
resolve(snapshot);
|
|
79
|
-
// A transport may report its state before `observeInfo` returns.
|
|
80
|
-
queueMicrotask(() => stop());
|
|
81
|
-
});
|
|
82
|
-
});
|
|
83
|
-
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { assertGettablePath } from 'pyric/database/internal';
|
|
1
2
|
import { finishSdkRead } from 'pyric/sandbox/internal';
|
|
2
3
|
import { beginWorkerDatabaseActivity } from './sdk-activity.js';
|
|
3
4
|
/** RTDB reads over the worker port. */
|
|
@@ -5,15 +6,13 @@ import { nextId } from './core.js';
|
|
|
5
6
|
import type { RtdbDataSnapshot } from './handles.js';
|
|
6
7
|
import { rtdbRpc, targetParts, type RtdbTarget } from './rtdb-references.js';
|
|
7
8
|
import { hydrateRtdbSnapshot } from './rtdb-snapshots.js';
|
|
8
|
-
import { readInfo, servesInfoLocally } from './rtdb-info.js';
|
|
9
9
|
|
|
10
10
|
export async function rtdbGet(target: RtdbTarget): Promise<RtdbDataSnapshot> {
|
|
11
11
|
const { ref, query } = targetParts(target);
|
|
12
12
|
const activity = beginWorkerDatabaseActivity(target, 'get', 'operation');
|
|
13
|
-
// The client answers `.info` itself; the read never reaches the host.
|
|
14
|
-
const readsInfo = servesInfoLocally(ref);
|
|
15
|
-
if (readsInfo) return finishSdkRead(activity, await readInfo(target));
|
|
16
13
|
try {
|
|
14
|
+
// Production refuses a one-shot read under `.info`; the read never reaches the host.
|
|
15
|
+
assertGettablePath(ref.path);
|
|
17
16
|
return finishSdkRead(activity, hydrateRtdbSnapshot(ref, await rtdbRpc(ref, {
|
|
18
17
|
t: 'op', id: nextId(), method: 'rtdb.get', path: ref.path, ...(query ? { query } : {}),
|
|
19
18
|
})));
|
|
@@ -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, {
|
|
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
|
-
|
|
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
|
/**
|