@pyric/cli 0.1.0-alpha.11 → 0.1.0-alpha.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/serve.js +4 -4
- package/dist/cli/serve.js.map +1 -1
- package/dist/conformance/.generated/can-i-use-browser.d.ts.map +1 -1
- package/dist/conformance/.generated/can-i-use-browser.js +1 -1
- package/dist/conformance/.generated/can-i-use-browser.js.map +1 -1
- package/dist/conformance/.generated/can-i-use.d.ts.map +1 -1
- package/dist/conformance/.generated/can-i-use.js +2 -2
- package/dist/conformance/.generated/can-i-use.js.map +1 -1
- package/dist/serve/bundler.d.ts +13 -8
- package/dist/serve/bundler.d.ts.map +1 -1
- package/dist/serve/bundler.js +31 -36
- package/dist/serve/bundler.js.map +1 -1
- package/dist/serve/docs-ui/docs/_rhythm/index.html +1 -1
- package/dist/serve/docs-ui/docs/agent/set-up-your-agent/index.html +1 -1
- package/dist/serve/docs-ui/docs/agent/watch-and-review/index.html +1 -1
- package/dist/serve/docs-ui/docs/agent/work-with-an-agent/index.html +1 -1
- package/dist/serve/docs-ui/docs/ai-compat/index.html +1 -1
- package/dist/serve/docs-ui/docs/api-reference/index.html +2 -2
- package/dist/serve/docs-ui/docs/api-reference.md +1 -1
- package/dist/serve/docs-ui/docs/app-compat/index.html +1 -1
- package/dist/serve/docs-ui/docs/auth-compat/index.html +1 -1
- package/dist/serve/docs-ui/docs/build/ai-logic/index.html +44 -22
- package/dist/serve/docs-ui/docs/build/ai-logic.md +69 -21
- package/dist/serve/docs-ui/docs/build/authentication/index.html +1 -1
- package/dist/serve/docs-ui/docs/build/cloud-firestore/index.html +1 -1
- package/dist/serve/docs-ui/docs/build/cloud-messaging/index.html +1 -1
- package/dist/serve/docs-ui/docs/build/cloud-storage/index.html +1 -1
- package/dist/serve/docs-ui/docs/build/realtime-database/index.html +1 -1
- package/dist/serve/docs-ui/docs/conformance-scores/index.html +1 -1
- package/dist/serve/docs-ui/docs/create-pyric-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/database-compat/index.html +1 -1
- package/dist/serve/docs-ui/docs/firestore-compat/index.html +1 -1
- package/dist/serve/docs-ui/docs/functions-rtdb-compat/index.html +1 -1
- package/dist/serve/docs-ui/docs/get-started/how-the-swap-works/index.html +2 -1
- package/dist/serve/docs-ui/docs/get-started/how-the-swap-works.md +2 -0
- package/dist/serve/docs-ui/docs/get-started/start-building/index.html +3 -2
- package/dist/serve/docs-ui/docs/get-started/start-building.md +3 -1
- package/dist/serve/docs-ui/docs/index.json +349 -29
- package/dist/serve/docs-ui/docs/messaging-compat/index.html +1 -1
- package/dist/serve/docs-ui/docs/observe/resolve-runtime-status/index.html +28 -0
- package/dist/serve/docs-ui/docs/observe/resolve-runtime-status.md +47 -0
- package/dist/serve/docs-ui/docs/observe/see-whats-happening/index.html +1 -1
- package/dist/serve/docs-ui/docs/observe/shape-your-data/index.html +1 -1
- package/dist/serve/docs-ui/docs/overview/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-admin-app-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-admin-auth-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-admin-database-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-admin-firestore-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-admin-messaging-reference-api/index.html +916 -0
- package/dist/serve/docs-ui/docs/pyric-admin-messaging-reference-api.md +706 -0
- package/dist/serve/docs-ui/docs/pyric-admin-storage-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-ai-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-ai-scripting-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-app-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-auth-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-cli-assurance-browser-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-cli-assurance-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-cli-bridge-client-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-cli-bridge-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-cli-conformance-browser-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-cli-conformance-docs-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-cli-conformance-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-cli-credentials-node-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-cli-discover-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-cli-register-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-cli-remote-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-cli-serve-worker-reference-api/index.html +168 -76
- package/dist/serve/docs-ui/docs/pyric-cli-serve-worker-reference-api.md +156 -73
- package/dist/serve/docs-ui/docs/pyric-cli-verify-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-cli-vite-reference-api/index.html +11 -8
- package/dist/serve/docs-ui/docs/pyric-cli-vite-reference-api.md +17 -4
- package/dist/serve/docs-ui/docs/pyric-database-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-firestore-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-messaging-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-messaging-sw-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-rules-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-sandbox-database-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-sandbox-firestore-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-sandbox-reference-api/index.html +37 -14
- package/dist/serve/docs-ui/docs/pyric-sandbox-reference-api.md +28 -8
- package/dist/serve/docs-ui/docs/pyric-storage-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-ui-agents-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-ui-auth-hooks-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-ui-auth-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-ui-events-hooks-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-ui-events-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-ui-firestore-hooks-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-ui-firestore-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-ui-primitives-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-ui-rtdb-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-ui-rules-hooks-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-ui-rules-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-ui-storage-hooks-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-ui-storage-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-ui-traffic-hooks-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-ui-traffic-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/rules-compat/index.html +1 -1
- package/dist/serve/docs-ui/docs/secure/audit-your-rules/index.html +1 -1
- package/dist/serve/docs-ui/docs/secure/firestore-rules-limits/index.html +1 -1
- package/dist/serve/docs-ui/docs/secure/read-a-denial/index.html +1 -1
- package/dist/serve/docs-ui/docs/secure/rtdb-rules-in-typescript/index.html +1 -1
- package/dist/serve/docs-ui/docs/secure/rules-standard-library/index.html +1 -1
- package/dist/serve/docs-ui/docs/secure/secure-it-with-rules/index.html +1 -1
- package/dist/serve/docs-ui/docs/secure/simulate-and-lint/index.html +1 -1
- package/dist/serve/docs-ui/docs/secure/write-a-rules-test-suite/index.html +1 -1
- package/dist/serve/docs-ui/docs/ship/ship-to-production/index.html +1 -1
- package/dist/serve/docs-ui/docs/ship/test-in-node/index.html +1 -1
- package/dist/serve/docs-ui/docs/storage-compat/index.html +1 -1
- package/dist/serve/docs-ui/docs/trust/how-we-know-it-matches-firebase/index.html +1 -1
- package/dist/serve/docs-ui/docs/trust/versioning-and-compatibility/index.html +1 -1
- package/dist/serve/docs-ui/docs/tutorial/index.html +429 -0
- package/dist/serve/docs-ui/docs/tutorial.md +701 -0
- package/dist/serve/docs-ui/llms.txt +3 -1
- package/dist/serve/entries/init.d.ts.map +1 -1
- package/dist/serve/entries/init.js +3 -0
- package/dist/serve/entries/init.js.map +1 -1
- package/dist/serve/entries/runtime.d.ts.map +1 -1
- package/dist/serve/entries/runtime.js +20 -5
- package/dist/serve/entries/runtime.js.map +1 -1
- package/dist/serve/entries/worker-runtime.d.ts +2 -2
- package/dist/serve/entries/worker-runtime.d.ts.map +1 -1
- package/dist/serve/entries/worker-runtime.js +45 -10
- package/dist/serve/entries/worker-runtime.js.map +1 -1
- package/dist/serve/runtime/chip-config.d.ts +19 -0
- package/dist/serve/runtime/chip-config.d.ts.map +1 -0
- package/dist/serve/runtime/chip-config.js +19 -0
- package/dist/serve/runtime/chip-config.js.map +1 -0
- package/dist/serve/runtime/chip-install.d.ts +10 -0
- package/dist/serve/runtime/chip-install.d.ts.map +1 -0
- package/dist/serve/runtime/chip-install.js +15 -0
- package/dist/serve/runtime/chip-install.js.map +1 -0
- package/dist/serve/runtime/chip.d.ts +17 -0
- package/dist/serve/runtime/chip.d.ts.map +1 -0
- package/dist/serve/runtime/chip.js +257 -0
- package/dist/serve/runtime/chip.js.map +1 -0
- package/dist/serve/runtime/manifest.d.ts +22 -0
- package/dist/serve/runtime/manifest.d.ts.map +1 -0
- package/dist/serve/runtime/manifest.js +20 -0
- package/dist/serve/runtime/manifest.js.map +1 -0
- package/dist/serve/runtime/status.d.ts +49 -0
- package/dist/serve/runtime/status.d.ts.map +1 -0
- package/dist/serve/runtime/status.js +164 -0
- package/dist/serve/runtime/status.js.map +1 -0
- package/dist/serve/runtime/worker-connection.d.ts +4 -0
- package/dist/serve/runtime/worker-connection.d.ts.map +1 -0
- package/dist/serve/runtime/worker-connection.js +11 -0
- package/dist/serve/runtime/worker-connection.js.map +1 -0
- package/dist/serve/runtime/worker-generation.d.ts +14 -0
- package/dist/serve/runtime/worker-generation.d.ts.map +1 -0
- package/dist/serve/runtime/worker-generation.js +56 -0
- package/dist/serve/runtime/worker-generation.js.map +1 -0
- package/dist/serve/runtime/worker-replacement.d.ts +18 -0
- package/dist/serve/runtime/worker-replacement.d.ts.map +1 -0
- package/dist/serve/runtime/worker-replacement.js +29 -0
- package/dist/serve/runtime/worker-replacement.js.map +1 -0
- package/dist/serve/standalone-assets.d.ts +2 -2
- package/dist/serve/standalone-assets.d.ts.map +1 -1
- package/dist/serve/standalone-assets.js +1 -1
- package/dist/serve/standalone-assets.js.map +1 -1
- package/dist/serve/studio-ui/assets/{RulesCodeEditor-CxVYcZXw.js → RulesCodeEditor-BDlH-5lp.js} +1 -1
- package/dist/serve/studio-ui/assets/{index-cpSfgQ7H.js → index-Bf9SSE8L.js} +5 -5
- package/dist/serve/studio-ui/assets/{seed-DeuFKi4J.js → seed-D_UjxfOU.js} +1 -1
- package/dist/serve/studio-ui/index.html +1 -1
- package/dist/serve/vite-ai-config.d.ts +25 -0
- package/dist/serve/vite-ai-config.d.ts.map +1 -0
- package/dist/serve/vite-ai-config.js +45 -0
- package/dist/serve/vite-ai-config.js.map +1 -0
- package/dist/serve/vite-plugin.d.ts +17 -36
- package/dist/serve/vite-plugin.d.ts.map +1 -1
- package/dist/serve/vite-plugin.js +38 -57
- package/dist/serve/vite-plugin.js.map +1 -1
- package/dist/serve/vite-rules-source.d.ts +8 -0
- package/dist/serve/vite-rules-source.d.ts.map +1 -0
- package/dist/serve/vite-rules-source.js +23 -0
- package/dist/serve/vite-rules-source.js.map +1 -0
- package/dist/serve/vite-worker-runtime.d.ts +20 -0
- package/dist/serve/vite-worker-runtime.d.ts.map +1 -0
- package/dist/serve/vite-worker-runtime.js +25 -0
- package/dist/serve/vite-worker-runtime.js.map +1 -0
- package/dist/serve/worker/client/connection.d.ts +7 -2
- package/dist/serve/worker/client/connection.d.ts.map +1 -1
- package/dist/serve/worker/client/connection.js +13 -16
- package/dist/serve/worker/client/connection.js.map +1 -1
- package/dist/serve/worker/client/core.d.ts +7 -0
- package/dist/serve/worker/client/core.d.ts.map +1 -1
- package/dist/serve/worker/client/core.js +28 -0
- package/dist/serve/worker/client/core.js.map +1 -1
- package/dist/serve/worker/client/runtime-control.d.ts +8 -0
- package/dist/serve/worker/client/runtime-control.d.ts.map +1 -0
- package/dist/serve/worker/client/runtime-control.js +11 -0
- package/dist/serve/worker/client/runtime-control.js.map +1 -0
- package/dist/serve/worker/entry.js +44 -2
- package/dist/serve/worker/entry.js.map +1 -1
- package/dist/serve/worker/host/core.js +1 -1
- package/dist/serve/worker/host/core.js.map +1 -1
- package/dist/serve/worker/host/firestore-writes.d.ts.map +1 -1
- package/dist/serve/worker/host/firestore-writes.js +3 -2
- package/dist/serve/worker/host/firestore-writes.js.map +1 -1
- package/dist/serve/worker/host/studio.js +1 -1
- package/dist/serve/worker/host/studio.js.map +1 -1
- package/dist/serve/worker/host-context.d.ts +1 -1
- package/dist/serve/worker/host-context.d.ts.map +1 -1
- package/dist/serve/worker/index.d.ts +2 -1
- package/dist/serve/worker/index.d.ts.map +1 -1
- package/dist/serve/worker/index.js +2 -1
- package/dist/serve/worker/index.js.map +1 -1
- package/dist/serve/worker/protocol.d.ts +15 -1
- package/dist/serve/worker/protocol.d.ts.map +1 -1
- package/dist/serve/worker/protocol.js +2 -2
- package/dist/serve/worker/protocol.js.map +1 -1
- package/dist/serve/worker/retirement.d.ts +21 -0
- package/dist/serve/worker/retirement.d.ts.map +1 -0
- package/dist/serve/worker/retirement.js +97 -0
- package/dist/serve/worker/retirement.js.map +1 -0
- package/dist/serve/worker/serve-init.d.ts.map +1 -1
- package/dist/serve/worker/serve-init.js +5 -6
- package/dist/serve/worker/serve-init.js.map +1 -1
- package/dist/vite.d.ts +1 -0
- package/dist/vite.d.ts.map +1 -1
- package/dist/vite.js.map +1 -1
- package/package.json +6 -4
- package/src/assurance/.generated/conformance-verdicts.ts +1090 -0
- package/src/assurance/attachment.ts +211 -0
- package/src/assurance/browser.ts +80 -0
- package/src/assurance/campaign.ts +496 -0
- package/src/assurance/capabilities.ts +446 -0
- package/src/assurance/cases.ts +43 -0
- package/src/assurance/index.ts +75 -0
- package/src/assurance/runner.ts +885 -0
- package/src/assurance/tool-names.ts +15 -0
- package/src/assurance/tools.ts +830 -0
- package/src/assurance/types.ts +352 -0
- package/src/assurance/validation.ts +434 -0
- package/src/bridge/client/bridge.ts +550 -0
- package/src/bridge/client/dispatch.ts +137 -0
- package/src/bridge/client.ts +45 -0
- package/src/bridge/protocol.ts +351 -0
- package/src/bridge/server/audit.ts +53 -0
- package/src/bridge/server/bridge.ts +597 -0
- package/src/bridge/server/headless.ts +160 -0
- package/src/bridge/server/json-schema-to-zod.ts +108 -0
- package/src/bridge/server/local-bridge.ts +66 -0
- package/src/bridge/server/logger.ts +50 -0
- package/src/bridge/server/mcp-contract.ts +93 -0
- package/src/bridge/server/mcp.ts +129 -0
- package/src/bridge/server/peer.ts +232 -0
- package/src/bridge/server/standalone.ts +347 -0
- package/src/bridge/server/tool-metadata.ts +99 -0
- package/src/bridge/server.ts +28 -0
- package/src/cli/can-i-use.ts +50 -0
- package/src/cli/cli.test.ts +493 -0
- package/src/cli/database-rules.ts +329 -0
- package/src/cli/dev-runner.ts +282 -0
- package/src/cli/firebase-json.ts +100 -0
- package/src/cli/firestore-indexes.ts +70 -0
- package/src/cli/index.ts +425 -0
- package/src/cli/init.ts +260 -0
- package/src/cli/mcp-proxy.ts +196 -0
- package/src/cli/parse-args.ts +80 -0
- package/src/cli/rules.ts +259 -0
- package/src/cli/scope.ts +67 -0
- package/src/cli/serve.ts +996 -0
- package/src/cli/service-commands.ts +73 -0
- package/src/cli/snapshot.ts +164 -0
- package/src/cli/storage-rules.ts +172 -0
- package/src/cli/verify.ts +416 -0
- package/src/conformance/.generated/can-i-use-browser.ts +113 -0
- package/src/conformance/.generated/can-i-use.ts +117 -0
- package/src/conformance/.generated/conformance-docs.ts +14 -0
- package/src/conformance/browser.ts +24 -0
- package/src/conformance/can-i-use-tool.ts +51 -0
- package/src/conformance/can-i-use.ts +18 -0
- package/src/conformance/docs.ts +8 -0
- package/src/conformance/index.ts +20 -0
- package/src/conformance/tools.ts +16 -0
- package/src/credentials/core/memoize-ttl.ts +142 -0
- package/src/credentials/core/types.ts +10 -0
- package/src/credentials/node/from-adc.ts +96 -0
- package/src/credentials/node/from-service-account.ts +133 -0
- package/src/credentials/node/index.ts +6 -0
- package/src/discover/concurrency.ts +127 -0
- package/src/discover/crawler-adapter.ts +142 -0
- package/src/discover/crawler.ts +1127 -0
- package/src/discover/credential-free.ts +27 -0
- package/src/discover/findCollectionGroup.ts +131 -0
- package/src/discover/firestore-source.ts +59 -0
- package/src/discover/index.ts +5 -0
- package/src/discover/merge.ts +523 -0
- package/src/discover/session.ts +402 -0
- package/src/discover/tools.ts +201 -0
- package/src/discover/types.ts +187 -0
- package/src/discover/wire.ts +324 -0
- package/src/functions-rtdb/child.ts +382 -0
- package/src/functions-rtdb/delivery.ts +7 -0
- package/src/functions-rtdb/discovery.ts +136 -0
- package/src/functions-rtdb/event.ts +64 -0
- package/src/functions-rtdb/execution.ts +111 -0
- package/src/functions-rtdb/in-memory-delivery.ts +40 -0
- package/src/functions-rtdb/project.ts +74 -0
- package/src/functions-rtdb/projection.ts +104 -0
- package/src/functions-rtdb/reference-pattern.ts +20 -0
- package/src/functions-rtdb/remote-delivery.ts +23 -0
- package/src/pkg-version.ts +49 -0
- package/src/register/esm-exports.ts +63 -0
- package/src/register/hooks.ts +30 -0
- package/src/register/index.ts +150 -0
- package/src/register/mapping.ts +33 -0
- package/src/remote/index.ts +1050 -0
- package/src/rtdb/crawl-snapshot.ts +118 -0
- package/src/rtdb/inspection.ts +137 -0
- package/src/rtdb/load-rules-document.ts +56 -0
- package/src/rtdb/rules-generation-tool.ts +38 -0
- package/src/rtdb/rules-json.ts +29 -0
- package/src/serve/activity-guard.ts +29 -0
- package/src/serve/activity-route.ts +164 -0
- package/src/serve/activity-warning.ts +26 -0
- package/src/serve/bridge-mount.ts +200 -0
- package/src/serve/bundler.ts +519 -0
- package/src/serve/capture-store.ts +65 -0
- package/src/serve/discovery.ts +199 -0
- package/src/serve/entries/ai.ts +292 -0
- package/src/serve/entries/app-backend.ts +4 -0
- package/src/serve/entries/app-client.ts +26 -0
- package/src/serve/entries/app-session-store.ts +47 -0
- package/src/serve/entries/app.ts +11 -0
- package/src/serve/entries/auth-helper-core.ts +181 -0
- package/src/serve/entries/auth-helper-dom.ts +134 -0
- package/src/serve/entries/auth-helper-runtime.ts +20 -0
- package/src/serve/entries/auth.ts +255 -0
- package/src/serve/entries/bridge-url.ts +31 -0
- package/src/serve/entries/database.ts +129 -0
- package/src/serve/entries/firestore.ts +232 -0
- package/src/serve/entries/init.ts +49 -0
- package/src/serve/entries/keepalive.ts +46 -0
- package/src/serve/entries/messaging-sw.ts +75 -0
- package/src/serve/entries/messaging.ts +96 -0
- package/src/serve/entries/runtime.ts +556 -0
- package/src/serve/entries/session-store.ts +97 -0
- package/src/serve/entries/storage.ts +92 -0
- package/src/serve/entries/tab-sync-wiring.ts +274 -0
- package/src/serve/entries/worker-runtime.ts +135 -0
- package/src/serve/init-payload.ts +42 -0
- package/src/serve/namespace.ts +675 -0
- package/src/serve/open-browser.ts +68 -0
- package/src/serve/rules.ts +272 -0
- package/src/serve/runtime/chip-config.ts +30 -0
- package/src/serve/runtime/chip-install.ts +27 -0
- package/src/serve/runtime/chip.ts +282 -0
- package/src/serve/runtime/manifest.ts +37 -0
- package/src/serve/runtime/status.ts +216 -0
- package/src/serve/runtime/worker-connection.ts +14 -0
- package/src/serve/runtime/worker-generation.ts +75 -0
- package/src/serve/runtime/worker-replacement.ts +42 -0
- package/src/serve/sandbox-marker.ts +31 -0
- package/src/serve/server.ts +428 -0
- package/src/serve/standalone-assets.ts +180 -0
- package/src/serve/state-store.ts +166 -0
- package/src/serve/studio/disk-project-store.ts +185 -0
- package/src/serve/studio/disk-workspace.ts +162 -0
- package/src/serve/studio/index.ts +20 -0
- package/src/serve/studio/routes.ts +237 -0
- package/src/serve/studio/store-types.ts +48 -0
- package/src/serve/studio/studio-storage.test.ts +289 -0
- package/src/serve/vite-ai-config.ts +77 -0
- package/src/serve/vite-plugin.ts +1075 -0
- package/src/serve/vite-rules-source.ts +28 -0
- package/src/serve/vite-worker-runtime.ts +51 -0
- package/src/serve/worker/activity-bootstrap.ts +24 -0
- package/src/serve/worker/client/admin-firestore.ts +38 -0
- package/src/serve/worker/client/ai.ts +124 -0
- package/src/serve/worker/client/auth.ts +489 -0
- package/src/serve/worker/client/connection.ts +227 -0
- package/src/serve/worker/client/core.ts +384 -0
- package/src/serve/worker/client/disconnect.ts +45 -0
- package/src/serve/worker/client/firestore-reads.ts +181 -0
- package/src/serve/worker/client/firestore-refs.ts +226 -0
- package/src/serve/worker/client/firestore-writes.ts +232 -0
- package/src/serve/worker/client/handles.ts +91 -0
- package/src/serve/worker/client/messaging.ts +119 -0
- package/src/serve/worker/client/presence.ts +174 -0
- package/src/serve/worker/client/rtdb.ts +311 -0
- package/src/serve/worker/client/rules.ts +63 -0
- package/src/serve/worker/client/runtime-control.ts +22 -0
- package/src/serve/worker/client/service-worker-connection.ts +59 -0
- package/src/serve/worker/client/snapshots.ts +86 -0
- package/src/serve/worker/client/storage.ts +195 -0
- package/src/serve/worker/client/studio.ts +88 -0
- package/src/serve/worker/client.ts +57 -0
- package/src/serve/worker/durable-persistence.ts +137 -0
- package/src/serve/worker/entry.ts +223 -0
- package/src/serve/worker/host/admin-firestore.ts +88 -0
- package/src/serve/worker/host/connection.ts +179 -0
- package/src/serve/worker/host/core.ts +387 -0
- package/src/serve/worker/host/dispatch.ts +316 -0
- package/src/serve/worker/host/firestore-reads.ts +117 -0
- package/src/serve/worker/host/firestore-writes.ts +456 -0
- package/src/serve/worker/host/presence.ts +312 -0
- package/src/serve/worker/host/rtdb.ts +136 -0
- package/src/serve/worker/host/rules.ts +128 -0
- package/src/serve/worker/host/storage.ts +312 -0
- package/src/serve/worker/host/studio.ts +75 -0
- package/src/serve/worker/host/subscriptions.ts +238 -0
- package/src/serve/worker/host-ai.ts +165 -0
- package/src/serve/worker/host-auth.ts +469 -0
- package/src/serve/worker/host-context.ts +273 -0
- package/src/serve/worker/host-events.ts +94 -0
- package/src/serve/worker/host-messaging.ts +240 -0
- package/src/serve/worker/host.ts +47 -0
- package/src/serve/worker/index.ts +196 -0
- package/src/serve/worker/presence-timing.ts +15 -0
- package/src/serve/worker/protocol.ts +1124 -0
- package/src/serve/worker/retirement.ts +125 -0
- package/src/serve/worker/serve-init.ts +605 -0
- package/src/serve/worker/service-worker-channel.ts +33 -0
- package/src/serve/worker/service-worker-relay.ts +86 -0
- package/src/serve/writer-lock.ts +53 -0
- package/src/verify/cases.ts +233 -0
- package/src/verify/fixture.ts +258 -0
- package/src/verify/index.ts +519 -0
- package/src/verify/tools.ts +108 -0
- package/src/version/compat-target.ts +17 -0
- package/src/vite.ts +18 -0
- package/dist/serve/docs-ui/docs/pyric-firestore-values-reference-api/index.html +0 -31
- package/dist/serve/docs-ui/docs/pyric-firestore-values-reference-api.md +0 -29
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The bundle the import map serves for `firebase/storage`.
|
|
3
|
+
*
|
|
4
|
+
* Served apps keep their normal Firebase Storage imports. In worker mode, browse
|
|
5
|
+
* and metadata reads use the shared worker-hosted object store; operations that
|
|
6
|
+
* still lack a worker protocol fail loudly. In in-page fallback mode, the entry
|
|
7
|
+
* delegates to `pyric/storage` against the page sandbox.
|
|
8
|
+
*/
|
|
9
|
+
import * as ip from 'pyric/storage';
|
|
10
|
+
import {
|
|
11
|
+
getStorage as pyricGetStorage,
|
|
12
|
+
type FirebaseStorage,
|
|
13
|
+
} from 'pyric/storage';
|
|
14
|
+
import {
|
|
15
|
+
getStorage as workerGetStorage,
|
|
16
|
+
getBlob as workerGetBlob,
|
|
17
|
+
getBytes as workerGetBytes,
|
|
18
|
+
getDownloadURL as workerGetDownloadURL,
|
|
19
|
+
getMetadata as workerGetMetadata,
|
|
20
|
+
listAll as workerListAll,
|
|
21
|
+
ref as workerRef,
|
|
22
|
+
uploadBytes as workerUploadBytes,
|
|
23
|
+
deleteObject as workerDeleteObject,
|
|
24
|
+
} from '../worker/client.js';
|
|
25
|
+
import { useWorker } from './worker-runtime.js';
|
|
26
|
+
import { getApp, type FirebaseApp } from 'pyric/app';
|
|
27
|
+
import { workerClientForApp } from './app-client.js';
|
|
28
|
+
|
|
29
|
+
const workerStorageByApp = new WeakMap<FirebaseApp, FirebaseStorage>();
|
|
30
|
+
|
|
31
|
+
export function getStorage(app?: FirebaseApp, _bucketUrl?: string): FirebaseStorage {
|
|
32
|
+
const resolved = app ?? getApp();
|
|
33
|
+
if (!useWorker) return pyricGetStorage(resolved, _bucketUrl);
|
|
34
|
+
const existing = workerStorageByApp.get(resolved);
|
|
35
|
+
if (existing) return existing;
|
|
36
|
+
const client = workerClientForApp(resolved);
|
|
37
|
+
const handle = Object.assign(workerGetStorage(client), { app: resolved }) as unknown as FirebaseStorage;
|
|
38
|
+
workerStorageByApp.set(resolved, handle);
|
|
39
|
+
return handle;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export const ref = (useWorker ? workerRef : ip.ref) as typeof ip.ref;
|
|
43
|
+
export const listAll = (useWorker ? workerListAll : ip.listAll) as typeof ip.listAll;
|
|
44
|
+
export const getMetadata = (
|
|
45
|
+
useWorker ? workerGetMetadata : ip.getMetadata
|
|
46
|
+
) as typeof ip.getMetadata;
|
|
47
|
+
export const getBlob = (useWorker ? workerGetBlob : ip.getBlob) as typeof ip.getBlob;
|
|
48
|
+
export const getDownloadURL = (
|
|
49
|
+
useWorker ? workerGetDownloadURL : ip.getDownloadURL
|
|
50
|
+
) as typeof ip.getDownloadURL;
|
|
51
|
+
|
|
52
|
+
export const StorageError = ip.StorageError;
|
|
53
|
+
|
|
54
|
+
// pyric replaces the Firebase emulator; connectStorageEmulator is a
|
|
55
|
+
// no-op. Served apps are already talking to the pyric sandbox (or the
|
|
56
|
+
// SharedWorker's shared store), which IS the local emulator — pointing
|
|
57
|
+
// it at a `firebase-tools` emulator host would be a step backward, not
|
|
58
|
+
// forward, so the call is logged and swallowed rather than forwarded.
|
|
59
|
+
export function connectStorageEmulator(
|
|
60
|
+
storage: unknown,
|
|
61
|
+
_host: string,
|
|
62
|
+
_port: number,
|
|
63
|
+
_options?: unknown,
|
|
64
|
+
): void {
|
|
65
|
+
console.info(
|
|
66
|
+
'[pyric dev] pyric replaces the Firebase emulator; connectStorageEmulator is a no-op.',
|
|
67
|
+
);
|
|
68
|
+
if (!useWorker) ip.connectStorageEmulator(storage as FirebaseStorage, _host, _port, _options as never);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function unsupportedWorkerApi(name: string): never {
|
|
72
|
+
throw new Error(
|
|
73
|
+
`firebase/storage ${name}() is not supported over the pyric SharedWorker yet. ` +
|
|
74
|
+
'Use the in-page fallback for this operation.',
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function workerOrInPage<T extends (...args: any[]) => unknown>(name: string, fn: T): T {
|
|
79
|
+
return (useWorker ? (() => unsupportedWorkerApi(name)) : fn) as T;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Byte ops use the worker protocol (base64 `storage.putBytes` /
|
|
83
|
+
// `storage.getBytes` / `storage.deleteObject`) and the initiating app port's
|
|
84
|
+
// authenticated session. All app ports reach the same object store and
|
|
85
|
+
// ruleset; only their active Auth sessions differ. Payloads are capped at
|
|
86
|
+
// 8 MiB per op.
|
|
87
|
+
export const uploadBytes = (useWorker ? workerUploadBytes : ip.uploadBytes) as typeof ip.uploadBytes;
|
|
88
|
+
export const getBytes = (useWorker ? workerGetBytes : ip.getBytes) as typeof ip.getBytes;
|
|
89
|
+
export const deleteObject = (useWorker ? workerDeleteObject : ip.deleteObject) as typeof ip.deleteObject;
|
|
90
|
+
|
|
91
|
+
export const uploadString = workerOrInPage('uploadString', ip.uploadString);
|
|
92
|
+
export const updateMetadata = workerOrInPage('updateMetadata', ip.updateMetadata);
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-tab realtime sync wiring for `pyric dev`.
|
|
3
|
+
*
|
|
4
|
+
* Two independent channels carry two independent kinds of state:
|
|
5
|
+
*
|
|
6
|
+
* 1. Firestore data — delegated entirely to `sandbox.enableTabSync()`.
|
|
7
|
+
* That call attaches a BroadcastChannel-based write-propagation and
|
|
8
|
+
* late-join handshake (hello/state) internally; we just supply the
|
|
9
|
+
* channel so the name is under our control.
|
|
10
|
+
*
|
|
11
|
+
* 2. Auth state — `enableTabSync` only carries Firestore write events;
|
|
12
|
+
* auth sign-in / sign-out lives entirely outside the Firestore environment.
|
|
13
|
+
* We bridge it on a SECOND channel using a simple full-state protocol:
|
|
14
|
+
* any auth change broadcasts the complete user DB + current UID; a new tab
|
|
15
|
+
* announces itself with `hello` and receives the current state in reply.
|
|
16
|
+
*
|
|
17
|
+
* Why full-state instead of deltas?
|
|
18
|
+
* Auth payloads are tiny (a handful of users). Broadcasting the entire user DB
|
|
19
|
+
* + current UID on every change avoids ordering bugs where a `seedUsers` arrives
|
|
20
|
+
* before a `restoreSession` (or vice versa) and leaves the receiving tab in an
|
|
21
|
+
* inconsistent partial state. One atomic message = one safe apply.
|
|
22
|
+
*
|
|
23
|
+
* Echo-loop prevention (load-bearing):
|
|
24
|
+
* Applying a received auth state calls `seedUsers` + `restoreSession`, which
|
|
25
|
+
* fire `subscribeUsers` / `onAuthStateChanged` callbacks — the same callbacks
|
|
26
|
+
* that trigger outbound broadcasts. Without a guard this creates an infinite
|
|
27
|
+
* cross-tab loop. The `applyingRemoteAuth` boolean breaks it: set to `true`
|
|
28
|
+
* immediately before the apply and `false` in the `finally` block; both
|
|
29
|
+
* broadcast callbacks check the flag and no-op while it is set.
|
|
30
|
+
* JavaScript is single-threaded, and `seedUsers` + `restoreSession` are
|
|
31
|
+
* synchronous (they resolve before any pending microtask can enter), so the
|
|
32
|
+
* boolean is collision-free.
|
|
33
|
+
*
|
|
34
|
+
* Late-join (hello/state):
|
|
35
|
+
* A tab that opens AFTER others have already signed in broadcasts `hello` on
|
|
36
|
+
* init. Any peer that receives it replies with `state` (full user DB +
|
|
37
|
+
* currentUid). The joining tab receives that `state` and calls
|
|
38
|
+
* `seedUsers` + `restoreSession` to catch up — same as if those ops had
|
|
39
|
+
* happened locally. The existing tabs' `onAuthStateChanged` listeners in the
|
|
40
|
+
* joining tab fire as a result, exactly as they would in production.
|
|
41
|
+
*
|
|
42
|
+
* Origin tagging:
|
|
43
|
+
* Every message carries an `origin` equal to the sending tab's random ID.
|
|
44
|
+
* Receiving tabs drop messages whose `origin` equals their own ID — both to
|
|
45
|
+
* comply with the echo guard and to be correct on injected test channels that
|
|
46
|
+
* may deliver messages back to the sender (the real BroadcastChannel spec does
|
|
47
|
+
* NOT deliver to the sender, but test stubs may differ).
|
|
48
|
+
*/
|
|
49
|
+
|
|
50
|
+
import type { Auth, SeedUser } from 'pyric/auth';
|
|
51
|
+
|
|
52
|
+
// ─── Auth channel message shapes ─────────────────────────────────────────────
|
|
53
|
+
|
|
54
|
+
/** Full auth state broadcast whenever auth changes or in response to a hello. */
|
|
55
|
+
interface AuthStateMessage {
|
|
56
|
+
kind: 'state';
|
|
57
|
+
/** Sending tab's random ID — used for echo suppression. */
|
|
58
|
+
origin: string;
|
|
59
|
+
/** Complete user DB snapshot in the shape `seedUsers` accepts. */
|
|
60
|
+
users: SeedUser[];
|
|
61
|
+
/** UID of the signed-in user, or null when signed out. */
|
|
62
|
+
currentUid: string | null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Sent by a new tab on init to request the current auth state from peers. */
|
|
66
|
+
interface AuthHelloMessage {
|
|
67
|
+
kind: 'hello';
|
|
68
|
+
/** Sending tab's random ID. */
|
|
69
|
+
origin: string;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
type AuthSyncMessage = AuthStateMessage | AuthHelloMessage;
|
|
73
|
+
|
|
74
|
+
/** Loose type guard — checks the discriminant fields only. */
|
|
75
|
+
function isAuthSyncMessage(v: unknown): v is AuthSyncMessage {
|
|
76
|
+
return (
|
|
77
|
+
typeof v === 'object' &&
|
|
78
|
+
v !== null &&
|
|
79
|
+
typeof (v as Record<string, unknown>).kind === 'string' &&
|
|
80
|
+
typeof (v as Record<string, unknown>).origin === 'string'
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// ─── Auth ops interface (injected to keep this module testable) ───────────────
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* The subset of `pyric/auth`'s `sandbox` namespace that we need.
|
|
88
|
+
* Injected so tests can stub without a real sandbox.
|
|
89
|
+
*/
|
|
90
|
+
export interface AuthOps {
|
|
91
|
+
exportUsers(auth: Auth): SeedUser[];
|
|
92
|
+
seedUsers(auth: Auth, users: ReadonlyArray<SeedUser>): void;
|
|
93
|
+
restoreSession(auth: Auth, uid: string): void;
|
|
94
|
+
subscribeUsers(auth: Auth, callback: () => void): () => void;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// ─── Public entry point ───────────────────────────────────────────────────────
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Wire cross-tab auth synchronization.
|
|
101
|
+
*
|
|
102
|
+
* Call once, after the sandbox and auth are initialised, near the END of
|
|
103
|
+
* `runtime.ts` init. The function is a no-op when `BroadcastChannel` is
|
|
104
|
+
* not available (SSR / old Node environments stay inert).
|
|
105
|
+
*
|
|
106
|
+
* @param auth The `Auth` handle for this page (from `getAuth(sandbox)`).
|
|
107
|
+
* @param authOps The `sandbox` namespace from `pyric/auth`.
|
|
108
|
+
* @param onAuthStateChanged `onAuthStateChanged` from `pyric/auth`.
|
|
109
|
+
* @param signOut `signOut` from `pyric/auth`.
|
|
110
|
+
* @param channel Optional injected channel (omit in production — a real
|
|
111
|
+
* `BroadcastChannel('pyric:serve:auth-sync')` is created).
|
|
112
|
+
* @param originId Optional injected origin ID (omit in production).
|
|
113
|
+
*
|
|
114
|
+
* @returns A disable function that removes all listeners and closes the channel.
|
|
115
|
+
* Safe to ignore in `pyric dev` — the tab is the lifetime.
|
|
116
|
+
*/
|
|
117
|
+
export function wireAuthTabSync(
|
|
118
|
+
auth: Auth,
|
|
119
|
+
authOps: AuthOps,
|
|
120
|
+
onAuthStateChanged: (auth: Auth, cb: (user: unknown) => void) => () => void,
|
|
121
|
+
signOut: (auth: Auth) => Promise<void>,
|
|
122
|
+
channel?: { postMessage(msg: unknown): void; addEventListener(type: 'message', l: (ev: { data: unknown }) => void): void; removeEventListener(type: 'message', l: (ev: { data: unknown }) => void): void; close(): void },
|
|
123
|
+
originId?: string,
|
|
124
|
+
): () => void {
|
|
125
|
+
// Guard: BroadcastChannel is browser-only. Non-browser imports (SSR, bundler
|
|
126
|
+
// tree-shaking analysis) must stay inert.
|
|
127
|
+
if (!channel && typeof BroadcastChannel === 'undefined') {
|
|
128
|
+
return () => { /* no-op: BroadcastChannel unavailable */ };
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const bc = channel ?? new BroadcastChannel('pyric:serve:auth-sync');
|
|
132
|
+
const ownedByUs = !channel;
|
|
133
|
+
|
|
134
|
+
const origin: string =
|
|
135
|
+
originId ??
|
|
136
|
+
(typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function'
|
|
137
|
+
? crypto.randomUUID()
|
|
138
|
+
: `auth-tab-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
|
139
|
+
|
|
140
|
+
// ── Echo / loop guard ────────────────────────────────────────────────────
|
|
141
|
+
//
|
|
142
|
+
// WHY: applying a received `state` calls `seedUsers` (fires `subscribeUsers`
|
|
143
|
+
// callbacks) and `restoreSession` (fires `onAuthStateChanged` callbacks).
|
|
144
|
+
// Both of those callbacks are hooked to broadcast the current state outward.
|
|
145
|
+
// Without this guard, the sequence would loop: Tab A signs in → broadcasts →
|
|
146
|
+
// Tab B receives → applies → subscribeUsers/onAuthStateChanged fire in Tab B
|
|
147
|
+
// → Tab B broadcasts → Tab A receives → applies → loops.
|
|
148
|
+
//
|
|
149
|
+
// Setting this flag to `true` around the synchronous apply block prevents
|
|
150
|
+
// the outbound broadcast callbacks from posting. Both `seedUsers` and
|
|
151
|
+
// `restoreSession` are synchronous; JS is single-threaded — the flag is
|
|
152
|
+
// reset to `false` before any micro/macrotask can re-enter.
|
|
153
|
+
let applyingRemoteAuth = false;
|
|
154
|
+
|
|
155
|
+
// ── Debounced outbound broadcast ─────────────────────────────────────────
|
|
156
|
+
//
|
|
157
|
+
// We debounce (~100ms) to coalesce a `seedUsers` + `restoreSession` pair
|
|
158
|
+
// (which fires both subscribeUsers AND onAuthStateChanged in quick succession)
|
|
159
|
+
// into a single outbound message. Without debounce the two callbacks could
|
|
160
|
+
// send two rapid-fire broadcasts to peers, causing redundant double-applies.
|
|
161
|
+
let broadcastTimer: ReturnType<typeof setTimeout> | null = null;
|
|
162
|
+
|
|
163
|
+
function scheduleBroadcast(): void {
|
|
164
|
+
// Skip if we're currently applying a remote state — that would echo.
|
|
165
|
+
if (applyingRemoteAuth) return;
|
|
166
|
+
if (broadcastTimer !== null) clearTimeout(broadcastTimer);
|
|
167
|
+
broadcastTimer = setTimeout(() => {
|
|
168
|
+
broadcastTimer = null;
|
|
169
|
+
if (applyingRemoteAuth) return; // double-check after the debounce delay
|
|
170
|
+
broadcastCurrentState();
|
|
171
|
+
}, 100);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function broadcastCurrentState(): void {
|
|
175
|
+
const msg: AuthStateMessage = {
|
|
176
|
+
kind: 'state',
|
|
177
|
+
origin,
|
|
178
|
+
users: authOps.exportUsers(auth),
|
|
179
|
+
currentUid: auth.currentUser?.uid ?? null,
|
|
180
|
+
};
|
|
181
|
+
bc.postMessage(msg);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// ── Subscribe to local auth changes ──────────────────────────────────────
|
|
185
|
+
//
|
|
186
|
+
// Two hooks cover the two dimensions of auth state:
|
|
187
|
+
// - `subscribeUsers` fires when the user DB changes (new user created,
|
|
188
|
+
// user properties changed, user deleted). This carries the FULL user DB.
|
|
189
|
+
// - `onAuthStateChanged` fires when the signed-in session changes (sign in,
|
|
190
|
+
// sign out, session swap). This carries `currentUid`.
|
|
191
|
+
//
|
|
192
|
+
// Broadcasting on BOTH ensures sign-in (changes currentUid without touching
|
|
193
|
+
// users DB), sign-up (creates a user AND changes currentUid), and user-DB
|
|
194
|
+
// edits all propagate. They share the debounce so a sign-up that fires
|
|
195
|
+
// subscribeUsers first and onAuthStateChanged second sends one message, not two.
|
|
196
|
+
|
|
197
|
+
const unsubUsers = authOps.subscribeUsers(auth, scheduleBroadcast);
|
|
198
|
+
const unsubAuthState = onAuthStateChanged(auth, scheduleBroadcast);
|
|
199
|
+
|
|
200
|
+
// ── Receive remote auth state ─────────────────────────────────────────────
|
|
201
|
+
|
|
202
|
+
const handleMessage = async (ev: { data: unknown }): Promise<void> => {
|
|
203
|
+
if (!isAuthSyncMessage(ev.data)) return;
|
|
204
|
+
const msg = ev.data;
|
|
205
|
+
|
|
206
|
+
// Echo suppression: ignore messages from ourselves.
|
|
207
|
+
if (msg.origin === origin) return;
|
|
208
|
+
|
|
209
|
+
if (msg.kind === 'hello') {
|
|
210
|
+
// A new tab joined and wants the current state. Reply immediately (no
|
|
211
|
+
// debounce — this is a direct reply to a single event, not a user write).
|
|
212
|
+
broadcastCurrentState();
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (msg.kind === 'state') {
|
|
217
|
+
// Apply received full auth state under the echo guard.
|
|
218
|
+
//
|
|
219
|
+
// Order is critical:
|
|
220
|
+
// 1. seedUsers FIRST — ensures the uid in `currentUid` exists in the DB.
|
|
221
|
+
// restoreSession throws 'user-not-found' if the uid is absent, which
|
|
222
|
+
// would leave the tab signed out even though the peer is signed in.
|
|
223
|
+
// 2. restoreSession SECOND — re-establishes the signed-in session.
|
|
224
|
+
// If `currentUid` is null, sign out instead (sign-out in Tab A must
|
|
225
|
+
// flow through to Tab B).
|
|
226
|
+
applyingRemoteAuth = true;
|
|
227
|
+
try {
|
|
228
|
+
authOps.seedUsers(auth, msg.users);
|
|
229
|
+
if (msg.currentUid) {
|
|
230
|
+
try {
|
|
231
|
+
authOps.restoreSession(auth, msg.currentUid);
|
|
232
|
+
} catch {
|
|
233
|
+
// uid was in the exported list but restoreSession still failed
|
|
234
|
+
// (e.g. user disabled between export and apply). Sign out to stay
|
|
235
|
+
// consistent with the peer's next broadcast rather than silently
|
|
236
|
+
// leaving the tab in a stale signed-in state.
|
|
237
|
+
await signOut(auth).catch(() => {});
|
|
238
|
+
}
|
|
239
|
+
} else {
|
|
240
|
+
// Peer signed out — mirror that here.
|
|
241
|
+
await signOut(auth).catch(() => {});
|
|
242
|
+
}
|
|
243
|
+
} finally {
|
|
244
|
+
applyingRemoteAuth = false;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
bc.addEventListener('message', handleMessage);
|
|
250
|
+
|
|
251
|
+
// ── Late-join hello ───────────────────────────────────────────────────────
|
|
252
|
+
//
|
|
253
|
+
// Post a `hello` AFTER wiring the message listener so we don't miss a `state`
|
|
254
|
+
// reply that arrives while still setting up (relevant on injected synchronous
|
|
255
|
+
// test channels). Peers reply with their current auth state; we apply it in
|
|
256
|
+
// `handleMessage` above. This lets a freshly-opened tab immediately inherit
|
|
257
|
+
// the signed-in user from an already-open tab without waiting for the next
|
|
258
|
+
// user-driven auth change.
|
|
259
|
+
const helloMsg: AuthHelloMessage = { kind: 'hello', origin };
|
|
260
|
+
bc.postMessage(helloMsg);
|
|
261
|
+
|
|
262
|
+
// ── Disable ───────────────────────────────────────────────────────────────
|
|
263
|
+
|
|
264
|
+
return () => {
|
|
265
|
+
if (broadcastTimer !== null) {
|
|
266
|
+
clearTimeout(broadcastTimer);
|
|
267
|
+
broadcastTimer = null;
|
|
268
|
+
}
|
|
269
|
+
unsubUsers();
|
|
270
|
+
unsubAuthState();
|
|
271
|
+
bc.removeEventListener('message', handleMessage);
|
|
272
|
+
if (ownedByUs) bc.close();
|
|
273
|
+
};
|
|
274
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/** SharedWorker availability, control-port ownership, presence, and staleness. */
|
|
2
|
+
import {
|
|
3
|
+
getFirestore,
|
|
4
|
+
getWorkerVersion,
|
|
5
|
+
startPresence,
|
|
6
|
+
subscribeEvents,
|
|
7
|
+
subscribePresence,
|
|
8
|
+
type ClientDb,
|
|
9
|
+
} from '../worker/client.js';
|
|
10
|
+
import { getServiceWorkerFirestore } from '../worker/client/service-worker-connection.js';
|
|
11
|
+
import { isServiceWorkerRealm } from '../worker/service-worker-channel.js';
|
|
12
|
+
import {
|
|
13
|
+
PYRIC_WORKER_NAME,
|
|
14
|
+
PYRIC_WORKER_URL,
|
|
15
|
+
} from '../runtime/manifest.js';
|
|
16
|
+
import { getPyricRuntimeStatus } from '../runtime/status.js';
|
|
17
|
+
import { connectRuntimeWorker } from '../runtime/worker-connection.js';
|
|
18
|
+
import { createWorkerReplacement } from '../runtime/worker-replacement.js';
|
|
19
|
+
import {
|
|
20
|
+
preflightWorkerEpochStorage,
|
|
21
|
+
rememberWorkerEpoch,
|
|
22
|
+
workerNameForEpoch,
|
|
23
|
+
} from '../runtime/worker-generation.js';
|
|
24
|
+
import {
|
|
25
|
+
onWorkerRuntimeReload,
|
|
26
|
+
retireWorkerRuntime,
|
|
27
|
+
} from '../worker/client/runtime-control.js';
|
|
28
|
+
|
|
29
|
+
const hasSharedWorker = typeof SharedWorker !== 'undefined';
|
|
30
|
+
const runtimeStatus = getPyricRuntimeStatus();
|
|
31
|
+
|
|
32
|
+
const workerRequested =
|
|
33
|
+
(hasSharedWorker || (isServiceWorkerRealm() && typeof BroadcastChannel !== 'undefined'))
|
|
34
|
+
&& !(globalThis as { __PYRIC_FORCE_INPAGE__?: boolean }).__PYRIC_FORCE_INPAGE__;
|
|
35
|
+
|
|
36
|
+
export const WORKER_URL = PYRIC_WORKER_URL;
|
|
37
|
+
let epochStorage: Storage | undefined;
|
|
38
|
+
try {
|
|
39
|
+
epochStorage = typeof localStorage === 'undefined' ? undefined : localStorage;
|
|
40
|
+
} catch {
|
|
41
|
+
epochStorage = undefined;
|
|
42
|
+
}
|
|
43
|
+
export const WORKER_NAME = workerNameForEpoch(
|
|
44
|
+
runtimeStatus.getSnapshot().servedEpoch,
|
|
45
|
+
epochStorage,
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
/** Control traffic only; Firebase apps receive independent app-owned ports. */
|
|
49
|
+
export const workerDb: ClientDb | null = workerRequested
|
|
50
|
+
? hasSharedWorker
|
|
51
|
+
? connectRuntimeWorker(
|
|
52
|
+
() => getFirestore(WORKER_URL, WORKER_NAME, {
|
|
53
|
+
onError: (error) => runtimeStatus.reportError(error, 'worker'),
|
|
54
|
+
}),
|
|
55
|
+
(error) => runtimeStatus.reportError(error, 'worker'),
|
|
56
|
+
)
|
|
57
|
+
: null
|
|
58
|
+
: null;
|
|
59
|
+
|
|
60
|
+
export const useWorker = workerRequested && (!hasSharedWorker || workerDb !== null);
|
|
61
|
+
|
|
62
|
+
export function openWorkerDb(appName: string): ClientDb {
|
|
63
|
+
if (hasSharedWorker) return getFirestore(WORKER_URL, WORKER_NAME);
|
|
64
|
+
if (isServiceWorkerRealm()) return getServiceWorkerFirestore(appName);
|
|
65
|
+
throw new Error('No Pyric worker transport is available in this browser context.');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export const presenceSession = useWorker && workerDb
|
|
69
|
+
? startPresence({ db: workerDb, kind: 'app' })
|
|
70
|
+
: null;
|
|
71
|
+
|
|
72
|
+
runtimeStatus.setWorker({
|
|
73
|
+
mode: useWorker ? 'shared-worker' : 'in-page',
|
|
74
|
+
runningEpoch: null,
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
const workerReplacement = useWorker && workerDb && typeof window !== 'undefined'
|
|
78
|
+
? createWorkerReplacement({
|
|
79
|
+
targetEpoch: runtimeStatus.getSnapshot().servedEpoch!,
|
|
80
|
+
retire: () => retireWorkerRuntime(
|
|
81
|
+
workerDb,
|
|
82
|
+
runtimeStatus.getSnapshot().servedEpoch!,
|
|
83
|
+
),
|
|
84
|
+
subscribeReload: onWorkerRuntimeReload,
|
|
85
|
+
preflight: () => preflightWorkerEpochStorage(epochStorage),
|
|
86
|
+
commitGeneration: (epoch) => rememberWorkerEpoch(epoch, epochStorage),
|
|
87
|
+
onPreparationError: (error) => runtimeStatus.reportError(error, 'worker'),
|
|
88
|
+
reload: () => window.location.reload(),
|
|
89
|
+
})
|
|
90
|
+
: null;
|
|
91
|
+
runtimeStatus.setWorkerUpdater(
|
|
92
|
+
workerReplacement ? () => workerReplacement.request() : null,
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
if (useWorker && workerDb) {
|
|
96
|
+
subscribeEvents(workerDb, (events) => runtimeStatus.recordSandboxEvents(events));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (useWorker && typeof document !== 'undefined') {
|
|
100
|
+
const servedVersion = runtimeStatus.getSnapshot().servedEpoch;
|
|
101
|
+
void getWorkerVersion(workerDb!)
|
|
102
|
+
.then(async (runningVersion) => {
|
|
103
|
+
runtimeStatus.setWorker({ mode: 'shared-worker', runningEpoch: runningVersion });
|
|
104
|
+
if (
|
|
105
|
+
!servedVersion
|
|
106
|
+
|| !runningVersion
|
|
107
|
+
|| runningVersion === 'dev'
|
|
108
|
+
|| servedVersion === runningVersion
|
|
109
|
+
) return;
|
|
110
|
+
const otherPages = await new Promise<number>((resolve) => {
|
|
111
|
+
const unsubscribe = subscribePresence(workerDb!, (snapshot) => {
|
|
112
|
+
unsubscribe();
|
|
113
|
+
resolve(snapshot.clients.filter(
|
|
114
|
+
(client) => client.clientId !== presenceSession?.clientId,
|
|
115
|
+
).length);
|
|
116
|
+
});
|
|
117
|
+
setTimeout(() => {
|
|
118
|
+
unsubscribe();
|
|
119
|
+
resolve(-1);
|
|
120
|
+
}, 2_000);
|
|
121
|
+
});
|
|
122
|
+
const othersHint = otherPages > 0
|
|
123
|
+
? ` ${otherPages} other page${otherPages === 1 ? '' : 's'} must disconnect before the worker can restart.`
|
|
124
|
+
: otherPages === 0
|
|
125
|
+
? ' This is the only connected page — reload it to pick up the new worker.'
|
|
126
|
+
: '';
|
|
127
|
+
console.warn(
|
|
128
|
+
`[pyric dev] the SharedWorker is running OLDER code (build ${runningVersion}) than what is now `
|
|
129
|
+
+ `served (build ${servedVersion}). A SharedWorker can't hot-update — CLOSE ALL TABS of this origin `
|
|
130
|
+
+ 'and reopen to load the new worker. (All tabs share one worker, so a partial reload leaves '
|
|
131
|
+
+ `the old code running for everyone.)${othersHint}`,
|
|
132
|
+
);
|
|
133
|
+
})
|
|
134
|
+
.catch((error) => runtimeStatus.reportError(error, 'worker'));
|
|
135
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { AiEngineConfigWire } from './worker/protocol.js';
|
|
2
|
+
|
|
3
|
+
/** Browser-safe wire contract served by `/__pyric/init.json`. */
|
|
4
|
+
export interface InitPayload {
|
|
5
|
+
/** Per-server capability for the warning-only activity report endpoint. */
|
|
6
|
+
activityToken?: string;
|
|
7
|
+
rules: string | null;
|
|
8
|
+
rulesHash: string | null;
|
|
9
|
+
databaseRules?: { rules: Record<string, unknown> } | null;
|
|
10
|
+
databaseRulesHash?: string | null;
|
|
11
|
+
databaseUrl?: string | null;
|
|
12
|
+
/** Storage rules are installed once, before the first Storage operation. */
|
|
13
|
+
storageRules: string | null;
|
|
14
|
+
storageRulesHash: string | null;
|
|
15
|
+
/**
|
|
16
|
+
* Local project identity — the served project directory. Scopes the
|
|
17
|
+
* storage IndexedDB database name (`pyric-storage:<projectKey>`, issue
|
|
18
|
+
* #359): IndexedDB is origin-scoped, so without this every project served
|
|
19
|
+
* on one localhost port shared one storage database. Local-only (a dev
|
|
20
|
+
* server path never leaves the machine). Absent/null on older servers —
|
|
21
|
+
* consumers fall back to the legacy shared name.
|
|
22
|
+
*/
|
|
23
|
+
projectKey?: string | null;
|
|
24
|
+
bridgeUrl: string | null;
|
|
25
|
+
seed: Record<string, Record<string, unknown>> | null;
|
|
26
|
+
persist?: boolean;
|
|
27
|
+
seedState?: unknown | null;
|
|
28
|
+
authUsers?: ReadonlyArray<Record<string, unknown>> | null;
|
|
29
|
+
capture?: boolean;
|
|
30
|
+
messaging?: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Plugin-level AI config (`@pyric/cli/vite`'s `ai.engine`). Only the ENGINE
|
|
33
|
+
* travels here — the OpenAI proxy upstream is a server-side namespace option
|
|
34
|
+
* that never reaches the page. The worker host reads `ai.engine` into
|
|
35
|
+
* `ctx.aiEngine`, which wins over any op-carried `engine` (see host-ai.ts).
|
|
36
|
+
* Absent under `pyric dev` (no CLI surface) and whenever the plugin sets no
|
|
37
|
+
* engine. The in-page fallback path receives the same engine synchronously
|
|
38
|
+
* via the injected `globalThis.__PYRIC_AI_ENGINE__` (init.json can't be read
|
|
39
|
+
* synchronously by the served `getAI`).
|
|
40
|
+
*/
|
|
41
|
+
ai?: { engine?: AiEngineConfigWire } | null;
|
|
42
|
+
}
|