pyric 0.1.0-alpha.20 → 0.1.0-alpha.21

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 (225) hide show
  1. package/README.md +64 -89
  2. package/dist/ai/blocked.d.ts +52 -0
  3. package/dist/ai/blocked.d.ts.map +1 -0
  4. package/dist/ai/blocked.js +83 -0
  5. package/dist/ai/blocked.js.map +1 -0
  6. package/dist/ai/broker/broker.d.ts +49 -0
  7. package/dist/ai/broker/broker.d.ts.map +1 -1
  8. package/dist/ai/broker/broker.js +90 -0
  9. package/dist/ai/broker/broker.js.map +1 -1
  10. package/dist/ai/broker/gemini-engine.d.ts +14 -1
  11. package/dist/ai/broker/gemini-engine.d.ts.map +1 -1
  12. package/dist/ai/broker/gemini-engine.js +30 -9
  13. package/dist/ai/broker/gemini-engine.js.map +1 -1
  14. package/dist/ai/broker/index.d.ts +2 -2
  15. package/dist/ai/broker/index.d.ts.map +1 -1
  16. package/dist/ai/broker/index.js +2 -2
  17. package/dist/ai/broker/index.js.map +1 -1
  18. package/dist/ai/broker/openai-engine.d.ts +10 -1
  19. package/dist/ai/broker/openai-engine.d.ts.map +1 -1
  20. package/dist/ai/broker/openai-engine.js +26 -4
  21. package/dist/ai/broker/openai-engine.js.map +1 -1
  22. package/dist/ai/broker/synthesizer.d.ts +14 -0
  23. package/dist/ai/broker/synthesizer.d.ts.map +1 -1
  24. package/dist/ai/broker/synthesizer.js +31 -0
  25. package/dist/ai/broker/synthesizer.js.map +1 -1
  26. package/dist/ai/broker/types.d.ts +24 -0
  27. package/dist/ai/broker/types.d.ts.map +1 -1
  28. package/dist/ai/internal.d.ts +5 -0
  29. package/dist/ai/internal.d.ts.map +1 -1
  30. package/dist/ai/internal.js +5 -0
  31. package/dist/ai/internal.js.map +1 -1
  32. package/dist/ai/response-helpers.d.ts.map +1 -1
  33. package/dist/ai/response-helpers.js +6 -24
  34. package/dist/ai/response-helpers.js.map +1 -1
  35. package/dist/analytics/index.d.ts +40 -0
  36. package/dist/analytics/index.d.ts.map +1 -0
  37. package/dist/analytics/index.js +25 -0
  38. package/dist/analytics/index.js.map +1 -0
  39. package/dist/app-check/index.d.ts +31 -0
  40. package/dist/app-check/index.d.ts.map +1 -0
  41. package/dist/app-check/index.js +20 -0
  42. package/dist/app-check/index.js.map +1 -0
  43. package/dist/database/controls.d.ts +1 -1
  44. package/dist/database/controls.js +1 -1
  45. package/dist/database/sandbox/rules-eval.d.ts +1 -1
  46. package/dist/database/sandbox/rules-eval.d.ts.map +1 -1
  47. package/dist/database/sandbox/rules-eval.js +2 -2
  48. package/dist/database/sandbox/rules-eval.js.map +1 -1
  49. package/dist/database/sandbox/write-plane.js +2 -2
  50. package/dist/database/sandbox/write-plane.js.map +1 -1
  51. package/dist/database/sandbox-controls.d.ts +12 -5
  52. package/dist/database/sandbox-controls.d.ts.map +1 -1
  53. package/dist/database/sandbox-controls.js +20 -9
  54. package/dist/database/sandbox-controls.js.map +1 -1
  55. package/dist/deferred/entry.d.ts +75 -0
  56. package/dist/deferred/entry.d.ts.map +1 -0
  57. package/dist/deferred/entry.js +143 -0
  58. package/dist/deferred/entry.js.map +1 -0
  59. package/dist/firestore/lite.d.ts +72 -0
  60. package/dist/firestore/lite.d.ts.map +1 -0
  61. package/dist/firestore/lite.js +22 -0
  62. package/dist/firestore/lite.js.map +1 -0
  63. package/dist/firestore/persistence.d.ts +2 -2
  64. package/dist/firestore/persistence.js +1 -1
  65. package/dist/functions/index.d.ts +29 -0
  66. package/dist/functions/index.d.ts.map +1 -0
  67. package/dist/functions/index.js +20 -0
  68. package/dist/functions/index.js.map +1 -0
  69. package/dist/performance/index.d.ts +22 -0
  70. package/dist/performance/index.d.ts.map +1 -0
  71. package/dist/performance/index.js +19 -0
  72. package/dist/performance/index.js.map +1 -0
  73. package/dist/remote-config/index.d.ts +40 -0
  74. package/dist/remote-config/index.d.ts.map +1 -0
  75. package/dist/remote-config/index.js +25 -0
  76. package/dist/remote-config/index.js.map +1 -0
  77. package/dist/rules/grammar/FirestoreValidator.d.ts.map +1 -1
  78. package/dist/rules/grammar/FirestoreValidator.js +43 -27
  79. package/dist/rules/grammar/FirestoreValidator.js.map +1 -1
  80. package/dist/rules/grammar/document-access-count.d.ts +24 -0
  81. package/dist/rules/grammar/document-access-count.d.ts.map +1 -0
  82. package/dist/rules/grammar/document-access-count.js +100 -0
  83. package/dist/rules/grammar/document-access-count.js.map +1 -0
  84. package/dist/rules/linter/ast-utils.d.ts +2 -6
  85. package/dist/rules/linter/ast-utils.d.ts.map +1 -1
  86. package/dist/rules/linter/ast-utils.js +2 -63
  87. package/dist/rules/linter/ast-utils.js.map +1 -1
  88. package/dist/rules/linter/hallucinations.d.ts +8 -0
  89. package/dist/rules/linter/hallucinations.d.ts.map +1 -1
  90. package/dist/rules/linter/hallucinations.js +121 -31
  91. package/dist/rules/linter/hallucinations.js.map +1 -1
  92. package/dist/rules/linter/linter.d.ts +10 -1
  93. package/dist/rules/linter/linter.d.ts.map +1 -1
  94. package/dist/rules/linter/linter.js +23 -9
  95. package/dist/rules/linter/linter.js.map +1 -1
  96. package/dist/rules/rtdb/grammar/simulator.d.ts.map +1 -1
  97. package/dist/rules/rtdb/grammar/simulator.js +13 -13
  98. package/dist/rules/rtdb/grammar/simulator.js.map +1 -1
  99. package/dist/rules/rtdb/simulation/handler.d.ts.map +1 -1
  100. package/dist/rules/rtdb/simulation/handler.js +96 -35
  101. package/dist/rules/rtdb/simulation/handler.js.map +1 -1
  102. package/dist/rules/simulator/document-lookups.d.ts +4 -0
  103. package/dist/rules/simulator/document-lookups.d.ts.map +1 -1
  104. package/dist/rules/simulator/document-lookups.js +42 -3
  105. package/dist/rules/simulator/document-lookups.js.map +1 -1
  106. package/dist/rules/simulator/eval-error.d.ts +11 -0
  107. package/dist/rules/simulator/eval-error.d.ts.map +1 -1
  108. package/dist/rules/simulator/eval-error.js +14 -0
  109. package/dist/rules/simulator/eval-error.js.map +1 -1
  110. package/dist/rules/simulator/evaluation-builtins.d.ts.map +1 -1
  111. package/dist/rules/simulator/evaluation-builtins.js +33 -8
  112. package/dist/rules/simulator/evaluation-builtins.js.map +1 -1
  113. package/dist/rules/simulator/evaluation-context.d.ts +12 -0
  114. package/dist/rules/simulator/evaluation-context.d.ts.map +1 -1
  115. package/dist/rules/simulator/evaluator.d.ts +1 -1
  116. package/dist/rules/simulator/evaluator.d.ts.map +1 -1
  117. package/dist/rules/simulator/evaluator.js +16 -4
  118. package/dist/rules/simulator/evaluator.js.map +1 -1
  119. package/dist/rules/simulator/handler.d.ts.map +1 -1
  120. package/dist/rules/simulator/handler.js +52 -5
  121. package/dist/rules/simulator/handler.js.map +1 -1
  122. package/dist/rules/simulator/lookup-budget.d.ts +37 -0
  123. package/dist/rules/simulator/lookup-budget.d.ts.map +1 -0
  124. package/dist/rules/simulator/lookup-budget.js +96 -0
  125. package/dist/rules/simulator/lookup-budget.js.map +1 -0
  126. package/dist/rules/stdlib-modules.d.ts +7 -0
  127. package/dist/rules/stdlib-modules.d.ts.map +1 -1
  128. package/dist/rules/stdlib-modules.js +11 -11
  129. package/dist/rules/stdlib-modules.js.map +1 -1
  130. package/dist/sandbox/admin-firestore/get-firestore.d.ts +6 -0
  131. package/dist/sandbox/admin-firestore/get-firestore.d.ts.map +1 -1
  132. package/dist/sandbox/admin-firestore/get-firestore.js +22 -3
  133. package/dist/sandbox/admin-firestore/get-firestore.js.map +1 -1
  134. package/dist/sandbox/index.d.ts +1 -1
  135. package/dist/sandbox/index.d.ts.map +1 -1
  136. package/dist/sandbox/index.js +1 -1
  137. package/dist/sandbox/index.js.map +1 -1
  138. package/dist/sandbox/persistence/types.d.ts +1 -1
  139. package/dist/sandbox/remote.d.ts +2 -2
  140. package/dist/sandbox/sandbox-context.d.ts +8 -1
  141. package/dist/sandbox/sandbox-context.d.ts.map +1 -1
  142. package/dist/sandbox/sandbox-context.js +59 -8
  143. package/dist/sandbox/sandbox-context.js.map +1 -1
  144. package/dist/sandbox/types/auth-state.d.ts +1 -0
  145. package/dist/sandbox/types/auth-state.d.ts.map +1 -1
  146. package/dist/sandbox/types/events.d.ts +2 -1
  147. package/dist/sandbox/types/events.d.ts.map +1 -1
  148. package/dist/sandbox/types/operation.d.ts +1 -0
  149. package/dist/sandbox/types/operation.d.ts.map +1 -1
  150. package/dist/storage/enforce.d.ts +20 -1
  151. package/dist/storage/enforce.d.ts.map +1 -1
  152. package/dist/storage/enforce.js +47 -14
  153. package/dist/storage/enforce.js.map +1 -1
  154. package/dist/storage/index.d.ts +1 -1
  155. package/dist/storage/index.d.ts.map +1 -1
  156. package/dist/storage/list.js +1 -1
  157. package/dist/storage/list.js.map +1 -1
  158. package/dist/storage/sandbox/rules-evaluation-error.d.ts +25 -0
  159. package/dist/storage/sandbox/rules-evaluation-error.d.ts.map +1 -1
  160. package/dist/storage/sandbox/rules-evaluation-error.js +29 -0
  161. package/dist/storage/sandbox/rules-evaluation-error.js.map +1 -1
  162. package/dist/storage/sandbox/rules-evaluator.d.ts +10 -5
  163. package/dist/storage/sandbox/rules-evaluator.d.ts.map +1 -1
  164. package/dist/storage/sandbox/rules-evaluator.js +105 -33
  165. package/dist/storage/sandbox/rules-evaluator.js.map +1 -1
  166. package/dist/storage/sandbox/rules-methods.d.ts.map +1 -1
  167. package/dist/storage/sandbox/rules-methods.js +17 -7
  168. package/dist/storage/sandbox/rules-methods.js.map +1 -1
  169. package/dist/storage/service.d.ts +23 -2
  170. package/dist/storage/service.d.ts.map +1 -1
  171. package/dist/storage/service.js +36 -26
  172. package/dist/storage/service.js.map +1 -1
  173. package/package.json +41 -1
  174. package/src/ai/blocked.ts +97 -0
  175. package/src/ai/broker/broker.ts +88 -0
  176. package/src/ai/broker/gemini-engine.ts +36 -9
  177. package/src/ai/broker/index.ts +2 -1
  178. package/src/ai/broker/openai-engine.ts +31 -3
  179. package/src/ai/broker/synthesizer.ts +34 -0
  180. package/src/ai/broker/types.ts +25 -0
  181. package/src/ai/internal.ts +5 -0
  182. package/src/ai/response-helpers.ts +6 -25
  183. package/src/analytics/index.ts +53 -0
  184. package/src/app/dispatch.test.ts +3 -1
  185. package/src/app-check/index.ts +41 -0
  186. package/src/database/controls.ts +1 -1
  187. package/src/database/sandbox/rules-eval.ts +2 -2
  188. package/src/database/sandbox/write-plane.ts +2 -2
  189. package/src/database/sandbox-controls.ts +29 -9
  190. package/src/deferred/entry.ts +167 -0
  191. package/src/firestore/lite.ts +91 -0
  192. package/src/firestore/persistence.ts +2 -2
  193. package/src/functions/index.ts +38 -0
  194. package/src/performance/index.ts +31 -0
  195. package/src/remote-config/index.ts +53 -0
  196. package/src/rules/grammar/FirestoreValidator.ts +44 -28
  197. package/src/rules/grammar/document-access-count.ts +75 -0
  198. package/src/rules/linter/ast-utils.ts +2 -36
  199. package/src/rules/linter/hallucinations.ts +123 -25
  200. package/src/rules/linter/linter.ts +32 -10
  201. package/src/rules/rtdb/grammar/simulator.ts +12 -13
  202. package/src/rules/rtdb/simulation/handler.ts +149 -46
  203. package/src/rules/simulator/document-lookups.ts +46 -3
  204. package/src/rules/simulator/eval-error.ts +16 -0
  205. package/src/rules/simulator/evaluation-builtins.ts +35 -8
  206. package/src/rules/simulator/evaluation-context.ts +12 -0
  207. package/src/rules/simulator/evaluator.ts +14 -3
  208. package/src/rules/simulator/handler.ts +68 -4
  209. package/src/rules/simulator/lookup-budget.ts +101 -0
  210. package/src/rules/stdlib-modules.ts +19 -11
  211. package/src/sandbox/admin-firestore/get-firestore.ts +19 -3
  212. package/src/sandbox/index.ts +5 -1
  213. package/src/sandbox/persistence/types.ts +1 -1
  214. package/src/sandbox/remote.ts +2 -2
  215. package/src/sandbox/sandbox-context.ts +69 -9
  216. package/src/sandbox/types/auth-state.ts +1 -1
  217. package/src/sandbox/types/events.ts +2 -1
  218. package/src/sandbox/types/operation.ts +1 -1
  219. package/src/storage/enforce.ts +53 -14
  220. package/src/storage/index.ts +1 -1
  221. package/src/storage/list.ts +1 -1
  222. package/src/storage/sandbox/rules-evaluation-error.ts +32 -0
  223. package/src/storage/sandbox/rules-evaluator.ts +109 -32
  224. package/src/storage/sandbox/rules-methods.ts +21 -7
  225. package/src/storage/service.ts +92 -27
@@ -92,6 +92,20 @@ export interface FirebaseStorage {
92
92
  /** Storage handle returned by Firebase-shaped app overloads. */
93
93
  export type AppFirebaseStorage = FirebaseStorage & { readonly app: FirebaseApp };
94
94
 
95
+ /**
96
+ * Cross-service IAM posture for `firestore.get()/exists()` in Storage rules.
97
+ *
98
+ * Production Storage rules can read Firestore ONLY when the project's
99
+ * Storage service agent holds `roles/firebaserules.firestoreServiceAgent`.
100
+ * `'granted'` (the default, the common configured-project state) serves
101
+ * lookups from the same-sandbox Firestore store; `'denied'` makes every
102
+ * EXECUTED lookup fail exactly like production without the role (error →
103
+ * rule denies), while short-circuited lookups are never executed and stay
104
+ * unaffected. Captured boundary: conformance observation
105
+ * `stdlib-realstorage-p3-lookup-budget` (registry row storage-rules#134).
106
+ */
107
+ export type CrossServiceIam = 'granted' | 'denied';
108
+
95
109
  /**
96
110
  * Internal sandbox service — owns the IDB connection + parsed rules.
97
111
  * Only constructed inside the sandbox `getStorageSandbox` path.
@@ -100,6 +114,7 @@ export class StorageService {
100
114
  constructor(
101
115
  readonly backend: StorageBackend,
102
116
  readonly rules: StorageRules | null = null,
117
+ readonly crossServiceIam: CrossServiceIam = 'granted',
103
118
  ) {}
104
119
  }
105
120
 
@@ -124,7 +139,7 @@ export interface StorageOptions {
124
139
  * origin-scoped, so without this every project served on the same
125
140
  * localhost port shared one storage database (issue #359). Ignored when
126
141
  * an explicit `dbName` is given; only honored on the FIRST call per
127
- * `Sandbox`. Hosts pass their project identity here (`pyric dev` passes
142
+ * `Sandbox`. Hosts pass their project identity here (`pyric sandbox` passes
128
143
  * the served project's key; app handles pass `options.projectId`).
129
144
  */
130
145
  projectId?: string;
@@ -134,12 +149,36 @@ export interface StorageOptions {
134
149
  * `Sandbox`.
135
150
  */
136
151
  rules?: string;
152
+ /**
153
+ * Model the production Storage→Firestore IAM dependency (see
154
+ * {@link CrossServiceIam}). Defaults to `'granted'`. Like `rules`,
155
+ * only honored on the FIRST storage call per `Sandbox`; a LATER,
156
+ * DIFFERING value throws instead of silently flipping the mode.
157
+ */
158
+ crossServiceIam?: CrossServiceIam;
137
159
  }
138
160
 
139
161
  // ─── Caches ─────────────────────────────────────────────────────────
140
162
 
141
- /** One `StorageService` per `Sandbox`. */
142
- const SERVICES = new WeakMap<Sandbox, Promise<StorageService>>();
163
+ /**
164
+ * Everything one `Sandbox` opened its storage service WITH, in one record.
165
+ * These four facts are written together on the first storage call and read
166
+ * together afterwards, so they live in one map rather than four parallel
167
+ * ones that can drift out of step.
168
+ */
169
+ interface OpenStorageService {
170
+ /** The service itself, resolving once its IndexedDB backend is open. */
171
+ readonly service: Promise<StorageService>;
172
+ /** The rules source it was opened with, `null` when opened without rules. */
173
+ readonly rulesSource: string | null;
174
+ /** The module-resolution record for that source, `null` without rules. */
175
+ readonly rulesResolution: StorageRulesResolution | null;
176
+ /** The cross-service IAM mode it was opened with. */
177
+ readonly crossServiceIam: CrossServiceIam;
178
+ }
179
+
180
+ /** One open storage service, with its opening configuration, per `Sandbox`. */
181
+ const OPEN_SERVICES = new WeakMap<Sandbox, OpenStorageService>();
143
182
 
144
183
  /** One sandbox `FirebaseStorage` handle per `SandboxContext`. */
145
184
  const SANDBOX_HANDLES = new WeakMap<SandboxContext, FirebaseStorage>();
@@ -155,15 +194,32 @@ const SANDBOX_HANDLES = new WeakMap<SandboxContext, FirebaseStorage>();
155
194
  const BARE_SANDBOX_HANDLES = new WeakMap<Sandbox, FirebaseStorage>();
156
195
 
157
196
  /**
158
- * The rules SOURCE each sandbox's service was opened with (`null` when
159
- * opened without rules). Late-config detection: rules are honored only on
160
- * the FIRST storage call per `Sandbox`, so silently discarding a LATER,
161
- * DIFFERENT `rules` option would be a silent rules wipe — {@link
162
- * ensureService} throws instead. Re-supplying the IDENTICAL source stays
163
- * fine (idempotent multi-handle construction, e.g. per-user contexts).
197
+ * Reject a late option that differs from the one the service is already
198
+ * open with. Both `rules` and `crossServiceIam` are honored only on the
199
+ * FIRST storage call per `Sandbox`. Silently discarding a later, differing
200
+ * value would wipe the rules, or reopen the false-ALLOW surface the IAM
201
+ * mode models, without a word. Re-supplying the IDENTICAL value stays fine
202
+ * (idempotent multi-handle construction, for example per-user contexts),
203
+ * and so does omitting it.
164
204
  */
165
- const SERVICE_RULES_SOURCE = new WeakMap<Sandbox, string | null>();
166
- const SERVICE_RULES_RESOLUTION = new WeakMap<Sandbox, StorageRulesResolution | null>();
205
+ function rejectDifferingLateConfig(
206
+ option: string,
207
+ descriptor: string,
208
+ supplied: string | undefined,
209
+ openedWith: string | null,
210
+ caller: string,
211
+ ): void {
212
+ if (supplied === undefined || supplied === openedWith) return;
213
+ const openedDescription = openedWith === null
214
+ ? `without ${option}`
215
+ : `with a different ${descriptor}`;
216
+ throw new Error(
217
+ `pyric/storage: ${caller} received a ${descriptor}, but this sandbox's storage service ` +
218
+ `is already open ${openedDescription}. ${option} is honored only on the FIRST storage ` +
219
+ 'call per Sandbox, so this value would be silently discarded. Configure it on the first ' +
220
+ 'storage call for this sandbox.',
221
+ );
222
+ }
167
223
 
168
224
  /**
169
225
  * Get (or open) the ONE per-sandbox `StorageService`. Loud on the
@@ -177,18 +233,23 @@ function ensureService(
177
233
  options: StorageOptions,
178
234
  caller: string,
179
235
  ): Promise<StorageService> {
180
- const existing = SERVICES.get(sandbox);
236
+ const existing = OPEN_SERVICES.get(sandbox);
181
237
  if (existing) {
182
- const openedWith = SERVICE_RULES_SOURCE.get(sandbox) ?? null;
183
- if (options.rules !== undefined && options.rules !== openedWith) {
184
- throw new Error(
185
- `pyric/storage: ${caller} received a rules source, but this sandbox's storage ` +
186
- `service is already open ${openedWith === null ? 'without rules' : 'with a different rules source'} ` +
187
- '— rules are honored only on the FIRST storage call per Sandbox, so these rules ' +
188
- 'would be silently discarded. Configure rules on the first storage call for this sandbox.',
189
- );
190
- }
191
- return existing;
238
+ rejectDifferingLateConfig(
239
+ 'rules',
240
+ 'rules source',
241
+ options.rules,
242
+ existing.rulesSource,
243
+ caller,
244
+ );
245
+ rejectDifferingLateConfig(
246
+ 'crossServiceIam',
247
+ 'crossServiceIam mode',
248
+ options.crossServiceIam,
249
+ existing.crossServiceIam,
250
+ caller,
251
+ );
252
+ return existing.service;
192
253
  }
193
254
  let rules: StorageRules | null = null;
194
255
  let resolution: StorageRulesResolution | null = null;
@@ -221,14 +282,18 @@ function ensureService(
221
282
  }
222
283
  // Explicit dbName wins; otherwise scope the default by project identity so
223
284
  // two projects on one origin never share a storage database (issue #359).
285
+ const crossServiceIam = options.crossServiceIam ?? 'granted';
224
286
  const servicePromise = openStorageBackend(
225
287
  options.dbName ?? storageDbName(options.projectId),
226
288
  ).then(
227
- (backend) => new StorageService(backend, rules),
289
+ (backend) => new StorageService(backend, rules, crossServiceIam),
228
290
  );
229
- SERVICES.set(sandbox, servicePromise);
230
- SERVICE_RULES_SOURCE.set(sandbox, options.rules ?? null);
231
- SERVICE_RULES_RESOLUTION.set(sandbox, resolution);
291
+ OPEN_SERVICES.set(sandbox, {
292
+ service: servicePromise,
293
+ rulesSource: options.rules ?? null,
294
+ rulesResolution: resolution,
295
+ crossServiceIam,
296
+ });
232
297
  // Join the sandbox's persistable-service REGISTRY so `sandbox.resetAll()`
233
298
  // reaches storage (issue #359: Studio's reset cleared Firestore + auth but
234
299
  // never storage — storage was invisible to the sandbox). Storage does NOT
@@ -428,5 +493,5 @@ export function getStorageService(storage: FirebaseStorage): Promise<StorageServ
428
493
  export function getStorageRulesResolution(
429
494
  storage: FirebaseStorage,
430
495
  ): StorageRulesResolution | null {
431
- return SERVICE_RULES_RESOLUTION.get(targetOf(storage).sandbox) ?? null;
496
+ return OPEN_SERVICES.get(targetOf(storage).sandbox)?.rulesResolution ?? null;
432
497
  }