@rulvar/core 1.111.0 → 1.112.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -3169,6 +3169,15 @@ declare function quotaRuleKey(rule: QuotaRule): string;
3169
3169
  * so ordinary JavaScript after the constructor (a pushed rule, a
3170
3170
  * reassigned cap) can no longer change a decision, a bucket key, or a
3171
3171
  * recorded fingerprint.
3172
+ *
3173
+ * A set containing two rules with the same canonical content key is
3174
+ * refused typed (RV704): the memory reference buckets by rule INDEX
3175
+ * (each copy counts independently, the full cap admits) while the
3176
+ * store references bucket by rule KEY (one shared bucket is debited
3177
+ * once per matching copy, half the cap admits), so the same duplicated
3178
+ * configuration admitted differently per storage. Refusing it at the
3179
+ * shared construction chokepoint is what keeps equal configurations
3180
+ * equal on every storage.
3172
3181
  */
3173
3182
  declare function snapshotQuotaRules(rules: readonly QuotaRule[], site?: string): readonly QuotaRule[];
3174
3183
  /** True when every dimension the rule pins matches the request. */
package/dist/index.js CHANGED
@@ -9286,9 +9286,25 @@ function quotaRuleKey(rule) {
9286
9286
  * so ordinary JavaScript after the constructor (a pushed rule, a
9287
9287
  * reassigned cap) can no longer change a decision, a bucket key, or a
9288
9288
  * recorded fingerprint.
9289
+ *
9290
+ * A set containing two rules with the same canonical content key is
9291
+ * refused typed (RV704): the memory reference buckets by rule INDEX
9292
+ * (each copy counts independently, the full cap admits) while the
9293
+ * store references bucket by rule KEY (one shared bucket is debited
9294
+ * once per matching copy, half the cap admits), so the same duplicated
9295
+ * configuration admitted differently per storage. Refusing it at the
9296
+ * shared construction chokepoint is what keeps equal configurations
9297
+ * equal on every storage.
9289
9298
  */
9290
9299
  function snapshotQuotaRules(rules, site = "quota rules") {
9291
9300
  validateQuotaRules(rules, site);
9301
+ const firstIndexByKey = /* @__PURE__ */ new Map();
9302
+ rules.forEach((rule, index) => {
9303
+ const key = quotaRuleKey(rule);
9304
+ const first = firstIndexByKey.get(key);
9305
+ if (first !== void 0) throw new ConfigError(`${site}[${String(index)}] duplicates ${site}[${String(first)}] (rule key ${key}): identical rules occupy independent buckets in memory but share one key-debited bucket on keyed storage, so one configuration would admit differently per store; delete the duplicate`);
9306
+ firstIndexByKey.set(key, index);
9307
+ });
9292
9308
  return Object.freeze(rules.map((rule) => Object.freeze({
9293
9309
  ...rule.provider === void 0 ? {} : { provider: rule.provider },
9294
9310
  ...rule.model === void 0 ? {} : { model: rule.model },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rulvar/core",
3
- "version": "1.111.0",
3
+ "version": "1.112.0",
4
4
  "description": "Rulvar core: L0 contracts, journal kernel, ctx primitives, agent runtime, model router, tool system, dynamic orchestrator, InMemory and JSONL stores, event stream.",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",