halt-rate 0.3.0 → 0.4.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.mjs CHANGED
@@ -484,7 +484,8 @@ var RateLimiter = class {
484
484
  return decision2;
485
485
  }
486
486
  const store = this.store;
487
- let algorithm = this.algorithmCache.get(policy.name);
487
+ const cacheKey = `${policy.name}|${policy.algorithm}|${policy.limit}|${policy.window}|${policy.burst}`;
488
+ let algorithm = this.algorithmCache.get(cacheKey);
488
489
  if (!algorithm) {
489
490
  if (policy.algorithm === "token_bucket" /* TOKEN_BUCKET */) {
490
491
  algorithm = new TokenBucket(policy.burst, policy.limit, policy.window);
@@ -498,7 +499,7 @@ var RateLimiter = class {
498
499
  } else {
499
500
  throw new Error(`Algorithm ${policy.algorithm} not implemented`);
500
501
  }
501
- this.algorithmCache.set(policy.name, algorithm);
502
+ this.algorithmCache.set(cacheKey, algorithm);
502
503
  }
503
504
  const span = this.otelTracer?.startSpan?.("halt.check", { attributes: { policy: policy.name, key } });
504
505
  const state = store.get(storageKey);
@@ -1093,6 +1094,70 @@ function getPlanPolicy(planName) {
1093
1094
  return PLAN_TIERS[normalized];
1094
1095
  }
1095
1096
 
1097
+ // src/core/registry.ts
1098
+ var PolicyRegistry = class {
1099
+ constructor(initial = []) {
1100
+ this.policies = /* @__PURE__ */ new Map();
1101
+ for (const p of initial) this.register(p);
1102
+ }
1103
+ /** Add or replace a policy (keyed by `policy.name`). */
1104
+ register(policy) {
1105
+ this.policies.set(policy.name, policy);
1106
+ return this;
1107
+ }
1108
+ get(name) {
1109
+ return this.policies.get(name);
1110
+ }
1111
+ has(name) {
1112
+ return this.policies.has(name);
1113
+ }
1114
+ /** Mutate fields of an existing policy at runtime (e.g. `{ limit: 500 }`). */
1115
+ update(name, patch) {
1116
+ const existing = this.policies.get(name);
1117
+ if (!existing) throw new Error(`Unknown policy: ${name}`);
1118
+ const updated = { ...existing, ...patch, name: existing.name };
1119
+ if (patch.limit !== void 0 && patch.burst === void 0) {
1120
+ delete updated.burst;
1121
+ }
1122
+ this.policies.set(name, updated);
1123
+ return updated;
1124
+ }
1125
+ remove(name) {
1126
+ return this.policies.delete(name);
1127
+ }
1128
+ list() {
1129
+ return [...this.policies.values()];
1130
+ }
1131
+ /**
1132
+ * Build a resolver for the limiter's `policy` option.
1133
+ * @param selector maps a request to a registered policy name.
1134
+ */
1135
+ resolver(selector) {
1136
+ return (request) => {
1137
+ const name = selector(request);
1138
+ const policy = this.policies.get(name);
1139
+ if (!policy) throw new Error(`Unknown policy: ${name}`);
1140
+ return policy;
1141
+ };
1142
+ }
1143
+ };
1144
+ function cachedPolicyResolver(loader, options = {}) {
1145
+ const ttlMs = options.ttlMs ?? 5e3;
1146
+ const keyFn = options.key ?? (() => "__default__");
1147
+ const cache = /* @__PURE__ */ new Map();
1148
+ return async (request) => {
1149
+ const key = keyFn(request);
1150
+ const now = Date.now();
1151
+ const hit = cache.get(key);
1152
+ if (hit && hit.expires > now) {
1153
+ return hit.policy;
1154
+ }
1155
+ const policy = await loader(request);
1156
+ cache.set(key, { policy, expires: now + ttlMs });
1157
+ return policy;
1158
+ };
1159
+ }
1160
+
1096
1161
  // src/core/quota.ts
1097
1162
  var QuotaPeriod = /* @__PURE__ */ ((QuotaPeriod2) => {
1098
1163
  QuotaPeriod2["HOURLY"] = "hourly";
@@ -1611,6 +1676,6 @@ var OpenTelemetryMetrics = class {
1611
1676
  }
1612
1677
  };
1613
1678
 
1614
- export { Algorithm, CompositeTelemetry, InMemoryStore, KeyStrategy, LoggingTelemetry, MetricsTelemetry, OpenTelemetryMetrics, PENALTY_LENIENT, PENALTY_MODERATE, PENALTY_STRICT, PenaltyManager, QUOTA_ENTERPRISE_MONTHLY, QUOTA_FREE_DAILY, QUOTA_FREE_MONTHLY, QUOTA_PRO_DAILY, QUOTA_PRO_MONTHLY, QuotaManager, QuotaPeriod, RateLimiter, RedisStore, StatsCollector, extractors_exports as extractors, isAtomicStore, normalizePolicy, presets_exports as presets, toHeaders };
1679
+ export { Algorithm, CompositeTelemetry, InMemoryStore, KeyStrategy, LoggingTelemetry, MetricsTelemetry, OpenTelemetryMetrics, PENALTY_LENIENT, PENALTY_MODERATE, PENALTY_STRICT, PenaltyManager, PolicyRegistry, QUOTA_ENTERPRISE_MONTHLY, QUOTA_FREE_DAILY, QUOTA_FREE_MONTHLY, QUOTA_PRO_DAILY, QUOTA_PRO_MONTHLY, QuotaManager, QuotaPeriod, RateLimiter, RedisStore, StatsCollector, cachedPolicyResolver, extractors_exports as extractors, isAtomicStore, normalizePolicy, presets_exports as presets, toHeaders };
1615
1680
  //# sourceMappingURL=index.mjs.map
1616
1681
  //# sourceMappingURL=index.mjs.map