@pyric/cli 0.1.0-alpha.30.2 → 0.1.0-alpha.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assurance/.generated/conformance-verdicts.d.ts +107 -40
- package/dist/assurance/.generated/conformance-verdicts.d.ts.map +1 -1
- package/dist/assurance/.generated/conformance-verdicts.js +107 -40
- package/dist/assurance/.generated/conformance-verdicts.js.map +1 -1
- package/dist/assurance/capabilities.d.ts.map +1 -1
- package/dist/assurance/capabilities.js +12 -14
- package/dist/assurance/capabilities.js.map +1 -1
- package/dist/bounded-request.d.ts +27 -5
- package/dist/bounded-request.d.ts.map +1 -1
- package/dist/bounded-request.js +48 -4
- package/dist/bounded-request.js.map +1 -1
- package/dist/bridge/client/bridge-url.d.ts +5 -1
- package/dist/bridge/client/bridge-url.d.ts.map +1 -1
- package/dist/bridge/client/bridge-url.js +5 -1
- package/dist/bridge/client/bridge-url.js.map +1 -1
- package/dist/bridge/client/bridge.d.ts +9 -3
- package/dist/bridge/client/bridge.d.ts.map +1 -1
- package/dist/bridge/client/bridge.js +17 -3
- package/dist/bridge/client/bridge.js.map +1 -1
- package/dist/bridge/frame-output.d.ts.map +1 -1
- package/dist/bridge/frame-output.js +46 -0
- package/dist/bridge/frame-output.js.map +1 -1
- package/dist/bridge/protocol.d.ts +10 -0
- package/dist/bridge/protocol.d.ts.map +1 -1
- package/dist/bridge/protocol.js +10 -0
- package/dist/bridge/protocol.js.map +1 -1
- package/dist/bridge/server/in-process.d.ts +16 -7
- package/dist/bridge/server/in-process.d.ts.map +1 -1
- package/dist/bridge/server/in-process.js +51 -17
- package/dist/bridge/server/in-process.js.map +1 -1
- package/dist/bridge/server/peer.d.ts +8 -0
- package/dist/bridge/server/peer.d.ts.map +1 -1
- package/dist/bridge/server/peer.js +10 -0
- package/dist/bridge/server/peer.js.map +1 -1
- package/dist/bridge/server/standalone.d.ts +7 -2
- package/dist/bridge/server/standalone.d.ts.map +1 -1
- package/dist/bridge/server/standalone.js +27 -99
- package/dist/bridge/server/standalone.js.map +1 -1
- package/dist/bridge/surface/arguments/firestore.d.ts +6 -6
- package/dist/bridge/surface/arguments/sandbox.d.ts +0 -47
- package/dist/bridge/surface/arguments/sandbox.d.ts.map +1 -1
- package/dist/bridge/surface/arguments/sandbox.js +2 -59
- package/dist/bridge/surface/arguments/sandbox.js.map +1 -1
- package/dist/bridge/surface/arguments/seed.d.ts +61 -0
- package/dist/bridge/surface/arguments/seed.d.ts.map +1 -0
- package/dist/bridge/surface/arguments/seed.js +67 -0
- package/dist/bridge/surface/arguments/seed.js.map +1 -0
- package/dist/bridge/surface/arguments/storage.d.ts +2 -2
- package/dist/bridge/surface/context.d.ts +5 -10
- package/dist/bridge/surface/context.d.ts.map +1 -1
- package/dist/bridge/surface/context.js +11 -23
- package/dist/bridge/surface/context.js.map +1 -1
- package/dist/bridge/surface/database-instance-catalog.d.ts +0 -3
- package/dist/bridge/surface/database-instance-catalog.d.ts.map +1 -1
- package/dist/bridge/surface/database-instance-catalog.js +4 -27
- package/dist/bridge/surface/database-instance-catalog.js.map +1 -1
- package/dist/bridge/surface/database-instance-project.d.ts +5 -0
- package/dist/bridge/surface/database-instance-project.d.ts.map +1 -0
- package/dist/bridge/surface/database-instance-project.js +32 -0
- package/dist/bridge/surface/database-instance-project.js.map +1 -0
- package/dist/bridge/surface/database-instance.d.ts +3 -3
- package/dist/bridge/surface/database-instance.d.ts.map +1 -1
- package/dist/bridge/surface/database-instance.js +1 -2
- package/dist/bridge/surface/database-instance.js.map +1 -1
- package/dist/bridge/surface/functions-runtime.d.ts +2 -2
- package/dist/bridge/surface/functions-runtime.js +5 -5
- package/dist/bridge/surface/functions-runtime.js.map +1 -1
- package/dist/bridge/surface/held-identity.d.ts +4 -4
- package/dist/bridge/surface/held-identity.d.ts.map +1 -1
- package/dist/bridge/surface/held-identity.js.map +1 -1
- package/dist/bridge/surface/manifest.sandbox.generated.d.ts +3 -0
- package/dist/bridge/surface/manifest.sandbox.generated.d.ts.map +1 -0
- package/dist/bridge/surface/manifest.sandbox.generated.js +135 -0
- package/dist/bridge/surface/manifest.sandbox.generated.js.map +1 -0
- package/dist/bridge/surface/method-types.d.ts +48 -7
- package/dist/bridge/surface/method-types.d.ts.map +1 -1
- package/dist/bridge/surface/method-types.js +15 -0
- package/dist/bridge/surface/method-types.js.map +1 -1
- package/dist/bridge/surface/methods/ai_logic/clearScripts.d.ts +2 -1
- package/dist/bridge/surface/methods/ai_logic/clearScripts.d.ts.map +1 -1
- package/dist/bridge/surface/methods/ai_logic/clearScripts.js +2 -1
- package/dist/bridge/surface/methods/ai_logic/clearScripts.js.map +1 -1
- package/dist/bridge/surface/methods/ai_logic/script.d.ts +2 -1
- package/dist/bridge/surface/methods/ai_logic/script.d.ts.map +1 -1
- package/dist/bridge/surface/methods/ai_logic/script.js +2 -1
- package/dist/bridge/surface/methods/ai_logic/script.js.map +1 -1
- package/dist/bridge/surface/methods/ai_logic/scripts.d.ts +2 -1
- package/dist/bridge/surface/methods/ai_logic/scripts.d.ts.map +1 -1
- package/dist/bridge/surface/methods/ai_logic/scripts.js +2 -1
- package/dist/bridge/surface/methods/ai_logic/scripts.js.map +1 -1
- package/dist/bridge/surface/methods/ai_logic/status.d.ts +2 -1
- package/dist/bridge/surface/methods/ai_logic/status.d.ts.map +1 -1
- package/dist/bridge/surface/methods/ai_logic/status.js +1 -0
- package/dist/bridge/surface/methods/ai_logic/status.js.map +1 -1
- package/dist/bridge/surface/methods/assurance/attach.d.ts +1 -0
- package/dist/bridge/surface/methods/assurance/attach.d.ts.map +1 -1
- package/dist/bridge/surface/methods/assurance/attach.js +1 -0
- package/dist/bridge/surface/methods/assurance/attach.js.map +1 -1
- package/dist/bridge/surface/methods/assurance/canIUse.d.ts +1 -0
- package/dist/bridge/surface/methods/assurance/canIUse.d.ts.map +1 -1
- package/dist/bridge/surface/methods/assurance/canIUse.js +1 -0
- package/dist/bridge/surface/methods/assurance/canIUse.js.map +1 -1
- package/dist/bridge/surface/methods/assurance/define.d.ts +1 -0
- package/dist/bridge/surface/methods/assurance/define.d.ts.map +1 -1
- package/dist/bridge/surface/methods/assurance/define.js +1 -0
- package/dist/bridge/surface/methods/assurance/define.js.map +1 -1
- package/dist/bridge/surface/methods/assurance/export.d.ts +1 -0
- package/dist/bridge/surface/methods/assurance/export.d.ts.map +1 -1
- package/dist/bridge/surface/methods/assurance/export.js +1 -0
- package/dist/bridge/surface/methods/assurance/export.js.map +1 -1
- package/dist/bridge/surface/methods/assurance/inspect.d.ts +1 -0
- package/dist/bridge/surface/methods/assurance/inspect.d.ts.map +1 -1
- package/dist/bridge/surface/methods/assurance/inspect.js +1 -0
- package/dist/bridge/surface/methods/assurance/inspect.js.map +1 -1
- package/dist/bridge/surface/methods/assurance/map.d.ts +1 -0
- package/dist/bridge/surface/methods/assurance/map.d.ts.map +1 -1
- package/dist/bridge/surface/methods/assurance/map.js +1 -0
- package/dist/bridge/surface/methods/assurance/map.js.map +1 -1
- package/dist/bridge/surface/methods/assurance/minimize.d.ts +1 -0
- package/dist/bridge/surface/methods/assurance/minimize.d.ts.map +1 -1
- package/dist/bridge/surface/methods/assurance/minimize.js +1 -0
- package/dist/bridge/surface/methods/assurance/minimize.js.map +1 -1
- package/dist/bridge/surface/methods/assurance/propose.d.ts +1 -0
- package/dist/bridge/surface/methods/assurance/propose.d.ts.map +1 -1
- package/dist/bridge/surface/methods/assurance/propose.js +1 -0
- package/dist/bridge/surface/methods/assurance/propose.js.map +1 -1
- package/dist/bridge/surface/methods/assurance/replaySession.d.ts +1 -0
- package/dist/bridge/surface/methods/assurance/replaySession.d.ts.map +1 -1
- package/dist/bridge/surface/methods/assurance/replaySession.js +1 -0
- package/dist/bridge/surface/methods/assurance/replaySession.js.map +1 -1
- package/dist/bridge/surface/methods/assurance/run.d.ts +1 -0
- package/dist/bridge/surface/methods/assurance/run.d.ts.map +1 -1
- package/dist/bridge/surface/methods/assurance/run.js +1 -0
- package/dist/bridge/surface/methods/assurance/run.js.map +1 -1
- package/dist/bridge/surface/methods/assurance/start.d.ts +1 -0
- package/dist/bridge/surface/methods/assurance/start.d.ts.map +1 -1
- package/dist/bridge/surface/methods/assurance/start.js +1 -0
- package/dist/bridge/surface/methods/assurance/start.js.map +1 -1
- package/dist/bridge/surface/methods/assurance/testRulesHosted.d.ts +1 -0
- package/dist/bridge/surface/methods/assurance/testRulesHosted.d.ts.map +1 -1
- package/dist/bridge/surface/methods/assurance/testRulesHosted.js +1 -0
- package/dist/bridge/surface/methods/assurance/testRulesHosted.js.map +1 -1
- package/dist/bridge/surface/methods/assurance/verify.d.ts +1 -0
- package/dist/bridge/surface/methods/assurance/verify.d.ts.map +1 -1
- package/dist/bridge/surface/methods/assurance/verify.js +1 -0
- package/dist/bridge/surface/methods/assurance/verify.js.map +1 -1
- package/dist/bridge/surface/methods/assurance/verifyCases.d.ts +1 -0
- package/dist/bridge/surface/methods/assurance/verifyCases.d.ts.map +1 -1
- package/dist/bridge/surface/methods/assurance/verifyCases.js +1 -0
- package/dist/bridge/surface/methods/assurance/verifyCases.js.map +1 -1
- package/dist/bridge/surface/methods/auth/actAsAdmin.d.ts +2 -1
- package/dist/bridge/surface/methods/auth/actAsAdmin.d.ts.map +1 -1
- package/dist/bridge/surface/methods/auth/actAsAdmin.js +1 -0
- package/dist/bridge/surface/methods/auth/actAsAdmin.js.map +1 -1
- package/dist/bridge/surface/methods/auth/actAsAnonymous.d.ts +2 -1
- package/dist/bridge/surface/methods/auth/actAsAnonymous.d.ts.map +1 -1
- package/dist/bridge/surface/methods/auth/actAsAnonymous.js +1 -0
- package/dist/bridge/surface/methods/auth/actAsAnonymous.js.map +1 -1
- package/dist/bridge/surface/methods/auth/createCustomToken.d.ts +1 -0
- package/dist/bridge/surface/methods/auth/createCustomToken.d.ts.map +1 -1
- package/dist/bridge/surface/methods/auth/createCustomToken.js +1 -0
- package/dist/bridge/surface/methods/auth/createCustomToken.js.map +1 -1
- package/dist/bridge/surface/methods/auth/createUser.d.ts +2 -1
- package/dist/bridge/surface/methods/auth/createUser.d.ts.map +1 -1
- package/dist/bridge/surface/methods/auth/createUser.js +2 -1
- package/dist/bridge/surface/methods/auth/createUser.js.map +1 -1
- package/dist/bridge/surface/methods/auth/deleteUser.d.ts +2 -1
- package/dist/bridge/surface/methods/auth/deleteUser.d.ts.map +1 -1
- package/dist/bridge/surface/methods/auth/deleteUser.js +2 -1
- package/dist/bridge/surface/methods/auth/deleteUser.js.map +1 -1
- package/dist/bridge/surface/methods/auth/getUser.d.ts +2 -1
- package/dist/bridge/surface/methods/auth/getUser.d.ts.map +1 -1
- package/dist/bridge/surface/methods/auth/getUser.js +2 -1
- package/dist/bridge/surface/methods/auth/getUser.js.map +1 -1
- package/dist/bridge/surface/methods/auth/getUserByEmail.d.ts +2 -1
- package/dist/bridge/surface/methods/auth/getUserByEmail.d.ts.map +1 -1
- package/dist/bridge/surface/methods/auth/getUserByEmail.js +2 -1
- package/dist/bridge/surface/methods/auth/getUserByEmail.js.map +1 -1
- package/dist/bridge/surface/methods/auth/impersonate.d.ts +2 -1
- package/dist/bridge/surface/methods/auth/impersonate.d.ts.map +1 -1
- package/dist/bridge/surface/methods/auth/impersonate.js +1 -0
- package/dist/bridge/surface/methods/auth/impersonate.js.map +1 -1
- package/dist/bridge/surface/methods/auth/importUsers.d.ts +2 -1
- package/dist/bridge/surface/methods/auth/importUsers.d.ts.map +1 -1
- package/dist/bridge/surface/methods/auth/importUsers.js +3 -2
- package/dist/bridge/surface/methods/auth/importUsers.js.map +1 -1
- package/dist/bridge/surface/methods/auth/listUsers.d.ts +2 -1
- package/dist/bridge/surface/methods/auth/listUsers.d.ts.map +1 -1
- package/dist/bridge/surface/methods/auth/listUsers.js +2 -1
- package/dist/bridge/surface/methods/auth/listUsers.js.map +1 -1
- package/dist/bridge/surface/methods/auth/sessions.d.ts +2 -1
- package/dist/bridge/surface/methods/auth/sessions.d.ts.map +1 -1
- package/dist/bridge/surface/methods/auth/sessions.js +1 -0
- package/dist/bridge/surface/methods/auth/sessions.js.map +1 -1
- package/dist/bridge/surface/methods/auth/setCustomUserClaims.d.ts +2 -1
- package/dist/bridge/surface/methods/auth/setCustomUserClaims.d.ts.map +1 -1
- package/dist/bridge/surface/methods/auth/setCustomUserClaims.js +2 -1
- package/dist/bridge/surface/methods/auth/setCustomUserClaims.js.map +1 -1
- package/dist/bridge/surface/methods/auth/signInAnonymously.d.ts +2 -1
- package/dist/bridge/surface/methods/auth/signInAnonymously.d.ts.map +1 -1
- package/dist/bridge/surface/methods/auth/signInAnonymously.js +1 -0
- package/dist/bridge/surface/methods/auth/signInAnonymously.js.map +1 -1
- package/dist/bridge/surface/methods/auth/signInWithCredential.d.ts +2 -1
- package/dist/bridge/surface/methods/auth/signInWithCredential.d.ts.map +1 -1
- package/dist/bridge/surface/methods/auth/signInWithCredential.js +1 -0
- package/dist/bridge/surface/methods/auth/signInWithCredential.js.map +1 -1
- package/dist/bridge/surface/methods/auth/signInWithCustomToken.d.ts +2 -1
- package/dist/bridge/surface/methods/auth/signInWithCustomToken.d.ts.map +1 -1
- package/dist/bridge/surface/methods/auth/signInWithCustomToken.js +1 -0
- package/dist/bridge/surface/methods/auth/signInWithCustomToken.js.map +1 -1
- package/dist/bridge/surface/methods/auth/signInWithEmailAndPassword.d.ts +2 -1
- package/dist/bridge/surface/methods/auth/signInWithEmailAndPassword.d.ts.map +1 -1
- package/dist/bridge/surface/methods/auth/signInWithEmailAndPassword.js +1 -0
- package/dist/bridge/surface/methods/auth/signInWithEmailAndPassword.js.map +1 -1
- package/dist/bridge/surface/methods/auth/signOut.d.ts +2 -1
- package/dist/bridge/surface/methods/auth/signOut.d.ts.map +1 -1
- package/dist/bridge/surface/methods/auth/signOut.js +1 -0
- package/dist/bridge/surface/methods/auth/signOut.js.map +1 -1
- package/dist/bridge/surface/methods/auth/takeAuthMail.d.ts +2 -1
- package/dist/bridge/surface/methods/auth/takeAuthMail.d.ts.map +1 -1
- package/dist/bridge/surface/methods/auth/takeAuthMail.js +1 -0
- package/dist/bridge/surface/methods/auth/takeAuthMail.js.map +1 -1
- package/dist/bridge/surface/methods/auth/updateUser.d.ts +2 -1
- package/dist/bridge/surface/methods/auth/updateUser.d.ts.map +1 -1
- package/dist/bridge/surface/methods/auth/updateUser.js +2 -1
- package/dist/bridge/surface/methods/auth/updateUser.js.map +1 -1
- package/dist/bridge/surface/methods/auth/useAppSession.d.ts +2 -1
- package/dist/bridge/surface/methods/auth/useAppSession.d.ts.map +1 -1
- package/dist/bridge/surface/methods/auth/useAppSession.js +1 -0
- package/dist/bridge/surface/methods/auth/useAppSession.js.map +1 -1
- package/dist/bridge/surface/methods/auth/whoami.d.ts +2 -1
- package/dist/bridge/surface/methods/auth/whoami.d.ts.map +1 -1
- package/dist/bridge/surface/methods/auth/whoami.js +1 -0
- package/dist/bridge/surface/methods/auth/whoami.js.map +1 -1
- package/dist/bridge/surface/methods/database/crawl.d.ts +2 -1
- package/dist/bridge/surface/methods/database/crawl.d.ts.map +1 -1
- package/dist/bridge/surface/methods/database/crawl.js +1 -0
- package/dist/bridge/surface/methods/database/crawl.js.map +1 -1
- package/dist/bridge/surface/methods/database/get.d.ts +2 -1
- package/dist/bridge/surface/methods/database/get.d.ts.map +1 -1
- package/dist/bridge/surface/methods/database/get.js +1 -0
- package/dist/bridge/surface/methods/database/get.js.map +1 -1
- package/dist/bridge/surface/methods/database/push.d.ts +2 -1
- package/dist/bridge/surface/methods/database/push.d.ts.map +1 -1
- package/dist/bridge/surface/methods/database/push.js +1 -0
- package/dist/bridge/surface/methods/database/push.js.map +1 -1
- package/dist/bridge/surface/methods/database/query.d.ts +2 -1
- package/dist/bridge/surface/methods/database/query.d.ts.map +1 -1
- package/dist/bridge/surface/methods/database/query.js +1 -0
- package/dist/bridge/surface/methods/database/query.js.map +1 -1
- package/dist/bridge/surface/methods/database/remove.d.ts +2 -1
- package/dist/bridge/surface/methods/database/remove.d.ts.map +1 -1
- package/dist/bridge/surface/methods/database/remove.js +1 -0
- package/dist/bridge/surface/methods/database/remove.js.map +1 -1
- package/dist/bridge/surface/methods/database/set.d.ts +2 -1
- package/dist/bridge/surface/methods/database/set.d.ts.map +1 -1
- package/dist/bridge/surface/methods/database/set.js +1 -0
- package/dist/bridge/surface/methods/database/set.js.map +1 -1
- package/dist/bridge/surface/methods/database/update.d.ts +2 -1
- package/dist/bridge/surface/methods/database/update.d.ts.map +1 -1
- package/dist/bridge/surface/methods/database/update.js +1 -0
- package/dist/bridge/surface/methods/database/update.js.map +1 -1
- package/dist/bridge/surface/methods/firestore/addDoc.d.ts +2 -1
- package/dist/bridge/surface/methods/firestore/addDoc.d.ts.map +1 -1
- package/dist/bridge/surface/methods/firestore/addDoc.js +2 -1
- package/dist/bridge/surface/methods/firestore/addDoc.js.map +1 -1
- package/dist/bridge/surface/methods/firestore/deleteDoc.d.ts +2 -1
- package/dist/bridge/surface/methods/firestore/deleteDoc.d.ts.map +1 -1
- package/dist/bridge/surface/methods/firestore/deleteDoc.js +2 -1
- package/dist/bridge/surface/methods/firestore/deleteDoc.js.map +1 -1
- package/dist/bridge/surface/methods/firestore/discoverPaths.d.ts +2 -1
- package/dist/bridge/surface/methods/firestore/discoverPaths.d.ts.map +1 -1
- package/dist/bridge/surface/methods/firestore/discoverPaths.js +1 -0
- package/dist/bridge/surface/methods/firestore/discoverPaths.js.map +1 -1
- package/dist/bridge/surface/methods/firestore/extractIndexes.d.ts +7 -6
- package/dist/bridge/surface/methods/firestore/extractIndexes.d.ts.map +1 -1
- package/dist/bridge/surface/methods/firestore/extractIndexes.js +1 -0
- package/dist/bridge/surface/methods/firestore/extractIndexes.js.map +1 -1
- package/dist/bridge/surface/methods/firestore/findCollectionGroup.d.ts +2 -1
- package/dist/bridge/surface/methods/firestore/findCollectionGroup.d.ts.map +1 -1
- package/dist/bridge/surface/methods/firestore/findCollectionGroup.js +1 -0
- package/dist/bridge/surface/methods/firestore/findCollectionGroup.js.map +1 -1
- package/dist/bridge/surface/methods/firestore/getAggregateFromServer.d.ts +6 -5
- package/dist/bridge/surface/methods/firestore/getAggregateFromServer.d.ts.map +1 -1
- package/dist/bridge/surface/methods/firestore/getAggregateFromServer.js +1 -0
- package/dist/bridge/surface/methods/firestore/getAggregateFromServer.js.map +1 -1
- package/dist/bridge/surface/methods/firestore/getCountFromServer.d.ts +6 -5
- package/dist/bridge/surface/methods/firestore/getCountFromServer.d.ts.map +1 -1
- package/dist/bridge/surface/methods/firestore/getCountFromServer.js +1 -0
- package/dist/bridge/surface/methods/firestore/getCountFromServer.js.map +1 -1
- package/dist/bridge/surface/methods/firestore/getDoc.d.ts +2 -1
- package/dist/bridge/surface/methods/firestore/getDoc.d.ts.map +1 -1
- package/dist/bridge/surface/methods/firestore/getDoc.js +2 -1
- package/dist/bridge/surface/methods/firestore/getDoc.js.map +1 -1
- package/dist/bridge/surface/methods/firestore/getDocs.d.ts +6 -5
- package/dist/bridge/surface/methods/firestore/getDocs.d.ts.map +1 -1
- package/dist/bridge/surface/methods/firestore/getDocs.js +2 -1
- package/dist/bridge/surface/methods/firestore/getDocs.js.map +1 -1
- package/dist/bridge/surface/methods/firestore/setDoc.d.ts +2 -1
- package/dist/bridge/surface/methods/firestore/setDoc.d.ts.map +1 -1
- package/dist/bridge/surface/methods/firestore/setDoc.js +2 -1
- package/dist/bridge/surface/methods/firestore/setDoc.js.map +1 -1
- package/dist/bridge/surface/methods/firestore/updateDoc.d.ts +2 -1
- package/dist/bridge/surface/methods/firestore/updateDoc.d.ts.map +1 -1
- package/dist/bridge/surface/methods/firestore/updateDoc.js +2 -1
- package/dist/bridge/surface/methods/firestore/updateDoc.js.map +1 -1
- package/dist/bridge/surface/methods/firestore/writeBatch.d.ts +2 -1
- package/dist/bridge/surface/methods/firestore/writeBatch.d.ts.map +1 -1
- package/dist/bridge/surface/methods/firestore/writeBatch.js +2 -1
- package/dist/bridge/surface/methods/firestore/writeBatch.js.map +1 -1
- package/dist/bridge/surface/methods/firestore/writeIndexes.d.ts +1 -0
- package/dist/bridge/surface/methods/firestore/writeIndexes.d.ts.map +1 -1
- package/dist/bridge/surface/methods/firestore/writeIndexes.js +1 -0
- package/dist/bridge/surface/methods/firestore/writeIndexes.js.map +1 -1
- package/dist/bridge/surface/methods/functions/executions.d.ts +3 -2
- package/dist/bridge/surface/methods/functions/executions.d.ts.map +1 -1
- package/dist/bridge/surface/methods/functions/executions.js +2 -1
- package/dist/bridge/surface/methods/functions/executions.js.map +1 -1
- package/dist/bridge/surface/methods/functions/fire.d.ts +1 -0
- package/dist/bridge/surface/methods/functions/fire.d.ts.map +1 -1
- package/dist/bridge/surface/methods/functions/fire.js +6 -5
- package/dist/bridge/surface/methods/functions/fire.js.map +1 -1
- package/dist/bridge/surface/methods/functions/listTriggers.d.ts +2 -1
- package/dist/bridge/surface/methods/functions/listTriggers.d.ts.map +1 -1
- package/dist/bridge/surface/methods/functions/listTriggers.js +1 -0
- package/dist/bridge/surface/methods/functions/listTriggers.js.map +1 -1
- package/dist/bridge/surface/methods/messaging/deliveries.d.ts +2 -1
- package/dist/bridge/surface/methods/messaging/deliveries.d.ts.map +1 -1
- package/dist/bridge/surface/methods/messaging/deliveries.js +1 -0
- package/dist/bridge/surface/methods/messaging/deliveries.js.map +1 -1
- package/dist/bridge/surface/methods/messaging/send.d.ts +2 -1
- package/dist/bridge/surface/methods/messaging/send.d.ts.map +1 -1
- package/dist/bridge/surface/methods/messaging/send.js +2 -1
- package/dist/bridge/surface/methods/messaging/send.js.map +1 -1
- package/dist/bridge/surface/methods/messaging/subscribeToTopic.d.ts +2 -1
- package/dist/bridge/surface/methods/messaging/subscribeToTopic.d.ts.map +1 -1
- package/dist/bridge/surface/methods/messaging/subscribeToTopic.js +1 -0
- package/dist/bridge/surface/methods/messaging/subscribeToTopic.js.map +1 -1
- package/dist/bridge/surface/methods/messaging/tokens.d.ts +2 -1
- package/dist/bridge/surface/methods/messaging/tokens.d.ts.map +1 -1
- package/dist/bridge/surface/methods/messaging/tokens.js +1 -0
- package/dist/bridge/surface/methods/messaging/tokens.js.map +1 -1
- package/dist/bridge/surface/methods/messaging/unsubscribeFromTopic.d.ts +2 -1
- package/dist/bridge/surface/methods/messaging/unsubscribeFromTopic.d.ts.map +1 -1
- package/dist/bridge/surface/methods/messaging/unsubscribeFromTopic.js +1 -0
- package/dist/bridge/surface/methods/messaging/unsubscribeFromTopic.js.map +1 -1
- package/dist/bridge/surface/methods/registry.d.ts.map +1 -1
- package/dist/bridge/surface/methods/registry.js +2 -8
- package/dist/bridge/surface/methods/registry.js.map +1 -1
- package/dist/bridge/surface/methods/rules/explainDenial.d.ts +1 -0
- package/dist/bridge/surface/methods/rules/explainDenial.d.ts.map +1 -1
- package/dist/bridge/surface/methods/rules/explainDenial.js +1 -0
- package/dist/bridge/surface/methods/rules/explainDenial.js.map +1 -1
- package/dist/bridge/surface/methods/rules/getStdlib.d.ts +1 -0
- package/dist/bridge/surface/methods/rules/getStdlib.d.ts.map +1 -1
- package/dist/bridge/surface/methods/rules/getStdlib.js +1 -0
- package/dist/bridge/surface/methods/rules/getStdlib.js.map +1 -1
- package/dist/bridge/surface/methods/rules/lint.d.ts +1 -0
- package/dist/bridge/surface/methods/rules/lint.d.ts.map +1 -1
- package/dist/bridge/surface/methods/rules/lint.js +1 -0
- package/dist/bridge/surface/methods/rules/lint.js.map +1 -1
- package/dist/bridge/surface/methods/rules/listStdlib.d.ts +1 -0
- package/dist/bridge/surface/methods/rules/listStdlib.d.ts.map +1 -1
- package/dist/bridge/surface/methods/rules/listStdlib.js +1 -0
- package/dist/bridge/surface/methods/rules/listStdlib.js.map +1 -1
- package/dist/bridge/surface/methods/rules/set.d.ts +1 -0
- package/dist/bridge/surface/methods/rules/set.d.ts.map +1 -1
- package/dist/bridge/surface/methods/rules/set.js +1 -0
- package/dist/bridge/surface/methods/rules/set.js.map +1 -1
- package/dist/bridge/surface/methods/rules/simulate.d.ts +1 -0
- package/dist/bridge/surface/methods/rules/simulate.d.ts.map +1 -1
- package/dist/bridge/surface/methods/rules/simulate.js +1 -0
- package/dist/bridge/surface/methods/rules/simulate.js.map +1 -1
- package/dist/bridge/surface/methods/sandbox/activity.d.ts +2 -1
- package/dist/bridge/surface/methods/sandbox/activity.d.ts.map +1 -1
- package/dist/bridge/surface/methods/sandbox/activity.js +2 -1
- package/dist/bridge/surface/methods/sandbox/activity.js.map +1 -1
- package/dist/bridge/surface/methods/sandbox/advanceClock.d.ts +2 -1
- package/dist/bridge/surface/methods/sandbox/advanceClock.d.ts.map +1 -1
- package/dist/bridge/surface/methods/sandbox/advanceClock.js +1 -0
- package/dist/bridge/surface/methods/sandbox/advanceClock.js.map +1 -1
- package/dist/bridge/surface/methods/sandbox/apply.d.ts +1 -0
- package/dist/bridge/surface/methods/sandbox/apply.d.ts.map +1 -1
- package/dist/bridge/surface/methods/sandbox/apply.js +1 -0
- package/dist/bridge/surface/methods/sandbox/apply.js.map +1 -1
- package/dist/bridge/surface/methods/sandbox/checkpoint.d.ts +1 -0
- package/dist/bridge/surface/methods/sandbox/checkpoint.d.ts.map +1 -1
- package/dist/bridge/surface/methods/sandbox/checkpoint.js +1 -0
- package/dist/bridge/surface/methods/sandbox/checkpoint.js.map +1 -1
- package/dist/bridge/surface/methods/sandbox/deleteCapture.d.ts +1 -0
- package/dist/bridge/surface/methods/sandbox/deleteCapture.d.ts.map +1 -1
- package/dist/bridge/surface/methods/sandbox/deleteCapture.js +1 -1
- package/dist/bridge/surface/methods/sandbox/deleteCapture.js.map +1 -1
- package/dist/bridge/surface/methods/sandbox/deleteCheckpoint.d.ts +1 -0
- package/dist/bridge/surface/methods/sandbox/deleteCheckpoint.d.ts.map +1 -1
- package/dist/bridge/surface/methods/sandbox/deleteCheckpoint.js +1 -0
- package/dist/bridge/surface/methods/sandbox/deleteCheckpoint.js.map +1 -1
- package/dist/bridge/surface/methods/sandbox/diff.d.ts +1 -0
- package/dist/bridge/surface/methods/sandbox/diff.d.ts.map +1 -1
- package/dist/bridge/surface/methods/sandbox/diff.js +1 -0
- package/dist/bridge/surface/methods/sandbox/diff.js.map +1 -1
- package/dist/bridge/surface/methods/sandbox/discard.d.ts +1 -0
- package/dist/bridge/surface/methods/sandbox/discard.d.ts.map +1 -1
- package/dist/bridge/surface/methods/sandbox/discard.js +1 -0
- package/dist/bridge/surface/methods/sandbox/discard.js.map +1 -1
- package/dist/bridge/surface/methods/sandbox/events.d.ts +2 -1
- package/dist/bridge/surface/methods/sandbox/events.d.ts.map +1 -1
- package/dist/bridge/surface/methods/sandbox/events.js +2 -1
- package/dist/bridge/surface/methods/sandbox/events.js.map +1 -1
- package/dist/bridge/surface/methods/sandbox/exportFixture.d.ts +1 -0
- package/dist/bridge/surface/methods/sandbox/exportFixture.d.ts.map +1 -1
- package/dist/bridge/surface/methods/sandbox/exportFixture.js +1 -0
- package/dist/bridge/surface/methods/sandbox/exportFixture.js.map +1 -1
- package/dist/bridge/surface/methods/sandbox/fork.d.ts +1 -0
- package/dist/bridge/surface/methods/sandbox/fork.d.ts.map +1 -1
- package/dist/bridge/surface/methods/sandbox/fork.js +1 -0
- package/dist/bridge/surface/methods/sandbox/fork.js.map +1 -1
- package/dist/bridge/surface/methods/sandbox/inspect.d.ts +2 -1
- package/dist/bridge/surface/methods/sandbox/inspect.d.ts.map +1 -1
- package/dist/bridge/surface/methods/sandbox/inspect.js +2 -1
- package/dist/bridge/surface/methods/sandbox/inspect.js.map +1 -1
- package/dist/bridge/surface/methods/sandbox/listBranches.d.ts +1 -0
- package/dist/bridge/surface/methods/sandbox/listBranches.d.ts.map +1 -1
- package/dist/bridge/surface/methods/sandbox/listBranches.js +1 -0
- package/dist/bridge/surface/methods/sandbox/listBranches.js.map +1 -1
- package/dist/bridge/surface/methods/sandbox/listCaptures.d.ts +1 -0
- package/dist/bridge/surface/methods/sandbox/listCaptures.d.ts.map +1 -1
- package/dist/bridge/surface/methods/sandbox/listCaptures.js +1 -1
- package/dist/bridge/surface/methods/sandbox/listCaptures.js.map +1 -1
- package/dist/bridge/surface/methods/sandbox/listCheckpoints.d.ts +1 -0
- package/dist/bridge/surface/methods/sandbox/listCheckpoints.d.ts.map +1 -1
- package/dist/bridge/surface/methods/sandbox/listCheckpoints.js +1 -0
- package/dist/bridge/surface/methods/sandbox/listCheckpoints.js.map +1 -1
- package/dist/bridge/surface/methods/sandbox/listeners.d.ts +2 -1
- package/dist/bridge/surface/methods/sandbox/listeners.d.ts.map +1 -1
- package/dist/bridge/surface/methods/sandbox/listeners.js +1 -0
- package/dist/bridge/surface/methods/sandbox/listeners.js.map +1 -1
- package/dist/bridge/surface/methods/sandbox/openCapture.d.ts +1 -0
- package/dist/bridge/surface/methods/sandbox/openCapture.d.ts.map +1 -1
- package/dist/bridge/surface/methods/sandbox/openCapture.js +1 -1
- package/dist/bridge/surface/methods/sandbox/openCapture.js.map +1 -1
- package/dist/bridge/surface/methods/sandbox/promote.d.ts +1 -0
- package/dist/bridge/surface/methods/sandbox/promote.d.ts.map +1 -1
- package/dist/bridge/surface/methods/sandbox/promote.js +1 -0
- package/dist/bridge/surface/methods/sandbox/promote.js.map +1 -1
- package/dist/bridge/surface/methods/sandbox/renameCapture.d.ts +3 -2
- package/dist/bridge/surface/methods/sandbox/renameCapture.d.ts.map +1 -1
- package/dist/bridge/surface/methods/sandbox/renameCapture.js +1 -1
- package/dist/bridge/surface/methods/sandbox/renameCapture.js.map +1 -1
- package/dist/bridge/surface/methods/sandbox/reset.d.ts +3 -2
- package/dist/bridge/surface/methods/sandbox/reset.d.ts.map +1 -1
- package/dist/bridge/surface/methods/sandbox/reset.js +1 -0
- package/dist/bridge/surface/methods/sandbox/reset.js.map +1 -1
- package/dist/bridge/surface/methods/sandbox/resetClock.d.ts +2 -1
- package/dist/bridge/surface/methods/sandbox/resetClock.d.ts.map +1 -1
- package/dist/bridge/surface/methods/sandbox/resetClock.js +1 -0
- package/dist/bridge/surface/methods/sandbox/resetClock.js.map +1 -1
- package/dist/bridge/surface/methods/sandbox/restore.d.ts +1 -0
- package/dist/bridge/surface/methods/sandbox/restore.d.ts.map +1 -1
- package/dist/bridge/surface/methods/sandbox/restore.js +1 -0
- package/dist/bridge/surface/methods/sandbox/restore.js.map +1 -1
- package/dist/bridge/surface/methods/sandbox/saveCapture.d.ts +1 -0
- package/dist/bridge/surface/methods/sandbox/saveCapture.d.ts.map +1 -1
- package/dist/bridge/surface/methods/sandbox/saveCapture.js +1 -1
- package/dist/bridge/surface/methods/sandbox/saveCapture.js.map +1 -1
- package/dist/bridge/surface/methods/sandbox/seed.d.ts +2 -1
- package/dist/bridge/surface/methods/sandbox/seed.d.ts.map +1 -1
- package/dist/bridge/surface/methods/sandbox/seed.js +3 -2
- package/dist/bridge/surface/methods/sandbox/seed.js.map +1 -1
- package/dist/bridge/surface/methods/sandbox/seedFromFixture.d.ts +1 -0
- package/dist/bridge/surface/methods/sandbox/seedFromFixture.d.ts.map +1 -1
- package/dist/bridge/surface/methods/sandbox/seedFromFixture.js +1 -0
- package/dist/bridge/surface/methods/sandbox/seedFromFixture.js.map +1 -1
- package/dist/bridge/surface/methods/sandbox/setClock.d.ts +2 -1
- package/dist/bridge/surface/methods/sandbox/setClock.d.ts.map +1 -1
- package/dist/bridge/surface/methods/sandbox/setClock.js +1 -0
- package/dist/bridge/surface/methods/sandbox/setClock.js.map +1 -1
- package/dist/bridge/surface/methods/sandbox-registry.d.ts +6 -0
- package/dist/bridge/surface/methods/sandbox-registry.d.ts.map +1 -0
- package/dist/bridge/surface/methods/sandbox-registry.js +21 -0
- package/dist/bridge/surface/methods/sandbox-registry.js.map +1 -0
- package/dist/bridge/surface/methods/storage/deleteObject.d.ts +2 -1
- package/dist/bridge/surface/methods/storage/deleteObject.d.ts.map +1 -1
- package/dist/bridge/surface/methods/storage/deleteObject.js +1 -0
- package/dist/bridge/surface/methods/storage/deleteObject.js.map +1 -1
- package/dist/bridge/surface/methods/storage/getBytes.d.ts +2 -1
- package/dist/bridge/surface/methods/storage/getBytes.d.ts.map +1 -1
- package/dist/bridge/surface/methods/storage/getBytes.js +1 -0
- package/dist/bridge/surface/methods/storage/getBytes.js.map +1 -1
- package/dist/bridge/surface/methods/storage/getDownloadURL.d.ts +2 -1
- package/dist/bridge/surface/methods/storage/getDownloadURL.d.ts.map +1 -1
- package/dist/bridge/surface/methods/storage/getDownloadURL.js +1 -0
- package/dist/bridge/surface/methods/storage/getDownloadURL.js.map +1 -1
- package/dist/bridge/surface/methods/storage/getMetadata.d.ts +2 -1
- package/dist/bridge/surface/methods/storage/getMetadata.d.ts.map +1 -1
- package/dist/bridge/surface/methods/storage/getMetadata.js +1 -0
- package/dist/bridge/surface/methods/storage/getMetadata.js.map +1 -1
- package/dist/bridge/surface/methods/storage/listAll.d.ts +2 -1
- package/dist/bridge/surface/methods/storage/listAll.d.ts.map +1 -1
- package/dist/bridge/surface/methods/storage/listAll.js +1 -0
- package/dist/bridge/surface/methods/storage/listAll.js.map +1 -1
- package/dist/bridge/surface/methods/storage/provision.d.ts +1 -0
- package/dist/bridge/surface/methods/storage/provision.d.ts.map +1 -1
- package/dist/bridge/surface/methods/storage/provision.js +1 -0
- package/dist/bridge/surface/methods/storage/provision.js.map +1 -1
- package/dist/bridge/surface/methods/storage/setCrossServiceIam.d.ts +2 -1
- package/dist/bridge/surface/methods/storage/setCrossServiceIam.d.ts.map +1 -1
- package/dist/bridge/surface/methods/storage/setCrossServiceIam.js +1 -0
- package/dist/bridge/surface/methods/storage/setCrossServiceIam.js.map +1 -1
- package/dist/bridge/surface/methods/storage/status.d.ts +1 -0
- package/dist/bridge/surface/methods/storage/status.d.ts.map +1 -1
- package/dist/bridge/surface/methods/storage/status.js +1 -0
- package/dist/bridge/surface/methods/storage/status.js.map +1 -1
- package/dist/bridge/surface/methods/storage/updateMetadata.d.ts +6 -5
- package/dist/bridge/surface/methods/storage/updateMetadata.d.ts.map +1 -1
- package/dist/bridge/surface/methods/storage/updateMetadata.js +1 -0
- package/dist/bridge/surface/methods/storage/updateMetadata.js.map +1 -1
- package/dist/bridge/surface/methods/storage/uploadBytes.d.ts +1 -0
- package/dist/bridge/surface/methods/storage/uploadBytes.d.ts.map +1 -1
- package/dist/bridge/surface/methods/storage/uploadBytes.js +1 -0
- package/dist/bridge/surface/methods/storage/uploadBytes.js.map +1 -1
- package/dist/bridge/surface/render/discriminator-schemas.d.ts +2 -2
- package/dist/bridge/surface/request-instant.d.ts +2 -2
- package/dist/bridge/surface/request-instant.d.ts.map +1 -1
- package/dist/bridge/surface/request-instant.js.map +1 -1
- package/dist/bridge/surface/rules-engines/database.d.ts.map +1 -1
- package/dist/bridge/surface/rules-engines/database.js +1 -2
- package/dist/bridge/surface/rules-engines/database.js.map +1 -1
- package/dist/bridge/surface/rules-engines/storage-request-resource.d.ts +38 -0
- package/dist/bridge/surface/rules-engines/storage-request-resource.d.ts.map +1 -0
- package/dist/bridge/surface/rules-engines/storage-request-resource.js +246 -0
- package/dist/bridge/surface/rules-engines/storage-request-resource.js.map +1 -0
- package/dist/bridge/surface/rules-engines/storage.d.ts.map +1 -1
- package/dist/bridge/surface/rules-engines/storage.js +44 -65
- package/dist/bridge/surface/rules-engines/storage.js.map +1 -1
- package/dist/bridge/surface/sandbox-context.d.ts +25 -0
- package/dist/bridge/surface/sandbox-context.d.ts.map +1 -0
- package/dist/bridge/surface/sandbox-context.js +29 -0
- package/dist/bridge/surface/sandbox-context.js.map +1 -0
- package/dist/bridge/surface/service-handles.d.ts +6 -6
- package/dist/bridge/surface/service-handles.d.ts.map +1 -1
- package/dist/bridge/surface/service-handles.js +7 -4
- package/dist/bridge/surface/service-handles.js.map +1 -1
- package/dist/bridge/surface/sign-in.d.ts +2 -2
- package/dist/bridge/surface/sign-in.d.ts.map +1 -1
- package/dist/bridge/surface/sign-in.js +1 -1
- package/dist/bridge/surface/sign-in.js.map +1 -1
- package/dist/bridge/surface/storage-rules.d.ts +2 -9
- package/dist/bridge/surface/storage-rules.d.ts.map +1 -1
- package/dist/bridge/surface/storage-rules.js +0 -10
- package/dist/bridge/surface/storage-rules.js.map +1 -1
- package/dist/bridge/surface/stored-identity.d.ts +3 -3
- package/dist/bridge/surface/stored-identity.d.ts.map +1 -1
- package/dist/bridge/surface/stored-identity.js.map +1 -1
- package/dist/bridge/surface/types.d.ts +29 -8
- package/dist/bridge/surface/types.d.ts.map +1 -1
- package/dist/cli/firebase-json.d.ts +35 -7
- package/dist/cli/firebase-json.d.ts.map +1 -1
- package/dist/cli/firebase-json.js +13 -0
- package/dist/cli/firebase-json.js.map +1 -1
- package/dist/cli/firestore-indexes.d.ts.map +1 -1
- package/dist/cli/firestore-indexes.js +2 -1
- package/dist/cli/firestore-indexes.js.map +1 -1
- package/dist/cli/index.d.ts +6 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +44 -11
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/public-dir-hint.d.ts +8 -0
- package/dist/cli/public-dir-hint.d.ts.map +1 -0
- package/dist/cli/public-dir-hint.js +30 -0
- package/dist/cli/public-dir-hint.js.map +1 -0
- package/dist/cli/run.d.ts +8 -2
- package/dist/cli/run.d.ts.map +1 -1
- package/dist/cli/run.js +28 -5
- package/dist/cli/run.js.map +1 -1
- package/dist/cli/sandbox-runner.d.ts.map +1 -1
- package/dist/cli/sandbox-runner.js +11 -2
- package/dist/cli/sandbox-runner.js.map +1 -1
- package/dist/cli/serve-diagnostics.d.ts.map +1 -1
- package/dist/cli/serve-diagnostics.js +11 -1
- package/dist/cli/serve-diagnostics.js.map +1 -1
- package/dist/cli/serve.d.ts.map +1 -1
- package/dist/cli/serve.js +25 -24
- package/dist/cli/serve.js.map +1 -1
- package/dist/cli/snapshot.d.ts.map +1 -1
- package/dist/cli/snapshot.js +13 -5
- package/dist/cli/snapshot.js.map +1 -1
- package/dist/cli/surface-method-runner.js +1 -1
- package/dist/cli/surface-method-runner.js.map +1 -1
- package/dist/cli/verify.d.ts.map +1 -1
- package/dist/cli/verify.js +10 -1
- package/dist/cli/verify.js.map +1 -1
- package/dist/conformance/.generated/can-i-use-browser.d.ts.map +1 -1
- package/dist/conformance/.generated/can-i-use-browser.js +19 -3
- package/dist/conformance/.generated/can-i-use-browser.js.map +1 -1
- package/dist/conformance/.generated/can-i-use.d.ts.map +1 -1
- package/dist/conformance/.generated/can-i-use.js +1 -1
- package/dist/conformance/.generated/can-i-use.js.map +1 -1
- package/dist/conformance/.generated/conformance-docs.d.ts.map +1 -1
- package/dist/conformance/.generated/conformance-docs.js +1 -1
- package/dist/conformance/.generated/conformance-docs.js.map +1 -1
- package/dist/conformance/.generated/served-availability.d.ts.map +1 -1
- package/dist/conformance/.generated/served-availability.js +1 -1
- package/dist/conformance/.generated/served-availability.js.map +1 -1
- package/dist/discover/merge.d.ts.map +1 -1
- package/dist/discover/merge.js +11 -3
- package/dist/discover/merge.js.map +1 -1
- package/dist/functions/child-protocol.d.ts +43 -0
- package/dist/functions/child-protocol.d.ts.map +1 -0
- package/dist/functions/child-protocol.js +14 -0
- package/dist/functions/child-protocol.js.map +1 -0
- package/dist/functions/child.d.ts +2 -0
- package/dist/functions/child.d.ts.map +1 -0
- package/dist/functions/child.js +92 -0
- package/dist/functions/child.js.map +1 -0
- package/dist/{functions-rtdb → functions}/development-runtime.d.ts +5 -4
- package/dist/functions/development-runtime.d.ts.map +1 -0
- package/dist/{functions-rtdb → functions}/development-runtime.js +2 -2
- package/dist/functions/development-runtime.js.map +1 -0
- package/dist/functions/endpoints.d.ts +39 -0
- package/dist/functions/endpoints.d.ts.map +1 -0
- package/dist/functions/endpoints.js +48 -0
- package/dist/functions/endpoints.js.map +1 -0
- package/dist/functions/events.d.ts.map +1 -0
- package/dist/functions/events.js.map +1 -0
- package/dist/functions/execution-log.d.ts.map +1 -0
- package/dist/functions/execution-log.js.map +1 -0
- package/dist/functions/module-loader.d.ts.map +1 -0
- package/dist/functions/module-loader.js.map +1 -0
- package/dist/functions/project.d.ts +7 -0
- package/dist/functions/project.d.ts.map +1 -0
- package/dist/{functions-rtdb → functions}/project.js +1 -1
- package/dist/functions/project.js.map +1 -0
- package/dist/functions/rtdb/child-runtime.d.ts +23 -0
- package/dist/functions/rtdb/child-runtime.d.ts.map +1 -0
- package/dist/functions/rtdb/child-runtime.js +102 -0
- package/dist/functions/rtdb/child-runtime.js.map +1 -0
- package/dist/functions/rtdb/delivery.d.ts.map +1 -0
- package/dist/functions/rtdb/delivery.js.map +1 -0
- package/dist/{functions-rtdb → functions/rtdb}/discovery.d.ts +3 -20
- package/dist/functions/rtdb/discovery.d.ts.map +1 -0
- package/dist/{functions-rtdb → functions/rtdb}/discovery.js +6 -21
- package/dist/functions/rtdb/discovery.js.map +1 -0
- package/dist/functions/rtdb/event.d.ts.map +1 -0
- package/dist/functions/rtdb/event.js.map +1 -0
- package/dist/functions/rtdb/execution.d.ts.map +1 -0
- package/dist/functions/rtdb/execution.js.map +1 -0
- package/dist/functions/rtdb/in-memory-delivery.d.ts.map +1 -0
- package/dist/functions/rtdb/in-memory-delivery.js.map +1 -0
- package/dist/functions/rtdb/projection.d.ts.map +1 -0
- package/dist/functions/rtdb/projection.js.map +1 -0
- package/dist/functions/rtdb/reference-pattern.d.ts.map +1 -0
- package/dist/functions/rtdb/reference-pattern.js.map +1 -0
- package/dist/{functions-rtdb → functions/rtdb}/remote-delivery.d.ts +1 -1
- package/dist/functions/rtdb/remote-delivery.d.ts.map +1 -0
- package/dist/functions/rtdb/remote-delivery.js.map +1 -0
- package/dist/{functions-rtdb/child.d.ts → functions/spawn.d.ts} +16 -38
- package/dist/functions/spawn.d.ts.map +1 -0
- package/dist/functions/spawn.js +166 -0
- package/dist/functions/spawn.js.map +1 -0
- package/dist/functions/unsupported.d.ts +23 -0
- package/dist/functions/unsupported.d.ts.map +1 -0
- package/dist/functions/unsupported.js +25 -0
- package/dist/functions/unsupported.js.map +1 -0
- package/dist/google-endpoints.d.ts +9 -2
- package/dist/google-endpoints.d.ts.map +1 -1
- package/dist/google-endpoints.js +72 -3
- package/dist/google-endpoints.js.map +1 -1
- package/dist/next/bridge-import-loader.d.ts +9 -0
- package/dist/next/bridge-import-loader.d.ts.map +1 -0
- package/dist/next/bridge-import-loader.js +45 -0
- package/dist/next/bridge-import-loader.js.map +1 -0
- package/dist/next/bridge-imports.d.ts +26 -0
- package/dist/next/bridge-imports.d.ts.map +1 -0
- package/dist/next/bridge-imports.js +90 -0
- package/dist/next/bridge-imports.js.map +1 -0
- package/dist/next/client-aliases.d.ts +13 -5
- package/dist/next/client-aliases.d.ts.map +1 -1
- package/dist/next/client-aliases.js +14 -27
- package/dist/next/client-aliases.js.map +1 -1
- package/dist/next/guard.d.ts +3 -2
- package/dist/next/guard.d.ts.map +1 -1
- package/dist/next/guard.js +28 -10
- package/dist/next/guard.js.map +1 -1
- package/dist/next/turbopack-config.d.ts +9 -0
- package/dist/next/turbopack-config.d.ts.map +1 -1
- package/dist/next/turbopack-config.js +21 -0
- package/dist/next/turbopack-config.js.map +1 -1
- package/dist/next/webpack-config.d.ts +9 -0
- package/dist/next/webpack-config.d.ts.map +1 -1
- package/dist/next/webpack-config.js +18 -0
- package/dist/next/webpack-config.js.map +1 -1
- package/dist/next/with-pyric.d.ts +1 -0
- package/dist/next/with-pyric.d.ts.map +1 -1
- package/dist/next/with-pyric.js +25 -0
- package/dist/next/with-pyric.js.map +1 -1
- package/dist/register/activation.d.ts +16 -0
- package/dist/register/activation.d.ts.map +1 -0
- package/dist/register/activation.js +22 -0
- package/dist/register/activation.js.map +1 -0
- package/dist/register/app-bridge-admin-app-check.d.ts.map +1 -1
- package/dist/register/app-bridge-admin-app-check.js +3 -1
- package/dist/register/app-bridge-admin-app-check.js.map +1 -1
- package/dist/register/child-process-guard.d.ts +43 -0
- package/dist/register/child-process-guard.d.ts.map +1 -0
- package/dist/register/child-process-guard.js +165 -0
- package/dist/register/child-process-guard.js.map +1 -0
- package/dist/register/client/bootstrap.js +6 -1
- package/dist/register/client/bootstrap.js.map +1 -1
- package/dist/register/connect-prototypes.d.ts +9 -0
- package/dist/register/connect-prototypes.d.ts.map +1 -1
- package/dist/register/connect-prototypes.js +11 -0
- package/dist/register/connect-prototypes.js.map +1 -1
- package/dist/register/hooks.d.ts +7 -1
- package/dist/register/hooks.d.ts.map +1 -1
- package/dist/register/hooks.js +44 -2
- package/dist/register/hooks.js.map +1 -1
- package/dist/register/index.d.ts.map +1 -1
- package/dist/register/index.js +93 -23
- package/dist/register/index.js.map +1 -1
- package/dist/register/mapping.d.ts +33 -0
- package/dist/register/mapping.d.ts.map +1 -1
- package/dist/register/mapping.js +45 -3
- package/dist/register/mapping.js.map +1 -1
- package/dist/register/net-guard.d.ts +17 -0
- package/dist/register/net-guard.d.ts.map +1 -1
- package/dist/register/net-guard.js +118 -41
- package/dist/register/net-guard.js.map +1 -1
- package/dist/register/package-identity.d.ts +16 -0
- package/dist/register/package-identity.d.ts.map +1 -0
- package/dist/register/package-identity.js +97 -0
- package/dist/register/package-identity.js.map +1 -0
- package/dist/register/swap-or-stop.d.ts +27 -0
- package/dist/register/swap-or-stop.d.ts.map +1 -0
- package/dist/register/swap-or-stop.js +170 -0
- package/dist/register/swap-or-stop.js.map +1 -0
- package/dist/register/worker-guard.d.ts +20 -0
- package/dist/register/worker-guard.d.ts.map +1 -0
- package/dist/register/worker-guard.js +77 -0
- package/dist/register/worker-guard.js.map +1 -0
- package/dist/remote/resolve-host.d.ts +9 -0
- package/dist/remote/resolve-host.d.ts.map +1 -1
- package/dist/remote/resolve-host.js +13 -2
- package/dist/remote/resolve-host.js.map +1 -1
- package/dist/serve/ai-proxy.d.ts +2 -0
- package/dist/serve/ai-proxy.d.ts.map +1 -1
- package/dist/serve/ai-proxy.js +33 -8
- package/dist/serve/ai-proxy.js.map +1 -1
- package/dist/serve/app-ports.d.ts +48 -0
- package/dist/serve/app-ports.d.ts.map +1 -0
- package/dist/serve/app-ports.js +82 -0
- package/dist/serve/app-ports.js.map +1 -0
- package/dist/serve/bridge-mount.d.ts +13 -0
- package/dist/serve/bridge-mount.d.ts.map +1 -1
- package/dist/serve/bridge-mount.js +28 -8
- package/dist/serve/bridge-mount.js.map +1 -1
- package/dist/serve/bundler.d.ts +6 -0
- package/dist/serve/bundler.d.ts.map +1 -1
- package/dist/serve/bundler.js +78 -26
- package/dist/serve/bundler.js.map +1 -1
- package/dist/serve/discovery.d.ts +17 -3
- package/dist/serve/discovery.d.ts.map +1 -1
- package/dist/serve/discovery.js +199 -63
- package/dist/serve/discovery.js.map +1 -1
- package/dist/serve/entries/auth.d.ts.map +1 -1
- package/dist/serve/entries/auth.js +19 -1
- package/dist/serve/entries/auth.js.map +1 -1
- package/dist/serve/entries/firestore-surface.d.ts +1 -0
- package/dist/serve/entries/firestore-surface.d.ts.map +1 -1
- package/dist/serve/entries/firestore-surface.js +1 -0
- package/dist/serve/entries/firestore-surface.js.map +1 -1
- package/dist/serve/entries/init-payload.d.ts +9 -2
- package/dist/serve/entries/init-payload.d.ts.map +1 -1
- package/dist/serve/entries/init-payload.js +25 -5
- package/dist/serve/entries/init-payload.js.map +1 -1
- package/dist/serve/entries/moved-host.d.ts +18 -0
- package/dist/serve/entries/moved-host.d.ts.map +1 -0
- package/dist/serve/entries/moved-host.js +14 -0
- package/dist/serve/entries/moved-host.js.map +1 -0
- package/dist/serve/entries/runtime-origin.d.ts +6 -0
- package/dist/serve/entries/runtime-origin.d.ts.map +1 -1
- package/dist/serve/entries/runtime-origin.js.map +1 -1
- package/dist/serve/entries/unsupported/firestore.d.ts +0 -1
- package/dist/serve/entries/unsupported/firestore.d.ts.map +1 -1
- package/dist/serve/entries/unsupported/firestore.js +0 -1
- package/dist/serve/entries/unsupported/firestore.js.map +1 -1
- package/dist/serve/entries/worker-runtime.d.ts.map +1 -1
- package/dist/serve/entries/worker-runtime.js +19 -7
- package/dist/serve/entries/worker-runtime.js.map +1 -1
- package/dist/serve/firebase-module-swap.d.ts +19 -2
- package/dist/serve/firebase-module-swap.d.ts.map +1 -1
- package/dist/serve/firebase-module-swap.js +92 -16
- package/dist/serve/firebase-module-swap.js.map +1 -1
- package/dist/serve/gemini-proxy.js +2 -2
- package/dist/serve/gemini-proxy.js.map +1 -1
- package/dist/serve/hosted/runtime.d.ts.map +1 -1
- package/dist/serve/hosted/runtime.js +10 -3
- package/dist/serve/hosted/runtime.js.map +1 -1
- package/dist/serve/namespace.d.ts +7 -0
- package/dist/serve/namespace.d.ts.map +1 -1
- package/dist/serve/namespace.js +32 -24
- package/dist/serve/namespace.js.map +1 -1
- package/dist/serve/repro/compare.d.ts +2 -1
- package/dist/serve/repro/compare.d.ts.map +1 -1
- package/dist/serve/repro/compare.js +9 -1
- package/dist/serve/repro/compare.js.map +1 -1
- package/dist/serve/rules-fixture-check.d.ts.map +1 -1
- package/dist/serve/rules-fixture-check.js +22 -10
- package/dist/serve/rules-fixture-check.js.map +1 -1
- package/dist/serve/rules.d.ts +35 -9
- package/dist/serve/rules.d.ts.map +1 -1
- package/dist/serve/rules.js +72 -21
- package/dist/serve/rules.js.map +1 -1
- package/dist/serve/sandbox-session.d.ts +3 -0
- package/dist/serve/sandbox-session.d.ts.map +1 -1
- package/dist/serve/sandbox-session.js +10 -3
- package/dist/serve/sandbox-session.js.map +1 -1
- package/dist/serve/server.d.ts +10 -2
- package/dist/serve/server.d.ts.map +1 -1
- package/dist/serve/server.js +24 -4
- package/dist/serve/server.js.map +1 -1
- package/dist/serve/site-ui/_astro/{RulesCodeEditor.Cp_-FK3b.js → RulesCodeEditor.D-eQLCvu.js} +1 -1
- package/dist/serve/site-ui/_astro/app.BjQiEBOY.js +1 -0
- package/dist/serve/site-ui/_astro/app.C84Qr10v.js +151 -0
- package/dist/serve/site-ui/_astro/{chess-showcase.CVhCNUi_.js → chess-showcase.CsH1s4g7.js} +1 -1
- package/dist/serve/site-ui/_astro/{example-runtime.zC1l-Yka.js → example-runtime.CTp8mh7I.js} +1 -1
- package/dist/serve/site-ui/_astro/sandbox-controls.DI8MTU3L.js +1725 -0
- package/dist/serve/site-ui/_astro/{seed.B4vza_Bv.js → seed.BvtbLu-q.js} +1 -1
- package/dist/serve/site-ui/_astro/writes.D8gcuovU.js +1 -0
- package/dist/serve/site-ui/auth/index.html +1 -1
- package/dist/serve/site-ui/docs/ai-compat/index.html +48 -9
- package/dist/serve/site-ui/docs/ai-compat.md +53 -8
- package/dist/serve/site-ui/docs/app-check-compat/index.html +102 -26
- package/dist/serve/site-ui/docs/app-check-compat.md +107 -25
- package/dist/serve/site-ui/docs/auth-compat/index.html +33 -21
- package/dist/serve/site-ui/docs/auth-compat.md +35 -20
- package/dist/serve/site-ui/docs/build/ai-logic/index.html +13 -4
- package/dist/serve/site-ui/docs/build/ai-logic.md +14 -4
- package/dist/serve/site-ui/docs/conformance-scores/index.html +2 -2
- package/dist/serve/site-ui/docs/conformance-scores.md +2 -2
- package/dist/serve/site-ui/docs/database-compat/index.html +113 -49
- package/dist/serve/site-ui/docs/database-compat.md +118 -48
- package/dist/serve/site-ui/docs/firestore-compat/index.html +144 -37
- package/dist/serve/site-ui/docs/firestore-compat.md +149 -36
- package/dist/serve/site-ui/docs/firestore-swift-compat/index.html +1 -1
- package/dist/serve/site-ui/docs/firestore-swift-compat.md +1 -1
- package/dist/serve/site-ui/docs/functions-rtdb-compat/index.html +12 -12
- package/dist/serve/site-ui/docs/functions-rtdb-compat.md +12 -12
- package/dist/serve/site-ui/docs/get-started/how-the-swap-works/index.html +1 -1
- package/dist/serve/site-ui/docs/get-started/how-the-swap-works.md +1 -1
- package/dist/serve/site-ui/docs/get-started/node-host/index.html +1 -0
- package/dist/serve/site-ui/docs/get-started/node-host.md +2 -0
- package/dist/serve/site-ui/docs/index.json +45 -5
- package/dist/serve/site-ui/docs/messaging-compat/index.html +2 -2
- package/dist/serve/site-ui/docs/messaging-compat.md +2 -2
- package/dist/serve/site-ui/docs/pyric-ai-scripting-reference-api/index.html +3 -1
- package/dist/serve/site-ui/docs/pyric-ai-scripting-reference-api.md +2 -0
- package/dist/serve/site-ui/docs/pyric-app-check-reference-api/index.html +11 -3
- package/dist/serve/site-ui/docs/pyric-app-check-reference-api.md +13 -3
- package/dist/serve/site-ui/docs/pyric-cli-bridge-client-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-cli-bridge-client-reference-api.md +1 -1
- package/dist/serve/site-ui/docs/pyric-cli-next-reference-api/index.html +1 -0
- package/dist/serve/site-ui/docs/pyric-cli-next-reference-api.md +1 -0
- package/dist/serve/site-ui/docs/pyric-cli-register-client-firestore-reference-api/index.html +1 -1
- package/dist/serve/site-ui/docs/pyric-cli-register-client-firestore-reference-api.md +1 -1
- package/dist/serve/site-ui/docs/pyric-cli-serve-worker-reference-api/index.html +17 -9
- package/dist/serve/site-ui/docs/pyric-cli-serve-worker-reference-api.md +13 -11
- package/dist/serve/site-ui/docs/pyric-database-reference-api/index.html +8 -4
- package/dist/serve/site-ui/docs/pyric-database-reference-api.md +9 -4
- package/dist/serve/site-ui/docs/pyric-rules-reference-api/index.html +15 -4
- package/dist/serve/site-ui/docs/pyric-rules-reference-api.md +21 -4
- package/dist/serve/site-ui/docs/pyric-storage-reference-api/index.html +7 -1
- package/dist/serve/site-ui/docs/pyric-storage-reference-api.md +1 -0
- package/dist/serve/site-ui/docs/reference/cli/index.html +4 -3
- package/dist/serve/site-ui/docs/reference/cli.md +5 -3
- package/dist/serve/site-ui/docs/rules-compat/index.html +150 -49
- package/dist/serve/site-ui/docs/rules-compat.md +149 -51
- package/dist/serve/site-ui/docs/storage-compat/index.html +20 -20
- package/dist/serve/site-ui/docs/storage-compat.md +20 -20
- package/dist/serve/site-ui/docs/tutorial/index.html +2 -1
- package/dist/serve/site-ui/docs/tutorial.md +2 -1
- package/dist/serve/site-ui/examples/chess/index.html +1 -1
- package/dist/serve/site-ui/firestore/index.html +1 -1
- package/dist/serve/site-ui/rtdb/index.html +1 -1
- package/dist/serve/site-ui/settings/index.html +1 -1
- package/dist/serve/site-ui/storage/index.html +1 -1
- package/dist/serve/site-ui/studio/index.html +1 -1
- package/dist/serve/site-ui/traffic/index.html +1 -1
- package/dist/serve/vite-ai-config.d.ts +8 -0
- package/dist/serve/vite-ai-config.d.ts.map +1 -1
- package/dist/serve/vite-ai-config.js +16 -1
- package/dist/serve/vite-ai-config.js.map +1 -1
- package/dist/serve/vite-functions-development.d.ts +3 -3
- package/dist/serve/vite-functions-development.d.ts.map +1 -1
- package/dist/serve/vite-functions-development.js +1 -1
- package/dist/serve/vite-functions-development.js.map +1 -1
- package/dist/serve/vite-generation-bridge.d.ts +2 -2
- package/dist/serve/vite-generation-bridge.d.ts.map +1 -1
- package/dist/serve/vite-generation-functions.d.ts +3 -3
- package/dist/serve/vite-generation-functions.d.ts.map +1 -1
- package/dist/serve/vite-generation-functions.js +1 -1
- package/dist/serve/vite-generation-functions.js.map +1 -1
- package/dist/serve/vite-generation-middleware.d.ts +1 -1
- package/dist/serve/vite-generation-middleware.d.ts.map +1 -1
- package/dist/serve/vite-generation-middleware.js +36 -1
- package/dist/serve/vite-generation-middleware.js.map +1 -1
- package/dist/serve/vite-module-swap.d.ts.map +1 -1
- package/dist/serve/vite-module-swap.js +8 -0
- package/dist/serve/vite-module-swap.js.map +1 -1
- package/dist/serve/vite-page-runtime.d.ts.map +1 -1
- package/dist/serve/vite-page-runtime.js +7 -1
- package/dist/serve/vite-page-runtime.js.map +1 -1
- package/dist/serve/vite-rules-source.d.ts +1 -1
- package/dist/serve/vite-rules-source.d.ts.map +1 -1
- package/dist/serve/vite-rules-source.js +2 -4
- package/dist/serve/vite-rules-source.js.map +1 -1
- package/dist/serve/vite-sandbox-generation.d.ts +2 -2
- package/dist/serve/vite-sandbox-generation.d.ts.map +1 -1
- package/dist/serve/vite-sandbox-generation.js +2 -2
- package/dist/serve/vite-sandbox-generation.js.map +1 -1
- package/dist/serve/worker/client/auth.d.ts +29 -5
- package/dist/serve/worker/client/auth.d.ts.map +1 -1
- package/dist/serve/worker/client/auth.js +61 -11
- package/dist/serve/worker/client/auth.js.map +1 -1
- package/dist/serve/worker/client/clock.d.ts +0 -16
- package/dist/serve/worker/client/clock.d.ts.map +1 -1
- package/dist/serve/worker/client/clock.js +23 -10
- package/dist/serve/worker/client/clock.js.map +1 -1
- package/dist/serve/worker/client/firestore-reads.d.ts +12 -5
- package/dist/serve/worker/client/firestore-reads.d.ts.map +1 -1
- package/dist/serve/worker/client/firestore-reads.js +47 -16
- package/dist/serve/worker/client/firestore-reads.js.map +1 -1
- package/dist/serve/worker/client/firestore-refs.d.ts.map +1 -1
- package/dist/serve/worker/client/firestore-refs.js +4 -14
- package/dist/serve/worker/client/firestore-refs.js.map +1 -1
- package/dist/serve/worker/client/rtdb-connection-lifecycle.d.ts.map +1 -1
- package/dist/serve/worker/client/rtdb-connection-lifecycle.js +5 -0
- package/dist/serve/worker/client/rtdb-connection-lifecycle.js.map +1 -1
- package/dist/serve/worker/client/rtdb-snapshots.d.ts +7 -0
- package/dist/serve/worker/client/rtdb-snapshots.d.ts.map +1 -1
- package/dist/serve/worker/client/rtdb-snapshots.js +25 -1
- package/dist/serve/worker/client/rtdb-snapshots.js.map +1 -1
- package/dist/serve/worker/client/rtdb-value-validation.d.ts +21 -0
- package/dist/serve/worker/client/rtdb-value-validation.d.ts.map +1 -0
- package/dist/serve/worker/client/rtdb-value-validation.js +183 -0
- package/dist/serve/worker/client/rtdb-value-validation.js.map +1 -0
- package/dist/serve/worker/client/rtdb-writes.d.ts.map +1 -1
- package/dist/serve/worker/client/rtdb-writes.js +7 -0
- package/dist/serve/worker/client/rtdb-writes.js.map +1 -1
- package/dist/serve/worker/client/snapshots.d.ts +2 -0
- package/dist/serve/worker/client/snapshots.d.ts.map +1 -1
- package/dist/serve/worker/client/snapshots.js +13 -7
- package/dist/serve/worker/client/snapshots.js.map +1 -1
- package/dist/serve/worker/client/websocket-connection.d.ts +6 -0
- package/dist/serve/worker/client/websocket-connection.d.ts.map +1 -1
- package/dist/serve/worker/client/websocket-connection.js +32 -3
- package/dist/serve/worker/client/websocket-connection.js.map +1 -1
- package/dist/serve/worker/host/auth-credential-sign-in.d.ts +33 -0
- package/dist/serve/worker/host/auth-credential-sign-in.d.ts.map +1 -0
- package/dist/serve/worker/host/auth-credential-sign-in.js +70 -0
- package/dist/serve/worker/host/auth-credential-sign-in.js.map +1 -0
- package/dist/serve/worker/host/auth-reauthentication.js +3 -3
- package/dist/serve/worker/host/auth-reauthentication.js.map +1 -1
- package/dist/serve/worker/host/rules.d.ts +15 -1
- package/dist/serve/worker/host/rules.d.ts.map +1 -1
- package/dist/serve/worker/host/rules.js +43 -34
- package/dist/serve/worker/host/rules.js.map +1 -1
- package/dist/serve/worker/host/staged-sign-in-results.d.ts +24 -0
- package/dist/serve/worker/host/staged-sign-in-results.d.ts.map +1 -0
- package/dist/serve/worker/host/staged-sign-in-results.js +40 -0
- package/dist/serve/worker/host/staged-sign-in-results.js.map +1 -0
- package/dist/serve/worker/host/storage.d.ts.map +1 -1
- package/dist/serve/worker/host/storage.js +20 -12
- package/dist/serve/worker/host/storage.js.map +1 -1
- package/dist/serve/worker/host/subscriptions.d.ts.map +1 -1
- package/dist/serve/worker/host/subscriptions.js +6 -2
- package/dist/serve/worker/host/subscriptions.js.map +1 -1
- package/dist/serve/worker/host-auth.d.ts +3 -0
- package/dist/serve/worker/host-auth.d.ts.map +1 -1
- package/dist/serve/worker/host-auth.js +32 -13
- package/dist/serve/worker/host-auth.js.map +1 -1
- package/dist/serve/worker/inbound-validation/operation-arguments.d.ts.map +1 -1
- package/dist/serve/worker/inbound-validation/operation-arguments.js +27 -14
- package/dist/serve/worker/inbound-validation/operation-arguments.js.map +1 -1
- package/dist/serve/worker/operation-persistence.d.ts +1 -0
- package/dist/serve/worker/operation-persistence.d.ts.map +1 -1
- package/dist/serve/worker/operation-persistence.js +1 -0
- package/dist/serve/worker/operation-persistence.js.map +1 -1
- package/dist/serve/worker/protocol.d.ts +9 -1
- package/dist/serve/worker/protocol.d.ts.map +1 -1
- package/dist/serve/worker/protocol.js.map +1 -1
- package/dist/serve/worker/rtdb-value-equality.d.ts.map +1 -1
- package/dist/serve/worker/rtdb-value-equality.js +3 -1
- package/dist/serve/worker/rtdb-value-equality.js.map +1 -1
- package/dist/serve/worker/serve-init.d.ts.map +1 -1
- package/dist/serve/worker/serve-init.js +3 -9
- package/dist/serve/worker/serve-init.js.map +1 -1
- package/dist/serve/worker-build-lock.d.ts +23 -0
- package/dist/serve/worker-build-lock.d.ts.map +1 -0
- package/dist/serve/worker-build-lock.js +179 -0
- package/dist/serve/worker-build-lock.js.map +1 -0
- package/dist/serve/worker-bundle-cache.d.ts +17 -0
- package/dist/serve/worker-bundle-cache.d.ts.map +1 -0
- package/dist/serve/worker-bundle-cache.js +89 -0
- package/dist/serve/worker-bundle-cache.js.map +1 -0
- package/dist/verify/cases.js +7 -3
- package/dist/verify/cases.js.map +1 -1
- package/dist/verify/index.d.ts.map +1 -1
- package/dist/verify/index.js +11 -5
- package/dist/verify/index.js.map +1 -1
- package/package.json +10 -4
- package/src/assurance/.generated/conformance-verdicts.ts +107 -40
- package/src/assurance/capabilities.ts +11 -15
- package/src/bounded-request.ts +66 -7
- package/src/bridge/client/bridge-url.ts +5 -1
- package/src/bridge/client/bridge.ts +19 -3
- package/src/bridge/frame-output.ts +45 -0
- package/src/bridge/protocol.ts +13 -0
- package/src/bridge/server/in-process.ts +57 -17
- package/src/bridge/server/peer.ts +11 -0
- package/src/bridge/server/standalone.ts +29 -107
- package/src/bridge/surface/arguments/sandbox.ts +2 -69
- package/src/bridge/surface/arguments/seed.ts +78 -0
- package/src/bridge/surface/context.ts +26 -37
- package/src/bridge/surface/database-instance-catalog.ts +4 -32
- package/src/bridge/surface/database-instance-project.ts +40 -0
- package/src/bridge/surface/database-instance.ts +4 -4
- package/src/bridge/surface/functions-runtime.ts +5 -5
- package/src/bridge/surface/held-identity.ts +4 -4
- package/src/bridge/surface/manifest.sandbox.generated.ts +137 -0
- package/src/bridge/surface/method-types.ts +61 -7
- package/src/bridge/surface/methods/ai_logic/clearScripts.ts +2 -1
- package/src/bridge/surface/methods/ai_logic/script.ts +2 -1
- package/src/bridge/surface/methods/ai_logic/scripts.ts +2 -1
- package/src/bridge/surface/methods/ai_logic/status.ts +1 -0
- package/src/bridge/surface/methods/assurance/attach.ts +1 -0
- package/src/bridge/surface/methods/assurance/canIUse.ts +1 -0
- package/src/bridge/surface/methods/assurance/define.ts +1 -0
- package/src/bridge/surface/methods/assurance/export.ts +1 -0
- package/src/bridge/surface/methods/assurance/inspect.ts +1 -0
- package/src/bridge/surface/methods/assurance/map.ts +1 -0
- package/src/bridge/surface/methods/assurance/minimize.ts +1 -0
- package/src/bridge/surface/methods/assurance/propose.ts +1 -0
- package/src/bridge/surface/methods/assurance/replaySession.ts +1 -0
- package/src/bridge/surface/methods/assurance/run.ts +1 -0
- package/src/bridge/surface/methods/assurance/start.ts +1 -0
- package/src/bridge/surface/methods/assurance/testRulesHosted.ts +1 -0
- package/src/bridge/surface/methods/assurance/verify.ts +1 -0
- package/src/bridge/surface/methods/assurance/verifyCases.ts +1 -0
- package/src/bridge/surface/methods/auth/actAsAdmin.ts +1 -0
- package/src/bridge/surface/methods/auth/actAsAnonymous.ts +1 -0
- package/src/bridge/surface/methods/auth/createCustomToken.ts +1 -0
- package/src/bridge/surface/methods/auth/createUser.ts +2 -1
- package/src/bridge/surface/methods/auth/deleteUser.ts +2 -1
- package/src/bridge/surface/methods/auth/getUser.ts +2 -1
- package/src/bridge/surface/methods/auth/getUserByEmail.ts +2 -1
- package/src/bridge/surface/methods/auth/impersonate.ts +1 -0
- package/src/bridge/surface/methods/auth/importUsers.ts +3 -2
- package/src/bridge/surface/methods/auth/listUsers.ts +2 -1
- package/src/bridge/surface/methods/auth/sessions.ts +1 -0
- package/src/bridge/surface/methods/auth/setCustomUserClaims.ts +2 -1
- package/src/bridge/surface/methods/auth/signInAnonymously.ts +1 -0
- package/src/bridge/surface/methods/auth/signInWithCredential.ts +1 -0
- package/src/bridge/surface/methods/auth/signInWithCustomToken.ts +1 -0
- package/src/bridge/surface/methods/auth/signInWithEmailAndPassword.ts +1 -0
- package/src/bridge/surface/methods/auth/signOut.ts +1 -0
- package/src/bridge/surface/methods/auth/takeAuthMail.ts +1 -0
- package/src/bridge/surface/methods/auth/updateUser.ts +2 -1
- package/src/bridge/surface/methods/auth/useAppSession.ts +1 -0
- package/src/bridge/surface/methods/auth/whoami.ts +1 -0
- package/src/bridge/surface/methods/database/crawl.ts +1 -0
- package/src/bridge/surface/methods/database/get.ts +1 -0
- package/src/bridge/surface/methods/database/push.ts +1 -0
- package/src/bridge/surface/methods/database/query.ts +1 -0
- package/src/bridge/surface/methods/database/remove.ts +1 -0
- package/src/bridge/surface/methods/database/set.ts +1 -0
- package/src/bridge/surface/methods/database/update.ts +1 -0
- package/src/bridge/surface/methods/firestore/addDoc.ts +2 -1
- package/src/bridge/surface/methods/firestore/deleteDoc.ts +2 -1
- package/src/bridge/surface/methods/firestore/discoverPaths.ts +1 -0
- package/src/bridge/surface/methods/firestore/extractIndexes.ts +1 -0
- package/src/bridge/surface/methods/firestore/findCollectionGroup.ts +1 -0
- package/src/bridge/surface/methods/firestore/getAggregateFromServer.ts +1 -0
- package/src/bridge/surface/methods/firestore/getCountFromServer.ts +1 -0
- package/src/bridge/surface/methods/firestore/getDoc.ts +2 -1
- package/src/bridge/surface/methods/firestore/getDocs.ts +2 -1
- package/src/bridge/surface/methods/firestore/setDoc.ts +2 -1
- package/src/bridge/surface/methods/firestore/updateDoc.ts +2 -1
- package/src/bridge/surface/methods/firestore/writeBatch.ts +2 -1
- package/src/bridge/surface/methods/firestore/writeIndexes.ts +1 -0
- package/src/bridge/surface/methods/functions/executions.ts +2 -1
- package/src/bridge/surface/methods/functions/fire.ts +7 -6
- package/src/bridge/surface/methods/functions/listTriggers.ts +1 -0
- package/src/bridge/surface/methods/messaging/deliveries.ts +1 -0
- package/src/bridge/surface/methods/messaging/send.ts +2 -1
- package/src/bridge/surface/methods/messaging/subscribeToTopic.ts +1 -0
- package/src/bridge/surface/methods/messaging/tokens.ts +1 -0
- package/src/bridge/surface/methods/messaging/unsubscribeFromTopic.ts +1 -0
- package/src/bridge/surface/methods/registry.ts +2 -12
- package/src/bridge/surface/methods/rules/explainDenial.ts +1 -0
- package/src/bridge/surface/methods/rules/getStdlib.ts +1 -0
- package/src/bridge/surface/methods/rules/lint.ts +1 -0
- package/src/bridge/surface/methods/rules/listStdlib.ts +1 -0
- package/src/bridge/surface/methods/rules/set.ts +1 -0
- package/src/bridge/surface/methods/rules/simulate.ts +1 -0
- package/src/bridge/surface/methods/sandbox/activity.ts +2 -1
- package/src/bridge/surface/methods/sandbox/advanceClock.ts +1 -0
- package/src/bridge/surface/methods/sandbox/apply.ts +1 -0
- package/src/bridge/surface/methods/sandbox/checkpoint.ts +1 -0
- package/src/bridge/surface/methods/sandbox/deleteCapture.ts +1 -1
- package/src/bridge/surface/methods/sandbox/deleteCheckpoint.ts +1 -0
- package/src/bridge/surface/methods/sandbox/diff.ts +1 -0
- package/src/bridge/surface/methods/sandbox/discard.ts +1 -0
- package/src/bridge/surface/methods/sandbox/events.ts +2 -1
- package/src/bridge/surface/methods/sandbox/exportFixture.ts +1 -0
- package/src/bridge/surface/methods/sandbox/fork.ts +1 -0
- package/src/bridge/surface/methods/sandbox/inspect.ts +2 -1
- package/src/bridge/surface/methods/sandbox/listBranches.ts +1 -0
- package/src/bridge/surface/methods/sandbox/listCaptures.ts +1 -1
- package/src/bridge/surface/methods/sandbox/listCheckpoints.ts +1 -0
- package/src/bridge/surface/methods/sandbox/listeners.ts +1 -0
- package/src/bridge/surface/methods/sandbox/openCapture.ts +1 -1
- package/src/bridge/surface/methods/sandbox/promote.ts +1 -0
- package/src/bridge/surface/methods/sandbox/renameCapture.ts +1 -1
- package/src/bridge/surface/methods/sandbox/reset.ts +3 -2
- package/src/bridge/surface/methods/sandbox/resetClock.ts +1 -0
- package/src/bridge/surface/methods/sandbox/restore.ts +1 -0
- package/src/bridge/surface/methods/sandbox/saveCapture.ts +1 -1
- package/src/bridge/surface/methods/sandbox/seed.ts +3 -2
- package/src/bridge/surface/methods/sandbox/seedFromFixture.ts +1 -0
- package/src/bridge/surface/methods/sandbox/setClock.ts +1 -0
- package/src/bridge/surface/methods/sandbox-registry.ts +24 -0
- package/src/bridge/surface/methods/storage/deleteObject.ts +1 -0
- package/src/bridge/surface/methods/storage/getBytes.ts +1 -0
- package/src/bridge/surface/methods/storage/getDownloadURL.ts +1 -0
- package/src/bridge/surface/methods/storage/getMetadata.ts +1 -0
- package/src/bridge/surface/methods/storage/listAll.ts +1 -0
- package/src/bridge/surface/methods/storage/provision.ts +1 -0
- package/src/bridge/surface/methods/storage/setCrossServiceIam.ts +1 -0
- package/src/bridge/surface/methods/storage/status.ts +1 -0
- package/src/bridge/surface/methods/storage/updateMetadata.ts +1 -0
- package/src/bridge/surface/methods/storage/uploadBytes.ts +1 -0
- package/src/bridge/surface/request-instant.ts +2 -2
- package/src/bridge/surface/rules-engines/database.ts +1 -2
- package/src/bridge/surface/rules-engines/storage-request-resource.ts +275 -0
- package/src/bridge/surface/rules-engines/storage.ts +54 -81
- package/src/bridge/surface/sandbox-context.ts +49 -0
- package/src/bridge/surface/service-handles.ts +13 -9
- package/src/bridge/surface/sign-in.ts +3 -3
- package/src/bridge/surface/storage-rules.ts +2 -14
- package/src/bridge/surface/stored-identity.ts +3 -3
- package/src/bridge/surface/types.ts +30 -8
- package/src/cli/firebase-json.ts +45 -7
- package/src/cli/firestore-indexes.ts +2 -1
- package/src/cli/index.ts +46 -11
- package/src/cli/public-dir-hint.ts +35 -0
- package/src/cli/run.ts +35 -7
- package/src/cli/sandbox-runner.ts +10 -2
- package/src/cli/serve-diagnostics.ts +12 -1
- package/src/cli/serve.ts +27 -26
- package/src/cli/service-commands.test.ts +7 -0
- package/src/cli/snapshot.ts +15 -6
- package/src/cli/surface-method-runner.ts +1 -1
- package/src/cli/verify.ts +11 -1
- package/src/conformance/.generated/can-i-use-browser.ts +22 -3
- package/src/conformance/.generated/can-i-use.ts +1 -1
- package/src/conformance/.generated/conformance-docs.ts +1 -1
- package/src/conformance/.generated/served-availability.ts +1 -1
- package/src/discover/merge.ts +12 -3
- package/src/functions/child-protocol.ts +59 -0
- package/src/functions/child.ts +107 -0
- package/src/{functions-rtdb → functions}/development-runtime.ts +13 -14
- package/src/functions/endpoints.ts +80 -0
- package/src/{functions-rtdb → functions}/project.ts +2 -2
- package/src/functions/rtdb/child-runtime.ts +139 -0
- package/src/{functions-rtdb → functions/rtdb}/discovery.ts +9 -43
- package/src/{functions-rtdb → functions/rtdb}/remote-delivery.ts +1 -1
- package/src/functions/spawn.ts +212 -0
- package/src/functions/unsupported.ts +42 -0
- package/src/google-endpoints.ts +74 -3
- package/src/next/bridge-import-loader.ts +56 -0
- package/src/next/bridge-imports.ts +108 -0
- package/src/next/client-aliases.ts +14 -27
- package/src/next/guard.ts +33 -11
- package/src/next/turbopack-config.ts +26 -0
- package/src/next/webpack-config.ts +26 -0
- package/src/next/with-pyric.ts +26 -0
- package/src/register/activation.ts +26 -0
- package/src/register/app-bridge-admin-app-check.ts +3 -1
- package/src/register/child-process-guard.ts +189 -0
- package/src/register/client/bootstrap.ts +7 -1
- package/src/register/connect-prototypes.ts +12 -0
- package/src/register/hooks.ts +56 -3
- package/src/register/index.ts +92 -20
- package/src/register/mapping.ts +57 -3
- package/src/register/net-guard.ts +149 -39
- package/src/register/package-identity.ts +98 -0
- package/src/register/swap-or-stop.ts +194 -0
- package/src/register/worker-guard.ts +90 -0
- package/src/remote/resolve-host.ts +14 -2
- package/src/serve/ai-proxy.ts +36 -8
- package/src/serve/app-ports.ts +119 -0
- package/src/serve/bridge-mount.ts +36 -8
- package/src/serve/bundler.ts +87 -29
- package/src/serve/discovery.ts +244 -67
- package/src/serve/entries/auth.ts +22 -1
- package/src/serve/entries/firestore-surface.ts +1 -0
- package/src/serve/entries/init-payload.ts +28 -5
- package/src/serve/entries/moved-host.ts +28 -0
- package/src/serve/entries/runtime-origin.ts +6 -0
- package/src/serve/entries/unsupported/firestore.ts +0 -1
- package/src/serve/entries/worker-runtime.ts +19 -6
- package/src/serve/firebase-module-swap.ts +98 -15
- package/src/serve/gemini-proxy.ts +2 -2
- package/src/serve/hosted/runtime.ts +10 -2
- package/src/serve/namespace.ts +41 -29
- package/src/serve/repro/compare.ts +9 -1
- package/src/serve/rules-fixture-check.ts +34 -9
- package/src/serve/rules.ts +104 -20
- package/src/serve/sandbox-session.ts +11 -3
- package/src/serve/server.ts +29 -2
- package/src/serve/vite-ai-config.ts +19 -1
- package/src/serve/vite-functions-development.ts +3 -3
- package/src/serve/vite-generation-bridge.ts +2 -2
- package/src/serve/vite-generation-functions.ts +5 -5
- package/src/serve/vite-generation-middleware.ts +38 -2
- package/src/serve/vite-module-swap.ts +8 -0
- package/src/serve/vite-page-runtime.ts +6 -0
- package/src/serve/vite-rules-source.ts +2 -5
- package/src/serve/vite-sandbox-generation.ts +5 -5
- package/src/serve/worker/__tests__/auth-credential-sign-in.test.ts +343 -0
- package/src/serve/worker/__tests__/auth-m2-parity.test.ts +45 -1
- package/src/serve/worker/__tests__/staged-sign-in-results.test.ts +139 -0
- package/src/serve/worker/client/auth.ts +97 -21
- package/src/serve/worker/client/clock.ts +10 -12
- package/src/serve/worker/client/firestore-reads.ts +70 -21
- package/src/serve/worker/client/firestore-refs.ts +4 -14
- package/src/serve/worker/client/rtdb-connection-lifecycle.ts +5 -0
- package/src/serve/worker/client/rtdb-snapshots.ts +24 -2
- package/src/serve/worker/client/rtdb-value-validation.ts +198 -0
- package/src/serve/worker/client/rtdb-writes.ts +7 -0
- package/src/serve/worker/client/snapshots.ts +15 -6
- package/src/serve/worker/client/websocket-connection.ts +39 -3
- package/src/serve/worker/host/auth-credential-sign-in.ts +97 -0
- package/src/serve/worker/host/auth-reauthentication.ts +3 -3
- package/src/serve/worker/host/rules.ts +53 -34
- package/src/serve/worker/host/staged-sign-in-results.ts +53 -0
- package/src/serve/worker/host/storage.ts +26 -11
- package/src/serve/worker/host/subscriptions.ts +7 -4
- package/src/serve/worker/host-auth.ts +33 -15
- package/src/serve/worker/inbound-validation/operation-arguments.ts +27 -14
- package/src/serve/worker/operation-persistence.ts +1 -0
- package/src/serve/worker/protocol.ts +7 -1
- package/src/serve/worker/rtdb-value-equality.ts +3 -1
- package/src/serve/worker/serve-init.ts +3 -9
- package/src/serve/worker-build-lock.ts +207 -0
- package/src/serve/worker-bundle-cache.ts +90 -0
- package/src/verify/cases.ts +6 -3
- package/src/verify/index.ts +16 -4
- package/dist/functions-rtdb/child.d.ts.map +0 -1
- package/dist/functions-rtdb/child.js +0 -386
- package/dist/functions-rtdb/child.js.map +0 -1
- package/dist/functions-rtdb/delivery.d.ts.map +0 -1
- package/dist/functions-rtdb/delivery.js.map +0 -1
- package/dist/functions-rtdb/development-runtime.d.ts.map +0 -1
- package/dist/functions-rtdb/development-runtime.js.map +0 -1
- package/dist/functions-rtdb/discovery.d.ts.map +0 -1
- package/dist/functions-rtdb/discovery.js.map +0 -1
- package/dist/functions-rtdb/event.d.ts.map +0 -1
- package/dist/functions-rtdb/event.js.map +0 -1
- package/dist/functions-rtdb/events.d.ts.map +0 -1
- package/dist/functions-rtdb/events.js.map +0 -1
- package/dist/functions-rtdb/execution-log.d.ts.map +0 -1
- package/dist/functions-rtdb/execution-log.js.map +0 -1
- package/dist/functions-rtdb/execution.d.ts.map +0 -1
- package/dist/functions-rtdb/execution.js.map +0 -1
- package/dist/functions-rtdb/in-memory-delivery.d.ts.map +0 -1
- package/dist/functions-rtdb/in-memory-delivery.js.map +0 -1
- package/dist/functions-rtdb/module-loader.d.ts.map +0 -1
- package/dist/functions-rtdb/module-loader.js.map +0 -1
- package/dist/functions-rtdb/project.d.ts +0 -7
- package/dist/functions-rtdb/project.d.ts.map +0 -1
- package/dist/functions-rtdb/project.js.map +0 -1
- package/dist/functions-rtdb/projection.d.ts.map +0 -1
- package/dist/functions-rtdb/projection.js.map +0 -1
- package/dist/functions-rtdb/reference-pattern.d.ts.map +0 -1
- package/dist/functions-rtdb/reference-pattern.js.map +0 -1
- package/dist/functions-rtdb/remote-delivery.d.ts.map +0 -1
- package/dist/functions-rtdb/remote-delivery.js.map +0 -1
- package/dist/serve/gemini-error-redaction.d.ts +0 -6
- package/dist/serve/gemini-error-redaction.d.ts.map +0 -1
- package/dist/serve/gemini-error-redaction.js +0 -61
- package/dist/serve/gemini-error-redaction.js.map +0 -1
- package/dist/serve/site-ui/_astro/app.C--T2qDK.js +0 -151
- package/dist/serve/site-ui/_astro/app.DqyhRyrU.js +0 -1
- package/dist/serve/site-ui/_astro/sandbox-controls.Bv6lfpIe.js +0 -1725
- package/dist/serve/site-ui/_astro/writes.Bg0YEEA7.js +0 -1
- package/src/functions-rtdb/child.ts +0 -512
- package/src/serve/gemini-error-redaction.ts +0 -60
- /package/dist/{functions-rtdb → functions}/events.d.ts +0 -0
- /package/dist/{functions-rtdb → functions}/events.js +0 -0
- /package/dist/{functions-rtdb → functions}/execution-log.d.ts +0 -0
- /package/dist/{functions-rtdb → functions}/execution-log.js +0 -0
- /package/dist/{functions-rtdb → functions}/module-loader.d.ts +0 -0
- /package/dist/{functions-rtdb → functions}/module-loader.js +0 -0
- /package/dist/{functions-rtdb → functions/rtdb}/delivery.d.ts +0 -0
- /package/dist/{functions-rtdb → functions/rtdb}/delivery.js +0 -0
- /package/dist/{functions-rtdb → functions/rtdb}/event.d.ts +0 -0
- /package/dist/{functions-rtdb → functions/rtdb}/event.js +0 -0
- /package/dist/{functions-rtdb → functions/rtdb}/execution.d.ts +0 -0
- /package/dist/{functions-rtdb → functions/rtdb}/execution.js +0 -0
- /package/dist/{functions-rtdb → functions/rtdb}/in-memory-delivery.d.ts +0 -0
- /package/dist/{functions-rtdb → functions/rtdb}/in-memory-delivery.js +0 -0
- /package/dist/{functions-rtdb → functions/rtdb}/projection.d.ts +0 -0
- /package/dist/{functions-rtdb → functions/rtdb}/projection.js +0 -0
- /package/dist/{functions-rtdb → functions/rtdb}/reference-pattern.d.ts +0 -0
- /package/dist/{functions-rtdb → functions/rtdb}/reference-pattern.js +0 -0
- /package/dist/{functions-rtdb → functions/rtdb}/remote-delivery.js +0 -0
- /package/src/{functions-rtdb → functions}/events.ts +0 -0
- /package/src/{functions-rtdb → functions}/execution-log.ts +0 -0
- /package/src/{functions-rtdb → functions}/module-loader.ts +0 -0
- /package/src/{functions-rtdb → functions/rtdb}/delivery.ts +0 -0
- /package/src/{functions-rtdb → functions/rtdb}/event.ts +0 -0
- /package/src/{functions-rtdb → functions/rtdb}/execution.ts +0 -0
- /package/src/{functions-rtdb → functions/rtdb}/in-memory-delivery.ts +0 -0
- /package/src/{functions-rtdb → functions/rtdb}/projection.ts +0 -0
- /package/src/{functions-rtdb → functions/rtdb}/reference-pattern.ts +0 -0
|
@@ -11,7 +11,6 @@ html{scrollbar-gutter:stable}
|
|
|
11
11
|
<div class="compat-key">
|
|
12
12
|
<span class="compat-key-item"><span class="compat-dot" data-status="ok"></span><strong>Conforming</strong> — the simulator matches the production verdict, locked by a replayed observation</span>
|
|
13
13
|
<span class="compat-key-item"><span class="compat-dot" data-status="diverged"></span><strong>Diverged (documented)</strong> — a known simulator divergence from production with a written reason</span>
|
|
14
|
-
<span class="compat-key-item"><span class="compat-dot" data-status="unverified"></span><strong>Unverified</strong> — a claim we haven't yet observed against the production engine</span>
|
|
15
14
|
</div>
|
|
16
15
|
<h2 id="firestore-rules-engine--production-simulator-conformance-rules-firestore-corpus">Firestore rules engine — production simulator conformance (rules-firestore corpus)</h2>
|
|
17
16
|
<div class="compat-list">
|
|
@@ -121,15 +120,15 @@ html{scrollbar-gutter:stable}
|
|
|
121
120
|
</details>
|
|
122
121
|
<details class="compat-row" data-status="ok">
|
|
123
122
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Rules simulator</code><span class="compat-sub"><span class="compat-behavior">Function declarations at GLOBAL and SERVICE scope in a Firestore ruleset, with match-scope shadowing, including a flat <code>&&</code> or <code>||</code> chain of up to 98 operands in a function at either scope. The linter and validator check functions at every scope, and the NESTING_DEPTH error fires above 98 operands</span></span></span></summary>
|
|
124
|
-
<div class="compat-evidence"><div class="compat-probe">RECAPTURED, 2026-09-26: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore-rules/rules-firestore-global-and-service-scope-functions.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-firestore-global-and-service-scope-functions</code></a> records 9 production Firestore Rules Test API verdicts: the 5 scope and shadowing cases, plus a 98-operand <code>&&</code> chain declared at global scope and a 98-operand <code>||</code> chain declared at service scope, each compiling and evaluating to ALLOW and DENY. The simulator seeds each match walk with global, then service, then match functions, so an inner declaration shadows an outer one, and <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/oracle-conformance.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>unit:rules/oracle-conformance.test.ts</code></a> replays all 9 verdicts. The same day, the Rules Test API rejected a 99-operand <code>&&</code> or <code>||</code> chain in a function at global, service, or match scope with "Expression is too complex to evaluate safely." and compiled the 98-operand chain at each scope. A rejected ruleset returns no verdicts, so the 99-operand bound is not a replayable case. <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/linter/linter.test.ts#L5" target="_blank" rel="noopener noreferrer"><code>unit:rules/linter/linter.test.ts</code></a> pins the lint side: functions at all three scopes count toward <code>functionCount</code> and <code>maxEstimatedExpressions</code>, NESTING_DEPTH counts each operator on the left-associative spine as one level, 99 operands raise the limit error at every scope, 98 raise none, and a match-scope function shadows a service-scope one. <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/grammar/validator.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>unit:rules/grammar/validator.test.ts</code></a> pins that a call to a global-scope or service-scope function is not reported as undefined and that SEC-3 follows the call into it. Extended 2026-09-27: the Rules Test API compile-limit capture in <code>packages/pyric/test/rules/linter/fixtures/compile-limits/captures.json</code> records the other structural limits for Firestore and Storage rulesets. A call chain of 21 functions compiles and 22 is rejected with "Maximum allowed call depth of 20 is reached", also when no rule calls the chain; 11 <code>let</code> bindings compile and 12 are rejected with "Maximum allowed variable count of 10 for a given function has been reached."; a right-nested <code>&&</code> chain of 49 terms and 97 parentheses around one comparison compile, and 50 terms or 98 parentheses are rejected with "Expression is too complex to evaluate safely.". <code>unit:rules/linter/linter.test.ts</code> reads that fixture and pins the CALL_DEPTH error above 21 functions, for a chain no rule calls as well, and the LET_LIMIT boundary. The runtime limit of 1000 evaluated expressions per request, measured by padding in <code>packages/pyric/test/rules/linter/fixtures/expression-cost/captures.json</code>, is enforced by <code>simulate</code> and by Storage rules evaluation: each request counts its expressions in production's unit across every allow rule and match block it reaches, and a request past the limit is DENY with production's message "Unable to evaluate the expression as the maximum of 1000 expressions to evaluate has been reached.". <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/simulator/expression-budget-fixture.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>unit:rules/simulator/expression-budget-fixture.test.ts</code></a> replays production's padding thresholds through the simulator: the count lies inside production's window on 33 of the 34 captured requests, and the chess queen move production stopped at the limit is denied at the limit. <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/simulator/expression-budget.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>unit:rules/simulator/expression-budget.test.ts</code></a> and <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/sandbox/rules-expression-budget.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>unit:storage/sandbox/rules-expression-budget.test.ts</code></a> pin the unit and the limit boundary in each service. Extended 2026-09-27: the Rules Test API name-resolution capture in <code>packages/pyric/test/rules/grammar/fixtures/name-resolution/captures.json</code> records that production resolves function and variable names by declaration scope. A call to a function declared only in a sibling match, or from a service or global function to a function in an inner scope, compiles with "Invalid function name: s."; an identifier that is no let binding, parameter, enclosing path capture, or global compiles with "Invalid variable name: d."; both deny the request that evaluates them; and a function no resolved call reaches, at global, service, or match scope, draws "Unused function: f.". <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/grammar/validator-name-resolution.test.ts#L5" target="_blank" rel="noopener noreferrer"><code>unit:rules/grammar/validator-name-resolution.test.ts</code></a> replays each captured ruleset through the validator: SEM-4 reports each unresolved call, SEM-5 each unbound variable, and QUA-4 each unused function, in rule conditions and function bodies at every scope. The Firestore simulator and the Storage evaluator enforce all three limits when a ruleset loads (<code>packages/pyric/src/rules/grammar/compile-limits.ts</code>) and refuse a ruleset over one with production's messages: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/grammar/compile-limits.test.ts#L27" target="_blank" rel="noopener noreferrer"><code>unit:rules/grammar/compile-limits.test.ts</code></a> matches the reported messages to every probe in the fixture; <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/simulator/compile-limits.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>unit:rules/simulator/compile-limits.test.ts</code></a> pins that <code>firestoreRules</code>, <code>simulate</code> and the sandbox's deployed rules refuse each rejected Firestore probe and evaluate each compiling one, including the 21-function chain, to production's verdicts; and <code>unit:rules/linter/linter.test.ts</code> pins the NESTING_DEPTH error at the same boundary. Extended 2026-09-28: the compile-limit capture adds two <code>member-chain</code> probes, a member chain <code>request.auth.token.m.m...</code> behind a comparison that grants uid a. A 100-term chain compiles; a ruleset holding a 4,900-term chain drew <code>500 INTERNAL: Internal error encountered.</code> from the Rules Test API on every attempt instead of a compile result, recorded as the probe's <code>apiError</code>; the length between them is not measured. <code>unit:rules/linter/linter.test.ts</code> pins that the linter reports MEMBER_CHAIN_LENGTH as an error for the 4,900-term probe and nothing for the 100-term one, that a chain deeper through the functions it calls than the linter's recursive checks can walk is reported as EXPRESSION_TOO_DEEP rather than thrown, and that the linter reports SLASH_STARTS_PATH with production's message "Missing 'match' keyword before path." for the rejected <code>slash-divisor</code> probe and nothing for the compiling one. Extended 2026-10-07: the compile-limit capture adds the <code>glob-in-path</code> and <code>glob-nested</code> probes. A match declaration path holds at most one recursive wildcard, counted across the block's path and the paths of the blocks it is nested in: <code>match /{g1=<strong>}/p/{g2=</strong>}</code>, and <code>match /{rest=<strong>}</strong></code><strong> nested in <code>match /{g=</code></strong><code>}/p/{gid}</code>, are rejected with "Invalid glob match expression. Only one glob match is permitted in a match declaration path." on the line of the declaration that holds the second one, while <code>match /{g1=<strong>}/p/{d}</strong></code><strong>, and <code>match /{rest=</code></strong><code>}</code> nested in <code>match /p/{gid}</code>, compile. The compile-limit module reports it with that text, so <code>firestoreRules()</code>, <code>simulate</code>, the sandbox's deployed rules and the served rules loader refuse the ruleset; <code>unit:rules/grammar/compile-limits.test.ts</code> pins the message and line, <code>unit:rules/simulator/compile-limits.test.ts</code> the refusal and the compiling probes' verdicts, and <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/serve/rules.test.ts#L1" target="_blank" rel="noopener noreferrer"><code>cli:serve/rules.test.ts</code></a> the served refusal. The compiling <code>glob-nested</code> probe also showed production matching a nested <code>match /{rest=**}</code> with zero segments after a block that consumed the whole path, which the Firestore matcher now does; <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/simulator/match-resolution.test.ts#L6" target="_blank" rel="noopener noreferrer"><code>unit:rules/simulator/match-resolution.test.ts</code></a> pins it.</div></div>
|
|
123
|
+
<div class="compat-evidence"><div class="compat-probe">RECAPTURED, 2026-09-26: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore-rules/rules-firestore-global-and-service-scope-functions.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-firestore-global-and-service-scope-functions</code></a> records 9 production Firestore Rules Test API verdicts: the 5 scope and shadowing cases, plus a 98-operand <code>&&</code> chain declared at global scope and a 98-operand <code>||</code> chain declared at service scope, each compiling and evaluating to ALLOW and DENY. The simulator seeds each match walk with global, then service, then match functions, so an inner declaration shadows an outer one, and <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/oracle-conformance.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>unit:rules/oracle-conformance.test.ts</code></a> replays all 9 verdicts. The same day, the Rules Test API rejected a 99-operand <code>&&</code> or <code>||</code> chain in a function at global, service, or match scope with "Expression is too complex to evaluate safely." and compiled the 98-operand chain at each scope. A rejected ruleset returns no verdicts, so the 99-operand bound is not a replayable case. <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/linter/linter.test.ts#L5" target="_blank" rel="noopener noreferrer"><code>unit:rules/linter/linter.test.ts</code></a> pins the lint side: functions at all three scopes count toward <code>functionCount</code> and <code>maxEstimatedExpressions</code>, NESTING_DEPTH counts each operator on the left-associative spine as one level, 99 operands raise the limit error at every scope, 98 raise none, and a match-scope function shadows a service-scope one. <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/grammar/validator.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>unit:rules/grammar/validator.test.ts</code></a> pins that a call to a global-scope or service-scope function is not reported as undefined and that SEC-3 follows the call into it. Extended 2026-09-27: the Rules Test API compile-limit capture in <code>packages/pyric/test/rules/linter/fixtures/compile-limits/captures.json</code> records the other structural limits for Firestore and Storage rulesets. A call chain of 21 functions compiles and 22 is rejected with "Maximum allowed call depth of 20 is reached", also when no rule calls the chain; 11 <code>let</code> bindings compile and 12 are rejected with "Maximum allowed variable count of 10 for a given function has been reached."; a right-nested <code>&&</code> chain of 49 terms and 97 parentheses around one comparison compile, and 50 terms or 98 parentheses are rejected with "Expression is too complex to evaluate safely.". <code>unit:rules/linter/linter.test.ts</code> reads that fixture and pins the CALL_DEPTH error above 21 functions, for a chain no rule calls as well, and the LET_LIMIT boundary. The runtime limit of 1000 evaluated expressions per request, measured by padding in <code>packages/pyric/test/rules/linter/fixtures/expression-cost/captures.json</code>, is enforced by <code>simulate</code> and by Storage rules evaluation: each request counts its expressions in production's unit across every allow rule and match block it reaches, and a request past the limit is DENY with production's message "Unable to evaluate the expression as the maximum of 1000 expressions to evaluate has been reached.". <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/simulator/expression-budget-fixture.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>unit:rules/simulator/expression-budget-fixture.test.ts</code></a> replays production's padding thresholds through the simulator: the count lies inside production's window on 33 of the 34 captured requests, and the chess queen move production stopped at the limit is denied at the limit. <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/simulator/expression-budget.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>unit:rules/simulator/expression-budget.test.ts</code></a> and <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/sandbox/rules-expression-budget.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>unit:storage/sandbox/rules-expression-budget.test.ts</code></a> pin the unit and the limit boundary in each service. Extended 2026-09-27: the Rules Test API name-resolution capture in <code>packages/pyric/test/rules/grammar/fixtures/name-resolution/captures.json</code> records that production resolves function and variable names by declaration scope. A call to a function declared only in a sibling match, or from a service or global function to a function in an inner scope, compiles with "Invalid function name: s."; an identifier that is no let binding, parameter, enclosing path capture, or global compiles with "Invalid variable name: d."; both deny the request that evaluates them; and a function no resolved call reaches, at global, service, or match scope, draws "Unused function: f.". <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/grammar/validator-name-resolution.test.ts#L5" target="_blank" rel="noopener noreferrer"><code>unit:rules/grammar/validator-name-resolution.test.ts</code></a> replays each captured ruleset through the validator: SEM-4 reports each unresolved call, SEM-5 each unbound variable, and QUA-4 each unused function, in rule conditions and function bodies at every scope. The Firestore simulator and the Storage evaluator enforce all three limits when a ruleset loads (<code>packages/pyric/src/rules/grammar/compile-limits.ts</code>) and refuse a ruleset over one with production's messages: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/grammar/compile-limits.test.ts#L27" target="_blank" rel="noopener noreferrer"><code>unit:rules/grammar/compile-limits.test.ts</code></a> matches the reported messages to every probe in the fixture; <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/simulator/compile-limits.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>unit:rules/simulator/compile-limits.test.ts</code></a> pins that <code>firestoreRules</code>, <code>simulate</code> and the sandbox's deployed rules refuse each rejected Firestore probe and evaluate each compiling one, including the 21-function chain, to production's verdicts; and <code>unit:rules/linter/linter.test.ts</code> pins the NESTING_DEPTH error at the same boundary. Extended 2026-09-28: the compile-limit capture adds two <code>member-chain</code> probes, a member chain <code>request.auth.token.m.m...</code> behind a comparison that grants uid a. A 100-term chain compiles; a ruleset holding a 4,900-term chain drew <code>500 INTERNAL: Internal error encountered.</code> from the Rules Test API on every attempt instead of a compile result, recorded as the probe's <code>apiError</code>; the length between them is not measured. <code>unit:rules/linter/linter.test.ts</code> pins that the linter reports MEMBER_CHAIN_LENGTH as an error for the 4,900-term probe and nothing for the 100-term one, that a chain deeper through the functions it calls than the linter's recursive checks can walk is reported as EXPRESSION_TOO_DEEP rather than thrown, and that the linter reports SLASH_STARTS_PATH with production's message "Missing 'match' keyword before path." for the rejected <code>slash-divisor</code> probe and nothing for the compiling one. Extended 2026-10-07: the compile-limit capture adds the <code>glob-in-path</code> and <code>glob-nested</code> probes. A match declaration path holds at most one recursive wildcard, counted across the block's path and the paths of the blocks it is nested in: <code>match /{g1=<strong>}/p/{g2=</strong>}</code>, and <code>match /{rest=<strong>}</strong></code><strong> nested in <code>match /{g=</code></strong><code>}/p/{gid}</code>, are rejected with "Invalid glob match expression. Only one glob match is permitted in a match declaration path." on the line of the declaration that holds the second one, while <code>match /{g1=<strong>}/p/{d}</strong></code><strong>, and <code>match /{rest=</code></strong><code>}</code> nested in <code>match /p/{gid}</code>, compile. The compile-limit module reports it with that text, so <code>firestoreRules()</code>, <code>simulate</code>, the sandbox's deployed rules and the served rules loader refuse the ruleset; <code>unit:rules/grammar/compile-limits.test.ts</code> pins the message and line, <code>unit:rules/simulator/compile-limits.test.ts</code> the refusal and the compiling probes' verdicts, and <a href="https://github.com/davideast/pyric/blob/main/packages/cli/test/serve/rules.test.ts#L1" target="_blank" rel="noopener noreferrer"><code>cli:serve/rules.test.ts</code></a> the served refusal. The compiling <code>glob-nested</code> probe also showed production matching a nested <code>match /{rest=**}</code> with zero segments after a block that consumed the whole path, which the Firestore matcher now does; <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/simulator/match-resolution.test.ts#L6" target="_blank" rel="noopener noreferrer"><code>unit:rules/simulator/match-resolution.test.ts</code></a> pins it. Extended 2026-10-27: the compile-limit capture adds the <code>and-chain</code> and <code>and-chain-calls</code> probes, flat chains with no parentheses. 98 comparisons compile and 99 are rejected, while 99 zero-argument function calls compile and 100 are rejected, each with "Expression is too complex to evaluate safely."; <code>unit:rules/linter/linter.test.ts</code> and <code>unit:rules/simulator/compile-limits.test.ts</code> replay all six probes.</div></div>
|
|
125
124
|
</details>
|
|
126
125
|
<details class="compat-row" data-status="ok">
|
|
127
126
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Rules simulator</code><span class="compat-sub"><span class="compat-behavior">Hierarchical nested <code>match</code> composition. A child match resolves relative to its parent and grants only the exact nested document path. A recursive wildcard matches zero or more segments: followed by further segments, as in the collection-group shape <code>match /{path=<strong>}/items/{id}</strong></code><strong>, it governs every document in a collection with that id at any depth and nothing else, and a match nested in that block matches no request; in the last position, as in <code>match /{document=</code></strong><code>}</code>, a nested match resolves at any depth below it with the recursive wildcard bound to the longest prefix that lets it match</span></span></span></summary>
|
|
128
127
|
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore-rules/rules-firestore-hierarchical-match-cascade.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-firestore-hierarchical-match-cascade</code></a> captures the exact nested child ALLOW plus parent, sibling, and over-deep DENY controls from the production Firestore Rules Test API, replayed verdict-for-verdict by <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/oracle-conformance.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>packages/pyric/test/rules/oracle-conformance.test.ts</code></a>. RECAPTURED, 2026-10-07: the scenario adds 21 production verdicts (11 ALLOW, 10 DENY; 25 in all) for recursive wildcard placement. Under <code>match /{path=<strong>}/items/{id}</strong></code><strong>, get and list allow in <code>items/i1</code>, <code>users/u1/items/i1</code>, <code>a/b/c/d/items/i1</code>, <code>items/i1/items/i2</code>, and <code>items/items/items/i3</code>, and deny in a sibling collection, under an <code>items</code> document, and on the parent document. The trailing document wildcard binds the last segment. A match nested in <code>match /{group=</code></strong><code>}/groups/{groupId}</code> denies at the root and at depth. A match nested in <code>match /{document=**}</code> allows at the root and at depth, and in <code>notes/n1/notes/n2</code> its document wildcard binds <code>n2</code>. <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/simulator/match-resolution.test.ts#L6" target="_blank" rel="noopener noreferrer"><code>unit:rules/simulator/match-resolution.test.ts</code></a> pins the bindings for each placement, and <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/firestore/collection-group-rules.test.ts#L13" target="_blank" rel="noopener noreferrer"><code>unit:firestore/collection-group-rules.test.ts</code></a> pins direct reads, collection-group queries, and query proof under the collection-group shape through the public SDK.</div></div>
|
|
129
128
|
</details>
|
|
130
129
|
<details class="compat-row" data-status="ok">
|
|
131
|
-
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Rules simulator</code><span class="compat-sub"><span class="compat-behavior">Strict boolean operands for <code>&&</code>, <code>||</code>, and ternary control flow, plus create-time <code>resource</code> / <code>request.resource</code> semantics</span></span></span></summary>
|
|
132
|
-
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore-rules/rules-firestore-strict-boolean-control-flow.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-firestore-strict-boolean-control-flow</code></a> captures three direct non-boolean DENYs, three tautological error discriminators that still DENY, a boolean ALLOW control, create-time <code>resource == null</code> DENY, and incoming <code>request.resource.data</code> ALLOW; replayed verdict-for-verdict by <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/oracle-conformance.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>packages/pyric/test/rules/oracle-conformance.test.ts</code></a>. RECAPTURED, 2026-09-27: 9 allow-condition cases (18 in all) show the allow condition itself must be a bool. Production compiles <code>allow create: if 'ab';</code> and the conditions <code>1</code>, <code>0</code>, <code>1.0</code>, <code>null</code>, <code>[]</code>, <code>request.resource.data</code> (a map), and a string field, then denies each with <code>Type error. Received: [<type>] Expected: [bool].</code>; <code>if true</code> allows. The simulator decided an allow by JavaScript truthiness, so a non-empty string, a nonzero number, a list, or a map allowed; it now raises the same type error as an ERROR verdict for that rule and matches all 18 verdicts.</div></div>
|
|
130
|
+
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Rules simulator</code><span class="compat-sub"><span class="compat-behavior">Strict boolean operands for <code>&&</code>, <code>||</code>, and ternary control flow, plus create-time <code>resource</code> / <code>request.resource</code> semantics. A rules case is one request in <code>simulate()</code>, the MCP <code>firestore_simulate_rules</code> tool and the Rules Test API wire: a <code>create</code> case that names an existing <code>resource</code>, a case with an unknown method or field or no path, and a <code>requestTime</code> that is not ISO-8601 with an offset are refused with the reason; value helpers reach the wire in the form production reads as their type, and a plain map with a <code>__type</code> key is a map in both engines</span></span></span></summary>
|
|
131
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore-rules/rules-firestore-strict-boolean-control-flow.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-firestore-strict-boolean-control-flow</code></a> captures three direct non-boolean DENYs, three tautological error discriminators that still DENY, a boolean ALLOW control, create-time <code>resource == null</code> DENY, and incoming <code>request.resource.data</code> ALLOW; replayed verdict-for-verdict by <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/oracle-conformance.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>packages/pyric/test/rules/oracle-conformance.test.ts</code></a>. RECAPTURED, 2026-09-27: 9 allow-condition cases (18 in all) show the allow condition itself must be a bool. Production compiles <code>allow create: if 'ab';</code> and the conditions <code>1</code>, <code>0</code>, <code>1.0</code>, <code>null</code>, <code>[]</code>, <code>request.resource.data</code> (a map), and a string field, then denies each with <code>Type error. Received: [<type>] Expected: [bool].</code>; <code>if true</code> allows. The simulator decided an allow by JavaScript truthiness, so a non-empty string, a nonzero number, a list, or a map allowed; it now raises the same type error as an ERROR verdict for that rule and matches all 18 verdicts. Extended 2026-10-28 (#1338): the Rules Test API capture in <code>packages/pyric/test/rules/test/fixtures/case-wire/captures.json</code> (<code>packages/conformance/src/capture-rules-case-wire.ts</code>, 4 calls, no deploy) records production binding a <code>create</code> case's <code>resource</code>, which no real create has (<code>resource.data.x == 1</code> and <code>resource != null</code> ALLOW, FS2-CON-12), and reading <code>{__type: 'timestamp', seconds, nanos}</code> as a map (FS2-CON-5). It also records the JSON forms production reads as typed values, each keeping its value: an ISO-8601 string with an offset as a timestamp, <code>{latitude, longitude}</code> as a latlng, <code>-1.500000000s</code> as a duration, and a <code>/databases/(default)/documents/...</code> string as a path, while a base64 string stays a string. <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/test/case-wire-capture.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>unit:rules/test/case-wire-capture.test.ts</code></a> replays it: <code>simulate()</code> and <code>buildApiTestCase</code> refuse each create case with a resource, a create without one gets production's verdicts, <code>simulate()</code> reads the <code>__type</code> map as a map, and <code>buildApiTestCase</code> sends each value helper in the captured form, which <code>simulate()</code> reads as production does for timestamp, latlng and duration. <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/api/case-shape.test.ts#L3" target="_blank" rel="noopener noreferrer"><code>unit:rules/api/case-shape.test.ts</code></a> pins the refusals through <code>simulate()</code> and the MCP tool and the value helpers' range checks, and <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/test/api-wire.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>unit:rules/test/api-wire.test.ts</code></a> pins the built request for a merge set, a replacing set, a dotted update, a full-form path and each value helper, and that <code>TestFirestoreRulesHandler</code> refuses a malformed case before calling the API.</div></div>
|
|
133
132
|
</details>
|
|
134
133
|
<details class="compat-row" data-status="ok">
|
|
135
134
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Rules simulator</code><span class="compat-sub"><span class="compat-behavior">The exact six shared <code>auth</code> and <code>membership</code> function bodies preserve signed-in, owner, custom-claim, explicit membership-map, and role semantics under Firestore Rules</span></span></span></summary>
|
|
@@ -155,6 +154,30 @@ html{scrollbar-gutter:stable}
|
|
|
155
154
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Rules simulator</code><span class="compat-sub"><span class="compat-behavior">Rules Test API <code>functionMocks</code>: <code>get</code> and <code>exists</code> are separate service functions, so an <code>exists</code> mock never supplies a <code>get()</code> document, mock order does not matter, and a mock path is normalized like a lookup path (leading slash). Also, a sibling <code>{document=**}</code> read deny does not revoke a <code>match /menuItems/{id}</code> <code>list</code> grant (OR across matching blocks)</span></span></span></summary>
|
|
156
155
|
<div class="compat-evidence"><div class="compat-probe">NEW ROW, 2026-10-17: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore-rules/rules-firestore-simcase-mock-and-path-fidelity.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-firestore-simcase-mock-and-path-fidelity</code></a> captures 6 production verdicts (5 ALLOW, 1 DENY) for FS-CON-6 / FS-COV-6a / FS-MET-6b / FS-COV-6b. An exists-only mock plus <code>get()</code> DENIES ("Function not found error: Name: [get]."), <code>exists: false</code> next to a <code>get</code> mock makes <code>!exists()</code> ALLOW, <code>[exists, get]</code> and <code>[get, exists]</code> both ALLOW, a <code>'/other/x'</code> mock path ALLOWS, and a doc-path <code>list</code> beside a <code>{document=**}</code> read deny ALLOWS. <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/oracle-conformance.test.ts#L28" target="_blank" rel="noopener noreferrer"><code>unit:rules/oracle-conformance.test.ts</code></a> replays all 6, and <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/simulator/simcase-fidelity.test.ts#L147" target="_blank" rel="noopener noreferrer"><code>unit:rules/simulator/simcase-fidelity.test.ts</code></a> pins the simulator's <code>functionMocks</code> handling and the collection-path <code>list</code> form.</div></div>
|
|
157
156
|
</details>
|
|
157
|
+
<details class="compat-row" data-status="diverged">
|
|
158
|
+
<summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">Rules simulator</code><span class="compat-sub"><span class="compat-behavior">Integers are 64-bit: values above 2^53 keep every bit, overflow is an evaluation error except <code>MIN / -1</code>, which is <code>MIN</code>, unary minus negates a duration, and a timestamp or duration result past its range is an error (FS2-SIM-1, FS2-SIM-2, FS2-SIM-3, FS2-SIM-9)</span></span></span></summary>
|
|
159
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore-rules/rules-firestore-int64-and-duration-range.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-firestore-int64-and-duration-range</code></a> (captured 2026-10-27, 22 cases) records production ALLOWING <code>neg duration equals duration.value(-1, 's')</code>, <code>int64 max is greater than max minus one</code>, <code>2^53 plus one differs from 2^53</code>, <code>string() of 2^53 plus one</code> and <code>min divided by minus one is min</code>, and DENYING <code>max plus one is positive</code>, <code>max timestamp plus 1 ms is later</code> and <code>duration past the protobuf bound is positive</code>. The simulator holds ints as JavaScript doubles and errors on a negated duration, so it disagrees on 12 cases: 3 under FS2-SIM-1, 7 under FS2-SIM-2 and 2 under FS2-SIM-3. <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/oracle-conformance.test.ts#L24" target="_blank" rel="noopener noreferrer"><code>unit:rules/oracle-conformance.test.ts</code></a> pins both sides of each in <code>packages/conformance/firestore-rules-exceptions/</code>. The other 10 cases agree, including the 3 under FS2-SIM-9: a timestamp or duration past its range is production's <code>Timestamp is not valid</code> or <code>Duration is not valid</code> error (#1336), replayed by <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/simulator/time-range.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>unit:rules/simulator/time-range.test.ts</code></a>. Timestamp and duration range is no longer in this row's diverged scope. Comparison operators are not in scope: they compare correctly, and the wrong answers come from the integer values they are handed. Pending fix in the 2026-10-27 Firestore hunt's stream S4.</div></div>
|
|
160
|
+
</details>
|
|
161
|
+
<details class="compat-row" data-status="diverged">
|
|
162
|
+
<summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">Rules simulator</code><span class="compat-sub"><span class="compat-behavior"><code>matches()</code>, <code>split()</code> and <code>replace()</code> use RE2: <code>\s</code> is <code>[\t\n\f\r ]</code>, <code>.</code> matches <code>\r</code>, <code>\pL</code>, <code>[[:alpha:]]</code> and <code>\Q..\E</code> are a class, a POSIX class and literal quoting, <code>split()</code> does not return capture-group text, and <code>(?i)</code> applies in <code>split()</code> and <code>replace()</code> (FS2-SIM-4, FS2-SIM-5a, FS2-SIM-5a2, FS2-SIM-5b, FS2-SIM-5c, FS2-SIM-5d, FS2-SIM-6)</span></span></span></summary>
|
|
163
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore-rules/rules-firestore-re2-pattern-semantics.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-firestore-re2-pattern-semantics</code></a> (captured 2026-10-27, recaptured 2026-10-28 with 6 more cases, 34 in all, every earlier verdict unchanged) records production ALLOWING <code>NBSP is not \s</code>, <code>em space is not \s</code>, <code>vertical tab is not \s</code>, <code>real nbsp from data is not \s</code>, <code>dot matches carriage return</code>, <code>a is \pL</code>, <code>a is [[:alpha:]]</code>, <code>quoted a.b matches a.b</code>, <code>split on a capturing group returns two pieces</code>, <code>split with (?i) splits both cases</code>, <code>replace \s leaves NBSP</code>, <code>split dot matches carriage return</code>, <code>replace dot matches carriage return</code>, <code>replace (?s) dot matches newline</code>, <code>split (?i)k splits on the Kelvin sign</code> and <code>replace (?i)k replaces the Kelvin sign</code>. The simulator disagrees on 5 cases: 2 under FS2-SIM-5c, 1 under FS2-SIM-5d and 2 under ST-H1-3 (<code>split()</code> and <code>replace()</code> compile without JavaScript's <code>u</code> flag, so <code>(?i)</code> does not fold U+212A; the <code>matches()</code> half is #1332). <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/oracle-conformance.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>unit:rules/oracle-conformance.test.ts</code></a> pins both sides of each in <code>packages/conformance/firestore-rules-exceptions/</code>. The other 29 cases agree. Fixed in #1295: the 5 FS2-SIM-5a cases (the matcher spells <code>\s</code> and <code>\S</code> out as <code>[\t\n\f\r ]</code> and its complement), FS2-SIM-5a2 in <code>matches()</code> (<code>.</code> matches every character but <code>\n</code>), the 2 FS2-SIM-5b cases (<code>\pL</code> reads as <code>\p{L}</code>), and the 3 FS2-SIM-6 cases (<code>split()</code> and <code>replace()</code> take the pattern's inline flags and RE2's <code>\s</code>, through the <code>scanRe2Pattern</code> Storage already used). <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/simulator/re2-space-and-trim.test.ts#L9" target="_blank" rel="noopener noreferrer"><code>unit:rules/simulator/re2-space-and-trim.test.ts</code></a> and <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/simulator/re2-dot-property-and-flags.test.ts#L9" target="_blank" rel="noopener noreferrer"><code>unit:rules/simulator/re2-dot-property-and-flags.test.ts</code></a> replay those cases from this observation. Fixed in #1333: the 3 FS2-SIM-4 cases (the shared <code>splitRe2</code> never returns capture-group text) and FS2-SIM-5a2 in <code>split()</code> and <code>replace()</code> (<code>compileRe2Regex</code> spells <code>.</code> as <code>[^\n]</code> unless <code>(?s)</code> is on); <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/simulator/split-and-replace.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>unit:rules/simulator/split-and-replace.test.ts</code></a> replays them from this observation on Firestore and Storage. <code>split()</code> and <code>replace()</code> are in this row's diverged scope only for the Kelvin sign. Pending fix in the 2026-10-27 Firestore hunt's stream S3.</div></div>
|
|
164
|
+
</details>
|
|
165
|
+
<details class="compat-row" data-status="ok">
|
|
166
|
+
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Rules simulator</code><span class="compat-sub"><span class="compat-behavior">Without <code>rules_version = '2'</code>, a recursive wildcard does not match an empty path: <code>/cities/{city}/{document=**}</code> does not match <code>cities/SF</code> (FS2-SIM-12)</span></span></span></summary>
|
|
167
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore-rules/rules-firestore-rules-version-1-recursive-glob.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-firestore-rules-version-1-recursive-glob</code></a> (captured 2026-10-27, 3 cases) records production DENYING <code>get</code> on <code>cities/SF</code> under a version-1 <code>match /cities/{city}/{document=<strong>}</strong></code><strong> and ALLOWING <code>cities/SF/landmarks/x</code> and <code>top/a</code> under <code>/top/{document=</code></strong><code>}</code>. <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/oracle-conformance.test.ts#L29" target="_blank" rel="noopener noreferrer"><code>unit:rules/oracle-conformance.test.ts</code></a> replays all 3 cases against the local simulator, whose match resolver reads the ruleset's version and needs one or more segments for a recursive wildcard under version 1 (#1337). <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/simulator/rules-version-1-glob.test.ts#L5" target="_blank" rel="noopener noreferrer"><code>unit:rules/simulator/rules-version-1-glob.test.ts</code></a> denies <code>get</code> and <code>list</code> of the city document with no <code>rules_version</code> and with <code>rules_version = '1'</code>, through <code>simulate()</code> and the in-page sandbox's <code>getDoc</code>, covers a nested block under a final glob and a root ending in a glob, and keeps version 2 and <code>2+modules</code> matching zero segments. The in-page sandbox's <code>getDocs</code> of the bare collection still lists through a widened document path (#1428).</div></div>
|
|
168
|
+
</details>
|
|
169
|
+
<details class="compat-row" data-status="ok">
|
|
170
|
+
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Rules simulator</code><span class="compat-sub"><span class="compat-behavior">The literal segments of the service's root <code>match</code> path are compared with the request path: a ruleset rooted at another collection grants nothing outside it (FS2-ENF-1)</span></span></span></summary>
|
|
171
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore-rules/rules-firestore-service-root-match-path.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-firestore-service-root-match-path</code></a> (captured 2026-10-27, 3 cases) records production DENYING <code>get</code> and <code>create</code> outside the root collection and ALLOWING the control inside it, under the root <code>/databases/{database}/documents/z</code>. <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/oracle-conformance.test.ts#L31" target="_blank" rel="noopener noreferrer"><code>unit:rules/oracle-conformance.test.ts</code></a> replays all 3 cases against the local simulator, which matches the root against <code>/databases/(default)/documents/<path></code> before resolving its children (#1327). <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/simulator/root-match-path.test.ts#L8" target="_blank" rel="noopener noreferrer"><code>unit:rules/simulator/root-match-path.test.ts</code></a> denies <code>get</code>, <code>create</code>, <code>list</code> and a collection-group query of <code>c/1</code> under the roots <code>/databases/other/documents</code>, <code>/databases/{db}/documents/z</code>, <code>/databases/{db}/documentz</code> and <code>/b/{bucket}/o</code>, through <code>simulate()</code> and the in-page sandbox, and keeps the standard root, a root ending in a collection and a root ending in a recursive wildcard granting.</div></div>
|
|
172
|
+
</details>
|
|
173
|
+
<details class="compat-row" data-status="ok">
|
|
174
|
+
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Rules simulator</code><span class="compat-sub"><span class="compat-behavior"><code>split()</code> drops trailing empty pieces, and <code>trim()</code> strips only code units at or below U+0020, keeping NBSP, U+3000 and the byte-order mark (FS2-SIM-7, FS2-SIM-8)</span></span></span></summary>
|
|
175
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore-rules/rules-firestore-split-and-trim-edges.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-firestore-split-and-trim-edges</code></a> (captured 2026-10-27, recaptured 2026-10-28 with 7 more cases, 18 in all, every earlier verdict unchanged) records production ALLOWING <code>'a,b,'.split(',') == ['a', 'b']</code>, <code>'a,b,,'.split(',').size() == 2</code>, <code>',a,'.split(',') == ['', 'a']</code> and <code>'a1b2'.split('([0-9])') == ['a', 'b']</code>, ALLOWING <code>'a.b.c'.split('.') == ['']</code>, <code>','.split(',') == ['']</code> and <code>',,'.split(',') == ['']</code> while DENYING <code>','.split(',').size() == 0</code> and <code>'a.b.c'.split('.').size() == 0</code> (trailing empty pieces are dropped, but one piece always remains), DENYING <code>'a,b,'.split(',') == ['a', 'b', '']</code>, and ALLOWING <code>trim keeps NBSP</code>, <code>trim keeps ideographic space</code>, <code>trim keeps BOM</code> and <code>trim strips C0 controls</code>. <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/oracle-conformance.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>unit:rules/oracle-conformance.test.ts</code></a> replays all 18 cases against the local simulator. <code>split()</code> goes through the shared <code>splitRe2</code> (#1333), which Storage also calls, and <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/simulator/split-and-replace.test.ts#L8" target="_blank" rel="noopener noreferrer"><code>unit:rules/simulator/split-and-replace.test.ts</code></a> replays the split cases from this observation on Firestore and Storage. <code>trim()</code> strips code units at or below U+0020 through the shared <code>trimRulesString</code> (#1295), and <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/simulator/re2-space-and-trim.test.ts#L10" target="_blank" rel="noopener noreferrer"><code>unit:rules/simulator/re2-space-and-trim.test.ts</code></a> replays the 4 FS2-SIM-8 cases.</div></div>
|
|
176
|
+
</details>
|
|
177
|
+
<details class="compat-row" data-status="ok">
|
|
178
|
+
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Rules simulator</code><span class="compat-sub"><span class="compat-behavior"><code>debug()</code>, <code>map.hasAny()</code>, <code>map.hasOnly()</code>, an unknown method and an unknown global compile, and calling one is a "Function not found" runtime error that <code>|| true</code> absorbs</span></span></span></summary>
|
|
179
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore-rules/rules-firestore-unknown-function-names-runtime-error.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-firestore-unknown-function-names-runtime-error</code></a> (captured 2026-10-27, 9 cases) records production compiling each name, DENYING the bare <code>debug()</code>, <code>map.hasAny</code> and <code>map.hasOnly</code> calls, and ALLOWING each under <code>|| true</code> and <code>!(map.hasOnly && false)</code>, so production does not reject them at compile. <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/oracle-conformance.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>unit:rules/oracle-conformance.test.ts</code></a> replays all 9 verdicts with no pin.</div></div>
|
|
180
|
+
</details>
|
|
158
181
|
</div>
|
|
159
182
|
<h2 id="storage-rules-engine--parsestoragerules--evaluatestoragerules-rules-storage-corpus">Storage rules engine — <code>parseStorageRules</code> / <code>evaluateStorageRules</code> (rules-storage corpus)</h2>
|
|
160
183
|
<div class="compat-list">
|
|
@@ -168,7 +191,7 @@ html{scrollbar-gutter:stable}
|
|
|
168
191
|
</details>
|
|
169
192
|
<details class="compat-row" data-status="ok">
|
|
170
193
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Rules</code><span class="compat-sub"><span class="compat-behavior"><code>evaluateStorageRules</code> supports granular verbs (<code>get</code>/<code>list</code>/<code>create</code>/<code>update</code>/<code>delete</code>) alongside <code>read</code>/<code>write</code> umbrella expansion, comma-separated verb lists, and per-verb default-deny</span></span></span></summary>
|
|
171
|
-
<div class="compat-evidence"><div class="compat-probe">STALE ROW, corrected 2026-07-10: production capture proves the evaluator already supports the full six-verb grant surface (umbrella read→{get,list}, write→{create,update,delete}, single granular grants, comma-separated grants, per-verb deny-by-default), matching production verdict-for-verdict on 12 of the scenario's 13 non-existence cases. <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-verbs-umbrella-granular.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-verbs-umbrella-granular</code></a> (all <code>read</code>/<code>write</code>/<code>get</code>/comma-verb cases). One related existence-semantics case in the same scenario diverges over-permissively and is carried by <code>#118 Rules</code> (the <code>resource == null</code> create-if-absent guard), not by this granular-verb row. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#
|
|
194
|
+
<div class="compat-evidence"><div class="compat-probe">STALE ROW, corrected 2026-07-10: production capture proves the evaluator already supports the full six-verb grant surface (umbrella read→{get,list}, write→{create,update,delete}, single granular grants, comma-separated grants, per-verb deny-by-default), matching production verdict-for-verdict on 12 of the scenario's 13 non-existence cases. <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-verbs-umbrella-granular.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-verbs-umbrella-granular</code></a> (all <code>read</code>/<code>write</code>/<code>get</code>/comma-verb cases). One related existence-semantics case in the same scenario diverges over-permissively and is carried by <code>#118 Rules</code> (the <code>resource == null</code> create-if-absent guard), not by this granular-verb row. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>rules-oracle-conformance.test.ts</code></a>)</div></div>
|
|
172
195
|
</details>
|
|
173
196
|
<details class="compat-row" data-status="ok">
|
|
174
197
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Rules</code><span class="compat-sub"><span class="compat-behavior"><code>parseStorageRules</code> rejects unterminated string literals with <code>SyntaxError</code></span></span></span></summary>
|
|
@@ -200,63 +223,63 @@ html{scrollbar-gutter:stable}
|
|
|
200
223
|
</details>
|
|
201
224
|
<details class="compat-row" data-status="ok">
|
|
202
225
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Rules</code><span class="compat-sub"><span class="compat-behavior">User-defined <code>function</code> definitions — <code>let</code> bindings, functions calling functions, and match-block-scoped helper functions (lexical scoping)</span></span></span></summary>
|
|
203
|
-
<div class="compat-evidence"><div class="compat-probe">STALE ROW, corrected 2026-07-10: production capture proves the evaluator supports user-defined functions with <code>let</code> bindings, nested function calls, and block-scoped helpers. <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-functions-let-scope.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-functions-let-scope</code></a> matches production verdict-for-verdict on all 5 cases. CORRECTED, 2026-09-27: a call to an undefined function is not a compile-time rejection. Production compiles the ruleset with the warning <code>Invalid function name: notDeclared.</code> and reports <code>Function not found error: Name: [notDeclared].</code> at evaluation; the recapture adds 2 cases (7 in all) in which <code>request.auth.uid == 'a' || notDeclared()</code> inside a function allows uid a and denies uid b, and the evaluator matches both. A match-block function with the same name as a service-level function compiles with the warning <code>Unused function: f.</code> for the outer one and shadows it (<code>oracle:rules-storage-function-scopes-and-shadowing</code> pins the same resolution for a global function). Only a second definition of one name in the same scope is a compile-time rejection: <code>Function f is already defined.</code> Extended 2026-09-27: the Rules Test API compile-limit capture in <code>packages/pyric/test/rules/linter/fixtures/compile-limits/captures.json</code> records production's structural limits for Storage rulesets: 21 functions on one call stack compile and 22 are rejected, also when no rule calls the chain; 11 <code>let</code> bindings compile and 12 are rejected; and nesting past 99 levels is rejected with "Expression is too complex to evaluate safely.". The Storage evaluator enforces them when <code>parseStorageRules</code> loads a ruleset and has no runtime call depth cap, so a 21-function chain evaluates as production does. <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/sandbox/compile-limits.test.ts#L14" target="_blank" rel="noopener noreferrer"><code>unit:storage/sandbox/compile-limits.test.ts</code></a> pins that each rejected Storage probe is refused with production's messages and each compiling one, including the 21-function chain, evaluates to production's verdicts. <code>parseStorageRules</code> rejects a second definition of one function name in one scope through the same module with <code>Function f is already defined.</code>, pinned by <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/sandbox/rules.test.ts#L9" target="_blank" rel="noopener noreferrer"><code>unit:storage/sandbox/rules.test.ts</code></a>. A second 2026-09-27 recapture adds 7 cases (14 in all): an argument or <code>let</code> whose value errors (<code>1 / request.resource.size</code> of 0, or a method an int does not define) is bound and the function body runs, so a function that never reads it allows, also through two calls, and one that reads it denies; <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/sandbox/rules-error-values.test.ts#L3" target="_blank" rel="noopener noreferrer"><code>unit:storage/sandbox/rules-error-values.test.ts</code></a> pins each verdict. The compile-limit capture adds the <code>slash-divisor</code> probe: <code>(4/2)</code> is rejected with "Missing 'match' keyword before path.", production reading a slash directly followed by a path segment as the start of a path, and <code>(4/ 2)</code> compiles. Extended 2026-10-07: the <code>glob-in-path</code> and <code>glob-nested</code> probes show Storage rejecting a second recursive wildcard in one match declaration path, counted across nested blocks, with "Invalid glob match expression. Only one glob match is permitted in a match declaration path.", as Firestore does; <code>parseStorageRules</code> refuses both rejected probes through the same module and evaluates both compiling ones to production's verdicts, pinned by <code>unit:storage/sandbox/compile-limits.test.ts</code>. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#
|
|
226
|
+
<div class="compat-evidence"><div class="compat-probe">STALE ROW, corrected 2026-07-10: production capture proves the evaluator supports user-defined functions with <code>let</code> bindings, nested function calls, and block-scoped helpers. <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-functions-let-scope.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-functions-let-scope</code></a> matches production verdict-for-verdict on all 5 cases. CORRECTED, 2026-09-27: a call to an undefined function is not a compile-time rejection. Production compiles the ruleset with the warning <code>Invalid function name: notDeclared.</code> and reports <code>Function not found error: Name: [notDeclared].</code> at evaluation; the recapture adds 2 cases (7 in all) in which <code>request.auth.uid == 'a' || notDeclared()</code> inside a function allows uid a and denies uid b, and the evaluator matches both. A match-block function with the same name as a service-level function compiles with the warning <code>Unused function: f.</code> for the outer one and shadows it (<code>oracle:rules-storage-function-scopes-and-shadowing</code> pins the same resolution for a global function). Only a second definition of one name in the same scope is a compile-time rejection: <code>Function f is already defined.</code> Extended 2026-09-27: the Rules Test API compile-limit capture in <code>packages/pyric/test/rules/linter/fixtures/compile-limits/captures.json</code> records production's structural limits for Storage rulesets: 21 functions on one call stack compile and 22 are rejected, also when no rule calls the chain; 11 <code>let</code> bindings compile and 12 are rejected; and nesting past 99 levels is rejected with "Expression is too complex to evaluate safely.". The Storage evaluator enforces them when <code>parseStorageRules</code> loads a ruleset and has no runtime call depth cap, so a 21-function chain evaluates as production does. <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/sandbox/compile-limits.test.ts#L14" target="_blank" rel="noopener noreferrer"><code>unit:storage/sandbox/compile-limits.test.ts</code></a> pins that each rejected Storage probe is refused with production's messages and each compiling one, including the 21-function chain, evaluates to production's verdicts. <code>parseStorageRules</code> rejects a second definition of one function name in one scope through the same module with <code>Function f is already defined.</code>, pinned by <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/sandbox/rules.test.ts#L9" target="_blank" rel="noopener noreferrer"><code>unit:storage/sandbox/rules.test.ts</code></a>. A second 2026-09-27 recapture adds 7 cases (14 in all): an argument or <code>let</code> whose value errors (<code>1 / request.resource.size</code> of 0, or a method an int does not define) is bound and the function body runs, so a function that never reads it allows, also through two calls, and one that reads it denies; <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/sandbox/rules-error-values.test.ts#L3" target="_blank" rel="noopener noreferrer"><code>unit:storage/sandbox/rules-error-values.test.ts</code></a> pins each verdict. The compile-limit capture adds the <code>slash-divisor</code> probe: <code>(4/2)</code> is rejected with "Missing 'match' keyword before path.", production reading a slash directly followed by a path segment as the start of a path, and <code>(4/ 2)</code> compiles. Extended 2026-10-07: the <code>glob-in-path</code> and <code>glob-nested</code> probes show Storage rejecting a second recursive wildcard in one match declaration path, counted across nested blocks, with "Invalid glob match expression. Only one glob match is permitted in a match declaration path.", as Firestore does; <code>parseStorageRules</code> refuses both rejected probes through the same module and evaluates both compiling ones to production's verdicts, pinned by <code>unit:storage/sandbox/compile-limits.test.ts</code>. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#L27" target="_blank" rel="noopener noreferrer"><code>rules-oracle-conformance.test.ts</code></a>)</div></div>
|
|
204
227
|
</details>
|
|
205
228
|
<details class="compat-row" data-status="ok">
|
|
206
229
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Rules</code><span class="compat-sub"><span class="compat-behavior"><code>request.time</code> compared against <code>timestamp.date(y,m,d)</code> and <code>timestamp.value(ms)</code> constructors</span></span></span></summary>
|
|
207
|
-
<div class="compat-evidence"><div class="compat-probe">NEW ROW, 2026-07-10: production capture proves the evaluator supports <code>request.time</code> comparisons against both timestamp constructors. <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-request-time-timestamp.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-request-time-timestamp</code></a> matches production verdict-for-verdict on all 4 cases (deadline-before/after via <code>timestamp.date()</code>, epoch-bound before/after via <code>timestamp.value()</code>). (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#
|
|
230
|
+
<div class="compat-evidence"><div class="compat-probe">NEW ROW, 2026-07-10: production capture proves the evaluator supports <code>request.time</code> comparisons against both timestamp constructors. <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-request-time-timestamp.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-request-time-timestamp</code></a> matches production verdict-for-verdict on all 4 cases (deadline-before/after via <code>timestamp.date()</code>, epoch-bound before/after via <code>timestamp.value()</code>). (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>rules-oracle-conformance.test.ts</code></a>)</div></div>
|
|
208
231
|
</details>
|
|
209
232
|
<details class="compat-row" data-status="ok">
|
|
210
233
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Rules</code><span class="compat-sub"><span class="compat-behavior"><code>string.matches(regex)</code> with whole-string anchoring (a partial match denies)</span></span></span></summary>
|
|
211
|
-
<div class="compat-evidence"><div class="compat-probe">NEW ROW, 2026-07-10: production capture proves <code>matches()</code> is whole-string anchored, matching a RE2 pattern only when it covers the entire string. <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-matches-regex.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-matches-regex</code></a> matches production verdict-for-verdict on all 3 cases. RE2-inexpressible patterns are rejected at ruleset compile time by production and are covered by evaluator unit tests instead. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#
|
|
234
|
+
<div class="compat-evidence"><div class="compat-probe">NEW ROW, 2026-07-10: production capture proves <code>matches()</code> is whole-string anchored, matching a RE2 pattern only when it covers the entire string. <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-matches-regex.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-matches-regex</code></a> matches production verdict-for-verdict on all 3 cases. RE2-inexpressible patterns are rejected at ruleset compile time by production and are covered by evaluator unit tests instead. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#L51" target="_blank" rel="noopener noreferrer"><code>rules-oracle-conformance.test.ts</code></a>)</div></div>
|
|
212
235
|
</details>
|
|
213
236
|
<details class="compat-row" data-status="ok">
|
|
214
237
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Rules</code><span class="compat-sub"><span class="compat-behavior"><code>resource.metadata.<key></code> custom-metadata access in dotted (<code>resource.metadata.owner</code>) and bracket (<code>resource.metadata['owner']</code>) form, including missing-key deny</span></span></span></summary>
|
|
215
|
-
<div class="compat-evidence"><div class="compat-probe">NEW ROW, 2026-07-10: production capture proves dotted and bracket metadata access resolve identically, and a missing key denies. <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-metadata-access.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-metadata-access</code></a> matches production verdict-for-verdict on all 5 cases. RECAPTURED, 2026-09-28: the scenario adds 6 production cases (11 in all) for a missing key under bracket access on <code>resource.metadata</code> and <code>request.resource.metadata</code>: <code>!= 'x'</code>, <code>== null</code> and <code>!(... == 'x')</code> deny and <code>|| true</code> allows, as for a missing key under dot access. The evaluator already matched all 11. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#
|
|
238
|
+
<div class="compat-evidence"><div class="compat-probe">NEW ROW, 2026-07-10: production capture proves dotted and bracket metadata access resolve identically, and a missing key denies. <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-metadata-access.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-metadata-access</code></a> matches production verdict-for-verdict on all 5 cases. RECAPTURED, 2026-09-28: the scenario adds 6 production cases (11 in all) for a missing key under bracket access on <code>resource.metadata</code> and <code>request.resource.metadata</code>: <code>!= 'x'</code>, <code>== null</code> and <code>!(... == 'x')</code> deny and <code>|| true</code> allows, as for a missing key under dot access. The evaluator already matched all 11. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>rules-oracle-conformance.test.ts</code></a>)</div></div>
|
|
216
239
|
</details>
|
|
217
240
|
<details class="compat-row" data-status="ok">
|
|
218
241
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Rules</code><span class="compat-sub"><span class="compat-behavior">Cross-service <code>firestore.get()</code> / <code>firestore.exists()</code> lookups from a Storage ruleset, with <code>$(expr)</code> path interpolation and qualified function-mock names</span></span></span></summary>
|
|
219
|
-
<div class="compat-evidence"><div class="compat-probe">NEW ROW, 2026-07-10: production capture proves the evaluator resolves cross-service Firestore lookups from Storage rules, including interpolated document paths and both the map-returning <code>get()</code> and bool-returning <code>exists()</code> forms. <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-firestore-lookup.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-firestore-lookup</code></a> matches production verdict-for-verdict on all 4 cases. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#
|
|
242
|
+
<div class="compat-evidence"><div class="compat-probe">NEW ROW, 2026-07-10: production capture proves the evaluator resolves cross-service Firestore lookups from Storage rules, including interpolated document paths and both the map-returning <code>get()</code> and bool-returning <code>exists()</code> forms. <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-firestore-lookup.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-firestore-lookup</code></a> matches production verdict-for-verdict on all 4 cases. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>rules-oracle-conformance.test.ts</code></a>)</div></div>
|
|
220
243
|
</details>
|
|
221
244
|
<details class="compat-row" data-status="ok">
|
|
222
245
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Rules</code><span class="compat-sub"><span class="compat-behavior"><code>resource.name</code> (full object path), <code>resource.timeCreated</code>, <code>resource.updated</code>, <code>resource.bucket</code> — the object-identity and time fields of the existing-object binding, plus <code>duration.value(n, unit)</code>, and <code>request.resource.name</code>, the incoming object's full path in the same form</span></span></span></summary>
|
|
223
|
-
<div class="compat-evidence"><div class="compat-probe">ROW CORRECTED, 2026-07-12: the fields are now modeled, sourced from the persisted object record. <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-resource-object-identity.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-resource-object-identity</code></a> matches production verdict-for-verdict on all 9 cases — an extension guard (<code>resource.name.matches</code>), a freshness window (<code>resource.timeCreated + duration.value(1, 'h')</code>), an immutability check (<code>resource.timeCreated == resource.updated</code>), and an absent-property negation. Production semantics pinned by the capture: <code>resource.name</code> is the object's FULL path within the bucket (GCS object-name convention, sourced from the persisted record's <code>fullPath</code>, not the client SDK's last-segment <code>name</code>); <code>timeCreated</code>/<code>updated</code> are timestamps (production rejects an int with "Unsupported operation error. Received: int < timestamp"); the update-time field is <code>updated</code> — there is NO <code>resource.timeUpdated</code>. An absent property is an evaluation error that absorbs to DENY and SURVIVES negation, so <code>resource.name != 'x'</code> on an object with no name denies rather than false-allowing. The prior row recorded these fields as an unsupported gap; that gap is closed. RECAPTURED, 2026-09-28: the scenario adds 5 production cases (14 in all). <code>request.resource.name</code> on a create is the incoming object's full path, so <code>split('/')[1]</code> is the file segment of <code>named/a.png</code>; a delete carries no <code>request.resource</code>, so reading its name is "Property resource is undefined on object." and the <code>!=</code> comparison still denies; <code>resource.name.split('/')[1]</code> on a get is the file segment; and a segment index past the end is "Index out of bound error. Index: [2] , size: [2].", which survives negation. The evaluator matches production on all 14 cases. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#
|
|
246
|
+
<div class="compat-evidence"><div class="compat-probe">ROW CORRECTED, 2026-07-12: the fields are now modeled, sourced from the persisted object record. <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-resource-object-identity.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-resource-object-identity</code></a> matches production verdict-for-verdict on all 9 cases — an extension guard (<code>resource.name.matches</code>), a freshness window (<code>resource.timeCreated + duration.value(1, 'h')</code>), an immutability check (<code>resource.timeCreated == resource.updated</code>), and an absent-property negation. Production semantics pinned by the capture: <code>resource.name</code> is the object's FULL path within the bucket (GCS object-name convention, sourced from the persisted record's <code>fullPath</code>, not the client SDK's last-segment <code>name</code>); <code>timeCreated</code>/<code>updated</code> are timestamps (production rejects an int with "Unsupported operation error. Received: int < timestamp"); the update-time field is <code>updated</code> — there is NO <code>resource.timeUpdated</code>. An absent property is an evaluation error that absorbs to DENY and SURVIVES negation, so <code>resource.name != 'x'</code> on an object with no name denies rather than false-allowing. The prior row recorded these fields as an unsupported gap; that gap is closed. RECAPTURED, 2026-09-28: the scenario adds 5 production cases (14 in all). <code>request.resource.name</code> on a create is the incoming object's full path, so <code>split('/')[1]</code> is the file segment of <code>named/a.png</code>; a delete carries no <code>request.resource</code>, so reading its name is "Property resource is undefined on object." and the <code>!=</code> comparison still denies; <code>resource.name.split('/')[1]</code> on a get is the file segment; and a segment index past the end is "Index out of bound error. Index: [2] , size: [2].", which survives negation. The evaluator matches production on all 14 cases. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>rules-oracle-conformance.test.ts</code></a>)</div></div>
|
|
224
247
|
</details>
|
|
225
248
|
<details class="compat-row" data-status="ok">
|
|
226
249
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Rules</code><span class="compat-sub"><span class="compat-behavior"><code>request.resource.size</code> arithmetic bounds (<code>+</code>/<code>-</code>/<code><em></em></code><em>/<code>/</code>, <code>>=</code>/<code><=</code>), request/existing content-type and custom <code>metadata</code> checks, <code>request.method</code>/<code>request.path</code>, a <code>request.auth.token</code> custom-claim branch, granular <code>allow list</code>, and a recursive <code>{allPaths=</code></em><code>*}</code> public read tree</span></span></span></summary>
|
|
227
|
-
<div class="compat-evidence"><div class="compat-probe">NEW ROW, 2026-07-12: production capture proves the evaluator matches the production Storage Rules Test API verdict-for-verdict on all 8 cases (owner-scoped size-bounded upload, immutable-content-type update, list grant, public recursive read). <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-metadata-verbs-and-arithmetic.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-metadata-verbs-and-arithmetic</code></a> — replayed by <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#
|
|
250
|
+
<div class="compat-evidence"><div class="compat-probe">NEW ROW, 2026-07-12: production capture proves the evaluator matches the production Storage Rules Test API verdict-for-verdict on all 8 cases (owner-scoped size-bounded upload, immutable-content-type update, list grant, public recursive read). <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-metadata-verbs-and-arithmetic.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-metadata-verbs-and-arithmetic</code></a> — replayed by <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>packages/pyric/test/storage/rules-oracle-conformance.test.ts</code></a>.</div></div>
|
|
228
251
|
</details>
|
|
229
252
|
<details class="compat-row" data-status="ok">
|
|
230
253
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Rules</code><span class="compat-sub"><span class="compat-behavior"><code>resource</code> on a create where no object exists: the create-if-absent guard <code>allow create: if resource == null</code></span></span></span></summary>
|
|
231
|
-
<div class="compat-evidence"><div class="compat-probe">GAP CLOSED, 2026-07-21: production raises a null-value error when <code>resource</code> is referenced on a create where no object exists, including <code>resource == null</code>, so the expression denies. The evaluator now represents the missing binding as a Rules error rather than a usable null. <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-verbs-umbrella-granular.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-verbs-umbrella-granular</code></a> replays the captured DENY directly with no divergence pin; <code>unit:storage/sandbox/rules-evaluator.test.ts</code> covers the evaluator seam. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#
|
|
254
|
+
<div class="compat-evidence"><div class="compat-probe">GAP CLOSED, 2026-07-21: production raises a null-value error when <code>resource</code> is referenced on a create where no object exists, including <code>resource == null</code>, so the expression denies. The evaluator now represents the missing binding as a Rules error rather than a usable null. <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-verbs-umbrella-granular.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-verbs-umbrella-granular</code></a> replays the captured DENY directly with no divergence pin; <code>unit:storage/sandbox/rules-evaluator.test.ts</code> covers the evaluator seam. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>rules-oracle-conformance.test.ts</code></a>)</div></div>
|
|
232
255
|
</details>
|
|
233
256
|
<details class="compat-row" data-status="ok">
|
|
234
257
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Rules</code><span class="compat-sub"><span class="compat-behavior">Ternary <code>?:</code> — branch selection, error-in-condition deny, and <code>error || true</code> absorption</span></span></span></summary>
|
|
235
|
-
<div class="compat-evidence"><div class="compat-probe">NEW ROW, 2026-07-17: production capture proves the evaluator selects ternary branches by condition, denies when the CONDITION errors (even with identical <code>true</code> branches — production errors the whole conditional rather than falling through to a branch), and still absorbs the same error through <code>|| true</code>. <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-ternary-and-error-absorption.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-ternary-and-error-absorption</code></a> matches production verdict-for-verdict on all 5 cases. A division-by-zero in the condition previously yielded JS Infinity (no error) and false-ALLOWed the erroring-condition case; zero divisors now error-and-deny. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#
|
|
258
|
+
<div class="compat-evidence"><div class="compat-probe">NEW ROW, 2026-07-17: production capture proves the evaluator selects ternary branches by condition, denies when the CONDITION errors (even with identical <code>true</code> branches — production errors the whole conditional rather than falling through to a branch), and still absorbs the same error through <code>|| true</code>. <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-ternary-and-error-absorption.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-ternary-and-error-absorption</code></a> matches production verdict-for-verdict on all 5 cases. A division-by-zero in the condition previously yielded JS Infinity (no error) and false-ALLOWed the erroring-condition case; zero divisors now error-and-deny. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>rules-oracle-conformance.test.ts</code></a>)</div></div>
|
|
236
259
|
</details>
|
|
237
260
|
<details class="compat-row" data-status="ok">
|
|
238
261
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Rules</code><span class="compat-sub"><span class="compat-behavior"><code>in</code> on list literals (membership) and on maps (OWN-key membership) — prototype names (<code>toString</code>/<code>constructor</code>/<code>hasOwnProperty</code>) are never keys</span></span></span></summary>
|
|
239
|
-
<div class="compat-evidence"><div class="compat-probe">NEW ROW, 2026-07-17: production capture proves <code>in</code> tests list membership and map OWN keys only — <code>'toString' in metadata</code> is false unless the map literally carries that key, for both custom-metadata maps and map literals. <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-in-membership-and-proto-keys.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-in-membership-and-proto-keys</code></a> matches production verdict-for-verdict on all 8 cases. Pre-fix the evaluator used JS <code>in</code>, which walks the prototype chain and false-ALLOWed the proto-key cases (the same bug class the Firestore simulator fixed under RULES-B7); the own-key check now guards it, pinned by <code>unit:storage/sandbox/rules-values.test.ts</code> JS-semantics guards. RECAPTURED, 2026-09-28: the scenario adds 9 production cases (17 in all) for <code>in</code> over a value that is not a collection. Each is an evaluation error that denies, so its negation denies too: <code>!('a' in null)</code> is "Null value error."; <code>in</code> over a string, an int, a float, a bool, <code>request.time</code>, <code>request.path</code> or a map diff is "Function not found error: Name: [in]."; and <code>!(1 in metadata)</code> is "Unsupported operation error. Received: map.in(int). Expected: map.in(string).". Storage reports the same verdict and text as Firestore for each. The evaluator returned false for an int key into a map, so its negation allowed, and reported its own text for the other shapes; it now shares the Firestore simulator's <code>in</code> implementation (<code>rules/simulator/membership.ts</code>) and matches production on all 17 cases. <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/membership.test.ts#L16" target="_blank" rel="noopener noreferrer"><code>unit:rules/membership.test.ts</code></a> runs every shape through both engines and pins the text. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#
|
|
262
|
+
<div class="compat-evidence"><div class="compat-probe">NEW ROW, 2026-07-17: production capture proves <code>in</code> tests list membership and map OWN keys only — <code>'toString' in metadata</code> is false unless the map literally carries that key, for both custom-metadata maps and map literals. <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-in-membership-and-proto-keys.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-in-membership-and-proto-keys</code></a> matches production verdict-for-verdict on all 8 cases. Pre-fix the evaluator used JS <code>in</code>, which walks the prototype chain and false-ALLOWed the proto-key cases (the same bug class the Firestore simulator fixed under RULES-B7); the own-key check now guards it, pinned by <code>unit:storage/sandbox/rules-values.test.ts</code> JS-semantics guards. RECAPTURED, 2026-09-28: the scenario adds 9 production cases (17 in all) for <code>in</code> over a value that is not a collection. Each is an evaluation error that denies, so its negation denies too: <code>!('a' in null)</code> is "Null value error."; <code>in</code> over a string, an int, a float, a bool, <code>request.time</code>, <code>request.path</code> or a map diff is "Function not found error: Name: [in]."; and <code>!(1 in metadata)</code> is "Unsupported operation error. Received: map.in(int). Expected: map.in(string).". Storage reports the same verdict and text as Firestore for each. The evaluator returned false for an int key into a map, so its negation allowed, and reported its own text for the other shapes; it now shares the Firestore simulator's <code>in</code> implementation (<code>rules/simulator/membership.ts</code>) and matches production on all 17 cases. <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/membership.test.ts#L16" target="_blank" rel="noopener noreferrer"><code>unit:rules/membership.test.ts</code></a> runs every shape through both engines and pins the text. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>rules-oracle-conformance.test.ts</code></a>)</div></div>
|
|
240
263
|
</details>
|
|
241
264
|
<details class="compat-row" data-status="ok">
|
|
242
265
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Rules</code><span class="compat-sub"><span class="compat-behavior"><code>is</code> type tests over the storage bindings (<code>size is int</code>, <code>contentType is string</code>, <code>metadata is map</code>, <code>split() is list</code>) and int/float LITERAL typing (<code>1.0 is float</code>, <code>1 is int</code>, cross checks false)</span></span></span></summary>
|
|
243
|
-
<div class="compat-evidence"><div class="compat-probe">ROW CORRECTED, 2026-07-17 (same day): the evaluator now adopts the simulator's RULES-B5 float model — a <code>RulesFloat</code> wrapper tags float values (a numeric literal written with a decimal point converts to one) while a bare JS number means int — so <code>1.0 is float</code> / <code>!(1.0 is int)</code> type by literal form exactly as production does. <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-type-checks-is.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-type-checks-is</code></a> now matches production verdict-for-verdict on all 3 cases with no knownGap abstentions. The prior same-day row recorded the value-typed divergence; that gap is closed. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#
|
|
266
|
+
<div class="compat-evidence"><div class="compat-probe">ROW CORRECTED, 2026-07-17 (same day): the evaluator now adopts the simulator's RULES-B5 float model — a <code>RulesFloat</code> wrapper tags float values (a numeric literal written with a decimal point converts to one) while a bare JS number means int — so <code>1.0 is float</code> / <code>!(1.0 is int)</code> type by literal form exactly as production does. <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-type-checks-is.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-type-checks-is</code></a> now matches production verdict-for-verdict on all 3 cases with no knownGap abstentions. The prior same-day row recorded the value-typed divergence; that gap is closed. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>rules-oracle-conformance.test.ts</code></a>)</div></div>
|
|
244
267
|
</details>
|
|
245
268
|
<details class="compat-row" data-status="ok">
|
|
246
269
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Rules</code><span class="compat-sub"><span class="compat-behavior">List/map literals, range-slice access <code>[i:j]</code> on lists and strings (out-of-range bounds ERROR, no JS clamp), and structural <code>==</code> on lists</span></span></span></summary>
|
|
247
|
-
<div class="compat-evidence"><div class="compat-probe">NEW ROW, 2026-07-17: production capture proves slices evaluate on <code>split()</code> lists and on strings, that an out-of-range slice END errors and denies (production does NOT clamp to length the way JS <code>.slice()</code> does — same semantics the Firestore capture pinned in rules-firestore-range-slice-list-and-string), and that a slice compares equal to a list literal structurally. <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-list-map-literals-and-slice.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-list-map-literals-and-slice</code></a> matches production verdict-for-verdict on all 6 cases. Pre-fix the evaluator clamped bounds JS-style and compared lists by reference identity; both fixed and pinned by <code>unit:storage/sandbox/rules-values.test.ts</code> JS-semantics guards. RECAPTURED, 2026-09-27: the scenario adds 13 production cases (19 in all) with the Firestore bounds shapes, and Storage reports the same verdict and text as Firestore for each. <code>start</code> must be an index and <code>end - 1</code> must be an index ("Index out of bound error. Index: [i] , size: [n]."), so <code>[0:0]</code>, <code>[n:n]</code>, <code>[1:0]</code> and a slice of an empty list or string deny; <code>start</code> must not exceed <code>end</code> ("Illegal range error. From index: [3] , To index: [1]."); <code>[i:i]</code> for 0 < i < n is empty. The evaluator accepted <code>[0:0]</code>, <code>[n:n]</code> and slices of empty values; it now shares the Firestore simulator's bounds check (<code>rules/simulator/slice-bounds.ts</code>) and matches production on all 19 cases. <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/slice-bounds.test.ts#L14" target="_blank" rel="noopener noreferrer"><code>unit:rules/slice-bounds.test.ts</code></a> runs every shape through both engines. RECAPTURED, 2026-09-28: 4 production cases (23 in all) index a <code>split()</code> list: an index in range reads the element, an index past either end is "Index out of bound error. Index: [5] , size: [4].", and a string key is "Unsupported operation error. Received: list[string]. Expected: list[int]." The evaluator read <code>list['length']</code> as the list's length and allowed that case; list index access now goes through <code>rules/simulator/index-access.ts</code>, shared with the Firestore simulator, and matches production on all 23 cases (<code>unit:rules/index-access.test.ts</code>). RECAPTURED, 2026-09-28: 19 equality cases (42 in all) pin <code>==</code> over numbers inside List and Map literals, with the same verdicts as the Firestore capture: <code>1 == 1.0</code> and <code>0 == -0.0</code> allow, while <code>[1] == [1.0]</code>, <code>[[1]] == [[1.0]]</code>, <code>{'a': 1} == {'a': 1.0}</code>, <code>[0.0] == [-0.0]</code> and <code>[NaN] == [NaN]</code> deny and the <code>!=</code> forms allow. The evaluator compared List and Map elements by numeric value, so each int-and-float shape and each <code>0.0</code> and <code>-0.0</code> shape allowed; <code>==</code> now calls <code>rulesValuesEqual</code> in <code>rules/simulator/value-equality.ts</code>, shared with the Firestore simulator. It matches all 42 verdicts (<a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/simulator/collection-identity.test.ts#L22" target="_blank" rel="noopener noreferrer"><code>unit:rules/simulator/collection-identity.test.ts</code></a>). RECAPTURED, 2026-09-30: 12 production cases (90 in all) send the Firestore scenario's new shapes and get the same verdicts. Dot access on a path reads an unbound name with the bracket form's error (<code>request.path.segments</code> is "Property segments is undefined on object."), and an int index on a timestamp or a latlng is "Function not found error: Name: [[]]." Storage rules evaluate <code>latlng.value()</code>: production fails only at the index that follows it. The evaluator had no <code>latlng</code> namespace and denied every call as an unsupported method, which denied the <code>latlng.value(1.0, 2.0)[0] == null || true</code> case production allows; it now builds a LatLng (<code>unit:storage/sandbox/rules-latlng.test.ts</code>) and matches production on all 90 cases. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#
|
|
270
|
+
<div class="compat-evidence"><div class="compat-probe">NEW ROW, 2026-07-17: production capture proves slices evaluate on <code>split()</code> lists and on strings, that an out-of-range slice END errors and denies (production does NOT clamp to length the way JS <code>.slice()</code> does — same semantics the Firestore capture pinned in rules-firestore-range-slice-list-and-string), and that a slice compares equal to a list literal structurally. <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-list-map-literals-and-slice.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-list-map-literals-and-slice</code></a> matches production verdict-for-verdict on all 6 cases. Pre-fix the evaluator clamped bounds JS-style and compared lists by reference identity; both fixed and pinned by <code>unit:storage/sandbox/rules-values.test.ts</code> JS-semantics guards. RECAPTURED, 2026-09-27: the scenario adds 13 production cases (19 in all) with the Firestore bounds shapes, and Storage reports the same verdict and text as Firestore for each. <code>start</code> must be an index and <code>end - 1</code> must be an index ("Index out of bound error. Index: [i] , size: [n]."), so <code>[0:0]</code>, <code>[n:n]</code>, <code>[1:0]</code> and a slice of an empty list or string deny; <code>start</code> must not exceed <code>end</code> ("Illegal range error. From index: [3] , To index: [1]."); <code>[i:i]</code> for 0 < i < n is empty. The evaluator accepted <code>[0:0]</code>, <code>[n:n]</code> and slices of empty values; it now shares the Firestore simulator's bounds check (<code>rules/simulator/slice-bounds.ts</code>) and matches production on all 19 cases. <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/slice-bounds.test.ts#L14" target="_blank" rel="noopener noreferrer"><code>unit:rules/slice-bounds.test.ts</code></a> runs every shape through both engines. RECAPTURED, 2026-09-28: 4 production cases (23 in all) index a <code>split()</code> list: an index in range reads the element, an index past either end is "Index out of bound error. Index: [5] , size: [4].", and a string key is "Unsupported operation error. Received: list[string]. Expected: list[int]." The evaluator read <code>list['length']</code> as the list's length and allowed that case; list index access now goes through <code>rules/simulator/index-access.ts</code>, shared with the Firestore simulator, and matches production on all 23 cases (<code>unit:rules/index-access.test.ts</code>). RECAPTURED, 2026-09-28: 19 equality cases (42 in all) pin <code>==</code> over numbers inside List and Map literals, with the same verdicts as the Firestore capture: <code>1 == 1.0</code> and <code>0 == -0.0</code> allow, while <code>[1] == [1.0]</code>, <code>[[1]] == [[1.0]]</code>, <code>{'a': 1} == {'a': 1.0}</code>, <code>[0.0] == [-0.0]</code> and <code>[NaN] == [NaN]</code> deny and the <code>!=</code> forms allow. The evaluator compared List and Map elements by numeric value, so each int-and-float shape and each <code>0.0</code> and <code>-0.0</code> shape allowed; <code>==</code> now calls <code>rulesValuesEqual</code> in <code>rules/simulator/value-equality.ts</code>, shared with the Firestore simulator. It matches all 42 verdicts (<a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/simulator/collection-identity.test.ts#L22" target="_blank" rel="noopener noreferrer"><code>unit:rules/simulator/collection-identity.test.ts</code></a>). RECAPTURED, 2026-09-30: 12 production cases (90 in all) send the Firestore scenario's new shapes and get the same verdicts. Dot access on a path reads an unbound name with the bracket form's error (<code>request.path.segments</code> is "Property segments is undefined on object."), and an int index on a timestamp or a latlng is "Function not found error: Name: [[]]." Storage rules evaluate <code>latlng.value()</code>: production fails only at the index that follows it. The evaluator had no <code>latlng</code> namespace and denied every call as an unsupported method, which denied the <code>latlng.value(1.0, 2.0)[0] == null || true</code> case production allows; it now builds a LatLng (<code>unit:storage/sandbox/rules-latlng.test.ts</code>) and matches production on all 90 cases. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>rules-oracle-conformance.test.ts</code></a>)</div></div>
|
|
248
271
|
</details>
|
|
249
272
|
<details class="compat-row" data-status="ok">
|
|
250
273
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Rules</code><span class="compat-sub"><span class="compat-behavior">Float literals, <code>%</code>, unary minus, float division, negative-dividend modulo (<code>-7 % 2 == -1</code>), TRUNCATING int division (<code>10 / 4 == 2</code>, <code>-7 / 2 == -3</code>), <code>+</code> concatenating two strings, and <code>+</code>, <code>-</code>, <code>*</code>, <code>/</code>, <code>%</code> on any other non-numeric operand pair (list + list, string + int, int + string, list + string, map + map) as an Unsupported operation error; <code><</code>, <code>></code>, <code><=</code>, <code>>=</code> ordering ints and floats by value and two strings lexicographically, with every other operand pair (int and string, bool and bool, bool and int, timestamp and int, list and list, map and map, int and null) as an Unsupported operation error; and <code>==</code>, <code>!=</code> across types as false and true</span></span></span></summary>
|
|
251
|
-
<div class="compat-evidence"><div class="compat-probe">ROW CORRECTED, 2026-07-17 (same day): with the RULES-B5 float model adopted from the simulator, int ÷ int truncates toward zero, float division stays float (re-tagged), and int-op-float promotes to float. <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-float-modulo-unary-minus.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-float-modulo-unary-minus</code></a> now matches production verdict-for-verdict on all 6 cases with no knownGap abstentions. An int zero divisor errors-and-denies; a float zero divisor follows the simulator's CEL-pinned ±Infinity/NaN. The prior same-day row recorded the truncation divergence; that gap is closed. ROW EXTENDED, 2026-09-27: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-arithmetic-operand-types.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-arithmetic-operand-types</code></a> captures 20 production cases for operand types. <code>+</code> concatenates two strings, including a path built from <code>request.auth.uid</code> and path variables, and int + float promotes to float. Production has no list + list overload: <code>[1] + [2] == [1, 2]</code> denies with "Unsupported operation error. Received: list + list.", as do string + int (bound or literal), int + string, list + string, string + list, map + map, and <code>-</code>, <code>*</code>, <code>/</code>, <code>%</code> on strings. The error denies through <code>!=</code> and <code>!</code> and <code>|| true</code> absorbs it. The evaluator previously returned an undefined value for a non-numeric pair, so a concatenated string compared with <code>==</code> denied and with <code>!=</code> allowed; it now concatenates two strings and returns the error value for every other pair, matching production verdict-for-verdict on all 20 cases. ROW EXTENDED, 2026-09-27: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-ordering-operand-types.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-ordering-operand-types</code></a> captures 28 production cases for ordering and equality operand types. Ints and floats order by value and two strings order lexicographically. Every other ordering pair is "Unsupported operation error. Received: <left> <op> <right>. Expected: string < string, bytes < bytes, int < int, float < float, duration < duration, timestamp < timestamp, constraint < any, any < constraint.": int < string and string >= int (bound or literal), bool < bool, bool < int, timestamp > int, int <= timestamp, list < list, map < map, and int < null. The error denies on its own and through <code>!</code>, <code>|| true</code> absorbs it, <code>!(error && false)</code> allows, and <code>!(error && true)</code> denies. <code>==</code> and <code>!=</code> across types are not errors: int == string is false, and int != string, bool != int, timestamp != int, list != map, and null != int are true. The evaluator previously computed an ordering of mismatched operands as NaN and returned false, so <code>!(1 < 'a')</code> allowed; it now returns the error value, matching production verdict-for-verdict on all 28 cases. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#
|
|
274
|
+
<div class="compat-evidence"><div class="compat-probe">ROW CORRECTED, 2026-07-17 (same day): with the RULES-B5 float model adopted from the simulator, int ÷ int truncates toward zero, float division stays float (re-tagged), and int-op-float promotes to float. <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-float-modulo-unary-minus.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-float-modulo-unary-minus</code></a> now matches production verdict-for-verdict on all 6 cases with no knownGap abstentions. An int zero divisor errors-and-denies; a float zero divisor follows the simulator's CEL-pinned ±Infinity/NaN. The prior same-day row recorded the truncation divergence; that gap is closed. ROW EXTENDED, 2026-09-27: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-arithmetic-operand-types.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-arithmetic-operand-types</code></a> captures 20 production cases for operand types. <code>+</code> concatenates two strings, including a path built from <code>request.auth.uid</code> and path variables, and int + float promotes to float. Production has no list + list overload: <code>[1] + [2] == [1, 2]</code> denies with "Unsupported operation error. Received: list + list.", as do string + int (bound or literal), int + string, list + string, string + list, map + map, and <code>-</code>, <code>*</code>, <code>/</code>, <code>%</code> on strings. The error denies through <code>!=</code> and <code>!</code> and <code>|| true</code> absorbs it. The evaluator previously returned an undefined value for a non-numeric pair, so a concatenated string compared with <code>==</code> denied and with <code>!=</code> allowed; it now concatenates two strings and returns the error value for every other pair, matching production verdict-for-verdict on all 20 cases. ROW EXTENDED, 2026-09-27: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-ordering-operand-types.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-ordering-operand-types</code></a> captures 28 production cases for ordering and equality operand types. Ints and floats order by value and two strings order lexicographically. Every other ordering pair is "Unsupported operation error. Received: <left> <op> <right>. Expected: string < string, bytes < bytes, int < int, float < float, duration < duration, timestamp < timestamp, constraint < any, any < constraint.": int < string and string >= int (bound or literal), bool < bool, bool < int, timestamp > int, int <= timestamp, list < list, map < map, and int < null. The error denies on its own and through <code>!</code>, <code>|| true</code> absorbs it, <code>!(error && false)</code> allows, and <code>!(error && true)</code> denies. <code>==</code> and <code>!=</code> across types are not errors: int == string is false, and int != string, bool != int, timestamp != int, list != map, and null != int are true. The evaluator previously computed an ordering of mismatched operands as NaN and returned false, so <code>!(1 < 'a')</code> allowed; it now returns the error value, matching production verdict-for-verdict on all 28 cases. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>rules-oracle-conformance.test.ts</code></a>)</div></div>
|
|
252
275
|
</details>
|
|
253
276
|
<details class="compat-row" data-status="ok">
|
|
254
277
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Rules</code><span class="compat-sub"><span class="compat-behavior">Function declarations at GLOBAL scope (above <code>service</code>) and SERVICE scope (inside <code>service</code>, outside any match), with inner-shadows-outer resolution; the global conversion functions <code>int()</code>, <code>string()</code>, and <code>float()</code>, which a same-named ruleset function shadows; no global <code>bool()</code> function; and a call to an undefined function or with the wrong argument count as an error value that <code>&&</code> and <code>||</code> absorb; and the <code>math</code> namespace (<code>abs</code>, <code>ceil</code>, <code>floor</code>, <code>round</code>, <code>sqrt</code>, <code>pow</code>, <code>isNaN</code>, with no <code>isInfinite</code>) with production's result types and argument errors</span></span></span></summary>
|
|
255
|
-
<div class="compat-evidence"><div class="compat-probe">NEW ROW, 2026-07-17: production capture proves production accepts functions declared at global and service scope and resolves a match-scope function over a same-named global (inner shadows outer). <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-function-scopes-and-shadowing.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-function-scopes-and-shadowing</code></a> matches production verdict-for-verdict on all 5 cases. The shared grammar previously rejected both placements at parse (PR #333 grammar fix); the storage evaluator attaches global- and service-scope functions to the root match block, which the capture proves is verdict-equivalent to production's scoping for this surface. ROW EXTENDED, 2026-09-27: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-conversion-functions.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-conversion-functions</code></a> captures 54 production cases for <code>int()</code>, <code>string()</code>, and <code>float()</code>. <code>int(string)</code> parses a whole signed int64 decimal string; whitespace, a fraction, hex, and out-of-range text are "Type conversion error". <code>int(float)</code> truncates toward zero, saturates at the int64 bounds, and maps NaN to 0. <code>float(string)</code> accepts decimal, exponent, and hex forms, surrounding whitespace, an f or d suffix, <code>NaN</code>, and <code>Infinity</code>, and rejects <code>inf</code> and <code>nan</code>. <code>string()</code> accepts int, float, null, string, bool, and path; a float prints with at least one fractional digit and in <code><digits>E<exponent></code> form outside <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-bool-function.json" target="_blank" rel="noopener noreferrer">1e-3, 1e7). A bool or null argument to <code>int()</code> or <code>float()</code>, and a list, map, or bytes argument to <code>string()</code>, is "Unsupported operation error". Every conversion failure, a call to an undefined function ("Function not found error"), and a call with the wrong argument count ("Incorrect number of arguments") is an error value that denies through <code>!=</code> and <code>!</code> and that <code>|| true</code> absorbs; <code>!(foo(1) && false)</code> allows. A ruleset function named <code>int</code> shadows the global. The evaluator previously resolved only ruleset functions and failed every other call closed, so any rule that converted a value denied; it now implements the three conversions and returns the error values, matching production verdict-for-verdict on all 54 cases. RECAPTURED, 2026-09-27: the scenario adds 8 production cases (62 in all). <code>string()</code> prints a float as Java <code>Double.toString</code> does: when the shortest decimal has one significant digit, the two-digit decimal closest to the value wins, so the smallest subnormal prints as <code>4.9E-324</code> (not <code>5.0E-324</code>), <code>1e-323</code> as <code>9.9E-324</code>, and <code>2e-323</code> as <code>2.0E-323</code>. <code>int('1e3')</code> is a "Type conversion error", <code>int(9223372036854775807)</code> keeps the int64 maximum, and <code>string()</code> of a duration or latlng is "Unsupported operation error". The evaluator printed the shortest decimal (<code>5.0E-324</code>) before; it now matches production on all 62 cases, and the Firestore simulator shares its conversion functions. ROW EXTENDED, 2026-09-27: [<code>oracle:rules-storage-bool-function</code></a> captures 25 production cases for <code>bool()</code>. The rules.Boolean reference page shows <code>bool("true") == true</code>, but Storage rules have no global <code>bool()</code>: a call with a string ('true', 'false', 'TRUE', '1'), int (1, 0), bool, null, list, or metadata argument is "Function not found error: Name: [bool]." Each call denies through <code>==</code> and <code>!</code>, a bare <code>bool('true')</code> condition denies, <code>|| true</code> absorbs the error, <code>!(bool('true') && false)</code> allows, and a ruleset function named <code>bool</code> is callable. The evaluator returns the Function not found error value for <code>bool()</code> and matches production verdict-for-verdict on all 25 cases. ROW EXTENDED, 2026-09-27: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-math-namespace.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-math-namespace</code></a> captures 22 production cases for the <code>math</code> namespace, the same cases the Firestore scenario <code>time-math-and-casts</code> sends, with the same verdicts. <code>math.abs()</code> keeps its argument's type, <code>math.ceil()</code> and <code>math.floor()</code> return a float, <code>math.round()</code> rounds half up to an int, <code>math.sqrt()</code> and <code>math.pow()</code> return a float, and <code>math.isNaN()</code> accepts an int or a float. A wrong argument type ("Unsupported operation error"), a wrong argument count ("Incorrect number of arguments"), and <code>math.isInfinite()</code> ("Function not found error", although the rules reference lists it) are error values that deny through <code>!</code> and that <code>|| true</code> absorbs. The evaluator had no <code>math</code> namespace and denied every call as an unsupported method, which denied the 9 cases production allows; it now calls the math module the Firestore simulator uses and matches production on all 22. EXTENDED, 2026-09-27: production rejects a ruleset that defines one function name twice in one match block with "Function f is already defined." at compile time. The Storage parser accepted it and resolved the later definition; it now rejects it with that text through the shared compile-limit module, and a nested match block function that shadows an outer one still parses. <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/sandbox/rules.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>unit:storage/sandbox/rules.test.ts</code></a> pins both. A second 2026-09-27 recapture adds one math case (23 in all): <code>math.abs()</code> of the int64 minimum returns it unchanged, so it stays negative, as the shared math module now does. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#
|
|
278
|
+
<div class="compat-evidence"><div class="compat-probe">NEW ROW, 2026-07-17: production capture proves production accepts functions declared at global and service scope and resolves a match-scope function over a same-named global (inner shadows outer). <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-function-scopes-and-shadowing.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-function-scopes-and-shadowing</code></a> matches production verdict-for-verdict on all 5 cases. The shared grammar previously rejected both placements at parse (PR #333 grammar fix); the storage evaluator attaches global- and service-scope functions to the root match block, which the capture proves is verdict-equivalent to production's scoping for this surface. ROW EXTENDED, 2026-09-27: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-conversion-functions.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-conversion-functions</code></a> captures 54 production cases for <code>int()</code>, <code>string()</code>, and <code>float()</code>. <code>int(string)</code> parses a whole signed int64 decimal string; whitespace, a fraction, hex, and out-of-range text are "Type conversion error". <code>int(float)</code> truncates toward zero, saturates at the int64 bounds, and maps NaN to 0. <code>float(string)</code> accepts decimal, exponent, and hex forms, surrounding whitespace, an f or d suffix, <code>NaN</code>, and <code>Infinity</code>, and rejects <code>inf</code> and <code>nan</code>. <code>string()</code> accepts int, float, null, string, bool, and path; a float prints with at least one fractional digit and in <code><digits>E<exponent></code> form outside <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-bool-function.json" target="_blank" rel="noopener noreferrer">1e-3, 1e7). A bool or null argument to <code>int()</code> or <code>float()</code>, and a list, map, or bytes argument to <code>string()</code>, is "Unsupported operation error". Every conversion failure, a call to an undefined function ("Function not found error"), and a call with the wrong argument count ("Incorrect number of arguments") is an error value that denies through <code>!=</code> and <code>!</code> and that <code>|| true</code> absorbs; <code>!(foo(1) && false)</code> allows. A ruleset function named <code>int</code> shadows the global. The evaluator previously resolved only ruleset functions and failed every other call closed, so any rule that converted a value denied; it now implements the three conversions and returns the error values, matching production verdict-for-verdict on all 54 cases. RECAPTURED, 2026-09-27: the scenario adds 8 production cases (62 in all). <code>string()</code> prints a float as Java <code>Double.toString</code> does: when the shortest decimal has one significant digit, the two-digit decimal closest to the value wins, so the smallest subnormal prints as <code>4.9E-324</code> (not <code>5.0E-324</code>), <code>1e-323</code> as <code>9.9E-324</code>, and <code>2e-323</code> as <code>2.0E-323</code>. <code>int('1e3')</code> is a "Type conversion error", <code>int(9223372036854775807)</code> keeps the int64 maximum, and <code>string()</code> of a duration or latlng is "Unsupported operation error". The evaluator printed the shortest decimal (<code>5.0E-324</code>) before; it now matches production on all 62 cases, and the Firestore simulator shares its conversion functions. ROW EXTENDED, 2026-09-27: [<code>oracle:rules-storage-bool-function</code></a> captures 25 production cases for <code>bool()</code>. The rules.Boolean reference page shows <code>bool("true") == true</code>, but Storage rules have no global <code>bool()</code>: a call with a string ('true', 'false', 'TRUE', '1'), int (1, 0), bool, null, list, or metadata argument is "Function not found error: Name: [bool]." Each call denies through <code>==</code> and <code>!</code>, a bare <code>bool('true')</code> condition denies, <code>|| true</code> absorbs the error, <code>!(bool('true') && false)</code> allows, and a ruleset function named <code>bool</code> is callable. The evaluator returns the Function not found error value for <code>bool()</code> and matches production verdict-for-verdict on all 25 cases. ROW EXTENDED, 2026-09-27: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-math-namespace.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-math-namespace</code></a> captures 22 production cases for the <code>math</code> namespace, the same cases the Firestore scenario <code>time-math-and-casts</code> sends, with the same verdicts. <code>math.abs()</code> keeps its argument's type, <code>math.ceil()</code> and <code>math.floor()</code> return a float, <code>math.round()</code> rounds half up to an int, <code>math.sqrt()</code> and <code>math.pow()</code> return a float, and <code>math.isNaN()</code> accepts an int or a float. A wrong argument type ("Unsupported operation error"), a wrong argument count ("Incorrect number of arguments"), and <code>math.isInfinite()</code> ("Function not found error", although the rules reference lists it) are error values that deny through <code>!</code> and that <code>|| true</code> absorbs. The evaluator had no <code>math</code> namespace and denied every call as an unsupported method, which denied the 9 cases production allows; it now calls the math module the Firestore simulator uses and matches production on all 22. EXTENDED, 2026-09-27: production rejects a ruleset that defines one function name twice in one match block with "Function f is already defined." at compile time. The Storage parser accepted it and resolved the later definition; it now rejects it with that text through the shared compile-limit module, and a nested match block function that shadows an outer one still parses. <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/sandbox/rules.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>unit:storage/sandbox/rules.test.ts</code></a> pins both. A second 2026-09-27 recapture adds one math case (23 in all): <code>math.abs()</code> of the int64 minimum returns it unchanged, so it stays negative, as the shared math module now does. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>rules-oracle-conformance.test.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/simulator/math-builtins.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>math-builtins.test.ts</code></a>)</div></div>
|
|
256
279
|
</details>
|
|
257
280
|
<details class="compat-row" data-status="ok">
|
|
258
281
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Rules</code><span class="compat-sub"><span class="compat-behavior"><code>2+modules</code> Storage rules resolve through service-aware contracts: the common <code>auth</code> and <code>membership</code> exports are admitted and retain their production auth, claim, map-membership, and role semantics; Firestore-only helpers are rejected before evaluation</span></span></span></summary>
|
|
259
|
-
<div class="compat-evidence"><div class="compat-probe">NEW ROW, 2026-07-20; paired proof added 2026-07-21: one targeted production Rules Test API capture proves the exact six bundled function bodies (<code>isAuthenticated</code>, <code>isOwner</code>, <code>hasClaim</code>, <code>hasClaimRole</code>, <code>isMemberOf</code>, <code>hasRole</code>) under <code>firebase.storage</code>, matching the local evaluator verdict-for-verdict on 12 allow/deny cases. <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-common-auth-membership.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-common-auth-membership</code></a> supplies the Storage answer key; Firestore Rules row #189 independently captures the same exact bodies under <code>cloud.firestore</code> (12 matching verdicts). A separately authored, explicitly reconstructed normalized-AST digest locks the captured Storage corpus bodies, and the source-lock test requires the Firestore corpus and shipped modules to remain AST-identical. Resolver tests separately prove <code>2+modules</code> lowers these imports to plain v2 and fails closed with <code>INCOMPATIBLE_FUNCTION</code> for a Firestore-only export and for a transitive helper that reads <code>resource.data</code>; the Storage service integration test proves normal setup invokes that checked resolver. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/test/src/storage-stdlib-source-lock.test.ts#L89" target="_blank" rel="noopener noreferrer"><code>storage-stdlib-source-lock.test.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#
|
|
282
|
+
<div class="compat-evidence"><div class="compat-probe">NEW ROW, 2026-07-20; paired proof added 2026-07-21: one targeted production Rules Test API capture proves the exact six bundled function bodies (<code>isAuthenticated</code>, <code>isOwner</code>, <code>hasClaim</code>, <code>hasClaimRole</code>, <code>isMemberOf</code>, <code>hasRole</code>) under <code>firebase.storage</code>, matching the local evaluator verdict-for-verdict on 12 allow/deny cases. <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-common-auth-membership.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-common-auth-membership</code></a> supplies the Storage answer key; Firestore Rules row #189 independently captures the same exact bodies under <code>cloud.firestore</code> (12 matching verdicts). A separately authored, explicitly reconstructed normalized-AST digest locks the captured Storage corpus bodies, and the source-lock test requires the Firestore corpus and shipped modules to remain AST-identical. Resolver tests separately prove <code>2+modules</code> lowers these imports to plain v2 and fails closed with <code>INCOMPATIBLE_FUNCTION</code> for a Firestore-only export and for a transitive helper that reads <code>resource.data</code>; the Storage service integration test proves normal setup invokes that checked resolver. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/test/src/storage-stdlib-source-lock.test.ts#L89" target="_blank" rel="noopener noreferrer"><code>storage-stdlib-source-lock.test.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#L22" target="_blank" rel="noopener noreferrer"><code>rules-oracle-conformance.test.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/modules/resolver.test.ts#L9" target="_blank" rel="noopener noreferrer"><code>resolver.test.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/enforce.test.ts#L18" target="_blank" rel="noopener noreferrer"><code>enforce.test.ts</code></a>)</div></div>
|
|
260
283
|
</details>
|
|
261
284
|
<details class="compat-row" data-status="ok">
|
|
262
285
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Rules</code><span class="compat-sub"><span class="compat-behavior">Storage-native upload primitives at security boundaries: inclusive byte maximums, exact and whole-string MIME checks, metadata-only updates with unchanged bytes/owner, string path wildcards, generation/metageneration identity, and strict creation-time windows. A client upload is evaluated as <code>create</code> whether or not an object exists at the path, with <code>resource</code> bound to the stored object when one does; a metadata update is evaluated as <code>update</code>. A real upload or metadata update builds <code>request.resource</code> with the object's <code>name</code> (full object path) and <code>bucket</code>, its settable content fields (a client upload that sets no <code>contentDisposition</code> stores <code>inline; filename*=utf-8''<file></code>, the file name percent-encoded per RFC 5987, and an admin-plane write stores none; an unset <code>contentEncoding</code> reads as <code>identity</code> on both writes and in a client metadata read; other unset fields and <code>metadata</code> are null), null <code>generation</code>/<code>metageneration</code>/<code>etag</code> on an upload, the stored <code>generation</code>/<code>metageneration</code> on a metadata update, and no <code>timeCreated</code> or <code>updated</code></span></span></span></summary>
|
|
@@ -264,19 +287,19 @@ html{scrollbar-gutter:stable}
|
|
|
264
287
|
</details>
|
|
265
288
|
<details class="compat-row" data-status="ok">
|
|
266
289
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Rules</code><span class="compat-sub"><span class="compat-behavior">Storage custom-metadata collection methods: <code>Map.keys().hasAll()</code> for required-key policy, <code>Map.values()</code> in written order, <code>Map.get(key, default)</code> for an absent key, and the List methods <code>toSet()</code>, <code>join()</code>, <code>hasAny()</code>, <code>hasOnly()</code>, <code>concat()</code>, and <code>removeAll()</code> on the <code>keys()</code> List and on List literals</span></span></span></summary>
|
|
267
|
-
<div class="compat-evidence"><div class="compat-probe">GAP CLOSED, 2026-07-21: the Storage evaluator now implements own-key <code>Map.keys()</code>, structural <code>hasAll()</code>, and string-key <code>Map.get(key, default)</code>. <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-upload-primitives-boundaries.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-upload-primitives-boundaries</code></a> replays the production required-key and absent-key-default cases directly with no divergence pin; public-seam unit cases cover both admitted forms. RECAPTURED, 2026-09-28: the scenario adds 11 production cases (37 in all) that show <code>Map.keys()</code> returns a List: <code>keys() is list</code> allows, <code>keys() is set</code> is false (its negation allows), and <code>toSet()</code>, <code>hasAll()</code>, <code>hasAny()</code>, <code>hasOnly()</code>, <code>size()</code>, <code>toSet().size()</code>, <code>[0]</code> and <code>join(',')</code> evaluate on it. The evaluator had no <code>join()</code> and did not accept <code>set</code> as an <code>is</code> type name; it now implements both and matches production on all 37 cases. The <code>2+modules</code> resolver typed <code>keys()</code> as a Set and took the <code>hasAll()</code> receiver contract from that typing, so a module function calling <code>keys().size()</code> was rejected; <code>keys()</code> is now a List, <code>hasAll()</code> accepts a List or Set receiver and <code>size()</code> a Set as well, and <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/modules/service-compatibility.test.ts#L1" target="_blank" rel="noopener noreferrer"><code>unit:rules/modules/service-compatibility.test.ts</code></a> pins the admitted shapes. RECAPTURED, 2026-09-28: the scenario adds 52 List-method cases (89 in all), each method with value cases from <code>keys()</code> and from List literals, a <code>!=</code> witness, and error cases written as a negated comparison that would allow if the call returned a value. <code>toSet()</code> takes no argument, and a Set or Map receiver is "Function not found error". <code>join(separator)</code> requires one string separator and converts each element as <code>string()</code> does, so <code>['a', 1, true, null].join('|')</code> is <code>'a|1|true|null'</code> and <code>[2.0, -0.5].join(',')</code> is <code>'2.0,-0.5'</code>, while a List element is <code>string()</code>'s unsupported-operation error. <code>concat(list)</code> appends in order and keeps duplicates, and <code>removeAll(list)</code> drops every element equal to one in the argument, List, Map, and null elements included; a Set, string, or missing argument to either is an error, and a Set, Map, or string receiver is "Function not found error". On a List receiver, <code>hasAll()</code>, <code>hasAny()</code>, and <code>hasOnly()</code> take only a List argument: a Set argument is "Unsupported operation error. Received: list.hasAny(set). Expected: list.hasAny(list)."; a Set receiver takes a List or Set. The Storage evaluator had no <code>concat()</code> or <code>removeAll()</code>, required a List of strings for <code>join()</code>, and accepted a Set argument to a List receiver; it now matches production on all 89 cases and reports production's message for each error case, which <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/simulator/list-methods.test.ts#L9" target="_blank" rel="noopener noreferrer"><code>unit:rules/simulator/list-methods.test.ts</code></a> pins. <code>concat()</code>, <code>removeAll()</code>, and <code>join()</code> live in one module (rules/simulator/list-methods.ts) that the Firestore simulator also calls for <code>concat()</code> and <code>removeAll()</code>. The Storage language catalog gains the six List-method constructs on this row, so the <code>2+modules</code> resolver admits them in a Storage module function. RECAPTURED, 2026-09-28: 63 production cases (152 in all), with the same verdicts and error text as the Firestore <code>list-methods-concat-removeall-toset</code> capture. List membership and <code>removeAll()</code> compare an element's numeric type: <code>1.0 in [1]</code>, <code>[1].hasAny([1.0])</code>, <code>[1].hasAll([1.0])</code>, <code>[1].hasOnly([1.0])</code> and <code>[[1]].hasAny([[1.0]])</code> deny, <code>[1, 2].removeAll([1.0])</code> is <code>[1, 2]</code>, <code>-0.0 in [0.0]</code> allows, and NaN is in no List. Further <code>join()</code> cases: two separators is "Incorrect number of arguments. Received: 2.", <code>['a', null].join(',')</code> is not <code>'a,'</code>, <code>[2.0].join(',')</code> is not <code>'2'</code>, floats print as <code>string()</code> prints them (<code>1.0E8</code>), a path element is its text, a Map, bytes or duration element is an error, and <code>|| true</code> absorbs each error. <code>removeAll()</code>, <code>join()</code>, <code>toSet()</code>, <code>hasAny()</code>, <code>hasAll()</code> and <code>hasOnly()</code> on a string, and <code>concat()</code>, <code>join()</code> and <code>hasAll()</code> on a Map, are "Function not found error". <code>keys()</code> lists a Map literal's keys and custom metadata keys in ascending Unicode code point order (<code>'1' < 'B' < 'a' < 'b'</code>, <code>'10'</code> before <code>'9'</code>, <code>'z'</code> before <code>'😀'</code>), which changes <code>keys()[0]</code> and <code>keys().join()</code>. The evaluator matched List elements by numeric value and returned keys in JavaScript property order; List membership and <code>removeAll()</code> now compare under <code>listElementsEqual</code> in <code>rules/simulator/value-equality.ts</code> and <code>keys()</code> comes from <code>rules/simulator/map-keys.ts</code>, both shared with the Firestore simulator, and it matches all 152 verdicts (<code>unit:rules/simulator/list-methods.test.ts</code>, <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/simulator/collection-identity.test.ts#L22" target="_blank" rel="noopener noreferrer"><code>unit:rules/simulator/collection-identity.test.ts</code></a>). RECAPTURED, 2026-09-30: 33 production cases (185 in all). Production accepts <code>Map.values()</code> on a Map literal and on custom metadata: a literal's values come in the order written, integer-like keys included, a repeated key keeps its first position and its last value, <code>values().join(',')</code> and <code>values()[0]</code> follow that order, and custom metadata values come in the order the request sent them. <code>keys()</code> and <code>values()</code> take no argument, and <code>keys()</code>, <code>values()</code> or <code>get()</code> on a List or string is "Function not found error". A List receiver's <code>hasAll()</code>, <code>hasAny()</code> and <code>hasOnly()</code> take one List: a Map or null argument is an unsupported operation, no argument or two arguments "Incorrect number of arguments", and <code>|| true</code> absorbs each error. The evaluator had no <code>values()</code>, so 13 cases disagreed; <code>values()</code> now comes from <code>rules/simulator/map-keys.ts</code>, shared with the Firestore simulator, and the List membership methods check their argument through <code>listMembership</code> in <code>rules/simulator/list-methods.ts</code>. The evaluator matches all 185 verdicts (<a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#
|
|
290
|
+
<div class="compat-evidence"><div class="compat-probe">GAP CLOSED, 2026-07-21: the Storage evaluator now implements own-key <code>Map.keys()</code>, structural <code>hasAll()</code>, and string-key <code>Map.get(key, default)</code>. <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-upload-primitives-boundaries.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-upload-primitives-boundaries</code></a> replays the production required-key and absent-key-default cases directly with no divergence pin; public-seam unit cases cover both admitted forms. RECAPTURED, 2026-09-28: the scenario adds 11 production cases (37 in all) that show <code>Map.keys()</code> returns a List: <code>keys() is list</code> allows, <code>keys() is set</code> is false (its negation allows), and <code>toSet()</code>, <code>hasAll()</code>, <code>hasAny()</code>, <code>hasOnly()</code>, <code>size()</code>, <code>toSet().size()</code>, <code>[0]</code> and <code>join(',')</code> evaluate on it. The evaluator had no <code>join()</code> and did not accept <code>set</code> as an <code>is</code> type name; it now implements both and matches production on all 37 cases. The <code>2+modules</code> resolver typed <code>keys()</code> as a Set and took the <code>hasAll()</code> receiver contract from that typing, so a module function calling <code>keys().size()</code> was rejected; <code>keys()</code> is now a List, <code>hasAll()</code> accepts a List or Set receiver and <code>size()</code> a Set as well, and <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/modules/service-compatibility.test.ts#L1" target="_blank" rel="noopener noreferrer"><code>unit:rules/modules/service-compatibility.test.ts</code></a> pins the admitted shapes. RECAPTURED, 2026-09-28: the scenario adds 52 List-method cases (89 in all), each method with value cases from <code>keys()</code> and from List literals, a <code>!=</code> witness, and error cases written as a negated comparison that would allow if the call returned a value. <code>toSet()</code> takes no argument, and a Set or Map receiver is "Function not found error". <code>join(separator)</code> requires one string separator and converts each element as <code>string()</code> does, so <code>['a', 1, true, null].join('|')</code> is <code>'a|1|true|null'</code> and <code>[2.0, -0.5].join(',')</code> is <code>'2.0,-0.5'</code>, while a List element is <code>string()</code>'s unsupported-operation error. <code>concat(list)</code> appends in order and keeps duplicates, and <code>removeAll(list)</code> drops every element equal to one in the argument, List, Map, and null elements included; a Set, string, or missing argument to either is an error, and a Set, Map, or string receiver is "Function not found error". On a List receiver, <code>hasAll()</code>, <code>hasAny()</code>, and <code>hasOnly()</code> take only a List argument: a Set argument is "Unsupported operation error. Received: list.hasAny(set). Expected: list.hasAny(list)."; a Set receiver takes a List or Set. The Storage evaluator had no <code>concat()</code> or <code>removeAll()</code>, required a List of strings for <code>join()</code>, and accepted a Set argument to a List receiver; it now matches production on all 89 cases and reports production's message for each error case, which <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/simulator/list-methods.test.ts#L9" target="_blank" rel="noopener noreferrer"><code>unit:rules/simulator/list-methods.test.ts</code></a> pins. <code>concat()</code>, <code>removeAll()</code>, and <code>join()</code> live in one module (rules/simulator/list-methods.ts) that the Firestore simulator also calls for <code>concat()</code> and <code>removeAll()</code>. The Storage language catalog gains the six List-method constructs on this row, so the <code>2+modules</code> resolver admits them in a Storage module function. RECAPTURED, 2026-09-28: 63 production cases (152 in all), with the same verdicts and error text as the Firestore <code>list-methods-concat-removeall-toset</code> capture. List membership and <code>removeAll()</code> compare an element's numeric type: <code>1.0 in [1]</code>, <code>[1].hasAny([1.0])</code>, <code>[1].hasAll([1.0])</code>, <code>[1].hasOnly([1.0])</code> and <code>[[1]].hasAny([[1.0]])</code> deny, <code>[1, 2].removeAll([1.0])</code> is <code>[1, 2]</code>, <code>-0.0 in [0.0]</code> allows, and NaN is in no List. Further <code>join()</code> cases: two separators is "Incorrect number of arguments. Received: 2.", <code>['a', null].join(',')</code> is not <code>'a,'</code>, <code>[2.0].join(',')</code> is not <code>'2'</code>, floats print as <code>string()</code> prints them (<code>1.0E8</code>), a path element is its text, a Map, bytes or duration element is an error, and <code>|| true</code> absorbs each error. <code>removeAll()</code>, <code>join()</code>, <code>toSet()</code>, <code>hasAny()</code>, <code>hasAll()</code> and <code>hasOnly()</code> on a string, and <code>concat()</code>, <code>join()</code> and <code>hasAll()</code> on a Map, are "Function not found error". <code>keys()</code> lists a Map literal's keys and custom metadata keys in ascending Unicode code point order (<code>'1' < 'B' < 'a' < 'b'</code>, <code>'10'</code> before <code>'9'</code>, <code>'z'</code> before <code>'😀'</code>), which changes <code>keys()[0]</code> and <code>keys().join()</code>. The evaluator matched List elements by numeric value and returned keys in JavaScript property order; List membership and <code>removeAll()</code> now compare under <code>listElementsEqual</code> in <code>rules/simulator/value-equality.ts</code> and <code>keys()</code> comes from <code>rules/simulator/map-keys.ts</code>, both shared with the Firestore simulator, and it matches all 152 verdicts (<code>unit:rules/simulator/list-methods.test.ts</code>, <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/simulator/collection-identity.test.ts#L22" target="_blank" rel="noopener noreferrer"><code>unit:rules/simulator/collection-identity.test.ts</code></a>). RECAPTURED, 2026-09-30: 33 production cases (185 in all). Production accepts <code>Map.values()</code> on a Map literal and on custom metadata: a literal's values come in the order written, integer-like keys included, a repeated key keeps its first position and its last value, <code>values().join(',')</code> and <code>values()[0]</code> follow that order, and custom metadata values come in the order the request sent them. <code>keys()</code> and <code>values()</code> take no argument, and <code>keys()</code>, <code>values()</code> or <code>get()</code> on a List or string is "Function not found error". A List receiver's <code>hasAll()</code>, <code>hasAny()</code> and <code>hasOnly()</code> take one List: a Map or null argument is an unsupported operation, no argument or two arguments "Incorrect number of arguments", and <code>|| true</code> absorbs each error. The evaluator had no <code>values()</code>, so 13 cases disagreed; <code>values()</code> now comes from <code>rules/simulator/map-keys.ts</code>, shared with the Firestore simulator, and the List membership methods check their argument through <code>listMembership</code> in <code>rules/simulator/list-methods.ts</code>. The evaluator matches all 185 verdicts (<a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>unit:storage/rules-oracle-conformance.test.ts</code></a>, <code>unit:rules/simulator/list-methods.test.ts</code>). (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/map-keys-list.test.ts#L8" target="_blank" rel="noopener noreferrer"><code>map-keys-list.test.ts</code></a>)</div></div>
|
|
268
291
|
</details>
|
|
269
292
|
<details class="compat-row" data-status="ok">
|
|
270
293
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Rules</code><span class="compat-sub"><span class="compat-behavior"><code>request.resource</code> on delete where no incoming object exists: <code>request.resource == null</code></span></span></span></summary>
|
|
271
|
-
<div class="compat-evidence"><div class="compat-probe">GAP CLOSED, 2026-07-21: production errors when <code>request.resource</code> is absent on delete, including a direct null comparison. The evaluator now carries the absent incoming binding as a Rules error. <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-upload-primitives-boundaries.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-upload-primitives-boundaries</code></a> replays the captured DENY directly with no divergence pin; <code>unit:storage/sandbox/rules-evaluator.test.ts</code> covers the evaluator seam. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#
|
|
294
|
+
<div class="compat-evidence"><div class="compat-probe">GAP CLOSED, 2026-07-21: production errors when <code>request.resource</code> is absent on delete, including a direct null comparison. The evaluator now carries the absent incoming binding as a Rules error. <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-upload-primitives-boundaries.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-upload-primitives-boundaries</code></a> replays the captured DENY directly with no divergence pin; <code>unit:storage/sandbox/rules-evaluator.test.ts</code> covers the evaluator seam. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>rules-oracle-conformance.test.ts</code></a>)</div></div>
|
|
272
295
|
</details>
|
|
273
296
|
<details class="compat-row" data-status="diverged">
|
|
274
297
|
<summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">Rules</code><span class="compat-sub"><span class="compat-behavior">Mocked Storage-to-Firestore lookup evaluation: get/exists return and missing-field errors, repeated and same-resolved-path calls, get+exists composition, and lazy <code>||</code>/<code>&&</code>/ternary branches with hosted function-call diagnostics</span></span></span></summary>
|
|
275
|
-
<div class="compat-evidence"><div class="compat-probe">DOCUMENTED MOCK-ORACLE DIVERGENCE, 2026-07-21: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-firestore-lookup-budget.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-firestore-lookup-budget</code></a> matches the local evaluator on 13 of 14 cases and hosted diagnostics prove lazy branches omit lookup calls. The one mismatch is intentional and pinned on both sides: the Rules Test API ALLOWs three distinct mocked exists calls, while the evaluator DENYs the third distinct path to match the separately captured real-resource production contract in row #131. This row remains diverged because its mocked observation is not verdict-identical; it supplies no budget credit. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#
|
|
298
|
+
<div class="compat-evidence"><div class="compat-probe">DOCUMENTED MOCK-ORACLE DIVERGENCE, 2026-07-21: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-firestore-lookup-budget.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-firestore-lookup-budget</code></a> matches the local evaluator on 13 of 14 cases and hosted diagnostics prove lazy branches omit lookup calls. The one mismatch is intentional and pinned on both sides: the Rules Test API ALLOWs three distinct mocked exists calls, while the evaluator DENYs the third distinct path to match the separately captured real-resource production contract in row #131. This row remains diverged because its mocked observation is not verdict-identical; it supplies no budget credit. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>rules-oracle-conformance.test.ts</code></a>)</div></div>
|
|
276
299
|
</details>
|
|
277
300
|
<details class="compat-row" data-status="ok">
|
|
278
301
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Rules</code><span class="compat-sub"><span class="compat-behavior">Anonymous <code>request.auth != null</code> used as a ternary condition before a cross-service lookup</span></span></span></summary>
|
|
279
|
-
<div class="compat-evidence"><div class="compat-probe">GAP CLOSED, 2026-07-21: anonymous <code>request.auth</code> is now represented as an absent-property Rules error, so a ternary condition denies before selecting either lookup branch, matching hosted diagnostics. <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-firestore-lookup-budget.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-firestore-lookup-budget</code></a> replays the captured DENY directly with no divergence pin; <code>unit:storage/sandbox/rules-methods.test.ts</code> covers the lookup evaluator seam. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#
|
|
302
|
+
<div class="compat-evidence"><div class="compat-probe">GAP CLOSED, 2026-07-21: anonymous <code>request.auth</code> is now represented as an absent-property Rules error, so a ternary condition denies before selecting either lookup branch, matching hosted diagnostics. <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-firestore-lookup-budget.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-firestore-lookup-budget</code></a> replays the captured DENY directly with no divergence pin; <code>unit:storage/sandbox/rules-methods.test.ts</code> covers the lookup evaluator seam. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>rules-oracle-conformance.test.ts</code></a>)</div></div>
|
|
280
303
|
</details>
|
|
281
304
|
<details class="compat-row" data-status="ok">
|
|
282
305
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Rules</code><span class="compat-sub"><span class="compat-behavior">Real-resource Storage-to-Firestore contract: at most two distinct document paths per evaluation with repeated-path caching; typed document data and missing-document errors; default-database-only and owning-project isolation; lazy/helper/let composition; and independence from Firestore client rules</span></span></span></summary>
|
|
@@ -284,43 +307,107 @@ html{scrollbar-gutter:stable}
|
|
|
284
307
|
</details>
|
|
285
308
|
<details class="compat-row" data-status="ok">
|
|
286
309
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Rules</code><span class="compat-sub"><span class="compat-behavior">The service-scoped <code>storage/uploads</code>, <code>storage/metadata</code>, <code>storage/objects</code>, and <code>storage/time</code> modules resolve through <code>2+modules</code> and preserve their production upload-bound, MIME, metadata, operation-identity, and strict timestamp-window semantics</span></span></span></summary>
|
|
287
|
-
<div class="compat-evidence"><div class="compat-probe">NEW ROW, 2026-07-21: one bounded Rules Test API batch captures 15 verdicts for the exact 13 deployable function bodies in the four shipped modules (<a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-stdlib-storage-modules.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-stdlib-storage-modules</code></a>). A separately authored, explicitly reconstructed normalized-AST digest locks the captured corpus bodies, and the source-lock test requires the shipped functions of the same names to remain AST-identical. The generic Storage stdlib fixture runner and oracle replay execute those bodies locally; resolver tests prove the modules are admitted only for <code>firebase.storage</code>, lower to plain v2, and fail closed under Firestore. The module surface excludes the advanced integrity and metadata-diff candidates. UPDATED, 2026-09-28: Storage module functions may call <code>firestore.get()</code> and <code>firestore.exists()</code> within production's limit of two distinct Firestore documents per request. <code>storage/objects</code> adds <code>nameSegment(index)</code> and <code>matchesDocument(path, field)</code>; <code>matchesDocument</code> passes a path argument to <code>firestore.get()</code> and reads one document per call. <code>oracle:rules-storage-resource-object-identity</code> (row #116) captures the <code>request.resource.name</code> and <code>split('/')</code> index shapes they rely on, and the module replay <code>test/rules/modules/fixtures/stdlib-replay-storage-objects.json</code> records production agreeing with all 26 <code>storage/objects</code> cases, checked by <code>unit:conformance/firestore-stdlib-replay.test.ts</code>; <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/test/src/storage-stdlib-source-lock.test.ts#L54" target="_blank" rel="noopener noreferrer"><code>unit:conformance/storage-stdlib-source-lock.test.ts</code></a> requires that replay for every shipped function outside the captured batch. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#
|
|
310
|
+
<div class="compat-evidence"><div class="compat-probe">NEW ROW, 2026-07-21: one bounded Rules Test API batch captures 15 verdicts for the exact 13 deployable function bodies in the four shipped modules (<a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-stdlib-storage-modules.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-stdlib-storage-modules</code></a>). A separately authored, explicitly reconstructed normalized-AST digest locks the captured corpus bodies, and the source-lock test requires the shipped functions of the same names to remain AST-identical. The generic Storage stdlib fixture runner and oracle replay execute those bodies locally; resolver tests prove the modules are admitted only for <code>firebase.storage</code>, lower to plain v2, and fail closed under Firestore. The module surface excludes the advanced integrity and metadata-diff candidates. UPDATED, 2026-09-28: Storage module functions may call <code>firestore.get()</code> and <code>firestore.exists()</code> within production's limit of two distinct Firestore documents per request. <code>storage/objects</code> adds <code>nameSegment(index)</code> and <code>matchesDocument(path, field)</code>; <code>matchesDocument</code> passes a path argument to <code>firestore.get()</code> and reads one document per call. <code>oracle:rules-storage-resource-object-identity</code> (row #116) captures the <code>request.resource.name</code> and <code>split('/')</code> index shapes they rely on, and the module replay <code>test/rules/modules/fixtures/stdlib-replay-storage-objects.json</code> records production agreeing with all 26 <code>storage/objects</code> cases, checked by <code>unit:conformance/firestore-stdlib-replay.test.ts</code>; <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/test/src/storage-stdlib-source-lock.test.ts#L54" target="_blank" rel="noopener noreferrer"><code>unit:conformance/storage-stdlib-source-lock.test.ts</code></a> requires that replay for every shipped function outside the captured batch. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#L22" target="_blank" rel="noopener noreferrer"><code>rules-oracle-conformance.test.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/stdlib-cases.test.ts#L6" target="_blank" rel="noopener noreferrer"><code>stdlib-cases.test.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/stdlib.test.ts#L3" target="_blank" rel="noopener noreferrer"><code>stdlib.test.ts</code></a>)</div></div>
|
|
288
311
|
</details>
|
|
289
312
|
<details class="compat-row" data-status="ok">
|
|
290
313
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Rules</code><span class="compat-sub"><span class="compat-behavior">CEL commutative error absorption in && and ||: a determining operand (false for &&, true for ||) absorbs the other operand's error; error && true and true && error both propagate to deny</span></span></span></summary>
|
|
291
|
-
<div class="compat-evidence"><div class="compat-probe">NEW ROW, 2026-09-01: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-error-absorption-and-direction.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-error-absorption-and-direction</code></a> captures the previously unpinned && direction of corpus scenario "error-absorption-and-direction" via consumed discriminators (!(error && false) and (error && false) == false both ALLOW; both propagation orders DENY; the || direction is consumed as a control). The local evaluator replays all six verdicts with no divergence pin after the tri-state operand change in storage/sandbox/rules-evaluator.ts, which mirrors the captured Firestore semantics (RULES-B3). The 2026-09-27 recapture adds allow rules after an error (<code>1 / 0 == 0</code>): a later true rule in the same block or a second matching block allows, a later false rule denies, and an error then a true rule past the 1000-expression limit denies with the earlier error as production's note; <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/sandbox/rules-expression-budget.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>unit:storage/sandbox/rules-expression-budget.test.ts</code></a> pins the count and the reported error. A second 2026-09-27 recapture adds 8 cases (18 in all): an int division or modulo by <code>request.resource.size</code> of 0 is "Divide by zero error."; with two erroring operands production reports the left one of <code>==</code>, the first list element and method argument, the receiver before an argument, and the collection of <code>in</code> before its element; and an operand past the limit after an error in the same expression denies with the limit message. <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/sandbox/rules-error-values.test.ts#L12" target="_blank" rel="noopener noreferrer"><code>unit:storage/sandbox/rules-error-values.test.ts</code></a> pins the errors production named. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#
|
|
314
|
+
<div class="compat-evidence"><div class="compat-probe">NEW ROW, 2026-09-01: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-error-absorption-and-direction.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-error-absorption-and-direction</code></a> captures the previously unpinned && direction of corpus scenario "error-absorption-and-direction" via consumed discriminators (!(error && false) and (error && false) == false both ALLOW; both propagation orders DENY; the || direction is consumed as a control). The local evaluator replays all six verdicts with no divergence pin after the tri-state operand change in storage/sandbox/rules-evaluator.ts, which mirrors the captured Firestore semantics (RULES-B3). The 2026-09-27 recapture adds allow rules after an error (<code>1 / 0 == 0</code>): a later true rule in the same block or a second matching block allows, a later false rule denies, and an error then a true rule past the 1000-expression limit denies with the earlier error as production's note; <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/sandbox/rules-expression-budget.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>unit:storage/sandbox/rules-expression-budget.test.ts</code></a> pins the count and the reported error. A second 2026-09-27 recapture adds 8 cases (18 in all): an int division or modulo by <code>request.resource.size</code> of 0 is "Divide by zero error."; with two erroring operands production reports the left one of <code>==</code>, the first list element and method argument, the receiver before an argument, and the collection of <code>in</code> before its element; and an operand past the limit after an error in the same expression denies with the limit message. <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/sandbox/rules-error-values.test.ts#L12" target="_blank" rel="noopener noreferrer"><code>unit:storage/sandbox/rules-error-values.test.ts</code></a> pins the errors production named. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>rules-oracle-conformance.test.ts</code></a>)</div></div>
|
|
292
315
|
</details>
|
|
293
316
|
<details class="compat-row" data-status="ok">
|
|
294
317
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Rules</code><span class="compat-sub"><span class="compat-behavior">Storage to Firestore IAM boundary: without <code>roles/firebaserules.firestoreServiceAgent</code> on the Storage service agent, every EXECUTED <code>firestore.get()/exists()</code> lookup fails and the rule DENIES, while a short-circuited lookup is never executed and its rule still ALLOWS. Modeled by the <code>crossServiceIam: 'granted' | 'denied'</code> storage sandbox option (default granted)</span></span></span></summary>
|
|
295
318
|
<div class="compat-evidence"><div class="compat-probe">PROMOTED, 2026-08-31: the real-resource IAM-DISABLED deploy-observe-restore baseline <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/stdlib-realstorage-p3-lookup-budget.json" target="_blank" rel="noopener noreferrer"><code>oracle:stdlib-realstorage-p3-lookup-budget</code></a> (previously an excused negative capture, and the sibling of row #131's IAM-enabled twin: same probe matrix, role removed) pins one/two/three/repeat/get-exists/missing-* = DENY and short = ALLOW. <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/test/src/storage-stdlib-real-replay.test.ts#L96" target="_blank" rel="noopener noreferrer"><code>unit:conformance/storage-stdlib-real-replay.test.ts</code></a> replays all 8 captured verdicts against the local evaluator injected with the EXACT production denied-mode lookup (<code>crossServiceIamDeniedLookup</code> from storage/enforce.ts); <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/e2e-soundness/cross-service-iam.test.ts#L22" target="_blank" rel="noopener noreferrer"><code>unit:pyric/test/e2e-soundness/cross-service-iam.test.ts</code></a> proves the <code>crossServiceIam</code> option reaches that lookup through the full sandbox enforcement path, that granted and default behavior is unchanged, and that a late differing mode throws. Scope: the capture pins only the executed-lookup/short-circuit boundary. How an IAM failure composes with CEL <code>&&</code>/<code>||</code> error absorption was not exercised IAM-disabled and remains unpinned; the denied-mode error uses the evaluator's absorbable error class, the existing no-capability precedent.</div></div>
|
|
296
319
|
</details>
|
|
297
|
-
<details class="compat-row" data-status="
|
|
298
|
-
<summary class="compat-line"><span class="compat-dot" data-status="
|
|
299
|
-
<div class="compat-evidence"><div class="compat-probe">BORN UNVERIFIED, 2026-09-08: the local evaluator follows CEL's boolean typing of the allow boundary and the pinned Firestore capture <code>oracle:rules-firestore-strict-boolean-control-flow</code>, which records production denying three direct non-boolean conditions under <code>cloud.firestore</code> (registry row #188). No Storage capture pins the same position under <code>firebase.storage</code> yet. Corpus scenario <code>strict-boolean-allow-and-ternary</code> is the capture that would verify this row; <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/sandbox/rules-evaluator.test.ts#L3" target="_blank" rel="noopener noreferrer"><code>unit:storage/sandbox/rules-evaluator.test.ts</code></a> pins the local behavior in the meantime.</div></div>
|
|
320
|
+
<details class="compat-row" data-status="ok">
|
|
321
|
+
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Rules</code><span class="compat-sub"><span class="compat-behavior">CEL types the top-level <code>allow</code> condition as bool: a non-boolean result is an evaluation error that denies the rule and lets the walk continue to the next rule, rather than a JavaScript truthiness coercion</span></span></span></summary>
|
|
322
|
+
<div class="compat-evidence"><div class="compat-probe">BORN UNVERIFIED, 2026-09-08: the local evaluator follows CEL's boolean typing of the allow boundary and the pinned Firestore capture <code>oracle:rules-firestore-strict-boolean-control-flow</code>, which records production denying three direct non-boolean conditions under <code>cloud.firestore</code> (registry row #188). No Storage capture pins the same position under <code>firebase.storage</code> yet. Corpus scenario <code>strict-boolean-allow-and-ternary</code> is the capture that would verify this row; <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/sandbox/rules-evaluator.test.ts#L3" target="_blank" rel="noopener noreferrer"><code>unit:storage/sandbox/rules-evaluator.test.ts</code></a> pins the local behavior in the meantime. VERIFIED, 2026-10-27: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-strict-boolean-allow-and-ternary.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-strict-boolean-allow-and-ternary</code></a> captures the scenario under <code>firebase.storage</code> (5 cases), and the evaluator agrees on all 5; <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>unit:storage/rules-oracle-conformance.test.ts</code></a> replays them with no pin.</div></div>
|
|
300
323
|
</details>
|
|
301
|
-
<details class="compat-row" data-status="
|
|
302
|
-
<summary class="compat-line"><span class="compat-dot" data-status="
|
|
303
|
-
<div class="compat-evidence"><div class="compat-probe">BORN UNVERIFIED, 2026-09-08: the local evaluator follows CEL's boolean typing of the ternary condition and the pinned Firestore capture <code>oracle:rules-firestore-strict-boolean-control-flow</code>, whose three tautological error discriminators still DENY under <code>cloud.firestore</code> (registry row #188). No Storage capture pins the same position under <code>firebase.storage</code> yet. Corpus scenario <code>strict-boolean-allow-and-ternary</code> consumes the error through <code>|| true</code> and <code>!(… && false)</code> and is the capture that would verify this row; <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/sandbox/rules-evaluator.test.ts#L3" target="_blank" rel="noopener noreferrer"><code>unit:storage/sandbox/rules-evaluator.test.ts</code></a> pins the local behavior in the meantime.</div></div>
|
|
324
|
+
<details class="compat-row" data-status="ok">
|
|
325
|
+
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Rules</code><span class="compat-sub"><span class="compat-behavior">CEL types a ternary CONDITION as bool: a non-boolean condition produces an evaluation error that propagates like any other rules error, so <code>|| true</code> and a negated <code>&& false</code> both absorb it</span></span></span></summary>
|
|
326
|
+
<div class="compat-evidence"><div class="compat-probe">BORN UNVERIFIED, 2026-09-08: the local evaluator follows CEL's boolean typing of the ternary condition and the pinned Firestore capture <code>oracle:rules-firestore-strict-boolean-control-flow</code>, whose three tautological error discriminators still DENY under <code>cloud.firestore</code> (registry row #188). No Storage capture pins the same position under <code>firebase.storage</code> yet. Corpus scenario <code>strict-boolean-allow-and-ternary</code> consumes the error through <code>|| true</code> and <code>!(… && false)</code> and is the capture that would verify this row; <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/sandbox/rules-evaluator.test.ts#L3" target="_blank" rel="noopener noreferrer"><code>unit:storage/sandbox/rules-evaluator.test.ts</code></a> pins the local behavior in the meantime. VERIFIED, 2026-10-27: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-strict-boolean-allow-and-ternary.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-strict-boolean-allow-and-ternary</code></a> captures the scenario under <code>firebase.storage</code> (5 cases), and the evaluator agrees on all 5; <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>unit:storage/rules-oracle-conformance.test.ts</code></a> replays them with no pin.</div></div>
|
|
304
327
|
</details>
|
|
305
328
|
<details class="compat-row" data-status="ok">
|
|
306
329
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Rules</code><span class="compat-sub"><span class="compat-behavior"><code>request.time</code>, <code>resource.timeCreated</code>, and <code>resource.updated</code> are timestamps; <code>timestamp.date</code>/<code>timestamp.value</code> build timestamps and <code>duration.value</code>/<code>duration.time</code>/<code>duration.abs</code> build durations. The timestamp accessors <code>year()</code>, <code>month()</code>, <code>day()</code>, <code>hours()</code>, <code>minutes()</code>, <code>seconds()</code>, <code>nanos()</code>, <code>dayOfWeek()</code>, <code>dayOfYear()</code>, <code>date()</code>, <code>time()</code>, and <code>toMillis()</code> read UTC components, where <code>seconds()</code> is the seconds of the minute and <code>toMillis()</code> is the only epoch value. Timestamp minus timestamp is a duration, timestamp plus or minus duration is a timestamp, values of one type compare with <code>==</code> and <code><</code>, and <code>is timestamp</code> and <code>is duration</code> hold. An int has no timestamp methods, an error that <code>|| true</code> absorbs, and comparing a timestamp with an int is an error</span></span></span></summary>
|
|
307
|
-
<div class="compat-evidence"><div class="compat-probe">CONFORMS, 2026-09-24: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-stdlib-timestamp-duration.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-stdlib-timestamp-duration</code></a> captures 15 production verdicts for corpus scenario "stdlib-timestamp-duration", including the paired seconds-of-minute and epoch-seconds cases and the production diagnostics <code>Function not found error: Name: [hours].</code> and <code>Unsupported operation error. Received: timestamp < int.</code> <code>request.time</code>, the resource time fields, and the <code>timestamp.<em></em></code><em> and <code>duration.</code></em> constructors are the shared Timestamp and Duration values, which answer their own accessors and operators. <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#
|
|
330
|
+
<div class="compat-evidence"><div class="compat-probe">CONFORMS, 2026-09-24: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-stdlib-timestamp-duration.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-stdlib-timestamp-duration</code></a> captures 15 production verdicts for corpus scenario "stdlib-timestamp-duration", including the paired seconds-of-minute and epoch-seconds cases and the production diagnostics <code>Function not found error: Name: [hours].</code> and <code>Unsupported operation error. Received: timestamp < int.</code> <code>request.time</code>, the resource time fields, and the <code>timestamp.<em></em></code><em> and <code>duration.</code></em> constructors are the shared Timestamp and Duration values, which answer their own accessors and operators. <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>unit:storage/rules-oracle-conformance.test.ts</code></a> replays all 15 verdicts; <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/sandbox/rules-time-methods.test.ts#L60" target="_blank" rel="noopener noreferrer"><code>unit:storage/sandbox/rules-time-methods.test.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/sandbox/rules-operators.test.ts#L10" target="_blank" rel="noopener noreferrer"><code>unit:storage/sandbox/rules-operators.test.ts</code></a>, and <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/sandbox/rules-bindings.test.ts#L24" target="_blank" rel="noopener noreferrer"><code>unit:storage/sandbox/rules-bindings.test.ts</code></a> pin the values and error messages.</div></div>
|
|
308
331
|
</details>
|
|
309
332
|
<details class="compat-row" data-status="ok">
|
|
310
333
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Rules</code><span class="compat-sub"><span class="compat-behavior">String literal escapes, including <code>\x</code> and octal escapes as code points and <code>\u</code> as one UTF-16 code unit, with <code>\/</code> a parse error; and String <code>lower()</code>, <code>upper()</code>, <code>trim()</code>, <code>replace(pattern, replacement)</code>, and <code>toUtf8()</code>. <code>replace</code> compiles the pattern as a regular expression, replaces every match, expands <code>$1</code> group references and reads <code>\$</code> as a dollar sign in the replacement, and is an error on an invalid pattern; <code>toUtf8()</code> returns Bytes. An int has no string methods, which is an error</span></span></span></summary>
|
|
311
|
-
<div class="compat-evidence"><div class="compat-probe">CONFORMS, 2026-09-24: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-stdlib-string-bytes-hashing.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-stdlib-string-bytes-hashing</code></a> captures 19 production verdicts for corpus scenario "stdlib-string-bytes-hashing"; its case and replace cases pin regular-expression replace-all, Java <code>Matcher</code> replacement templates, and <code>Invalid regular expression pattern</code> on <code>(</code>. A <code>$&</code> replacement makes the Rules Test API answer HTTP 500 for the whole request, so production's verdict for an invalid group reference is not captured; the evaluator denies it as an error. <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#
|
|
334
|
+
<div class="compat-evidence"><div class="compat-probe">CONFORMS, 2026-09-24: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-stdlib-string-bytes-hashing.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-stdlib-string-bytes-hashing</code></a> captures 19 production verdicts for corpus scenario "stdlib-string-bytes-hashing"; its case and replace cases pin regular-expression replace-all, Java <code>Matcher</code> replacement templates, and <code>Invalid regular expression pattern</code> on <code>(</code>. A <code>$&</code> replacement makes the Rules Test API answer HTTP 500 for the whole request, so production's verdict for an invalid group reference is not captured; the evaluator denies it as an error. <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>unit:storage/rules-oracle-conformance.test.ts</code></a> replays all 19 verdicts and <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/sandbox/rules-string-methods.test.ts#L69" target="_blank" rel="noopener noreferrer"><code>unit:storage/sandbox/rules-string-methods.test.ts</code></a> pins the methods, the replacement templates, and the receiver and argument errors. ROW EXTENDED, 2026-09-27: the same capture adds 18 production verdicts for string literal escapes in Storage rules, 37 in all, the same cases and verdicts as the string escape cases of the Firestore capture rules-firestore-string-literals-and-regex. A backslash escapes a backslash, either quote, <code>n</code>, <code>r</code>, <code>t</code>, <code>b</code>, or <code>f</code>. <code>\x</code> takes exactly two hexadecimal digits and <code>\</code> takes three octal digits up to 377; each is the code point of that value, not a byte. <code>\u</code> takes exactly four hexadecimal digits and is one UTF-16 code unit, and <code>size()</code> counts code units. Production rejects the Storage ruleset for the same malformed escapes as the Firestore one, including <code>\/</code>, <code>\a</code>, <code>\v</code>, <code>\0</code>, <code>\x4</code>, <code>\U</code> escapes, and a raw line break inside the quotes, and the shared grammar rejects them. The grammar previously rejected <code>\x</code>, <code>\u</code>, octal, <code>\b</code>, and <code>\f</code> escapes and accepted <code>\/</code>. <code>unit:storage/rules-oracle-conformance.test.ts</code> replays all 37 verdicts and <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/sandbox/rules-string-escapes.test.ts#L10" target="_blank" rel="noopener noreferrer"><code>unit:storage/sandbox/rules-string-escapes.test.ts</code></a> pins the string escape cases. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/grammar/string-escape-strict.test.ts#L107" target="_blank" rel="noopener noreferrer"><code>string-escape-strict.test.ts</code></a>)</div></div>
|
|
312
335
|
</details>
|
|
313
336
|
<details class="compat-row" data-status="ok">
|
|
314
337
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Rules</code><span class="compat-sub"><span class="compat-behavior">Bytes from a bytes literal <code>b'...'</code>, from <code>toUtf8()</code>, and from <code>hashing.md5</code>, <code>hashing.sha256</code>, <code>hashing.crc32</code>, and <code>hashing.crc32c</code> over a string or Bytes: <code>size()</code> counts bytes, <code>toBase64()</code> is padded base64url, <code>toHexString()</code> is uppercase hexadecimal, Bytes compare by value, a string hashes like its UTF-8 bytes, and CRC digests serialize least-significant byte first. Hashing an int is an error</span></span></span></summary>
|
|
315
|
-
<div class="compat-evidence"><div class="compat-probe">CONFORMS, 2026-09-24: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-stdlib-string-bytes-hashing.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-stdlib-string-bytes-hashing</code></a> pins the md5, sha256, crc32, and crc32c digests of <code>abc</code> as hexadecimal, the base64url and hexadecimal encodings of <code>hi</code>, Bytes equality, and the production diagnostic <code>Unsupported operation error. Received: hashing.md5(int).</code> These are the representations the Firestore capture rules-firestore-bytes-toutf8-and-hashing pins for <code>cloud.firestore</code> (row #161), and the Storage evaluator computes them with the same shared Bytes wrapper and built-in hashing functions, whose MD5 and SHA-256 <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/simulator/hashing-builtins.test.ts#L24" target="_blank" rel="noopener noreferrer"><code>unit:rules/simulator/hashing-builtins.test.ts</code></a> checks against the RFC 1321 and FIPS 180-4 vectors and <code>node:crypto</code>. <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#
|
|
338
|
+
<div class="compat-evidence"><div class="compat-probe">CONFORMS, 2026-09-24: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-stdlib-string-bytes-hashing.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-stdlib-string-bytes-hashing</code></a> pins the md5, sha256, crc32, and crc32c digests of <code>abc</code> as hexadecimal, the base64url and hexadecimal encodings of <code>hi</code>, Bytes equality, and the production diagnostic <code>Unsupported operation error. Received: hashing.md5(int).</code> These are the representations the Firestore capture rules-firestore-bytes-toutf8-and-hashing pins for <code>cloud.firestore</code> (row #161), and the Storage evaluator computes them with the same shared Bytes wrapper and built-in hashing functions, whose MD5 and SHA-256 <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/simulator/hashing-builtins.test.ts#L24" target="_blank" rel="noopener noreferrer"><code>unit:rules/simulator/hashing-builtins.test.ts</code></a> checks against the RFC 1321 and FIPS 180-4 vectors and <code>node:crypto</code>. <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>unit:storage/rules-oracle-conformance.test.ts</code></a> replays the verdicts and <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/sandbox/rules-hashing-methods.test.ts#L8" target="_blank" rel="noopener noreferrer"><code>unit:storage/sandbox/rules-hashing-methods.test.ts</code></a> pins the digests, encodings, and errors. ROW EXTENDED, 2026-09-27: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-bytes-literal.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-bytes-literal</code></a> captures 22 production verdicts for the bytes literal in Storage rules, the same cases and verdicts as the bytes literal cases of the Firestore capture rules-firestore-bytes-toutf8-and-hashing. The prefix is <code>b</code> or <code>B</code> directly before a single- or double-quoted body. A plain character contributes its UTF-8 encoding. A backslash escapes a backslash, either quote, <code>n</code>, <code>r</code>, <code>t</code>, <code>b</code>, or <code>f</code>; <code>x</code> with exactly two hexadecimal digits of either case, or three octal digits up to 377, is one byte. A bytes literal equals only bytes, orders lexicographically, is <code>in</code> a list by value, and hashes like the string it encodes; <code>+</code> on two bytes values and <code>[]</code> on bytes are error values that <code>|| true</code> absorbs. Production rejects the Storage ruleset for the same malformed forms as the Firestore one, including a <code>u</code> escape ("Invalid bytes literal"), and the shared grammar rejects them. The grammar previously had no bytes literal, so a Storage ruleset that used one failed to parse. The Storage evaluator now converts the literal to the Bytes value <code>toUtf8()</code> produces; <code>unit:storage/rules-oracle-conformance.test.ts</code> replays all 22 verdicts and <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/sandbox/rules-bytes-literal.test.ts#L9" target="_blank" rel="noopener noreferrer"><code>unit:storage/sandbox/rules-bytes-literal.test.ts</code></a> pins them. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/grammar/bytes-literal.test.ts#L74" target="_blank" rel="noopener noreferrer"><code>bytes-literal.test.ts</code></a>)</div></div>
|
|
316
339
|
</details>
|
|
317
340
|
<details class="compat-row" data-status="ok">
|
|
318
341
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Rules</code><span class="compat-sub"><span class="compat-behavior"><code>List.toSet()</code> removes duplicates and Sets compare by value regardless of order. Set <code>size()</code>, <code>hasAll</code>, <code>hasAny</code>, and <code>hasOnly</code> take a List or Set argument, and List <code>hasAll</code>, <code>hasAny</code>, and <code>hasOnly</code> take a List argument, where a Set argument is an error. Set <code>difference</code>, <code>union</code>, and <code>intersection</code> take a Set argument, where a List argument is an error, and a List receiver has none of them, which is an error. <code>in</code> tests Set membership</span></span></span></summary>
|
|
319
|
-
<div class="compat-evidence"><div class="compat-probe">CONFORMS, 2026-09-24: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-stdlib-sets-and-mapdiff.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-stdlib-sets-and-mapdiff</code></a> captures 13 production verdicts for corpus scenario "stdlib-sets-and-mapdiff", including the diagnostics <code>Unsupported operation error. Received: set.difference(list).</code> and <code>Function not found error: Name: [difference].</code> for a List receiver. Sets are the shared Set value. <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#
|
|
342
|
+
<div class="compat-evidence"><div class="compat-probe">CONFORMS, 2026-09-24: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-stdlib-sets-and-mapdiff.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-stdlib-sets-and-mapdiff</code></a> captures 13 production verdicts for corpus scenario "stdlib-sets-and-mapdiff", including the diagnostics <code>Unsupported operation error. Received: set.difference(list).</code> and <code>Function not found error: Name: [difference].</code> for a List receiver. Sets are the shared Set value. <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>unit:storage/rules-oracle-conformance.test.ts</code></a> replays all 13 verdicts and <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/sandbox/rules-collection-methods.test.ts#L8" target="_blank" rel="noopener noreferrer"><code>unit:storage/sandbox/rules-collection-methods.test.ts</code></a> pins membership, algebra, equality, <code>in</code>, and the receiver and argument errors. RECAPTURED, 2026-09-28: 27 Set cases, with the same verdicts as the Firestore <code>set-algebra-difference-union-intersection</code> capture, show a Set compares elements by numeric value at every depth: <code>[1].toSet() == [1.0].toSet()</code>, <code>[1, 1.0].toSet().size() == 1</code>, <code>1 in [1.0].toSet()</code>, <code>[1].toSet().hasAll([1.0])</code>, <code>[[1]].toSet() == [[1.0]].toSet()</code> and <code>[1] in [[1.0]].toSet()</code> allow, <code>difference()</code>, <code>intersection()</code> and <code>union()</code> treat an int and the equal float as one element, <code>0.0</code> and <code>-0.0</code> are one element, and two NaN are one element that is in its own Set. The evaluator kept two NaN as two elements; Sets now compare under <code>setElementsEqual</code> in <code>rules/simulator/value-equality.ts</code>, shared with the Firestore simulator, and the scenario's 46 verdicts all match (<a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/simulator/collection-identity.test.ts#L8" target="_blank" rel="noopener noreferrer"><code>unit:rules/simulator/collection-identity.test.ts</code></a>). RECAPTURED, 2026-09-30: 35 set method cases (81 in all) pin the same Set and MapDiff method surface as the Firestore capture: a Set has no <code>keys()</code>, <code>values()</code>, <code>get()</code> or <code>diff()</code>, a MapDiff no <code>keys()</code>, <code>values()</code>, <code>get()</code> or <code>size()</code>, <code>size()</code> takes no argument, the membership methods take one List or Set, the algebra methods one Set, and Set algebra on a List, string or Map is "Function not found error". The evaluator reported non-production text for an argument to a Set's <code>size()</code>, for <code>values()</code> on a Set or MapDiff, and for an algebra argument count; Set methods now dispatch through <code>setMethod</code> in <code>rules/simulator/firestore-set.ts</code>, shared with the Firestore simulator, and the evaluator matches all 81 verdicts (<code>unit:storage/rules-oracle-conformance.test.ts</code>, <code>unit:rules/simulator/collection-identity.test.ts</code>).</div></div>
|
|
320
343
|
</details>
|
|
321
344
|
<details class="compat-row" data-status="ok">
|
|
322
345
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Rules</code><span class="compat-sub"><span class="compat-behavior"><code>Map.diff(other)</code> over custom metadata returns a MapDiff whose <code>addedKeys()</code>, <code>removedKeys()</code>, <code>changedKeys()</code>, <code>unchangedKeys()</code>, and <code>affectedKeys()</code> are Sets of own keys, including keys named <code>constructor</code> and <code>toString</code>, so an <code>affectedKeys().hasOnly([...])</code> guard allows a change to the listed keys and denies any other</span></span></span></summary>
|
|
323
|
-
<div class="compat-evidence"><div class="compat-probe">CONFORMS, 2026-09-24: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-stdlib-sets-and-mapdiff.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-stdlib-sets-and-mapdiff</code></a> pins all five accessors on one metadata update, the label-only guard ALLOW and owner-change DENY, <code>in</code> over <code>changedKeys()</code>, and a diff that adds <code>constructor</code> and removes <code>toString</code>. The discovery capture stdlib-storage-p2-native-advanced recorded the same affected/added/removed guard verdicts earlier. The shared MapDiff reads own keys only. <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#
|
|
346
|
+
<div class="compat-evidence"><div class="compat-probe">CONFORMS, 2026-09-24: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-stdlib-sets-and-mapdiff.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-stdlib-sets-and-mapdiff</code></a> pins all five accessors on one metadata update, the label-only guard ALLOW and owner-change DENY, <code>in</code> over <code>changedKeys()</code>, and a diff that adds <code>constructor</code> and removes <code>toString</code>. The discovery capture stdlib-storage-p2-native-advanced recorded the same affected/added/removed guard verdicts earlier. The shared MapDiff reads own keys only. <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>unit:storage/rules-oracle-conformance.test.ts</code></a> replays the verdicts and <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/sandbox/rules-collection-methods.test.ts#L8" target="_blank" rel="noopener noreferrer"><code>unit:storage/sandbox/rules-collection-methods.test.ts</code></a> pins the accessors and own-key diffs. RECAPTURED, 2026-09-28: 6 <code>diff()</code> cases show a diff compares values by numeric value at every depth, as Firestore's does: an int and the equal float, an int List and the equal float List, <code>0.0</code> and <code>-0.0</code>, and two NaN are unchanged, and an int beside a different float is changed. The shared MapDiff reported two NaN as changed; it now compares under <code>setElementsEqual</code> in <code>rules/simulator/value-equality.ts</code> and matches all six (<code>unit:rules/simulator/collection-identity.test.ts</code>).</div></div>
|
|
347
|
+
</details>
|
|
348
|
+
<details class="compat-row" data-status="diverged">
|
|
349
|
+
<summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">Rules</code><span class="compat-sub"><span class="compat-behavior"><code>matches()</code> uses RE2: <code>\s</code> is <code>[\t\n\f\r ]</code>, <code>.</code> matches <code>\r</code>, <code>\pL</code>, <code>[[:alpha:]]</code> and <code>\Q..\E</code> are a class, a POSIX class and literal quoting, and <code>(?i)k</code> matches the Kelvin sign U+212A (ST-H1-3, sharing FS2-SIM-5a, FS2-SIM-5a2, FS2-SIM-5b, FS2-SIM-5c and FS2-SIM-5d)</span></span></span></summary>
|
|
350
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-h1-regex-re2-classes.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-h1-regex-re2-classes</code></a> (captured 2026-10-27, 16 cases) records production verdicts for POSIX classes, <code>\pL</code>, <code>\s</code> and <code>\S</code> over NBSP, U+2003 and vertical tab, <code>.</code> against <code>\r</code>, <code>\Q..\E</code> and <code>(?i)</code> over the Kelvin sign. The Storage evaluator calls the shared <code>re2FullMatch</code>, so it disagrees on 6 cases: 3 under FS2-SIM-5c, 2 under FS2-SIM-5d and 1 Storage-found Kelvin case, all filed as ST-H1-3. <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#L51" target="_blank" rel="noopener noreferrer"><code>unit:storage/rules-oracle-conformance.test.ts</code></a> pins both sides of each in <code>packages/conformance/storage-rules-exceptions/</code>. The other 10 cases agree, including 7 fixed in the shared matcher in #1295: the 4 <code>\s</code> and <code>\S</code> cases under FS2-SIM-5a, <code>.</code> against <code>\r</code> under FS2-SIM-5a2, and the 2 <code>\pL</code> cases under FS2-SIM-5b, replayed by <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/sandbox/rules-string-methods.test.ts#L108" target="_blank" rel="noopener noreferrer"><code>unit:storage/sandbox/rules-string-methods.test.ts</code></a>. Pending fix with the Firestore RE2 stream, since the matcher is shared.</div></div>
|
|
351
|
+
</details>
|
|
352
|
+
<details class="compat-row" data-status="diverged">
|
|
353
|
+
<summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">Rules</code><span class="compat-sub"><span class="compat-behavior"><code>resource</code> binds <code>md5Hash</code>, <code>crc32c</code>, <code>etag</code>, <code>contentDisposition</code>, <code>contentEncoding</code>, <code>contentLanguage</code> and <code>cacheControl</code> from the stored object (ST-H1-1)</span></span></span></summary>
|
|
354
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-h1-resource-unmodeled-fields.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-h1-resource-unmodeled-fields</code></a> (captured 2026-10-27, 9 cases) records production ALLOWING a rule that reads each of the seven fields' stored values. The evaluator does not model them, so it DENIES all seven; <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>unit:storage/rules-oracle-conformance.test.ts</code></a> pins both sides of each in <code>packages/conformance/storage-rules-exceptions/</code> under ST-H1-1. The other 2 cases agree. These constructs were labeled unprobeable; the capture shows they are probeable and diverged. Pending fix.</div></div>
|
|
355
|
+
</details>
|
|
356
|
+
<details class="compat-row" data-status="ok">
|
|
357
|
+
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Rules</code><span class="compat-sub"><span class="compat-behavior">Without <code>rules_version = '2'</code>, a recursive wildcard needs at least one segment (ST-H1-2)</span></span></span></summary>
|
|
358
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-h1-v1-recursive-wildcard.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-h1-v1-recursive-wildcard</code></a> (captured 2026-10-27, 4 cases) records production DENYING <code>v1 glob: zero segments for the glob deny</code> and ALLOWING the other 3. The evaluator agrees on all 4: under <code>rules_version = '1'</code>, the default, a recursive wildcard needs one or more segments (<code>recursiveWildcardMinimum</code> in <code>rules-path-match.ts</code>, #1330). <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>unit:storage/rules-oracle-conformance.test.ts</code></a> replays them with no pin, and <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/sandbox/rules-v1-recursive-wildcard.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>unit:storage/sandbox/rules-v1-recursive-wildcard.test.ts</code></a> covers versions 1 and 2.</div></div>
|
|
359
|
+
</details>
|
|
360
|
+
<details class="compat-row" data-status="diverged">
|
|
361
|
+
<summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">Rules</code><span class="compat-sub"><span class="compat-behavior">Integers are 64-bit: distinct values above 2^53 compare unequal, <code>int()</code> and <code>string()</code> keep every bit, and overflow is an evaluation error (ST-H1-4, sharing FS2-SIM-2 and FS2-SIM-3)</span></span></span></summary>
|
|
362
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-h1-int64-precision.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-h1-int64-precision</code></a> (captured 2026-10-27, 9 cases) records production DENYING <code>9007199254740993 == 9007199254740992</code> and <code>9223372036854775807 + 1 > 0</code>, and ALLOWING <code>9223372036854775807 > 9223372036854775806</code>, <code>9007199254740993 - 1 == 9007199254740992</code> and <code>string(9007199254740993) == '9007199254740993'</code>. The evaluator holds ints as JavaScript doubles, so it disagrees on 7 cases: 5 under FS2-SIM-2 and 2 under FS2-SIM-3, filed for Storage as ST-H1-4. <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>unit:storage/rules-oracle-conformance.test.ts</code></a> pins both sides of each in <code>packages/conformance/storage-rules-exceptions/</code>. The other 2 cases agree. Comparison operators are not in scope: the wrong answers come from the integer values. Pending fix.</div></div>
|
|
363
|
+
</details>
|
|
364
|
+
<details class="compat-row" data-status="diverged">
|
|
365
|
+
<summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">Rules</code><span class="compat-sub"><span class="compat-behavior"><code>in</code> and <code>is</code> chain and compare at parse: <code>size is int == true</code>, <code>(a in list) is bool</code>, <code>(a in list) in [true]</code> and <code>(1 is int) is bool</code> compile (ST-H1-5)</span></span></span></summary>
|
|
366
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-h1-in-is-precedence-deploy.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-h1-in-is-precedence-deploy</code></a> (captured 2026-10-27, 4 cases) records production compiling all four expressions and giving ALLOW, DENY, ALLOW and ALLOW. Pyric refuses the ruleset ("expected ';' after the allow statement"); <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>unit:storage/rules-oracle-conformance.test.ts</code></a> pins the refusal in <code>packages/conformance/storage-rules-exceptions/</code> under ST-H1-5. The grammar is shared with Firestore. Pending fix.</div></div>
|
|
367
|
+
</details>
|
|
368
|
+
<details class="compat-row" data-status="ok">
|
|
369
|
+
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Rules</code><span class="compat-sub"><span class="compat-behavior"><code>in</code> binds looser than <code>==</code> and <code>!=</code>: <code>'a' in ['a','b'] == true</code> reads as <code>'a' in (['a','b'] == true)</code> and denies, while the parenthesized form allows</span></span></span></summary>
|
|
370
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-h1-in-is-precedence.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-h1-in-is-precedence</code></a> (captured 2026-10-27, 6 cases) records production DENYING the four unparenthesized <code>in</code> comparisons and ALLOWING the parenthesized control and a nested ternary. The evaluator agrees on all 6, refuting the evaluation half of ST-H1-5; the documented precedence table is wrong for <code>in</code>. <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>unit:storage/rules-oracle-conformance.test.ts</code></a> replays them with no pin.</div></div>
|
|
371
|
+
</details>
|
|
372
|
+
<details class="compat-row" data-status="diverged">
|
|
373
|
+
<summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">Rules</code><span class="compat-sub"><span class="compat-behavior">A signed-in request always carries a <code>request.auth.token</code> map, even when the case names only a uid (ST-H1-7)</span></span></span></summary>
|
|
374
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-h1-auth-token-default.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-h1-auth-token-default</code></a> (captured 2026-10-27, 4 cases) records production ALLOWING <code>token is a map</code>, <code>token is empty</code> and <code>token.get default</code> when the case gives no token. The evaluator binds no token, so it DENIES all three; <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>unit:storage/rules-oracle-conformance.test.ts</code></a> pins both sides of each in <code>packages/conformance/storage-rules-exceptions/</code> under ST-H1-7. The explicit-token control agrees. Pending fix.</div></div>
|
|
375
|
+
</details>
|
|
376
|
+
<details class="compat-row" data-status="diverged">
|
|
377
|
+
<summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">Rules</code><span class="compat-sub"><span class="compat-behavior">An unknown method is an evaluation error value, so <code>'a'.foo() || true</code> and <code>!('a'.foo() == 1 && false)</code> allow (ST-H1-8)</span></span></span></summary>
|
|
378
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-h1-unknown-method-absorption.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-h1-unknown-method-absorption</code></a> (captured 2026-10-27, 4 cases) records production ALLOWING all four absorbed unknown-method cases. The evaluator throws an error <code>||</code> and <code>&&</code> cannot absorb, so it DENIES all four; <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>unit:storage/rules-oracle-conformance.test.ts</code></a> pins both sides of each in <code>packages/conformance/storage-rules-exceptions/</code> under ST-H1-8. Pending fix.</div></div>
|
|
379
|
+
</details>
|
|
380
|
+
<details class="compat-row" data-status="ok">
|
|
381
|
+
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Rules</code><span class="compat-sub"><span class="compat-behavior">A <code>/</code> inside an interpolated <code>$(v)</code> in a <code>firestore.get()</code> or <code>firestore.exists()</code> path stays inside one segment, so a request value cannot select a deeper document (ST-H1-10)</span></span></span></summary>
|
|
382
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-h1-lookup-interpolated-slash.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-h1-lookup-interpolated-slash</code></a> (captured 2026-10-27, 3 cases) records production DENYING <code>get</code> and <code>exists</code> when the interpolated value holds slashes, and ALLOWING the literal-path control. The evaluator agrees on all 3: an interpolated string is one segment, and a segment holding <code>/</code> names no document, so the call errors (<code>buildFirestoreDocPath</code> in <code>rules-firestore-lookup.ts</code>, #1330). <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>unit:storage/rules-oracle-conformance.test.ts</code></a> replays them with no pin, and <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/sandbox/rules-firestore-lookup-paths.test.ts#L8" target="_blank" rel="noopener noreferrer"><code>unit:storage/sandbox/rules-firestore-lookup-paths.test.ts</code></a> covers <code>get</code> and <code>exists</code>. The capture ran under the Rules Test API, where an unmocked call also errors, so it cannot show whether a real database answers <code>exists</code> on a slashed id with false; that the call errors, so <code>!exists(...)</code> also denies, is inferred and not captured.</div></div>
|
|
383
|
+
</details>
|
|
384
|
+
<details class="compat-row" data-status="ok">
|
|
385
|
+
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Rules</code><span class="compat-sub"><span class="compat-behavior">Strings order by UTF-16 code unit, so a character above the BMP sorts before U+FFFF and U+FF5A</span></span></span></summary>
|
|
386
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-h1-string-ordering.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-h1-string-ordering</code></a> (captured 2026-10-27, 5 cases) records production DENYING the three code-point-order comparisons and ALLOWING the ASCII and map-keys controls. The evaluator agrees on all 5, refuting ST-H1-9. <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>unit:storage/rules-oracle-conformance.test.ts</code></a> replays them with no pin.</div></div>
|
|
387
|
+
</details>
|
|
388
|
+
<details class="compat-row" data-status="diverged">
|
|
389
|
+
<summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">Rules</code><span class="compat-sub"><span class="compat-behavior"><code>service cloud.storage</code>, the spelling in the documented v2 snippet, is accepted beside <code>service firebase.storage</code> (ST-H1-14)</span></span></span></summary>
|
|
390
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-h1-deploy-cloud-storage-service.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-h1-deploy-cloud-storage-service</code></a> (captured 2026-10-27, 1 case) records production compiling a <code>service cloud.storage</code> ruleset and ALLOWING its read. Pyric refuses it ("Expected 'service firebase.storage', got 'service cloud.storage'"); <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#L22" target="_blank" rel="noopener noreferrer"><code>unit:storage/rules-oracle-conformance.test.ts</code></a> pins the refusal in <code>packages/conformance/storage-rules-exceptions/</code> under ST-H1-14. Pending fix.</div></div>
|
|
391
|
+
</details>
|
|
392
|
+
<details class="compat-row" data-status="ok">
|
|
393
|
+
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Rules</code><span class="compat-sub"><span class="compat-behavior"><code>$(rest)</code> from a <code>{rest=**}</code> capture interpolates its segments into a <code>firestore.exists()</code> path (ST-H2-9)</span></span></span></summary>
|
|
394
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-storage-lookup-path-interpolation.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-storage-lookup-path-interpolation</code></a> (captured 2026-10-27, 4 cases) records production ALLOWING a single-segment and a three-segment path capture interpolated into the lookup path, and DENYING the two <code>string()</code> forms. The evaluator agrees on all 4: a <code>path</code> value splices in as its segments, while <code>string(rest)</code> is one segment holding <code>/</code> and errors (<code>buildFirestoreDocPath</code> in <code>rules-firestore-lookup.ts</code>, #1330). <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>unit:storage/rules-oracle-conformance.test.ts</code></a> replays them with no pin, and <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/sandbox/rules-firestore-lookup-paths.test.ts#L9" target="_blank" rel="noopener noreferrer"><code>unit:storage/sandbox/rules-firestore-lookup-paths.test.ts</code></a> covers captures and <code>path()</code>.</div></div>
|
|
395
|
+
</details>
|
|
396
|
+
<details class="compat-row" data-status="ok">
|
|
397
|
+
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Rules</code><span class="compat-sub"><span class="compat-behavior">The bucket wildcard, <code>request.resource.bucket</code> and <code>resource.bucket</code> carry the real bucket name, so a ruleset that names its bucket is honored</span></span></span></summary>
|
|
398
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-storage-named-bucket-binding.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-storage-named-bucket-binding</code></a> (captured 2026-10-27, 4 cases) records production binding the real bucket name in all three places. The evaluator, given the case's bucket, agrees on all 4; <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>unit:storage/rules-oracle-conformance.test.ts</code></a> replays them with no pin. The live planes bind the same name: the in-page plane from the app's <code>storageBucket</code> or <code>getStorage</code>'s bucket URL, the SharedWorker and Node host from the name the caller knows the bucket by, and simulate, the <code>storage</code> methods and <code>pyric verify</code> from the served app's <code>storageBucket</code> or the name the live op recorded (<code>unit:storage/sandbox/rules-bucket-name.test.ts</code>, <code>test/apps/storage-rules-bucket-name</code>).</div></div>
|
|
399
|
+
</details>
|
|
400
|
+
<details class="compat-row" data-status="diverged">
|
|
401
|
+
<summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">Rules</code><span class="compat-sub"><span class="compat-behavior">One <code>service firebase.storage</code> may hold more than one top-level <code>match</code> block, one per bucket (ST-H2-8)</span></span></span></summary>
|
|
402
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-storage-two-top-level-bucket-matches.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-storage-two-top-level-bucket-matches</code></a> (captured 2026-10-27, 2 cases) records production compiling two top-level match blocks, ALLOWING <code>get</code> in the named bucket's block and DENYING the other bucket under the wildcard block. Pyric refuses the ruleset (<code>expected "}" or "function"</code>); <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#L22" target="_blank" rel="noopener noreferrer"><code>unit:storage/rules-oracle-conformance.test.ts</code></a> pins the refusal in <code>packages/conformance/storage-rules-exceptions/</code> under ST-H2-8. The grammar is shared with Firestore, where the Firestore hunt saw the same refusal. Pending fix.</div></div>
|
|
403
|
+
</details>
|
|
404
|
+
<details class="compat-row" data-status="ok">
|
|
405
|
+
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Rules</code><span class="compat-sub"><span class="compat-behavior">A root match that names its bucket literally grants only that bucket; a request in another bucket, including a bucket name that extends it, matches no rule and denies</span></span></span></summary>
|
|
406
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-xref-root-literal-bucket.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-xref-root-literal-bucket</code></a> (captured 2026-10-27, 5 cases) records production ALLOWING <code>get</code> and <code>create</code> in the named bucket and DENYING them in another bucket and in <code>demo-pyric.appspot.com2</code>. The evaluator agrees on all 5, so the Firestore root-path fail-open (FS2-ENF-1) does not reach Storage. <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#L51" target="_blank" rel="noopener noreferrer"><code>unit:storage/rules-oracle-conformance.test.ts</code></a> replays them with no pin.</div></div>
|
|
407
|
+
</details>
|
|
408
|
+
<details class="compat-row" data-status="diverged">
|
|
409
|
+
<summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">Rules</code><span class="compat-sub"><span class="compat-behavior">The language semantics the Firestore hunt found hold under <code>firebase.storage</code> too: <code>split()</code> drops capture-group text and trailing empty pieces, <code>trim()</code> keeps NBSP, ints are 64-bit with overflow errors and <code>MIN / -1 == MIN</code>, a timestamp past its range is an error, and unary minus negates a duration (ST-H1-13, sharing FS2-SIM-1, FS2-SIM-2, FS2-SIM-3, FS2-SIM-4, FS2-SIM-7, FS2-SIM-8 and FS2-SIM-9)</span></span></span></summary>
|
|
410
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-xref-shared-language-semantics.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-xref-shared-language-semantics</code></a> (captured 2026-10-27, 12 cases) records production giving the Firestore-reported answer on all 12. Storage has its own copies of integer operators and unary minus, and shares <code>Timestamp.binaryOp</code>, so the evaluator disagrees on 4 cases: one each under FS2-SIM-1 and FS2-SIM-2, and two under FS2-SIM-3. <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>unit:storage/rules-oracle-conformance.test.ts</code></a> pins both sides of each in <code>packages/conformance/storage-rules-exceptions/</code>. The other 8 cases agree, including <code>timestamp-plus-duration-past-range-errors</code> under FS2-SIM-9, since the shared <code>Timestamp</code> wrapper errors past its range (#1336, replayed by <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/simulator/time-range.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>unit:rules/simulator/time-range.test.ts</code></a>), <code>(?i)</code> in <code>split()</code> and <code>replace()</code>, <code>duration.value()</code> past its range, and <code>trim-keeps-nbsp</code> under FS2-SIM-8: Storage's <code>trim()</code> now calls the shared <code>trimRulesString</code> (#1295), replayed by <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/sandbox/rules-string-methods.test.ts#L91" target="_blank" rel="noopener noreferrer"><code>unit:storage/sandbox/rules-string-methods.test.ts</code></a>. Storage's <code>split()</code> and <code>replace()</code> now call the shared <code>splitRe2</code> and <code>replaceRe2</code> (#1333), so <code>split-drops-capture-text</code> (FS2-SIM-4) and <code>split-drops-trailing-empty</code> (FS2-SIM-7) agree, and a pattern from request data gets pyric's size limit and the 1000-repeat cap; <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/simulator/split-and-replace.test.ts#L10" target="_blank" rel="noopener noreferrer"><code>unit:rules/simulator/split-and-replace.test.ts</code></a> checks both engines. <code>trim()</code> and <code>split()</code> are no longer in this row's diverged scope. Pending fix.</div></div>
|
|
324
411
|
</details>
|
|
325
412
|
</div>
|
|
326
413
|
<h2 id="rtdb-rules-engine--production-simulator-conformance-rules-rtdb-corpus">RTDB rules engine — production simulator conformance (rules-rtdb corpus)</h2>
|
|
@@ -419,7 +506,7 @@ html{scrollbar-gutter:stable}
|
|
|
419
506
|
</details>
|
|
420
507
|
<details class="compat-row" data-status="ok">
|
|
421
508
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Rules simulator</code><span class="compat-sub"><span class="compat-behavior">Pyric's load-time check refuses the rulesets production's rules endpoint refuses for an unknown variable, <code>newData</code> in a <code>.read</code>, an unknown snapshot or value method, and an assignment, and loads the rulesets production accepts: an operand typed only at evaluation, an unknown property of <code>auth</code>, and a ruleset over the documented 256 KB limit. Rules file text reads as the deploy endpoint reads it: comments, a rule string that spans lines (LF or CRLF), a raw tab inside a rule string, and a trailing comma before <code>}</code> or <code>]</code> are accepted, and a backslash before a line break, a doubled comma and a leading comma are refused</span></span></span></summary>
|
|
422
|
-
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-rules/rules-rtdb-r29-deploy-rejections.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-rtdb-r29-deploy-rejections</code></a> sends 33 rulesets to production's rules endpoint as dry runs (<code>PUT /.settings/rules.json?dryRun=true</code>, the validation <code>firebase deploy</code> runs before it deploys) and records each verdict and error text. Production rejects <code>foo == 1</code> ("Unknown variable 'foo'."), <code>newData.exists()</code> in <code>.read</code> ("newData is invalid in .read expressions."), <code>data.foo()</code> and <code>newData.val().bar()</code> ("No such method/property"), and <code>auth.uid = 'x'</code> ("Rule expressions may not contain assignments."); Pyric's load-time check rejects the same five rulesets. Production accepts <code>auth.uid + 1 > 'x'</code>, <code>auth.foo == 1</code>, and a ruleset of about 300 KB; Pyric loads all three. <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb/rtdb-rules-text-dialect.json" target="_blank" rel="noopener noreferrer"><code>oracle:rtdb-rules-text-dialect</code></a> (captured 2026-10-07) sends 12 rules file texts byte for byte as dry runs and records production accepting comments, a rule string spanning lines with LF and with CRLF, a raw tab inside a rule string, and a trailing comma in a rule object, in every object, in an <code>.indexOn</code> array and before a comment, and refusing a backslash before a line break inside a rule string ("Invalid escape"), a doubled comma and a leading comma. <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/oracle-conformance.test.ts#
|
|
509
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-rules/rules-rtdb-r29-deploy-rejections.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-rtdb-r29-deploy-rejections</code></a> sends 33 rulesets to production's rules endpoint as dry runs (<code>PUT /.settings/rules.json?dryRun=true</code>, the validation <code>firebase deploy</code> runs before it deploys) and records each verdict and error text. Production rejects <code>foo == 1</code> ("Unknown variable 'foo'."), <code>newData.exists()</code> in <code>.read</code> ("newData is invalid in .read expressions."), <code>data.foo()</code> and <code>newData.val().bar()</code> ("No such method/property"), and <code>auth.uid = 'x'</code> ("Rule expressions may not contain assignments."); Pyric's load-time check rejects the same five rulesets. Production accepts <code>auth.uid + 1 > 'x'</code>, <code>auth.foo == 1</code>, and a ruleset of about 300 KB; Pyric loads all three. <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb/rtdb-rules-text-dialect.json" target="_blank" rel="noopener noreferrer"><code>oracle:rtdb-rules-text-dialect</code></a> (captured 2026-10-07) sends 12 rules file texts byte for byte as dry runs and records production accepting comments, a rule string spanning lines with LF and with CRLF, a raw tab inside a rule string, and a trailing comma in a rule object, in every object, in an <code>.indexOn</code> array and before a comment, and refusing a backslash before a line break inside a rule string ("Invalid escape"), a doubled comma and a leading comma. <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/database/oracle-conformance.test.ts#L474" target="_blank" rel="noopener noreferrer"><code>unit:database/oracle-conformance.test.ts</code></a> reads each text through <code>toStrictRulesJson</code>, the reader every RTDB rules load path uses, and the load-time check, and agrees with every verdict. <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/src/rules-rtdb-replay.test.ts#L89" target="_blank" rel="noopener noreferrer"><code>packages/conformance/src/rules-rtdb-replay.test.ts</code></a> replays deploy cases through the local check. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/rtdb/rules-conformance.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>rules-conformance.test.ts</code></a>)</div></div>
|
|
423
510
|
</details>
|
|
424
511
|
<details class="compat-row" data-status="ok">
|
|
425
512
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Rules simulator</code><span class="compat-sub"><span class="compat-behavior">A <code>.read</code> rule reads the request's query through <code>query</code>: <code>orderByChild</code>, <code>orderByValue</code>, <code>equalTo</code>, <code>startAt</code>, <code>endAt</code>, <code>limitToFirst</code> and <code>limitToLast</code> carry the query's values, <code>orderByChild</code> reads null for a read without a query, and a limit the query does not set reads null, so a bound on it fails the rule</span></span></span></summary>
|
|
@@ -455,7 +542,7 @@ html{scrollbar-gutter:stable}
|
|
|
455
542
|
</details>
|
|
456
543
|
<details class="compat-row" data-status="ok">
|
|
457
544
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Rules simulator</code><span class="compat-sub"><span class="compat-behavior">Custom-token sign-in populates standard ID-token fields on <code>auth.token</code> (<code>sub</code>, <code>iat</code>, <code>exp</code>, <code>iss</code>, <code>aud</code>, <code>auth_time</code>, <code>user_id</code>, <code>firebase.identities</code>, <code>firebase.sign_in_provider</code>) as production does (COV-3)</span></span></span></summary>
|
|
458
|
-
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-rules/rules-rtdb-r42-custom-token-standard-claims.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-rtdb-r42-custom-token-standard-claims</code></a> (captured 2026-10-15) records 12 production verdicts after custom-token sign-in (<code>claims: { role: 'member' }</code>): production ALLOWS all 12 checks (<code>sub != null</code>, <code>sub == auth.uid</code>, <code>iat != null</code>, <code>exp != null</code>, <code>iss != null</code>, <code>aud != null</code>, <code>auth_time != null</code>, <code>user_id != null</code>, <code>user_id == auth.uid</code>, <code>firebase.identities != null</code>, <code>firebase.sign_in_provider != null</code>, and <code>firebase.sign_in_provider == 'custom'</code>). <code>
|
|
545
|
+
<div class="compat-evidence"><div class="compat-probe"><a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/rtdb-rules/rules-rtdb-r42-custom-token-standard-claims.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-rtdb-r42-custom-token-standard-claims</code></a> (captured 2026-10-15) records 12 production verdicts after custom-token sign-in (<code>claims: { role: 'member' }</code>): production ALLOWS all 12 checks (<code>sub != null</code>, <code>sub == auth.uid</code>, <code>iat != null</code>, <code>exp != null</code>, <code>iss != null</code>, <code>aud != null</code>, <code>auth_time != null</code>, <code>user_id != null</code>, <code>user_id == auth.uid</code>, <code>firebase.identities != null</code>, <code>firebase.sign_in_provider != null</code>, and <code>firebase.sign_in_provider == 'custom'</code>). A signed-in sandbox session's <code>auth.token</code> is the minted ID token's full claim set (<code>rulesTokenFromIdToken</code> in <code>sandbox/internal/auth-token-claims.ts</code>, #1341), so <code>sub</code>, <code>iat</code>, <code>auth_time</code>, <code>exp</code>, <code>iss</code> and <code>aud</code> carry the minted values and <code>auth_time</code> stays at the sign-in. For a <code>simulate()</code> case that omits them, <code>withIdTokenClaims</code> fills the standard ID-token fields (<code>sub</code>, <code>user_id</code>, <code>iat</code>, <code>auth_time</code>, <code>exp</code>, <code>iss</code>, <code>aud</code>, <code>firebase.identities</code>). Both match all 12 verdicts. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/rtdb/rules-conformance.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>rules-conformance.test.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/rtdb/simulation/handler.test.ts#L1024" target="_blank" rel="noopener noreferrer"><code>handler.test.ts</code></a>, <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/auth/rules-token-claims.test.ts#L8" target="_blank" rel="noopener noreferrer"><code>rules-token-claims.test.ts</code></a>)</div></div>
|
|
459
546
|
</details>
|
|
460
547
|
<details class="compat-row" data-status="ok">
|
|
461
548
|
<summary class="compat-line"><span class="compat-dot" data-status="ok" role="img" aria-label="Conforming" title="Conforming"></span><span class="compat-main"><code class="compat-api">Rules simulator</code><span class="compat-sub"><span class="compat-behavior">Pyric's load-time check (<code>rtdbRulesSourceRejection</code>) and <code>checkRtdbRules</code> (<code>localDeployVerdict</code>) match production dry-run deploy validation for structural non-object children and unknown dotted keys (<code>CON-6</code>), <code>auth[...]</code> indexing (<code>FUZ-4</code>), double-quoted and single-quoted string escape sequences and raw newlines (<code>FUZ-1</code>), and regular expression literals in <code>matches()</code> (<code>FUZ-2</code>, <code>FUZ-8</code>), alongside <code>1e309</code> (<code>COV-2</code>) and <code>root.parent()</code> (<code>COV-1</code>)</span></span></span></summary>
|
|
@@ -538,14 +625,28 @@ html{scrollbar-gutter:stable}
|
|
|
538
625
|
<h3 id="documented-divergences">Documented divergences</h3>
|
|
539
626
|
<p>Known differences between Pyric and production Firebase. Each remains tracked as a non-conforming row.</p>
|
|
540
627
|
<div class="compat-list">
|
|
628
|
+
<details class="compat-row" data-status="diverged"><summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">Rules simulator</code><span class="compat-sub"><span class="compat-behavior">Integers are 64-bit: values above 2^53 keep every bit, overflow is an evaluation error except <code>MIN / -1</code>, which is <code>MIN</code>, unary minus negates a duration, and a timestamp or duration result past its range is an error (FS2-SIM-1, FS2-SIM-2, FS2-SIM-3, FS2-SIM-9)</span></span></span></summary>
|
|
629
|
+
<div class="compat-evidence"><div class="compat-note"><a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore-rules/rules-firestore-int64-and-duration-range.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-firestore-int64-and-duration-range</code></a> (captured 2026-10-27, 22 cases) records production ALLOWING <code>neg duration equals duration.value(-1, 's')</code>, <code>int64 max is greater than max minus one</code>, <code>2^53 plus one differs from 2^53</code>, <code>string() of 2^53 plus one</code> and <code>min divided by minus one is min</code>, and DENYING <code>max plus one is positive</code>, <code>max timestamp plus 1 ms is later</code> and <code>duration past the protobuf bound is positive</code>. The simulator holds ints as JavaScript doubles and errors on a negated duration, so it disagrees on 12 cases: 3 under FS2-SIM-1, 7 under FS2-SIM-2 and 2 under FS2-SIM-3. <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/oracle-conformance.test.ts#L24" target="_blank" rel="noopener noreferrer"><code>unit:rules/oracle-conformance.test.ts</code></a> pins both sides of each in <code>packages/conformance/firestore-rules-exceptions/</code>. The other 10 cases agree, including the 3 under FS2-SIM-9: a timestamp or duration past its range is production's <code>Timestamp is not valid</code> or <code>Duration is not valid</code> error (#1336), replayed by <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/simulator/time-range.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>unit:rules/simulator/time-range.test.ts</code></a>. Timestamp and duration range is no longer in this row's diverged scope. Comparison operators are not in scope: they compare correctly, and the wrong answers come from the integer values they are handed. Pending fix in the 2026-10-27 Firestore hunt's stream S4.</div></div></details>
|
|
630
|
+
<details class="compat-row" data-status="diverged"><summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">Rules simulator</code><span class="compat-sub"><span class="compat-behavior"><code>matches()</code>, <code>split()</code> and <code>replace()</code> use RE2: <code>\s</code> is <code>[\t\n\f\r ]</code>, <code>.</code> matches <code>\r</code>, <code>\pL</code>, <code>[[:alpha:]]</code> and <code>\Q..\E</code> are a class, a POSIX class and literal quoting, <code>split()</code> does not return capture-group text, and <code>(?i)</code> applies in <code>split()</code> and <code>replace()</code> (FS2-SIM-4, FS2-SIM-5a, FS2-SIM-5a2, FS2-SIM-5b, FS2-SIM-5c, FS2-SIM-5d, FS2-SIM-6)</span></span></span></summary>
|
|
631
|
+
<div class="compat-evidence"><div class="compat-note"><a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/firestore-rules/rules-firestore-re2-pattern-semantics.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-firestore-re2-pattern-semantics</code></a> (captured 2026-10-27, recaptured 2026-10-28 with 6 more cases, 34 in all, every earlier verdict unchanged) records production ALLOWING <code>NBSP is not \s</code>, <code>em space is not \s</code>, <code>vertical tab is not \s</code>, <code>real nbsp from data is not \s</code>, <code>dot matches carriage return</code>, <code>a is \pL</code>, <code>a is [[:alpha:]]</code>, <code>quoted a.b matches a.b</code>, <code>split on a capturing group returns two pieces</code>, <code>split with (?i) splits both cases</code>, <code>replace \s leaves NBSP</code>, <code>split dot matches carriage return</code>, <code>replace dot matches carriage return</code>, <code>replace (?s) dot matches newline</code>, <code>split (?i)k splits on the Kelvin sign</code> and <code>replace (?i)k replaces the Kelvin sign</code>. The simulator disagrees on 5 cases: 2 under FS2-SIM-5c, 1 under FS2-SIM-5d and 2 under ST-H1-3 (<code>split()</code> and <code>replace()</code> compile without JavaScript's <code>u</code> flag, so <code>(?i)</code> does not fold U+212A; the <code>matches()</code> half is #1332). <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/oracle-conformance.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>unit:rules/oracle-conformance.test.ts</code></a> pins both sides of each in <code>packages/conformance/firestore-rules-exceptions/</code>. The other 29 cases agree. Fixed in #1295: the 5 FS2-SIM-5a cases (the matcher spells <code>\s</code> and <code>\S</code> out as <code>[\t\n\f\r ]</code> and its complement), FS2-SIM-5a2 in <code>matches()</code> (<code>.</code> matches every character but <code>\n</code>), the 2 FS2-SIM-5b cases (<code>\pL</code> reads as <code>\p{L}</code>), and the 3 FS2-SIM-6 cases (<code>split()</code> and <code>replace()</code> take the pattern's inline flags and RE2's <code>\s</code>, through the <code>scanRe2Pattern</code> Storage already used). <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/simulator/re2-space-and-trim.test.ts#L9" target="_blank" rel="noopener noreferrer"><code>unit:rules/simulator/re2-space-and-trim.test.ts</code></a> and <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/simulator/re2-dot-property-and-flags.test.ts#L9" target="_blank" rel="noopener noreferrer"><code>unit:rules/simulator/re2-dot-property-and-flags.test.ts</code></a> replay those cases from this observation. Fixed in #1333: the 3 FS2-SIM-4 cases (the shared <code>splitRe2</code> never returns capture-group text) and FS2-SIM-5a2 in <code>split()</code> and <code>replace()</code> (<code>compileRe2Regex</code> spells <code>.</code> as <code>[^\n]</code> unless <code>(?s)</code> is on); <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/simulator/split-and-replace.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>unit:rules/simulator/split-and-replace.test.ts</code></a> replays them from this observation on Firestore and Storage. <code>split()</code> and <code>replace()</code> are in this row's diverged scope only for the Kelvin sign. Pending fix in the 2026-10-27 Firestore hunt's stream S3.</div></div></details>
|
|
541
632
|
<details class="compat-row" data-status="diverged"><summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">Rules</code><span class="compat-sub"><span class="compat-behavior">Mocked Storage-to-Firestore lookup evaluation: get/exists return and missing-field errors, repeated and same-resolved-path calls, get+exists composition, and lazy <code>||</code>/<code>&&</code>/ternary branches with hosted function-call diagnostics</span></span></span></summary>
|
|
542
|
-
<div class="compat-evidence"><div class="compat-note">DOCUMENTED MOCK-ORACLE DIVERGENCE, 2026-07-21: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-firestore-lookup-budget.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-firestore-lookup-budget</code></a> matches the local evaluator on 13 of 14 cases and hosted diagnostics prove lazy branches omit lookup calls. The one mismatch is intentional and pinned on both sides: the Rules Test API ALLOWs three distinct mocked exists calls, while the evaluator DENYs the third distinct path to match the separately captured real-resource production contract in row #131. This row remains diverged because its mocked observation is not verdict-identical; it supplies no budget credit. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#
|
|
543
|
-
</
|
|
544
|
-
<
|
|
545
|
-
<
|
|
546
|
-
<div class="compat-
|
|
547
|
-
<details class="compat-row" data-status="
|
|
548
|
-
<div class="compat-evidence"><div class="compat-note">
|
|
549
|
-
<details class="compat-row" data-status="
|
|
550
|
-
<div class="compat-evidence"><div class="compat-note"
|
|
551
|
-
|
|
633
|
+
<div class="compat-evidence"><div class="compat-note">DOCUMENTED MOCK-ORACLE DIVERGENCE, 2026-07-21: <a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-firestore-lookup-budget.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-firestore-lookup-budget</code></a> matches the local evaluator on 13 of 14 cases and hosted diagnostics prove lazy branches omit lookup calls. The one mismatch is intentional and pinned on both sides: the Rules Test API ALLOWs three distinct mocked exists calls, while the evaluator DENYs the third distinct path to match the separately captured real-resource production contract in row #131. This row remains diverged because its mocked observation is not verdict-identical; it supplies no budget credit. (Structured evidence: <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>rules-oracle-conformance.test.ts</code></a>)</div></div></details>
|
|
634
|
+
<details class="compat-row" data-status="diverged"><summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">Rules</code><span class="compat-sub"><span class="compat-behavior"><code>matches()</code> uses RE2: <code>\s</code> is <code>[\t\n\f\r ]</code>, <code>.</code> matches <code>\r</code>, <code>\pL</code>, <code>[[:alpha:]]</code> and <code>\Q..\E</code> are a class, a POSIX class and literal quoting, and <code>(?i)k</code> matches the Kelvin sign U+212A (ST-H1-3, sharing FS2-SIM-5a, FS2-SIM-5a2, FS2-SIM-5b, FS2-SIM-5c and FS2-SIM-5d)</span></span></span></summary>
|
|
635
|
+
<div class="compat-evidence"><div class="compat-note"><a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-h1-regex-re2-classes.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-h1-regex-re2-classes</code></a> (captured 2026-10-27, 16 cases) records production verdicts for POSIX classes, <code>\pL</code>, <code>\s</code> and <code>\S</code> over NBSP, U+2003 and vertical tab, <code>.</code> against <code>\r</code>, <code>\Q..\E</code> and <code>(?i)</code> over the Kelvin sign. The Storage evaluator calls the shared <code>re2FullMatch</code>, so it disagrees on 6 cases: 3 under FS2-SIM-5c, 2 under FS2-SIM-5d and 1 Storage-found Kelvin case, all filed as ST-H1-3. <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#L51" target="_blank" rel="noopener noreferrer"><code>unit:storage/rules-oracle-conformance.test.ts</code></a> pins both sides of each in <code>packages/conformance/storage-rules-exceptions/</code>. The other 10 cases agree, including 7 fixed in the shared matcher in #1295: the 4 <code>\s</code> and <code>\S</code> cases under FS2-SIM-5a, <code>.</code> against <code>\r</code> under FS2-SIM-5a2, and the 2 <code>\pL</code> cases under FS2-SIM-5b, replayed by <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/sandbox/rules-string-methods.test.ts#L108" target="_blank" rel="noopener noreferrer"><code>unit:storage/sandbox/rules-string-methods.test.ts</code></a>. Pending fix with the Firestore RE2 stream, since the matcher is shared.</div></div></details>
|
|
636
|
+
<details class="compat-row" data-status="diverged"><summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">Rules</code><span class="compat-sub"><span class="compat-behavior"><code>resource</code> binds <code>md5Hash</code>, <code>crc32c</code>, <code>etag</code>, <code>contentDisposition</code>, <code>contentEncoding</code>, <code>contentLanguage</code> and <code>cacheControl</code> from the stored object (ST-H1-1)</span></span></span></summary>
|
|
637
|
+
<div class="compat-evidence"><div class="compat-note"><a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-h1-resource-unmodeled-fields.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-h1-resource-unmodeled-fields</code></a> (captured 2026-10-27, 9 cases) records production ALLOWING a rule that reads each of the seven fields' stored values. The evaluator does not model them, so it DENIES all seven; <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>unit:storage/rules-oracle-conformance.test.ts</code></a> pins both sides of each in <code>packages/conformance/storage-rules-exceptions/</code> under ST-H1-1. The other 2 cases agree. These constructs were labeled unprobeable; the capture shows they are probeable and diverged. Pending fix.</div></div></details>
|
|
638
|
+
<details class="compat-row" data-status="diverged"><summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">Rules</code><span class="compat-sub"><span class="compat-behavior">Integers are 64-bit: distinct values above 2^53 compare unequal, <code>int()</code> and <code>string()</code> keep every bit, and overflow is an evaluation error (ST-H1-4, sharing FS2-SIM-2 and FS2-SIM-3)</span></span></span></summary>
|
|
639
|
+
<div class="compat-evidence"><div class="compat-note"><a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-h1-int64-precision.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-h1-int64-precision</code></a> (captured 2026-10-27, 9 cases) records production DENYING <code>9007199254740993 == 9007199254740992</code> and <code>9223372036854775807 + 1 > 0</code>, and ALLOWING <code>9223372036854775807 > 9223372036854775806</code>, <code>9007199254740993 - 1 == 9007199254740992</code> and <code>string(9007199254740993) == '9007199254740993'</code>. The evaluator holds ints as JavaScript doubles, so it disagrees on 7 cases: 5 under FS2-SIM-2 and 2 under FS2-SIM-3, filed for Storage as ST-H1-4. <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>unit:storage/rules-oracle-conformance.test.ts</code></a> pins both sides of each in <code>packages/conformance/storage-rules-exceptions/</code>. The other 2 cases agree. Comparison operators are not in scope: the wrong answers come from the integer values. Pending fix.</div></div></details>
|
|
640
|
+
<details class="compat-row" data-status="diverged"><summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">Rules</code><span class="compat-sub"><span class="compat-behavior"><code>in</code> and <code>is</code> chain and compare at parse: <code>size is int == true</code>, <code>(a in list) is bool</code>, <code>(a in list) in [true]</code> and <code>(1 is int) is bool</code> compile (ST-H1-5)</span></span></span></summary>
|
|
641
|
+
<div class="compat-evidence"><div class="compat-note"><a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-h1-in-is-precedence-deploy.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-h1-in-is-precedence-deploy</code></a> (captured 2026-10-27, 4 cases) records production compiling all four expressions and giving ALLOW, DENY, ALLOW and ALLOW. Pyric refuses the ruleset ("expected ';' after the allow statement"); <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>unit:storage/rules-oracle-conformance.test.ts</code></a> pins the refusal in <code>packages/conformance/storage-rules-exceptions/</code> under ST-H1-5. The grammar is shared with Firestore. Pending fix.</div></div></details>
|
|
642
|
+
<details class="compat-row" data-status="diverged"><summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">Rules</code><span class="compat-sub"><span class="compat-behavior">A signed-in request always carries a <code>request.auth.token</code> map, even when the case names only a uid (ST-H1-7)</span></span></span></summary>
|
|
643
|
+
<div class="compat-evidence"><div class="compat-note"><a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-h1-auth-token-default.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-h1-auth-token-default</code></a> (captured 2026-10-27, 4 cases) records production ALLOWING <code>token is a map</code>, <code>token is empty</code> and <code>token.get default</code> when the case gives no token. The evaluator binds no token, so it DENIES all three; <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>unit:storage/rules-oracle-conformance.test.ts</code></a> pins both sides of each in <code>packages/conformance/storage-rules-exceptions/</code> under ST-H1-7. The explicit-token control agrees. Pending fix.</div></div></details>
|
|
644
|
+
<details class="compat-row" data-status="diverged"><summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">Rules</code><span class="compat-sub"><span class="compat-behavior">An unknown method is an evaluation error value, so <code>'a'.foo() || true</code> and <code>!('a'.foo() == 1 && false)</code> allow (ST-H1-8)</span></span></span></summary>
|
|
645
|
+
<div class="compat-evidence"><div class="compat-note"><a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-h1-unknown-method-absorption.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-h1-unknown-method-absorption</code></a> (captured 2026-10-27, 4 cases) records production ALLOWING all four absorbed unknown-method cases. The evaluator throws an error <code>||</code> and <code>&&</code> cannot absorb, so it DENIES all four; <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>unit:storage/rules-oracle-conformance.test.ts</code></a> pins both sides of each in <code>packages/conformance/storage-rules-exceptions/</code> under ST-H1-8. Pending fix.</div></div></details>
|
|
646
|
+
<details class="compat-row" data-status="diverged"><summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">Rules</code><span class="compat-sub"><span class="compat-behavior"><code>service cloud.storage</code>, the spelling in the documented v2 snippet, is accepted beside <code>service firebase.storage</code> (ST-H1-14)</span></span></span></summary>
|
|
647
|
+
<div class="compat-evidence"><div class="compat-note"><a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-h1-deploy-cloud-storage-service.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-h1-deploy-cloud-storage-service</code></a> (captured 2026-10-27, 1 case) records production compiling a <code>service cloud.storage</code> ruleset and ALLOWING its read. Pyric refuses it ("Expected 'service firebase.storage', got 'service cloud.storage'"); <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#L22" target="_blank" rel="noopener noreferrer"><code>unit:storage/rules-oracle-conformance.test.ts</code></a> pins the refusal in <code>packages/conformance/storage-rules-exceptions/</code> under ST-H1-14. Pending fix.</div></div></details>
|
|
648
|
+
<details class="compat-row" data-status="diverged"><summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">Rules</code><span class="compat-sub"><span class="compat-behavior">One <code>service firebase.storage</code> may hold more than one top-level <code>match</code> block, one per bucket (ST-H2-8)</span></span></span></summary>
|
|
649
|
+
<div class="compat-evidence"><div class="compat-note"><a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-storage-two-top-level-bucket-matches.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-storage-two-top-level-bucket-matches</code></a> (captured 2026-10-27, 2 cases) records production compiling two top-level match blocks, ALLOWING <code>get</code> in the named bucket's block and DENYING the other bucket under the wildcard block. Pyric refuses the ruleset (<code>expected "}" or "function"</code>); <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#L22" target="_blank" rel="noopener noreferrer"><code>unit:storage/rules-oracle-conformance.test.ts</code></a> pins the refusal in <code>packages/conformance/storage-rules-exceptions/</code> under ST-H2-8. The grammar is shared with Firestore, where the Firestore hunt saw the same refusal. Pending fix.</div></div></details>
|
|
650
|
+
<details class="compat-row" data-status="diverged"><summary class="compat-line"><span class="compat-dot" data-status="diverged" role="img" aria-label="Diverged (documented)" title="Diverged (documented)"></span><span class="compat-main"><code class="compat-api">Rules</code><span class="compat-sub"><span class="compat-behavior">The language semantics the Firestore hunt found hold under <code>firebase.storage</code> too: <code>split()</code> drops capture-group text and trailing empty pieces, <code>trim()</code> keeps NBSP, ints are 64-bit with overflow errors and <code>MIN / -1 == MIN</code>, a timestamp past its range is an error, and unary minus negates a duration (ST-H1-13, sharing FS2-SIM-1, FS2-SIM-2, FS2-SIM-3, FS2-SIM-4, FS2-SIM-7, FS2-SIM-8 and FS2-SIM-9)</span></span></span></summary>
|
|
651
|
+
<div class="compat-evidence"><div class="compat-note"><a href="https://github.com/davideast/pyric/blob/main/packages/conformance/observations/storage-rules/rules-storage-xref-shared-language-semantics.json" target="_blank" rel="noopener noreferrer"><code>oracle:rules-storage-xref-shared-language-semantics</code></a> (captured 2026-10-27, 12 cases) records production giving the Firestore-reported answer on all 12. Storage has its own copies of integer operators and unary minus, and shares <code>Timestamp.binaryOp</code>, so the evaluator disagrees on 4 cases: one each under FS2-SIM-1 and FS2-SIM-2, and two under FS2-SIM-3. <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/rules-oracle-conformance.test.ts#L7" target="_blank" rel="noopener noreferrer"><code>unit:storage/rules-oracle-conformance.test.ts</code></a> pins both sides of each in <code>packages/conformance/storage-rules-exceptions/</code>. The other 8 cases agree, including <code>timestamp-plus-duration-past-range-errors</code> under FS2-SIM-9, since the shared <code>Timestamp</code> wrapper errors past its range (#1336, replayed by <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/simulator/time-range.test.ts#L2" target="_blank" rel="noopener noreferrer"><code>unit:rules/simulator/time-range.test.ts</code></a>), <code>(?i)</code> in <code>split()</code> and <code>replace()</code>, <code>duration.value()</code> past its range, and <code>trim-keeps-nbsp</code> under FS2-SIM-8: Storage's <code>trim()</code> now calls the shared <code>trimRulesString</code> (#1295), replayed by <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/storage/sandbox/rules-string-methods.test.ts#L91" target="_blank" rel="noopener noreferrer"><code>unit:storage/sandbox/rules-string-methods.test.ts</code></a>. Storage's <code>split()</code> and <code>replace()</code> now call the shared <code>splitRe2</code> and <code>replaceRe2</code> (#1333), so <code>split-drops-capture-text</code> (FS2-SIM-4) and <code>split-drops-trailing-empty</code> (FS2-SIM-7) agree, and a pattern from request data gets pyric's size limit and the 1000-repeat cap; <a href="https://github.com/davideast/pyric/blob/main/packages/pyric/test/rules/simulator/split-and-replace.test.ts#L10" target="_blank" rel="noopener noreferrer"><code>unit:rules/simulator/split-and-replace.test.ts</code></a> checks both engines. <code>trim()</code> and <code>split()</code> are no longer in this row's diverged scope. Pending fix.</div></div></details>
|
|
652
|
+
</div> </article> </main> <aside class="doc-toc" aria-label="On this page" data-astro-cid-mw7aashj> <h2 class="doc-toc-heading" data-astro-cid-mw7aashj>On this page</h2> <ul data-astro-cid-mw7aashj> <li data-depth="2" data-astro-cid-mw7aashj> <a href="#status-legend" data-astro-cid-mw7aashj>Status legend</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#firestore-rules-engine--production-simulator-conformance-rules-firestore-corpus" data-astro-cid-mw7aashj>Firestore rules engine — production simulator conformance (rules-firestore corpus)</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#storage-rules-engine--parsestoragerules--evaluatestoragerules-rules-storage-corpus" data-astro-cid-mw7aashj>Storage rules engine — parseStorageRules / evaluateStorageRules (rules-storage corpus)</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#rtdb-rules-engine--production-simulator-conformance-rules-rtdb-corpus" data-astro-cid-mw7aashj>RTDB rules engine — production simulator conformance (rules-rtdb corpus)</a> </li><li data-depth="2" data-astro-cid-mw7aashj> <a href="#current-gaps" data-astro-cid-mw7aashj>Current gaps</a> </li><li data-depth="3" data-astro-cid-mw7aashj> <a href="#documented-divergences" data-astro-cid-mw7aashj>Documented divergences</a> </li> </ul> </aside> </div> <script type="module">const a=document.querySelector("[data-api-symbol-filter]"),d=[...document.querySelectorAll("[data-api-symbol-group]")],n=document.querySelector("[data-api-symbol-empty]");a?.addEventListener("input",()=>{const t=a.value.trim().toLowerCase();let o=0;for(const i of d){let e=0;i.querySelectorAll("[data-api-symbol]").forEach(l=>{const r=!t||(l.dataset.apiSymbol??"").includes(t);l.hidden=!r,r&&e++}),i.hidden=e===0,o+=e}n&&(n.hidden=o>0)});</script> <script type="module">const s=new Map;document.querySelectorAll('.doc-toc a[href^="#"]').forEach(t=>s.set(decodeURIComponent(t.hash.slice(1)),t));if(s.size>0){const t=new Set,n=()=>{let o=null;for(const e of s.keys())if(t.has(e)){o=e;break}s.forEach((e,c)=>e.classList.toggle("active",c===o))},r=new IntersectionObserver(o=>{for(const e of o)e.isIntersecting?t.add(e.target.id):t.delete(e.target.id);n()},{rootMargin:"-72px 0px -60% 0px"});s.forEach((o,e)=>{const c=document.getElementById(e);c&&r.observe(c)})}</script> <script type="module">document.querySelector('.studio__nav [aria-current="page"]')?.scrollIntoView({block:"nearest",inline:"center"});</script> </body></html>
|