@pyric/cli 0.1.0-alpha.11 → 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/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/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 +1 -1
- 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 +1 -1
- package/dist/serve/docs-ui/docs/get-started/start-building/index.html +1 -1
- package/dist/serve/docs-ui/docs/index.json +200 -25
- package/dist/serve/docs-ui/docs/messaging-compat/index.html +1 -1
- 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 +131 -66
- package/dist/serve/docs-ui/docs/pyric-cli-serve-worker-reference-api.md +130 -65
- 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 +1 -1
- 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/llms.txt +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/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.js +2 -2
- package/dist/serve/worker/protocol.js.map +1 -1
- package/package.json +5 -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 -31
- package/dist/serve/docs-ui/docs/pyric-firestore-values-reference-api.md +0 -29
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { ParsedArgs } from './parse-args.js';
|
|
2
|
+
import {
|
|
3
|
+
runDatabaseRulesGenerate,
|
|
4
|
+
runDatabaseRulesLint,
|
|
5
|
+
runDatabaseRulesSimulate,
|
|
6
|
+
runDatabaseRulesValidate,
|
|
7
|
+
} from './database-rules.js';
|
|
8
|
+
import { runFirestoreIndexesGenerate } from './firestore-indexes.js';
|
|
9
|
+
import {
|
|
10
|
+
runRulesLint,
|
|
11
|
+
runRulesResolve,
|
|
12
|
+
runRulesSimulate,
|
|
13
|
+
runRulesValidate,
|
|
14
|
+
} from './rules.js';
|
|
15
|
+
import { runStorageRulesLint, runStorageRulesSimulate } from './storage-rules.js';
|
|
16
|
+
|
|
17
|
+
const SERVICES = new Set(['firestore', 'storage', 'database']);
|
|
18
|
+
|
|
19
|
+
function invocation(parsed: ParsedArgs): string {
|
|
20
|
+
return [parsed.subcommand, ...parsed.positional].filter(Boolean).join(' ');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function argumentsAfterPath(parsed: ParsedArgs, pathLength: number): ParsedArgs {
|
|
24
|
+
return { ...parsed, positional: parsed.positional.slice(pathLength - 1) };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Dispatch the `pyric <service> <artifact> <operation>` command family.
|
|
29
|
+
* Returns `null` when the first token is not a service command so the
|
|
30
|
+
* top-level dispatcher can continue with commands such as `dev` and `verify`.
|
|
31
|
+
*/
|
|
32
|
+
export async function dispatchServiceCommand(parsed: ParsedArgs): Promise<number | null> {
|
|
33
|
+
const service = parsed.subcommand;
|
|
34
|
+
if (!service || !SERVICES.has(service)) return null;
|
|
35
|
+
|
|
36
|
+
const [artifact, operation] = parsed.positional;
|
|
37
|
+
if (service === 'firestore' && artifact === 'rules' && operation === 'lint') {
|
|
38
|
+
return await runRulesLint(argumentsAfterPath(parsed, 3));
|
|
39
|
+
}
|
|
40
|
+
if (service === 'firestore' && artifact === 'rules' && operation === 'validate') {
|
|
41
|
+
return await runRulesValidate(argumentsAfterPath(parsed, 3));
|
|
42
|
+
}
|
|
43
|
+
if (service === 'firestore' && artifact === 'rules' && operation === 'simulate') {
|
|
44
|
+
return await runRulesSimulate(argumentsAfterPath(parsed, 3));
|
|
45
|
+
}
|
|
46
|
+
if (service === 'firestore' && artifact === 'rules' && operation === 'resolve') {
|
|
47
|
+
return await runRulesResolve(argumentsAfterPath(parsed, 3));
|
|
48
|
+
}
|
|
49
|
+
if (service === 'firestore' && artifact === 'indexes' && operation === 'generate') {
|
|
50
|
+
return await runFirestoreIndexesGenerate(argumentsAfterPath(parsed, 3));
|
|
51
|
+
}
|
|
52
|
+
if (service === 'storage' && artifact === 'rules' && operation === 'lint') {
|
|
53
|
+
return await runStorageRulesLint(argumentsAfterPath(parsed, 3));
|
|
54
|
+
}
|
|
55
|
+
if (service === 'storage' && artifact === 'rules' && operation === 'simulate') {
|
|
56
|
+
return await runStorageRulesSimulate(argumentsAfterPath(parsed, 3));
|
|
57
|
+
}
|
|
58
|
+
if (service === 'database' && artifact === 'rules' && operation === 'lint') {
|
|
59
|
+
return await runDatabaseRulesLint(argumentsAfterPath(parsed, 3));
|
|
60
|
+
}
|
|
61
|
+
if (service === 'database' && artifact === 'rules' && operation === 'validate') {
|
|
62
|
+
return await runDatabaseRulesValidate(argumentsAfterPath(parsed, 3));
|
|
63
|
+
}
|
|
64
|
+
if (service === 'database' && artifact === 'rules' && operation === 'simulate') {
|
|
65
|
+
return await runDatabaseRulesSimulate(argumentsAfterPath(parsed, 3));
|
|
66
|
+
}
|
|
67
|
+
if (service === 'database' && artifact === 'rules' && operation === 'generate') {
|
|
68
|
+
return await runDatabaseRulesGenerate(argumentsAfterPath(parsed, 3));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
process.stderr.write(`pyric: unknown command '${invocation(parsed)}'.\n`);
|
|
72
|
+
return 1;
|
|
73
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `pyric snapshot` — promote lived sandbox state to a committable fixture
|
|
3
|
+
* (flow doc section 3c: seed.json is intent, `.pyric/state/` is runtime, promote
|
|
4
|
+
* bridges them).
|
|
5
|
+
*
|
|
6
|
+
* pyric snapshot [--out FILE] [--port N] [--force] [--json]
|
|
7
|
+
*
|
|
8
|
+
* Source preference:
|
|
9
|
+
* 1. LIVE state from a running `pyric dev --persist`
|
|
10
|
+
* (`GET /__pyric/state` — `--port`, else the 3473+ scan window),
|
|
11
|
+
* 2. else the on-disk `.pyric/state/state.json`,
|
|
12
|
+
* 3. else exit 2 with a clear message.
|
|
13
|
+
*
|
|
14
|
+
* The output is a `PyricStateFile` envelope — directly re-servable:
|
|
15
|
+
* `pyric dev --seed <out>` (the state-file shape is detected by its
|
|
16
|
+
* `version` key and seeds docs + users).
|
|
17
|
+
*/
|
|
18
|
+
import { existsSync, writeFileSync } from 'node:fs';
|
|
19
|
+
import { resolve } from 'node:path';
|
|
20
|
+
import type { ParsedArgs } from './parse-args.js';
|
|
21
|
+
import { createStateStore, type PyricStateFile } from '../serve/state-store.js';
|
|
22
|
+
|
|
23
|
+
/** Ports probed when --port is absent — serve's default + scan window. */
|
|
24
|
+
const SCAN_PORTS = [3473, 3474, 3475, 3476, 3477];
|
|
25
|
+
|
|
26
|
+
/** Replacement for redacted passwords in a promoted fixture. Mirrors pyric's
|
|
27
|
+
* own `NO_PASSWORD_SENTINEL` so `seedUsers` accepts it on re-serve; defined
|
|
28
|
+
* locally to avoid depending on a pyric internal export. */
|
|
29
|
+
const REDACTED_PASSWORD = '__pyric_no_password__';
|
|
30
|
+
|
|
31
|
+
interface LiveState {
|
|
32
|
+
envelope: PyricStateFile;
|
|
33
|
+
/** The project dir the live serve reported (pre-mortem #4 guard). */
|
|
34
|
+
projectDir: string | null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async function fetchLive(port: number): Promise<LiveState | null> {
|
|
38
|
+
try {
|
|
39
|
+
const res = await fetch(`http://localhost:${port}/__pyric/state`, {
|
|
40
|
+
signal: AbortSignal.timeout(750),
|
|
41
|
+
});
|
|
42
|
+
if (res.status !== 200) return null;
|
|
43
|
+
const body = (await res.json()) as PyricStateFile;
|
|
44
|
+
if (!body || typeof body !== 'object' || !('version' in body)) return null;
|
|
45
|
+
return { envelope: body, projectDir: res.headers.get('x-pyric-project-dir') };
|
|
46
|
+
} catch {
|
|
47
|
+
return null; // nothing listening / not a pyric dev — fall through
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface SnapshotDeps {
|
|
52
|
+
cwd?: string;
|
|
53
|
+
stdout?: { write(s: string): void };
|
|
54
|
+
stderr?: { write(s: string): void };
|
|
55
|
+
/** Injectable live-fetch (tests). */
|
|
56
|
+
fetchLive?: typeof fetchLive;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export async function runSnapshot(parsed: ParsedArgs, deps: SnapshotDeps = {}): Promise<number> {
|
|
60
|
+
const out = deps.stdout ?? process.stdout;
|
|
61
|
+
const err = deps.stderr ?? process.stderr;
|
|
62
|
+
const cwd = deps.cwd ?? process.cwd();
|
|
63
|
+
const live = deps.fetchLive ?? fetchLive;
|
|
64
|
+
const json = parsed.flags.get('json') === true || parsed.flags.get('json') === 'true';
|
|
65
|
+
const force = Boolean(parsed.flags.get('force'));
|
|
66
|
+
const report = json ? err : out;
|
|
67
|
+
|
|
68
|
+
const outFlag = parsed.flags.get('out');
|
|
69
|
+
const outPath = resolve(cwd, typeof outFlag === 'string' ? outFlag : 'pyric-state.json');
|
|
70
|
+
if (existsSync(outPath) && !force) {
|
|
71
|
+
err.write(`pyric snapshot: ${outPath} already exists — pass --force to overwrite.\n`);
|
|
72
|
+
return 2;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const portFlag = parsed.flags.get('port');
|
|
76
|
+
const ports = typeof portFlag === 'string' ? [Number(portFlag)] : SCAN_PORTS;
|
|
77
|
+
if (ports.some((p) => !Number.isFinite(p) || p < 1 || p > 65535)) {
|
|
78
|
+
err.write(`pyric: invalid --port '${portFlag}'.\n`);
|
|
79
|
+
return 1;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
let envelope: PyricStateFile | null = null;
|
|
83
|
+
let source = '';
|
|
84
|
+
for (const port of ports) {
|
|
85
|
+
const found = await live(port);
|
|
86
|
+
if (!found) continue;
|
|
87
|
+
// Wrong-project guard (pre-mortem #4): the port scan can hit a NEIGHBOR
|
|
88
|
+
// project's serve (yours down, theirs on 3473). Refuse unless --port was
|
|
89
|
+
// explicit AND warn either way.
|
|
90
|
+
if (found.projectDir && found.projectDir !== cwd) {
|
|
91
|
+
const explicit = typeof portFlag === 'string';
|
|
92
|
+
err.write(
|
|
93
|
+
`pyric snapshot: the dev server on port ${port} persists a DIFFERENT project\n` +
|
|
94
|
+
` it serves: ${found.projectDir}\n you are in: ${cwd}\n`,
|
|
95
|
+
);
|
|
96
|
+
if (!explicit) {
|
|
97
|
+
err.write(' Skipping it (pass --port to promote it anyway).\n');
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
err.write(' --port was explicit — promoting it as asked.\n');
|
|
101
|
+
}
|
|
102
|
+
envelope = found.envelope;
|
|
103
|
+
source = `live serve on port ${port}`;
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
if (!envelope) {
|
|
107
|
+
const store = createStateStore(cwd);
|
|
108
|
+
envelope = store.load(); // throws loudly on corrupt — that's the right surface
|
|
109
|
+
if (envelope) source = store.path;
|
|
110
|
+
}
|
|
111
|
+
if (!envelope) {
|
|
112
|
+
err.write(
|
|
113
|
+
'pyric snapshot: no state found — no `pyric dev --persist` is running here and ' +
|
|
114
|
+
'no .pyric/state/state.json exists. Run with --persist (and use the app) first.\n',
|
|
115
|
+
);
|
|
116
|
+
return 2;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Password hygiene (pre-mortem #4): the promoted fixture is meant to be
|
|
120
|
+
// COMMITTED, and only `.pyric/` is gitignored. Redact user passwords by
|
|
121
|
+
// default — the sentinel round-trips through `seedUsers`, so re-serving
|
|
122
|
+
// still works (popup/helper sign-in doesn't use passwords; email/password
|
|
123
|
+
// sign-in with the original secret won't, which is the point). Opt out
|
|
124
|
+
// with --include-passwords.
|
|
125
|
+
const includePasswords = Boolean(parsed.flags.get('include-passwords'));
|
|
126
|
+
let redactedCount = 0;
|
|
127
|
+
if (!includePasswords && envelope.auth?.users) {
|
|
128
|
+
envelope = {
|
|
129
|
+
...envelope,
|
|
130
|
+
auth: {
|
|
131
|
+
users: envelope.auth.users.map((u) => {
|
|
132
|
+
if (typeof u.password === 'string' && u.password !== REDACTED_PASSWORD) {
|
|
133
|
+
redactedCount++;
|
|
134
|
+
return { ...u, password: REDACTED_PASSWORD };
|
|
135
|
+
}
|
|
136
|
+
return u;
|
|
137
|
+
}),
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Strip `savedAt` (pre-mortem: it churns every flush, so committed
|
|
143
|
+
// fixtures would re-diff on every re-promote). Restore ignores it.
|
|
144
|
+
const fsSection = envelope.firestore as { savedAt?: number } | null;
|
|
145
|
+
if (fsSection && typeof fsSection === 'object' && 'savedAt' in fsSection) {
|
|
146
|
+
const { savedAt: _dropped, ...rest } = fsSection;
|
|
147
|
+
envelope = { ...envelope, firestore: rest };
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
writeFileSync(outPath, JSON.stringify(envelope, null, 2) + '\n', 'utf8');
|
|
151
|
+
|
|
152
|
+
const docs = Object.keys(
|
|
153
|
+
((envelope.firestore as { firestore?: Record<string, unknown> } | null)?.firestore) ?? {},
|
|
154
|
+
).length;
|
|
155
|
+
const users = envelope.auth?.users?.length ?? 0;
|
|
156
|
+
report.write(`pyric snapshot: ${docs} doc(s) + ${users} user(s) from ${source}\n`);
|
|
157
|
+
report.write(` → ${outPath}\n`);
|
|
158
|
+
if (redactedCount > 0) {
|
|
159
|
+
report.write(` ⓘ redacted ${redactedCount} password(s) — re-run with --include-passwords to keep them\n`);
|
|
160
|
+
}
|
|
161
|
+
report.write(` Re-serve it: pyric dev --seed ${typeof outFlag === 'string' ? outFlag : 'pyric-state.json'}\n`);
|
|
162
|
+
if (json) out.write(JSON.stringify({ out: outPath, docs, users, source, redactedPasswords: redactedCount }) + '\n');
|
|
163
|
+
return 0;
|
|
164
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { resolve as resolvePath } from 'node:path';
|
|
3
|
+
import {
|
|
4
|
+
evaluateStorageRules,
|
|
5
|
+
parseStorageRules,
|
|
6
|
+
type StorageRequest,
|
|
7
|
+
type StorageResource,
|
|
8
|
+
} from 'pyric/storage';
|
|
9
|
+
import { readFirebaseJson, type FirebaseJson } from './firebase-json.js';
|
|
10
|
+
import type { ParsedArgs } from './parse-args.js';
|
|
11
|
+
|
|
12
|
+
export interface StorageRulesDeps {
|
|
13
|
+
readFile?: typeof readFile;
|
|
14
|
+
parseStorageRules?: typeof parseStorageRules;
|
|
15
|
+
evaluateStorageRules?: typeof evaluateStorageRules;
|
|
16
|
+
readFirebaseJson?: (cwd: string) => Promise<FirebaseJson>;
|
|
17
|
+
readStdin?: () => Promise<string>;
|
|
18
|
+
cwd?: string;
|
|
19
|
+
stdout?: { write(s: string): void };
|
|
20
|
+
stderr?: { write(s: string): void };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface StorageSimulationPayload {
|
|
24
|
+
source?: string;
|
|
25
|
+
request?: StorageRequest;
|
|
26
|
+
resource?: StorageResource | null;
|
|
27
|
+
now?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function defaultReadStdin(): Promise<string> {
|
|
31
|
+
return new Promise((resolve, reject) => {
|
|
32
|
+
let data = '';
|
|
33
|
+
process.stdin.setEncoding('utf-8');
|
|
34
|
+
process.stdin.on('data', (chunk) => {
|
|
35
|
+
data += chunk;
|
|
36
|
+
});
|
|
37
|
+
process.stdin.on('end', () => resolve(data));
|
|
38
|
+
process.stdin.on('error', reject);
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function projectRulesSource(deps: StorageRulesDeps): Promise<string> {
|
|
43
|
+
const cwd = deps.cwd ?? process.cwd();
|
|
44
|
+
const firebaseJson = await (deps.readFirebaseJson ?? readFirebaseJson)(cwd);
|
|
45
|
+
const storageConfig = Array.isArray(firebaseJson.storage)
|
|
46
|
+
? firebaseJson.storage.find((entry) => entry.rules)
|
|
47
|
+
: firebaseJson.storage;
|
|
48
|
+
const rulesPath = storageConfig?.rules;
|
|
49
|
+
if (!rulesPath) throw new Error('firebase.json has no `storage.rules` path.');
|
|
50
|
+
return await (deps.readFile ?? readFile)(resolvePath(cwd, rulesPath), 'utf-8');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export async function runStorageRulesLint(
|
|
54
|
+
parsed: ParsedArgs,
|
|
55
|
+
deps: StorageRulesDeps = {},
|
|
56
|
+
): Promise<number> {
|
|
57
|
+
const out = deps.stdout ?? process.stdout;
|
|
58
|
+
const err = deps.stderr ?? process.stderr;
|
|
59
|
+
const cwd = deps.cwd ?? process.cwd();
|
|
60
|
+
const path = parsed.positional[0];
|
|
61
|
+
if (!path) {
|
|
62
|
+
err.write(
|
|
63
|
+
'pyric storage rules lint: missing rules-file path. Usage: pyric storage rules lint <path>\n',
|
|
64
|
+
);
|
|
65
|
+
return 1;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
let source: string;
|
|
69
|
+
try {
|
|
70
|
+
source = await (deps.readFile ?? readFile)(resolvePath(cwd, path), 'utf-8');
|
|
71
|
+
} catch (error) {
|
|
72
|
+
err.write(
|
|
73
|
+
`pyric storage rules lint: ${error instanceof Error ? error.message : String(error)}\n`,
|
|
74
|
+
);
|
|
75
|
+
return 1;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
(deps.parseStorageRules ?? parseStorageRules)(source);
|
|
80
|
+
out.write(`${JSON.stringify({ warnings: [], metrics: { sourceSize: source.length } }, null, 2)}\n`);
|
|
81
|
+
return 0;
|
|
82
|
+
} catch (error) {
|
|
83
|
+
out.write(
|
|
84
|
+
`${JSON.stringify(
|
|
85
|
+
{
|
|
86
|
+
warnings: [],
|
|
87
|
+
parseError: { message: error instanceof Error ? error.message : String(error) },
|
|
88
|
+
metrics: { sourceSize: source.length },
|
|
89
|
+
},
|
|
90
|
+
null,
|
|
91
|
+
2,
|
|
92
|
+
)}\n`,
|
|
93
|
+
);
|
|
94
|
+
return 2;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export async function runStorageRulesSimulate(
|
|
99
|
+
parsed: ParsedArgs,
|
|
100
|
+
deps: StorageRulesDeps = {},
|
|
101
|
+
): Promise<number> {
|
|
102
|
+
const out = deps.stdout ?? process.stdout;
|
|
103
|
+
const err = deps.stderr ?? process.stderr;
|
|
104
|
+
let source: string;
|
|
105
|
+
let request: StorageRequest;
|
|
106
|
+
let resource: StorageResource | null;
|
|
107
|
+
let now = new Date();
|
|
108
|
+
|
|
109
|
+
if (parsed.flags.get('stdin') === true) {
|
|
110
|
+
let payload: StorageSimulationPayload;
|
|
111
|
+
try {
|
|
112
|
+
payload = JSON.parse(await (deps.readStdin ?? defaultReadStdin)()) as StorageSimulationPayload;
|
|
113
|
+
} catch (error) {
|
|
114
|
+
err.write(
|
|
115
|
+
`pyric storage rules simulate: failed to parse stdin JSON: ${error instanceof Error ? error.message : String(error)}\n`,
|
|
116
|
+
);
|
|
117
|
+
return 1;
|
|
118
|
+
}
|
|
119
|
+
if (!payload.request) {
|
|
120
|
+
err.write('pyric storage rules simulate: stdin payload must include `request`.\n');
|
|
121
|
+
return 1;
|
|
122
|
+
}
|
|
123
|
+
try {
|
|
124
|
+
source = payload.source ?? (await projectRulesSource(deps));
|
|
125
|
+
} catch (error) {
|
|
126
|
+
err.write(
|
|
127
|
+
`pyric storage rules simulate: ${error instanceof Error ? error.message : String(error)}\n`,
|
|
128
|
+
);
|
|
129
|
+
return 1;
|
|
130
|
+
}
|
|
131
|
+
request = payload.request;
|
|
132
|
+
resource = payload.resource ?? null;
|
|
133
|
+
if (payload.now !== undefined) {
|
|
134
|
+
now = new Date(payload.now);
|
|
135
|
+
if (Number.isNaN(now.getTime())) {
|
|
136
|
+
err.write('pyric storage rules simulate: `now` must be an ISO-8601 timestamp.\n');
|
|
137
|
+
return 1;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
} else {
|
|
141
|
+
try {
|
|
142
|
+
source = await projectRulesSource(deps);
|
|
143
|
+
} catch (error) {
|
|
144
|
+
err.write(
|
|
145
|
+
`pyric storage rules simulate: ${error instanceof Error ? error.message : String(error)}\n`,
|
|
146
|
+
);
|
|
147
|
+
return 1;
|
|
148
|
+
}
|
|
149
|
+
request = {
|
|
150
|
+
auth: null,
|
|
151
|
+
method: 'get',
|
|
152
|
+
path: 'b/pyric-default/o/sample/x',
|
|
153
|
+
};
|
|
154
|
+
resource = null;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
try {
|
|
158
|
+
const rules = (deps.parseStorageRules ?? parseStorageRules)(source);
|
|
159
|
+
const result = (deps.evaluateStorageRules ?? evaluateStorageRules)(
|
|
160
|
+
rules,
|
|
161
|
+
{ request, resource },
|
|
162
|
+
now,
|
|
163
|
+
);
|
|
164
|
+
out.write(`${JSON.stringify({ success: true, data: result }, null, 2)}\n`);
|
|
165
|
+
return 0;
|
|
166
|
+
} catch (error) {
|
|
167
|
+
err.write(
|
|
168
|
+
`pyric storage rules simulate: ${error instanceof Error ? error.message : String(error)}\n`,
|
|
169
|
+
);
|
|
170
|
+
return 2;
|
|
171
|
+
}
|
|
172
|
+
}
|