pyric 0.0.1 → 0.1.0-alpha.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (786) hide show
  1. package/README.md +52 -1
  2. package/dist/app/index.d.ts +81 -0
  3. package/dist/app/index.d.ts.map +1 -0
  4. package/dist/app/index.js +78 -0
  5. package/dist/app/index.js.map +1 -0
  6. package/dist/auth/index.d.ts +346 -0
  7. package/dist/auth/index.d.ts.map +1 -0
  8. package/dist/auth/index.js +701 -0
  9. package/dist/auth/index.js.map +1 -0
  10. package/dist/auth/prod-backend.d.ts +39 -0
  11. package/dist/auth/prod-backend.d.ts.map +1 -0
  12. package/dist/auth/prod-backend.js +179 -0
  13. package/dist/auth/prod-backend.js.map +1 -0
  14. package/dist/auth/providers.d.ts +107 -0
  15. package/dist/auth/providers.d.ts.map +1 -0
  16. package/dist/auth/providers.js +138 -0
  17. package/dist/auth/providers.js.map +1 -0
  18. package/dist/auth/sandbox-backend.d.ts +769 -0
  19. package/dist/auth/sandbox-backend.d.ts.map +1 -0
  20. package/dist/auth/sandbox-backend.js +1598 -0
  21. package/dist/auth/sandbox-backend.js.map +1 -0
  22. package/dist/auth/target.d.ts +40 -0
  23. package/dist/auth/target.d.ts.map +1 -0
  24. package/dist/auth/target.js +28 -0
  25. package/dist/auth/target.js.map +1 -0
  26. package/dist/auth/types.d.ts +252 -0
  27. package/dist/auth/types.d.ts.map +1 -0
  28. package/dist/auth/types.js +26 -0
  29. package/dist/auth/types.js.map +1 -0
  30. package/dist/database/constraints/atoms.d.ts +29 -0
  31. package/dist/database/constraints/atoms.d.ts.map +1 -0
  32. package/dist/database/constraints/atoms.js +43 -0
  33. package/dist/database/constraints/atoms.js.map +1 -0
  34. package/dist/database/constraints/compose.d.ts +16 -0
  35. package/dist/database/constraints/compose.d.ts.map +1 -0
  36. package/dist/database/constraints/compose.js +15 -0
  37. package/dist/database/constraints/compose.js.map +1 -0
  38. package/dist/database/constraints/data.d.ts +30 -0
  39. package/dist/database/constraints/data.d.ts.map +1 -0
  40. package/dist/database/constraints/data.js +46 -0
  41. package/dist/database/constraints/data.js.map +1 -0
  42. package/dist/database/constraints/document.d.ts +42 -0
  43. package/dist/database/constraints/document.d.ts.map +1 -0
  44. package/dist/database/constraints/document.js +86 -0
  45. package/dist/database/constraints/document.js.map +1 -0
  46. package/dist/database/constraints/game.d.ts +30 -0
  47. package/dist/database/constraints/game.d.ts.map +1 -0
  48. package/dist/database/constraints/game.js +49 -0
  49. package/dist/database/constraints/game.js.map +1 -0
  50. package/dist/database/constraints/index.d.ts +12 -0
  51. package/dist/database/constraints/index.d.ts.map +1 -0
  52. package/dist/database/constraints/index.js +9 -0
  53. package/dist/database/constraints/index.js.map +1 -0
  54. package/dist/database/constraints/policies.d.ts +16 -0
  55. package/dist/database/constraints/policies.d.ts.map +1 -0
  56. package/dist/database/constraints/policies.js +17 -0
  57. package/dist/database/constraints/policies.js.map +1 -0
  58. package/dist/database/constraints/ruleset.d.ts +5 -0
  59. package/dist/database/constraints/ruleset.d.ts.map +1 -0
  60. package/dist/database/constraints/ruleset.js +132 -0
  61. package/dist/database/constraints/ruleset.js.map +1 -0
  62. package/dist/database/constraints/schema.d.ts +17 -0
  63. package/dist/database/constraints/schema.d.ts.map +1 -0
  64. package/dist/database/constraints/schema.js +74 -0
  65. package/dist/database/constraints/schema.js.map +1 -0
  66. package/dist/database/constraints/types.d.ts +22 -0
  67. package/dist/database/constraints/types.d.ts.map +1 -0
  68. package/dist/database/constraints/types.js +2 -0
  69. package/dist/database/constraints/types.js.map +1 -0
  70. package/dist/database/crawl/handler.d.ts +7 -0
  71. package/dist/database/crawl/handler.d.ts.map +1 -0
  72. package/dist/database/crawl/handler.js +91 -0
  73. package/dist/database/crawl/handler.js.map +1 -0
  74. package/dist/database/crawl/semaphore.d.ts +9 -0
  75. package/dist/database/crawl/semaphore.d.ts.map +1 -0
  76. package/dist/database/crawl/semaphore.js +27 -0
  77. package/dist/database/crawl/semaphore.js.map +1 -0
  78. package/dist/database/crawl/spec.d.ts +33 -0
  79. package/dist/database/crawl/spec.d.ts.map +1 -0
  80. package/dist/database/crawl/spec.js +12 -0
  81. package/dist/database/crawl/spec.js.map +1 -0
  82. package/dist/database/data/handler.d.ts +9 -0
  83. package/dist/database/data/handler.d.ts.map +1 -0
  84. package/dist/database/data/handler.js +96 -0
  85. package/dist/database/data/handler.js.map +1 -0
  86. package/dist/database/data/spec.d.ts +77 -0
  87. package/dist/database/data/spec.d.ts.map +1 -0
  88. package/dist/database/data/spec.js +17 -0
  89. package/dist/database/data/spec.js.map +1 -0
  90. package/dist/database/data/validated.d.ts +8 -0
  91. package/dist/database/data/validated.d.ts.map +1 -0
  92. package/dist/database/data/validated.js +99 -0
  93. package/dist/database/data/validated.js.map +1 -0
  94. package/dist/database/grammar/RtdbExpr.ohm +98 -0
  95. package/dist/database/grammar/RtdbExpr.ohm.generated.d.ts +2 -0
  96. package/dist/database/grammar/RtdbExpr.ohm.generated.d.ts.map +1 -0
  97. package/dist/database/grammar/RtdbExpr.ohm.generated.js +103 -0
  98. package/dist/database/grammar/RtdbExpr.ohm.generated.js.map +1 -0
  99. package/dist/database/grammar/RtdbExprParser.d.ts +5 -0
  100. package/dist/database/grammar/RtdbExprParser.d.ts.map +1 -0
  101. package/dist/database/grammar/RtdbExprParser.js +53 -0
  102. package/dist/database/grammar/RtdbExprParser.js.map +1 -0
  103. package/dist/database/grammar/linter.d.ts +3 -0
  104. package/dist/database/grammar/linter.d.ts.map +1 -0
  105. package/dist/database/grammar/linter.js +66 -0
  106. package/dist/database/grammar/linter.js.map +1 -0
  107. package/dist/database/grammar/simulator.d.ts +31 -0
  108. package/dist/database/grammar/simulator.d.ts.map +1 -0
  109. package/dist/database/grammar/simulator.js +255 -0
  110. package/dist/database/grammar/simulator.js.map +1 -0
  111. package/dist/database/grammar/validator.d.ts +3 -0
  112. package/dist/database/grammar/validator.d.ts.map +1 -0
  113. package/dist/database/grammar/validator.js +84 -0
  114. package/dist/database/grammar/validator.js.map +1 -0
  115. package/dist/database/host.d.ts +50 -0
  116. package/dist/database/host.d.ts.map +1 -0
  117. package/dist/database/host.js +41 -0
  118. package/dist/database/host.js.map +1 -0
  119. package/dist/database/index.d.ts +40 -0
  120. package/dist/database/index.d.ts.map +1 -0
  121. package/dist/database/index.js +47 -0
  122. package/dist/database/index.js.map +1 -0
  123. package/dist/database/initialize-from-app.d.ts +28 -0
  124. package/dist/database/initialize-from-app.d.ts.map +1 -0
  125. package/dist/database/initialize-from-app.js +23 -0
  126. package/dist/database/initialize-from-app.js.map +1 -0
  127. package/dist/database/ir/handler.d.ts +6 -0
  128. package/dist/database/ir/handler.d.ts.map +1 -0
  129. package/dist/database/ir/handler.js +52 -0
  130. package/dist/database/ir/handler.js.map +1 -0
  131. package/dist/database/ir/spec.d.ts +25 -0
  132. package/dist/database/ir/spec.d.ts.map +1 -0
  133. package/dist/database/ir/spec.js +11 -0
  134. package/dist/database/ir/spec.js.map +1 -0
  135. package/dist/database/mapper.d.ts +9 -0
  136. package/dist/database/mapper.d.ts.map +1 -0
  137. package/dist/database/mapper.js +115 -0
  138. package/dist/database/mapper.js.map +1 -0
  139. package/dist/database/modular.d.ts +604 -0
  140. package/dist/database/modular.d.ts.map +1 -0
  141. package/dist/database/modular.js +1226 -0
  142. package/dist/database/modular.js.map +1 -0
  143. package/dist/database/replay.d.ts +33 -0
  144. package/dist/database/replay.d.ts.map +1 -0
  145. package/dist/database/replay.js +124 -0
  146. package/dist/database/replay.js.map +1 -0
  147. package/dist/database/resolver.d.ts +4 -0
  148. package/dist/database/resolver.d.ts.map +1 -0
  149. package/dist/database/resolver.js +66 -0
  150. package/dist/database/resolver.js.map +1 -0
  151. package/dist/database/sandbox/backend.d.ts +403 -0
  152. package/dist/database/sandbox/backend.d.ts.map +1 -0
  153. package/dist/database/sandbox/backend.js +1467 -0
  154. package/dist/database/sandbox/backend.js.map +1 -0
  155. package/dist/database/sandbox/data-tree.d.ts +123 -0
  156. package/dist/database/sandbox/data-tree.d.ts.map +1 -0
  157. package/dist/database/sandbox/data-tree.js +310 -0
  158. package/dist/database/sandbox/data-tree.js.map +1 -0
  159. package/dist/database/sandbox/normalize.d.ts +77 -0
  160. package/dist/database/sandbox/normalize.d.ts.map +1 -0
  161. package/dist/database/sandbox/normalize.js +165 -0
  162. package/dist/database/sandbox/normalize.js.map +1 -0
  163. package/dist/database/sandbox/push-id.d.ts +32 -0
  164. package/dist/database/sandbox/push-id.d.ts.map +1 -0
  165. package/dist/database/sandbox/push-id.js +82 -0
  166. package/dist/database/sandbox/push-id.js.map +1 -0
  167. package/dist/database/sandbox/query.d.ts +175 -0
  168. package/dist/database/sandbox/query.d.ts.map +1 -0
  169. package/dist/database/sandbox/query.js +269 -0
  170. package/dist/database/sandbox/query.js.map +1 -0
  171. package/dist/database/sandbox/rules-eval.d.ts +69 -0
  172. package/dist/database/sandbox/rules-eval.d.ts.map +1 -0
  173. package/dist/database/sandbox/rules-eval.js +124 -0
  174. package/dist/database/sandbox/rules-eval.js.map +1 -0
  175. package/dist/database/sandbox/sentinels.d.ts +71 -0
  176. package/dist/database/sandbox/sentinels.d.ts.map +1 -0
  177. package/dist/database/sandbox/sentinels.js +92 -0
  178. package/dist/database/sandbox/sentinels.js.map +1 -0
  179. package/dist/database/simulation/handler.d.ts +6 -0
  180. package/dist/database/simulation/handler.d.ts.map +1 -0
  181. package/dist/database/simulation/handler.js +236 -0
  182. package/dist/database/simulation/handler.js.map +1 -0
  183. package/dist/database/simulation/spec.d.ts +69 -0
  184. package/dist/database/simulation/spec.d.ts.map +1 -0
  185. package/dist/database/simulation/spec.js +25 -0
  186. package/dist/database/simulation/spec.js.map +1 -0
  187. package/dist/database/tools.d.ts +22 -0
  188. package/dist/database/tools.d.ts.map +1 -0
  189. package/dist/database/tools.js +272 -0
  190. package/dist/database/tools.js.map +1 -0
  191. package/dist/database/types.d.ts +210 -0
  192. package/dist/database/types.d.ts.map +1 -0
  193. package/dist/database/types.js +26 -0
  194. package/dist/database/types.js.map +1 -0
  195. package/dist/database/write/handler.d.ts +7 -0
  196. package/dist/database/write/handler.d.ts.map +1 -0
  197. package/dist/database/write/handler.js +42 -0
  198. package/dist/database/write/handler.js.map +1 -0
  199. package/dist/database/write/spec.d.ts +18 -0
  200. package/dist/database/write/spec.d.ts.map +1 -0
  201. package/dist/database/write/spec.js +7 -0
  202. package/dist/database/write/spec.js.map +1 -0
  203. package/dist/firestore/index.d.ts +533 -0
  204. package/dist/firestore/index.d.ts.map +1 -0
  205. package/dist/firestore/index.js +1536 -0
  206. package/dist/firestore/index.js.map +1 -0
  207. package/dist/firestore/tools.d.ts +67 -0
  208. package/dist/firestore/tools.d.ts.map +1 -0
  209. package/dist/firestore/tools.js +318 -0
  210. package/dist/firestore/tools.js.map +1 -0
  211. package/dist/firestore-values/index.d.ts +63 -0
  212. package/dist/firestore-values/index.d.ts.map +1 -0
  213. package/dist/firestore-values/index.js +148 -0
  214. package/dist/firestore-values/index.js.map +1 -0
  215. package/dist/project-scope.d.ts +5 -0
  216. package/dist/project-scope.d.ts.map +1 -0
  217. package/dist/project-scope.js +2 -0
  218. package/dist/project-scope.js.map +1 -0
  219. package/dist/rules/extract.d.ts +28 -0
  220. package/dist/rules/extract.d.ts.map +1 -0
  221. package/dist/rules/extract.js +25 -0
  222. package/dist/rules/extract.js.map +1 -0
  223. package/dist/rules/generators/assembler.d.ts +19 -0
  224. package/dist/rules/generators/assembler.d.ts.map +1 -0
  225. package/dist/rules/generators/assembler.js +292 -0
  226. package/dist/rules/generators/assembler.js.map +1 -0
  227. package/dist/rules/generators/expressions.d.ts +52 -0
  228. package/dist/rules/generators/expressions.d.ts.map +1 -0
  229. package/dist/rules/generators/expressions.js +117 -0
  230. package/dist/rules/generators/expressions.js.map +1 -0
  231. package/dist/rules/generators/grid.d.ts +22 -0
  232. package/dist/rules/generators/grid.d.ts.map +1 -0
  233. package/dist/rules/generators/grid.js +52 -0
  234. package/dist/rules/generators/grid.js.map +1 -0
  235. package/dist/rules/grammar/FirestoreAST.d.ts +129 -0
  236. package/dist/rules/grammar/FirestoreAST.d.ts.map +1 -0
  237. package/dist/rules/grammar/FirestoreAST.js +3 -0
  238. package/dist/rules/grammar/FirestoreAST.js.map +1 -0
  239. package/dist/rules/grammar/FirestoreAssembler.d.ts +5 -0
  240. package/dist/rules/grammar/FirestoreAssembler.d.ts.map +1 -0
  241. package/dist/rules/grammar/FirestoreAssembler.js +158 -0
  242. package/dist/rules/grammar/FirestoreAssembler.js.map +1 -0
  243. package/dist/rules/grammar/FirestoreParser.d.ts +46 -0
  244. package/dist/rules/grammar/FirestoreParser.d.ts.map +1 -0
  245. package/dist/rules/grammar/FirestoreParser.js +409 -0
  246. package/dist/rules/grammar/FirestoreParser.js.map +1 -0
  247. package/dist/rules/grammar/FirestoreRules.ohm +229 -0
  248. package/dist/rules/grammar/FirestoreRules.ohm.generated.d.ts +2 -0
  249. package/dist/rules/grammar/FirestoreRules.ohm.generated.d.ts.map +1 -0
  250. package/dist/rules/grammar/FirestoreRules.ohm.generated.js +234 -0
  251. package/dist/rules/grammar/FirestoreRules.ohm.generated.js.map +1 -0
  252. package/dist/rules/grammar/FirestoreValidator.d.ts +10 -0
  253. package/dist/rules/grammar/FirestoreValidator.d.ts.map +1 -0
  254. package/dist/rules/grammar/FirestoreValidator.js +482 -0
  255. package/dist/rules/grammar/FirestoreValidator.js.map +1 -0
  256. package/dist/rules/index.d.ts +67 -0
  257. package/dist/rules/index.d.ts.map +1 -0
  258. package/dist/rules/index.js +80 -0
  259. package/dist/rules/index.js.map +1 -0
  260. package/dist/rules/indexes/extract/annotation-collect.d.ts +42 -0
  261. package/dist/rules/indexes/extract/annotation-collect.d.ts.map +1 -0
  262. package/dist/rules/indexes/extract/annotation-collect.js +69 -0
  263. package/dist/rules/indexes/extract/annotation-collect.js.map +1 -0
  264. package/dist/rules/indexes/extract/annotations.d.ts +50 -0
  265. package/dist/rules/indexes/extract/annotations.d.ts.map +1 -0
  266. package/dist/rules/indexes/extract/annotations.js +144 -0
  267. package/dist/rules/indexes/extract/annotations.js.map +1 -0
  268. package/dist/rules/indexes/extract/ast.d.ts +54 -0
  269. package/dist/rules/indexes/extract/ast.d.ts.map +1 -0
  270. package/dist/rules/indexes/extract/ast.js +119 -0
  271. package/dist/rules/indexes/extract/ast.js.map +1 -0
  272. package/dist/rules/indexes/extract/classify.d.ts +41 -0
  273. package/dist/rules/indexes/extract/classify.d.ts.map +1 -0
  274. package/dist/rules/indexes/extract/classify.js +138 -0
  275. package/dist/rules/indexes/extract/classify.js.map +1 -0
  276. package/dist/rules/indexes/extract/composite.d.ts +38 -0
  277. package/dist/rules/indexes/extract/composite.d.ts.map +1 -0
  278. package/dist/rules/indexes/extract/composite.js +85 -0
  279. package/dist/rules/indexes/extract/composite.js.map +1 -0
  280. package/dist/rules/indexes/extract/dataflow.d.ts +45 -0
  281. package/dist/rules/indexes/extract/dataflow.d.ts.map +1 -0
  282. package/dist/rules/indexes/extract/dataflow.js +200 -0
  283. package/dist/rules/indexes/extract/dataflow.js.map +1 -0
  284. package/dist/rules/indexes/extract/enumerate.d.ts +41 -0
  285. package/dist/rules/indexes/extract/enumerate.d.ts.map +1 -0
  286. package/dist/rules/indexes/extract/enumerate.js +163 -0
  287. package/dist/rules/indexes/extract/enumerate.js.map +1 -0
  288. package/dist/rules/indexes/extract/extractor.d.ts +3 -0
  289. package/dist/rules/indexes/extract/extractor.d.ts.map +1 -0
  290. package/dist/rules/indexes/extract/extractor.js +274 -0
  291. package/dist/rules/indexes/extract/extractor.js.map +1 -0
  292. package/dist/rules/indexes/extract/types.d.ts +211 -0
  293. package/dist/rules/indexes/extract/types.d.ts.map +1 -0
  294. package/dist/rules/indexes/extract/types.js +14 -0
  295. package/dist/rules/indexes/extract/types.js.map +1 -0
  296. package/dist/rules/indexes/extractHandler.d.ts +16 -0
  297. package/dist/rules/indexes/extractHandler.d.ts.map +1 -0
  298. package/dist/rules/indexes/extractHandler.js +100 -0
  299. package/dist/rules/indexes/extractHandler.js.map +1 -0
  300. package/dist/rules/indexes/extractTool.d.ts +15 -0
  301. package/dist/rules/indexes/extractTool.d.ts.map +1 -0
  302. package/dist/rules/indexes/extractTool.js +55 -0
  303. package/dist/rules/indexes/extractTool.js.map +1 -0
  304. package/dist/rules/indexes/types.d.ts +71 -0
  305. package/dist/rules/indexes/types.d.ts.map +1 -0
  306. package/dist/rules/indexes/types.js +15 -0
  307. package/dist/rules/indexes/types.js.map +1 -0
  308. package/dist/rules/inspect/handler.d.ts +6 -0
  309. package/dist/rules/inspect/handler.d.ts.map +1 -0
  310. package/dist/rules/inspect/handler.js +144 -0
  311. package/dist/rules/inspect/handler.js.map +1 -0
  312. package/dist/rules/inspect/spec.d.ts +37 -0
  313. package/dist/rules/inspect/spec.d.ts.map +1 -0
  314. package/dist/rules/inspect/spec.js +2 -0
  315. package/dist/rules/inspect/spec.js.map +1 -0
  316. package/dist/rules/inspect/tools.d.ts +21 -0
  317. package/dist/rules/inspect/tools.d.ts.map +1 -0
  318. package/dist/rules/inspect/tools.js +21 -0
  319. package/dist/rules/inspect/tools.js.map +1 -0
  320. package/dist/rules/linter/ast-utils.d.ts +97 -0
  321. package/dist/rules/linter/ast-utils.d.ts.map +1 -0
  322. package/dist/rules/linter/ast-utils.js +491 -0
  323. package/dist/rules/linter/ast-utils.js.map +1 -0
  324. package/dist/rules/linter/hallucinations.d.ts +34 -0
  325. package/dist/rules/linter/hallucinations.d.ts.map +1 -0
  326. package/dist/rules/linter/hallucinations.js +334 -0
  327. package/dist/rules/linter/hallucinations.js.map +1 -0
  328. package/dist/rules/linter/linter.d.ts +84 -0
  329. package/dist/rules/linter/linter.d.ts.map +1 -0
  330. package/dist/rules/linter/linter.js +784 -0
  331. package/dist/rules/linter/linter.js.map +1 -0
  332. package/dist/rules/modules/resolver-browser.d.ts +41 -0
  333. package/dist/rules/modules/resolver-browser.d.ts.map +1 -0
  334. package/dist/rules/modules/resolver-browser.js +63 -0
  335. package/dist/rules/modules/resolver-browser.js.map +1 -0
  336. package/dist/rules/modules/resolver-core.d.ts +51 -0
  337. package/dist/rules/modules/resolver-core.d.ts.map +1 -0
  338. package/dist/rules/modules/resolver-core.js +338 -0
  339. package/dist/rules/modules/resolver-core.js.map +1 -0
  340. package/dist/rules/modules/resolver.d.ts +17 -0
  341. package/dist/rules/modules/resolver.d.ts.map +1 -0
  342. package/dist/rules/modules/resolver.js +49 -0
  343. package/dist/rules/modules/resolver.js.map +1 -0
  344. package/dist/rules/modules/stdlib/atomic.rules +52 -0
  345. package/dist/rules/modules/stdlib/atomic.test.json +152 -0
  346. package/dist/rules/modules/stdlib/auth.rules +7 -0
  347. package/dist/rules/modules/stdlib/auth.test.json +53 -0
  348. package/dist/rules/modules/stdlib/content.rules +50 -0
  349. package/dist/rules/modules/stdlib/content.test.json +238 -0
  350. package/dist/rules/modules/stdlib/counters.rules +41 -0
  351. package/dist/rules/modules/stdlib/counters.test.json +254 -0
  352. package/dist/rules/modules/stdlib/geometry.rules +43 -0
  353. package/dist/rules/modules/stdlib/geometry.test.json +200 -0
  354. package/dist/rules/modules/stdlib/joining.rules +49 -0
  355. package/dist/rules/modules/stdlib/joining.test.json +287 -0
  356. package/dist/rules/modules/stdlib/lifecycle.rules +51 -0
  357. package/dist/rules/modules/stdlib/lifecycle.test.json +288 -0
  358. package/dist/rules/modules/stdlib/lobby.rules +44 -0
  359. package/dist/rules/modules/stdlib/lobby.test.json +32 -0
  360. package/dist/rules/modules/stdlib/membership.rules +40 -0
  361. package/dist/rules/modules/stdlib/membership.test.json +107 -0
  362. package/dist/rules/modules/stdlib/spaces.rules +46 -0
  363. package/dist/rules/modules/stdlib/spaces.test.json +392 -0
  364. package/dist/rules/modules/stdlib/state.rules +28 -0
  365. package/dist/rules/modules/stdlib/state.test.json +55 -0
  366. package/dist/rules/modules/stdlib/timing.rules +20 -0
  367. package/dist/rules/modules/stdlib/timing.test.json +106 -0
  368. package/dist/rules/modules/stdlib/transitions.rules +34 -0
  369. package/dist/rules/modules/stdlib/transitions.test.json +86 -0
  370. package/dist/rules/modules/stdlib/turns.rules +27 -0
  371. package/dist/rules/modules/stdlib/turns.test.json +64 -0
  372. package/dist/rules/modules/stdlib/validation.rules +31 -0
  373. package/dist/rules/modules/stdlib/validation.test.json +184 -0
  374. package/dist/rules/modules/stdlib-content.d.ts +11 -0
  375. package/dist/rules/modules/stdlib-content.d.ts.map +1 -0
  376. package/dist/rules/modules/stdlib-content.js +590 -0
  377. package/dist/rules/modules/stdlib-content.js.map +1 -0
  378. package/dist/rules/node.d.ts +17 -0
  379. package/dist/rules/node.d.ts.map +1 -0
  380. package/dist/rules/node.js +21 -0
  381. package/dist/rules/node.js.map +1 -0
  382. package/dist/rules/rtdb.d.ts +28 -0
  383. package/dist/rules/rtdb.d.ts.map +1 -0
  384. package/dist/rules/rtdb.js +15 -0
  385. package/dist/rules/rtdb.js.map +1 -0
  386. package/dist/rules/simulator/evaluator.d.ts +179 -0
  387. package/dist/rules/simulator/evaluator.d.ts.map +1 -0
  388. package/dist/rules/simulator/evaluator.js +1221 -0
  389. package/dist/rules/simulator/evaluator.js.map +1 -0
  390. package/dist/rules/simulator/expression/eval-errors.d.ts +86 -0
  391. package/dist/rules/simulator/expression/eval-errors.d.ts.map +1 -0
  392. package/dist/rules/simulator/expression/eval-errors.js +68 -0
  393. package/dist/rules/simulator/expression/eval-errors.js.map +1 -0
  394. package/dist/rules/simulator/expression/evaluator.d.ts +43 -0
  395. package/dist/rules/simulator/expression/evaluator.d.ts.map +1 -0
  396. package/dist/rules/simulator/expression/evaluator.js +298 -0
  397. package/dist/rules/simulator/expression/evaluator.js.map +1 -0
  398. package/dist/rules/simulator/expression/lexer.d.ts +36 -0
  399. package/dist/rules/simulator/expression/lexer.d.ts.map +1 -0
  400. package/dist/rules/simulator/expression/lexer.js +318 -0
  401. package/dist/rules/simulator/expression/lexer.js.map +1 -0
  402. package/dist/rules/simulator/expression/parser.d.ts +39 -0
  403. package/dist/rules/simulator/expression/parser.d.ts.map +1 -0
  404. package/dist/rules/simulator/expression/parser.js +329 -0
  405. package/dist/rules/simulator/expression/parser.js.map +1 -0
  406. package/dist/rules/simulator/expression/types.d.ts +137 -0
  407. package/dist/rules/simulator/expression/types.d.ts.map +1 -0
  408. package/dist/rules/simulator/expression/types.js +90 -0
  409. package/dist/rules/simulator/expression/types.js.map +1 -0
  410. package/dist/rules/simulator/expression/walk-data.d.ts +52 -0
  411. package/dist/rules/simulator/expression/walk-data.d.ts.map +1 -0
  412. package/dist/rules/simulator/expression/walk-data.js +167 -0
  413. package/dist/rules/simulator/expression/walk-data.js.map +1 -0
  414. package/dist/rules/simulator/firestore-set.d.ts +25 -0
  415. package/dist/rules/simulator/firestore-set.d.ts.map +1 -0
  416. package/dist/rules/simulator/firestore-set.js +89 -0
  417. package/dist/rules/simulator/firestore-set.js.map +1 -0
  418. package/dist/rules/simulator/handler.d.ts +36 -0
  419. package/dist/rules/simulator/handler.d.ts.map +1 -0
  420. package/dist/rules/simulator/handler.js +583 -0
  421. package/dist/rules/simulator/handler.js.map +1 -0
  422. package/dist/rules/simulator/mapdiff.d.ts +29 -0
  423. package/dist/rules/simulator/mapdiff.d.ts.map +1 -0
  424. package/dist/rules/simulator/mapdiff.js +72 -0
  425. package/dist/rules/simulator/mapdiff.js.map +1 -0
  426. package/dist/rules/simulator/project-after-state.d.ts +33 -0
  427. package/dist/rules/simulator/project-after-state.d.ts.map +1 -0
  428. package/dist/rules/simulator/project-after-state.js +88 -0
  429. package/dist/rules/simulator/project-after-state.js.map +1 -0
  430. package/dist/rules/simulator/query-proof.d.ts +80 -0
  431. package/dist/rules/simulator/query-proof.d.ts.map +1 -0
  432. package/dist/rules/simulator/query-proof.js +142 -0
  433. package/dist/rules/simulator/query-proof.js.map +1 -0
  434. package/dist/rules/simulator/value-equality.d.ts +8 -0
  435. package/dist/rules/simulator/value-equality.d.ts.map +1 -0
  436. package/dist/rules/simulator/value-equality.js +40 -0
  437. package/dist/rules/simulator/value-equality.js.map +1 -0
  438. package/dist/rules/simulator/wrappers/base.d.ts +129 -0
  439. package/dist/rules/simulator/wrappers/base.d.ts.map +1 -0
  440. package/dist/rules/simulator/wrappers/base.js +90 -0
  441. package/dist/rules/simulator/wrappers/base.js.map +1 -0
  442. package/dist/rules/simulator/wrappers/bytes.d.ts +45 -0
  443. package/dist/rules/simulator/wrappers/bytes.d.ts.map +1 -0
  444. package/dist/rules/simulator/wrappers/bytes.js +130 -0
  445. package/dist/rules/simulator/wrappers/bytes.js.map +1 -0
  446. package/dist/rules/simulator/wrappers/duration.d.ts +76 -0
  447. package/dist/rules/simulator/wrappers/duration.d.ts.map +1 -0
  448. package/dist/rules/simulator/wrappers/duration.js +176 -0
  449. package/dist/rules/simulator/wrappers/duration.js.map +1 -0
  450. package/dist/rules/simulator/wrappers/float.d.ts +52 -0
  451. package/dist/rules/simulator/wrappers/float.d.ts.map +1 -0
  452. package/dist/rules/simulator/wrappers/float.js +88 -0
  453. package/dist/rules/simulator/wrappers/float.js.map +1 -0
  454. package/dist/rules/simulator/wrappers/latlng.d.ts +40 -0
  455. package/dist/rules/simulator/wrappers/latlng.d.ts.map +1 -0
  456. package/dist/rules/simulator/wrappers/latlng.js +100 -0
  457. package/dist/rules/simulator/wrappers/latlng.js.map +1 -0
  458. package/dist/rules/simulator/wrappers/path.d.ts +75 -0
  459. package/dist/rules/simulator/wrappers/path.d.ts.map +1 -0
  460. package/dist/rules/simulator/wrappers/path.js +158 -0
  461. package/dist/rules/simulator/wrappers/path.js.map +1 -0
  462. package/dist/rules/simulator/wrappers/reference.d.ts +82 -0
  463. package/dist/rules/simulator/wrappers/reference.d.ts.map +1 -0
  464. package/dist/rules/simulator/wrappers/reference.js +145 -0
  465. package/dist/rules/simulator/wrappers/reference.js.map +1 -0
  466. package/dist/rules/simulator/wrappers/timestamp.d.ts +86 -0
  467. package/dist/rules/simulator/wrappers/timestamp.d.ts.map +1 -0
  468. package/dist/rules/simulator/wrappers/timestamp.js +207 -0
  469. package/dist/rules/simulator/wrappers/timestamp.js.map +1 -0
  470. package/dist/rules/simulator/wrappers/vector.d.ts +43 -0
  471. package/dist/rules/simulator/wrappers/vector.d.ts.map +1 -0
  472. package/dist/rules/simulator/wrappers/vector.js +80 -0
  473. package/dist/rules/simulator/wrappers/vector.js.map +1 -0
  474. package/dist/rules/simulator-tools-impl.d.ts +64 -0
  475. package/dist/rules/simulator-tools-impl.d.ts.map +1 -0
  476. package/dist/rules/simulator-tools-impl.js +462 -0
  477. package/dist/rules/simulator-tools-impl.js.map +1 -0
  478. package/dist/rules/simulator.d.ts +20 -0
  479. package/dist/rules/simulator.d.ts.map +1 -0
  480. package/dist/rules/simulator.js +24 -0
  481. package/dist/rules/simulator.js.map +1 -0
  482. package/dist/rules/stdlib-modules.d.ts +73 -0
  483. package/dist/rules/stdlib-modules.d.ts.map +1 -0
  484. package/dist/rules/stdlib-modules.js +849 -0
  485. package/dist/rules/stdlib-modules.js.map +1 -0
  486. package/dist/rules/stdlib-tools.d.ts +19 -0
  487. package/dist/rules/stdlib-tools.d.ts.map +1 -0
  488. package/dist/rules/stdlib-tools.js +164 -0
  489. package/dist/rules/stdlib-tools.js.map +1 -0
  490. package/dist/rules/test/handler.d.ts +9 -0
  491. package/dist/rules/test/handler.d.ts.map +1 -0
  492. package/dist/rules/test/handler.js +124 -0
  493. package/dist/rules/test/handler.js.map +1 -0
  494. package/dist/rules/test/spec.d.ts +517 -0
  495. package/dist/rules/test/spec.d.ts.map +1 -0
  496. package/dist/rules/test/spec.js +227 -0
  497. package/dist/rules/test/spec.js.map +1 -0
  498. package/dist/rules/tools.d.ts +41 -0
  499. package/dist/rules/tools.d.ts.map +1 -0
  500. package/dist/rules/tools.js +124 -0
  501. package/dist/rules/tools.js.map +1 -0
  502. package/dist/rules/write/handler.d.ts +6 -0
  503. package/dist/rules/write/handler.d.ts.map +1 -0
  504. package/dist/rules/write/handler.js +126 -0
  505. package/dist/rules/write/handler.js.map +1 -0
  506. package/dist/rules/write/spec.d.ts +28 -0
  507. package/dist/rules/write/spec.d.ts.map +1 -0
  508. package/dist/rules/write/spec.js +2 -0
  509. package/dist/rules/write/spec.js.map +1 -0
  510. package/dist/sandbox/admin-compat.d.ts +20 -0
  511. package/dist/sandbox/admin-compat.d.ts.map +1 -0
  512. package/dist/sandbox/admin-compat.js +19 -0
  513. package/dist/sandbox/admin-compat.js.map +1 -0
  514. package/dist/sandbox/admin-firestore/error-translation.d.ts +86 -0
  515. package/dist/sandbox/admin-firestore/error-translation.d.ts.map +1 -0
  516. package/dist/sandbox/admin-firestore/error-translation.js +204 -0
  517. package/dist/sandbox/admin-firestore/error-translation.js.map +1 -0
  518. package/dist/sandbox/admin-firestore/index.d.ts +169 -0
  519. package/dist/sandbox/admin-firestore/index.d.ts.map +1 -0
  520. package/dist/sandbox/admin-firestore/index.js +456 -0
  521. package/dist/sandbox/admin-firestore/index.js.map +1 -0
  522. package/dist/sandbox/admin-firestore/remote.d.ts +97 -0
  523. package/dist/sandbox/admin-firestore/remote.d.ts.map +1 -0
  524. package/dist/sandbox/admin-firestore/remote.js +735 -0
  525. package/dist/sandbox/admin-firestore/remote.js.map +1 -0
  526. package/dist/sandbox/branches/index.d.ts +141 -0
  527. package/dist/sandbox/branches/index.d.ts.map +1 -0
  528. package/dist/sandbox/branches/index.js +327 -0
  529. package/dist/sandbox/branches/index.js.map +1 -0
  530. package/dist/sandbox/firestore/admin-compat/batch.d.ts +34 -0
  531. package/dist/sandbox/firestore/admin-compat/batch.d.ts.map +1 -0
  532. package/dist/sandbox/firestore/admin-compat/batch.js +73 -0
  533. package/dist/sandbox/firestore/admin-compat/batch.js.map +1 -0
  534. package/dist/sandbox/firestore/admin-compat/doc-ref.d.ts +38 -0
  535. package/dist/sandbox/firestore/admin-compat/doc-ref.d.ts.map +1 -0
  536. package/dist/sandbox/firestore/admin-compat/doc-ref.js +154 -0
  537. package/dist/sandbox/firestore/admin-compat/doc-ref.js.map +1 -0
  538. package/dist/sandbox/firestore/admin-compat/firestore.d.ts +45 -0
  539. package/dist/sandbox/firestore/admin-compat/firestore.d.ts.map +1 -0
  540. package/dist/sandbox/firestore/admin-compat/firestore.js +114 -0
  541. package/dist/sandbox/firestore/admin-compat/firestore.js.map +1 -0
  542. package/dist/sandbox/firestore/admin-compat/index.d.ts +45 -0
  543. package/dist/sandbox/firestore/admin-compat/index.d.ts.map +1 -0
  544. package/dist/sandbox/firestore/admin-compat/index.js +27 -0
  545. package/dist/sandbox/firestore/admin-compat/index.js.map +1 -0
  546. package/dist/sandbox/firestore/admin-compat/paths.d.ts +37 -0
  547. package/dist/sandbox/firestore/admin-compat/paths.d.ts.map +1 -0
  548. package/dist/sandbox/firestore/admin-compat/paths.js +49 -0
  549. package/dist/sandbox/firestore/admin-compat/paths.js.map +1 -0
  550. package/dist/sandbox/firestore/admin-compat/query.d.ts +261 -0
  551. package/dist/sandbox/firestore/admin-compat/query.d.ts.map +1 -0
  552. package/dist/sandbox/firestore/admin-compat/query.js +704 -0
  553. package/dist/sandbox/firestore/admin-compat/query.js.map +1 -0
  554. package/dist/sandbox/firestore/admin-compat/read-translation.d.ts +8 -0
  555. package/dist/sandbox/firestore/admin-compat/read-translation.d.ts.map +1 -0
  556. package/dist/sandbox/firestore/admin-compat/read-translation.js +59 -0
  557. package/dist/sandbox/firestore/admin-compat/read-translation.js.map +1 -0
  558. package/dist/sandbox/firestore/admin-compat/snapshots.d.ts +39 -0
  559. package/dist/sandbox/firestore/admin-compat/snapshots.d.ts.map +1 -0
  560. package/dist/sandbox/firestore/admin-compat/snapshots.js +50 -0
  561. package/dist/sandbox/firestore/admin-compat/snapshots.js.map +1 -0
  562. package/dist/sandbox/firestore/admin-compat/transaction.d.ts +35 -0
  563. package/dist/sandbox/firestore/admin-compat/transaction.d.ts.map +1 -0
  564. package/dist/sandbox/firestore/admin-compat/transaction.js +88 -0
  565. package/dist/sandbox/firestore/admin-compat/transaction.js.map +1 -0
  566. package/dist/sandbox/firestore/admin-compat/types.d.ts +297 -0
  567. package/dist/sandbox/firestore/admin-compat/types.d.ts.map +1 -0
  568. package/dist/sandbox/firestore/admin-compat/types.js +127 -0
  569. package/dist/sandbox/firestore/admin-compat/types.js.map +1 -0
  570. package/dist/sandbox/firestore/admin-compat/value-order.d.ts +49 -0
  571. package/dist/sandbox/firestore/admin-compat/value-order.d.ts.map +1 -0
  572. package/dist/sandbox/firestore/admin-compat/value-order.js +172 -0
  573. package/dist/sandbox/firestore/admin-compat/value-order.js.map +1 -0
  574. package/dist/sandbox/firestore/auto-id.d.ts +26 -0
  575. package/dist/sandbox/firestore/auto-id.d.ts.map +1 -0
  576. package/dist/sandbox/firestore/auto-id.js +36 -0
  577. package/dist/sandbox/firestore/auto-id.js.map +1 -0
  578. package/dist/sandbox/firestore/converters/bytes-geopoint.d.ts +48 -0
  579. package/dist/sandbox/firestore/converters/bytes-geopoint.d.ts.map +1 -0
  580. package/dist/sandbox/firestore/converters/bytes-geopoint.js +85 -0
  581. package/dist/sandbox/firestore/converters/bytes-geopoint.js.map +1 -0
  582. package/dist/sandbox/firestore/converters/fieldvalue.d.ts +69 -0
  583. package/dist/sandbox/firestore/converters/fieldvalue.d.ts.map +1 -0
  584. package/dist/sandbox/firestore/converters/fieldvalue.js +166 -0
  585. package/dist/sandbox/firestore/converters/fieldvalue.js.map +1 -0
  586. package/dist/sandbox/firestore/converters/reference.d.ts +31 -0
  587. package/dist/sandbox/firestore/converters/reference.d.ts.map +1 -0
  588. package/dist/sandbox/firestore/converters/reference.js +51 -0
  589. package/dist/sandbox/firestore/converters/reference.js.map +1 -0
  590. package/dist/sandbox/firestore/converters/timestamp.d.ts +32 -0
  591. package/dist/sandbox/firestore/converters/timestamp.d.ts.map +1 -0
  592. package/dist/sandbox/firestore/converters/timestamp.js +63 -0
  593. package/dist/sandbox/firestore/converters/timestamp.js.map +1 -0
  594. package/dist/sandbox/firestore/converters/user-timestamp.d.ts +46 -0
  595. package/dist/sandbox/firestore/converters/user-timestamp.d.ts.map +1 -0
  596. package/dist/sandbox/firestore/converters/user-timestamp.js +65 -0
  597. package/dist/sandbox/firestore/converters/user-timestamp.js.map +1 -0
  598. package/dist/sandbox/firestore/converters/vector.d.ts +24 -0
  599. package/dist/sandbox/firestore/converters/vector.d.ts.map +1 -0
  600. package/dist/sandbox/firestore/converters/vector.js +53 -0
  601. package/dist/sandbox/firestore/converters/vector.js.map +1 -0
  602. package/dist/sandbox/firestore/errors.d.ts +82 -0
  603. package/dist/sandbox/firestore/errors.d.ts.map +1 -0
  604. package/dist/sandbox/firestore/errors.js +54 -0
  605. package/dist/sandbox/firestore/errors.js.map +1 -0
  606. package/dist/sandbox/firestore/event-log.d.ts +99 -0
  607. package/dist/sandbox/firestore/event-log.d.ts.map +1 -0
  608. package/dist/sandbox/firestore/event-log.js +67 -0
  609. package/dist/sandbox/firestore/event-log.js.map +1 -0
  610. package/dist/sandbox/firestore/field-merge.d.ts +64 -0
  611. package/dist/sandbox/firestore/field-merge.d.ts.map +1 -0
  612. package/dist/sandbox/firestore/field-merge.js +221 -0
  613. package/dist/sandbox/firestore/field-merge.js.map +1 -0
  614. package/dist/sandbox/firestore/list-query-proof.d.ts +77 -0
  615. package/dist/sandbox/firestore/list-query-proof.d.ts.map +1 -0
  616. package/dist/sandbox/firestore/list-query-proof.js +170 -0
  617. package/dist/sandbox/firestore/list-query-proof.js.map +1 -0
  618. package/dist/sandbox/firestore/local-environment.d.ts +806 -0
  619. package/dist/sandbox/firestore/local-environment.d.ts.map +1 -0
  620. package/dist/sandbox/firestore/local-environment.js +3028 -0
  621. package/dist/sandbox/firestore/local-environment.js.map +1 -0
  622. package/dist/sandbox/firestore/local-state.d.ts +196 -0
  623. package/dist/sandbox/firestore/local-state.d.ts.map +1 -0
  624. package/dist/sandbox/firestore/local-state.js +413 -0
  625. package/dist/sandbox/firestore/local-state.js.map +1 -0
  626. package/dist/sandbox/firestore/overlay-backing.d.ts +44 -0
  627. package/dist/sandbox/firestore/overlay-backing.d.ts.map +1 -0
  628. package/dist/sandbox/firestore/overlay-backing.js +82 -0
  629. package/dist/sandbox/firestore/overlay-backing.js.map +1 -0
  630. package/dist/sandbox/firestore/sentinel-capture.d.ts +30 -0
  631. package/dist/sandbox/firestore/sentinel-capture.d.ts.map +1 -0
  632. package/dist/sandbox/firestore/sentinel-capture.js +61 -0
  633. package/dist/sandbox/firestore/sentinel-capture.js.map +1 -0
  634. package/dist/sandbox/firestore/snapshot-listeners.d.ts +284 -0
  635. package/dist/sandbox/firestore/snapshot-listeners.d.ts.map +1 -0
  636. package/dist/sandbox/firestore/snapshot-listeners.js +194 -0
  637. package/dist/sandbox/firestore/snapshot-listeners.js.map +1 -0
  638. package/dist/sandbox/firestore/topk.d.ts +12 -0
  639. package/dist/sandbox/firestore/topk.d.ts.map +1 -0
  640. package/dist/sandbox/firestore/topk.js +42 -0
  641. package/dist/sandbox/firestore/topk.js.map +1 -0
  642. package/dist/sandbox/firestore/transaction-merge.d.ts +55 -0
  643. package/dist/sandbox/firestore/transaction-merge.d.ts.map +1 -0
  644. package/dist/sandbox/firestore/transaction-merge.js +107 -0
  645. package/dist/sandbox/firestore/transaction-merge.js.map +1 -0
  646. package/dist/sandbox/firestore/transaction-types.d.ts +152 -0
  647. package/dist/sandbox/firestore/transaction-types.d.ts.map +1 -0
  648. package/dist/sandbox/firestore/transaction-types.js +10 -0
  649. package/dist/sandbox/firestore/transaction-types.js.map +1 -0
  650. package/dist/sandbox/firestore/transaction.d.ts +79 -0
  651. package/dist/sandbox/firestore/transaction.d.ts.map +1 -0
  652. package/dist/sandbox/firestore/transaction.js +95 -0
  653. package/dist/sandbox/firestore/transaction.js.map +1 -0
  654. package/dist/sandbox/firestore/value-equality.d.ts +2 -0
  655. package/dist/sandbox/firestore/value-equality.d.ts.map +1 -0
  656. package/dist/sandbox/firestore/value-equality.js +35 -0
  657. package/dist/sandbox/firestore/value-equality.js.map +1 -0
  658. package/dist/sandbox/firestore/value-resolver.d.ts +172 -0
  659. package/dist/sandbox/firestore/value-resolver.d.ts.map +1 -0
  660. package/dist/sandbox/firestore/value-resolver.js +221 -0
  661. package/dist/sandbox/firestore/value-resolver.js.map +1 -0
  662. package/dist/sandbox/firestore/wire-encoder.d.ts +14 -0
  663. package/dist/sandbox/firestore/wire-encoder.d.ts.map +1 -0
  664. package/dist/sandbox/firestore/wire-encoder.js +130 -0
  665. package/dist/sandbox/firestore/wire-encoder.js.map +1 -0
  666. package/dist/sandbox/index.d.ts +50 -0
  667. package/dist/sandbox/index.d.ts.map +1 -0
  668. package/dist/sandbox/index.js +63 -0
  669. package/dist/sandbox/index.js.map +1 -0
  670. package/dist/sandbox/internal/index.d.ts +36 -0
  671. package/dist/sandbox/internal/index.d.ts.map +1 -0
  672. package/dist/sandbox/internal/index.js +72 -0
  673. package/dist/sandbox/internal/index.js.map +1 -0
  674. package/dist/sandbox/internal/sandbox-impl.d.ts +343 -0
  675. package/dist/sandbox/internal/sandbox-impl.d.ts.map +1 -0
  676. package/dist/sandbox/internal/sandbox-impl.js +718 -0
  677. package/dist/sandbox/internal/sandbox-impl.js.map +1 -0
  678. package/dist/sandbox/persistence/backends.d.ts +37 -0
  679. package/dist/sandbox/persistence/backends.d.ts.map +1 -0
  680. package/dist/sandbox/persistence/backends.js +202 -0
  681. package/dist/sandbox/persistence/backends.js.map +1 -0
  682. package/dist/sandbox/persistence/chunk-format.d.ts +58 -0
  683. package/dist/sandbox/persistence/chunk-format.d.ts.map +1 -0
  684. package/dist/sandbox/persistence/chunk-format.js +163 -0
  685. package/dist/sandbox/persistence/chunk-format.js.map +1 -0
  686. package/dist/sandbox/persistence/controller.d.ts +39 -0
  687. package/dist/sandbox/persistence/controller.d.ts.map +1 -0
  688. package/dist/sandbox/persistence/controller.js +605 -0
  689. package/dist/sandbox/persistence/controller.js.map +1 -0
  690. package/dist/sandbox/persistence/index.d.ts +13 -0
  691. package/dist/sandbox/persistence/index.d.ts.map +1 -0
  692. package/dist/sandbox/persistence/index.js +5 -0
  693. package/dist/sandbox/persistence/index.js.map +1 -0
  694. package/dist/sandbox/persistence/serialize.d.ts +53 -0
  695. package/dist/sandbox/persistence/serialize.d.ts.map +1 -0
  696. package/dist/sandbox/persistence/serialize.js +98 -0
  697. package/dist/sandbox/persistence/serialize.js.map +1 -0
  698. package/dist/sandbox/persistence/types.d.ts +118 -0
  699. package/dist/sandbox/persistence/types.d.ts.map +1 -0
  700. package/dist/sandbox/persistence/types.js +15 -0
  701. package/dist/sandbox/persistence/types.js.map +1 -0
  702. package/dist/sandbox/remote.d.ts +118 -0
  703. package/dist/sandbox/remote.d.ts.map +1 -0
  704. package/dist/sandbox/remote.js +60 -0
  705. package/dist/sandbox/remote.js.map +1 -0
  706. package/dist/sandbox/replay/index.d.ts +106 -0
  707. package/dist/sandbox/replay/index.d.ts.map +1 -0
  708. package/dist/sandbox/replay/index.js +246 -0
  709. package/dist/sandbox/replay/index.js.map +1 -0
  710. package/dist/sandbox/sandbox-context.d.ts +40 -0
  711. package/dist/sandbox/sandbox-context.d.ts.map +1 -0
  712. package/dist/sandbox/sandbox-context.js +72 -0
  713. package/dist/sandbox/sandbox-context.js.map +1 -0
  714. package/dist/sandbox/tab-sync/index.d.ts +120 -0
  715. package/dist/sandbox/tab-sync/index.d.ts.map +1 -0
  716. package/dist/sandbox/tab-sync/index.js +272 -0
  717. package/dist/sandbox/tab-sync/index.js.map +1 -0
  718. package/dist/sandbox/types.d.ts +1235 -0
  719. package/dist/sandbox/types.d.ts.map +1 -0
  720. package/dist/sandbox/types.js +46 -0
  721. package/dist/sandbox/types.js.map +1 -0
  722. package/dist/storage/admin/api.d.ts +206 -0
  723. package/dist/storage/admin/api.d.ts.map +1 -0
  724. package/dist/storage/admin/api.js +349 -0
  725. package/dist/storage/admin/api.js.map +1 -0
  726. package/dist/storage/admin/handler.d.ts +29 -0
  727. package/dist/storage/admin/handler.d.ts.map +1 -0
  728. package/dist/storage/admin/handler.js +82 -0
  729. package/dist/storage/admin/handler.js.map +1 -0
  730. package/dist/storage/admin/spec.d.ts +64 -0
  731. package/dist/storage/admin/spec.d.ts.map +1 -0
  732. package/dist/storage/admin/spec.js +8 -0
  733. package/dist/storage/admin/spec.js.map +1 -0
  734. package/dist/storage/admin/tools.d.ts +23 -0
  735. package/dist/storage/admin/tools.d.ts.map +1 -0
  736. package/dist/storage/admin/tools.js +90 -0
  737. package/dist/storage/admin/tools.js.map +1 -0
  738. package/dist/storage/download.d.ts +27 -0
  739. package/dist/storage/download.d.ts.map +1 -0
  740. package/dist/storage/download.js +136 -0
  741. package/dist/storage/download.js.map +1 -0
  742. package/dist/storage/enforce.d.ts +26 -0
  743. package/dist/storage/enforce.d.ts.map +1 -0
  744. package/dist/storage/enforce.js +16 -0
  745. package/dist/storage/enforce.js.map +1 -0
  746. package/dist/storage/errors.d.ts +46 -0
  747. package/dist/storage/errors.d.ts.map +1 -0
  748. package/dist/storage/errors.js +62 -0
  749. package/dist/storage/errors.js.map +1 -0
  750. package/dist/storage/index.d.ts +49 -0
  751. package/dist/storage/index.d.ts.map +1 -0
  752. package/dist/storage/index.js +60 -0
  753. package/dist/storage/index.js.map +1 -0
  754. package/dist/storage/internal.d.ts +18 -0
  755. package/dist/storage/internal.d.ts.map +1 -0
  756. package/dist/storage/internal.js +18 -0
  757. package/dist/storage/internal.js.map +1 -0
  758. package/dist/storage/list.d.ts +17 -0
  759. package/dist/storage/list.d.ts.map +1 -0
  760. package/dist/storage/list.js +91 -0
  761. package/dist/storage/list.js.map +1 -0
  762. package/dist/storage/metadata.d.ts +86 -0
  763. package/dist/storage/metadata.d.ts.map +1 -0
  764. package/dist/storage/metadata.js +167 -0
  765. package/dist/storage/metadata.js.map +1 -0
  766. package/dist/storage/persistence.d.ts +134 -0
  767. package/dist/storage/persistence.d.ts.map +1 -0
  768. package/dist/storage/persistence.js +132 -0
  769. package/dist/storage/persistence.js.map +1 -0
  770. package/dist/storage/reference.d.ts +63 -0
  771. package/dist/storage/reference.d.ts.map +1 -0
  772. package/dist/storage/reference.js +156 -0
  773. package/dist/storage/reference.js.map +1 -0
  774. package/dist/storage/rules.d.ts +176 -0
  775. package/dist/storage/rules.d.ts.map +1 -0
  776. package/dist/storage/rules.js +621 -0
  777. package/dist/storage/rules.js.map +1 -0
  778. package/dist/storage/service.d.ts +139 -0
  779. package/dist/storage/service.d.ts.map +1 -0
  780. package/dist/storage/service.js +241 -0
  781. package/dist/storage/service.js.map +1 -0
  782. package/dist/storage/upload.d.ts +43 -0
  783. package/dist/storage/upload.d.ts.map +1 -0
  784. package/dist/storage/upload.js +235 -0
  785. package/dist/storage/upload.js.map +1 -0
  786. package/package.json +109 -3
@@ -0,0 +1,1221 @@
1
+ import { assembleExpression } from '../grammar/FirestoreAssembler.js';
2
+ import { MapDiff } from './mapdiff.js';
3
+ import { FirestoreSet } from './firestore-set.js';
4
+ import { rulesValuesEqual } from './value-equality.js';
5
+ import { RulesValue, NO_OP } from './wrappers/base.js';
6
+ import { LatLng } from './wrappers/latlng.js';
7
+ import { Duration } from './wrappers/duration.js';
8
+ import { Timestamp } from './wrappers/timestamp.js';
9
+ import { Bytes } from './wrappers/bytes.js';
10
+ import { Path } from './wrappers/path.js';
11
+ import { RulesFloat } from './wrappers/float.js';
12
+ import { md5 as md5Bytes } from 'js-md5';
13
+ import { sha256 as sha256Bytes } from 'js-sha256';
14
+ // ═══ Evaluation errors ═══
15
+ export class EvalError extends Error {
16
+ expr;
17
+ constructor(message, expr) {
18
+ super(message);
19
+ this.expr = expr;
20
+ this.name = 'EvalError';
21
+ }
22
+ }
23
+ /**
24
+ * Thrown when the simulator encounters a feature it doesn't yet implement
25
+ * (an unknown built-in function, namespace method, or method on a wrapper
26
+ * type we haven't added). Distinct from EvalError so the handler can map it
27
+ * to TestResult.state = 'UNSUPPORTED' instead of a silent DENY — that way
28
+ * agents see "sim abstained" rather than "your rule is wrong" when the gap
29
+ * is on our side. See REBUILD_PLAN.md Item 0.A.
30
+ */
31
+ export class UnsupportedError extends EvalError {
32
+ constructor(message, expr) {
33
+ super(message, expr);
34
+ this.name = 'UnsupportedError';
35
+ }
36
+ }
37
+ /**
38
+ * Records every sub-expression evaluation. Attach to a
39
+ * `SimulationContext.trace` to enable; the evaluator no-ops when absent
40
+ * so unsentinel'd callers pay zero cost.
41
+ *
42
+ * Recording strategy: each call to `evaluate(expr, ...)` is wrapped via
43
+ * `capture(expr, fn)`, which reserves a slot in `entries` (assigning
44
+ * `parent` from the current stack), pushes the slot index, invokes
45
+ * `fn`, then unwinds. This keeps recursion in the existing evaluator
46
+ * paths working unchanged — the wrapper sits on top.
47
+ */
48
+ export class TraceRecorder {
49
+ entries = [];
50
+ parents = [];
51
+ /** Stack of enclosing user-defined function names. The top of stack is
52
+ * stamped onto each pushed entry as `inlinedFrom`. Independent of the
53
+ * `parents` stack because parameter expressions evaluate inside the
54
+ * capture stack but *outside* the function frame. */
55
+ frames = [];
56
+ capture(expr, fn) {
57
+ const index = this.entries.length;
58
+ const parent = this.parents.length > 0 ? this.parents[this.parents.length - 1] : null;
59
+ // Reserve the slot first so any child evaluations point at us as
60
+ // their parent. The `value` field is filled in after `fn()` runs.
61
+ const entry = { source: assembleExpression(expr), kind: expr.type, parent };
62
+ this.stampFrame(entry);
63
+ this.entries.push(entry);
64
+ this.parents.push(index);
65
+ try {
66
+ const value = fn();
67
+ this.entries[index].value = value;
68
+ return value;
69
+ }
70
+ catch (e) {
71
+ this.entries[index].error = e instanceof Error ? e.message : String(e);
72
+ throw e;
73
+ }
74
+ finally {
75
+ this.parents.pop();
76
+ }
77
+ }
78
+ /** Record an operand the evaluator skipped because the LHS short-circuited.
79
+ * Emits an entry under the *current* parent so the trace tree mirrors
80
+ * the source AST (skip placeholder is a sibling of the LHS, not nested). */
81
+ skip(expr) {
82
+ const parent = this.parents.length > 0 ? this.parents[this.parents.length - 1] : null;
83
+ const entry = {
84
+ source: assembleExpression(expr),
85
+ kind: expr.type,
86
+ parent,
87
+ skipped: true,
88
+ };
89
+ this.stampFrame(entry);
90
+ this.entries.push(entry);
91
+ }
92
+ /** Push a user-defined function frame onto the inlinedFrom stack.
93
+ * Returns the popped value of the previous top (currently unused —
94
+ * callers pair `enterFrame` with `exitFrame()` via the stack, not a
95
+ * saved token — but keeping a return value lets future code switch
96
+ * to a save/restore idiom without API churn). */
97
+ enterFrame(name) {
98
+ this.frames.push(name);
99
+ }
100
+ /** Pop the topmost frame. Safe to call on an empty stack — no-ops. */
101
+ exitFrame() {
102
+ this.frames.pop();
103
+ }
104
+ stampFrame(entry) {
105
+ if (this.frames.length === 0)
106
+ return;
107
+ entry.inlinedFrom = { name: this.frames[this.frames.length - 1] };
108
+ }
109
+ /** Annotate the entry at `index` as a `let` binding — the value was
110
+ * bound to `name` in scope. Called from `evaluateFunctionCall` after
111
+ * each `let <name> = <expr>` line; the index is the binding root's
112
+ * position in the trace (`entries.length` captured BEFORE the call).
113
+ * Using a stable index instead of "last entry" is necessary because
114
+ * the binding's expression may have recursive children — the most
115
+ * recently pushed entry is the deepest leaf, not the root. */
116
+ markEntryAsLetBinding(index, name) {
117
+ if (index < 0 || index >= this.entries.length)
118
+ return;
119
+ this.entries[index].letBinding = { name };
120
+ }
121
+ }
122
+ // ═══ Evaluator ═══
123
+ /**
124
+ * Evaluate an expression against the simulation context. The public
125
+ * entry point — recursive calls inside the evaluator come back through
126
+ * here, so the optional trace wrapping naturally applies to every
127
+ * sub-expression.
128
+ */
129
+ export function evaluate(expr, ctx, scope = {}) {
130
+ if (!ctx.trace)
131
+ return evaluateExpr(expr, ctx, scope);
132
+ return ctx.trace.capture(expr, () => evaluateExpr(expr, ctx, scope));
133
+ }
134
+ function evaluateExpr(expr, ctx, scope) {
135
+ switch (expr.type) {
136
+ // ═══ Layer 1: Literals, identifiers, binary ops ═══
137
+ case 'literal':
138
+ // RULES-B5: a source literal written with a decimal point is a FLOAT
139
+ // (`1.0`, `1.5`), even when its value is integral. The grammar's
140
+ // `number_float` rule preserves the original text in `raw`, so a `.`
141
+ // there is the float signal. Bare integer literals (`1`) stay raw
142
+ // numbers (= int). Non-numeric literals (string/bool/null) pass through.
143
+ if (typeof expr.value === 'number' && expr.raw.includes('.')) {
144
+ return new RulesFloat(expr.value);
145
+ }
146
+ return expr.value;
147
+ case 'identifier':
148
+ return resolveIdentifier(expr.name, ctx, scope);
149
+ case 'binaryOp':
150
+ return evaluateBinaryOp(expr.op, expr.left, expr.right, ctx, scope);
151
+ case 'unaryOp': {
152
+ if (expr.op === '!')
153
+ return !evaluate(expr.operand, ctx, scope);
154
+ if (expr.op === '-') {
155
+ const v = evaluate(expr.operand, ctx, scope);
156
+ // RULES-B5: negating a float stays a float (`-1.5 is float`); negating
157
+ // an int stays an int. Preserve the operand's type tag.
158
+ if (v instanceof RulesFloat)
159
+ return new RulesFloat(-v.value);
160
+ return -v;
161
+ }
162
+ throw new EvalError(`Unknown unary op: ${expr.op}`, expr);
163
+ }
164
+ case 'ternary': {
165
+ const cond = evaluate(expr.condition, ctx, scope);
166
+ return cond ? evaluate(expr.consequent, ctx, scope) : evaluate(expr.alternate, ctx, scope);
167
+ }
168
+ // ═══ Layer 2: Member access, bracket access, `in` ═══
169
+ case 'memberAccess': {
170
+ const obj = evaluate(expr.object, ctx, scope);
171
+ // RULES-B2: field access on null/undefined is a runtime ERROR in
172
+ // Firestore rules (CEL field selection has no overload for null),
173
+ // not a silent null. Production denies the request. The common
174
+ // guard `request.auth != null && request.auth.uid == ...` stays
175
+ // safe because `&&` absorbs this error commutatively (RULES-B3) —
176
+ // the `false` LHS uniquely determines the result.
177
+ if (obj === null || obj === undefined) {
178
+ throw new EvalError(`Property '${expr.property}' accessed on ${obj === null ? 'null' : 'undefined'} value`, expr);
179
+ }
180
+ // Wrapper-owned property dispatch (Item 0.B hook 2). Wrappers like
181
+ // Timestamp expose no readable properties — `t.year` returns null,
182
+ // `t.year()` goes through callMethod. The base default returns null
183
+ // so unknown properties stay consistent with Firestore's "missing
184
+ // map key reads as null" semantics.
185
+ if (obj instanceof RulesValue)
186
+ return obj.field(expr.property);
187
+ if (obj instanceof MapDiff) {
188
+ // MapDiff methods that return FirestoreSet
189
+ switch (expr.property) {
190
+ case 'addedKeys': return () => obj.addedKeys();
191
+ case 'removedKeys': return () => obj.removedKeys();
192
+ case 'changedKeys': return () => obj.changedKeys();
193
+ case 'affectedKeys': return () => obj.affectedKeys();
194
+ case 'unchangedKeys': return () => obj.unchangedKeys();
195
+ }
196
+ }
197
+ if (obj instanceof FirestoreSet) {
198
+ switch (expr.property) {
199
+ case 'size': return () => obj.size();
200
+ }
201
+ }
202
+ // RULES-B2: reading a key that does not exist on a map is a runtime
203
+ // ERROR in Firestore rules (`resource.data.typo` denies the request),
204
+ // not the silent null this path used to return — that inversion made
205
+ // `resource.data.typo == null` ALLOW where production DENYs. A key
206
+ // present with an explicit null value still returns null (the key
207
+ // exists). Guard with the `in` operator (`'f' in resource.data`) or
208
+ // `resource.data.get('f', default)` to read a possibly-absent field.
209
+ if (Object.hasOwn(obj, expr.property)) {
210
+ return obj[expr.property];
211
+ }
212
+ throw new EvalError(`No field '${expr.property}' on map (use 'in' or .get() to read a possibly-absent field)`, expr);
213
+ }
214
+ case 'bracketAccess': {
215
+ const obj = evaluate(expr.object, ctx, scope);
216
+ const idx = evaluate(expr.index, ctx, scope);
217
+ // RULES-B2: index/key access on null/undefined errors in production
218
+ // (no CEL index overload for null), absorbed by &&/|| where guarded.
219
+ if (obj === null || obj === undefined) {
220
+ throw new EvalError(`Index access on ${obj === null ? 'null' : 'undefined'} value`, expr);
221
+ }
222
+ // Wrapper-owned bracket dispatch (Item 0.B hook 2, bracket variant).
223
+ // Path is the only wrapper that uses bracket access semantically
224
+ // (`/users/$(uid)`-style binding), but routing every wrapper through
225
+ // `field()` here keeps the contract uniform — wrappers that don't
226
+ // implement bracket access return null.
227
+ if (obj instanceof RulesValue)
228
+ return obj.field(String(idx));
229
+ // RULES-B2 scope note: DYNAMIC bracket/index access (`data[expr]`) is the
230
+ // documented idiom for "look up a key that may be absent" (e.g. a chess
231
+ // rule's `cfg().paths[from][to]`, `resource.data[squareVar]`). The
232
+ // Firebase docs explicitly confirm the ERROR semantics for DOTTED field
233
+ // access (`resource.data.typo`) — handled in `memberAccess` above — but
234
+ // NOT for dynamic index access, and flagship rules rely on null-on-miss
235
+ // here. Without an emulator to confirm bracket-vs-field divergence, we
236
+ // keep dynamic index access returning null on a missing key (the
237
+ // conservative, non-bug-laundering choice — the disputed-edge STOP).
238
+ // Present-with-null still returns null.
239
+ const key = String(idx);
240
+ return Object.hasOwn(obj, key) ? obj[key] : null;
241
+ }
242
+ case 'sliceAccess': {
243
+ // Range slice `obj[start:end]` — Item 4 (REBUILD_PLAN.md).
244
+ // Per type table:
245
+ // List: `[i:j]` returns sub-list, j exclusive.
246
+ // String: `[i:j]` returns substring, j exclusive.
247
+ // Indices must be integers; production rejects non-integer (incl.
248
+ // booleans coerced) so we surface as EvalError → DENY.
249
+ // Out-of-bounds: clamp to [0, length] (matches JS Array.slice and
250
+ // is the standard CEL-style semantics — locked in by parity pack).
251
+ // Negative indices: rejected (CEL doesn't support Python negatives).
252
+ const obj = evaluate(expr.object, ctx, scope);
253
+ const start = evaluate(expr.start, ctx, scope);
254
+ const end = evaluate(expr.end, ctx, scope);
255
+ if (obj === null || obj === undefined)
256
+ return null;
257
+ if (typeof start !== 'number' || !Number.isInteger(start)) {
258
+ throw new EvalError(`Slice start must be an integer, got ${typeof start}`);
259
+ }
260
+ if (typeof end !== 'number' || !Number.isInteger(end)) {
261
+ throw new EvalError(`Slice end must be an integer, got ${typeof end}`);
262
+ }
263
+ if (start < 0 || end < 0) {
264
+ throw new EvalError(`Slice indices must be non-negative, got [${start}:${end}]`);
265
+ }
266
+ if (typeof obj === 'string')
267
+ return obj.slice(start, end);
268
+ if (Array.isArray(obj))
269
+ return obj.slice(start, end);
270
+ throw new EvalError(`Slice not supported on ${typeof obj}`);
271
+ }
272
+ case 'inExpr': {
273
+ const element = evaluate(expr.element, ctx, scope);
274
+ const collection = evaluate(expr.collection, ctx, scope);
275
+ if (collection === null || collection === undefined)
276
+ return false;
277
+ // Use Rules value equality instead of Array.includes so wrapper
278
+ // value-equality applies (Item 0.B hook 6). Without this,
279
+ // `someTimestamp in [t1, t2]` is always false because `===`
280
+ // doesn't see the wrappers as equal even when their contents
281
+ // match. Map-key membership stays as a String() check — keys
282
+ // are always strings in Firestore rules.
283
+ if (Array.isArray(collection)) {
284
+ return collection.some(v => rulesValuesEqual(v, element));
285
+ }
286
+ // RULES-B7: map-key membership must use OWN keys only. `in` walks the JS
287
+ // prototype chain, so `'toString' in resource.data` wrongly returned
288
+ // true (and an `in`-guarded access then leaked the Object method).
289
+ // Object.hasOwn ignores inherited keys, matching Firestore's "the map
290
+ // has no inherited keys" model.
291
+ if (typeof collection === 'object')
292
+ return Object.hasOwn(collection, String(element));
293
+ return false;
294
+ }
295
+ case 'isExpr': {
296
+ const value = evaluate(expr.value, ctx, scope);
297
+ switch (expr.typeName) {
298
+ case 'string': return typeof value === 'string';
299
+ // RULES-B5: int and float are DISTINCT types. A bare JS number is an
300
+ // int; a RulesFloat wrapper is a float. `number` matches either.
301
+ // `1.5 is int` → false, `1 is float` → false, `1.0 is float` → true.
302
+ case 'int':
303
+ if (value instanceof RulesValue)
304
+ return false;
305
+ return typeof value === 'number';
306
+ case 'float':
307
+ return value instanceof RulesFloat;
308
+ case 'number':
309
+ // Reject wrappers that coerce to NaN (LatLng/Path) — they're
310
+ // not numbers even though valueOf() returns a Number. A RulesFloat
311
+ // IS a number, so it must pass here.
312
+ if (value instanceof RulesFloat)
313
+ return true;
314
+ if (value instanceof RulesValue)
315
+ return false;
316
+ return typeof value === 'number';
317
+ case 'bool': return typeof value === 'boolean';
318
+ case 'null': return value === null;
319
+ case 'list': return Array.isArray(value);
320
+ case 'map':
321
+ // Wrappers are objects but `is map` should be false for them —
322
+ // a Timestamp is not a Map. Filter via instanceof RulesValue.
323
+ if (value instanceof RulesValue)
324
+ return false;
325
+ // RULES-B12: MapDiff and FirestoreSet are internal types, not user
326
+ // maps — `someDiff is map` / `someSet is map` must be false.
327
+ if (value instanceof MapDiff || value instanceof FirestoreSet)
328
+ return false;
329
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
330
+ default:
331
+ // Wrapper type tags ('timestamp', 'duration', 'bytes', 'latlng',
332
+ // 'path') match via typeName. Item 0.B hook 5.
333
+ if (value instanceof RulesValue)
334
+ return value.typeName === expr.typeName;
335
+ return false;
336
+ }
337
+ }
338
+ case 'listLiteral':
339
+ return expr.elements.map(e => evaluate(e, ctx, scope));
340
+ case 'mapLiteral': {
341
+ const map = {};
342
+ for (const entry of expr.entries) {
343
+ const key = String(evaluate(entry.key, ctx, scope));
344
+ map[key] = evaluate(entry.value, ctx, scope);
345
+ }
346
+ return map;
347
+ }
348
+ // ═══ Layer 3: Function calls ═══
349
+ case 'functionCall':
350
+ return evaluateFunctionCall(expr.name, expr.args, ctx, scope);
351
+ // ═══ Layer 4: Method calls ═══
352
+ case 'methodCall':
353
+ return evaluateMethodCall(expr.object, expr.method, expr.args, ctx, scope);
354
+ // ═══ Layer 5: Path literals ═══
355
+ case 'pathLiteral': {
356
+ // Resolve segments — string literals pass through, embedded
357
+ // expressions (`$(uid)`) get evaluated and stringified.
358
+ // Item 5.4: returns Path wrapper instead of raw string so `is path`
359
+ // works. get/exists already String()-coerce, so resolveGet/Exists
360
+ // see the same '/foo/bar' shape via Path.toString().
361
+ const parts = [];
362
+ for (const seg of expr.segments) {
363
+ if (typeof seg === 'string') {
364
+ parts.push(seg);
365
+ }
366
+ else {
367
+ parts.push(String(evaluate(seg, ctx, scope)));
368
+ }
369
+ }
370
+ return new Path(parts);
371
+ }
372
+ default:
373
+ throw new EvalError(`Unknown expression type: ${expr.type}`, expr);
374
+ }
375
+ }
376
+ // ═══ Identifier resolution ═══
377
+ function resolveIdentifier(name, ctx, scope) {
378
+ // Local scope first (let bindings, function parameters)
379
+ if (name in scope)
380
+ return scope[name];
381
+ // Path variables
382
+ if (name in ctx.pathVariables)
383
+ return ctx.pathVariables[name];
384
+ // Built-in globals
385
+ switch (name) {
386
+ case 'request': return ctx.request;
387
+ case 'resource': return ctx.resource;
388
+ case 'true': return true;
389
+ case 'false': return false;
390
+ case 'null': return null;
391
+ }
392
+ // RULES-B2: an unbound identifier (typo'd variable, undeclared name) is a
393
+ // compile/runtime error in production rules, not a silent `undefined` that
394
+ // later reads as null. Surface it as an EvalError so the handler DENYs and
395
+ // &&/|| can absorb it where guarded.
396
+ throw new EvalError(`Undefined variable '${name}'`);
397
+ }
398
+ /** True for the built-in top-level globals resolvable as values. Used to
399
+ * decide whether an unknown method-call target is a possible (unimplemented)
400
+ * namespace vs a real resolvable identifier. */
401
+ function isKnownGlobal(name) {
402
+ return name === 'request' || name === 'resource'
403
+ || name === 'true' || name === 'false' || name === 'null';
404
+ }
405
+ // ═══ Binary operations with short-circuit ═══
406
+ function evaluateBinaryOp(op, left, right, ctx, scope) {
407
+ // RULES-B3: && and || are COMMUTATIVE error-absorbing operators in CEL,
408
+ // not plain left-to-right short-circuit. Per the CEL spec: "if any of
409
+ // their operands uniquely determines the result (false for &&, true for
410
+ // ||) the other operand may or may not be evaluated, and if that
411
+ // evaluation produces a runtime error, it will be ignored." So
412
+ // `error && false` → false and `error || true` → true — the error is
413
+ // absorbed by the determining operand regardless of position.
414
+ //
415
+ // We preserve laziness in the no-error path (the determining LHS skips
416
+ // the RHS), and only evaluate the RHS to attempt absorption when the LHS
417
+ // either errored or did not determine the result. If neither operand
418
+ // determines the result and one errored, the error propagates (re-thrown)
419
+ // so the handler DENYs.
420
+ if (op === '&&') {
421
+ let lv, lErr;
422
+ try {
423
+ lv = evaluate(left, ctx, scope);
424
+ }
425
+ catch (e) {
426
+ lErr = e;
427
+ }
428
+ if (lErr === undefined && !lv) {
429
+ // LHS is false → determines the result; RHS need not be evaluated.
430
+ ctx.trace?.skip(right);
431
+ return false;
432
+ }
433
+ // LHS errored or was true — RHS may absorb the error (if RHS is false)
434
+ // or determine the truthy result.
435
+ let rv, rErr;
436
+ try {
437
+ rv = evaluate(right, ctx, scope);
438
+ }
439
+ catch (e) {
440
+ rErr = e;
441
+ }
442
+ if (rErr === undefined && !rv)
443
+ return false; // RHS false absorbs any LHS error
444
+ if (lErr !== undefined)
445
+ throw lErr; // LHS error not absorbed
446
+ if (rErr !== undefined)
447
+ throw rErr; // RHS error, LHS was true
448
+ return !!rv;
449
+ }
450
+ if (op === '||') {
451
+ let lv, lErr;
452
+ try {
453
+ lv = evaluate(left, ctx, scope);
454
+ }
455
+ catch (e) {
456
+ lErr = e;
457
+ }
458
+ if (lErr === undefined && lv) {
459
+ // LHS is true → determines the result; RHS need not be evaluated.
460
+ ctx.trace?.skip(right);
461
+ return true;
462
+ }
463
+ let rv, rErr;
464
+ try {
465
+ rv = evaluate(right, ctx, scope);
466
+ }
467
+ catch (e) {
468
+ rErr = e;
469
+ }
470
+ if (rErr === undefined && rv)
471
+ return true; // RHS true absorbs any LHS error
472
+ if (lErr !== undefined)
473
+ throw lErr; // LHS error not absorbed
474
+ if (rErr !== undefined)
475
+ throw rErr; // RHS error, LHS was false
476
+ return !!rv;
477
+ }
478
+ const lv = evaluate(left, ctx, scope);
479
+ const rv = evaluate(right, ctx, scope);
480
+ // Wrapper-aware binary op dispatch (Item 0.B hook 4). Cross-type
481
+ // arithmetic like `Timestamp + Duration → Timestamp` and lexicographic
482
+ // `Bytes < Bytes` are handled here via the wrapper's own `binaryOp`
483
+ // method. NO_OP means the wrapper doesn't claim this op — fall through
484
+ // to the generic numeric switch below.
485
+ //
486
+ // == and != intentionally bypass this hook — they route through
487
+ // rulesValuesEqual above, which already calls the wrapper's
488
+ // `equals()`. Splitting "value equality" from "operator dispatch"
489
+ // keeps each wrapper's contract clean.
490
+ if (op !== '==' && op !== '!=') {
491
+ if (lv instanceof RulesValue) {
492
+ const r = lv.binaryOp(op, rv);
493
+ if (r !== NO_OP)
494
+ return r;
495
+ }
496
+ // RULES-B5: `int OP float` (bare-number LHS, RulesFloat RHS) must yield a
497
+ // float and preserve operand ORDER (unlike Duration/Timestamp this is a
498
+ // genuinely ordered numeric op for `-`/`/`/`%` and the comparisons). Route
499
+ // it through `RulesFloat(lv).binaryOp(op, rv)` so the result re-tags as a
500
+ // float and `5 / 2.0` does float division (2.5), not int truncation.
501
+ if (typeof lv === 'number' && rv instanceof RulesFloat) {
502
+ const r = new RulesFloat(lv).binaryOp(op, rv);
503
+ if (r !== NO_OP)
504
+ return r;
505
+ }
506
+ // Right-side dispatch for symmetric/commutative ops only. Avoids
507
+ // surprises like `1 - duration` accidentally reversing semantics.
508
+ if (rv instanceof RulesValue && (op === '+' || op === '*')) {
509
+ const r = rv.binaryOp(op, lv);
510
+ if (r !== NO_OP)
511
+ return r;
512
+ }
513
+ // Risk 2 (REBUILD_PLAN Item 1.2): if either operand is still a
514
+ // RulesValue at this point, the only remaining path is generic
515
+ // numeric coercion via valueOf(). For LatLng that yields NaN
516
+ // (silent DENY); for Duration/Timestamp/Bytes it would silently
517
+ // drop the type. Both are real type errors in the rule (e.g.
518
+ // `latlng + 1`, `duration > 60`), not sim gaps — surface as
519
+ // EvalError so handler.ts maps to DENY-with-error instead of a
520
+ // misleading "passed but came out false" or "UNSUPPORTED".
521
+ //
522
+ // Exception: `string + wrapper` is documented coercion that flows
523
+ // through `String(rv)` concat in the switch below.
524
+ if (op !== '+' || (typeof lv !== 'string' && typeof rv !== 'string')) {
525
+ if (lv instanceof RulesValue || rv instanceof RulesValue) {
526
+ const lhsType = lv instanceof RulesValue ? lv.typeName : typeof lv;
527
+ const rhsType = rv instanceof RulesValue ? rv.typeName : typeof rv;
528
+ throw new EvalError(`Operator '${op}' is not defined between ${lhsType} and ${rhsType}`);
529
+ }
530
+ }
531
+ }
532
+ // RULES-B12: ordered comparisons (`< > <= >=`) require both operands to be the
533
+ // SAME comparable type. CEL has no cross-type ordering overload, so `'a' < 1`
534
+ // is an error, not the JS-coerced `false` the bare `as number` casts produced.
535
+ // (Comparable wrappers — Bytes, Timestamp, Duration — were already dispatched
536
+ // via binaryOp above; anything reaching here is a raw number or string.)
537
+ if (op === '<' || op === '>' || op === '<=' || op === '>=') {
538
+ const sameComparable = (typeof lv === 'number' && typeof rv === 'number') ||
539
+ (typeof lv === 'string' && typeof rv === 'string');
540
+ if (!sameComparable) {
541
+ throw new EvalError(`Operator '${op}' is not defined between ${typeof lv} and ${typeof rv}`);
542
+ }
543
+ }
544
+ switch (op) {
545
+ case '==': return lv === rv || rulesValuesEqual(lv, rv);
546
+ case '!=': return !(lv === rv || rulesValuesEqual(lv, rv));
547
+ case '<': return lv < rv;
548
+ case '>': return lv > rv;
549
+ case '<=': return lv <= rv;
550
+ case '>=': return lv >= rv;
551
+ case '+': {
552
+ // RULES-B6: CEL `+` has no mixed-type overloads — both operands must be
553
+ // the same type. Allowed: string+string, number+number, list+list (concat).
554
+ // `'a' + 1` is an error (no string+int overload), not the silent
555
+ // `String(rv)` coercion the old impl did. (Wrapper `+` like
556
+ // Timestamp+Duration was already handled above via binaryOp dispatch;
557
+ // the documented `string + wrapper` affordance also flows above this.)
558
+ if (Array.isArray(lv) && Array.isArray(rv))
559
+ return [...lv, ...rv];
560
+ if (typeof lv === 'string' && typeof rv === 'string')
561
+ return lv + rv;
562
+ if (typeof lv === 'number' && typeof rv === 'number')
563
+ return lv + rv;
564
+ // Documented sim affordance (see the `op !== '+'` guard above): a
565
+ // `string + wrapper` (e.g. `'at ' + request.time`) concatenates the
566
+ // wrapper's string form. Preserved here for back-compat with that path.
567
+ if (typeof lv === 'string' && rv instanceof RulesValue)
568
+ return lv + String(rv);
569
+ throw new EvalError(`Operator '+' is not defined between ${Array.isArray(lv) ? 'list' : typeof lv} and ${Array.isArray(rv) ? 'list' : typeof rv}`);
570
+ }
571
+ case '-': return lv - rv;
572
+ case '*': return lv * rv;
573
+ case '/':
574
+ // RULES-B5: both operands are bare numbers here (= int ÷ int; any float
575
+ // operand was already handled by RulesFloat.binaryOp above). CEL INT64
576
+ // division TRUNCATES toward zero (`10 / 4 == 2`) and ERRORS on a zero
577
+ // divisor (it does NOT yield ±Infinity the way JS / float division does).
578
+ // The EvalError propagates via the tri-state so the rule DENYs.
579
+ if (rv === 0) {
580
+ throw new EvalError('Division by zero');
581
+ }
582
+ return Math.trunc(lv / rv);
583
+ case '%':
584
+ // CEL INT64 modulo likewise errors on a zero divisor (JS would give NaN).
585
+ if (rv === 0) {
586
+ throw new EvalError('Modulo by zero');
587
+ }
588
+ return lv % rv;
589
+ default: throw new EvalError(`Unknown binary op: ${op}`);
590
+ }
591
+ }
592
+ // ═══ Type-conversion builtins (RULES-B5 / RULES-B6) ═══
593
+ //
594
+ // CEL's int()/float()/bool() conversions are STRICT — they error on inputs
595
+ // that JS would silently coerce. The pre-fix impl used `parseInt` (which
596
+ // grabs a leading numeric prefix, so `int('12abc')` → 12) and `Boolean`
597
+ // (truthiness, so `bool('false')` → true). Both diverge from prod, which
598
+ // rejects a malformed string outright. Docs: rules.Integer / rules.Float /
599
+ // rules.Boolean (string converters require a fully-valid literal).
600
+ /** `int(x)` — truncate a number/float, parse a strict-integer string, or 0/1 a bool. */
601
+ function rulesInt(v) {
602
+ if (typeof v === 'number')
603
+ return Math.trunc(v);
604
+ if (v instanceof RulesFloat)
605
+ return Math.trunc(v.value);
606
+ if (typeof v === 'boolean')
607
+ return v ? 1 : 0;
608
+ if (typeof v === 'string') {
609
+ // CEL parses the WHOLE string as an integer; a trailing non-digit (or any
610
+ // float/garbage) is an error, not a salvaged prefix like parseInt gives.
611
+ if (/^[+-]?\d+$/.test(v.trim()))
612
+ return parseInt(v.trim(), 10);
613
+ throw new EvalError(`int() cannot convert string '${v}' to an integer`);
614
+ }
615
+ throw new EvalError(`int() cannot convert ${typeof v} to an integer`);
616
+ }
617
+ /** `float(x)` — produce a FLOAT (tagged) from a number, float, or numeric string. */
618
+ function rulesFloatBuiltin(v) {
619
+ if (v instanceof RulesFloat)
620
+ return v;
621
+ if (typeof v === 'number')
622
+ return new RulesFloat(v);
623
+ if (typeof v === 'string') {
624
+ const trimmed = v.trim();
625
+ // Strict numeric form (optional sign, digits, optional fraction). Rejects
626
+ // `'1.2.3'`, `'abc'`, trailing junk — all errors in prod.
627
+ if (/^[+-]?(\d+\.?\d*|\.\d+)$/.test(trimmed))
628
+ return new RulesFloat(parseFloat(trimmed));
629
+ throw new EvalError(`float() cannot convert string '${v}' to a float`);
630
+ }
631
+ throw new EvalError(`float() cannot convert ${typeof v} to a float`);
632
+ }
633
+ /** `bool(x)` — pass a bool through; parse only the exact strings 'true'/'false'. */
634
+ function rulesBool(v) {
635
+ if (typeof v === 'boolean')
636
+ return v;
637
+ if (typeof v === 'string') {
638
+ if (v === 'true')
639
+ return true;
640
+ if (v === 'false')
641
+ return false;
642
+ throw new EvalError(`bool() cannot convert string '${v}' to a boolean`);
643
+ }
644
+ throw new EvalError(`bool() cannot convert ${typeof v} to a boolean`);
645
+ }
646
+ // ═══ Function calls ═══
647
+ function evaluateFunctionCall(name, args, ctx, scope) {
648
+ // Built-in functions
649
+ switch (name) {
650
+ case 'get': {
651
+ const path = String(evaluate(args[0], ctx, scope));
652
+ return resolveGet(path, ctx);
653
+ }
654
+ case 'exists': {
655
+ const path = String(evaluate(args[0], ctx, scope));
656
+ return resolveExists(path, ctx);
657
+ }
658
+ case 'getAfter': {
659
+ // Item 7 — projected post-write state.
660
+ // For the document being written (matches request.path), return the
661
+ // afterState. For unrelated paths, fall through to get() — the write
662
+ // under evaluation doesn't affect them.
663
+ const argVal = evaluate(args[0], ctx, scope);
664
+ const pathStr = String(argVal);
665
+ if (pathStr === ctx.afterStatePath.toString()) {
666
+ // RULES-B8: getAfter() of a doc that won't exist post-write (delete,
667
+ // or projected-null) ERRORS like get() — guard with existsAfter().
668
+ if (ctx.afterState === null) {
669
+ throw new EvalError(`getAfter() of non-existent document '${pathStr}' (guard with existsAfter() first)`);
670
+ }
671
+ return makeGetResource(normalizePath(pathStr, ctx), ctx.afterState);
672
+ }
673
+ return resolveGet(pathStr, ctx);
674
+ }
675
+ case 'existsAfter': {
676
+ // Item 7 — projected existence after the write.
677
+ // Same dispatch shape as getAfter: target path uses ctx.existsAfter,
678
+ // other paths fall through to exists().
679
+ const argVal = evaluate(args[0], ctx, scope);
680
+ const pathStr = String(argVal);
681
+ if (pathStr === ctx.afterStatePath.toString()) {
682
+ return ctx.existsAfter;
683
+ }
684
+ return resolveExists(pathStr, ctx);
685
+ }
686
+ case 'debug': {
687
+ const val = evaluate(args[0], ctx, scope);
688
+ return val; // debug() returns its argument
689
+ }
690
+ // RULES-B5 / RULES-B6: type-conversion builtins follow CEL's strict
691
+ // semantics, not JS coercion. `String()` routes a RulesFloat through its
692
+ // `.toString()` so `string(1.0)` → "1.0" (decimal preserved); bare ints
693
+ // and other types stringify normally.
694
+ case 'string': return String(evaluate(args[0], ctx, scope));
695
+ case 'int': return rulesInt(evaluate(args[0], ctx, scope));
696
+ case 'float': return rulesFloatBuiltin(evaluate(args[0], ctx, scope));
697
+ case 'bool': return rulesBool(evaluate(args[0], ctx, scope));
698
+ case 'path': {
699
+ // Item 5.4 — `path('users/alice')` returns a Path wrapper. Accepts
700
+ // strings or existing Paths (idempotent). Other inputs throw —
701
+ // mismatched argument is a real type error in the rule.
702
+ const arg = evaluate(args[0], ctx, scope);
703
+ if (arg instanceof Path)
704
+ return arg;
705
+ if (typeof arg === 'string')
706
+ return Path.fromString(arg);
707
+ throw new EvalError(`path() requires a string argument, got ${typeof arg}`);
708
+ }
709
+ }
710
+ // User-defined functions
711
+ const fn = ctx.functions.get(name);
712
+ if (!fn)
713
+ throw new UnsupportedError(`Unknown function: ${name}`);
714
+ // Bind parameters — parameter expressions evaluate in the CALLER's
715
+ // scope and frame. We push the inlinedFrom frame *after* this loop
716
+ // so parameter traces stay attributed to the caller (where the
717
+ // argument expression literally lives in source).
718
+ const fnScope = { ...scope };
719
+ for (let i = 0; i < fn.parameters.length; i++) {
720
+ fnScope[fn.parameters[i]] = evaluate(args[i], ctx, scope);
721
+ }
722
+ // Push frame for let bindings + body so their trace entries get
723
+ // tagged `inlinedFrom: { name }`. try/finally so an evaluation
724
+ // error still pops the frame and doesn't pollute sibling rules.
725
+ ctx.trace?.enterFrame(name);
726
+ try {
727
+ // Evaluate let bindings. Capture the trace entries' next-write
728
+ // position BEFORE the binding's expression evaluates — that slot
729
+ // will be the binding-root entry (recursive children land below
730
+ // it). After the evaluate returns, tag that root slot with the
731
+ // bound name so the agent can attribute "this subtree was the
732
+ // value of `<name>`" without re-walking the function AST.
733
+ for (const binding of fn.lets) {
734
+ const bindingRootIdx = ctx.trace ? ctx.trace.entries.length : -1;
735
+ fnScope[binding.name] = evaluate(binding.value, ctx, fnScope);
736
+ if (bindingRootIdx >= 0) {
737
+ ctx.trace?.markEntryAsLetBinding(bindingRootIdx, binding.name);
738
+ }
739
+ }
740
+ // Evaluate body
741
+ return evaluate(fn.body, ctx, fnScope);
742
+ }
743
+ finally {
744
+ ctx.trace?.exitFrame();
745
+ }
746
+ }
747
+ // ═══ Method calls ═══
748
+ function evaluateMethodCall(objectExpr, method, args, ctx, scope) {
749
+ // Built-in namespaces (math, timestamp, duration) are not first-class
750
+ // values — they exist only as method-call targets. Dispatch them before
751
+ // calling evaluate(objectExpr), which would resolve the identifier to
752
+ // undefined and fall through to "Unknown method on undefined".
753
+ // Skip the dispatch if the namespace name is shadowed by a local binding
754
+ // or path variable.
755
+ if (objectExpr.type === 'identifier') {
756
+ const name = objectExpr.name;
757
+ if (isBuiltinNamespace(name) && !(name in scope) && !(name in ctx.pathVariables)) {
758
+ const argValues = args.map(a => evaluate(a, ctx, scope));
759
+ return evaluateNamespaceMethod(name, method, argValues);
760
+ }
761
+ // RULES-B2 interaction: an UNKNOWN bare identifier used as a method-call
762
+ // target (e.g. `foo.bar()`) is most likely a namespace the simulator
763
+ // hasn't implemented, not a typo'd value. Resolving it as a value now
764
+ // throws "Undefined variable" (RULES-B2), but we want the agent-facing
765
+ // UNSUPPORTED signal ("sim gap") here rather than a hard ERROR — that's
766
+ // the deliberate affordance the old `undefined.method()` path provided.
767
+ // (Bare `foo` used as a VALUE still errors via resolveIdentifier; only the
768
+ // method-call-target case is lifted to UNSUPPORTED.)
769
+ if (!isBuiltinNamespace(name)
770
+ && !(name in scope)
771
+ && !(name in ctx.pathVariables)
772
+ && !isKnownGlobal(name)) {
773
+ throw new UnsupportedError(`Unknown namespace or method '${name}.${method}()'`);
774
+ }
775
+ }
776
+ const obj = evaluate(objectExpr, ctx, scope);
777
+ const argValues = args.map(a => evaluate(a, ctx, scope));
778
+ // RulesValue method dispatch (Item 0.B hook 3). Placed above MapDiff /
779
+ // FirestoreSet / Map / Array / String branches because every wrapper
780
+ // is also `typeof === 'object'` — without this short-circuit, a
781
+ // Timestamp would fall into the Map branch and dispatch `.size()` on
782
+ // its private `{seconds, nanos}` shape. NO_OP from the wrapper means
783
+ // "I don't have this method" → UnsupportedError, which the handler
784
+ // maps to TestResult.state = 'UNSUPPORTED' (Item 0.A) so agents see
785
+ // a sim gap rather than a silent DENY.
786
+ if (obj instanceof RulesValue) {
787
+ const result = obj.callMethod(method, argValues);
788
+ if (result === NO_OP) {
789
+ throw new UnsupportedError(`Unknown method '${method}' on ${obj.typeName}`);
790
+ }
791
+ return result;
792
+ }
793
+ // MapDiff: obj.diff(other) → MapDiff
794
+ if (method === 'diff' && typeof obj === 'object' && obj !== null && !Array.isArray(obj)) {
795
+ const other = argValues[0];
796
+ if (typeof other === 'object' && other !== null) {
797
+ return new MapDiff(other, obj);
798
+ }
799
+ throw new EvalError('diff() requires a map argument');
800
+ }
801
+ // Callable methods (from memberAccess returning a function)
802
+ if (typeof obj === 'function') {
803
+ return obj(...argValues);
804
+ }
805
+ // FirestoreSet methods
806
+ if (obj instanceof FirestoreSet) {
807
+ switch (method) {
808
+ case 'hasOnly': return obj.hasOnly(argValues[0]);
809
+ case 'hasAll': return obj.hasAll(argValues[0]);
810
+ case 'hasAny': return obj.hasAny(argValues[0]);
811
+ case 'size': return obj.size();
812
+ // Set algebra (Item 5.1) — per REBUILD_PLAN type table.
813
+ case 'difference': return obj.difference(argValues[0]);
814
+ case 'union': return obj.union(argValues[0]);
815
+ case 'intersection': return obj.intersection(argValues[0]);
816
+ }
817
+ }
818
+ // MapDiff methods (when called directly, not via memberAccess)
819
+ if (obj instanceof MapDiff) {
820
+ switch (method) {
821
+ case 'addedKeys': return obj.addedKeys();
822
+ case 'removedKeys': return obj.removedKeys();
823
+ case 'changedKeys': return obj.changedKeys();
824
+ case 'affectedKeys': return obj.affectedKeys();
825
+ case 'unchangedKeys': return obj.unchangedKeys();
826
+ }
827
+ }
828
+ // Map/object methods
829
+ if (typeof obj === 'object' && obj !== null && !Array.isArray(obj)) {
830
+ const map = obj;
831
+ switch (method) {
832
+ case 'keys': return new FirestoreSet(Object.keys(map));
833
+ case 'values': return Object.values(map);
834
+ case 'size': return Object.keys(map).length;
835
+ // RULES-B7: key checks use OWN keys only (no prototype-chain leak).
836
+ case 'hasAll': return argValues[0].every(k => Object.hasOwn(map, k));
837
+ case 'hasAny': return argValues[0].some(k => Object.hasOwn(map, k));
838
+ case 'hasOnly': return Object.keys(map).every(k => argValues[0].includes(k));
839
+ case 'diff': {
840
+ const other = argValues[0];
841
+ return new MapDiff(other, map);
842
+ }
843
+ case 'get': {
844
+ // Map.get(key, default) — Item 3 (REBUILD_PLAN.md).
845
+ // Two forms (production behavior locked in by 0.H parity probe):
846
+ // m.get('a', default) → m.a if present, else default
847
+ // m.get(['a','b','c'], default) → m.a.b.c if full walk succeeds, else default
848
+ // Production *always* returns default on any walk failure (missing
849
+ // key, non-map intermediate, etc.). It does NOT distinguish "key
850
+ // missing" from "intermediate missing" — both collapse to default.
851
+ // An explicit `null` value at the leaf is returned as null (not
852
+ // default), since the key is present.
853
+ const keyArg = argValues[0];
854
+ const def = argValues[1];
855
+ if (Array.isArray(keyArg)) {
856
+ // List-form nested traversal
857
+ let cur = map;
858
+ for (const seg of keyArg) {
859
+ if (cur === null || cur === undefined)
860
+ return def;
861
+ // Cannot descend into non-maps (string, number, list, wrapper).
862
+ // Probe scenarios list_form_mid_is_string / list_form_mid_is_int
863
+ // confirmed prod returns default in these cases.
864
+ if (typeof cur !== 'object' || Array.isArray(cur) || cur instanceof RulesValue)
865
+ return def;
866
+ const k = String(seg);
867
+ if (!Object.hasOwn(cur, k))
868
+ return def; // RULES-B7: own keys only
869
+ cur = cur[k];
870
+ }
871
+ return cur;
872
+ }
873
+ // Single-key form
874
+ const k = String(keyArg);
875
+ return Object.hasOwn(map, k) ? map[k] : def; // RULES-B7: own keys only
876
+ }
877
+ }
878
+ }
879
+ // List methods
880
+ if (Array.isArray(obj)) {
881
+ switch (method) {
882
+ case 'size': return obj.length;
883
+ // RULES-B9: list membership uses VALUE equality, not JS identity, so it
884
+ // is consistent with `in` / `removeAll` (which already use
885
+ // rulesValuesEqual). Without this, `[t1].hasAll([t2])` for equal-valued
886
+ // Timestamp/Bytes/Path wrappers wrongly returned false because the two
887
+ // instances aren't `===`.
888
+ case 'hasAll': return argValues[0].every(v => obj.some(o => rulesValuesEqual(o, v)));
889
+ case 'hasAny': return argValues[0].some(v => obj.some(o => rulesValuesEqual(o, v)));
890
+ case 'hasOnly': {
891
+ const allowed = argValues[0];
892
+ return obj.every(v => allowed.some(o => rulesValuesEqual(o, v)));
893
+ }
894
+ case 'join': return obj.join(String(argValues[0] ?? ','));
895
+ // ─── Item 5.2: List.concat / removeAll / toSet ─────────────────────
896
+ case 'concat': {
897
+ const other = argValues[0];
898
+ if (!Array.isArray(other)) {
899
+ throw new EvalError(`List.concat requires a list argument, got ${typeof other}`);
900
+ }
901
+ return [...obj, ...other];
902
+ }
903
+ case 'removeAll': {
904
+ // Returns a new list with all items from `other` removed (by value
905
+ // equality). Uses rulesValuesEqual so wrapper instances (Timestamp,
906
+ // Duration, etc.) compare by value, not by JS identity — without
907
+ // this, `[t1].removeAll([t2])` where t1 and t2 are equal Timestamp
908
+ // wrappers would not remove t1.
909
+ const other = argValues[0];
910
+ if (!Array.isArray(other)) {
911
+ throw new EvalError(`List.removeAll requires a list argument, got ${typeof other}`);
912
+ }
913
+ return obj.filter(v => !other.some(o => rulesValuesEqual(v, o)));
914
+ }
915
+ case 'toSet': {
916
+ // Convert to FirestoreSet. The class stores strings only — coerce
917
+ // via String() on each element. This is sufficient for the common
918
+ // case of `[]Doc.tags.toSet()`-style usage where the source list
919
+ // is already strings. Non-string elements (numbers, booleans) get
920
+ // String()-coerced and may collapse onto each other (1 and '1'
921
+ // become the same set member). Locked-in behavior is whatever the
922
+ // parity pack confirms; if prod diverges we revisit.
923
+ return new FirestoreSet(obj.map(v => String(v)));
924
+ }
925
+ }
926
+ }
927
+ // String methods
928
+ if (typeof obj === 'string') {
929
+ switch (method) {
930
+ case 'size': return obj.length;
931
+ case 'lower': return obj.toLowerCase();
932
+ case 'upper': return obj.toUpperCase();
933
+ case 'trim': return obj.trim();
934
+ // RULES-B4: split()/matches()/replace() take RE2 *regular expressions*
935
+ // and matches() is a FULL-STRING (anchored) test — production: "returns
936
+ // true if the whole string matches the regex". The previous impl used a
937
+ // partial JS `.test()`, a literal-string `split`, and a first-only
938
+ // `replace`. We compile via the JS RegExp engine (RE2 and JS share the
939
+ // common subset used by rules; exotic RE2-only constructs are a noted
940
+ // limitation, not a behavior divergence for the documented cases) and
941
+ // anchor matches() with `^(?:…)$`, apply `g` to replace/split.
942
+ case 'split': return obj.split(compileRulesRegex(String(argValues[0]), 'g'));
943
+ case 'matches':
944
+ return compileRulesRegex(`^(?:${String(argValues[0])})$`).test(obj);
945
+ case 'replace':
946
+ return obj.replace(compileRulesRegex(String(argValues[0]), 'g'), String(argValues[1]));
947
+ case 'toUtf8': return Bytes.fromUtf8(obj); // Item 5.3 — only producer of Bytes from a literal.
948
+ }
949
+ }
950
+ throw new UnsupportedError(`Unknown method '${method}' on ${typeof obj}`);
951
+ }
952
+ /**
953
+ * Compile a Firestore-rules regex (RULES-B4). Rules regexes follow RE2
954
+ * syntax; we use the JS RegExp engine, which shares the common subset rules
955
+ * actually use. A pattern the engine can't compile is a real type error in
956
+ * the rule (not a sim gap) → EvalError so the handler DENYs, matching
957
+ * production's "invalid regex → error" behavior.
958
+ */
959
+ function compileRulesRegex(pattern, flags = '') {
960
+ try {
961
+ return new RegExp(pattern, flags);
962
+ }
963
+ catch (e) {
964
+ throw new EvalError(`Invalid regex '${pattern}': ${e.message}`);
965
+ }
966
+ }
967
+ // ═══ Built-in namespaces (math, timestamp, duration) ═══
968
+ //
969
+ // Production parity: these used to throw via the "Unknown method on
970
+ // undefined" path because the identifiers `math` / `timestamp` / `duration`
971
+ // resolve to undefined. The throw was caught by handler.ts and counted as
972
+ // deny — silently denying every ALLOW case that touched a math/time
973
+ // built-in. Confirmed by parity-stress-integration.test.ts (Pack 1).
974
+ //
975
+ // We model timestamp values and duration values as plain numbers
976
+ // (milliseconds since epoch and milliseconds, respectively). That lets
977
+ // JavaScript's `<`, `>`, `<=`, `>=`, `+`, `-` work without a wrapper
978
+ // class. This is sufficient for any rule whose timestamp/duration values
979
+ // originate from the namespace built-ins themselves.
980
+ const BUILTIN_NAMESPACES = new Set(['math', 'timestamp', 'duration', 'latlng', 'hashing']);
981
+ function isBuiltinNamespace(name) {
982
+ return BUILTIN_NAMESPACES.has(name);
983
+ }
984
+ function evaluateNamespaceMethod(ns, method, args) {
985
+ // RULES-B5: the numeric namespaces (math/latlng/timestamp/duration) operate
986
+ // on the underlying double — `latlng.value(37.7, ...)` or `math.ceil(1.5)`
987
+ // doesn't care whether the arg was written as an int or a float literal, and
988
+ // the constructors store bare numbers. Unwrap RulesFloat args to their raw
989
+ // value so `args[0] as number` casts inside each handler stay valid (a
990
+ // RulesFloat is otherwise an object, breaking `latlng.value` equality etc.).
991
+ // hashing.* takes Bytes/String — no numeric args — so it's left untouched.
992
+ const a = ns === 'hashing' ? args : args.map(unwrapFloat);
993
+ switch (ns) {
994
+ case 'math': return evaluateMathMethod(method, a);
995
+ case 'timestamp': return evaluateTimestampMethod(method, a);
996
+ case 'duration': return evaluateDurationMethod(method, a);
997
+ case 'latlng': return evaluateLatLngMethod(method, a);
998
+ case 'hashing': return evaluateHashingMethod(method, args);
999
+ }
1000
+ throw new UnsupportedError(`Unknown namespace '${ns}'`);
1001
+ }
1002
+ /** RULES-B5: collapse a RulesFloat to its raw double; pass everything else through. */
1003
+ function unwrapFloat(v) {
1004
+ return v instanceof RulesFloat ? v.value : v;
1005
+ }
1006
+ // ─── hashing.* (Item 5.3) ───────────────────────────────────────────────
1007
+ //
1008
+ // Per docs (rules.hashing): crc32, crc32c, md5, sha256.
1009
+ // All accept Bytes | String; strings are hashed as UTF-8 bytes.
1010
+ // All return Bytes (round-trips with Bytes.toBase64() / .toHexString()).
1011
+ //
1012
+ // md5 / sha256 use node:crypto (CLAUDE.md: dev tools must use node:* APIs).
1013
+ // crc32 (IEEE 802.3) and crc32c (Castagnoli) are implemented inline via
1014
+ // table lookup since no crc dep is in the SDK.
1015
+ function coerceToBytes(arg) {
1016
+ if (arg instanceof Bytes)
1017
+ return arg;
1018
+ if (typeof arg === 'string')
1019
+ return Bytes.fromUtf8(arg);
1020
+ throw new EvalError(`hashing.* requires Bytes or String, got ${typeof arg}`);
1021
+ }
1022
+ function evaluateHashingMethod(method, args) {
1023
+ const input = coerceToBytes(args[0]);
1024
+ switch (method) {
1025
+ case 'md5': {
1026
+ // js-md5 / js-sha256 keep this path browser-safe (no node:crypto).
1027
+ // Both libs return ArrayBuffer with `.arrayBuffer()`, identical
1028
+ // bytes to the previous createHash() output — pinned by the
1029
+ // hashing.* receipt corpus.
1030
+ return new Bytes(new Uint8Array(md5Bytes.arrayBuffer(input.data)));
1031
+ }
1032
+ case 'sha256': {
1033
+ return new Bytes(new Uint8Array(sha256Bytes.arrayBuffer(input.data)));
1034
+ }
1035
+ case 'crc32': {
1036
+ const u32 = crc32(input.data);
1037
+ return new Bytes(u32ToBeBytes(u32));
1038
+ }
1039
+ case 'crc32c': {
1040
+ const u32 = crc32c(input.data);
1041
+ return new Bytes(u32ToBeBytes(u32));
1042
+ }
1043
+ }
1044
+ throw new UnsupportedError(`Unknown hashing method '${method}'`);
1045
+ }
1046
+ function u32ToBeBytes(n) {
1047
+ // Big-endian 4 bytes — matches how Firestore returns CRC results.
1048
+ return new Uint8Array([
1049
+ (n >>> 24) & 0xff,
1050
+ (n >>> 16) & 0xff,
1051
+ (n >>> 8) & 0xff,
1052
+ n & 0xff,
1053
+ ]);
1054
+ }
1055
+ // CRC32 IEEE 802.3 (polynomial 0xEDB88320, reflected).
1056
+ const CRC32_TABLE = (() => {
1057
+ const t = new Uint32Array(256);
1058
+ for (let i = 0; i < 256; i++) {
1059
+ let c = i;
1060
+ for (let k = 0; k < 8; k++) {
1061
+ c = (c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1);
1062
+ }
1063
+ t[i] = c >>> 0;
1064
+ }
1065
+ return t;
1066
+ })();
1067
+ function crc32(data) {
1068
+ let c = 0xFFFFFFFF;
1069
+ for (let i = 0; i < data.length; i++) {
1070
+ c = CRC32_TABLE[(c ^ data[i]) & 0xff] ^ (c >>> 8);
1071
+ }
1072
+ return (c ^ 0xFFFFFFFF) >>> 0;
1073
+ }
1074
+ // CRC32C Castagnoli (polynomial 0x82F63B78, reflected).
1075
+ const CRC32C_TABLE = (() => {
1076
+ const t = new Uint32Array(256);
1077
+ for (let i = 0; i < 256; i++) {
1078
+ let c = i;
1079
+ for (let k = 0; k < 8; k++) {
1080
+ c = (c & 1) ? (0x82F63B78 ^ (c >>> 1)) : (c >>> 1);
1081
+ }
1082
+ t[i] = c >>> 0;
1083
+ }
1084
+ return t;
1085
+ })();
1086
+ function crc32c(data) {
1087
+ let c = 0xFFFFFFFF;
1088
+ for (let i = 0; i < data.length; i++) {
1089
+ c = CRC32C_TABLE[(c ^ data[i]) & 0xff] ^ (c >>> 8);
1090
+ }
1091
+ return (c ^ 0xFFFFFFFF) >>> 0;
1092
+ }
1093
+ function evaluateLatLngMethod(method, args) {
1094
+ switch (method) {
1095
+ case 'value': {
1096
+ // latlng.value(lat, lng) → LatLng wrapper. Item 1.1 closes the
1097
+ // entire `latlng` namespace gap (was throwing UnsupportedError).
1098
+ const [lat, lng] = args;
1099
+ return new LatLng(lat, lng);
1100
+ }
1101
+ }
1102
+ throw new UnsupportedError(`Unknown latlng method '${method}'`);
1103
+ }
1104
+ function evaluateMathMethod(method, args) {
1105
+ switch (method) {
1106
+ case 'abs': return Math.abs(args[0]);
1107
+ case 'ceil': return Math.ceil(args[0]);
1108
+ case 'floor': return Math.floor(args[0]);
1109
+ case 'round': return Math.round(args[0]);
1110
+ case 'sqrt': return Math.sqrt(args[0]);
1111
+ case 'pow': return Math.pow(args[0], args[1]);
1112
+ case 'isInfinite': {
1113
+ const n = args[0];
1114
+ return Number.isFinite(n) === false && Number.isNaN(n) === false;
1115
+ }
1116
+ case 'isNaN': return Number.isNaN(args[0]);
1117
+ }
1118
+ throw new UnsupportedError(`Unknown math method '${method}'`);
1119
+ }
1120
+ function evaluateTimestampMethod(method, args) {
1121
+ switch (method) {
1122
+ case 'date': {
1123
+ // timestamp.date(year, month, day) → Timestamp at midnight UTC.
1124
+ // Item 1.3 flip: was returning epoch-ms Number which broke
1125
+ // `is timestamp` and dropped sub-second precision under arithmetic.
1126
+ const [y, m, d] = args;
1127
+ return Timestamp.fromYMD(y, m, d);
1128
+ }
1129
+ case 'value': {
1130
+ // timestamp.value(epochMillis) → Timestamp wrapper. Item 1.3 flip.
1131
+ return Timestamp.fromMillis(args[0]);
1132
+ }
1133
+ }
1134
+ throw new UnsupportedError(`Unknown timestamp method '${method}'`);
1135
+ }
1136
+ function evaluateDurationMethod(method, args) {
1137
+ switch (method) {
1138
+ case 'value': {
1139
+ // duration.value(magnitude, unit) → Duration wrapper. Item 1.2 flip:
1140
+ // was returning a millis Number, which silently lost sub-ms precision
1141
+ // and made `is duration` always false. Item 1.0 dispatch hooks +
1142
+ // Duration.binaryOp keep arithmetic and comparison working without
1143
+ // numeric coercion.
1144
+ try {
1145
+ return Duration.fromValue(args[0], args[1]);
1146
+ }
1147
+ catch (e) {
1148
+ // Bad unit string is a real type error in the rule, not a sim gap.
1149
+ throw new EvalError(e.message);
1150
+ }
1151
+ }
1152
+ case 'time': {
1153
+ const [h, m, s, ns] = args;
1154
+ return Duration.fromTime(h, m, s, ns);
1155
+ }
1156
+ case 'abs': {
1157
+ const d = args[0];
1158
+ if (!(d instanceof Duration)) {
1159
+ throw new EvalError(`duration.abs() requires a Duration argument, got ${typeof d}`);
1160
+ }
1161
+ return Duration.abs(d);
1162
+ }
1163
+ }
1164
+ throw new UnsupportedError(`Unknown duration method '${method}'`);
1165
+ }
1166
+ // ═══ get()/exists() mock resolution ═══
1167
+ function normalizePath(rawPath, ctx) {
1168
+ // Path format: /databases/(default)/documents/collection/docId
1169
+ // or /databases/$(database)/documents/collection/docId
1170
+ return rawPath
1171
+ .replace(/\$\(database\)/g, '(default)')
1172
+ .replace(/^\/databases\/\(default\)\/documents\//, '');
1173
+ }
1174
+ /**
1175
+ * Build the resource value `get()` / `getAfter()` return for an existing
1176
+ * document. RULES-B8: production exposes the document identity alongside
1177
+ * `data` — `get(path).id` is the last path segment and `get(path).__name__`
1178
+ * is the full Path. These were never populated, so rules reading them
1179
+ * silently denied (the access returned undefined → now, post-B2, would
1180
+ * even error). Mirrors the top-level `resource` shape (SimResource).
1181
+ */
1182
+ function makeGetResource(relPath, data) {
1183
+ const segs = relPath.split('/').filter(Boolean);
1184
+ const id = segs.length > 0 ? segs[segs.length - 1] : '';
1185
+ const fullSegs = ['databases', '(default)', 'documents', ...segs];
1186
+ return { data, id, __name__: new Path(fullSegs) };
1187
+ }
1188
+ function resolveGet(rawPath, ctx) {
1189
+ const path = normalizePath(rawPath, ctx);
1190
+ let doc = ctx.mockDocuments.get(path);
1191
+ if (!doc && ctx.getDoc) {
1192
+ const faulted = ctx.getDoc(path);
1193
+ if (faulted) {
1194
+ ctx.mockDocuments.set(path, faulted); // memoize for repeat reads in this eval
1195
+ doc = faulted;
1196
+ }
1197
+ }
1198
+ if (doc)
1199
+ return makeGetResource(path, doc);
1200
+ // RULES-B8: get() of a missing document is a runtime ERROR in production
1201
+ // (it performs a real read; a non-existent doc denies the request), NOT
1202
+ // the silent null this used to return. The safe pattern is
1203
+ // `exists(path) && get(path).data...` — the false exists() absorbs this
1204
+ // error commutatively (RULES-B3). Throwing here makes that guard behave
1205
+ // exactly as prod, and makes an UNguarded get() of a missing doc DENY.
1206
+ throw new EvalError(`get() of non-existent document '${path}' (guard with exists() first)`);
1207
+ }
1208
+ function resolveExists(rawPath, ctx) {
1209
+ const path = normalizePath(rawPath, ctx);
1210
+ if (ctx.mockDocuments.has(path))
1211
+ return true;
1212
+ if (ctx.getDoc) {
1213
+ const faulted = ctx.getDoc(path);
1214
+ if (faulted) {
1215
+ ctx.mockDocuments.set(path, faulted); // memoize so a later get() is consistent
1216
+ return true;
1217
+ }
1218
+ }
1219
+ return false;
1220
+ }
1221
+ //# sourceMappingURL=evaluator.js.map