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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. package/README.md +1 -1
  2. package/dist/assurance/.generated/conformance-verdicts.d.ts +5 -0
  3. package/dist/assurance/.generated/conformance-verdicts.d.ts.map +1 -1
  4. package/dist/assurance/.generated/conformance-verdicts.js +5 -0
  5. package/dist/assurance/.generated/conformance-verdicts.js.map +1 -1
  6. package/dist/assurance/capabilities.d.ts.map +1 -1
  7. package/dist/assurance/capabilities.js +24 -5
  8. package/dist/assurance/capabilities.js.map +1 -1
  9. package/dist/assurance/runner.d.ts.map +1 -1
  10. package/dist/assurance/runner.js +37 -50
  11. package/dist/assurance/runner.js.map +1 -1
  12. package/dist/assurance/types.d.ts +5 -0
  13. package/dist/assurance/types.d.ts.map +1 -1
  14. package/dist/assurance/types.js.map +1 -1
  15. package/dist/assurance/validation.d.ts +2 -16
  16. package/dist/assurance/validation.d.ts.map +1 -1
  17. package/dist/assurance/validation.js +121 -1
  18. package/dist/assurance/validation.js.map +1 -1
  19. package/dist/bridge/surface/arguments/assurance.d.ts +25 -0
  20. package/dist/bridge/surface/arguments/assurance.d.ts.map +1 -1
  21. package/dist/bridge/surface/arguments/assurance.js +1 -0
  22. package/dist/bridge/surface/arguments/assurance.js.map +1 -1
  23. package/dist/bridge/surface/methods/assurance/define.d.ts +2 -2
  24. package/dist/bridge/surface/methods/assurance/map.d.ts +23 -0
  25. package/dist/bridge/surface/methods/assurance/map.d.ts.map +1 -1
  26. package/dist/bridge/surface/methods/assurance/propose.d.ts +7 -0
  27. package/dist/bridge/surface/methods/assurance/propose.d.ts.map +1 -1
  28. package/dist/bridge/surface/render/discriminator-schemas.d.ts +2 -2
  29. package/dist/cli/database-rules.js +1 -1
  30. package/dist/cli/database-rules.js.map +1 -1
  31. package/dist/cli/index.d.ts.map +1 -1
  32. package/dist/cli/index.js +13 -2
  33. package/dist/cli/index.js.map +1 -1
  34. package/dist/cli/pyric-config.d.ts +8 -0
  35. package/dist/cli/pyric-config.d.ts.map +1 -1
  36. package/dist/cli/pyric-config.js.map +1 -1
  37. package/dist/cli/serve.d.ts.map +1 -1
  38. package/dist/cli/serve.js +5 -3
  39. package/dist/cli/serve.js.map +1 -1
  40. package/dist/cli/verify.d.ts +6 -0
  41. package/dist/cli/verify.d.ts.map +1 -1
  42. package/dist/cli/verify.js +240 -15
  43. package/dist/cli/verify.js.map +1 -1
  44. package/dist/conformance/.generated/can-i-use.d.ts.map +1 -1
  45. package/dist/conformance/.generated/can-i-use.js +1 -1
  46. package/dist/conformance/.generated/can-i-use.js.map +1 -1
  47. package/dist/conformance/.generated/conformance-docs.d.ts.map +1 -1
  48. package/dist/conformance/.generated/conformance-docs.js +1 -1
  49. package/dist/conformance/.generated/conformance-docs.js.map +1 -1
  50. package/dist/rtdb/inspect-deployed-rules.d.ts +3 -17
  51. package/dist/rtdb/inspect-deployed-rules.d.ts.map +1 -1
  52. package/dist/rtdb/inspect-deployed-rules.js +4 -54
  53. package/dist/rtdb/inspect-deployed-rules.js.map +1 -1
  54. package/dist/serve/repro/denials.d.ts.map +1 -1
  55. package/dist/serve/repro/denials.js +2 -1
  56. package/dist/serve/repro/denials.js.map +1 -1
  57. package/dist/serve/site-ui/_astro/{app.BpEphDR7.js → app.CiMHEvEW.js} +15 -15
  58. package/dist/serve/site-ui/_astro/{app.DFdWhpVY.js → app.DpzsBfos.js} +1 -1
  59. package/dist/serve/site-ui/_astro/{seed.vvXV1ioI.js → seed.BpXSRzLo.js} +1 -1
  60. package/dist/serve/site-ui/auth/index.html +1 -1
  61. package/dist/serve/site-ui/docs/database-compat/index.html +4 -0
  62. package/dist/serve/site-ui/docs/database-compat.md +4 -0
  63. package/dist/serve/site-ui/docs/get-started/start-building/index.html +1 -0
  64. package/dist/serve/site-ui/docs/get-started/start-building.md +2 -0
  65. package/dist/serve/site-ui/docs/index.json +35 -0
  66. package/dist/serve/site-ui/docs/pyric-cli-assurance-reference-api/index.html +23 -4
  67. package/dist/serve/site-ui/docs/pyric-cli-assurance-reference-api.md +11 -4
  68. package/dist/serve/site-ui/docs/pyric-cli-verify-reference-api/index.html +15 -2
  69. package/dist/serve/site-ui/docs/pyric-cli-verify-reference-api.md +11 -10
  70. package/dist/serve/site-ui/docs/pyric-rules-reference-api/index.html +372 -201
  71. package/dist/serve/site-ui/docs/pyric-rules-reference-api.md +148 -9
  72. package/dist/serve/site-ui/docs/secure/rtdb-rules-in-typescript/index.html +56 -1
  73. package/dist/serve/site-ui/docs/secure/rtdb-rules-in-typescript.md +33 -0
  74. package/dist/serve/site-ui/docs/secure/write-a-rules-test-suite/index.html +1 -1
  75. package/dist/serve/site-ui/docs/secure/write-a-rules-test-suite.md +1 -1
  76. package/dist/serve/site-ui/docs/ship/ship-to-production/index.html +4 -1
  77. package/dist/serve/site-ui/docs/ship/ship-to-production.md +7 -1
  78. package/dist/serve/site-ui/examples/chess/index.html +1 -1
  79. package/dist/serve/site-ui/firestore/index.html +1 -1
  80. package/dist/serve/site-ui/rtdb/index.html +1 -1
  81. package/dist/serve/site-ui/settings/index.html +1 -1
  82. package/dist/serve/site-ui/storage/index.html +1 -1
  83. package/dist/serve/site-ui/studio/index.html +1 -1
  84. package/dist/serve/site-ui/traffic/index.html +1 -1
  85. package/dist/serve/verify-hint.d.ts +2 -0
  86. package/dist/serve/verify-hint.d.ts.map +1 -0
  87. package/dist/serve/verify-hint.js +10 -0
  88. package/dist/serve/verify-hint.js.map +1 -0
  89. package/dist/serve/vite-generation-rules-watch.d.ts.map +1 -1
  90. package/dist/serve/vite-generation-rules-watch.js +5 -3
  91. package/dist/serve/vite-generation-rules-watch.js.map +1 -1
  92. package/dist/verify/index.d.ts +16 -2
  93. package/dist/verify/index.d.ts.map +1 -1
  94. package/dist/verify/index.js +100 -18
  95. package/dist/verify/index.js.map +1 -1
  96. package/dist/verify/staging/engine.d.ts +102 -0
  97. package/dist/verify/staging/engine.d.ts.map +1 -0
  98. package/dist/verify/staging/engine.js +371 -0
  99. package/dist/verify/staging/engine.js.map +1 -0
  100. package/dist/verify/staging/rest.d.ts +68 -0
  101. package/dist/verify/staging/rest.d.ts.map +1 -0
  102. package/dist/verify/staging/rest.js +93 -0
  103. package/dist/verify/staging/rest.js.map +1 -0
  104. package/dist/verify/staging/target.d.ts +39 -0
  105. package/dist/verify/staging/target.d.ts.map +1 -0
  106. package/dist/verify/staging/target.js +117 -0
  107. package/dist/verify/staging/target.js.map +1 -0
  108. package/package.json +4 -4
  109. package/src/assurance/.generated/conformance-verdicts.ts +5 -0
  110. package/src/assurance/capabilities.ts +22 -4
  111. package/src/assurance/runner.ts +40 -58
  112. package/src/assurance/types.ts +3 -0
  113. package/src/assurance/validation.ts +137 -0
  114. package/src/bridge/surface/arguments/assurance.ts +1 -0
  115. package/src/cli/database-rules.ts +1 -1
  116. package/src/cli/index.ts +13 -2
  117. package/src/cli/pyric-config.ts +5 -0
  118. package/src/cli/serve.ts +6 -3
  119. package/src/cli/verify.ts +244 -18
  120. package/src/conformance/.generated/can-i-use.ts +1 -1
  121. package/src/conformance/.generated/conformance-docs.ts +1 -1
  122. package/src/rtdb/inspect-deployed-rules.ts +5 -77
  123. package/src/serve/repro/denials.ts +2 -1
  124. package/src/serve/verify-hint.ts +8 -0
  125. package/src/serve/vite-generation-rules-watch.ts +6 -3
  126. package/src/verify/index.ts +119 -19
  127. package/src/verify/staging/engine.ts +455 -0
  128. package/src/verify/staging/rest.ts +129 -0
  129. package/src/verify/staging/target.ts +137 -0
@@ -0,0 +1,102 @@
1
+ /**
2
+ * The staging engine: `pyric verify --engine staging` replays a captured RTDB
3
+ * session against a real Realtime Database instance the user dedicates to it,
4
+ * so production's rules engine decides every operation.
5
+ *
6
+ * It walks the same steps as the sandbox replay (`rtdbReplaySteps`), in order,
7
+ * performing each against the instance through the REST API as the captured
8
+ * user (`auth_variable_override`) or as admin:
9
+ *
10
+ * - `admin-commit`: written as admin, as context.
11
+ * - `app-commit`: written as the user; a denial is `now-denied`.
12
+ * - `allowed-read`: read as the user, with its query; a denial is `now-denied`.
13
+ * - `denial`: attempted as the user; success is `now-allowed`, and a write
14
+ * that landed is put back as admin, so the tree stays the session's.
15
+ * - `allowed-registration`: the `onDisconnect` write it registers, attempted
16
+ * as the user and put back (production decides a registration as that
17
+ * write when it is made, row M81); a denial is `now-denied`.
18
+ *
19
+ * Then the tree is compared with the one the session left (`state-drift`).
20
+ *
21
+ * The instance is the user's, so every run is guarded (`withStagingInstance`):
22
+ * it must carry the enrollment marker `pyric verify staging init` wrote and
23
+ * hold no data; the run takes a lease (a conditional write of
24
+ * `/__pyric_verify_lease__`, so a second run is refused); on every exit,
25
+ * including an aborted walk, its rules are restored and its data deleted,
26
+ * each step on its own and retried, then both are read back, and a run that
27
+ * cannot prove that fails loudly, naming `pyric verify staging restore`.
28
+ * A process killed outright (SIGKILL, power loss) cannot clean up; the CLI
29
+ * keeps the prior rules in `.pyric/staging-restore.json` for that command.
30
+ */
31
+ import type { SandboxEvent } from 'pyric/sandbox';
32
+ import { type RtdbReplayDivergence } from 'pyric/rules/internal/rtdb';
33
+ import { type RtdbRestClient } from './rest.js';
34
+ /** The comment `pyric verify staging init` puts at the top of the instance's rules. */
35
+ export declare const STAGING_MARKER_PREFIX = "/* pyric verify staging:";
36
+ export declare function stagingMarker(projectId: string, at?: Date): string;
37
+ /** The project an enrollment marker names, or undefined when the rules carry no marker. */
38
+ export declare function markedProject(rulesText: string): string | undefined;
39
+ export declare class StagingSafetyError extends Error {
40
+ constructor(message: string);
41
+ }
42
+ /** Production refused to deploy the candidate rules; its message is the reason. */
43
+ export declare class StagingDeployRefusedError extends Error {
44
+ readonly instanceLabel: string;
45
+ readonly production: string;
46
+ constructor(instanceLabel: string, production: string);
47
+ }
48
+ /** What a guarded run found when it put the instance back. */
49
+ export interface StagingInvariants {
50
+ rulesRestored: boolean;
51
+ dataRemoved: boolean;
52
+ }
53
+ /** The data node a run holds while it runs; written with a conditional request, so only one run gets it. */
54
+ export declare const STAGING_LEASE_KEY = "__pyric_verify_lease__";
55
+ /** Hooks a caller can attach to a guarded run. */
56
+ export interface StagingRunHooks {
57
+ /** Called with the prior rules text before anything is changed, so the caller can keep it for recovery. */
58
+ onBeforeChange?: (priorRules: string) => void;
59
+ /** Called once the instance reads back as it was found. */
60
+ onRestored?: () => void;
61
+ /** Abort the walk between steps; cleanup still runs. */
62
+ signal?: AbortSignal;
63
+ }
64
+ /**
65
+ * Run `fn` against the staging instance behind `client`, guarded: refuse an
66
+ * instance without the marker for `projectId`, with data, or with another
67
+ * run's lease; on every exit restore its rules and delete its data, each step
68
+ * attempted on its own and retried, then read both back.
69
+ */
70
+ export declare function withStagingInstance<T>(client: RtdbRestClient, projectId: string, fn: () => Promise<T>, hooks?: StagingRunHooks): Promise<{
71
+ result: T;
72
+ invariants: StagingInvariants;
73
+ }>;
74
+ /**
75
+ * Put a staging instance back: deploy `rulesText` first, since candidate
76
+ * rules may be open, then delete the data; each step on its own, retried,
77
+ * and read back. Never throws: the result says what holds.
78
+ */
79
+ export declare function restoreStagingInstance(client: RtdbRestClient, rulesText: string): Promise<StagingInvariants & {
80
+ errors: string[];
81
+ }>;
82
+ export interface StagingInstanceRun {
83
+ checkedEvents: number;
84
+ divergences: RtdbReplayDivergence[];
85
+ }
86
+ /**
87
+ * Replay one captured instance's steps against the staging instance under
88
+ * `rules`. Call inside `withStagingInstance`; the instance must be empty.
89
+ */
90
+ export declare function runStagingInstance(input: {
91
+ client: RtdbRestClient;
92
+ events: readonly SandboxEvent[];
93
+ rules: {
94
+ rules: Record<string, unknown>;
95
+ };
96
+ capturedState: unknown;
97
+ /** A named instance; absent is the default instance. */
98
+ instance?: string;
99
+ /** Abort between steps; the guard still cleans up. */
100
+ signal?: AbortSignal;
101
+ }): Promise<StagingInstanceRun>;
102
+ //# sourceMappingURL=engine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../../src/verify/staging/engine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,OAAO,KAAK,EAAsB,YAAY,EAAyB,MAAM,eAAe,CAAC;AAC7F,OAAO,EAKL,KAAK,oBAAoB,EAC1B,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EAAuC,KAAK,cAAc,EAAyB,MAAM,WAAW,CAAC;AAE5G,uFAAuF;AACvF,eAAO,MAAM,qBAAqB,6BAA6B,CAAC;AAEhE,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,OAAa,GAAG,MAAM,CAExE;AAED,2FAA2F;AAC3F,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAInE;AAED,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,OAAO,EAAE,MAAM;CAI5B;AAED,mFAAmF;AACnF,qBAAa,yBAA0B,SAAQ,KAAK;IACtC,QAAQ,CAAC,aAAa,EAAE,MAAM;IAAE,QAAQ,CAAC,UAAU,EAAE,MAAM;gBAAlD,aAAa,EAAE,MAAM,EAAW,UAAU,EAAE,MAAM;CAIxE;AAED,8DAA8D;AAC9D,MAAM,WAAW,iBAAiB;IAChC,aAAa,EAAE,OAAO,CAAC;IACvB,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,4GAA4G;AAC5G,eAAO,MAAM,iBAAiB,2BAA2B,CAAC;AAE1D,kDAAkD;AAClD,MAAM,WAAW,eAAe;IAC9B,2GAA2G;IAC3G,cAAc,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9C,2DAA2D;IAC3D,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;IACxB,wDAAwD;IACxD,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED;;;;;GAKG;AACH,wBAAsB,mBAAmB,CAAC,CAAC,EACzC,MAAM,EAAE,cAAc,EACtB,SAAS,EAAE,MAAM,EACjB,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,KAAK,GAAE,eAAoB,GAC1B,OAAO,CAAC;IAAE,MAAM,EAAE,CAAC,CAAC;IAAC,UAAU,EAAE,iBAAiB,CAAA;CAAE,CAAC,CAmDvD;AAED;;;;GAIG;AACH,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,cAAc,EACtB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,iBAAiB,GAAG;IAAE,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CA0BnD;AAED,MAAM,WAAW,kBAAkB;IACjC,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,oBAAoB,EAAE,CAAC;CACrC;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CAAC,KAAK,EAAE;IAC9C,MAAM,EAAE,cAAc,CAAC;IACvB,MAAM,EAAE,SAAS,YAAY,EAAE,CAAC;IAChC,KAAK,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,CAAC;IAC1C,aAAa,EAAE,OAAO,CAAC;IACvB,wDAAwD;IACxD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sDAAsD;IACtD,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAsD9B"}
@@ -0,0 +1,371 @@
1
+ import { rewoundRtdbTree, rtdbAuthVariable, rtdbReplaySteps, rtdbStateDrift, } from 'pyric/rules/internal/rtdb';
2
+ import { isRestDenial } from './rest.js';
3
+ /** The comment `pyric verify staging init` puts at the top of the instance's rules. */
4
+ export const STAGING_MARKER_PREFIX = '/* pyric verify staging:';
5
+ export function stagingMarker(projectId, at = new Date()) {
6
+ return `${STAGING_MARKER_PREFIX} this instance is dedicated to \`pyric verify --engine staging\` for project ${projectId}, enrolled ${at.toISOString()}. Its rules and data are replaced while a verification runs. */`;
7
+ }
8
+ /** The project an enrollment marker names, or undefined when the rules carry no marker. */
9
+ export function markedProject(rulesText) {
10
+ const trimmed = rulesText.trimStart();
11
+ if (!trimmed.startsWith(STAGING_MARKER_PREFIX))
12
+ return undefined;
13
+ return /for project ([^\s,]+),/.exec(trimmed.slice(0, trimmed.indexOf('*/') + 2))?.[1];
14
+ }
15
+ export class StagingSafetyError extends Error {
16
+ constructor(message) {
17
+ super(message);
18
+ this.name = 'StagingSafetyError';
19
+ }
20
+ }
21
+ /** Production refused to deploy the candidate rules; its message is the reason. */
22
+ export class StagingDeployRefusedError extends Error {
23
+ instanceLabel;
24
+ production;
25
+ constructor(instanceLabel, production) {
26
+ super(`production refused to deploy the candidate rules for ${instanceLabel}: ${production}`);
27
+ this.instanceLabel = instanceLabel;
28
+ this.production = production;
29
+ this.name = 'StagingDeployRefusedError';
30
+ }
31
+ }
32
+ /** The data node a run holds while it runs; written with a conditional request, so only one run gets it. */
33
+ export const STAGING_LEASE_KEY = '__pyric_verify_lease__';
34
+ /**
35
+ * Run `fn` against the staging instance behind `client`, guarded: refuse an
36
+ * instance without the marker for `projectId`, with data, or with another
37
+ * run's lease; on every exit restore its rules and delete its data, each step
38
+ * attempted on its own and retried, then read both back.
39
+ */
40
+ export async function withStagingInstance(client, projectId, fn, hooks = {}) {
41
+ const priorRules = await client.getRulesText();
42
+ const marked = markedProject(priorRules);
43
+ if (marked === undefined) {
44
+ throw new StagingSafetyError(`${client.databaseUrl} is not enrolled for staging verification (its rules do not start with the enrollment marker). ` +
45
+ 'Run `pyric verify staging init` once to enroll a dedicated, empty instance. If a verification was interrupted, run `pyric verify staging restore`.');
46
+ }
47
+ if (marked !== projectId) {
48
+ throw new StagingSafetyError(`${client.databaseUrl} is enrolled for project ${marked}, not ${projectId}.`);
49
+ }
50
+ const root = await client.get('/', { shallow: true });
51
+ if (root.status !== 200)
52
+ throw new StagingSafetyError(`reading ${client.databaseUrl} failed: HTTP ${root.status} ${root.text.slice(0, 200)}`);
53
+ if (root.body !== null) {
54
+ const leased = typeof root.body === 'object' && root.body !== null && STAGING_LEASE_KEY in root.body;
55
+ throw new StagingSafetyError(leased
56
+ ? `another verification holds ${client.databaseUrl} (its lease is set). Wait for it to finish; if none is running, run \`pyric verify staging restore\`.`
57
+ : `${client.databaseUrl} holds data, so it is not a dedicated staging instance. Refusing to replace it.`);
58
+ }
59
+ const lease = { run: `${process.pid}-${Date.now().toString(36)}`, at: new Date().toISOString() };
60
+ const empty = await client.get(STAGING_LEASE_KEY, { etag: true });
61
+ const taken = empty.etag === undefined
62
+ ? { status: 0, text: 'no ETag', body: null }
63
+ : await client.put(STAGING_LEASE_KEY, lease, { ifMatch: empty.etag });
64
+ if (taken.status !== 200) {
65
+ throw new StagingSafetyError(taken.status === 412
66
+ ? `another verification took ${client.databaseUrl} first. Run one verification at a time per staging instance.`
67
+ : `taking the lease on ${client.databaseUrl} failed: HTTP ${taken.status} ${String(taken.text).slice(0, 200)}`);
68
+ }
69
+ hooks.onBeforeChange?.(priorRules);
70
+ let outcome;
71
+ try {
72
+ outcome = { ok: true, value: await fn() };
73
+ }
74
+ catch (error) {
75
+ outcome = { ok: false, error };
76
+ }
77
+ const invariants = await restoreStagingInstance(client, priorRules);
78
+ if (!invariants.rulesRestored || !invariants.dataRemoved) {
79
+ throw new StagingSafetyError(`${client.databaseUrl} was not left as it was found (rules restored: ${invariants.rulesRestored}, data removed: ${invariants.dataRemoved}${invariants.errors.length > 0 ? `; ${invariants.errors.join('; ')}` : ''}). ` +
80
+ 'Run `pyric verify staging restore` to put it back.');
81
+ }
82
+ hooks.onRestored?.();
83
+ if (!outcome.ok)
84
+ throw outcome.error;
85
+ return { result: outcome.value, invariants: { rulesRestored: true, dataRemoved: true } };
86
+ }
87
+ /**
88
+ * Put a staging instance back: deploy `rulesText` first, since candidate
89
+ * rules may be open, then delete the data; each step on its own, retried,
90
+ * and read back. Never throws: the result says what holds.
91
+ */
92
+ export async function restoreStagingInstance(client, rulesText) {
93
+ const errors = [];
94
+ const attempt = async (what, step) => {
95
+ let last = '';
96
+ for (let tries = 0; tries < 3; tries += 1) {
97
+ try {
98
+ if (await step())
99
+ return true;
100
+ last = 'did not read back';
101
+ }
102
+ catch (error) {
103
+ last = error instanceof Error ? error.message : String(error);
104
+ }
105
+ await new Promise((resolve) => setTimeout(resolve, 250 * (tries + 1)));
106
+ }
107
+ errors.push(`${what}: ${last}`);
108
+ return false;
109
+ };
110
+ const rulesRestored = await attempt('restoring the rules', async () => {
111
+ const put = await client.putRules(rulesText);
112
+ return put.status === 200 && (await client.getRulesText()) === rulesText;
113
+ });
114
+ const dataRemoved = await attempt('deleting the data', async () => {
115
+ const removed = await client.delete('/');
116
+ const back = await client.get('/', { shallow: true });
117
+ return removed.status === 200 && back.status === 200 && back.body === null;
118
+ });
119
+ return { rulesRestored, dataRemoved, errors };
120
+ }
121
+ /**
122
+ * Replay one captured instance's steps against the staging instance under
123
+ * `rules`. Call inside `withStagingInstance`; the instance must be empty.
124
+ */
125
+ export async function runStagingInstance(input) {
126
+ const { client, events } = input;
127
+ const label = input.instance === undefined ? 'the default instance' : `instance "${input.instance}"`;
128
+ const source = JSON.stringify(input.rules, null, 2);
129
+ const dry = await client.putRules(source, { dryRun: true });
130
+ if (dry.status !== 200)
131
+ throw new StagingDeployRefusedError(label, productionMessage(dry));
132
+ const deployed = await client.putRules(source);
133
+ if (deployed.status !== 200)
134
+ throw new StagingDeployRefusedError(label, productionMessage(deployed));
135
+ const lease = (await expectOk(client.get(STAGING_LEASE_KEY), 'reading the lease')).body;
136
+ if (input.capturedState !== undefined) {
137
+ const initial = await rewoundRtdbTree(events, input.capturedState, input.instance);
138
+ if (initial !== null)
139
+ await expectOk(client.put('/', withLease(initial, lease)), 'seeding the session\'s starting tree');
140
+ }
141
+ const divergences = [];
142
+ let checkedEvents = 0;
143
+ const steps = rtdbReplaySteps(events, input.instance);
144
+ for (const [index, step] of steps.entries()) {
145
+ input.signal?.throwIfAborted();
146
+ const before = divergences.length;
147
+ if (step.kind === 'admin-commit') {
148
+ const res = await writeCommit(client, step.event, undefined, lease);
149
+ if (typeof res === 'string')
150
+ divergences.push({ kind: 'unsupported', path: pathOf(step.event), method: step.event.method, reason: res });
151
+ else
152
+ await expectOk(Promise.resolve(res), `the admin ${step.event.method} at ${step.event.path}`);
153
+ }
154
+ else if (step.kind === 'app-commit') {
155
+ checkedEvents += 1;
156
+ const res = await writeCommit(client, step.event, authOverride(step.event), lease);
157
+ if (typeof res === 'string')
158
+ divergences.push({ kind: 'unsupported', path: pathOf(step.event), method: step.event.method, reason: res });
159
+ else if (isRestDenial(res.status))
160
+ divergences.push(denied(step.event, res));
161
+ else if (res.status !== 200)
162
+ divergences.push(unsupported(step.event, res));
163
+ }
164
+ else if (step.kind === 'allowed-read') {
165
+ checkedEvents += 1;
166
+ const query = restQuery(step.event);
167
+ if (typeof query === 'string') {
168
+ divergences.push({ kind: 'unsupported', path: pathOf(step.event), method: step.event.method, reason: query });
169
+ }
170
+ else {
171
+ const res = await client.get(pathOf(step.event), { auth: authOverride(step.event), query });
172
+ if (isRestDenial(res.status))
173
+ divergences.push(denied(step.event, res));
174
+ else if (res.status !== 200)
175
+ divergences.push(unsupported(step.event, res));
176
+ }
177
+ }
178
+ else {
179
+ checkedEvents += 1;
180
+ await decide(client, step.event, step.kind === 'denial' ? 'deny' : 'allow', divergences, lease);
181
+ }
182
+ // Each divergence names the replay step it came from, so engines compare step for step.
183
+ for (const divergence of divergences.slice(before))
184
+ divergence.step = index;
185
+ }
186
+ if (input.capturedState !== undefined) {
187
+ const finalTree = await expectOk(client.get('/'), 'reading the replayed tree');
188
+ divergences.push(...rtdbStateDrift(input.capturedState, withoutLease(finalTree.body)));
189
+ }
190
+ return { checkedEvents, divergences };
191
+ }
192
+ /** A tree with the run's lease kept at the root, so a root write does not release it. */
193
+ function withLease(tree, lease) {
194
+ if (lease === null || lease === undefined)
195
+ return tree;
196
+ if (tree === null || tree === undefined)
197
+ return { [STAGING_LEASE_KEY]: lease };
198
+ if (!isObject(tree))
199
+ return tree;
200
+ return { ...tree, [STAGING_LEASE_KEY]: lease };
201
+ }
202
+ function withoutLease(tree) {
203
+ if (!isObject(tree) || !(STAGING_LEASE_KEY in tree))
204
+ return tree;
205
+ const { [STAGING_LEASE_KEY]: _lease, ...rest } = tree;
206
+ return Object.keys(rest).length === 0 ? null : rest;
207
+ }
208
+ /**
209
+ * Attempt a captured denial or registration as its user. A write that lands
210
+ * is put back as admin from the subtree read before it.
211
+ */
212
+ async function decide(client, event, captured, divergences, lease) {
213
+ const path = pathOf(event);
214
+ const auth = authOverride(event);
215
+ let res;
216
+ if (event.method === 'get' || event.method === 'listen') {
217
+ const query = restQuery(event);
218
+ if (typeof query === 'string') {
219
+ divergences.push({ kind: 'unsupported', path, method: event.method, reason: query });
220
+ return;
221
+ }
222
+ res = await client.get(path, { auth, query });
223
+ }
224
+ else {
225
+ const write = attemptedWrite(event);
226
+ if (typeof write === 'string') {
227
+ divergences.push({ kind: 'unsupported', path, method: event.method, reason: write });
228
+ return;
229
+ }
230
+ const before = await expectOk(client.get(write.restorePath, { format: 'export' }), `reading ${write.restorePath} before the attempt`);
231
+ res = await write.send(client, auth);
232
+ if (res.status === 200) {
233
+ const restored = write.restorePath === '/' ? withLease(before.body, lease) : before.body;
234
+ await expectOk(client.put(write.restorePath, restored), `putting ${write.restorePath} back after the attempt`);
235
+ }
236
+ }
237
+ if (res.status === 200) {
238
+ if (captured === 'deny') {
239
+ divergences.push({
240
+ kind: 'now-allowed',
241
+ path,
242
+ method: event.method,
243
+ reason: 'the captured rules denied this operation; production allows it under the candidate rules.',
244
+ });
245
+ }
246
+ }
247
+ else if (isRestDenial(res.status)) {
248
+ if (captured === 'allow')
249
+ divergences.push(denied(event, res));
250
+ }
251
+ else {
252
+ divergences.push(unsupported(event, res));
253
+ }
254
+ }
255
+ function attemptedWrite(event) {
256
+ const path = pathOf(event);
257
+ const data = event.request?.data;
258
+ const detail = (event.detail ?? {});
259
+ switch (event.method) {
260
+ case 'set':
261
+ case 'transaction':
262
+ return { restorePath: path, send: (client, auth) => client.put(path, withPriority(data ?? null, detail.priority), { auth }) };
263
+ case 'onDisconnect.set':
264
+ return { restorePath: path, send: (client, auth) => client.put(path, withPriority(data ?? null, detail.priority), { auth }) };
265
+ case 'remove':
266
+ case 'onDisconnect.remove':
267
+ return { restorePath: path, send: (client, auth) => client.delete(path, { auth }) };
268
+ case 'update':
269
+ if (!isObject(detail.patch) || typeof detail.rootPath !== 'string') {
270
+ return 'the capture records only the denied path of this update, not the paths written with it, so the update cannot be rebuilt.';
271
+ }
272
+ return { restorePath: detail.rootPath, send: (client, auth) => client.patch(detail.rootPath, detail.patch, { auth }) };
273
+ case 'onDisconnect.update':
274
+ if (!isObject(data))
275
+ return 'the captured onDisconnect update did not contain an object patch.';
276
+ return { restorePath: path, send: (client, auth) => client.patch(path, data, { auth }) };
277
+ case 'setPriority':
278
+ return { restorePath: path, send: (client, auth) => client.put(`${path}/.priority`, data ?? null, { auth }) };
279
+ default:
280
+ return `the staging engine does not check '${event.method}' operations.`;
281
+ }
282
+ }
283
+ /** Perform a captured commit; the reason it cannot be performed when its method is not one replay handles. */
284
+ async function writeCommit(client, commit, auth, lease) {
285
+ const path = commit.path ?? '/';
286
+ const atRoot = path === '/';
287
+ const request = auth === undefined ? {} : { auth };
288
+ const detail = commit.detail ?? {};
289
+ switch (commit.method) {
290
+ case 'update':
291
+ return client.patch(path, commit.data, request);
292
+ case 'remove':
293
+ // Removing the root as admin keeps the lease; a user's removal is decided as a write of nothing.
294
+ return atRoot ? client.put(path, withLease(null, lease), request) : client.delete(path, request);
295
+ case 'setPriority':
296
+ return client.put(`${path}/.priority`, commit.data ?? null, request);
297
+ case 'transaction':
298
+ return client.put(path, atRoot ? withLease(commit.data ?? null, lease) : commit.data ?? null, request);
299
+ case 'set': {
300
+ const value = withPriority(commit.data ?? null, Object.hasOwn(detail, 'priority') ? detail.priority : undefined);
301
+ return client.put(path, atRoot ? withLease(value, lease) : value, request);
302
+ }
303
+ default:
304
+ return `the staging engine does not replay '${commit.method}' commits.`;
305
+ }
306
+ }
307
+ /** A value with a `.priority`, in the shape the REST API reads it; the value itself when there is none. */
308
+ function withPriority(value, priority) {
309
+ if (priority === undefined || priority === null)
310
+ return value;
311
+ if (isObject(value))
312
+ return { ...value, '.priority': priority };
313
+ return { '.value': value, '.priority': priority };
314
+ }
315
+ /** The REST query parameters for a captured query, or the reason REST cannot express it. */
316
+ function restQuery(event) {
317
+ const spec = event.request?.query;
318
+ if (spec === undefined)
319
+ return undefined;
320
+ const query = {};
321
+ const order = spec.orderBy;
322
+ if (order === null) {
323
+ if (spec.bounds.length > 0 || spec.limit !== null)
324
+ query.orderBy = JSON.stringify('$priority');
325
+ }
326
+ else if (order.kind === 'child') {
327
+ query.orderBy = JSON.stringify(order.path);
328
+ }
329
+ else {
330
+ query.orderBy = JSON.stringify(`$${order.kind}`);
331
+ }
332
+ for (const bound of spec.bounds) {
333
+ if (bound.key !== undefined)
334
+ return `the REST API cannot express a ${bound.kind} bound with a key.`;
335
+ if (bound.kind !== 'startAt' && bound.kind !== 'endAt' && bound.kind !== 'equalTo') {
336
+ return `the REST API has no ${bound.kind} parameter.`;
337
+ }
338
+ query[bound.kind] = JSON.stringify(bound.value);
339
+ }
340
+ if (spec.limit !== null)
341
+ query[spec.limit.kind] = String(spec.limit.n);
342
+ return query;
343
+ }
344
+ function authOverride(event) {
345
+ if (event.auth === null)
346
+ return null;
347
+ return rtdbAuthVariable(event.auth, typeof event.at === 'number' ? event.at : Date.now());
348
+ }
349
+ function pathOf(event) {
350
+ return event.path ?? '/';
351
+ }
352
+ function denied(event, res) {
353
+ return { kind: 'now-denied', path: pathOf(event), method: event.method, reason: `production denied it under the candidate rules (HTTP ${res.status}).` };
354
+ }
355
+ function unsupported(event, res) {
356
+ return { kind: 'unsupported', path: pathOf(event), method: event.method, reason: `production answered HTTP ${res.status}: ${productionMessage(res)}` };
357
+ }
358
+ function productionMessage(res) {
359
+ const error = isObject(res.body) ? res.body.error : undefined;
360
+ return (typeof error === 'string' ? error : res.text).slice(0, 500);
361
+ }
362
+ async function expectOk(pending, what) {
363
+ const res = await pending;
364
+ if (res.status !== 200)
365
+ throw new Error(`staging engine: ${what} failed: HTTP ${res.status} ${productionMessage(res)}`);
366
+ return res;
367
+ }
368
+ function isObject(value) {
369
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
370
+ }
371
+ //# sourceMappingURL=engine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.js","sourceRoot":"","sources":["../../../src/verify/staging/engine.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * A minimal Realtime Database REST client for the staging engine: data reads
3
+ * and writes as admin or as an application user (`auth_variable_override`),
4
+ * and the rules endpoint (`/.settings/rules.json`).
5
+ *
6
+ * Production applies an `auth_variable_override` object as the rules `auth`
7
+ * variable, `auth.token` claims included; the identity capture recorded in
8
+ * `packages/conformance/observations/rtdb-staging/rtdb-staging-auth-override.json`
9
+ * pins that for every field the engine passes.
10
+ */
11
+ /** The rules `auth` variable a request runs as; `null` is signed out. Absent is admin. */
12
+ export type RtdbAuthOverride = Record<string, unknown> | null;
13
+ export interface RtdbRestResponse {
14
+ status: number;
15
+ /** Parsed JSON body, or the raw text when it is not JSON. */
16
+ body: unknown;
17
+ text: string;
18
+ /** The `ETag` of the location, when the request asked for it (`etag: true`). */
19
+ etag?: string;
20
+ }
21
+ export interface RtdbRestRequest {
22
+ /** Run as this `auth`; absent runs as admin. */
23
+ auth?: RtdbAuthOverride;
24
+ /** REST query parameters (`orderBy`, `startAt`, ...), already JSON-encoded where REST requires it. */
25
+ query?: Record<string, string>;
26
+ /** `export` returns priorities as `.priority` keys. */
27
+ format?: 'export';
28
+ shallow?: boolean;
29
+ /** Ask for the location's `ETag` (a read), for a later conditional write. */
30
+ etag?: boolean;
31
+ /** Write only if the location's `ETag` is still this one; production answers 412 otherwise. */
32
+ ifMatch?: string;
33
+ }
34
+ export type FetchLike = (url: string, init: {
35
+ method: string;
36
+ headers: Record<string, string>;
37
+ body?: string;
38
+ }) => Promise<{
39
+ status: number;
40
+ text(): Promise<string>;
41
+ headers?: {
42
+ get(name: string): string | null;
43
+ };
44
+ }>;
45
+ export declare class RtdbRestClient {
46
+ private readonly token;
47
+ private readonly fetchImpl;
48
+ readonly databaseUrl: string;
49
+ constructor(databaseUrl: string, token: () => Promise<string>, fetchImpl?: FetchLike);
50
+ get(path: string, request?: RtdbRestRequest): Promise<RtdbRestResponse>;
51
+ put(path: string, value: unknown, request?: RtdbRestRequest): Promise<RtdbRestResponse>;
52
+ patch(path: string, value: unknown, request?: RtdbRestRequest): Promise<RtdbRestResponse>;
53
+ delete(path: string, request?: RtdbRestRequest): Promise<RtdbRestResponse>;
54
+ /** The deployed rules as stored, comments included. */
55
+ getRulesText(): Promise<string>;
56
+ /** Deploy `source` as the rules, or validate it without deploying (`dryRun`). */
57
+ putRules(source: string, options?: {
58
+ dryRun?: boolean;
59
+ }): Promise<RtdbRestResponse>;
60
+ private rules;
61
+ private send;
62
+ private fetchResponse;
63
+ }
64
+ /** `/a/b` for any of `a/b`, `/a/b/`, `a//b`; the root is the empty string. */
65
+ export declare function restPath(path: string): string;
66
+ /** Whether a REST status is the rules denying the request. */
67
+ export declare function isRestDenial(status: number): boolean;
68
+ //# sourceMappingURL=rest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rest.d.ts","sourceRoot":"","sources":["../../../src/verify/staging/rest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,0FAA0F;AAC1F,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;AAE9D,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,6DAA6D;IAC7D,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,gFAAgF;IAChF,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,eAAe;IAC9B,gDAAgD;IAChD,IAAI,CAAC,EAAE,gBAAgB,CAAC;IACxB,sGAAsG;IACtG,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,uDAAuD;IACvD,MAAM,CAAC,EAAE,QAAQ,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,6EAA6E;IAC7E,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,+FAA+F;IAC/F,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,MAAM,SAAS,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,KAAK,OAAO,CAAC;IACzH,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IACxB,OAAO,CAAC,EAAE;QAAE,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;CAChD,CAAC,CAAC;AAEH,qBAAa,cAAc;IAKvB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAL5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;gBAG3B,WAAW,EAAE,MAAM,EACF,KAAK,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,EAC5B,SAAS,GAAE,SAA2C;IAKzE,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAI3E,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAI3F,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAI7F,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAI9E,uDAAuD;IACjD,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC;IAMrC,iFAAiF;IACjF,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE;QAAE,MAAM,CAAC,EAAE,OAAO,CAAA;KAAO,GAAG,OAAO,CAAC,gBAAgB,CAAC;YAIzE,KAAK;YAKL,IAAI;YAcJ,aAAa;CAgB5B;AAED,8EAA8E;AAC9E,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAG7C;AAED,8DAA8D;AAC9D,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAEpD"}