@pyric/cli 0.1.0-alpha.10 → 0.1.0-alpha.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md.orig +2 -2
- 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/server/peer.d.ts +1 -1
- package/dist/bridge/server.d.ts +1 -1
- package/dist/bridge/server.js +1 -1
- package/dist/cli/index.d.ts +11 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +13 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init.d.ts +3 -3
- package/dist/cli/init.d.ts.map +1 -1
- package/dist/cli/init.js +5 -5
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/serve.d.ts.map +1 -1
- package/dist/cli/serve.js +8 -2
- 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/conformance/.generated/conformance-docs.d.ts +12 -0
- package/dist/conformance/.generated/conformance-docs.d.ts.map +1 -0
- package/dist/conformance/.generated/conformance-docs.js +2 -0
- package/dist/conformance/.generated/conformance-docs.js.map +1 -0
- package/dist/conformance/docs.d.ts +6 -0
- package/dist/conformance/docs.d.ts.map +1 -0
- package/dist/conformance/docs.js +6 -0
- package/dist/conformance/docs.js.map +1 -0
- package/dist/remote/index.d.ts.map +1 -1
- package/dist/remote/index.js +1 -0
- package/dist/remote/index.js.map +1 -1
- package/dist/serve/activity-guard.d.ts +4 -0
- package/dist/serve/activity-guard.d.ts.map +1 -0
- package/dist/serve/activity-guard.js +22 -0
- package/dist/serve/activity-guard.js.map +1 -0
- package/dist/serve/activity-route.d.ts +4 -0
- package/dist/serve/activity-route.d.ts.map +1 -0
- package/dist/serve/activity-route.js +157 -0
- package/dist/serve/activity-route.js.map +1 -0
- package/dist/serve/activity-warning.d.ts +4 -0
- package/dist/serve/activity-warning.d.ts.map +1 -0
- package/dist/serve/activity-warning.js +21 -0
- package/dist/serve/activity-warning.js.map +1 -0
- package/dist/serve/bridge-mount.d.ts +5 -0
- package/dist/serve/bridge-mount.d.ts.map +1 -1
- package/dist/serve/bridge-mount.js +1 -0
- package/dist/serve/bridge-mount.js.map +1 -1
- package/dist/serve/docs-ui/404.html +3 -0
- package/dist/serve/docs-ui/_astro/{_slug_.DPUwShm2.css → _slug_.CXh9yTP1.css} +1 -1
- package/dist/serve/docs-ui/_astro/_slug_.DUci-duz.css +1 -0
- package/dist/serve/docs-ui/docs/_rhythm/index.html +2 -1
- package/dist/serve/docs-ui/docs/agent/set-up-your-agent/index.html +3 -2
- package/dist/serve/docs-ui/docs/agent/set-up-your-agent.md +1 -1
- package/dist/serve/docs-ui/docs/agent/watch-and-review/index.html +2 -1
- package/dist/serve/docs-ui/docs/agent/work-with-an-agent/index.html +2 -1
- package/dist/serve/docs-ui/docs/ai-compat/index.html +2 -1
- package/dist/serve/docs-ui/docs/api-reference/index.html +51 -49
- package/dist/serve/docs-ui/docs/api-reference.md +3 -3
- package/dist/serve/docs-ui/docs/app-compat/index.html +2 -1
- package/dist/serve/docs-ui/docs/auth-compat/index.html +2 -1
- package/dist/serve/docs-ui/docs/build/ai-logic/index.html +18 -3
- package/dist/serve/docs-ui/docs/build/ai-logic.md +30 -2
- package/dist/serve/docs-ui/docs/build/authentication/index.html +2 -1
- package/dist/serve/docs-ui/docs/build/authentication.md +1 -1
- package/dist/serve/docs-ui/docs/build/cloud-firestore/index.html +2 -1
- package/dist/serve/docs-ui/docs/build/cloud-messaging/index.html +46 -2
- package/dist/serve/docs-ui/docs/build/cloud-messaging.md +56 -1
- package/dist/serve/docs-ui/docs/build/cloud-storage/index.html +2 -1
- package/dist/serve/docs-ui/docs/build/cloud-storage.md +1 -1
- package/dist/serve/docs-ui/docs/build/realtime-database/index.html +21 -3
- package/dist/serve/docs-ui/docs/build/realtime-database.md +25 -1
- package/dist/serve/docs-ui/docs/conformance-scores/index.html +2 -1
- package/dist/serve/docs-ui/docs/create-pyric-reference-api/index.html +61 -28
- package/dist/serve/docs-ui/docs/create-pyric-reference-api.md +65 -20
- package/dist/serve/docs-ui/docs/database-compat/index.html +2 -1
- package/dist/serve/docs-ui/docs/firestore-compat/index.html +6 -5
- package/dist/serve/docs-ui/docs/firestore-compat.md +4 -4
- package/dist/serve/docs-ui/docs/functions-rtdb-compat/index.html +2 -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/start-building/index.html +4 -3
- package/dist/serve/docs-ui/docs/get-started/start-building.md +2 -2
- package/dist/serve/docs-ui/docs/index.json +979 -579
- package/dist/serve/docs-ui/docs/messaging-compat/index.html +2 -1
- package/dist/serve/docs-ui/docs/observe/see-whats-happening/index.html +2 -1
- package/dist/serve/docs-ui/docs/observe/shape-your-data/index.html +2 -1
- package/dist/serve/docs-ui/docs/overview/index.html +2 -1
- package/dist/serve/docs-ui/docs/pyric-admin-app-reference-api/index.html +5 -4
- package/dist/serve/docs-ui/docs/pyric-admin-app-reference-api.md +3 -4
- package/dist/serve/docs-ui/docs/pyric-admin-auth-reference-api/index.html +5 -4
- package/dist/serve/docs-ui/docs/pyric-admin-auth-reference-api.md +3 -4
- package/dist/serve/docs-ui/docs/pyric-admin-database-reference-api/index.html +5 -4
- package/dist/serve/docs-ui/docs/pyric-admin-database-reference-api.md +3 -4
- package/dist/serve/docs-ui/docs/pyric-admin-firestore-reference-api/index.html +2503 -12
- package/dist/serve/docs-ui/docs/pyric-admin-firestore-reference-api.md +2219 -8
- 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 +4 -3
- package/dist/serve/docs-ui/docs/pyric-admin-storage-reference-api.md +2 -3
- package/dist/serve/docs-ui/docs/pyric-ai-reference-api/index.html +16 -11
- package/dist/serve/docs-ui/docs/pyric-ai-reference-api.md +14 -13
- package/dist/serve/docs-ui/docs/pyric-ai-scripting-reference-api/index.html +4 -3
- package/dist/serve/docs-ui/docs/pyric-ai-scripting-reference-api.md +2 -3
- package/dist/serve/docs-ui/docs/pyric-app-reference-api/index.html +7 -2
- package/dist/serve/docs-ui/docs/pyric-app-reference-api.md +5 -2
- package/dist/serve/docs-ui/docs/pyric-auth-reference-api/index.html +23 -12
- package/dist/serve/docs-ui/docs/pyric-auth-reference-api.md +15 -12
- package/dist/serve/docs-ui/docs/pyric-cli-assurance-browser-reference-api/index.html +10 -48
- package/dist/serve/docs-ui/docs/pyric-cli-assurance-browser-reference-api.md +11 -24
- package/dist/serve/docs-ui/docs/pyric-cli-assurance-reference-api/index.html +87 -97
- package/dist/serve/docs-ui/docs/pyric-cli-assurance-reference-api.md +53 -65
- package/dist/serve/docs-ui/docs/pyric-cli-bridge-client-reference-api/index.html +14 -15
- package/dist/serve/docs-ui/docs/pyric-cli-bridge-client-reference-api.md +19 -28
- package/dist/serve/docs-ui/docs/pyric-cli-bridge-reference-api/index.html +4 -3
- package/dist/serve/docs-ui/docs/pyric-cli-bridge-reference-api.md +2 -3
- package/dist/serve/docs-ui/docs/pyric-cli-conformance-browser-reference-api/index.html +3 -2
- package/dist/serve/docs-ui/docs/pyric-cli-conformance-browser-reference-api.md +1 -2
- package/dist/serve/docs-ui/docs/pyric-cli-conformance-docs-reference-api/index.html +51 -0
- package/dist/serve/docs-ui/docs/pyric-cli-conformance-docs-reference-api.md +36 -0
- package/dist/serve/docs-ui/docs/pyric-cli-conformance-reference-api/index.html +3 -2
- package/dist/serve/docs-ui/docs/pyric-cli-conformance-reference-api.md +1 -2
- package/dist/serve/docs-ui/docs/pyric-cli-credentials-node-reference-api/index.html +3 -2
- package/dist/serve/docs-ui/docs/pyric-cli-credentials-node-reference-api.md +1 -2
- package/dist/serve/docs-ui/docs/pyric-cli-discover-reference-api/index.html +3 -2
- package/dist/serve/docs-ui/docs/pyric-cli-discover-reference-api.md +1 -2
- package/dist/serve/docs-ui/docs/pyric-cli-register-reference-api/index.html +3 -2
- package/dist/serve/docs-ui/docs/pyric-cli-register-reference-api.md +1 -2
- package/dist/serve/docs-ui/docs/pyric-cli-remote-reference-api/index.html +1067 -83
- package/dist/serve/docs-ui/docs/pyric-cli-remote-reference-api.md +1291 -160
- package/dist/serve/docs-ui/docs/pyric-cli-serve-worker-reference-api/index.html +253 -157
- package/dist/serve/docs-ui/docs/pyric-cli-serve-worker-reference-api.md +179 -85
- package/dist/serve/docs-ui/docs/pyric-cli-verify-reference-api/index.html +72 -7
- package/dist/serve/docs-ui/docs/pyric-cli-verify-reference-api.md +22 -7
- package/dist/serve/docs-ui/docs/pyric-cli-vite-reference-api/index.html +33 -12
- package/dist/serve/docs-ui/docs/pyric-cli-vite-reference-api.md +15 -12
- package/dist/serve/docs-ui/docs/pyric-database-reference-api/index.html +18 -17
- package/dist/serve/docs-ui/docs/pyric-database-reference-api.md +19 -22
- package/dist/serve/docs-ui/docs/pyric-firestore-reference-api/index.html +1512 -545
- package/dist/serve/docs-ui/docs/pyric-firestore-reference-api.md +1311 -316
- package/dist/serve/docs-ui/docs/pyric-messaging-reference-api/index.html +5 -4
- package/dist/serve/docs-ui/docs/pyric-messaging-reference-api.md +3 -4
- package/dist/serve/docs-ui/docs/pyric-messaging-sw-reference-api/index.html +69 -369
- package/dist/serve/docs-ui/docs/pyric-messaging-sw-reference-api.md +103 -216
- package/dist/serve/docs-ui/docs/pyric-rules-reference-api/index.html +10 -9
- package/dist/serve/docs-ui/docs/pyric-rules-reference-api.md +9 -10
- package/dist/serve/docs-ui/docs/pyric-sandbox-database-reference-api/index.html +8 -7
- package/dist/serve/docs-ui/docs/pyric-sandbox-database-reference-api.md +6 -7
- package/dist/serve/docs-ui/docs/pyric-sandbox-firestore-reference-api/index.html +10 -9
- package/dist/serve/docs-ui/docs/pyric-sandbox-firestore-reference-api.md +9 -10
- package/dist/serve/docs-ui/docs/pyric-sandbox-reference-api/index.html +371 -884
- package/dist/serve/docs-ui/docs/pyric-sandbox-reference-api.md +369 -883
- package/dist/serve/docs-ui/docs/pyric-storage-reference-api/index.html +25 -17
- package/dist/serve/docs-ui/docs/pyric-storage-reference-api.md +21 -19
- package/dist/serve/docs-ui/docs/pyric-ui-agents-reference-api/index.html +3 -2
- package/dist/serve/docs-ui/docs/pyric-ui-agents-reference-api.md +1 -2
- package/dist/serve/docs-ui/docs/pyric-ui-auth-hooks-reference-api/index.html +63 -776
- package/dist/serve/docs-ui/docs/pyric-ui-auth-hooks-reference-api.md +69 -510
- package/dist/serve/docs-ui/docs/pyric-ui-auth-reference-api/index.html +22 -21
- package/dist/serve/docs-ui/docs/pyric-ui-auth-reference-api.md +29 -30
- package/dist/serve/docs-ui/docs/pyric-ui-events-hooks-reference-api/index.html +15 -168
- package/dist/serve/docs-ui/docs/pyric-ui-events-hooks-reference-api.md +17 -87
- package/dist/serve/docs-ui/docs/pyric-ui-events-reference-api/index.html +6 -5
- package/dist/serve/docs-ui/docs/pyric-ui-events-reference-api.md +5 -6
- package/dist/serve/docs-ui/docs/pyric-ui-firestore-hooks-reference-api/index.html +82 -970
- package/dist/serve/docs-ui/docs/pyric-ui-firestore-hooks-reference-api.md +104 -479
- package/dist/serve/docs-ui/docs/pyric-ui-firestore-reference-api/index.html +30 -29
- package/dist/serve/docs-ui/docs/pyric-ui-firestore-reference-api.md +54 -55
- package/dist/serve/docs-ui/docs/pyric-ui-primitives-reference-api/index.html +3 -2
- package/dist/serve/docs-ui/docs/pyric-ui-primitives-reference-api.md +1 -2
- package/dist/serve/docs-ui/docs/pyric-ui-rtdb-reference-api/index.html +5 -4
- package/dist/serve/docs-ui/docs/pyric-ui-rtdb-reference-api.md +3 -4
- package/dist/serve/docs-ui/docs/pyric-ui-rules-hooks-reference-api/index.html +9 -124
- package/dist/serve/docs-ui/docs/pyric-ui-rules-hooks-reference-api.md +7 -58
- package/dist/serve/docs-ui/docs/pyric-ui-rules-reference-api/index.html +24 -23
- package/dist/serve/docs-ui/docs/pyric-ui-rules-reference-api.md +31 -34
- package/dist/serve/docs-ui/docs/pyric-ui-storage-hooks-reference-api/index.html +104 -1385
- package/dist/serve/docs-ui/docs/pyric-ui-storage-hooks-reference-api.md +133 -817
- package/dist/serve/docs-ui/docs/pyric-ui-storage-reference-api/index.html +34 -33
- package/dist/serve/docs-ui/docs/pyric-ui-storage-reference-api.md +37 -38
- package/dist/serve/docs-ui/docs/pyric-ui-traffic-hooks-reference-api/index.html +116 -1169
- package/dist/serve/docs-ui/docs/pyric-ui-traffic-hooks-reference-api.md +142 -590
- package/dist/serve/docs-ui/docs/pyric-ui-traffic-reference-api/index.html +4 -3
- package/dist/serve/docs-ui/docs/pyric-ui-traffic-reference-api.md +3 -4
- package/dist/serve/docs-ui/docs/rules-compat/index.html +34 -1
- package/dist/serve/docs-ui/docs/rules-compat.md +32 -0
- package/dist/serve/docs-ui/docs/secure/audit-your-rules/index.html +19 -2
- package/dist/serve/docs-ui/docs/secure/audit-your-rules.md +32 -0
- package/dist/serve/docs-ui/docs/secure/firestore-rules-limits/index.html +2 -1
- package/dist/serve/docs-ui/docs/secure/read-a-denial/index.html +2 -1
- package/dist/serve/docs-ui/docs/secure/rtdb-rules-in-typescript/index.html +2 -1
- package/dist/serve/docs-ui/docs/secure/rtdb-rules-in-typescript.md +1 -1
- package/dist/serve/docs-ui/docs/secure/rules-standard-library/index.html +2 -1
- package/dist/serve/docs-ui/docs/secure/secure-it-with-rules/index.html +2 -1
- package/dist/serve/docs-ui/docs/secure/simulate-and-lint/index.html +2 -1
- package/dist/serve/docs-ui/docs/secure/write-a-rules-test-suite/index.html +2 -1
- package/dist/serve/docs-ui/docs/ship/ship-to-production/index.html +2 -1
- package/dist/serve/docs-ui/docs/ship/test-in-node/index.html +2 -1
- package/dist/serve/docs-ui/docs/storage-compat/index.html +2 -1
- package/dist/serve/docs-ui/docs/trust/how-we-know-it-matches-firebase/index.html +2 -1
- package/dist/serve/docs-ui/docs/trust/versioning-and-compatibility/index.html +2 -1
- package/dist/serve/docs-ui/llms.txt +2 -1
- package/dist/serve/entries/ai.d.ts.map +1 -1
- package/dist/serve/entries/ai.js +39 -1
- package/dist/serve/entries/ai.js.map +1 -1
- package/dist/serve/entries/messaging-sw.d.ts.map +1 -1
- package/dist/serve/entries/messaging-sw.js +5 -1
- package/dist/serve/entries/messaging-sw.js.map +1 -1
- package/dist/serve/entries/messaging.d.ts.map +1 -1
- package/dist/serve/entries/messaging.js +7 -1
- package/dist/serve/entries/messaging.js.map +1 -1
- package/dist/serve/entries/runtime.d.ts.map +1 -1
- package/dist/serve/entries/runtime.js +20 -1
- package/dist/serve/entries/runtime.js.map +1 -1
- package/dist/serve/init-payload.d.ts +25 -0
- package/dist/serve/init-payload.d.ts.map +1 -1
- package/dist/serve/namespace.d.ts +39 -0
- package/dist/serve/namespace.d.ts.map +1 -1
- package/dist/serve/namespace.js +80 -1
- package/dist/serve/namespace.js.map +1 -1
- package/dist/serve/standalone-assets.d.ts +11 -9
- package/dist/serve/standalone-assets.d.ts.map +1 -1
- package/dist/serve/standalone-assets.js +6 -5
- package/dist/serve/standalone-assets.js.map +1 -1
- package/dist/serve/studio-ui/assets/{RulesCodeEditor-CXdSLKhO.js → RulesCodeEditor-CxVYcZXw.js} +1 -1
- package/dist/serve/studio-ui/assets/index-cpSfgQ7H.js +1218 -0
- package/dist/serve/studio-ui/assets/seed-DeuFKi4J.js +20 -0
- package/dist/serve/studio-ui/index.html +1 -1
- package/dist/serve/vite-plugin.d.ts +92 -15
- package/dist/serve/vite-plugin.d.ts.map +1 -1
- package/dist/serve/vite-plugin.js +281 -11
- package/dist/serve/vite-plugin.js.map +1 -1
- package/dist/serve/worker/activity-bootstrap.d.ts +8 -0
- package/dist/serve/worker/activity-bootstrap.d.ts.map +1 -0
- package/dist/serve/worker/activity-bootstrap.js +11 -0
- package/dist/serve/worker/activity-bootstrap.js.map +1 -0
- package/dist/serve/worker/client/connection.d.ts.map +1 -1
- package/dist/serve/worker/client/connection.js +17 -5
- package/dist/serve/worker/client/connection.js.map +1 -1
- package/dist/serve/worker/client/core.d.ts +2 -2
- package/dist/serve/worker/client/core.d.ts.map +1 -1
- package/dist/serve/worker/client/core.js +33 -5
- package/dist/serve/worker/client/core.js.map +1 -1
- package/dist/serve/worker/client/firestore-writes.d.ts.map +1 -1
- package/dist/serve/worker/client/firestore-writes.js +1 -0
- package/dist/serve/worker/client/firestore-writes.js.map +1 -1
- package/dist/serve/worker/client/messaging.d.ts +9 -1
- package/dist/serve/worker/client/messaging.d.ts.map +1 -1
- package/dist/serve/worker/client/messaging.js +15 -0
- package/dist/serve/worker/client/messaging.js.map +1 -1
- package/dist/serve/worker/client/studio.d.ts +10 -0
- package/dist/serve/worker/client/studio.d.ts.map +1 -1
- package/dist/serve/worker/client/studio.js +11 -0
- package/dist/serve/worker/client/studio.js.map +1 -1
- package/dist/serve/worker/durable-persistence.d.ts +12 -0
- package/dist/serve/worker/durable-persistence.d.ts.map +1 -0
- package/dist/serve/worker/durable-persistence.js +118 -0
- package/dist/serve/worker/durable-persistence.js.map +1 -0
- package/dist/serve/worker/entry.d.ts +7 -3
- package/dist/serve/worker/entry.d.ts.map +1 -1
- package/dist/serve/worker/entry.js +7 -3
- package/dist/serve/worker/entry.js.map +1 -1
- package/dist/serve/worker/host/core.d.ts +9 -5
- package/dist/serve/worker/host/core.d.ts.map +1 -1
- package/dist/serve/worker/host/core.js +34 -9
- package/dist/serve/worker/host/core.js.map +1 -1
- package/dist/serve/worker/host/dispatch.d.ts.map +1 -1
- package/dist/serve/worker/host/dispatch.js +9 -2
- package/dist/serve/worker/host/dispatch.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.d.ts +1 -1
- package/dist/serve/worker/host/studio.d.ts.map +1 -1
- package/dist/serve/worker/host/studio.js +36 -1
- package/dist/serve/worker/host/studio.js.map +1 -1
- package/dist/serve/worker/host/subscriptions.d.ts.map +1 -1
- package/dist/serve/worker/host/subscriptions.js +27 -7
- package/dist/serve/worker/host/subscriptions.js.map +1 -1
- package/dist/serve/worker/host-context.d.ts +15 -0
- package/dist/serve/worker/host-context.d.ts.map +1 -1
- package/dist/serve/worker/host-context.js +10 -0
- package/dist/serve/worker/host-context.js.map +1 -1
- package/dist/serve/worker/host-messaging.d.ts.map +1 -1
- package/dist/serve/worker/host-messaging.js +9 -2
- package/dist/serve/worker/host-messaging.js.map +1 -1
- package/dist/serve/worker/index.d.ts +3 -2
- package/dist/serve/worker/index.d.ts.map +1 -1
- package/dist/serve/worker/index.js +4 -1
- package/dist/serve/worker/index.js.map +1 -1
- package/dist/serve/worker/protocol.d.ts +25 -3
- 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/serve-init.d.ts +23 -37
- package/dist/serve/worker/serve-init.d.ts.map +1 -1
- package/dist/serve/worker/serve-init.js +95 -172
- package/dist/serve/worker/serve-init.js.map +1 -1
- package/dist/vite.d.ts +4 -4
- package/dist/vite.d.ts.map +1 -1
- package/dist/vite.js +3 -3
- package/dist/vite.js.map +1 -1
- package/package.json +9 -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 +493 -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 +46 -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 +540 -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 +75 -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/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-plugin.ts +1102 -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 +202 -0
- package/src/serve/worker/client/core.ts +350 -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/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 +179 -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 +1112 -0
- package/src/serve/worker/serve-init.ts +606 -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 +17 -0
- package/dist/serve/docs-ui/docs/pyric-firestore-values-reference-api/index.html +0 -30
- package/dist/serve/docs-ui/docs/pyric-firestore-values-reference-api.md +0 -30
- package/dist/serve/studio-ui/assets/index-BvtQIpGn.js +0 -1211
- package/dist/serve/studio-ui/assets/seed-3DMedLL3.js +0 -2
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `--bridge` for `pyric dev` — mounts the MCP bridge on the SAME origin
|
|
3
|
+
* the app is served from, so one URL carries the app, the sandbox SDK, AND
|
|
4
|
+
* the agent endpoint (the retrofit story end-to-end: `pyric dev --bridge`,
|
|
5
|
+
* point an MCP client at `http://localhost:3473/__pyric/mcp`, and the agent
|
|
6
|
+
* drives the sandbox living in the served page).
|
|
7
|
+
*
|
|
8
|
+
* REUSES the bridge internals (`createBridge`, `buildMcpServer`,
|
|
9
|
+
* `attachPeer`, `collectBody`) directly — the shared transport helpers live in
|
|
10
|
+
* `../bridge/server/peer.ts`; this module composes the bridge into the serve /
|
|
11
|
+
* Vite-plugin origin, it is not a fork of the bridge.
|
|
12
|
+
*
|
|
13
|
+
* Routes (composed into the `/__pyric/` namespace handler):
|
|
14
|
+
* POST /__pyric/mcp MCP over streamable HTTP
|
|
15
|
+
* GET /__pyric/health bridge health JSON
|
|
16
|
+
* WS /__pyric/sandbox the in-page sandbox peer (server `upgrade`)
|
|
17
|
+
*/
|
|
18
|
+
import { randomUUID } from 'node:crypto';
|
|
19
|
+
import type { IncomingMessage, Server, ServerResponse } from 'node:http';
|
|
20
|
+
import { WebSocketServer, type WebSocket } from 'ws';
|
|
21
|
+
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
22
|
+
import { createBridge, type BridgeToolEvent } from '../bridge/server/bridge.js';
|
|
23
|
+
import { buildMcpServer } from '../bridge/server/mcp.js';
|
|
24
|
+
import { getDefaultMcpToolSurface } from '../bridge/server/mcp-contract.js';
|
|
25
|
+
import { createAuditWriter } from '../bridge/server/audit.js';
|
|
26
|
+
import { attachPeer } from '../bridge/server/peer.js';
|
|
27
|
+
import { pyricVersion } from './standalone-assets.js';
|
|
28
|
+
import { isAllowedUpgrade } from './server.js';
|
|
29
|
+
|
|
30
|
+
const WS_PATH = '/__pyric/sandbox';
|
|
31
|
+
const MCP_PATH = '/__pyric/mcp';
|
|
32
|
+
const HEALTH_PATH = '/__pyric/health';
|
|
33
|
+
const BRIDGE_VERSION = pyricVersion();
|
|
34
|
+
|
|
35
|
+
export interface BridgeMountOptions {
|
|
36
|
+
project?: string;
|
|
37
|
+
disableAuditLog?: boolean;
|
|
38
|
+
/** WS-upgrade rebinding/origin guard config. The upgrade path bypasses the
|
|
39
|
+
* static server's request-time `isAllowedHost`, so the mount guards it here
|
|
40
|
+
* with the SAME allow rule (bound host + loopback + `--allowed-host`).
|
|
41
|
+
* `allowedHosts: true` = the caller (vite `server.allowedHosts: true`) opted
|
|
42
|
+
* into all hosts. Omit ⇒ loopback-only. */
|
|
43
|
+
upgradeGuard?: { boundHost: string; allowedHosts?: string[] | true };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface BridgeMount {
|
|
47
|
+
/** Stable per-process identity (mirrors `/__pyric/health`'s instanceId).
|
|
48
|
+
* The pointer writer records this so the proxy can verify it reached this
|
|
49
|
+
* exact server across a cross-family port collision. */
|
|
50
|
+
readonly instanceId: string;
|
|
51
|
+
/** Namespace-handler tier: returns true when the request was handled. */
|
|
52
|
+
handler(req: IncomingMessage, res: ServerResponse, url: URL): Promise<boolean>;
|
|
53
|
+
/** Attach the WS upgrade listener to the serve server. */
|
|
54
|
+
attachUpgrade(server: Server): void;
|
|
55
|
+
/** Whether a sandbox peer (a browser tab / SharedWorker relay) is currently
|
|
56
|
+
* connected. In-process callers (the Vite plugin's Functions start) poll
|
|
57
|
+
* this instead of self-fetching `/__pyric/health`, which avoids a loopback
|
|
58
|
+
* fetch on the same event loop. Mirrors `health().sandboxConnected`. */
|
|
59
|
+
sandboxConnected(): boolean;
|
|
60
|
+
/** The browser-side WS URL for the init payload (`bridgeUrl`). */
|
|
61
|
+
wsUrl(origin: { host: string; port: number }): string;
|
|
62
|
+
/** The MCP endpoint for the banner. */
|
|
63
|
+
mcpUrl(origin: { host: string; port: number }): string;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function createBridgeMount(opts: BridgeMountOptions = {}): BridgeMount {
|
|
67
|
+
const project = opts.project ?? 'sandbox';
|
|
68
|
+
const auditWriter = opts.disableAuditLog ? null : createAuditWriter(project);
|
|
69
|
+
|
|
70
|
+
const bridge = createBridge({
|
|
71
|
+
project,
|
|
72
|
+
version: BRIDGE_VERSION,
|
|
73
|
+
onToolEvent: auditWriter ? (event: BridgeToolEvent) => auditWriter.write(event) : undefined,
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// STATEFUL MCP: a per-session transport+server map, mirroring the standalone
|
|
77
|
+
// bridge (`bridge/server/standalone.ts`). Each Streamable-HTTP client gets an
|
|
78
|
+
// `Mcp-Session-Id` on initialize and resumes the SAME session on every later
|
|
79
|
+
// request. The old per-request, `sessionIdGenerator: undefined` build returned
|
|
80
|
+
// no session id, so clients (Claude Code, Cursor) dropped and 404'd on every
|
|
81
|
+
// reconnect/dev-server restart. The long-lived `bridge` (peer + dispatch +
|
|
82
|
+
// audit) is shared; each session owns its transport+server.
|
|
83
|
+
const SESSION_IDLE_MS = 10 * 60_000;
|
|
84
|
+
const MAX_SESSIONS = 64;
|
|
85
|
+
type Session = {
|
|
86
|
+
transport: StreamableHTTPServerTransport;
|
|
87
|
+
close: () => Promise<void>;
|
|
88
|
+
idle: ReturnType<typeof setTimeout> | null;
|
|
89
|
+
sessionId: string | null;
|
|
90
|
+
};
|
|
91
|
+
const sessions = new Map<string, Session>();
|
|
92
|
+
|
|
93
|
+
const bumpIdle = (s: Session): void => {
|
|
94
|
+
if (s.idle) clearTimeout(s.idle);
|
|
95
|
+
s.idle = setTimeout(() => {
|
|
96
|
+
if (s.sessionId) sessions.delete(s.sessionId);
|
|
97
|
+
void s.close();
|
|
98
|
+
}, SESSION_IDLE_MS);
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const newSession = async (): Promise<Session> => {
|
|
102
|
+
if (sessions.size >= MAX_SESSIONS) {
|
|
103
|
+
const e = new Error(`pyric bridge: at session cap (${MAX_SESSIONS})`);
|
|
104
|
+
(e as { statusCode?: number }).statusCode = 503;
|
|
105
|
+
throw e;
|
|
106
|
+
}
|
|
107
|
+
const session: Session = {
|
|
108
|
+
transport: null as unknown as StreamableHTTPServerTransport,
|
|
109
|
+
close: async () => {},
|
|
110
|
+
idle: null,
|
|
111
|
+
sessionId: null,
|
|
112
|
+
};
|
|
113
|
+
session.transport = new StreamableHTTPServerTransport({
|
|
114
|
+
sessionIdGenerator: () => randomUUID(),
|
|
115
|
+
onsessioninitialized: (id) => {
|
|
116
|
+
session.sessionId = id;
|
|
117
|
+
sessions.set(id, session);
|
|
118
|
+
bumpIdle(session);
|
|
119
|
+
},
|
|
120
|
+
});
|
|
121
|
+
session.transport.onclose = () => {
|
|
122
|
+
if (session.sessionId) sessions.delete(session.sessionId);
|
|
123
|
+
};
|
|
124
|
+
const surface = getDefaultMcpToolSurface();
|
|
125
|
+
const server = buildMcpServer(bridge, {
|
|
126
|
+
forwarded: surface.forwarded,
|
|
127
|
+
inProcess: surface.inProcess,
|
|
128
|
+
});
|
|
129
|
+
session.close = async () => {
|
|
130
|
+
if (session.idle) clearTimeout(session.idle);
|
|
131
|
+
await server.close().catch(() => {});
|
|
132
|
+
await session.transport.close().catch(() => {});
|
|
133
|
+
};
|
|
134
|
+
await server.connect(session.transport);
|
|
135
|
+
return session;
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
return {
|
|
139
|
+
async handler(req, res, url) {
|
|
140
|
+
if (url.pathname === HEALTH_PATH) {
|
|
141
|
+
res.writeHead(200, { 'content-type': 'application/json' }).end(JSON.stringify(bridge.health()));
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
144
|
+
if (url.pathname === MCP_PATH) {
|
|
145
|
+
try {
|
|
146
|
+
const sessionId = (req.headers['mcp-session-id'] ?? req.headers['Mcp-Session-Id']) as string | undefined;
|
|
147
|
+
let session = sessionId ? sessions.get(sessionId) : undefined;
|
|
148
|
+
if (session) bumpIdle(session);
|
|
149
|
+
else session = await newSession();
|
|
150
|
+
|
|
151
|
+
if (req.method === 'DELETE' && sessionId) {
|
|
152
|
+
await session.close();
|
|
153
|
+
sessions.delete(sessionId);
|
|
154
|
+
res.writeHead(204).end();
|
|
155
|
+
return true;
|
|
156
|
+
}
|
|
157
|
+
// The transport reads the raw request stream itself. Do NOT pre-parse
|
|
158
|
+
// the body (the old `collectBody` path); a double read hangs the
|
|
159
|
+
// initialize POST.
|
|
160
|
+
await session.transport.handleRequest(req, res);
|
|
161
|
+
} catch (err) {
|
|
162
|
+
const statusCode = (err as { statusCode?: number })?.statusCode ?? 500;
|
|
163
|
+
if (!res.headersSent) {
|
|
164
|
+
res.writeHead(statusCode, { 'content-type': 'application/json' });
|
|
165
|
+
res.end(JSON.stringify({ error: err instanceof Error ? err.message : String(err) }));
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return true;
|
|
169
|
+
}
|
|
170
|
+
return false;
|
|
171
|
+
},
|
|
172
|
+
attachUpgrade(server) {
|
|
173
|
+
const wss = new WebSocketServer({ noServer: true });
|
|
174
|
+
const guard = opts.upgradeGuard;
|
|
175
|
+
server.on('upgrade', (req, socket, head) => {
|
|
176
|
+
if ((req.url ?? '') !== WS_PATH) return;
|
|
177
|
+
// DNS-rebinding + cross-origin hijack guard. The static/dev server runs
|
|
178
|
+
// isAllowedHost on the `request` event only; `upgrade` is a separate
|
|
179
|
+
// listener that bypasses it, so re-check both Host and Origin here
|
|
180
|
+
// before registering the peer (which last-wins the tool channel).
|
|
181
|
+
// `allowedHosts: true` means the caller explicitly opted into all hosts.
|
|
182
|
+
if (guard && guard.allowedHosts !== true) {
|
|
183
|
+
const boundHost = guard.boundHost;
|
|
184
|
+
const extra = Array.isArray(guard.allowedHosts) ? guard.allowedHosts : [];
|
|
185
|
+
if (!isAllowedUpgrade(req.headers, boundHost, extra)) {
|
|
186
|
+
socket.destroy();
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
wss.handleUpgrade(req, socket as never, head, (ws: WebSocket) => {
|
|
191
|
+
attachPeer(bridge, ws);
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
},
|
|
195
|
+
sandboxConnected: () => bridge.health().sandboxConnected === true,
|
|
196
|
+
instanceId: bridge.instanceId,
|
|
197
|
+
wsUrl: ({ host, port }) => `ws://${host}:${port}${WS_PATH}`,
|
|
198
|
+
mcpUrl: ({ host, port }) => `http://${host}:${port}${MCP_PATH}`,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
@@ -0,0 +1,493 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `pyric dev` SDK bundler — builds the browser-standalone ESM bundles the
|
|
3
|
+
* import map points `firebase/*` at (`/__pyric/sdk/*.js`).
|
|
4
|
+
*
|
|
5
|
+
* esbuild (a real dependency — the `pyric` bin runs under node via npx, so
|
|
6
|
+
* Bun.build is unavailable) bundles the wrapper entries in `entries/` with
|
|
7
|
+
* SPLITTING so the shared runtime chunk holds exactly ONE sandbox per page:
|
|
8
|
+
* both the auth and firestore bundles must close over the same instance.
|
|
9
|
+
*
|
|
10
|
+
* Two bundler plugins make pyric browser-standalone:
|
|
11
|
+
* - `pyric/*` resolver — resolves `pyric/...` specifiers from THIS package's
|
|
12
|
+
* dependency context (entries may live in a temp dir that lacks pyric).
|
|
13
|
+
* - node-builtin shims (`url`/`fs`/`path`) — still load-bearing despite the
|
|
14
|
+
* Phase U resolver split: `pyric/firestore` re-exports the agent tool
|
|
15
|
+
* factories (`firestore/index.js` → `rules/tools.js`), and tools.js
|
|
16
|
+
* statically imports the NODE module resolver. The browser path never
|
|
17
|
+
* calls into disk reads, so benign shims suffice. Droppable only when
|
|
18
|
+
* pyric breaks that static chain (tracked in #553).
|
|
19
|
+
*
|
|
20
|
+
* Output is cached under `~/.pyric/serve-cache/<pyric-version>-<hash>/` so a
|
|
21
|
+
* warm `pyric dev` start skips the (~seconds) bundle step. `--no-cache`
|
|
22
|
+
* bypasses.
|
|
23
|
+
*/
|
|
24
|
+
import { createHash } from 'node:crypto';
|
|
25
|
+
import { mkdirSync, mkdtempSync, readFileSync, readdirSync, existsSync, rmSync, writeFileSync } from 'node:fs';
|
|
26
|
+
import { homedir } from 'node:os';
|
|
27
|
+
import { dirname, join, relative, sep } from 'node:path';
|
|
28
|
+
import { fileURLToPath } from 'node:url';
|
|
29
|
+
import * as esbuild from 'esbuild';
|
|
30
|
+
|
|
31
|
+
/** Modules the import map serves. Keys are the bare specifiers app code uses. */
|
|
32
|
+
export const SDK_MODULES = [
|
|
33
|
+
'firebase/ai',
|
|
34
|
+
'firebase/app',
|
|
35
|
+
'firebase/auth',
|
|
36
|
+
'firebase/firestore',
|
|
37
|
+
'firebase/database',
|
|
38
|
+
'firebase/messaging',
|
|
39
|
+
'firebase/messaging/sw',
|
|
40
|
+
'firebase/storage',
|
|
41
|
+
] as const;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* The wrapper entries shipped with @pyric/cli, located relative to this
|
|
45
|
+
* module: compiled `.js` siblings when running from dist (npx install), `.ts`
|
|
46
|
+
* sources in the workspace (tests / dev). esbuild bundles either.
|
|
47
|
+
*/
|
|
48
|
+
export function defaultSdkEntries(): Record<string, string> {
|
|
49
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
50
|
+
const pick = (name: string): string => {
|
|
51
|
+
const js = join(here, 'entries', `${name}.js`);
|
|
52
|
+
if (existsSync(js)) return js;
|
|
53
|
+
const ts = join(here, 'entries', `${name}.ts`);
|
|
54
|
+
if (existsSync(ts)) return ts;
|
|
55
|
+
throw new Error(`pyric dev: missing SDK entry '${name}' next to ${here}`);
|
|
56
|
+
};
|
|
57
|
+
return {
|
|
58
|
+
ai: pick('ai'),
|
|
59
|
+
app: pick('app'),
|
|
60
|
+
auth: pick('auth'),
|
|
61
|
+
firestore: pick('firestore'),
|
|
62
|
+
database: pick('database'),
|
|
63
|
+
messaging: pick('messaging'),
|
|
64
|
+
'messaging-sw': pick('messaging-sw'),
|
|
65
|
+
storage: pick('storage'),
|
|
66
|
+
init: pick('init'),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Resolve the built Studio app dir (the plugin's `ui` option + the CLI's
|
|
72
|
+
* `--ui`). Reached by file path so @pyric/cli never imports `@pyric/studio`:
|
|
73
|
+
* the packaged location (`dist/serve/studio-ui`, copied at build) or, in the
|
|
74
|
+
* monorepo, the sibling studio build. Null when neither exists. The standalone
|
|
75
|
+
* binary embeds these same bytes instead (see `standalone-assets.ts`).
|
|
76
|
+
*/
|
|
77
|
+
export function resolveStudioUiDir(): string | null {
|
|
78
|
+
const candidates = [
|
|
79
|
+
new URL('./studio-ui/', import.meta.url),
|
|
80
|
+
new URL('../../../studio/dist/app/', import.meta.url),
|
|
81
|
+
];
|
|
82
|
+
for (const candidate of candidates) {
|
|
83
|
+
const dir = fileURLToPath(candidate);
|
|
84
|
+
if (existsSync(dir)) return dir;
|
|
85
|
+
}
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Resolve the built docs site dir (site-docs). `pyric dev --ui` embeds it so
|
|
91
|
+
* the Studio Docs tab has local docs without the hosted site. Built with base
|
|
92
|
+
* `/__pyric/ui/` (see scripts/build.sh) so every page/asset/twin URL resolves
|
|
93
|
+
* under the CLI mount: pages at `/__pyric/ui/docs/<slug>/`, assets at
|
|
94
|
+
* `/__pyric/ui/_astro/*`, the search index at `/__pyric/ui/docs/index.json`,
|
|
95
|
+
* and the shell chrome's tab links back at `/__pyric/ui/<tab>`. The dir holds
|
|
96
|
+
* the site-docs `dist/` verbatim: `docs/`, `_astro/`, `index.html`, `llms.txt`.
|
|
97
|
+
*/
|
|
98
|
+
export function resolveDocsUiDir(): string | null {
|
|
99
|
+
const candidates = [
|
|
100
|
+
new URL('./docs-ui/', import.meta.url),
|
|
101
|
+
];
|
|
102
|
+
for (const candidate of candidates) {
|
|
103
|
+
const dir = fileURLToPath(candidate);
|
|
104
|
+
if (existsSync(dir)) return dir;
|
|
105
|
+
}
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// ─── pyric dist discovery ─────────────────────────────────────────────
|
|
110
|
+
|
|
111
|
+
/** Locate the installed pyric package root (works in the workspace and when
|
|
112
|
+
* @pyric/cli is npm-installed — pyric is a direct dependency). */
|
|
113
|
+
export function pyricPackageRoot(): string {
|
|
114
|
+
// Resolve a real exported subpath, then walk up to the package root.
|
|
115
|
+
const entry = fileURLToPath(import.meta.resolve('pyric/firestore'));
|
|
116
|
+
let dir = dirname(entry);
|
|
117
|
+
while (dir.includes(sep) && !existsSync(join(dir, 'package.json'))) {
|
|
118
|
+
dir = dirname(dir);
|
|
119
|
+
}
|
|
120
|
+
const pkg = JSON.parse(readFileSync(join(dir, 'package.json'), 'utf8')) as { name?: string };
|
|
121
|
+
if (pkg.name !== 'pyric') {
|
|
122
|
+
throw new Error(`pyric dev: expected to resolve the pyric package, found '${pkg.name}' at ${dir}`);
|
|
123
|
+
}
|
|
124
|
+
return dir;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function pyricVersion(root: string = pyricPackageRoot()): string {
|
|
128
|
+
const pkg = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8')) as { version?: string };
|
|
129
|
+
return pkg.version ?? '0.0.0';
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// ─── plugins ──────────────────────────────────────────────────────────
|
|
133
|
+
|
|
134
|
+
/** Resolve `pyric/*` specifiers from THIS package's own dependency context
|
|
135
|
+
* (`import.meta.resolve`) rather than from the entry file's directory —
|
|
136
|
+
* entries may live in a temp dir or the user's project, neither of which
|
|
137
|
+
* has pyric in scope; @pyric/cli always does (direct dependency). */
|
|
138
|
+
function pyricResolvePlugin(): esbuild.Plugin {
|
|
139
|
+
return {
|
|
140
|
+
name: 'pyric-serve-resolve-pyric',
|
|
141
|
+
setup(build) {
|
|
142
|
+
build.onResolve({ filter: /^pyric(\/|$)/ }, (args) => ({
|
|
143
|
+
path: fileURLToPath(import.meta.resolve(args.path)),
|
|
144
|
+
}));
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/** The node builtins pyric's browser graph reaches for (via the rules module
|
|
150
|
+
* resolver). Bare and `node:`-prefixed both match. */
|
|
151
|
+
export const NODE_BUILTIN_RE = /^(node:)?(url|fs|path)$/;
|
|
152
|
+
|
|
153
|
+
/** Benign browser shim SOURCE per builtin (see header — droppable only when
|
|
154
|
+
* pyric breaks the `firestore/index → rules/tools → modules/resolver` static
|
|
155
|
+
* chain, #553). Exported so the Vite plugin (`./vite`) reuses the SAME shims
|
|
156
|
+
* through its own resolveId/load + optimizeDeps esbuild pass, instead of
|
|
157
|
+
* re-deriving them. */
|
|
158
|
+
export const NODE_BUILTIN_SHIMS: Record<string, string> = {
|
|
159
|
+
fs: `const no = () => { throw new Error('pyric dev: fs is not available in the browser'); };
|
|
160
|
+
export const readFileSync = no; export const existsSync = () => false;
|
|
161
|
+
export const readdirSync = no; export const writeFileSync = no; export const mkdirSync = no;
|
|
162
|
+
export default { readFileSync, existsSync, readdirSync, writeFileSync, mkdirSync };`,
|
|
163
|
+
url: `export const fileURLToPath = (u) => String(u).replace('file://', '');
|
|
164
|
+
export const pathToFileURL = (p) => new URL('file://' + p);
|
|
165
|
+
export default { fileURLToPath, pathToFileURL };`,
|
|
166
|
+
path: `export const join = (...p) => p.join('/');
|
|
167
|
+
export const dirname = (p) => p.split('/').slice(0, -1).join('/') || '/';
|
|
168
|
+
export const resolve = (...p) => p.join('/');
|
|
169
|
+
export const basename = (p) => p.split('/').pop() ?? '';
|
|
170
|
+
export default { join, dirname, resolve, basename };`,
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
function nodeShimPlugin(): esbuild.Plugin {
|
|
174
|
+
return {
|
|
175
|
+
name: 'pyric-serve-node-shims',
|
|
176
|
+
setup(build) {
|
|
177
|
+
build.onResolve({ filter: NODE_BUILTIN_RE }, (args) => ({
|
|
178
|
+
path: args.path.replace(/^node:/, ''),
|
|
179
|
+
namespace: 'pyric-node-shim',
|
|
180
|
+
}));
|
|
181
|
+
build.onLoad({ filter: /.*/, namespace: 'pyric-node-shim' }, (args) => ({
|
|
182
|
+
contents: NODE_BUILTIN_SHIMS[args.path]!,
|
|
183
|
+
loader: 'js',
|
|
184
|
+
}));
|
|
185
|
+
},
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// ─── cache ────────────────────────────────────────────────────────────
|
|
190
|
+
|
|
191
|
+
export interface BundleOptions {
|
|
192
|
+
/** Entry files (the wrapper modules in `entries/`), keyed by the served
|
|
193
|
+
* basename (`auth` → `/__pyric/sdk/auth.js`). */
|
|
194
|
+
entries: Record<string, string>;
|
|
195
|
+
/** Bypass the cache and build an immutable, process-independent generation. */
|
|
196
|
+
noCache?: boolean;
|
|
197
|
+
/** Override the cache root (tests). Default `~/.pyric/serve-cache`. */
|
|
198
|
+
cacheRoot?: string;
|
|
199
|
+
minify?: boolean;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export interface BundleResult {
|
|
203
|
+
outDir: string;
|
|
204
|
+
/** Served basename → absolute file path (entry bundles + shared chunks). */
|
|
205
|
+
files: string[];
|
|
206
|
+
cached: boolean;
|
|
207
|
+
/** Release a bypass generation after its server stops. Cache generations
|
|
208
|
+
* deliberately remain durable, so their disposer is a no-op. */
|
|
209
|
+
dispose(): void;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/** Bump when bundler logic changes in a way that must invalidate caches. */
|
|
213
|
+
const BUNDLER_REV = 9; // 9: cache keys also cover dependency-resolution metadata
|
|
214
|
+
|
|
215
|
+
const RESOLUTION_FILES = [
|
|
216
|
+
'bun.lock',
|
|
217
|
+
'bun.lockb',
|
|
218
|
+
'package-lock.json',
|
|
219
|
+
'pnpm-lock.yaml',
|
|
220
|
+
'yarn.lock',
|
|
221
|
+
] as const;
|
|
222
|
+
|
|
223
|
+
function nearestPackageRoot(start: string): string | undefined {
|
|
224
|
+
let dir = start;
|
|
225
|
+
while (true) {
|
|
226
|
+
if (existsSync(join(dir, 'package.json'))) return dir;
|
|
227
|
+
const parent = dirname(dir);
|
|
228
|
+
if (parent === dir) return undefined;
|
|
229
|
+
dir = parent;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Hash package-manager identity separately from executable source bytes.
|
|
235
|
+
* Workspace dependency upgrades commonly change package.json/bun.lock before
|
|
236
|
+
* the prerelease package version moves; installed packages get the same signal
|
|
237
|
+
* from their consumer lockfile. Package managers own third-party byte
|
|
238
|
+
* integrity, so their manifests/locks are the supported cache boundary.
|
|
239
|
+
*/
|
|
240
|
+
function packageResolutionHash(graphRoot: string, pyricRoot: string): string {
|
|
241
|
+
const h = createHash('sha256');
|
|
242
|
+
const cliRoot = nearestPackageRoot(graphRoot);
|
|
243
|
+
for (const [label, root] of [
|
|
244
|
+
['cli', cliRoot],
|
|
245
|
+
['pyric', pyricRoot],
|
|
246
|
+
] as const) {
|
|
247
|
+
if (!root) continue;
|
|
248
|
+
const manifest = join(root, 'package.json');
|
|
249
|
+
if (!existsSync(manifest)) continue;
|
|
250
|
+
h.update(label);
|
|
251
|
+
h.update(readFileSync(manifest));
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
let dir = cliRoot ?? graphRoot;
|
|
255
|
+
while (true) {
|
|
256
|
+
for (const name of RESOLUTION_FILES) {
|
|
257
|
+
const file = join(dir, name);
|
|
258
|
+
if (!existsSync(file)) continue;
|
|
259
|
+
h.update(name);
|
|
260
|
+
h.update(readFileSync(file));
|
|
261
|
+
}
|
|
262
|
+
const parent = dirname(dir);
|
|
263
|
+
if (parent === dir) break;
|
|
264
|
+
dir = parent;
|
|
265
|
+
}
|
|
266
|
+
return h.digest('hex');
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
export function cacheKey(
|
|
270
|
+
opts: BundleOptions,
|
|
271
|
+
version: string,
|
|
272
|
+
graphRoot = dirname(dirname(fileURLToPath(import.meta.url))),
|
|
273
|
+
pyricRoot = pyricPackageRoot(),
|
|
274
|
+
): string {
|
|
275
|
+
const h = createHash('sha256');
|
|
276
|
+
h.update(`rev${BUNDLER_REV}`);
|
|
277
|
+
h.update(version);
|
|
278
|
+
// Hash the WHOLE local CLI graph, not just entries or entries/ siblings.
|
|
279
|
+
// Page adapters import worker/client, worker/protocol, bridge, and verify
|
|
280
|
+
// leaves outside that directory; missing one can pair a stale page client
|
|
281
|
+
// with a freshly rebuilt worker. The installed pyric implementation is
|
|
282
|
+
// hashed separately: workspace and prerelease builds can change dist bytes
|
|
283
|
+
// without changing package.json's version.
|
|
284
|
+
h.update(sourceTreeHash(graphRoot));
|
|
285
|
+
h.update(sourceTreeHash(join(pyricRoot, 'dist')));
|
|
286
|
+
h.update(packageResolutionHash(graphRoot, pyricRoot));
|
|
287
|
+
for (const [name, file] of Object.entries(opts.entries).sort()) {
|
|
288
|
+
h.update(name);
|
|
289
|
+
h.update(readFileSync(file, 'utf8'));
|
|
290
|
+
}
|
|
291
|
+
return `${version}-${h.digest('hex').slice(0, 12)}`;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// ─── the bundle step ──────────────────────────────────────────────────
|
|
295
|
+
|
|
296
|
+
export async function bundleSdk(opts: BundleOptions): Promise<BundleResult> {
|
|
297
|
+
const root = pyricPackageRoot();
|
|
298
|
+
const version = pyricVersion(root);
|
|
299
|
+
const key = cacheKey(opts, version);
|
|
300
|
+
const cacheRoot = opts.cacheRoot ?? join(homedir(), '.pyric', 'serve-cache');
|
|
301
|
+
mkdirSync(cacheRoot, { recursive: true });
|
|
302
|
+
// A running server keeps serving this directory after bundleSdk returns.
|
|
303
|
+
// Therefore `--no-cache` must never overwrite the deterministic warm-cache
|
|
304
|
+
// path: another concurrent `pyric dev --no-cache` could otherwise replace
|
|
305
|
+
// an entry and its shared chunks between browser requests, splitting
|
|
306
|
+
// singleton module state (notably the app registry). Each bypass build gets
|
|
307
|
+
// an immutable generation; the OS-level mkdir primitive makes concurrent
|
|
308
|
+
// callers distinct without coordination.
|
|
309
|
+
const generationRoot = opts.noCache
|
|
310
|
+
? mkdtempSync(join(cacheRoot, `.no-cache-${key}-`))
|
|
311
|
+
: join(cacheRoot, key);
|
|
312
|
+
const outDir = join(generationRoot, 'sdk');
|
|
313
|
+
const dispose = opts.noCache
|
|
314
|
+
? () => rmSync(generationRoot, { recursive: true, force: true })
|
|
315
|
+
: () => {};
|
|
316
|
+
|
|
317
|
+
if (!opts.noCache && existsSync(join(outDir, '.complete'))) {
|
|
318
|
+
const files = (readdirSync(outDir) as string[])
|
|
319
|
+
.filter((f) => f.endsWith('.js'))
|
|
320
|
+
.map((f) => join(outDir, f));
|
|
321
|
+
return { outDir, files, cached: true, dispose };
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
mkdirSync(outDir, { recursive: true });
|
|
325
|
+
try {
|
|
326
|
+
const result = await esbuild.build({
|
|
327
|
+
entryPoints: Object.fromEntries(
|
|
328
|
+
Object.entries(opts.entries).map(([name, file]) => [name, file]),
|
|
329
|
+
),
|
|
330
|
+
outdir: outDir,
|
|
331
|
+
bundle: true,
|
|
332
|
+
format: 'esm',
|
|
333
|
+
platform: 'browser',
|
|
334
|
+
splitting: true,
|
|
335
|
+
sourcemap: 'linked',
|
|
336
|
+
minify: opts.minify ?? true,
|
|
337
|
+
logLevel: 'silent',
|
|
338
|
+
// Provenance: any stack frame or "view source" into these bundles must
|
|
339
|
+
// self-identify as the sandbox shim, not the real Firebase SDK.
|
|
340
|
+
chunkNames: 'pyric-sandbox-[hash]',
|
|
341
|
+
banner: {
|
|
342
|
+
js: '/* pyric dev: pyric sandbox shim serving firebase/* — NOT the real Firebase SDK */',
|
|
343
|
+
},
|
|
344
|
+
plugins: [pyricResolvePlugin(), nodeShimPlugin()],
|
|
345
|
+
});
|
|
346
|
+
if (result.errors.length > 0) {
|
|
347
|
+
throw new Error(
|
|
348
|
+
`pyric dev: SDK bundle failed:\n${result.errors.map((e) => e.text).join('\n')}`,
|
|
349
|
+
);
|
|
350
|
+
}
|
|
351
|
+
writeFileSync(join(outDir, '.complete'), new Date().toISOString());
|
|
352
|
+
const files = (readdirSync(outDir) as string[])
|
|
353
|
+
.filter((f) => f.endsWith('.js'))
|
|
354
|
+
.map((f) => join(outDir, f));
|
|
355
|
+
return { outDir, files, cached: false, dispose };
|
|
356
|
+
} catch (error) {
|
|
357
|
+
dispose();
|
|
358
|
+
throw error;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// ─── the SharedWorker bundle (Phase 3c.A) ─────────────────────────────────
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Locate the SharedWorker entry (`worker/entry.ts`) — compiled `.js` sibling
|
|
366
|
+
* from dist, `.ts` source in the workspace. Same dual-resolution as
|
|
367
|
+
* `defaultSdkEntries`.
|
|
368
|
+
*/
|
|
369
|
+
export function workerEntryPath(): string {
|
|
370
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
371
|
+
const js = join(here, 'worker', 'entry.js');
|
|
372
|
+
if (existsSync(js)) return js;
|
|
373
|
+
const ts = join(here, 'worker', 'entry.ts');
|
|
374
|
+
if (existsSync(ts)) return ts;
|
|
375
|
+
throw new Error(`pyric dev: missing SharedWorker entry next to ${here}`);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
export interface WorkerBundleOptions {
|
|
379
|
+
/** Where to write `worker.js` — normally the SDK bundle's `outDir` so the
|
|
380
|
+
* existing `/__pyric/sdk/<file>` route serves it with no extra wiring. */
|
|
381
|
+
outDir: string;
|
|
382
|
+
/** Bypass + overwrite the cached worker bundle. */
|
|
383
|
+
noCache?: boolean;
|
|
384
|
+
minify?: boolean;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Content hash of the worker's executable CLI graph + pyric version. The worker bundle
|
|
389
|
+
* shares the SDK `outDir` (keyed by the SDK entries), which does NOT cover the
|
|
390
|
+
* worker graph — so the worker bundle carries its OWN content marker. Hash the
|
|
391
|
+
* full CLI source/dist tree because the worker imports bridge and verify leaves
|
|
392
|
+
* outside `serve/worker`; an allowlist would silently miss future dependencies.
|
|
393
|
+
* Installed pyric dist bytes are also hashed because workspace and prerelease
|
|
394
|
+
* builds can change implementation without a version bump.
|
|
395
|
+
*/
|
|
396
|
+
export function workerSourceHash(
|
|
397
|
+
pyricRoot = pyricPackageRoot(),
|
|
398
|
+
graphRoot = dirname(dirname(fileURLToPath(import.meta.url))),
|
|
399
|
+
): string {
|
|
400
|
+
const h = createHash('sha256');
|
|
401
|
+
h.update(`rev${BUNDLER_REV}`);
|
|
402
|
+
h.update(pyricVersion(pyricRoot));
|
|
403
|
+
h.update(sourceTreeHash(join(pyricRoot, 'dist')));
|
|
404
|
+
h.update(sourceTreeHash(graphRoot));
|
|
405
|
+
h.update(packageResolutionHash(graphRoot, pyricRoot));
|
|
406
|
+
return h.digest('hex').slice(0, 16);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/** Hash every JS/TS source below a directory, including split host/client leaves. */
|
|
410
|
+
export function sourceTreeHash(root: string): string {
|
|
411
|
+
const files: string[] = [];
|
|
412
|
+
const visit = (dir: string): void => {
|
|
413
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
414
|
+
const path = join(dir, entry.name);
|
|
415
|
+
if (entry.isDirectory()) visit(path);
|
|
416
|
+
else if (entry.isFile() && (entry.name.endsWith('.ts') || entry.name.endsWith('.js'))) {
|
|
417
|
+
files.push(path);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
};
|
|
421
|
+
visit(root);
|
|
422
|
+
const h = createHash('sha256');
|
|
423
|
+
for (const file of files.sort()) {
|
|
424
|
+
h.update(relative(root, file).split(sep).join('/'));
|
|
425
|
+
h.update(readFileSync(file, 'utf8'));
|
|
426
|
+
}
|
|
427
|
+
return h.digest('hex');
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* Bundle the SharedWorker entry into a single classic worker script served at
|
|
432
|
+
* `/__pyric/sdk/worker.js`.
|
|
433
|
+
*
|
|
434
|
+
* WHY A SEPARATE BUILD (not part of `bundleSdk`'s splitting graph): a
|
|
435
|
+
* SharedWorker script is loaded by URL into its OWN realm — it cannot share
|
|
436
|
+
* the page's runtime chunk. So it is bundled standalone: `splitting: false`,
|
|
437
|
+
* `format: 'iife'` (the client opens it with `new SharedWorker(url, { type:
|
|
438
|
+
* 'classic' })`), everything inlined into one file. It reuses the same three
|
|
439
|
+
* browser-standalone plugins as the SDK bundle (pyric-resolve + node-shims +
|
|
440
|
+
* firebase-stub) so `pyric/*`, node builtins, and the `firebase/*` peer stubs
|
|
441
|
+
* resolve identically.
|
|
442
|
+
*
|
|
443
|
+
* NOTE: the worker legitimately holds the whole sandbox engine — it IS the
|
|
444
|
+
* backend — so this bundle is large, but it loads ONCE per origin (not per
|
|
445
|
+
* page). Trimming node/admin dead paths is tracked as Phase 3b.
|
|
446
|
+
*
|
|
447
|
+
* The `.worker-complete` marker is independent of `bundleSdk`'s `.complete`,
|
|
448
|
+
* while `cacheKey()` includes the complete CLI source graph. A worker/client
|
|
449
|
+
* or protocol edit therefore invalidates the shared SDK generation too, so
|
|
450
|
+
* the page and worker halves cannot come from different source generations.
|
|
451
|
+
*/
|
|
452
|
+
export async function bundleWorker(opts: WorkerBundleOptions): Promise<string> {
|
|
453
|
+
const outFile = join(opts.outDir, 'worker.js');
|
|
454
|
+
const marker = join(opts.outDir, '.worker-complete');
|
|
455
|
+
const hash = workerSourceHash();
|
|
456
|
+
if (
|
|
457
|
+
!opts.noCache &&
|
|
458
|
+
existsSync(outFile) &&
|
|
459
|
+
existsSync(marker) &&
|
|
460
|
+
readFileSync(marker, 'utf8') === hash
|
|
461
|
+
) {
|
|
462
|
+
return outFile;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
mkdirSync(opts.outDir, { recursive: true });
|
|
466
|
+
const root = pyricPackageRoot();
|
|
467
|
+
const result = await esbuild.build({
|
|
468
|
+
entryPoints: [workerEntryPath()],
|
|
469
|
+
outfile: outFile,
|
|
470
|
+
bundle: true,
|
|
471
|
+
format: 'iife',
|
|
472
|
+
platform: 'browser',
|
|
473
|
+
splitting: false,
|
|
474
|
+
sourcemap: 'linked',
|
|
475
|
+
minify: opts.minify ?? true,
|
|
476
|
+
logLevel: 'silent',
|
|
477
|
+
// Bake the build hash so the worker can report its version (staleness
|
|
478
|
+
// guard): the page warns when a still-running OLD worker is older than the
|
|
479
|
+
// served bundle. Matches the `<meta name="pyric-worker-v">` value.
|
|
480
|
+
define: { __PYRIC_WORKER_VERSION__: JSON.stringify(hash) },
|
|
481
|
+
banner: {
|
|
482
|
+
js: '/* pyric dev: SharedWorker sandbox host serving firebase/* — NOT the real Firebase SDK */',
|
|
483
|
+
},
|
|
484
|
+
plugins: [pyricResolvePlugin(), nodeShimPlugin()],
|
|
485
|
+
});
|
|
486
|
+
if (result.errors.length > 0) {
|
|
487
|
+
throw new Error(
|
|
488
|
+
`pyric dev: SharedWorker bundle failed:\n${result.errors.map((e) => e.text).join('\n')}`,
|
|
489
|
+
);
|
|
490
|
+
}
|
|
491
|
+
writeFileSync(marker, hash);
|
|
492
|
+
return outFile;
|
|
493
|
+
}
|