@nebulr-group/bridge-auth-core 0.1.1 → 0.4.0-beta.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.
Files changed (135) hide show
  1. package/dist/billing/bridge-subscription.d.ts +71 -0
  2. package/dist/billing/bridge-subscription.d.ts.map +1 -0
  3. package/dist/billing/bridge-subscription.js +209 -0
  4. package/dist/billing/bridge-subscription.js.map +1 -0
  5. package/dist/billing/entitlements-store.d.ts +61 -0
  6. package/dist/billing/entitlements-store.d.ts.map +1 -0
  7. package/dist/billing/entitlements-store.js +127 -0
  8. package/dist/billing/entitlements-store.js.map +1 -0
  9. package/dist/billing/fetch-billing-state.d.ts +9 -0
  10. package/dist/billing/fetch-billing-state.d.ts.map +1 -0
  11. package/dist/billing/fetch-billing-state.js +22 -0
  12. package/dist/billing/fetch-billing-state.js.map +1 -0
  13. package/dist/billing/lock-signal.d.ts +6 -0
  14. package/dist/billing/lock-signal.d.ts.map +1 -0
  15. package/dist/billing/lock-signal.js +8 -0
  16. package/dist/billing/lock-signal.js.map +1 -0
  17. package/dist/billing/quota-store.d.ts +86 -0
  18. package/dist/billing/quota-store.d.ts.map +1 -0
  19. package/dist/billing/quota-store.js +180 -0
  20. package/dist/billing/quota-store.js.map +1 -0
  21. package/dist/billing/types.d.ts +64 -0
  22. package/dist/billing/types.d.ts.map +1 -0
  23. package/dist/billing/types.js +49 -0
  24. package/dist/billing/types.js.map +1 -0
  25. package/dist/billing/use-bridge.d.ts +131 -0
  26. package/dist/billing/use-bridge.d.ts.map +1 -0
  27. package/dist/billing/use-bridge.js +181 -0
  28. package/dist/billing/use-bridge.js.map +1 -0
  29. package/dist/bridge-auth.d.ts +39 -1
  30. package/dist/bridge-auth.d.ts.map +1 -1
  31. package/dist/bridge-auth.js +63 -4
  32. package/dist/bridge-auth.js.map +1 -1
  33. package/dist/direct-auth.d.ts +1 -0
  34. package/dist/direct-auth.d.ts.map +1 -1
  35. package/dist/direct-auth.js +7 -0
  36. package/dist/direct-auth.js.map +1 -1
  37. package/dist/errors.d.ts +11 -0
  38. package/dist/errors.d.ts.map +1 -1
  39. package/dist/errors.js +14 -0
  40. package/dist/errors.js.map +1 -1
  41. package/dist/flags/attribute-providers.d.ts +229 -0
  42. package/dist/flags/attribute-providers.d.ts.map +1 -0
  43. package/dist/flags/attribute-providers.js +375 -0
  44. package/dist/flags/attribute-providers.js.map +1 -0
  45. package/dist/flags/dev-attribute-provider.d.ts +67 -0
  46. package/dist/flags/dev-attribute-provider.d.ts.map +1 -0
  47. package/dist/flags/dev-attribute-provider.js +200 -0
  48. package/dist/flags/dev-attribute-provider.js.map +1 -0
  49. package/dist/flags/evaluator.d.ts +58 -0
  50. package/dist/flags/evaluator.d.ts.map +1 -0
  51. package/dist/flags/evaluator.js +160 -0
  52. package/dist/flags/evaluator.js.map +1 -0
  53. package/dist/flags/flag.d.ts +199 -0
  54. package/dist/flags/flag.d.ts.map +1 -0
  55. package/dist/flags/flag.js +337 -0
  56. package/dist/flags/flag.js.map +1 -0
  57. package/dist/flags/identity.d.ts +77 -0
  58. package/dist/flags/identity.d.ts.map +1 -0
  59. package/dist/flags/identity.js +134 -0
  60. package/dist/flags/identity.js.map +1 -0
  61. package/dist/flags/index.d.ts +20 -0
  62. package/dist/flags/index.d.ts.map +1 -0
  63. package/dist/flags/index.js +21 -0
  64. package/dist/flags/index.js.map +1 -0
  65. package/dist/flags/operators.d.ts +63 -0
  66. package/dist/flags/operators.d.ts.map +1 -0
  67. package/dist/flags/operators.js +299 -0
  68. package/dist/flags/operators.js.map +1 -0
  69. package/dist/flags/propagation.d.ts +16 -0
  70. package/dist/flags/propagation.d.ts.map +1 -0
  71. package/dist/flags/propagation.js +107 -0
  72. package/dist/flags/propagation.js.map +1 -0
  73. package/dist/flags/realtime.d.ts +328 -0
  74. package/dist/flags/realtime.d.ts.map +1 -0
  75. package/dist/flags/realtime.js +504 -0
  76. package/dist/flags/realtime.js.map +1 -0
  77. package/dist/flags/runtime-mode.d.ts +32 -0
  78. package/dist/flags/runtime-mode.d.ts.map +1 -0
  79. package/dist/flags/runtime-mode.js +84 -0
  80. package/dist/flags/runtime-mode.js.map +1 -0
  81. package/dist/flags/telemetry.d.ts +79 -0
  82. package/dist/flags/telemetry.d.ts.map +1 -0
  83. package/dist/flags/telemetry.js +286 -0
  84. package/dist/flags/telemetry.js.map +1 -0
  85. package/dist/http.d.ts.map +1 -1
  86. package/dist/http.js +11 -1
  87. package/dist/http.js.map +1 -1
  88. package/dist/index.d.ts +15 -3
  89. package/dist/index.d.ts.map +1 -1
  90. package/dist/index.js +16 -1
  91. package/dist/index.js.map +1 -1
  92. package/dist/management-types.d.ts +38 -0
  93. package/dist/management-types.d.ts.map +1 -1
  94. package/dist/route-guard.d.ts.map +1 -1
  95. package/dist/route-guard.js +15 -5
  96. package/dist/route-guard.js.map +1 -1
  97. package/dist/token-manager.d.ts +16 -2
  98. package/dist/token-manager.d.ts.map +1 -1
  99. package/dist/token-manager.js +80 -6
  100. package/dist/token-manager.js.map +1 -1
  101. package/dist/token-utils.d.ts +4 -0
  102. package/dist/token-utils.d.ts.map +1 -1
  103. package/dist/token-utils.js +10 -0
  104. package/dist/token-utils.js.map +1 -1
  105. package/dist/types.d.ts +26 -1
  106. package/dist/types.d.ts.map +1 -1
  107. package/dist/usage/index.d.ts +3 -0
  108. package/dist/usage/index.d.ts.map +1 -0
  109. package/dist/usage/index.js +4 -0
  110. package/dist/usage/index.js.map +1 -0
  111. package/dist/usage/storage/durable-storage.d.ts +47 -0
  112. package/dist/usage/storage/durable-storage.d.ts.map +1 -0
  113. package/dist/usage/storage/durable-storage.js +27 -0
  114. package/dist/usage/storage/durable-storage.js.map +1 -0
  115. package/dist/usage/storage/index.d.ts +7 -0
  116. package/dist/usage/storage/index.d.ts.map +1 -0
  117. package/dist/usage/storage/index.js +28 -0
  118. package/dist/usage/storage/index.js.map +1 -0
  119. package/dist/usage/storage/indexeddb-storage.d.ts +18 -0
  120. package/dist/usage/storage/indexeddb-storage.d.ts.map +1 -0
  121. package/dist/usage/storage/indexeddb-storage.js +159 -0
  122. package/dist/usage/storage/indexeddb-storage.js.map +1 -0
  123. package/dist/usage/storage/node-fs-storage.d.ts +29 -0
  124. package/dist/usage/storage/node-fs-storage.d.ts.map +1 -0
  125. package/dist/usage/storage/node-fs-storage.js +180 -0
  126. package/dist/usage/storage/node-fs-storage.js.map +1 -0
  127. package/dist/usage/storage/noop-storage.d.ts +17 -0
  128. package/dist/usage/storage/noop-storage.d.ts.map +1 -0
  129. package/dist/usage/storage/noop-storage.js +67 -0
  130. package/dist/usage/storage/noop-storage.js.map +1 -0
  131. package/dist/usage/usage-reporter.d.ts +64 -0
  132. package/dist/usage/usage-reporter.d.ts.map +1 -0
  133. package/dist/usage/usage-reporter.js +234 -0
  134. package/dist/usage/usage-reporter.js.map +1 -0
  135. package/package.json +2 -1
@@ -0,0 +1,199 @@
1
+ import { type EvalContext, type Rule, type FlagState } from './evaluator.js';
2
+ import { AttributeProviderRegistry, type AttributeProvider } from './attribute-providers.js';
3
+ /**
4
+ * The attribute types the SDK and admin UI both understand. `semver` is a
5
+ * commonly-needed string subtype that powers `app_version > 4.12` style rules;
6
+ * runtime evaluation treats it as a string with regex-friendly comparators.
7
+ */
8
+ export type DeclaredAttributeType = 'string' | 'number' | 'boolean' | 'date' | 'semver' | 'json';
9
+ export interface AttributeDeclaration {
10
+ name: string;
11
+ type: DeclaredAttributeType;
12
+ /** Unix-ms when this declaration was made. */
13
+ timestamp: number;
14
+ }
15
+ export type FlagValueType = 'boolean' | 'string' | 'number' | 'json';
16
+ export interface CachedFlag {
17
+ key: string;
18
+ state: FlagState;
19
+ valueType: FlagValueType;
20
+ offValue: unknown;
21
+ onValue: unknown;
22
+ rule?: Rule;
23
+ }
24
+ export interface EvalTelemetry {
25
+ flag: string;
26
+ value: unknown;
27
+ variantIndex: number;
28
+ identity?: string;
29
+ /** Unix-ms when this eval happened. */
30
+ timestamp: number;
31
+ /** Optional opaque fingerprint of the call site (TBP-156). */
32
+ callSiteFingerprint?: string;
33
+ }
34
+ export interface DiscoveryTelemetry {
35
+ flag: string;
36
+ defaultValue: unknown;
37
+ observedType: FlagValueType;
38
+ timestamp: number;
39
+ }
40
+ export interface AttributeObservation {
41
+ /** Attribute key as the dev supplied it (e.g. 'country', 'myapp:foo'). */
42
+ key: string;
43
+ /** The sample value observed for the key in this eval. */
44
+ sampleValue: unknown;
45
+ /** Inferred type — fed into discovery so the admin UI gets a sensible picker. */
46
+ observedType: FlagValueType;
47
+ timestamp: number;
48
+ }
49
+ export interface BridgeFlagsHooks {
50
+ /** Called on every successful eval. Should not throw. */
51
+ onEval?: (ev: EvalTelemetry) => void;
52
+ /** Called the first time the SDK sees an unknown flag key. */
53
+ onDiscover?: (ev: DiscoveryTelemetry) => void;
54
+ /**
55
+ * Called once per distinct (key, sampleValue) seen in `bridge.flag()`
56
+ * per-call `context.attributes`. The batcher relays this to
57
+ * `/v1/flags/discover` with `kind: 'attribute'` so the admin UI's attribute
58
+ * catalog shows what dev code actually supplies.
59
+ */
60
+ onAttributeObserved?: (ev: AttributeObservation) => void;
61
+ /**
62
+ * @deprecated Use per-call `bridge.flag(key, default, { attributes: {...} })`
63
+ * instead — per-call observations now feed the admin attribute catalog
64
+ * automatically via `onAttributeObserved`. `declareAttributes()` will be
65
+ * removed in a future minor release.
66
+ */
67
+ onAttributeDeclaration?: (decl: AttributeDeclaration) => void;
68
+ }
69
+ /**
70
+ * The result of a flag evaluation. `passed` is true when a targeting rule (or
71
+ * global on) resolved this flag to its on-value; false when the flag is off,
72
+ * no rule matched, or the flag isn't in the cache yet. `value` is always the
73
+ * Bridge-decided value — the on-value when passed, the off/default value when not.
74
+ */
75
+ export interface FlagEvalResult<T> {
76
+ passed: boolean;
77
+ value: T;
78
+ }
79
+ /**
80
+ * BridgeFlags — the SDK-side flag cache + eval entry point. Most apps see
81
+ * exactly one instance, wired up at bootstrap time. Tests construct their
82
+ * own instances directly.
83
+ */
84
+ /**
85
+ * Runtime mode for the SDK. Backends (Node servers) opt into `'backend'` to
86
+ * disable auto-anonymous identity — per locked decision #27, backend evals
87
+ * with no identity refuse to bucket rolled-out rules and return the safe
88
+ * default. Frontends use `'frontend'` (the default).
89
+ */
90
+ export type BridgeFlagsMode = 'frontend' | 'backend';
91
+ /**
92
+ * Minimal shape of the SDK's usage reporter — kept narrow so `BridgeFlags`
93
+ * doesn't depend on the full `UsageReporter` class. Framework wrappers pass
94
+ * `bridge.usage` (which has the same `report(metric, value?)` signature).
95
+ */
96
+ export interface FlagUsageReporterLike {
97
+ report(metric: string, value?: number): void;
98
+ }
99
+ export declare class BridgeFlags {
100
+ private cache;
101
+ private context;
102
+ private hooks;
103
+ private discoveredKeys;
104
+ /** Per-runtime dedup for attribute observations: `${key}::${canonicalValue}`. */
105
+ private observedAttributeKeys;
106
+ private attributeDeclarations;
107
+ private mode;
108
+ private serverInstanceIdValue?;
109
+ private missingIdentityWarned;
110
+ /** Billing 2.0 US-10 (TBP-262): self-report `bridge.flag_evaluations` per eval. */
111
+ private usageReporter?;
112
+ /**
113
+ * Phase 1 / US-13 (TBP-293, TBP-294) — AttributeProvider registry consulted
114
+ * on every flag eval. Bridge-managed providers (`bridge:auth`,
115
+ * `bridge:billing`) are auto-registered by framework SDKs; apps register
116
+ * their own providers via `registerAttributeProvider()`.
117
+ */
118
+ private readonly registry;
119
+ constructor(options?: {
120
+ mode?: BridgeFlagsMode;
121
+ usageReporter?: FlagUsageReporterLike;
122
+ });
123
+ /**
124
+ * Register an `AttributeProvider`. Idempotent on `provider.name` —
125
+ * re-registering with the same name replaces the previous instance.
126
+ * Provider attributes flow into every `flag()` eval automatically.
127
+ * Dev-supplied attrs (via `setContext` / per-call `context`) win on
128
+ * collision (locked decision #20).
129
+ */
130
+ registerAttributeProvider(provider: AttributeProvider): void;
131
+ /** Remove a previously-registered provider by name. No-op if not present. */
132
+ unregisterAttributeProvider(name: string): void;
133
+ /**
134
+ * Expose the registry for advanced callers (e.g. async `applyTo()` refreshes
135
+ * driven by framework auth events). Most consumers should prefer
136
+ * `registerAttributeProvider` / `unregisterAttributeProvider`.
137
+ */
138
+ getAttributeProviderRegistry(): AttributeProviderRegistry;
139
+ /**
140
+ * Wire a usage reporter post-construction. The flag-eval path will call
141
+ * `reporter.report('bridge.flag_evaluations', 1)` on every successful eval
142
+ * (i.e. when a cached flag is present and evaluated). Discovery-only paths
143
+ * — first-sight unknown flag → default — do NOT count.
144
+ */
145
+ setUsageReporter(reporter: FlagUsageReporterLike | undefined): void;
146
+ /** Read the active runtime mode. */
147
+ getMode(): BridgeFlagsMode;
148
+ /**
149
+ * Set a stable server-instance identity (TBP-172). When set, backend flags
150
+ * that explicitly opt into system-level targeting can bucket on this value
151
+ * (e.g. canary a feature to one specific instance). Most evals continue to
152
+ * use the per-call/per-context identity.
153
+ */
154
+ setServerInstanceId(id: string): void;
155
+ /** Read the configured server-instance id, or undefined when not set. */
156
+ getServerInstanceId(): string | undefined;
157
+ /** Replace or merge the eval context. */
158
+ setContext(ctx: EvalContext, merge?: boolean): void;
159
+ /** Return a defensive shallow copy of the current eval context. */
160
+ getContext(): EvalContext;
161
+ /** Replace the cache from a bulk hydrate (e.g. response from bridge-api). */
162
+ hydrate(flags: CachedFlag[]): void;
163
+ /** Replace or insert a single flag (used by live updates). */
164
+ upsert(flag: CachedFlag): void;
165
+ /** Remove a flag from the cache. */
166
+ remove(key: string): void;
167
+ /** Register telemetry hooks. Replaces any previous hooks. */
168
+ setHooks(hooks: BridgeFlagsHooks): void;
169
+ /**
170
+ * Read a flag value. Returns `defaultValue` when the flag isn't known yet.
171
+ * TypeScript infers `T` from `defaultValue`, so callers don't write casts.
172
+ *
173
+ * An optional per-call `context` overrides the SDK's global context for
174
+ * just this eval. Attributes deep-merge — per-call wins on overlap, global
175
+ * keys not in the override are preserved.
176
+ */
177
+ flag<T>(key: string, defaultValue: T, context?: Partial<EvalContext>): FlagEvalResult<T>;
178
+ /**
179
+ * Internal — evaluate the cached flag against an eval context.
180
+ */
181
+ private evaluateCached;
182
+ /** Number of cached flags. Useful for debugging + tests. */
183
+ cacheSize(): number;
184
+ /** Get a snapshot of cached flag keys. */
185
+ cachedKeys(): string[];
186
+ /**
187
+ * @deprecated Per-call observations now power the admin attribute catalog
188
+ * automatically — just pass attributes when you evaluate a flag:
189
+ * `bridge.flag('feat', false, { attributes: { plan: 'pro' } })`.
190
+ * The SDK reports each `(key, sampleValue)` once via `onAttributeObserved`,
191
+ * which the batcher relays to `/v1/flags/discover` with `kind: 'attribute'`.
192
+ * `declareAttributes()` will be removed in a future minor release.
193
+ */
194
+ declareAttributes(declarations: Record<string, DeclaredAttributeType>): void;
195
+ /** Read the current attribute type declarations. Useful for tests + framework wrappers. */
196
+ getAttributeDeclarations(): Record<string, DeclaredAttributeType>;
197
+ private safeHook;
198
+ }
199
+ //# sourceMappingURL=flag.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flag.d.ts","sourceRoot":"","sources":["../../src/flags/flag.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,337 @@
1
+ // bridge.flag(key, default) — the canonical SDK call (TBP-160).
2
+ //
3
+ // One method, one signature. The TypeScript return type matches the default
4
+ // value's type (boolean / string / number / structured), so callers don't
5
+ // need a separate cast.
6
+ //
7
+ // Local-first evaluation:
8
+ // 1. Look up the flag in the in-memory cache
9
+ // 2. If absent → return the developer's `defaultValue` (and emit a
10
+ // "discovered" hook so the SDK can register the key with bridge-api)
11
+ // 3. If present → evaluate locally against the dev-supplied context
12
+ // 4. Always emit an `onEval` hook so the SDK can batch eval telemetry
13
+ //
14
+ // Hooks are pluggable so this module stays platform-agnostic. The actual
15
+ // wiring (HTTP batcher, fetch client, etc.) is the SDK integration's job
16
+ // — see TBP-157 / TBP-156 for the implementations.
17
+ import { evaluateRule, } from './evaluator.js';
18
+ import { AttributeProviderRegistry, } from './attribute-providers.js';
19
+ export class BridgeFlags {
20
+ cache = new Map();
21
+ context = { attributes: {} };
22
+ hooks = {};
23
+ discoveredKeys = new Set();
24
+ /** Per-runtime dedup for attribute observations: `${key}::${canonicalValue}`. */
25
+ observedAttributeKeys = new Set();
26
+ attributeDeclarations = new Map();
27
+ mode = 'frontend';
28
+ serverInstanceIdValue;
29
+ missingIdentityWarned = new Set();
30
+ /** Billing 2.0 US-10 (TBP-262): self-report `bridge.flag_evaluations` per eval. */
31
+ usageReporter;
32
+ /**
33
+ * Phase 1 / US-13 (TBP-293, TBP-294) — AttributeProvider registry consulted
34
+ * on every flag eval. Bridge-managed providers (`bridge:auth`,
35
+ * `bridge:billing`) are auto-registered by framework SDKs; apps register
36
+ * their own providers via `registerAttributeProvider()`.
37
+ */
38
+ registry = new AttributeProviderRegistry();
39
+ constructor(options = {}) {
40
+ if (options.mode)
41
+ this.mode = options.mode;
42
+ if (options.usageReporter)
43
+ this.usageReporter = options.usageReporter;
44
+ }
45
+ /**
46
+ * Register an `AttributeProvider`. Idempotent on `provider.name` —
47
+ * re-registering with the same name replaces the previous instance.
48
+ * Provider attributes flow into every `flag()` eval automatically.
49
+ * Dev-supplied attrs (via `setContext` / per-call `context`) win on
50
+ * collision (locked decision #20).
51
+ */
52
+ registerAttributeProvider(provider) {
53
+ this.registry.register(provider);
54
+ }
55
+ /** Remove a previously-registered provider by name. No-op if not present. */
56
+ unregisterAttributeProvider(name) {
57
+ this.registry.unregister(name);
58
+ }
59
+ /**
60
+ * Expose the registry for advanced callers (e.g. async `applyTo()` refreshes
61
+ * driven by framework auth events). Most consumers should prefer
62
+ * `registerAttributeProvider` / `unregisterAttributeProvider`.
63
+ */
64
+ getAttributeProviderRegistry() {
65
+ return this.registry;
66
+ }
67
+ /**
68
+ * Wire a usage reporter post-construction. The flag-eval path will call
69
+ * `reporter.report('bridge.flag_evaluations', 1)` on every successful eval
70
+ * (i.e. when a cached flag is present and evaluated). Discovery-only paths
71
+ * — first-sight unknown flag → default — do NOT count.
72
+ */
73
+ setUsageReporter(reporter) {
74
+ this.usageReporter = reporter;
75
+ }
76
+ /** Read the active runtime mode. */
77
+ getMode() {
78
+ return this.mode;
79
+ }
80
+ /**
81
+ * Set a stable server-instance identity (TBP-172). When set, backend flags
82
+ * that explicitly opt into system-level targeting can bucket on this value
83
+ * (e.g. canary a feature to one specific instance). Most evals continue to
84
+ * use the per-call/per-context identity.
85
+ */
86
+ setServerInstanceId(id) {
87
+ if (typeof id !== 'string' || id.length === 0)
88
+ return;
89
+ this.serverInstanceIdValue = id;
90
+ }
91
+ /** Read the configured server-instance id, or undefined when not set. */
92
+ getServerInstanceId() {
93
+ return this.serverInstanceIdValue;
94
+ }
95
+ /** Replace or merge the eval context. */
96
+ setContext(ctx, merge = false) {
97
+ if (merge) {
98
+ this.context = {
99
+ identity: ctx.identity ?? this.context.identity,
100
+ attributes: { ...this.context.attributes, ...ctx.attributes },
101
+ };
102
+ }
103
+ else {
104
+ this.context = ctx;
105
+ }
106
+ }
107
+ /** Return a defensive shallow copy of the current eval context. */
108
+ getContext() {
109
+ return { identity: this.context.identity, attributes: { ...this.context.attributes } };
110
+ }
111
+ /** Replace the cache from a bulk hydrate (e.g. response from bridge-api). */
112
+ hydrate(flags) {
113
+ this.cache.clear();
114
+ for (const f of flags) {
115
+ this.cache.set(f.key, f);
116
+ }
117
+ }
118
+ /** Replace or insert a single flag (used by live updates). */
119
+ upsert(flag) {
120
+ this.cache.set(flag.key, flag);
121
+ }
122
+ /** Remove a flag from the cache. */
123
+ remove(key) {
124
+ this.cache.delete(key);
125
+ }
126
+ /** Register telemetry hooks. Replaces any previous hooks. */
127
+ setHooks(hooks) {
128
+ this.hooks = hooks;
129
+ }
130
+ /**
131
+ * Read a flag value. Returns `defaultValue` when the flag isn't known yet.
132
+ * TypeScript infers `T` from `defaultValue`, so callers don't write casts.
133
+ *
134
+ * An optional per-call `context` overrides the SDK's global context for
135
+ * just this eval. Attributes deep-merge — per-call wins on overlap, global
136
+ * keys not in the override are preserved.
137
+ */
138
+ flag(key, defaultValue, context) {
139
+ const cached = this.cache.get(key);
140
+ const now = Date.now();
141
+ const observedType = inferType(defaultValue);
142
+ // Phase 1 / US-13 — merge AttributeProvider-supplied attrs into the eval
143
+ // context on every call. Precedence (locked decision #20):
144
+ // providers (lowest) < setContext globals < per-call context (highest)
145
+ // Sync-only here: `collectSync()` skips any async provider for this eval
146
+ // (those refresh via `applyTo()` on auth/billing change events).
147
+ const providerAttrs = this.registry.size() > 0 ? this.registry.collectSync() : undefined;
148
+ let effectiveCtx;
149
+ if (!context && !providerAttrs) {
150
+ effectiveCtx = this.context;
151
+ }
152
+ else {
153
+ effectiveCtx = {
154
+ identity: context?.identity ?? this.context.identity,
155
+ attributes: {
156
+ ...(providerAttrs ?? {}),
157
+ ...this.context.attributes,
158
+ ...(context?.attributes ?? {}),
159
+ },
160
+ };
161
+ }
162
+ // Observe per-call attribute keys so the admin UI's attribute catalog
163
+ // surfaces what dev code actually supplies (powers TBP-178 collision
164
+ // detection + custom attribute autocomplete). Dedup per (key, value) in
165
+ // this runtime to keep the batcher quiet.
166
+ if (context?.attributes) {
167
+ for (const [attrKey, attrVal] of Object.entries(context.attributes)) {
168
+ if (!attrKey)
169
+ continue;
170
+ const dedupKey = `${attrKey}::${canonicalSampleKey(attrVal)}`;
171
+ if (this.observedAttributeKeys.has(dedupKey))
172
+ continue;
173
+ this.observedAttributeKeys.add(dedupKey);
174
+ this.safeHook(() => this.hooks.onAttributeObserved?.({
175
+ key: attrKey,
176
+ sampleValue: attrVal,
177
+ observedType: inferType(attrVal),
178
+ timestamp: now,
179
+ }));
180
+ }
181
+ }
182
+ // Backend mode (TBP-170): refuse to evaluate user-level rules without an
183
+ // identity. Returns the developer's `defaultValue` rather than guessing
184
+ // with anonymous bucketing. Warn once per flag per runtime.
185
+ if (this.mode === 'backend' && !effectiveCtx.identity && cached?.state === 'on-with-rule') {
186
+ if (!this.missingIdentityWarned.has(key)) {
187
+ this.missingIdentityWarned.add(key);
188
+ try {
189
+ // eslint-disable-next-line no-console
190
+ globalThis.console?.warn?.(`[bridge.flag] '${key}': backend eval requires an explicit identity; returning default. ` +
191
+ `Pass context.identity per call, or use a server-instance id for system-level flags.`);
192
+ }
193
+ catch {
194
+ // ignore console errors
195
+ }
196
+ }
197
+ return { passed: false, value: defaultValue };
198
+ }
199
+ if (!cached) {
200
+ // First sight — emit a discovery event so the server creates the
201
+ // record. Dedupe per (key) in this runtime; server is the authority.
202
+ if (!this.discoveredKeys.has(key)) {
203
+ this.discoveredKeys.add(key);
204
+ this.safeHook(() => this.hooks.onDiscover?.({
205
+ flag: key,
206
+ defaultValue,
207
+ observedType,
208
+ timestamp: now,
209
+ }));
210
+ }
211
+ return { passed: false, value: defaultValue };
212
+ }
213
+ const result = this.evaluateCached(cached, effectiveCtx);
214
+ const value = result.value === undefined ? defaultValue : result.value;
215
+ this.safeHook(() => this.hooks.onEval?.({
216
+ flag: key,
217
+ value,
218
+ variantIndex: result.variantIndex,
219
+ identity: effectiveCtx.identity,
220
+ timestamp: now,
221
+ }));
222
+ // Billing 2.0 US-10 (TBP-262): SDK-side flag_evaluations self-report.
223
+ // Fire-and-forget; the reporter handles batching + errors. One call per
224
+ // eval — there's no batched flag API, so no multiplier needed.
225
+ if (this.usageReporter) {
226
+ this.safeHook(() => this.usageReporter?.report('bridge.flag_evaluations', 1));
227
+ }
228
+ // Fail-safe type check: if the cached value doesn't match the dev's
229
+ // declared default type, fall back to the default. Protects the app
230
+ // from admin-side type mistakes (TBP-159 validation should catch most
231
+ // of these at save time; this is the last-mile safety net).
232
+ if (!typeMatches(value, observedType)) {
233
+ return { passed: false, value: defaultValue };
234
+ }
235
+ return { passed: result.matched, value };
236
+ }
237
+ /**
238
+ * Internal — evaluate the cached flag against an eval context.
239
+ */
240
+ evaluateCached(cached, ctx) {
241
+ switch (cached.state) {
242
+ case 'off':
243
+ return { value: cached.offValue, variantIndex: -1, matched: false, excludedByRollout: false };
244
+ case 'on':
245
+ return { value: cached.onValue, variantIndex: 0, matched: true, excludedByRollout: false };
246
+ case 'on-with-rule': {
247
+ if (!cached.rule) {
248
+ // Defensive: state says on-with-rule but no rule. Fall back to on.
249
+ return { value: cached.onValue, variantIndex: -1, matched: false, excludedByRollout: false };
250
+ }
251
+ return evaluateRule(cached.rule, cached.key, ctx);
252
+ }
253
+ default:
254
+ return { value: cached.offValue, variantIndex: -1, matched: false, excludedByRollout: false };
255
+ }
256
+ }
257
+ /** Number of cached flags. Useful for debugging + tests. */
258
+ cacheSize() {
259
+ return this.cache.size;
260
+ }
261
+ /** Get a snapshot of cached flag keys. */
262
+ cachedKeys() {
263
+ return Array.from(this.cache.keys());
264
+ }
265
+ /**
266
+ * @deprecated Per-call observations now power the admin attribute catalog
267
+ * automatically — just pass attributes when you evaluate a flag:
268
+ * `bridge.flag('feat', false, { attributes: { plan: 'pro' } })`.
269
+ * The SDK reports each `(key, sampleValue)` once via `onAttributeObserved`,
270
+ * which the batcher relays to `/v1/flags/discover` with `kind: 'attribute'`.
271
+ * `declareAttributes()` will be removed in a future minor release.
272
+ */
273
+ declareAttributes(declarations) {
274
+ const now = Date.now();
275
+ for (const [name, type] of Object.entries(declarations)) {
276
+ if (typeof name !== 'string' || name.length === 0)
277
+ continue;
278
+ if (this.attributeDeclarations.get(name) === type)
279
+ continue; // same as before; no-op
280
+ this.attributeDeclarations.set(name, type);
281
+ this.safeHook(() => this.hooks.onAttributeDeclaration?.({ name, type, timestamp: now }));
282
+ }
283
+ }
284
+ /** Read the current attribute type declarations. Useful for tests + framework wrappers. */
285
+ getAttributeDeclarations() {
286
+ return Object.fromEntries(this.attributeDeclarations.entries());
287
+ }
288
+ safeHook(fn) {
289
+ try {
290
+ fn();
291
+ }
292
+ catch {
293
+ // Telemetry must never break eval. Silently swallow.
294
+ }
295
+ }
296
+ }
297
+ // ── Type inference helpers ──────────────────────────────────────────────────
298
+ function inferType(v) {
299
+ if (typeof v === 'boolean')
300
+ return 'boolean';
301
+ if (typeof v === 'number')
302
+ return 'number';
303
+ if (typeof v === 'string')
304
+ return 'string';
305
+ return 'json';
306
+ }
307
+ /** Stable string key for an attribute sample value — used for in-process dedup
308
+ * in `observedAttributeKeys`. Safe for primitives + JSON-serialisable values. */
309
+ function canonicalSampleKey(v) {
310
+ if (v === undefined)
311
+ return '∅undef';
312
+ if (v === null)
313
+ return '∅null';
314
+ if (typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean')
315
+ return String(v);
316
+ try {
317
+ return JSON.stringify(v);
318
+ }
319
+ catch {
320
+ return '∅unserialisable';
321
+ }
322
+ }
323
+ function typeMatches(v, expected) {
324
+ if (v === undefined || v === null)
325
+ return false;
326
+ switch (expected) {
327
+ case 'boolean':
328
+ return typeof v === 'boolean';
329
+ case 'number':
330
+ return typeof v === 'number';
331
+ case 'string':
332
+ return typeof v === 'string';
333
+ case 'json':
334
+ return true;
335
+ }
336
+ }
337
+ //# sourceMappingURL=flag.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flag.js","sourceRoot":"","sources":["../../src/flags/flag.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,77 @@
1
+ import type { BridgeFlags } from './flag.js';
2
+ /** How aggressively the anon ID is persisted. */
3
+ export type AnonymousTrackingMode = 'persistent' | 'session' | 'none';
4
+ /**
5
+ * Pluggable storage for the anonymous identity. Framework SDKs implement
6
+ * this against the right platform API.
7
+ */
8
+ export interface IdentityStorage {
9
+ /** Persistence flavor — informational only; storage choice itself enforces it. */
10
+ readonly mode: AnonymousTrackingMode;
11
+ /** Read the stored anon ID, or undefined if not set. */
12
+ read(): string | undefined;
13
+ /** Write the anon ID. */
14
+ write(id: string): void;
15
+ /** Drop the anon ID (e.g. on logout). */
16
+ clear(): void;
17
+ }
18
+ /** In-memory storage. Used for SSR, tests, or when `tracking: 'none'`. */
19
+ export declare class MemoryIdentityStorage implements IdentityStorage {
20
+ readonly mode: AnonymousTrackingMode;
21
+ private value;
22
+ constructor(mode?: AnonymousTrackingMode);
23
+ read(): string | undefined;
24
+ write(id: string): void;
25
+ clear(): void;
26
+ }
27
+ /** RFC4122-shaped UUID v4. Uses `crypto.randomUUID` when available, falls back to Math.random. */
28
+ export declare function generateAnonymousId(): string;
29
+ /**
30
+ * Identity manager bundled with a `BridgeFlags` instance. Created via the
31
+ * factory below — framework SDKs call `attachIdentity(bridge, storage)`
32
+ * during bootstrap so anonymous ID auto-populates the eval context.
33
+ */
34
+ export declare class BridgeIdentity {
35
+ private readonly bridge;
36
+ private readonly storage;
37
+ /** Fires when the identity transitions from anonymous → known (TBP-169). */
38
+ private onIdentifyHook?;
39
+ /** True once `identify()` has been called (or a non-anonymous identity was loaded). */
40
+ private knownUserId?;
41
+ constructor(bridge: BridgeFlags, storage: IdentityStorage);
42
+ /** Read the current anonymous ID, generating + persisting on first call. */
43
+ ensureAnonymousId(): string;
44
+ /**
45
+ * Push the anonymous ID into the BridgeFlags global eval context. Idempotent.
46
+ * No-op if a known userId is already set.
47
+ */
48
+ hydrateAnonymous(): string;
49
+ /**
50
+ * Promote the SDK from anonymous to known. Updates the global identity in
51
+ * `BridgeFlags` and fires the `onIdentify` hook so the server can link
52
+ * anon-id telemetry to the known user's history.
53
+ */
54
+ identify(userId: string): void;
55
+ /**
56
+ * Drop the known identity and revert to anonymous. Used on logout. The
57
+ * anonymous ID itself is preserved unless `dropAnonymous` is also passed.
58
+ */
59
+ logout(options?: {
60
+ dropAnonymous?: boolean;
61
+ }): void;
62
+ /** Register a one-time hook fired on identify(). Used by the SDK telemetry batcher. */
63
+ setOnIdentify(hook: (args: {
64
+ anonymousId?: string;
65
+ userId: string;
66
+ }) => void): void;
67
+ /** True when `identify()` has set a known user. */
68
+ isKnown(): boolean;
69
+ /** Read the current effective identity (known userId or anonymous ID). */
70
+ current(): string | undefined;
71
+ }
72
+ /**
73
+ * Factory: attach an anonymous-aware identity manager to a `BridgeFlags`
74
+ * instance. Pass a storage implementation appropriate for the platform.
75
+ */
76
+ export declare function attachIdentity(bridge: BridgeFlags, storage: IdentityStorage): BridgeIdentity;
77
+ //# sourceMappingURL=identity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../../src/flags/identity.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAE7C,iDAAiD;AACjD,MAAM,MAAM,qBAAqB,GAAG,YAAY,GAAG,SAAS,GAAG,MAAM,CAAC;AAEtE;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,kFAAkF;IAClF,QAAQ,CAAC,IAAI,EAAE,qBAAqB,CAAC;IACrC,wDAAwD;IACxD,IAAI,IAAI,MAAM,GAAG,SAAS,CAAC;IAC3B,yBAAyB;IACzB,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,yCAAyC;IACzC,KAAK,IAAI,IAAI,CAAC;CACf;AAED,0EAA0E;AAC1E,qBAAa,qBAAsB,YAAW,eAAe;IAC3D,QAAQ,CAAC,IAAI,EAAE,qBAAqB,CAAC;IACrC,OAAO,CAAC,KAAK,CAAqB;gBAEtB,IAAI,GAAE,qBAA8B;IAIhD,IAAI,IAAI,MAAM,GAAG,SAAS;IAG1B,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAGvB,KAAK,IAAI,IAAI;CAGd;AAED,kGAAkG;AAClG,wBAAgB,mBAAmB,IAAI,MAAM,CAS5C;AAED;;;;GAIG;AACH,qBAAa,cAAc;IAOvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAP1B,4EAA4E;IAC5E,OAAO,CAAC,cAAc,CAAC,CAA2D;IAClF,uFAAuF;IACvF,OAAO,CAAC,WAAW,CAAC,CAAS;gBAGV,MAAM,EAAE,WAAW,EACnB,OAAO,EAAE,eAAe;IAG3C,4EAA4E;IAC5E,iBAAiB,IAAI,MAAM;IAQ3B;;;OAGG;IACH,gBAAgB,IAAI,MAAM;IAO1B;;;;OAIG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAc9B;;;OAGG;IACH,MAAM,CAAC,OAAO,GAAE;QAAE,aAAa,CAAC,EAAE,OAAO,CAAA;KAAO,GAAG,IAAI;IAMvD,uFAAuF;IACvF,aAAa,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE;QAAE,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,GAAG,IAAI;IAInF,mDAAmD;IACnD,OAAO,IAAI,OAAO;IAIlB,0EAA0E;IAC1E,OAAO,IAAI,MAAM,GAAG,SAAS;CAI9B;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,eAAe,GAAG,cAAc,CAK5F"}