@pyric/cli 0.1.0-alpha.26 → 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 (119) 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/serve/repro/denials.d.ts.map +1 -1
  51. package/dist/serve/repro/denials.js +2 -1
  52. package/dist/serve/repro/denials.js.map +1 -1
  53. package/dist/serve/site-ui/_astro/{app.BpEphDR7.js → app.CiMHEvEW.js} +15 -15
  54. package/dist/serve/site-ui/_astro/{app.DFdWhpVY.js → app.DpzsBfos.js} +1 -1
  55. package/dist/serve/site-ui/_astro/{seed.vvXV1ioI.js → seed.BpXSRzLo.js} +1 -1
  56. package/dist/serve/site-ui/auth/index.html +1 -1
  57. package/dist/serve/site-ui/docs/database-compat/index.html +4 -0
  58. package/dist/serve/site-ui/docs/database-compat.md +4 -0
  59. package/dist/serve/site-ui/docs/get-started/start-building/index.html +1 -0
  60. package/dist/serve/site-ui/docs/get-started/start-building.md +2 -0
  61. package/dist/serve/site-ui/docs/pyric-cli-assurance-reference-api/index.html +23 -4
  62. package/dist/serve/site-ui/docs/pyric-cli-assurance-reference-api.md +11 -4
  63. package/dist/serve/site-ui/docs/pyric-cli-verify-reference-api/index.html +15 -2
  64. package/dist/serve/site-ui/docs/pyric-cli-verify-reference-api.md +11 -10
  65. package/dist/serve/site-ui/docs/secure/write-a-rules-test-suite/index.html +1 -1
  66. package/dist/serve/site-ui/docs/secure/write-a-rules-test-suite.md +1 -1
  67. package/dist/serve/site-ui/docs/ship/ship-to-production/index.html +4 -1
  68. package/dist/serve/site-ui/docs/ship/ship-to-production.md +7 -1
  69. package/dist/serve/site-ui/examples/chess/index.html +1 -1
  70. package/dist/serve/site-ui/firestore/index.html +1 -1
  71. package/dist/serve/site-ui/rtdb/index.html +1 -1
  72. package/dist/serve/site-ui/settings/index.html +1 -1
  73. package/dist/serve/site-ui/storage/index.html +1 -1
  74. package/dist/serve/site-ui/studio/index.html +1 -1
  75. package/dist/serve/site-ui/traffic/index.html +1 -1
  76. package/dist/serve/verify-hint.d.ts +2 -0
  77. package/dist/serve/verify-hint.d.ts.map +1 -0
  78. package/dist/serve/verify-hint.js +10 -0
  79. package/dist/serve/verify-hint.js.map +1 -0
  80. package/dist/serve/vite-generation-rules-watch.d.ts.map +1 -1
  81. package/dist/serve/vite-generation-rules-watch.js +5 -3
  82. package/dist/serve/vite-generation-rules-watch.js.map +1 -1
  83. package/dist/verify/index.d.ts +16 -2
  84. package/dist/verify/index.d.ts.map +1 -1
  85. package/dist/verify/index.js +100 -18
  86. package/dist/verify/index.js.map +1 -1
  87. package/dist/verify/staging/engine.d.ts +102 -0
  88. package/dist/verify/staging/engine.d.ts.map +1 -0
  89. package/dist/verify/staging/engine.js +371 -0
  90. package/dist/verify/staging/engine.js.map +1 -0
  91. package/dist/verify/staging/rest.d.ts +68 -0
  92. package/dist/verify/staging/rest.d.ts.map +1 -0
  93. package/dist/verify/staging/rest.js +93 -0
  94. package/dist/verify/staging/rest.js.map +1 -0
  95. package/dist/verify/staging/target.d.ts +39 -0
  96. package/dist/verify/staging/target.d.ts.map +1 -0
  97. package/dist/verify/staging/target.js +117 -0
  98. package/dist/verify/staging/target.js.map +1 -0
  99. package/package.json +4 -4
  100. package/src/assurance/.generated/conformance-verdicts.ts +5 -0
  101. package/src/assurance/capabilities.ts +22 -4
  102. package/src/assurance/runner.ts +40 -58
  103. package/src/assurance/types.ts +3 -0
  104. package/src/assurance/validation.ts +137 -0
  105. package/src/bridge/surface/arguments/assurance.ts +1 -0
  106. package/src/cli/database-rules.ts +1 -1
  107. package/src/cli/index.ts +13 -2
  108. package/src/cli/pyric-config.ts +5 -0
  109. package/src/cli/serve.ts +6 -3
  110. package/src/cli/verify.ts +244 -18
  111. package/src/conformance/.generated/can-i-use.ts +1 -1
  112. package/src/conformance/.generated/conformance-docs.ts +1 -1
  113. package/src/serve/repro/denials.ts +2 -1
  114. package/src/serve/verify-hint.ts +8 -0
  115. package/src/serve/vite-generation-rules-watch.ts +6 -3
  116. package/src/verify/index.ts +119 -19
  117. package/src/verify/staging/engine.ts +455 -0
  118. package/src/verify/staging/rest.ts +129 -0
  119. package/src/verify/staging/target.ts +137 -0
@@ -6,7 +6,7 @@
6
6
  </script><link rel="preconnect" href="https://use.typekit.net" crossorigin><link rel="stylesheet" href="https://use.typekit.net/jwb3ycu.css"><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;700&display=swap" rel="stylesheet"><link rel="stylesheet" href="/__pyric/ui/_astro/_studio_.CzyEuvVS.css">
7
7
  <style>html{scrollbar-gutter:stable}
8
8
  </style>
9
- <link rel="stylesheet" href="/__pyric/ui/_astro/app.8TtSeQQX.css"></head> <body> <div id="root" data-astro-cid-w5sm6wlz> <style>astro-island,astro-slot,astro-static-slot{display:contents}</style><script>(()=>{var e=async t=>{await(await t())()};(self.Astro||(self.Astro={})).only=e;window.dispatchEvent(new Event("astro:only"));})();</script><script>(()=>{var A=Object.defineProperty;var g=(i,o,a)=>o in i?A(i,o,{enumerable:!0,configurable:!0,writable:!0,value:a}):i[o]=a;var d=(i,o,a)=>g(i,typeof o!="symbol"?o+"":o,a);{let i={0:t=>m(t),1:t=>a(t),2:t=>new RegExp(t),3:t=>new Date(t),4:t=>new Map(a(t)),5:t=>new Set(a(t)),6:t=>BigInt(t),7:t=>new URL(t),8:t=>new Uint8Array(t),9:t=>new Uint16Array(t),10:t=>new Uint32Array(t),11:t=>1/0*t},o=t=>{let[l,e]=t;return l in i?i[l](e):void 0},a=t=>t.map(o),m=t=>typeof t!="object"||t===null?t:Object.fromEntries(Object.entries(t).map(([l,e])=>[l,o(e)]));class y extends HTMLElement{constructor(){super(...arguments);d(this,"Component");d(this,"hydrator");d(this,"hydrate",async()=>{var b;if(!this.hydrator||!this.isConnected)return;let e=(b=this.parentElement)==null?void 0:b.closest("astro-island[ssr]");if(e){e.addEventListener("astro:hydrate",this.hydrate,{once:!0});return}let c=this.querySelectorAll("astro-slot"),n={},h=this.querySelectorAll("template[data-astro-template]");for(let r of h){let s=r.closest(this.tagName);s!=null&&s.isSameNode(this)&&(n[r.getAttribute("data-astro-template")||"default"]=r.innerHTML,r.remove())}for(let r of c){let s=r.closest(this.tagName);s!=null&&s.isSameNode(this)&&(n[r.getAttribute("name")||"default"]=r.innerHTML)}let p;try{p=this.hasAttribute("props")?m(JSON.parse(this.getAttribute("props"))):{}}catch(r){let s=this.getAttribute("component-url")||"<unknown>",v=this.getAttribute("component-export");throw v&&(s+=` (export ${v})`),console.error(`[hydrate] Error parsing props for component ${s}`,this.getAttribute("props"),r),r}let u;await this.hydrator(this)(this.Component,p,n,{client:this.getAttribute("client")}),this.removeAttribute("ssr"),this.dispatchEvent(new CustomEvent("astro:hydrate"))});d(this,"unmount",()=>{this.isConnected||this.dispatchEvent(new CustomEvent("astro:unmount"))})}disconnectedCallback(){document.removeEventListener("astro:after-swap",this.unmount),document.addEventListener("astro:after-swap",this.unmount,{once:!0})}connectedCallback(){if(!this.hasAttribute("await-children")||document.readyState==="interactive"||document.readyState==="complete")this.childrenConnectedCallback();else{let e=()=>{document.removeEventListener("DOMContentLoaded",e),c.disconnect(),this.childrenConnectedCallback()},c=new MutationObserver(()=>{var n;((n=this.lastChild)==null?void 0:n.nodeType)===Node.COMMENT_NODE&&this.lastChild.nodeValue==="astro:end"&&(this.lastChild.remove(),e())});c.observe(this,{childList:!0}),document.addEventListener("DOMContentLoaded",e)}}async childrenConnectedCallback(){let e=this.getAttribute("before-hydration-url");e&&await import(e),this.start()}async start(){let e=JSON.parse(this.getAttribute("opts")),c=this.getAttribute("client");if(Astro[c]===void 0){window.addEventListener(`astro:${c}`,()=>this.start(),{once:!0});return}try{await Astro[c](async()=>{let n=this.getAttribute("renderer-url"),[h,{default:p}]=await Promise.all([import(this.getAttribute("component-url")),n?import(n):()=>()=>{}]),u=this.getAttribute("component-export")||"default";if(!u.includes("."))this.Component=h[u];else{this.Component=h;for(let f of u.split("."))this.Component=this.Component[f]}return this.hydrator=p,this.hydrate},e,this)}catch(n){console.error(`[astro-island] Error hydrating ${this.getAttribute("component-url")}`,n)}}attributeChangedCallback(){this.hydrate()}}d(y,"observedAttributes",["props"]),customElements.get("astro-island")||customElements.define("astro-island",y)}})();</script><astro-island uid="b1btC" component-url="/__pyric/ui/_astro/app.DFdWhpVY.js" component-export="StudioApp" renderer-url="/__pyric/ui/_astro/client.D2ORs4B-.js" props="{&quot;data-astro-cid-w5sm6wlz&quot;:[0,true]}" ssr client="only" opts="{&quot;name&quot;:&quot;StudioApp&quot;,&quot;value&quot;:&quot;react&quot;}" await-children><p class="studio-loading" aria-live="polite" data-astro-cid-w5sm6wlz>
9
+ <link rel="stylesheet" href="/__pyric/ui/_astro/app.8TtSeQQX.css"></head> <body> <div id="root" data-astro-cid-w5sm6wlz> <style>astro-island,astro-slot,astro-static-slot{display:contents}</style><script>(()=>{var e=async t=>{await(await t())()};(self.Astro||(self.Astro={})).only=e;window.dispatchEvent(new Event("astro:only"));})();</script><script>(()=>{var A=Object.defineProperty;var g=(i,o,a)=>o in i?A(i,o,{enumerable:!0,configurable:!0,writable:!0,value:a}):i[o]=a;var d=(i,o,a)=>g(i,typeof o!="symbol"?o+"":o,a);{let i={0:t=>m(t),1:t=>a(t),2:t=>new RegExp(t),3:t=>new Date(t),4:t=>new Map(a(t)),5:t=>new Set(a(t)),6:t=>BigInt(t),7:t=>new URL(t),8:t=>new Uint8Array(t),9:t=>new Uint16Array(t),10:t=>new Uint32Array(t),11:t=>1/0*t},o=t=>{let[l,e]=t;return l in i?i[l](e):void 0},a=t=>t.map(o),m=t=>typeof t!="object"||t===null?t:Object.fromEntries(Object.entries(t).map(([l,e])=>[l,o(e)]));class y extends HTMLElement{constructor(){super(...arguments);d(this,"Component");d(this,"hydrator");d(this,"hydrate",async()=>{var b;if(!this.hydrator||!this.isConnected)return;let e=(b=this.parentElement)==null?void 0:b.closest("astro-island[ssr]");if(e){e.addEventListener("astro:hydrate",this.hydrate,{once:!0});return}let c=this.querySelectorAll("astro-slot"),n={},h=this.querySelectorAll("template[data-astro-template]");for(let r of h){let s=r.closest(this.tagName);s!=null&&s.isSameNode(this)&&(n[r.getAttribute("data-astro-template")||"default"]=r.innerHTML,r.remove())}for(let r of c){let s=r.closest(this.tagName);s!=null&&s.isSameNode(this)&&(n[r.getAttribute("name")||"default"]=r.innerHTML)}let p;try{p=this.hasAttribute("props")?m(JSON.parse(this.getAttribute("props"))):{}}catch(r){let s=this.getAttribute("component-url")||"<unknown>",v=this.getAttribute("component-export");throw v&&(s+=` (export ${v})`),console.error(`[hydrate] Error parsing props for component ${s}`,this.getAttribute("props"),r),r}let u;await this.hydrator(this)(this.Component,p,n,{client:this.getAttribute("client")}),this.removeAttribute("ssr"),this.dispatchEvent(new CustomEvent("astro:hydrate"))});d(this,"unmount",()=>{this.isConnected||this.dispatchEvent(new CustomEvent("astro:unmount"))})}disconnectedCallback(){document.removeEventListener("astro:after-swap",this.unmount),document.addEventListener("astro:after-swap",this.unmount,{once:!0})}connectedCallback(){if(!this.hasAttribute("await-children")||document.readyState==="interactive"||document.readyState==="complete")this.childrenConnectedCallback();else{let e=()=>{document.removeEventListener("DOMContentLoaded",e),c.disconnect(),this.childrenConnectedCallback()},c=new MutationObserver(()=>{var n;((n=this.lastChild)==null?void 0:n.nodeType)===Node.COMMENT_NODE&&this.lastChild.nodeValue==="astro:end"&&(this.lastChild.remove(),e())});c.observe(this,{childList:!0}),document.addEventListener("DOMContentLoaded",e)}}async childrenConnectedCallback(){let e=this.getAttribute("before-hydration-url");e&&await import(e),this.start()}async start(){let e=JSON.parse(this.getAttribute("opts")),c=this.getAttribute("client");if(Astro[c]===void 0){window.addEventListener(`astro:${c}`,()=>this.start(),{once:!0});return}try{await Astro[c](async()=>{let n=this.getAttribute("renderer-url"),[h,{default:p}]=await Promise.all([import(this.getAttribute("component-url")),n?import(n):()=>()=>{}]),u=this.getAttribute("component-export")||"default";if(!u.includes("."))this.Component=h[u];else{this.Component=h;for(let f of u.split("."))this.Component=this.Component[f]}return this.hydrator=p,this.hydrate},e,this)}catch(n){console.error(`[astro-island] Error hydrating ${this.getAttribute("component-url")}`,n)}}attributeChangedCallback(){this.hydrate()}}d(y,"observedAttributes",["props"]),customElements.get("astro-island")||customElements.define("astro-island",y)}})();</script><astro-island uid="b1btC" component-url="/__pyric/ui/_astro/app.DpzsBfos.js" component-export="StudioApp" renderer-url="/__pyric/ui/_astro/client.D2ORs4B-.js" props="{&quot;data-astro-cid-w5sm6wlz&quot;:[0,true]}" ssr client="only" opts="{&quot;name&quot;:&quot;StudioApp&quot;,&quot;value&quot;:&quot;react&quot;}" await-children><p class="studio-loading" aria-live="polite" data-astro-cid-w5sm6wlz>
10
10
  Starting Pyric Studio…
11
11
  </p><template data-astro-template="fallback"><p class="studio-loading" aria-live="polite" data-astro-cid-w5sm6wlz>
12
12
  Starting Pyric Studio…
@@ -6,7 +6,7 @@
6
6
  </script><link rel="preconnect" href="https://use.typekit.net" crossorigin><link rel="stylesheet" href="https://use.typekit.net/jwb3ycu.css"><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;700&display=swap" rel="stylesheet"><link rel="stylesheet" href="/__pyric/ui/_astro/_studio_.CzyEuvVS.css">
7
7
  <style>html{scrollbar-gutter:stable}
8
8
  </style>
9
- <link rel="stylesheet" href="/__pyric/ui/_astro/app.8TtSeQQX.css"></head> <body> <div id="root" data-astro-cid-w5sm6wlz> <style>astro-island,astro-slot,astro-static-slot{display:contents}</style><script>(()=>{var e=async t=>{await(await t())()};(self.Astro||(self.Astro={})).only=e;window.dispatchEvent(new Event("astro:only"));})();</script><script>(()=>{var A=Object.defineProperty;var g=(i,o,a)=>o in i?A(i,o,{enumerable:!0,configurable:!0,writable:!0,value:a}):i[o]=a;var d=(i,o,a)=>g(i,typeof o!="symbol"?o+"":o,a);{let i={0:t=>m(t),1:t=>a(t),2:t=>new RegExp(t),3:t=>new Date(t),4:t=>new Map(a(t)),5:t=>new Set(a(t)),6:t=>BigInt(t),7:t=>new URL(t),8:t=>new Uint8Array(t),9:t=>new Uint16Array(t),10:t=>new Uint32Array(t),11:t=>1/0*t},o=t=>{let[l,e]=t;return l in i?i[l](e):void 0},a=t=>t.map(o),m=t=>typeof t!="object"||t===null?t:Object.fromEntries(Object.entries(t).map(([l,e])=>[l,o(e)]));class y extends HTMLElement{constructor(){super(...arguments);d(this,"Component");d(this,"hydrator");d(this,"hydrate",async()=>{var b;if(!this.hydrator||!this.isConnected)return;let e=(b=this.parentElement)==null?void 0:b.closest("astro-island[ssr]");if(e){e.addEventListener("astro:hydrate",this.hydrate,{once:!0});return}let c=this.querySelectorAll("astro-slot"),n={},h=this.querySelectorAll("template[data-astro-template]");for(let r of h){let s=r.closest(this.tagName);s!=null&&s.isSameNode(this)&&(n[r.getAttribute("data-astro-template")||"default"]=r.innerHTML,r.remove())}for(let r of c){let s=r.closest(this.tagName);s!=null&&s.isSameNode(this)&&(n[r.getAttribute("name")||"default"]=r.innerHTML)}let p;try{p=this.hasAttribute("props")?m(JSON.parse(this.getAttribute("props"))):{}}catch(r){let s=this.getAttribute("component-url")||"<unknown>",v=this.getAttribute("component-export");throw v&&(s+=` (export ${v})`),console.error(`[hydrate] Error parsing props for component ${s}`,this.getAttribute("props"),r),r}let u;await this.hydrator(this)(this.Component,p,n,{client:this.getAttribute("client")}),this.removeAttribute("ssr"),this.dispatchEvent(new CustomEvent("astro:hydrate"))});d(this,"unmount",()=>{this.isConnected||this.dispatchEvent(new CustomEvent("astro:unmount"))})}disconnectedCallback(){document.removeEventListener("astro:after-swap",this.unmount),document.addEventListener("astro:after-swap",this.unmount,{once:!0})}connectedCallback(){if(!this.hasAttribute("await-children")||document.readyState==="interactive"||document.readyState==="complete")this.childrenConnectedCallback();else{let e=()=>{document.removeEventListener("DOMContentLoaded",e),c.disconnect(),this.childrenConnectedCallback()},c=new MutationObserver(()=>{var n;((n=this.lastChild)==null?void 0:n.nodeType)===Node.COMMENT_NODE&&this.lastChild.nodeValue==="astro:end"&&(this.lastChild.remove(),e())});c.observe(this,{childList:!0}),document.addEventListener("DOMContentLoaded",e)}}async childrenConnectedCallback(){let e=this.getAttribute("before-hydration-url");e&&await import(e),this.start()}async start(){let e=JSON.parse(this.getAttribute("opts")),c=this.getAttribute("client");if(Astro[c]===void 0){window.addEventListener(`astro:${c}`,()=>this.start(),{once:!0});return}try{await Astro[c](async()=>{let n=this.getAttribute("renderer-url"),[h,{default:p}]=await Promise.all([import(this.getAttribute("component-url")),n?import(n):()=>()=>{}]),u=this.getAttribute("component-export")||"default";if(!u.includes("."))this.Component=h[u];else{this.Component=h;for(let f of u.split("."))this.Component=this.Component[f]}return this.hydrator=p,this.hydrate},e,this)}catch(n){console.error(`[astro-island] Error hydrating ${this.getAttribute("component-url")}`,n)}}attributeChangedCallback(){this.hydrate()}}d(y,"observedAttributes",["props"]),customElements.get("astro-island")||customElements.define("astro-island",y)}})();</script><astro-island uid="b1btC" component-url="/__pyric/ui/_astro/app.DFdWhpVY.js" component-export="StudioApp" renderer-url="/__pyric/ui/_astro/client.D2ORs4B-.js" props="{&quot;data-astro-cid-w5sm6wlz&quot;:[0,true]}" ssr client="only" opts="{&quot;name&quot;:&quot;StudioApp&quot;,&quot;value&quot;:&quot;react&quot;}" await-children><p class="studio-loading" aria-live="polite" data-astro-cid-w5sm6wlz>
9
+ <link rel="stylesheet" href="/__pyric/ui/_astro/app.8TtSeQQX.css"></head> <body> <div id="root" data-astro-cid-w5sm6wlz> <style>astro-island,astro-slot,astro-static-slot{display:contents}</style><script>(()=>{var e=async t=>{await(await t())()};(self.Astro||(self.Astro={})).only=e;window.dispatchEvent(new Event("astro:only"));})();</script><script>(()=>{var A=Object.defineProperty;var g=(i,o,a)=>o in i?A(i,o,{enumerable:!0,configurable:!0,writable:!0,value:a}):i[o]=a;var d=(i,o,a)=>g(i,typeof o!="symbol"?o+"":o,a);{let i={0:t=>m(t),1:t=>a(t),2:t=>new RegExp(t),3:t=>new Date(t),4:t=>new Map(a(t)),5:t=>new Set(a(t)),6:t=>BigInt(t),7:t=>new URL(t),8:t=>new Uint8Array(t),9:t=>new Uint16Array(t),10:t=>new Uint32Array(t),11:t=>1/0*t},o=t=>{let[l,e]=t;return l in i?i[l](e):void 0},a=t=>t.map(o),m=t=>typeof t!="object"||t===null?t:Object.fromEntries(Object.entries(t).map(([l,e])=>[l,o(e)]));class y extends HTMLElement{constructor(){super(...arguments);d(this,"Component");d(this,"hydrator");d(this,"hydrate",async()=>{var b;if(!this.hydrator||!this.isConnected)return;let e=(b=this.parentElement)==null?void 0:b.closest("astro-island[ssr]");if(e){e.addEventListener("astro:hydrate",this.hydrate,{once:!0});return}let c=this.querySelectorAll("astro-slot"),n={},h=this.querySelectorAll("template[data-astro-template]");for(let r of h){let s=r.closest(this.tagName);s!=null&&s.isSameNode(this)&&(n[r.getAttribute("data-astro-template")||"default"]=r.innerHTML,r.remove())}for(let r of c){let s=r.closest(this.tagName);s!=null&&s.isSameNode(this)&&(n[r.getAttribute("name")||"default"]=r.innerHTML)}let p;try{p=this.hasAttribute("props")?m(JSON.parse(this.getAttribute("props"))):{}}catch(r){let s=this.getAttribute("component-url")||"<unknown>",v=this.getAttribute("component-export");throw v&&(s+=` (export ${v})`),console.error(`[hydrate] Error parsing props for component ${s}`,this.getAttribute("props"),r),r}let u;await this.hydrator(this)(this.Component,p,n,{client:this.getAttribute("client")}),this.removeAttribute("ssr"),this.dispatchEvent(new CustomEvent("astro:hydrate"))});d(this,"unmount",()=>{this.isConnected||this.dispatchEvent(new CustomEvent("astro:unmount"))})}disconnectedCallback(){document.removeEventListener("astro:after-swap",this.unmount),document.addEventListener("astro:after-swap",this.unmount,{once:!0})}connectedCallback(){if(!this.hasAttribute("await-children")||document.readyState==="interactive"||document.readyState==="complete")this.childrenConnectedCallback();else{let e=()=>{document.removeEventListener("DOMContentLoaded",e),c.disconnect(),this.childrenConnectedCallback()},c=new MutationObserver(()=>{var n;((n=this.lastChild)==null?void 0:n.nodeType)===Node.COMMENT_NODE&&this.lastChild.nodeValue==="astro:end"&&(this.lastChild.remove(),e())});c.observe(this,{childList:!0}),document.addEventListener("DOMContentLoaded",e)}}async childrenConnectedCallback(){let e=this.getAttribute("before-hydration-url");e&&await import(e),this.start()}async start(){let e=JSON.parse(this.getAttribute("opts")),c=this.getAttribute("client");if(Astro[c]===void 0){window.addEventListener(`astro:${c}`,()=>this.start(),{once:!0});return}try{await Astro[c](async()=>{let n=this.getAttribute("renderer-url"),[h,{default:p}]=await Promise.all([import(this.getAttribute("component-url")),n?import(n):()=>()=>{}]),u=this.getAttribute("component-export")||"default";if(!u.includes("."))this.Component=h[u];else{this.Component=h;for(let f of u.split("."))this.Component=this.Component[f]}return this.hydrator=p,this.hydrate},e,this)}catch(n){console.error(`[astro-island] Error hydrating ${this.getAttribute("component-url")}`,n)}}attributeChangedCallback(){this.hydrate()}}d(y,"observedAttributes",["props"]),customElements.get("astro-island")||customElements.define("astro-island",y)}})();</script><astro-island uid="b1btC" component-url="/__pyric/ui/_astro/app.DpzsBfos.js" component-export="StudioApp" renderer-url="/__pyric/ui/_astro/client.D2ORs4B-.js" props="{&quot;data-astro-cid-w5sm6wlz&quot;:[0,true]}" ssr client="only" opts="{&quot;name&quot;:&quot;StudioApp&quot;,&quot;value&quot;:&quot;react&quot;}" await-children><p class="studio-loading" aria-live="polite" data-astro-cid-w5sm6wlz>
10
10
  Starting Pyric Studio…
11
11
  </p><template data-astro-template="fallback"><p class="studio-loading" aria-live="polite" data-astro-cid-w5sm6wlz>
12
12
  Starting Pyric Studio…
@@ -6,7 +6,7 @@
6
6
  </script><link rel="preconnect" href="https://use.typekit.net" crossorigin><link rel="stylesheet" href="https://use.typekit.net/jwb3ycu.css"><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;700&display=swap" rel="stylesheet"><link rel="stylesheet" href="/__pyric/ui/_astro/_studio_.CzyEuvVS.css">
7
7
  <style>html{scrollbar-gutter:stable}
8
8
  </style>
9
- <link rel="stylesheet" href="/__pyric/ui/_astro/app.8TtSeQQX.css"></head> <body> <div id="root" data-astro-cid-w5sm6wlz> <style>astro-island,astro-slot,astro-static-slot{display:contents}</style><script>(()=>{var e=async t=>{await(await t())()};(self.Astro||(self.Astro={})).only=e;window.dispatchEvent(new Event("astro:only"));})();</script><script>(()=>{var A=Object.defineProperty;var g=(i,o,a)=>o in i?A(i,o,{enumerable:!0,configurable:!0,writable:!0,value:a}):i[o]=a;var d=(i,o,a)=>g(i,typeof o!="symbol"?o+"":o,a);{let i={0:t=>m(t),1:t=>a(t),2:t=>new RegExp(t),3:t=>new Date(t),4:t=>new Map(a(t)),5:t=>new Set(a(t)),6:t=>BigInt(t),7:t=>new URL(t),8:t=>new Uint8Array(t),9:t=>new Uint16Array(t),10:t=>new Uint32Array(t),11:t=>1/0*t},o=t=>{let[l,e]=t;return l in i?i[l](e):void 0},a=t=>t.map(o),m=t=>typeof t!="object"||t===null?t:Object.fromEntries(Object.entries(t).map(([l,e])=>[l,o(e)]));class y extends HTMLElement{constructor(){super(...arguments);d(this,"Component");d(this,"hydrator");d(this,"hydrate",async()=>{var b;if(!this.hydrator||!this.isConnected)return;let e=(b=this.parentElement)==null?void 0:b.closest("astro-island[ssr]");if(e){e.addEventListener("astro:hydrate",this.hydrate,{once:!0});return}let c=this.querySelectorAll("astro-slot"),n={},h=this.querySelectorAll("template[data-astro-template]");for(let r of h){let s=r.closest(this.tagName);s!=null&&s.isSameNode(this)&&(n[r.getAttribute("data-astro-template")||"default"]=r.innerHTML,r.remove())}for(let r of c){let s=r.closest(this.tagName);s!=null&&s.isSameNode(this)&&(n[r.getAttribute("name")||"default"]=r.innerHTML)}let p;try{p=this.hasAttribute("props")?m(JSON.parse(this.getAttribute("props"))):{}}catch(r){let s=this.getAttribute("component-url")||"<unknown>",v=this.getAttribute("component-export");throw v&&(s+=` (export ${v})`),console.error(`[hydrate] Error parsing props for component ${s}`,this.getAttribute("props"),r),r}let u;await this.hydrator(this)(this.Component,p,n,{client:this.getAttribute("client")}),this.removeAttribute("ssr"),this.dispatchEvent(new CustomEvent("astro:hydrate"))});d(this,"unmount",()=>{this.isConnected||this.dispatchEvent(new CustomEvent("astro:unmount"))})}disconnectedCallback(){document.removeEventListener("astro:after-swap",this.unmount),document.addEventListener("astro:after-swap",this.unmount,{once:!0})}connectedCallback(){if(!this.hasAttribute("await-children")||document.readyState==="interactive"||document.readyState==="complete")this.childrenConnectedCallback();else{let e=()=>{document.removeEventListener("DOMContentLoaded",e),c.disconnect(),this.childrenConnectedCallback()},c=new MutationObserver(()=>{var n;((n=this.lastChild)==null?void 0:n.nodeType)===Node.COMMENT_NODE&&this.lastChild.nodeValue==="astro:end"&&(this.lastChild.remove(),e())});c.observe(this,{childList:!0}),document.addEventListener("DOMContentLoaded",e)}}async childrenConnectedCallback(){let e=this.getAttribute("before-hydration-url");e&&await import(e),this.start()}async start(){let e=JSON.parse(this.getAttribute("opts")),c=this.getAttribute("client");if(Astro[c]===void 0){window.addEventListener(`astro:${c}`,()=>this.start(),{once:!0});return}try{await Astro[c](async()=>{let n=this.getAttribute("renderer-url"),[h,{default:p}]=await Promise.all([import(this.getAttribute("component-url")),n?import(n):()=>()=>{}]),u=this.getAttribute("component-export")||"default";if(!u.includes("."))this.Component=h[u];else{this.Component=h;for(let f of u.split("."))this.Component=this.Component[f]}return this.hydrator=p,this.hydrate},e,this)}catch(n){console.error(`[astro-island] Error hydrating ${this.getAttribute("component-url")}`,n)}}attributeChangedCallback(){this.hydrate()}}d(y,"observedAttributes",["props"]),customElements.get("astro-island")||customElements.define("astro-island",y)}})();</script><astro-island uid="b1btC" component-url="/__pyric/ui/_astro/app.DFdWhpVY.js" component-export="StudioApp" renderer-url="/__pyric/ui/_astro/client.D2ORs4B-.js" props="{&quot;data-astro-cid-w5sm6wlz&quot;:[0,true]}" ssr client="only" opts="{&quot;name&quot;:&quot;StudioApp&quot;,&quot;value&quot;:&quot;react&quot;}" await-children><p class="studio-loading" aria-live="polite" data-astro-cid-w5sm6wlz>
9
+ <link rel="stylesheet" href="/__pyric/ui/_astro/app.8TtSeQQX.css"></head> <body> <div id="root" data-astro-cid-w5sm6wlz> <style>astro-island,astro-slot,astro-static-slot{display:contents}</style><script>(()=>{var e=async t=>{await(await t())()};(self.Astro||(self.Astro={})).only=e;window.dispatchEvent(new Event("astro:only"));})();</script><script>(()=>{var A=Object.defineProperty;var g=(i,o,a)=>o in i?A(i,o,{enumerable:!0,configurable:!0,writable:!0,value:a}):i[o]=a;var d=(i,o,a)=>g(i,typeof o!="symbol"?o+"":o,a);{let i={0:t=>m(t),1:t=>a(t),2:t=>new RegExp(t),3:t=>new Date(t),4:t=>new Map(a(t)),5:t=>new Set(a(t)),6:t=>BigInt(t),7:t=>new URL(t),8:t=>new Uint8Array(t),9:t=>new Uint16Array(t),10:t=>new Uint32Array(t),11:t=>1/0*t},o=t=>{let[l,e]=t;return l in i?i[l](e):void 0},a=t=>t.map(o),m=t=>typeof t!="object"||t===null?t:Object.fromEntries(Object.entries(t).map(([l,e])=>[l,o(e)]));class y extends HTMLElement{constructor(){super(...arguments);d(this,"Component");d(this,"hydrator");d(this,"hydrate",async()=>{var b;if(!this.hydrator||!this.isConnected)return;let e=(b=this.parentElement)==null?void 0:b.closest("astro-island[ssr]");if(e){e.addEventListener("astro:hydrate",this.hydrate,{once:!0});return}let c=this.querySelectorAll("astro-slot"),n={},h=this.querySelectorAll("template[data-astro-template]");for(let r of h){let s=r.closest(this.tagName);s!=null&&s.isSameNode(this)&&(n[r.getAttribute("data-astro-template")||"default"]=r.innerHTML,r.remove())}for(let r of c){let s=r.closest(this.tagName);s!=null&&s.isSameNode(this)&&(n[r.getAttribute("name")||"default"]=r.innerHTML)}let p;try{p=this.hasAttribute("props")?m(JSON.parse(this.getAttribute("props"))):{}}catch(r){let s=this.getAttribute("component-url")||"<unknown>",v=this.getAttribute("component-export");throw v&&(s+=` (export ${v})`),console.error(`[hydrate] Error parsing props for component ${s}`,this.getAttribute("props"),r),r}let u;await this.hydrator(this)(this.Component,p,n,{client:this.getAttribute("client")}),this.removeAttribute("ssr"),this.dispatchEvent(new CustomEvent("astro:hydrate"))});d(this,"unmount",()=>{this.isConnected||this.dispatchEvent(new CustomEvent("astro:unmount"))})}disconnectedCallback(){document.removeEventListener("astro:after-swap",this.unmount),document.addEventListener("astro:after-swap",this.unmount,{once:!0})}connectedCallback(){if(!this.hasAttribute("await-children")||document.readyState==="interactive"||document.readyState==="complete")this.childrenConnectedCallback();else{let e=()=>{document.removeEventListener("DOMContentLoaded",e),c.disconnect(),this.childrenConnectedCallback()},c=new MutationObserver(()=>{var n;((n=this.lastChild)==null?void 0:n.nodeType)===Node.COMMENT_NODE&&this.lastChild.nodeValue==="astro:end"&&(this.lastChild.remove(),e())});c.observe(this,{childList:!0}),document.addEventListener("DOMContentLoaded",e)}}async childrenConnectedCallback(){let e=this.getAttribute("before-hydration-url");e&&await import(e),this.start()}async start(){let e=JSON.parse(this.getAttribute("opts")),c=this.getAttribute("client");if(Astro[c]===void 0){window.addEventListener(`astro:${c}`,()=>this.start(),{once:!0});return}try{await Astro[c](async()=>{let n=this.getAttribute("renderer-url"),[h,{default:p}]=await Promise.all([import(this.getAttribute("component-url")),n?import(n):()=>()=>{}]),u=this.getAttribute("component-export")||"default";if(!u.includes("."))this.Component=h[u];else{this.Component=h;for(let f of u.split("."))this.Component=this.Component[f]}return this.hydrator=p,this.hydrate},e,this)}catch(n){console.error(`[astro-island] Error hydrating ${this.getAttribute("component-url")}`,n)}}attributeChangedCallback(){this.hydrate()}}d(y,"observedAttributes",["props"]),customElements.get("astro-island")||customElements.define("astro-island",y)}})();</script><astro-island uid="b1btC" component-url="/__pyric/ui/_astro/app.DpzsBfos.js" component-export="StudioApp" renderer-url="/__pyric/ui/_astro/client.D2ORs4B-.js" props="{&quot;data-astro-cid-w5sm6wlz&quot;:[0,true]}" ssr client="only" opts="{&quot;name&quot;:&quot;StudioApp&quot;,&quot;value&quot;:&quot;react&quot;}" await-children><p class="studio-loading" aria-live="polite" data-astro-cid-w5sm6wlz>
10
10
  Starting Pyric Studio…
11
11
  </p><template data-astro-template="fallback"><p class="studio-loading" aria-live="polite" data-astro-cid-w5sm6wlz>
12
12
  Starting Pyric Studio…
@@ -6,7 +6,7 @@
6
6
  </script><link rel="preconnect" href="https://use.typekit.net" crossorigin><link rel="stylesheet" href="https://use.typekit.net/jwb3ycu.css"><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;700&display=swap" rel="stylesheet"><link rel="stylesheet" href="/__pyric/ui/_astro/_studio_.CzyEuvVS.css">
7
7
  <style>html{scrollbar-gutter:stable}
8
8
  </style>
9
- <link rel="stylesheet" href="/__pyric/ui/_astro/app.8TtSeQQX.css"></head> <body> <div id="root" data-astro-cid-w5sm6wlz> <style>astro-island,astro-slot,astro-static-slot{display:contents}</style><script>(()=>{var e=async t=>{await(await t())()};(self.Astro||(self.Astro={})).only=e;window.dispatchEvent(new Event("astro:only"));})();</script><script>(()=>{var A=Object.defineProperty;var g=(i,o,a)=>o in i?A(i,o,{enumerable:!0,configurable:!0,writable:!0,value:a}):i[o]=a;var d=(i,o,a)=>g(i,typeof o!="symbol"?o+"":o,a);{let i={0:t=>m(t),1:t=>a(t),2:t=>new RegExp(t),3:t=>new Date(t),4:t=>new Map(a(t)),5:t=>new Set(a(t)),6:t=>BigInt(t),7:t=>new URL(t),8:t=>new Uint8Array(t),9:t=>new Uint16Array(t),10:t=>new Uint32Array(t),11:t=>1/0*t},o=t=>{let[l,e]=t;return l in i?i[l](e):void 0},a=t=>t.map(o),m=t=>typeof t!="object"||t===null?t:Object.fromEntries(Object.entries(t).map(([l,e])=>[l,o(e)]));class y extends HTMLElement{constructor(){super(...arguments);d(this,"Component");d(this,"hydrator");d(this,"hydrate",async()=>{var b;if(!this.hydrator||!this.isConnected)return;let e=(b=this.parentElement)==null?void 0:b.closest("astro-island[ssr]");if(e){e.addEventListener("astro:hydrate",this.hydrate,{once:!0});return}let c=this.querySelectorAll("astro-slot"),n={},h=this.querySelectorAll("template[data-astro-template]");for(let r of h){let s=r.closest(this.tagName);s!=null&&s.isSameNode(this)&&(n[r.getAttribute("data-astro-template")||"default"]=r.innerHTML,r.remove())}for(let r of c){let s=r.closest(this.tagName);s!=null&&s.isSameNode(this)&&(n[r.getAttribute("name")||"default"]=r.innerHTML)}let p;try{p=this.hasAttribute("props")?m(JSON.parse(this.getAttribute("props"))):{}}catch(r){let s=this.getAttribute("component-url")||"<unknown>",v=this.getAttribute("component-export");throw v&&(s+=` (export ${v})`),console.error(`[hydrate] Error parsing props for component ${s}`,this.getAttribute("props"),r),r}let u;await this.hydrator(this)(this.Component,p,n,{client:this.getAttribute("client")}),this.removeAttribute("ssr"),this.dispatchEvent(new CustomEvent("astro:hydrate"))});d(this,"unmount",()=>{this.isConnected||this.dispatchEvent(new CustomEvent("astro:unmount"))})}disconnectedCallback(){document.removeEventListener("astro:after-swap",this.unmount),document.addEventListener("astro:after-swap",this.unmount,{once:!0})}connectedCallback(){if(!this.hasAttribute("await-children")||document.readyState==="interactive"||document.readyState==="complete")this.childrenConnectedCallback();else{let e=()=>{document.removeEventListener("DOMContentLoaded",e),c.disconnect(),this.childrenConnectedCallback()},c=new MutationObserver(()=>{var n;((n=this.lastChild)==null?void 0:n.nodeType)===Node.COMMENT_NODE&&this.lastChild.nodeValue==="astro:end"&&(this.lastChild.remove(),e())});c.observe(this,{childList:!0}),document.addEventListener("DOMContentLoaded",e)}}async childrenConnectedCallback(){let e=this.getAttribute("before-hydration-url");e&&await import(e),this.start()}async start(){let e=JSON.parse(this.getAttribute("opts")),c=this.getAttribute("client");if(Astro[c]===void 0){window.addEventListener(`astro:${c}`,()=>this.start(),{once:!0});return}try{await Astro[c](async()=>{let n=this.getAttribute("renderer-url"),[h,{default:p}]=await Promise.all([import(this.getAttribute("component-url")),n?import(n):()=>()=>{}]),u=this.getAttribute("component-export")||"default";if(!u.includes("."))this.Component=h[u];else{this.Component=h;for(let f of u.split("."))this.Component=this.Component[f]}return this.hydrator=p,this.hydrate},e,this)}catch(n){console.error(`[astro-island] Error hydrating ${this.getAttribute("component-url")}`,n)}}attributeChangedCallback(){this.hydrate()}}d(y,"observedAttributes",["props"]),customElements.get("astro-island")||customElements.define("astro-island",y)}})();</script><astro-island uid="b1btC" component-url="/__pyric/ui/_astro/app.DFdWhpVY.js" component-export="StudioApp" renderer-url="/__pyric/ui/_astro/client.D2ORs4B-.js" props="{&quot;data-astro-cid-w5sm6wlz&quot;:[0,true]}" ssr client="only" opts="{&quot;name&quot;:&quot;StudioApp&quot;,&quot;value&quot;:&quot;react&quot;}" await-children><p class="studio-loading" aria-live="polite" data-astro-cid-w5sm6wlz>
9
+ <link rel="stylesheet" href="/__pyric/ui/_astro/app.8TtSeQQX.css"></head> <body> <div id="root" data-astro-cid-w5sm6wlz> <style>astro-island,astro-slot,astro-static-slot{display:contents}</style><script>(()=>{var e=async t=>{await(await t())()};(self.Astro||(self.Astro={})).only=e;window.dispatchEvent(new Event("astro:only"));})();</script><script>(()=>{var A=Object.defineProperty;var g=(i,o,a)=>o in i?A(i,o,{enumerable:!0,configurable:!0,writable:!0,value:a}):i[o]=a;var d=(i,o,a)=>g(i,typeof o!="symbol"?o+"":o,a);{let i={0:t=>m(t),1:t=>a(t),2:t=>new RegExp(t),3:t=>new Date(t),4:t=>new Map(a(t)),5:t=>new Set(a(t)),6:t=>BigInt(t),7:t=>new URL(t),8:t=>new Uint8Array(t),9:t=>new Uint16Array(t),10:t=>new Uint32Array(t),11:t=>1/0*t},o=t=>{let[l,e]=t;return l in i?i[l](e):void 0},a=t=>t.map(o),m=t=>typeof t!="object"||t===null?t:Object.fromEntries(Object.entries(t).map(([l,e])=>[l,o(e)]));class y extends HTMLElement{constructor(){super(...arguments);d(this,"Component");d(this,"hydrator");d(this,"hydrate",async()=>{var b;if(!this.hydrator||!this.isConnected)return;let e=(b=this.parentElement)==null?void 0:b.closest("astro-island[ssr]");if(e){e.addEventListener("astro:hydrate",this.hydrate,{once:!0});return}let c=this.querySelectorAll("astro-slot"),n={},h=this.querySelectorAll("template[data-astro-template]");for(let r of h){let s=r.closest(this.tagName);s!=null&&s.isSameNode(this)&&(n[r.getAttribute("data-astro-template")||"default"]=r.innerHTML,r.remove())}for(let r of c){let s=r.closest(this.tagName);s!=null&&s.isSameNode(this)&&(n[r.getAttribute("name")||"default"]=r.innerHTML)}let p;try{p=this.hasAttribute("props")?m(JSON.parse(this.getAttribute("props"))):{}}catch(r){let s=this.getAttribute("component-url")||"<unknown>",v=this.getAttribute("component-export");throw v&&(s+=` (export ${v})`),console.error(`[hydrate] Error parsing props for component ${s}`,this.getAttribute("props"),r),r}let u;await this.hydrator(this)(this.Component,p,n,{client:this.getAttribute("client")}),this.removeAttribute("ssr"),this.dispatchEvent(new CustomEvent("astro:hydrate"))});d(this,"unmount",()=>{this.isConnected||this.dispatchEvent(new CustomEvent("astro:unmount"))})}disconnectedCallback(){document.removeEventListener("astro:after-swap",this.unmount),document.addEventListener("astro:after-swap",this.unmount,{once:!0})}connectedCallback(){if(!this.hasAttribute("await-children")||document.readyState==="interactive"||document.readyState==="complete")this.childrenConnectedCallback();else{let e=()=>{document.removeEventListener("DOMContentLoaded",e),c.disconnect(),this.childrenConnectedCallback()},c=new MutationObserver(()=>{var n;((n=this.lastChild)==null?void 0:n.nodeType)===Node.COMMENT_NODE&&this.lastChild.nodeValue==="astro:end"&&(this.lastChild.remove(),e())});c.observe(this,{childList:!0}),document.addEventListener("DOMContentLoaded",e)}}async childrenConnectedCallback(){let e=this.getAttribute("before-hydration-url");e&&await import(e),this.start()}async start(){let e=JSON.parse(this.getAttribute("opts")),c=this.getAttribute("client");if(Astro[c]===void 0){window.addEventListener(`astro:${c}`,()=>this.start(),{once:!0});return}try{await Astro[c](async()=>{let n=this.getAttribute("renderer-url"),[h,{default:p}]=await Promise.all([import(this.getAttribute("component-url")),n?import(n):()=>()=>{}]),u=this.getAttribute("component-export")||"default";if(!u.includes("."))this.Component=h[u];else{this.Component=h;for(let f of u.split("."))this.Component=this.Component[f]}return this.hydrator=p,this.hydrate},e,this)}catch(n){console.error(`[astro-island] Error hydrating ${this.getAttribute("component-url")}`,n)}}attributeChangedCallback(){this.hydrate()}}d(y,"observedAttributes",["props"]),customElements.get("astro-island")||customElements.define("astro-island",y)}})();</script><astro-island uid="b1btC" component-url="/__pyric/ui/_astro/app.DpzsBfos.js" component-export="StudioApp" renderer-url="/__pyric/ui/_astro/client.D2ORs4B-.js" props="{&quot;data-astro-cid-w5sm6wlz&quot;:[0,true]}" ssr client="only" opts="{&quot;name&quot;:&quot;StudioApp&quot;,&quot;value&quot;:&quot;react&quot;}" await-children><p class="studio-loading" aria-live="polite" data-astro-cid-w5sm6wlz>
10
10
  Starting Pyric Studio…
11
11
  </p><template data-astro-template="fallback"><p class="studio-loading" aria-live="polite" data-astro-cid-w5sm6wlz>
12
12
  Starting Pyric Studio…
@@ -6,7 +6,7 @@
6
6
  </script><link rel="preconnect" href="https://use.typekit.net" crossorigin><link rel="stylesheet" href="https://use.typekit.net/jwb3ycu.css"><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;700&display=swap" rel="stylesheet"><link rel="stylesheet" href="/__pyric/ui/_astro/_studio_.CzyEuvVS.css">
7
7
  <style>html{scrollbar-gutter:stable}
8
8
  </style>
9
- <link rel="stylesheet" href="/__pyric/ui/_astro/app.8TtSeQQX.css"></head> <body> <div id="root" data-astro-cid-w5sm6wlz> <style>astro-island,astro-slot,astro-static-slot{display:contents}</style><script>(()=>{var e=async t=>{await(await t())()};(self.Astro||(self.Astro={})).only=e;window.dispatchEvent(new Event("astro:only"));})();</script><script>(()=>{var A=Object.defineProperty;var g=(i,o,a)=>o in i?A(i,o,{enumerable:!0,configurable:!0,writable:!0,value:a}):i[o]=a;var d=(i,o,a)=>g(i,typeof o!="symbol"?o+"":o,a);{let i={0:t=>m(t),1:t=>a(t),2:t=>new RegExp(t),3:t=>new Date(t),4:t=>new Map(a(t)),5:t=>new Set(a(t)),6:t=>BigInt(t),7:t=>new URL(t),8:t=>new Uint8Array(t),9:t=>new Uint16Array(t),10:t=>new Uint32Array(t),11:t=>1/0*t},o=t=>{let[l,e]=t;return l in i?i[l](e):void 0},a=t=>t.map(o),m=t=>typeof t!="object"||t===null?t:Object.fromEntries(Object.entries(t).map(([l,e])=>[l,o(e)]));class y extends HTMLElement{constructor(){super(...arguments);d(this,"Component");d(this,"hydrator");d(this,"hydrate",async()=>{var b;if(!this.hydrator||!this.isConnected)return;let e=(b=this.parentElement)==null?void 0:b.closest("astro-island[ssr]");if(e){e.addEventListener("astro:hydrate",this.hydrate,{once:!0});return}let c=this.querySelectorAll("astro-slot"),n={},h=this.querySelectorAll("template[data-astro-template]");for(let r of h){let s=r.closest(this.tagName);s!=null&&s.isSameNode(this)&&(n[r.getAttribute("data-astro-template")||"default"]=r.innerHTML,r.remove())}for(let r of c){let s=r.closest(this.tagName);s!=null&&s.isSameNode(this)&&(n[r.getAttribute("name")||"default"]=r.innerHTML)}let p;try{p=this.hasAttribute("props")?m(JSON.parse(this.getAttribute("props"))):{}}catch(r){let s=this.getAttribute("component-url")||"<unknown>",v=this.getAttribute("component-export");throw v&&(s+=` (export ${v})`),console.error(`[hydrate] Error parsing props for component ${s}`,this.getAttribute("props"),r),r}let u;await this.hydrator(this)(this.Component,p,n,{client:this.getAttribute("client")}),this.removeAttribute("ssr"),this.dispatchEvent(new CustomEvent("astro:hydrate"))});d(this,"unmount",()=>{this.isConnected||this.dispatchEvent(new CustomEvent("astro:unmount"))})}disconnectedCallback(){document.removeEventListener("astro:after-swap",this.unmount),document.addEventListener("astro:after-swap",this.unmount,{once:!0})}connectedCallback(){if(!this.hasAttribute("await-children")||document.readyState==="interactive"||document.readyState==="complete")this.childrenConnectedCallback();else{let e=()=>{document.removeEventListener("DOMContentLoaded",e),c.disconnect(),this.childrenConnectedCallback()},c=new MutationObserver(()=>{var n;((n=this.lastChild)==null?void 0:n.nodeType)===Node.COMMENT_NODE&&this.lastChild.nodeValue==="astro:end"&&(this.lastChild.remove(),e())});c.observe(this,{childList:!0}),document.addEventListener("DOMContentLoaded",e)}}async childrenConnectedCallback(){let e=this.getAttribute("before-hydration-url");e&&await import(e),this.start()}async start(){let e=JSON.parse(this.getAttribute("opts")),c=this.getAttribute("client");if(Astro[c]===void 0){window.addEventListener(`astro:${c}`,()=>this.start(),{once:!0});return}try{await Astro[c](async()=>{let n=this.getAttribute("renderer-url"),[h,{default:p}]=await Promise.all([import(this.getAttribute("component-url")),n?import(n):()=>()=>{}]),u=this.getAttribute("component-export")||"default";if(!u.includes("."))this.Component=h[u];else{this.Component=h;for(let f of u.split("."))this.Component=this.Component[f]}return this.hydrator=p,this.hydrate},e,this)}catch(n){console.error(`[astro-island] Error hydrating ${this.getAttribute("component-url")}`,n)}}attributeChangedCallback(){this.hydrate()}}d(y,"observedAttributes",["props"]),customElements.get("astro-island")||customElements.define("astro-island",y)}})();</script><astro-island uid="b1btC" component-url="/__pyric/ui/_astro/app.DFdWhpVY.js" component-export="StudioApp" renderer-url="/__pyric/ui/_astro/client.D2ORs4B-.js" props="{&quot;data-astro-cid-w5sm6wlz&quot;:[0,true]}" ssr client="only" opts="{&quot;name&quot;:&quot;StudioApp&quot;,&quot;value&quot;:&quot;react&quot;}" await-children><p class="studio-loading" aria-live="polite" data-astro-cid-w5sm6wlz>
9
+ <link rel="stylesheet" href="/__pyric/ui/_astro/app.8TtSeQQX.css"></head> <body> <div id="root" data-astro-cid-w5sm6wlz> <style>astro-island,astro-slot,astro-static-slot{display:contents}</style><script>(()=>{var e=async t=>{await(await t())()};(self.Astro||(self.Astro={})).only=e;window.dispatchEvent(new Event("astro:only"));})();</script><script>(()=>{var A=Object.defineProperty;var g=(i,o,a)=>o in i?A(i,o,{enumerable:!0,configurable:!0,writable:!0,value:a}):i[o]=a;var d=(i,o,a)=>g(i,typeof o!="symbol"?o+"":o,a);{let i={0:t=>m(t),1:t=>a(t),2:t=>new RegExp(t),3:t=>new Date(t),4:t=>new Map(a(t)),5:t=>new Set(a(t)),6:t=>BigInt(t),7:t=>new URL(t),8:t=>new Uint8Array(t),9:t=>new Uint16Array(t),10:t=>new Uint32Array(t),11:t=>1/0*t},o=t=>{let[l,e]=t;return l in i?i[l](e):void 0},a=t=>t.map(o),m=t=>typeof t!="object"||t===null?t:Object.fromEntries(Object.entries(t).map(([l,e])=>[l,o(e)]));class y extends HTMLElement{constructor(){super(...arguments);d(this,"Component");d(this,"hydrator");d(this,"hydrate",async()=>{var b;if(!this.hydrator||!this.isConnected)return;let e=(b=this.parentElement)==null?void 0:b.closest("astro-island[ssr]");if(e){e.addEventListener("astro:hydrate",this.hydrate,{once:!0});return}let c=this.querySelectorAll("astro-slot"),n={},h=this.querySelectorAll("template[data-astro-template]");for(let r of h){let s=r.closest(this.tagName);s!=null&&s.isSameNode(this)&&(n[r.getAttribute("data-astro-template")||"default"]=r.innerHTML,r.remove())}for(let r of c){let s=r.closest(this.tagName);s!=null&&s.isSameNode(this)&&(n[r.getAttribute("name")||"default"]=r.innerHTML)}let p;try{p=this.hasAttribute("props")?m(JSON.parse(this.getAttribute("props"))):{}}catch(r){let s=this.getAttribute("component-url")||"<unknown>",v=this.getAttribute("component-export");throw v&&(s+=` (export ${v})`),console.error(`[hydrate] Error parsing props for component ${s}`,this.getAttribute("props"),r),r}let u;await this.hydrator(this)(this.Component,p,n,{client:this.getAttribute("client")}),this.removeAttribute("ssr"),this.dispatchEvent(new CustomEvent("astro:hydrate"))});d(this,"unmount",()=>{this.isConnected||this.dispatchEvent(new CustomEvent("astro:unmount"))})}disconnectedCallback(){document.removeEventListener("astro:after-swap",this.unmount),document.addEventListener("astro:after-swap",this.unmount,{once:!0})}connectedCallback(){if(!this.hasAttribute("await-children")||document.readyState==="interactive"||document.readyState==="complete")this.childrenConnectedCallback();else{let e=()=>{document.removeEventListener("DOMContentLoaded",e),c.disconnect(),this.childrenConnectedCallback()},c=new MutationObserver(()=>{var n;((n=this.lastChild)==null?void 0:n.nodeType)===Node.COMMENT_NODE&&this.lastChild.nodeValue==="astro:end"&&(this.lastChild.remove(),e())});c.observe(this,{childList:!0}),document.addEventListener("DOMContentLoaded",e)}}async childrenConnectedCallback(){let e=this.getAttribute("before-hydration-url");e&&await import(e),this.start()}async start(){let e=JSON.parse(this.getAttribute("opts")),c=this.getAttribute("client");if(Astro[c]===void 0){window.addEventListener(`astro:${c}`,()=>this.start(),{once:!0});return}try{await Astro[c](async()=>{let n=this.getAttribute("renderer-url"),[h,{default:p}]=await Promise.all([import(this.getAttribute("component-url")),n?import(n):()=>()=>{}]),u=this.getAttribute("component-export")||"default";if(!u.includes("."))this.Component=h[u];else{this.Component=h;for(let f of u.split("."))this.Component=this.Component[f]}return this.hydrator=p,this.hydrate},e,this)}catch(n){console.error(`[astro-island] Error hydrating ${this.getAttribute("component-url")}`,n)}}attributeChangedCallback(){this.hydrate()}}d(y,"observedAttributes",["props"]),customElements.get("astro-island")||customElements.define("astro-island",y)}})();</script><astro-island uid="b1btC" component-url="/__pyric/ui/_astro/app.DpzsBfos.js" component-export="StudioApp" renderer-url="/__pyric/ui/_astro/client.D2ORs4B-.js" props="{&quot;data-astro-cid-w5sm6wlz&quot;:[0,true]}" ssr client="only" opts="{&quot;name&quot;:&quot;StudioApp&quot;,&quot;value&quot;:&quot;react&quot;}" await-children><p class="studio-loading" aria-live="polite" data-astro-cid-w5sm6wlz>
10
10
  Starting Pyric Studio…
11
11
  </p><template data-astro-template="fallback"><p class="studio-loading" aria-live="polite" data-astro-cid-w5sm6wlz>
12
12
  Starting Pyric Studio…
@@ -0,0 +1,2 @@
1
+ export declare function createVerifyHint(): () => string;
2
+ //# sourceMappingURL=verify-hint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify-hint.d.ts","sourceRoot":"","sources":["../../src/serve/verify-hint.ts"],"names":[],"mappings":"AAAA,wBAAgB,gBAAgB,IAAI,MAAM,MAAM,CAO/C"}
@@ -0,0 +1,10 @@
1
+ export function createVerifyHint() {
2
+ let printedVerifyHint = false;
3
+ return () => {
4
+ if (printedVerifyHint)
5
+ return '';
6
+ printedVerifyHint = true;
7
+ return ' (run `pyric verify` to replay the captured session against these rules)';
8
+ };
9
+ }
10
+ //# sourceMappingURL=verify-hint.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify-hint.js","sourceRoot":"","sources":["../../src/serve/verify-hint.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,gBAAgB;IAC9B,IAAI,iBAAiB,GAAG,KAAK,CAAC;IAC9B,OAAO,GAAG,EAAE;QACV,IAAI,iBAAiB;YAAE,OAAO,EAAE,CAAC;QACjC,iBAAiB,GAAG,IAAI,CAAC;QACzB,OAAO,0EAA0E,CAAC;IACpF,CAAC,CAAC;AACJ,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"vite-generation-rules-watch.d.ts","sourceRoot":"","sources":["../../src/serve/vite-generation-rules-watch.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,MAAM,CAAC;AAE1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE;IAC9C,MAAM,EAAE,aAAa,CAAC;IACtB,OAAO,EAAE,cAAc,CAAC;CACzB,GAAG,MAAM,IAAI,CAwGb"}
1
+ {"version":3,"file":"vite-generation-rules-watch.d.ts","sourceRoot":"","sources":["../../src/serve/vite-generation-rules-watch.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,MAAM,CAAC;AAE1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAG3D;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE;IAC9C,MAAM,EAAE,aAAa,CAAC;IACtB,OAAO,EAAE,cAAc,CAAC;CACzB,GAAG,MAAM,IAAI,CA0Gb"}
@@ -1,5 +1,6 @@
1
1
  import path from 'node:path';
2
2
  import { formatDatabaseRulesRemoved, formatStorageRulesRemoved } from './rules.js';
3
+ import { createVerifyHint } from './verify-hint.js';
3
4
  /**
4
5
  * Adapt Vite's watcher to the session's last-good rules reload operations.
5
6
  * The Firestore, Storage, and each Realtime Database instance's rules files are watched at
@@ -11,6 +12,7 @@ import { formatDatabaseRulesRemoved, formatStorageRulesRemoved } from './rules.j
11
12
  * only the instances that deploy that file.
12
13
  */
13
14
  export function watchViteGenerationRules(input) {
15
+ const getVerifyHint = createVerifyHint();
14
16
  const { server, session } = input;
15
17
  const databaseFiles = session.databaseRulesFiles();
16
18
  const databaseFileByResolved = new Map(databaseFiles.map((file) => [path.resolve(file), file]));
@@ -47,7 +49,7 @@ export function watchViteGenerationRules(input) {
47
49
  server.watcher.add([...session.firestoreRulesFiles()]);
48
50
  const isReloaded = result.kind === 'reloaded';
49
51
  if (isReloaded) {
50
- server.config.logger.info(` ↻ [pyric] rules reloaded (${result.rulesHash})`);
52
+ server.config.logger.info(` ↻ [pyric] rules reloaded (${result.rulesHash})${getVerifyHint()}`);
51
53
  if (result.fixtureCheck && result.fixtureCheck.ran > 0) {
52
54
  const logFn = result.fixtureCheck.ok
53
55
  ? (m) => server.config.logger.info(m)
@@ -68,7 +70,7 @@ export function watchViteGenerationRules(input) {
68
70
  void session.reloadDatabaseRules(databaseFile).then((results) => {
69
71
  for (const { instance, result } of results) {
70
72
  if (result.kind === 'reloaded') {
71
- server.config.logger.info(` ↻ [pyric] rtdb rules reloaded for ${instance} (${result.rulesHash})`);
73
+ server.config.logger.info(` ↻ [pyric] rtdb rules reloaded for ${instance} (${result.rulesHash})${getVerifyHint()}`);
72
74
  if (result.fixtureCheck && result.fixtureCheck.ran > 0) {
73
75
  const logFn = result.fixtureCheck.ok
74
76
  ? (m) => server.config.logger.info(m)
@@ -89,7 +91,7 @@ export function watchViteGenerationRules(input) {
89
91
  if (isStorageMatch) {
90
92
  void session.reloadStorageRules().then((result) => {
91
93
  if (result.kind === 'reloaded') {
92
- server.config.logger.info(` ↻ [pyric] storage rules reloaded (${result.rulesHash})`);
94
+ server.config.logger.info(` ↻ [pyric] storage rules reloaded (${result.rulesHash})${getVerifyHint()}`);
93
95
  }
94
96
  else if (result.kind === 'rejected') {
95
97
  server.config.logger.warn(` ⚠ [pyric] storage rules NOT reloaded (last-good stays live): ${result.error.message}`);
@@ -1 +1 @@
1
- {"version":3,"file":"vite-generation-rules-watch.js","sourceRoot":"","sources":["../../src/serve/vite-generation-rules-watch.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"vite-generation-rules-watch.js","sourceRoot":"","sources":["../../src/serve/vite-generation-rules-watch.ts"],"names":[],"mappings":"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"}
@@ -3,6 +3,8 @@ import { type RtdbRulesDocument } from 'pyric/rules/internal/rtdb';
3
3
  import { type ExpressionReportLevel, type TestResult } from 'pyric/rules/internal';
4
4
  import type { ProjectScope } from '../credentials/core/types.js';
5
5
  import { type PyricVerifyFixture } from './fixture.js';
6
+ import { type StagingRunHooks } from './staging/engine.js';
7
+ import type { RtdbRestClient } from './staging/rest.js';
6
8
  import { type DeriveRulesTestCasesOptions, type DeriveRulesTestCasesResult } from './cases.js';
7
9
  export { buildVerifyFixture, fixtureVerifiableServices, parseVerifyFixture, restoreStorageRulesFromFixture, VERIFY_FIXTURE_SCHEMA, type BuildVerifyFixtureInput, type PyricVerifyFixture, type VerifyFirestoreRulesBlock, type VerifyRtdbInstanceCapture, type VerifyRtdbRulesBlock, type VerifyStorageRulesBlock, } from './fixture.js';
8
10
  export { capturedDefaultRtdbInstance, recordedRtdbInstances, recordsDefaultRtdbInstance, } from './rtdb-instances.js';
@@ -10,7 +12,7 @@ export { deriveRulesTestCases, type DerivedRulesTestCase, type DeriveRulesTestCa
10
12
  export { createVerifyTools, type VerifyToolDeps } from './tools.js';
11
13
  export { executeHostedRulesTest, HOSTED_CREDENTIAL_ENV_KEYS, HOSTED_CREDENTIAL_SOURCES, hostedRulesCredentials, isMissingCredentials, runHostedRulesTest, useHostedRulesTester, type HostedRulesCredentials, type HostedRulesScope, type HostedRulesTester, type HostedRulesTestOutcome, type MissingCredentials, } from './hosted-rules-test.js';
12
14
  export type VerifiableService = 'firestore' | 'rtdb' | 'storage';
13
- export type VerifyEngine = 'sandbox' | 'rulesTestApi';
15
+ export type VerifyEngine = 'sandbox' | 'rulesTestApi' | 'staging';
14
16
  type RtdbCandidateRules = {
15
17
  rules: Record<string, unknown>;
16
18
  } | RtdbRulesDocument;
@@ -39,6 +41,15 @@ export interface VerifyFixtureOptions {
39
41
  expressionReportLevel?: ExpressionReportLevel;
40
42
  };
41
43
  caseDerivation?: Omit<DeriveRulesTestCasesOptions, 'service'>;
44
+ /**
45
+ * The staging engine's instance: a client for a dedicated RTDB instance
46
+ * enrolled with `pyric verify staging init`, and the project it is
47
+ * enrolled for. Required when `engines` includes `staging`.
48
+ */
49
+ staging?: {
50
+ client: RtdbRestClient;
51
+ projectId: string;
52
+ } & StagingRunHooks;
42
53
  }
43
54
  export interface VerifyResult {
44
55
  ok: boolean;
@@ -108,7 +119,10 @@ type VerifyDivergenceKind = {
108
119
  path?: string;
109
120
  method?: string;
110
121
  sandbox: string;
111
- rulesTestApi: string;
122
+ /** Firestore: the hosted Rules Test API's decision. */
123
+ rulesTestApi?: string;
124
+ /** RTDB: what production did on the staging instance. */
125
+ staging?: string;
112
126
  reason?: string;
113
127
  };
114
128
  export declare class VerifyInputError extends Error {
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/verify/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/verify/index.ts"],"names":[],"mappings":"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"}
@@ -6,6 +6,7 @@ import { rulesSourceRejection, SimulateFirestoreRulesHandler, } from 'pyric/rule
6
6
  import { executeHostedRulesTest } from './hosted-rules-test.js';
7
7
  import { isRtdbRulesDocument, parseRtdbRulesJson, } from '../rtdb/rules-json.js';
8
8
  import { fixtureVerifiableServices, parseVerifyFixture, } from './fixture.js';
9
+ import { runStagingInstance, StagingDeployRefusedError, withStagingInstance } from './staging/engine.js';
9
10
  import { recordedRtdbInstances, recordsDefaultRtdbInstance, replayRtdbInstance, } from './rtdb-instances.js';
10
11
  import { deriveRulesTestCases, } from './cases.js';
11
12
  export { buildVerifyFixture, fixtureVerifiableServices, parseVerifyFixture, restoreStorageRulesFromFixture, VERIFY_FIXTURE_SCHEMA, } from './fixture.js';
@@ -28,11 +29,14 @@ export async function verifyFixture(fixtureInput, opts) {
28
29
  }
29
30
  const services = {};
30
31
  for (const service of selected) {
32
+ if (engines.includes('staging') && service !== 'rtdb') {
33
+ throw new VerifyInputError(`the staging engine checks RTDB only; ${service} is not supported by it yet. Select --service rtdb, or the sandbox engine for ${service}.`);
34
+ }
31
35
  if (service === 'firestore') {
32
36
  services.firestore = await verifyFirestore(fixture, requireFirestoreRules(opts.rules), engines, opts);
33
37
  }
34
38
  else if (service === 'rtdb') {
35
- services.rtdb = await verifyRtdb(fixture, opts.rules, engines);
39
+ services.rtdb = await verifyRtdb(fixture, opts.rules, engines, opts.staging);
36
40
  }
37
41
  else if (service === 'storage') {
38
42
  services.storage = await verifyStorage(fixture, opts.rules, engines);
@@ -336,18 +340,22 @@ async function verifyFirestoreRulesTestApi(fixture, rules, opts) {
336
340
  divergences,
337
341
  };
338
342
  }
339
- async function verifyRtdb(fixture, rules, engines) {
343
+ async function verifyRtdb(fixture, rules, engines, staging) {
340
344
  const rtdb = fixture.services.rtdb;
341
345
  if (!rtdb) {
342
346
  throw new VerifyInputError('fixture does not contain services.rtdb.');
343
347
  }
344
348
  if (engines.includes('rulesTestApi')) {
345
- throw new VerifyInputError('rulesTestApi verification is Firestore-only; select --service firestore or use the sandbox engine for RTDB.');
349
+ throw new VerifyInputError('rulesTestApi verification is Firestore-only; select --service firestore or use the sandbox or staging engine for RTDB.');
350
+ }
351
+ if (engines.includes('staging') && staging === undefined) {
352
+ throw new VerifyInputError('the staging engine needs a staging instance (opts.staging).');
346
353
  }
347
- const replays = [];
354
+ // The instances to check, each with its candidate rules.
355
+ const checks = [];
348
356
  const checksDefault = rules.rtdb !== undefined || recordsDefaultRtdbInstance(fixture);
349
357
  if (checksDefault) {
350
- replays.push(await replayRtdbInstance(fixture, requireRtdbRules(rules.rtdb, 'missing candidate RTDB rules. Pass rules.rtdb.')));
358
+ checks.push({ rules: requireRtdbRules(rules.rtdb, 'missing candidate RTDB rules. Pass rules.rtdb.'), capturedState: rtdb.state.tree });
351
359
  }
352
360
  const skippedInstances = [];
353
361
  for (const instance of recordedRtdbInstances(fixture)) {
@@ -357,25 +365,99 @@ async function verifyRtdb(fixture, rules, engines) {
357
365
  continue;
358
366
  }
359
367
  const parsed = requireRtdbRules(candidate, `missing candidate RTDB rules for instance "${instance}".`, instance);
360
- replays.push(await replayRtdbInstance(fixture, parsed, instance));
368
+ checks.push({ instance, rules: parsed, capturedState: rtdb.instances?.[instance]?.state.tree });
361
369
  }
362
- const divergences = replays.flatMap((replayed) => replayed.divergences.map(mapRtdbReplayDivergence));
363
- const checkedEvents = replays.reduce((total, replayed) => total + replayed.checkedEvents, 0);
364
- const sandbox = {
365
- engine: 'sandbox',
366
- ok: divergences.every(isInformational),
367
- checkedEvents,
368
- divergences,
369
- };
370
+ const engineResults = {};
371
+ if (engines.includes('sandbox')) {
372
+ const replays = [];
373
+ for (const check of checks)
374
+ replays.push(await replayRtdbInstance(fixture, check.rules, check.instance));
375
+ const divergences = replays.flatMap((replayed) => replayed.divergences.map(mapRtdbReplayDivergence));
376
+ engineResults.sandbox = {
377
+ engine: 'sandbox',
378
+ ok: divergences.every(isInformational),
379
+ checkedEvents: replays.reduce((total, replayed) => total + replayed.checkedEvents, 0),
380
+ divergences,
381
+ };
382
+ }
383
+ if (engines.includes('staging') && staging !== undefined) {
384
+ engineResults.staging = await verifyRtdbOnStaging(fixture, checks, staging);
385
+ }
386
+ if (engineResults.sandbox && engineResults.staging) {
387
+ addRtdbEngineDrift(engineResults.sandbox, engineResults.staging);
388
+ }
389
+ const divergences = collectEngineDivergences(engineResults);
370
390
  return {
371
391
  service: 'rtdb',
372
392
  ok: divergences.every(isInformational),
373
- checkedEvents,
393
+ checkedEvents: engineResults.sandbox?.checkedEvents ?? engineResults.staging?.checkedEvents ?? 0,
374
394
  divergences,
375
- engines: { sandbox },
395
+ engines: engineResults,
376
396
  ...(skippedInstances.length > 0 ? { skippedInstances } : {}),
377
397
  };
378
398
  }
399
+ /**
400
+ * Replay each instance on the staging instance in turn, each run guarded and
401
+ * the instance put back before the next. Production refusing to deploy an
402
+ * instance's candidate rules is reported for that instance; the others run.
403
+ */
404
+ async function verifyRtdbOnStaging(fixture, checks, staging) {
405
+ const divergences = [];
406
+ let checkedEvents = 0;
407
+ for (const check of checks) {
408
+ const tag = check.instance === undefined ? {} : { instance: check.instance };
409
+ try {
410
+ const { result } = await withStagingInstance(staging.client, staging.projectId, () => runStagingInstance({
411
+ client: staging.client,
412
+ events: fixture.events,
413
+ rules: check.rules,
414
+ capturedState: check.capturedState,
415
+ ...(check.instance === undefined ? {} : { instance: check.instance }),
416
+ ...(staging.signal ? { signal: staging.signal } : {}),
417
+ }), staging);
418
+ checkedEvents += result.checkedEvents;
419
+ divergences.push(...result.divergences.map((divergence) => ({ service: 'rtdb', ...divergence, ...tag })));
420
+ }
421
+ catch (error) {
422
+ if (!(error instanceof StagingDeployRefusedError))
423
+ throw error;
424
+ divergences.push({ service: 'rtdb', kind: 'unsupported', reason: error.message, ...tag });
425
+ }
426
+ }
427
+ return { engine: 'staging', ok: divergences.every(isInformational), checkedEvents, divergences };
428
+ }
429
+ /**
430
+ * Where the sandbox and production decided a captured step differently: one
431
+ * engine reports `now-denied`, `now-allowed` or `unsupported` for a step and
432
+ * the other reports something else, or nothing. Steps are matched by their
433
+ * index in the replay both engines walk.
434
+ */
435
+ function addRtdbEngineDrift(sandbox, staging) {
436
+ const decisive = new Set(['now-denied', 'now-allowed', 'unsupported']);
437
+ const keyOf = (divergence) => `${divergence.instance ?? ''}|${divergence.step ?? `${divergence.method}|${divergence.path}`}`;
438
+ const byKey = (divergences) => new Map(divergences.filter((divergence) => decisive.has(divergence.kind)).map((divergence) => [keyOf(divergence), divergence]));
439
+ const local = byKey(sandbox.divergences);
440
+ const remote = byKey(staging.divergences);
441
+ for (const key of new Set([...local.keys(), ...remote.keys()])) {
442
+ const sandboxKind = local.get(key)?.kind ?? 'unchanged';
443
+ const stagingKind = remote.get(key)?.kind ?? 'unchanged';
444
+ if (sandboxKind === stagingKind)
445
+ continue;
446
+ const sample = (local.get(key) ?? remote.get(key));
447
+ staging.divergences.push({
448
+ service: 'rtdb',
449
+ kind: 'engine-drift',
450
+ path: sample.path,
451
+ method: sample.method,
452
+ sandbox: sandboxKind,
453
+ staging: stagingKind,
454
+ reason: `the sandbox reported ${sandboxKind}; production reported ${stagingKind}.`,
455
+ ...(sample.instance === undefined ? {} : { instance: sample.instance }),
456
+ ...(sample.step === undefined ? {} : { step: sample.step }),
457
+ });
458
+ }
459
+ staging.ok = staging.divergences.every(isInformational);
460
+ }
379
461
  async function verifyStorage(fixture, rules, engines) {
380
462
  const storage = fixture.services.storage;
381
463
  if (!storage) {
@@ -585,7 +667,7 @@ function normalizeEngines(input) {
585
667
  const engines = input ?? ['sandbox'];
586
668
  const out = [];
587
669
  for (const engine of engines) {
588
- if (engine !== 'sandbox' && engine !== 'rulesTestApi') {
670
+ if (engine !== 'sandbox' && engine !== 'rulesTestApi' && engine !== 'staging') {
589
671
  throw new VerifyInputError(`unsupported verify engine: ${String(engine)}`);
590
672
  }
591
673
  if (!out.includes(engine))
@@ -597,7 +679,7 @@ function collectEngineDivergences(engines) {
597
679
  return Object.values(engines).flatMap((result) => result?.divergences ?? []);
598
680
  }
599
681
  function serviceCheckedEvents(engines) {
600
- return engines.sandbox?.checkedEvents ?? engines.rulesTestApi?.checkedEvents ?? 0;
682
+ return engines.sandbox?.checkedEvents ?? engines.rulesTestApi?.checkedEvents ?? engines.staging?.checkedEvents ?? 0;
601
683
  }
602
684
  function mapUnsupportedEvent(event) {
603
685
  return {