@voltro/plugin-flags 0.54.0 → 0.56.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.js CHANGED
@@ -1,25 +1,25 @@
1
- import { evaluateDescriptor as e, evaluateVariantsDescriptor as t, flagsRpcClientImports as n } from "./rpc.js";
2
- import { i as r, n as i, r as a, t as o } from "./define-D9Eet7wA.js";
3
- import { FlagDisabled as s } from "./errors.js";
4
- import { Effect as c } from "effect";
5
- import { definePlugin as l, pluginInstanceName as u } from "@voltro/protocol";
6
- import { boolean as d, derivedRowId as f, id as p, integer as m, json as h, queryFor as g, registerRetention as _, retentionTtlMsFromEnv as v, table as y, text as b, timestamp as x } from "@voltro/database";
1
+ import { BASE_NAME as e, evaluateDescriptor as t, evaluateVariantsDescriptor as n, flagsRpcClientImports as r } from "./rpc.js";
2
+ import { i, n as a, r as o, t as s } from "./define-D9Eet7wA.js";
3
+ import { FlagDisabled as c } from "./errors.js";
4
+ import { Effect as l } from "effect";
5
+ import { definePlugin as u, pluginInstanceName as d } from "@voltro/protocol";
6
+ import { boolean as f, derivedRowId as p, id as m, integer as h, json as g, queryFor as _, registerRetention as v, retentionTtlMsFromEnv as y, table as b, text as x, timestamp as S } from "@voltro/database";
7
7
  //#region src/evaluate.ts
8
- var S = (e) => typeof e == "boolean" ? { enabled: e } : e, C = (e, t) => {
8
+ var C = (e) => typeof e == "boolean" ? { enabled: e } : e, w = (e, t) => {
9
9
  let n = 2166136261, r = `${e}:${t}`;
10
10
  for (let e = 0; e < r.length; e++) n ^= r.charCodeAt(e), n = Math.imul(n, 16777619);
11
11
  return n >>> 0;
12
- }, ee = (e, t) => C(e, t) % 100, w = (e, t) => C(`${e}:variant`, t) % 1e4, T = (e, t) => {
12
+ }, T = (e, t) => w(e, t) % 100, ee = (e, t) => w(`${e}:variant`, t) % 1e4, E = (e, t) => {
13
13
  if (e.subjectIds && !(t.id != null && e.subjectIds.includes(t.id)) || e.tenantIds && !(t.tenantId != null && e.tenantIds.includes(t.tenantId)) || e.subjectTypes && !(t.type != null && e.subjectTypes.includes(t.type))) return !1;
14
14
  if (e.metadata) {
15
15
  let n = t.metadata ?? {};
16
16
  for (let [t, r] of Object.entries(e.metadata)) if (String(n[t]) !== r) return !1;
17
17
  }
18
18
  return !0;
19
- }, E = (e) => typeof e == "number" ? e : Date.parse(e), D = (e, t) => (e.rolloutBy === "tenant" ? t.tenantId : t.id) ?? "anon", O = (e, t) => {
20
- if (e.activateAt != null && t < E(e.activateAt) || e.deactivateAt != null && t >= E(e.deactivateAt)) return { active: !1 };
19
+ }, D = (e) => typeof e == "number" ? e : Date.parse(e), O = (e, t) => (e.rolloutBy === "tenant" ? t.tenantId : t.id) ?? "anon", k = (e, t) => {
20
+ if (e.activateAt != null && t < D(e.activateAt) || e.deactivateAt != null && t >= D(e.deactivateAt)) return { active: !1 };
21
21
  if (e.ramp) {
22
- let { from: n, to: r, startAt: i, endAt: a } = e.ramp, o = E(i), s = E(a);
22
+ let { from: n, to: r, startAt: i, endAt: a } = e.ramp, o = D(i), s = D(a);
23
23
  if (t <= o) return {
24
24
  active: !0,
25
25
  rollout: n
@@ -35,71 +35,71 @@ var S = (e) => typeof e == "boolean" ? { enabled: e } : e, C = (e, t) => {
35
35
  };
36
36
  }
37
37
  return { active: !0 };
38
- }, k = (e, t, n, r = Date.now()) => {
39
- let i = S(t);
38
+ }, A = (e, t, n, r = Date.now()) => {
39
+ let i = C(t);
40
40
  if (i.enabled === !1) return !1;
41
41
  let a;
42
42
  if (i.schedule) {
43
- let e = O(i.schedule, r);
43
+ let e = k(i.schedule, r);
44
44
  if (!e.active) return !1;
45
45
  a = e.rollout;
46
46
  }
47
- if (i.targeting && i.targeting.length > 0 && !i.targeting.some((e) => T(e, n))) return !1;
47
+ if (i.targeting && i.targeting.length > 0 && !i.targeting.some((e) => E(e, n))) return !1;
48
48
  let o = a ?? i.rollout ?? 100;
49
49
  if (o < 100 && o > 0) {
50
- if (!(ee(e, D(i, n)) < o)) return !1;
50
+ if (!(T(e, O(i, n)) < o)) return !1;
51
51
  } else if (o <= 0) return !1;
52
- return i.variants && i.variants.length > 0 && i.offVariant != null ? A(e, i, n).name !== i.offVariant : !0;
53
- }, te = (e) => {
52
+ return i.variants && i.variants.length > 0 && i.offVariant != null ? M(e, i, n).name !== i.offVariant : !0;
53
+ }, j = (e) => {
54
54
  let t = e.reduce((e, t) => e + (t.weight ?? 1), 0) || e.length, n = 0;
55
55
  return e.map((r, i) => (n += r.weight ?? 1, i === e.length - 1 ? 1e4 : Math.round(n / t * 1e4)));
56
- }, A = (e, t, n) => {
56
+ }, M = (e, t, n) => {
57
57
  let r = t.variants ?? [];
58
58
  if (r.length === 0) throw Error(`flag "${e}" has no variants`);
59
- let i = w(e, D(t, n)), a = te(r);
59
+ let i = ee(e, O(t, n)), a = j(r);
60
60
  for (let e = 0; e < r.length; e++) if (i < a[e]) return r[e];
61
61
  return r[r.length - 1];
62
- }, j = (e, t, n, r = Date.now()) => {
63
- let i = S(t), a = k(e, t, n, r);
62
+ }, N = (e, t, n, r = Date.now()) => {
63
+ let i = C(t), a = A(e, t, n, r);
64
64
  if (!i.variants || i.variants.length === 0) return {
65
65
  name: a ? "on" : "off",
66
66
  value: a,
67
67
  enabled: a
68
68
  };
69
- let o = A(e, i, n);
69
+ let o = M(e, i, n);
70
70
  return {
71
71
  name: o.name,
72
72
  value: o.value,
73
73
  enabled: a
74
74
  };
75
- }, ne = (e, t, n = Date.now()) => {
75
+ }, te = (e, t, n = Date.now()) => {
76
76
  let r = {};
77
- for (let [i, a] of Object.entries(e)) r[i] = k(i, a, t, n);
77
+ for (let [i, a] of Object.entries(e)) r[i] = A(i, a, t, n);
78
78
  return r;
79
- }, re = (e, t, n = Date.now()) => {
79
+ }, ne = (e, t, n = Date.now()) => {
80
80
  let r = {};
81
- for (let [i, a] of Object.entries(e)) r[i] = j(i, a, t, n);
81
+ for (let [i, a] of Object.entries(e)) r[i] = N(i, a, t, n);
82
82
  return r;
83
- }, M = "_voltro_feature_flag_audit", N = "_voltro_feature_flags", P = y(N, {
84
- id: p({ prefix: "flag" }),
85
- key: b().unique(),
86
- enabled: d().default(!0),
87
- rollout: m().nullable(),
88
- rolloutBy: b().nullable(),
89
- targeting: h().nullable(),
90
- variants: h().nullable(),
91
- offVariant: b().nullable(),
92
- schedule: h().nullable(),
93
- description: b().nullable(),
94
- updatedAt: x().default("now").onUpdate("now")
95
- }), F = y(M, {
96
- id: p({ prefix: "flagaud" }),
97
- flag: b(),
98
- enabled: d(),
99
- previousEnabled: d().nullable(),
100
- actorId: b().nullable(),
101
- at: x().default("now")
102
- }).index("byFlagAuditFlag", ["flag"]).index("byFlagAuditAt", ["at"]), I = (e, t) => {
83
+ }, P = "_voltro_feature_flag_audit", F = "_voltro_feature_flags", I = b(F, {
84
+ id: m({ prefix: "flag" }),
85
+ key: x().unique(),
86
+ enabled: f().default(!0),
87
+ rollout: h().nullable(),
88
+ rolloutBy: x().nullable(),
89
+ targeting: g().nullable(),
90
+ variants: g().nullable(),
91
+ offVariant: x().nullable(),
92
+ schedule: g().nullable(),
93
+ description: x().nullable(),
94
+ updatedAt: S().default("now").onUpdate("now")
95
+ }), L = b(P, {
96
+ id: m({ prefix: "flagaud" }),
97
+ flag: x(),
98
+ enabled: f(),
99
+ previousEnabled: f().nullable(),
100
+ actorId: x().nullable(),
101
+ at: S().default("now")
102
+ }).index("byFlagAuditFlag", ["flag"]).index("byFlagAuditAt", ["at"]), re = (e, t) => {
103
103
  let n = { ...e };
104
104
  for (let e of t) {
105
105
  let t = {
@@ -115,7 +115,7 @@ var S = (e) => typeof e == "boolean" ? { enabled: e } : e, C = (e, t) => {
115
115
  n[e.key] = t;
116
116
  }
117
117
  return n;
118
- }, L = new class {
118
+ }, R = new class {
119
119
  flags = {};
120
120
  set(e) {
121
121
  this.flags = e;
@@ -126,19 +126,19 @@ var S = (e) => typeof e == "boolean" ? { enabled: e } : e, C = (e, t) => {
126
126
  get(e) {
127
127
  return this.flags[e];
128
128
  }
129
- }(), R = (e, t) => z(e, t) !== null, z = (e, t) => {
130
- let n = L.get(e);
129
+ }(), z = (e, t) => B(e, t) !== null, B = (e, t) => {
130
+ let n = R.get(e);
131
131
  if (n === void 0) return null;
132
- let r = S(n), i = r.enabled !== !1;
133
- return L.set({
134
- ...L.all(),
132
+ let r = C(n), i = r.enabled !== !1;
133
+ return R.set({
134
+ ...R.all(),
135
135
  [e]: {
136
136
  ...r,
137
137
  enabled: t
138
138
  }
139
139
  }), i;
140
- }, B = (e) => Object.entries(e).map(([e, t]) => {
141
- let n = S(t);
140
+ }, V = (e) => Object.entries(e).map(([e, t]) => {
141
+ let n = C(t);
142
142
  return {
143
143
  key: e,
144
144
  enabled: n.enabled !== !1,
@@ -148,7 +148,7 @@ var S = (e) => typeof e == "boolean" ? { enabled: e } : e, C = (e, t) => {
148
148
  description: n.description
149
149
  };
150
150
  }), ie = (e, t) => {
151
- let n = S(t);
151
+ let n = C(t);
152
152
  return {
153
153
  key: e,
154
154
  enabled: n.enabled !== !1,
@@ -161,7 +161,7 @@ var S = (e) => typeof e == "boolean" ? { enabled: e } : e, C = (e, t) => {
161
161
  description: n.description ?? null
162
162
  };
163
163
  }, ae = (e) => ({
164
- loadOverrides: async () => (await e.query(g(P).descriptor)).map((e) => ({
164
+ loadOverrides: async () => (await e.query(_(I).descriptor)).map((e) => ({
165
165
  key: String(e.key),
166
166
  enabled: e.enabled === !0 || e.enabled === 1,
167
167
  rollout: e.rollout == null ? null : Number(e.rollout),
@@ -174,7 +174,7 @@ var S = (e) => typeof e == "boolean" ? { enabled: e } : e, C = (e, t) => {
174
174
  })),
175
175
  upsertOverride: async (t) => {
176
176
  let n = {
177
- id: f("flag", t.key),
177
+ id: p("flag", t.key),
178
178
  key: t.key,
179
179
  enabled: t.enabled,
180
180
  rollout: t.rollout,
@@ -186,7 +186,7 @@ var S = (e) => typeof e == "boolean" ? { enabled: e } : e, C = (e, t) => {
186
186
  description: t.description,
187
187
  updatedAt: /* @__PURE__ */ new Date()
188
188
  };
189
- await e.upsert(N, n, { conflictColumns: ["key"] });
189
+ await e.upsert(F, n, { conflictColumns: ["key"] });
190
190
  }
191
191
  }), oe = (e) => ({
192
192
  flag: String(e.flag),
@@ -196,7 +196,7 @@ var S = (e) => typeof e == "boolean" ? { enabled: e } : e, C = (e, t) => {
196
196
  at: e.at instanceof Date ? e.at : new Date(String(e.at))
197
197
  }), se = (e) => ({
198
198
  record: async (t) => {
199
- await e.insert(M, {
199
+ await e.insert(P, {
200
200
  id: `flagaud_${t.at.getTime()}_${Math.random().toString(36).slice(2, 10)}`,
201
201
  flag: t.flag,
202
202
  enabled: t.enabled,
@@ -206,12 +206,12 @@ var S = (e) => typeof e == "boolean" ? { enabled: e } : e, C = (e, t) => {
206
206
  });
207
207
  },
208
208
  list: async (t) => {
209
- let n = g(F).orderBy("at", "desc");
209
+ let n = _(L).orderBy("at", "desc");
210
210
  t?.limit != null && (n = n.limit(t.limit));
211
211
  let r = (await e.query(n.descriptor)).map(oe);
212
212
  return t?.flag == null ? r : r.filter((e) => e.flag === t.flag);
213
213
  }
214
- }), V = (e, t) => {
214
+ }), H = (e, t) => {
215
215
  let n = new Map(t.map((e) => [e.name, e])), r = [];
216
216
  for (let t of e) {
217
217
  let e = t.experiment;
@@ -250,24 +250,24 @@ var S = (e) => typeof e == "boolean" ? { enabled: e } : e, C = (e, t) => {
250
250
  }
251
251
  }
252
252
  return r;
253
- }, H = (e, t) => {
254
- let n = V(e, t);
253
+ }, U = (e, t) => {
254
+ let n = H(e, t);
255
255
  if (n.length === 0) return;
256
256
  let r = n.map((e) => ` · flag "${e.flag}" → experiment "${e.experiment}": ${e.problem}`);
257
257
  throw Error(`@voltro/plugin-flags: ${n.length} flag→experiment link problem(s):\n${r.join("\n")}`);
258
- }, U = 864e5, W = (e) => typeof e == "number" ? e : Date.parse(e), G = (e, t) => {
259
- let n = S(e);
258
+ }, W = 864e5, G = (e) => typeof e == "number" ? e : Date.parse(e), K = (e, t) => {
259
+ let n = C(e);
260
260
  if (n.enabled === !1) return {
261
261
  shape: "constantOff",
262
262
  reason: "enabled: false — resolves false for every caller"
263
263
  };
264
264
  if (n.schedule) {
265
265
  let { activateAt: e, deactivateAt: r } = n.schedule;
266
- if (r != null && t >= W(r)) return {
266
+ if (r != null && t >= G(r)) return {
267
267
  shape: "expired",
268
268
  reason: `schedule.deactivateAt (${String(r)}) has passed — the flag can never be on again`
269
269
  };
270
- if (e != null && t < W(e)) return {
270
+ if (e != null && t < G(e)) return {
271
271
  shape: "notYetActive",
272
272
  reason: `schedule.activateAt (${String(e)}) is in the future — pending, not dead`
273
273
  };
@@ -277,7 +277,7 @@ var S = (e) => typeof e == "boolean" ? { enabled: e } : e, C = (e, t) => {
277
277
  shape: "constantOff",
278
278
  reason: `rollout: ${r} — nobody is in the rollout`
279
279
  };
280
- let i = (n.targeting?.length ?? 0) > 0, a = (n.variants?.length ?? 0) > 1, o = n.schedule !== void 0 && (n.schedule.ramp !== void 0 || n.schedule.deactivateAt != null && t < W(n.schedule.deactivateAt));
280
+ let i = (n.targeting?.length ?? 0) > 0, a = (n.variants?.length ?? 0) > 1, o = n.schedule !== void 0 && (n.schedule.ramp !== void 0 || n.schedule.deactivateAt != null && t < G(n.schedule.deactivateAt));
281
281
  return r >= 100 && !i && !a && !o ? {
282
282
  shape: "constantOn",
283
283
  reason: "rollout 100 with no targeting, no variants and no live schedule — resolves true for every caller"
@@ -290,14 +290,14 @@ var S = (e) => typeof e == "boolean" ? { enabled: e } : e, C = (e, t) => {
290
290
  o ? "a live schedule" : void 0
291
291
  ].filter((e) => e !== void 0).join(", ")})`
292
292
  };
293
- }, K = [
293
+ }, ce = [
294
294
  "Reachability is not decided. \"Not evaluated since <date>\" is a measurement; \"this code path is dead\" is not decidable in general. A seasonal flag, a flag behind a route nobody visited this month, and a flag whose last call site was deleted are indistinguishable from here.",
295
295
  "Only SERVER-side reads are counted as use: isFlagEnabled / requireFlag / flagValue and a gatedBy interception. A flag consulted only in the browser via useFlag() / useFlagValue() reads from the bulk set the server already sent, so it never reaches this report as a named read.",
296
296
  "A \"bulk\" observation means the flag was INCLUDED in a flags.evaluate / flags.variants response. It does not mean any client read that key — one useFlags() poll evaluates the whole registry.",
297
297
  "The window is bounded by retention (see coverage.retentionDays). Nothing older is evidence: a flag last read BEFORE the window has no observation at all and reads \"neverObserved\", which is exactly what a flag declared this morning reads.",
298
298
  "\"neverObserved\" is not a removal candidate. A flag declared yesterday and a flag abandoned last year produce the identical row; only a flag that WAS observed and then stopped (\"stale\") is proven unused."
299
- ], ce = (e) => {
300
- let t = e.now ?? Date.now(), n = e.staleAfterDays, r = t - n * U, i = /* @__PURE__ */ new Map(), a = /* @__PURE__ */ new Map(), o;
299
+ ], le = (e) => {
300
+ let t = e.now ?? Date.now(), n = e.staleAfterDays, r = t - n * W, i = /* @__PURE__ */ new Map(), a = /* @__PURE__ */ new Map(), o;
301
301
  for (let t of e.usage) {
302
302
  let e = t.lastAt.getTime();
303
303
  if (!Number.isFinite(e)) continue;
@@ -305,7 +305,7 @@ var S = (e) => typeof e == "boolean" ? { enabled: e } : e, C = (e, t) => {
305
305
  let n = t.source === "bulk" ? a : i, r = n.get(t.flag);
306
306
  (r === void 0 || e > r) && n.set(t.flag, e);
307
307
  }
308
- let s = o === void 0 ? 0 : Math.floor((t - o) / U);
308
+ let s = o === void 0 ? 0 : Math.floor((t - o) / W);
309
309
  return {
310
310
  coverage: {
311
311
  tracking: e.tracking,
@@ -315,7 +315,7 @@ var S = (e) => typeof e == "boolean" ? { enabled: e } : e, C = (e, t) => {
315
315
  staleAfterDays: n
316
316
  },
317
317
  findings: Object.entries(e.flags).map(([o, c]) => {
318
- let { shape: l, reason: u } = G(c, t), d = i.get(o), f = a.get(o), p = [u], m;
318
+ let { shape: l, reason: u } = K(c, t), d = i.get(o), f = a.get(o), p = [u], m;
319
319
  e.tracking ? d !== void 0 && d >= r ? (m = "evaluated", p.push(`last server-side read ${new Date(d).toISOString()}`)) : d === void 0 ? (m = "neverObserved", p.push(s > 0 ? `no server-side read in the ${s}-day observation window` : "no server-side read observed yet (the observation window is empty)"), f !== void 0 && p.push("the flag WAS delivered to a client in a bulk flags.evaluate response — which does not prove it was read")) : (m = "stale", p.push(`last server-side read ${new Date(d).toISOString()}, more than ${n} day(s) ago, over an observation window of ${s} day(s)`)) : (m = "untracked", p.push("evaluation tracking is off — no usage evidence exists for this deployment"));
320
320
  let h = l === "constantOn" || l === "constantOff" || l === "expired";
321
321
  return h && p.push("the definition alone makes this flag a constant — it selects nothing"), {
@@ -330,23 +330,23 @@ var S = (e) => typeof e == "boolean" ? { enabled: e } : e, C = (e, t) => {
330
330
  why: p
331
331
  };
332
332
  }),
333
- limits: K
333
+ limits: ce
334
334
  };
335
- }, q = "_voltro_feature_flag_usage", J = y(q, {
336
- id: p({ prefix: "flagusg" }),
337
- flag: b(),
338
- day: b(),
339
- source: b(),
340
- lastAt: x()
335
+ }, q = "_voltro_feature_flag_usage", J = b(q, {
336
+ id: m({ prefix: "flagusg" }),
337
+ flag: x(),
338
+ day: x(),
339
+ source: x(),
340
+ lastAt: S()
341
341
  }).unique([
342
342
  "flag",
343
343
  "day",
344
344
  "source"
345
- ]).index("byFlagUsageLastAt", ["lastAt"]).index("byFlagUsageFlag", ["flag"]), le = (e) => e.toISOString().slice(0, 10), ue = (e) => {
345
+ ]).index("byFlagUsageLastAt", ["lastAt"]).index("byFlagUsageFlag", ["flag"]), ue = (e) => e.toISOString().slice(0, 10), de = (e) => {
346
346
  let t = 2166136261;
347
347
  for (let n = 0; n < e.length; n++) t ^= e.charCodeAt(n), t = Math.imul(t, 16777619);
348
348
  return (t >>> 0).toString(16).padStart(8, "0");
349
- }, de = (e, t, n) => `flagusg_${t.replace(/-/g, "")}_${n === "targeted" ? "t" : "b"}_${ue(e)}`, Y = new class {
349
+ }, fe = (e, t, n) => `flagusg_${t.replace(/-/g, "")}_${n === "targeted" ? "t" : "b"}_${de(e)}`, Y = new class {
350
350
  on = !1;
351
351
  pending = /* @__PURE__ */ new Map();
352
352
  liveCounts = /* @__PURE__ */ new Map();
@@ -384,12 +384,12 @@ var S = (e) => typeof e == "boolean" ? { enabled: e } : e, C = (e, t) => {
384
384
  reset() {
385
385
  this.on = !1, this.pending.clear(), this.liveCounts.clear();
386
386
  }
387
- }(), X = (e, t, n) => Y.record(e, t, n), fe = (e) => e === "bulk" ? "bulk" : "targeted", pe = (e) => ({
387
+ }(), X = (e, t, n) => Y.record(e, t, n), pe = (e) => e === "bulk" ? "bulk" : "targeted", me = (e) => ({
388
388
  upsertMany: async (t) => {
389
389
  for (let n of t) {
390
- let t = le(n.lastAt);
390
+ let t = ue(n.lastAt);
391
391
  await e.upsert(q, {
392
- id: de(n.flag, t, n.source),
392
+ id: fe(n.flag, t, n.source),
393
393
  flag: n.flag,
394
394
  day: t,
395
395
  source: n.source,
@@ -401,13 +401,13 @@ var S = (e) => typeof e == "boolean" ? { enabled: e } : e, C = (e, t) => {
401
401
  ] });
402
402
  }
403
403
  },
404
- load: async () => (await e.query(g(J).descriptor)).map((e) => ({
404
+ load: async () => (await e.query(_(J).descriptor)).map((e) => ({
405
405
  flag: String(e.flag),
406
406
  day: String(e.day),
407
- source: fe(e.source),
407
+ source: pe(e.source),
408
408
  lastAt: e.lastAt instanceof Date ? e.lastAt : new Date(String(e.lastAt))
409
409
  }))
410
- }), me = 3e5, he = (e) => {
410
+ }), he = 3e5, ge = (e) => {
411
411
  let t = e.intervalMs ?? 3e5, n = !1, r = async () => {
412
412
  let t = Y.drain();
413
413
  if (t.length !== 0) try {
@@ -431,9 +431,9 @@ var S = (e) => typeof e == "boolean" ? { enabled: e } : e, C = (e, t) => {
431
431
  n || (n = !0, clearInterval(i), await r());
432
432
  }
433
433
  };
434
- }, ge = (e = process.env, t = 90) => {
435
- let n = v(e.VOLTRO_FLAG_USAGE_TTL_HOURS, 24 * t);
436
- return _({
434
+ }, _e = (e = process.env, t = 90) => {
435
+ let n = y(e.VOLTRO_FLAG_USAGE_TTL_HOURS, 24 * t);
436
+ return v({
437
437
  table: q,
438
438
  timeColumn: "lastAt",
439
439
  source: "plugin",
@@ -441,88 +441,88 @@ var S = (e) => typeof e == "boolean" ? { enabled: e } : e, C = (e, t) => {
441
441
  envVar: "VOLTRO_FLAG_USAGE_TTL_HOURS"
442
442
  }), n;
443
443
  }, Z = (e) => "request" in e && e.request?.subject || "subject" in e && e.subject || {}, Q = (e, t) => {
444
- let n = L.get(t);
445
- return X(t, "targeted"), n !== void 0 && k(t, n, Z(e));
446
- }, _e = (e, t) => Q(e, t) ? c.void : c.fail(new s({ flag: t })), ve = (e, t) => {
447
- let n = L.get(t.key);
448
- return X(t.key, "targeted"), n === void 0 ? t.defaultValue : r(t, j(t.key, n, Z(e)));
449
- }, ye = (e, t) => {
450
- let n = L.get(t.key);
451
- return X(t.key, "targeted"), n === void 0 ? null : j(t.key, n, Z(e)).name;
452
- }, be = (e, t) => e === t || e.startsWith("/") && e.endsWith("/") && new RegExp(e.slice(1, -1)).test(t), $ = "@voltro/plugin-flags", xe = (r = {}) => {
453
- let a = r.gatedBy ?? {}, o = u({
444
+ let n = R.get(t);
445
+ return X(t, "targeted"), n !== void 0 && A(t, n, Z(e));
446
+ }, ve = (e, t) => Q(e, t) ? l.void : l.fail(new c({ flag: t })), ye = (e, t) => {
447
+ let n = R.get(t.key);
448
+ return X(t.key, "targeted"), n === void 0 ? t.defaultValue : i(t, N(t.key, n, Z(e)));
449
+ }, be = (e, t) => {
450
+ let n = R.get(t.key);
451
+ return X(t.key, "targeted"), n === void 0 ? null : N(t.key, n, Z(e)).name;
452
+ }, xe = (e, t) => e === t || e.startsWith("/") && e.endsWith("/") && new RegExp(e.slice(1, -1)).test(t), $ = e, Se = (e = {}) => {
453
+ let i = e.gatedBy ?? {}, o = d({
454
454
  base: $,
455
- alias: r.alias,
456
- instance: r.name
457
- }), d = r.typedFlags ?? [], f = /* @__PURE__ */ new Map();
458
- for (let e of d) {
459
- if (f.has(e.key)) throw Error(`${$}: two typedFlags declare the key ${JSON.stringify(e.key)}`);
460
- if (r.flags?.[e.key] !== void 0) throw Error(`${$}: ${JSON.stringify(e.key)} is declared BOTH in \`flags\` and in \`typedFlags\`. One key, one declaration — the typed one would otherwise be silently overwritten by the untyped baseline.`);
461
- f.set(e.key, e);
455
+ alias: e.alias,
456
+ instance: e.name
457
+ }), s = e.typedFlags ?? [], f = /* @__PURE__ */ new Map();
458
+ for (let t of s) {
459
+ if (f.has(t.key)) throw Error(`${$}: two typedFlags declare the key ${JSON.stringify(t.key)}`);
460
+ if (e.flags?.[t.key] !== void 0) throw Error(`${$}: ${JSON.stringify(t.key)} is declared BOTH in \`flags\` and in \`typedFlags\`. One key, one declaration — the typed one would otherwise be silently overwritten by the untyped baseline.`);
461
+ f.set(t.key, t);
462
462
  }
463
- d.some((e) => e.experiment !== void 0) && H(d, r.experiments ?? []);
464
- let p = { ...r.flags ?? {} };
465
- for (let e of d) p[e.key] = e.definition;
466
- let m = r.store === "postgres", h, g, _, v, y = [], b = r.usage ?? {}, x = b.track ?? m;
463
+ s.some((e) => e.experiment !== void 0) && U(s, e.experiments ?? []);
464
+ let p = { ...e.flags ?? {} };
465
+ for (let e of s) p[e.key] = e.definition;
466
+ let m = e.store === "postgres", h, g, _, v, y = [], b = e.usage ?? {}, x = b.track ?? m;
467
467
  if (x && !m) throw Error(`${$}: usage.track requires store: 'postgres' — there is nowhere to record an evaluation on the memory tier, and a dead-flag report that silently observed nothing is exactly the defect it exists to find.`);
468
468
  let S = b.staleAfterDays ?? 30, C = b.retentionDays ?? 90;
469
469
  if (S > C) throw Error(`${$}: usage.staleAfterDays (${S}) exceeds usage.retentionDays (${C}), so the observation window can never be long enough to prove a flag stale and every verdict would be withheld.`);
470
- let ee = (e) => {
471
- let t = I(p, e), n = [];
470
+ let w = (e) => {
471
+ let t = re(p, e), n = [];
472
472
  for (let [e, r] of f) {
473
- let a = t[e];
474
- if (a === void 0) continue;
475
- let o = i(r, typeof a == "boolean" ? { enabled: a } : a);
473
+ let i = t[e];
474
+ if (i === void 0) continue;
475
+ let o = a(r, typeof i == "boolean" ? { enabled: i } : i);
476
476
  o.ok || (n.push(o.refusal), t[e] = r.definition);
477
477
  }
478
- y = n, L.set(t);
479
- }, w = async () => {
480
- h && ee(await h.loadOverrides());
481
- }, T = (e) => {
482
- if (a[e] !== void 0) return a[e];
483
- for (let [t, n] of Object.entries(a)) if (t !== e && be(t, e)) return n;
478
+ y = n, R.set(t);
479
+ }, T = async () => {
480
+ h && w(await h.loadOverrides());
481
+ }, ee = (e) => {
482
+ if (i[e] !== void 0) return i[e];
483
+ for (let [t, n] of Object.entries(i)) if (t !== e && xe(t, e)) return n;
484
484
  }, E = (e, t) => {
485
- let n = T(t.tag);
486
- return n === void 0 || Q({ subject: t.subject }, n) ? e : c.fail(new s({ flag: n }));
487
- }, D = Object.keys(a).length > 0, O = (e) => {
485
+ let n = ee(t.tag);
486
+ return n === void 0 || Q({ subject: t.subject }, n) ? e : l.fail(new c({ flag: n }));
487
+ }, D = Object.keys(i).length > 0, O = (e) => {
488
488
  for (let t of e) X(t, "bulk");
489
489
  }, k = {
490
- ...e,
490
+ ...t,
491
491
  description: "Resolve every feature flag for the calling subject.",
492
- execute: (e, t) => c.sync(() => {
493
- let e = L.all();
494
- return O(Object.keys(e)), ne(e, t.request.subject ?? {});
492
+ execute: (e, t) => l.sync(() => {
493
+ let e = R.all();
494
+ return O(Object.keys(e)), te(e, t.request.subject ?? {});
495
495
  })
496
- }, te = {
497
- ...t,
496
+ }, A = {
497
+ ...n,
498
498
  description: "Resolve every feature flag to its served variant for the calling subject.",
499
- execute: (e, t) => c.sync(() => {
500
- let e = L.all();
501
- return O(Object.keys(e)), re(e, t.request.subject ?? {});
499
+ execute: (e, t) => l.sync(() => {
500
+ let e = R.all();
501
+ return O(Object.keys(e)), ne(e, t.request.subject ?? {});
502
502
  })
503
- }, A = (e) => ({
503
+ }, j = (e) => ({
504
504
  kind: "json",
505
505
  data: e
506
- }), j = (e, t) => ({
506
+ }), M = (e, t) => ({
507
507
  kind: "json",
508
508
  status: e,
509
509
  data: { error: t }
510
- }), M = async () => {
510
+ }), N = async () => {
511
511
  let e = [];
512
512
  if (x && _) try {
513
513
  e = await _.load();
514
514
  } catch {
515
515
  e = [];
516
516
  }
517
- return ce({
518
- flags: L.all(),
517
+ return le({
518
+ flags: R.all(),
519
519
  usage: e,
520
520
  tracking: x,
521
521
  liveCounts: Y.liveTargetedCounts(),
522
522
  staleAfterDays: S,
523
523
  retentionDays: C
524
524
  });
525
- }, N = () => {
525
+ }, P = () => {
526
526
  let e = {};
527
527
  for (let [t, n] of f) e[t] = {
528
528
  valueType: n.valueType,
@@ -530,17 +530,17 @@ var S = (e) => typeof e == "boolean" ? { enabled: e } : e, C = (e, t) => {
530
530
  experiment: n.experiment ?? null
531
531
  };
532
532
  return e;
533
- }, R = [
533
+ }, F = [
534
534
  {
535
535
  method: "GET",
536
536
  path: "/list",
537
537
  description: "Every resolved flag (key, enabled, rollout, description) plus the dead-flag lifecycle report: per-flag shape + usage verdict, the observation coverage window, and the limits of what the report can prove.",
538
- handler: () => c.promise(async () => {
539
- let e = await M();
540
- return A({
541
- flags: B(L.all()),
538
+ handler: () => l.promise(async () => {
539
+ let e = await N();
540
+ return j({
541
+ flags: V(R.all()),
542
542
  lifecycle: e,
543
- typed: N(),
543
+ typed: P(),
544
544
  overrideRefusals: y
545
545
  });
546
546
  })
@@ -549,26 +549,26 @@ var S = (e) => typeof e == "boolean" ? { enabled: e } : e, C = (e, t) => {
549
549
  method: "POST",
550
550
  path: "/toggle",
551
551
  description: "Flip a flag's kill-switch on/off (live registry; durable on the postgres tier). Logs an audit row.",
552
- handler: (e) => c.gen(function* () {
552
+ handler: (e) => l.gen(function* () {
553
553
  let t;
554
554
  try {
555
555
  t = JSON.parse(e.body || "{}");
556
556
  } catch {
557
- return j(400, "invalid JSON body");
557
+ return M(400, "invalid JSON body");
558
558
  }
559
- if (!t.key || typeof t.enabled != "boolean") return j(400, "key (string) + enabled (boolean) required");
560
- let n = z(t.key, t.enabled);
561
- if (n === null) return j(404, `unknown flag "${t.key}"`);
562
- let r = h ? L.get(t.key) : void 0;
563
- return h && r !== void 0 && (yield* c.promise(() => h.upsertOverride(ie(t.key, r)))), g && (yield* c.promise(() => g.record({
559
+ if (!t.key || typeof t.enabled != "boolean") return M(400, "key (string) + enabled (boolean) required");
560
+ let n = B(t.key, t.enabled);
561
+ if (n === null) return M(404, `unknown flag "${t.key}"`);
562
+ let r = h ? R.get(t.key) : void 0;
563
+ return h && r !== void 0 && (yield* l.promise(() => h.upsertOverride(ie(t.key, r)))), g && (yield* l.promise(() => g.record({
564
564
  flag: t.key,
565
565
  enabled: t.enabled,
566
566
  previousEnabled: n,
567
567
  actorId: typeof t.actor == "string" ? t.actor : null,
568
568
  at: /* @__PURE__ */ new Date()
569
- }))), A({
569
+ }))), j({
570
570
  ok: !0,
571
- flags: B(L.all())
571
+ flags: V(R.all())
572
572
  });
573
573
  })
574
574
  },
@@ -576,16 +576,16 @@ var S = (e) => typeof e == "boolean" ? { enabled: e } : e, C = (e, t) => {
576
576
  method: "GET",
577
577
  path: "/audit",
578
578
  description: "The kill-switch flip audit trail, newest-first. `?flag=<key>` filters; `?limit=<n>` caps (postgres tier only).",
579
- handler: (e) => c.gen(function* () {
580
- if (!g) return A({
579
+ handler: (e) => l.gen(function* () {
580
+ if (!g) return j({
581
581
  audit: [],
582
582
  note: "audit trail requires store: \"postgres\""
583
583
  });
584
- let t = new URL(e.url, "http://local"), n = t.searchParams.get("flag"), r = t.searchParams.get("limit"), i = r != null && Number.isFinite(Number(r)) ? Number(r) : 100, a = yield* c.promise(() => g.list({
584
+ let t = new URL(e.url, "http://local"), n = t.searchParams.get("flag"), r = t.searchParams.get("limit"), i = r != null && Number.isFinite(Number(r)) ? Number(r) : 100, a = yield* l.promise(() => g.list({
585
585
  ...n == null ? {} : { flag: n },
586
586
  limit: i
587
587
  }));
588
- return A({ audit: a.map((e) => ({
588
+ return j({ audit: a.map((e) => ({
589
589
  flag: e.flag,
590
590
  enabled: e.enabled,
591
591
  previousEnabled: e.previousEnabled,
@@ -594,7 +594,7 @@ var S = (e) => typeof e == "boolean" ? { enabled: e } : e, C = (e, t) => {
594
594
  })) });
595
595
  })
596
596
  }
597
- ], oe = [
597
+ ], z = [
598
598
  ...D ? [
599
599
  "rpc:intercept:mutation",
600
600
  "rpc:intercept:query",
@@ -604,16 +604,16 @@ var S = (e) => typeof e == "boolean" ? { enabled: e } : e, C = (e, t) => {
604
604
  "inspect:read",
605
605
  "inspect:write"
606
606
  ];
607
- return l({
607
+ return u({
608
608
  name: o,
609
609
  baseName: $,
610
610
  description: "Feature flags — targeting, % rollout, kill-switch, declarative gating + client evaluation.",
611
- permissions: oe,
612
- routes: [k, te],
613
- rpcClientDescriptors: n,
614
- inspectEndpoints: R,
611
+ permissions: z,
612
+ routes: [k, A],
613
+ rpcClientDescriptors: r,
614
+ inspectEndpoints: F,
615
615
  errorSchemas: [{
616
- schema: s,
616
+ schema: c,
617
617
  import: {
618
618
  module: "@voltro/plugin-flags/errors",
619
619
  name: "FlagDisabled"
@@ -625,38 +625,38 @@ var S = (e) => typeof e == "boolean" ? { enabled: e } : e, C = (e, t) => {
625
625
  interceptAction: E
626
626
  } : {},
627
627
  ...m ? { extendSchema: { tables: x ? [
628
- P,
629
- F,
628
+ I,
629
+ L,
630
630
  J
631
- ] : [P, F] } } : {},
631
+ ] : [I, L] } } : {},
632
632
  ...m ? { bindDataStore: (e) => {
633
- h = ae(e), g = se(e), x && (_ = pe(e));
633
+ h = ae(e), g = se(e), x && (_ = me(e));
634
634
  } } : {},
635
- ...m ? { onChangeEvent: (e) => e.table === "_voltro_feature_flags" ? c.tryPromise(() => w()) : c.void } : {},
636
- onActivate: (e) => c.gen(function* () {
637
- L.set(p), m && h && (yield* c.promise(w));
638
- let t;
639
- x && _ && (t = ge(process.env, C), Y.enable(), v = he({
635
+ ...m ? { onChangeEvent: (e) => e.table === "_voltro_feature_flags" ? l.tryPromise(() => T()) : l.void } : {},
636
+ onActivate: (t) => l.gen(function* () {
637
+ R.set(p), m && h && (yield* l.promise(T));
638
+ let n;
639
+ x && _ && (n = _e(process.env, C), Y.enable(), v = ge({
640
640
  store: _,
641
641
  intervalMs: b.flushIntervalMs ?? 3e5,
642
- log: { warn: (t, n) => e.logger.warn(t, n) }
642
+ log: { warn: (e, n) => t.logger.warn(e, n) }
643
643
  }));
644
- for (let t of y) e.logger.warn("feature flag override REFUSED — the code-declared definition stands", {
645
- flag: t.key,
646
- reason: t.reason
644
+ for (let e of y) t.logger.warn("feature flag override REFUSED — the code-declared definition stands", {
645
+ flag: e.key,
646
+ reason: e.reason
647
647
  });
648
- e.logger.info("feature flags active", {
648
+ t.logger.info("feature flags active", {
649
649
  count: Object.keys(p).length,
650
650
  typed: f.size,
651
- gated: Object.keys(a).length,
652
- store: r.store ?? "memory",
653
- usage: x ? `tracking (stale after ${S}d, observations kept ${Math.round((t ?? 0) / 864e5)}d)` : "off — no dead-flag evidence is collected"
651
+ gated: Object.keys(i).length,
652
+ store: e.store ?? "memory",
653
+ usage: x ? `tracking (stale after ${S}d, observations kept ${Math.round((n ?? 0) / 864e5)}d)` : "off — no dead-flag evidence is collected"
654
654
  });
655
655
  }),
656
- onDeactivate: () => c.promise(async () => {
657
- await v?.stop(), v = void 0, Y.disable(), L.set({});
656
+ onDeactivate: () => l.promise(async () => {
657
+ await v?.stop(), v = void 0, Y.disable(), R.set({});
658
658
  })
659
659
  });
660
660
  };
661
661
  //#endregion
662
- export { me as DEFAULT_FLAG_USAGE_FLUSH_MS, K as FLAG_LIFECYCLE_LIMITS, q as FLAG_USAGE_TABLE, s as FlagDisabled, H as assertFlagExperimentLinks, V as checkFlagExperimentLinks, G as classifyShape, pe as dataStoreFlagUsageStore, o as defineFlag, B as describeFlags, ne as evaluateAll, re as evaluateAllVariants, k as evaluateFlag, O as evaluateSchedule, j as evaluateVariant, P as featureFlagsTable, F as flagAuditTable, ce as flagLifecycleReport, L as flagRegistry, Y as flagUsageBuffer, de as flagUsageRowId, J as flagUsageTable, ve as flagValue, ye as flagVariant, xe as flagsPlugin, z as flipFlagEnabled, i as gateOverride, Q as isFlagEnabled, a as isTypedFlag, I as mergeOverrides, X as recordFlagEvaluation, ge as registerFlagUsageRetention, _e as requireFlag, A as resolveVariant, R as setFlagEnabled, he as startFlagUsageFlush, S as toDefinition, r as typedValueOf, le as utcDay };
662
+ export { he as DEFAULT_FLAG_USAGE_FLUSH_MS, ce as FLAG_LIFECYCLE_LIMITS, q as FLAG_USAGE_TABLE, c as FlagDisabled, U as assertFlagExperimentLinks, H as checkFlagExperimentLinks, K as classifyShape, me as dataStoreFlagUsageStore, s as defineFlag, V as describeFlags, te as evaluateAll, ne as evaluateAllVariants, A as evaluateFlag, k as evaluateSchedule, N as evaluateVariant, I as featureFlagsTable, L as flagAuditTable, le as flagLifecycleReport, R as flagRegistry, Y as flagUsageBuffer, fe as flagUsageRowId, J as flagUsageTable, ye as flagValue, be as flagVariant, Se as flagsPlugin, B as flipFlagEnabled, a as gateOverride, Q as isFlagEnabled, o as isTypedFlag, re as mergeOverrides, X as recordFlagEvaluation, _e as registerFlagUsageRetention, ve as requireFlag, M as resolveVariant, z as setFlagEnabled, ge as startFlagUsageFlush, C as toDefinition, i as typedValueOf, ue as utcDay };