flagmint-js-sdk 2.2.0 → 2.2.2-rc.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
@@ -1,6 +1,7 @@
1
1
  export { FlagClient } from './sdk/core/client';
2
2
  export type { FlagClientOptions } from './sdk/core/client';
3
3
  export type { CacheAdapter, FeatureFlags } from './sdk/core/helpers/types';
4
+ export type { ApplicationEvent, ApplicationEventKind } from './sdk/core/helpers/applicationEvents';
4
5
  export * as syncCache from './sdk/core/helpers/cacheHelper';
5
6
  export * as asyncCache from './sdk/core/helpers/cacheHelper.async';
6
7
  export type { Transport } from './sdk/core/transports/Transport';
@@ -11,3 +12,5 @@ export { evaluateFlagValue } from './sdk/core/evaluation/evaluateFlagValue';
11
12
  export { evaluateRollout } from './sdk/core/evaluation/evaluateRollout';
12
13
  export { isInSegment } from './sdk/core/evaluation/isInSegment';
13
14
  export * from './sdk/core/evaluation/rolloutUtils';
15
+ export { RulesStore, createEmptyRulesState, reduceRules, performAslHandshake, generateAslClientKeyPair, deriveAslMacKey, signConfigPayload, verifyConfigPayloadSignature, canonicalizeForSigning, isConfigPayloadExpired, evaluateSdkFlag, evaluateAllSdkFlags, coerceType, evaluateRule, evaluateFlagWithTargetingRules, applyRolloutStrategy, computeCurrentPercentage, defaultHash, flattenEvaluationContext, prepareContextForLocalEval, hashPiiValue, applyPiiHashToFlatContext, stringHash, hashPercent, } from './sdk/core/config-sync';
16
+ export type { SdkFlagConfig, SdkSegment, RulesState, RulesCacheSnapshot, ConfigSyncPayload, LeasePayload, FullConfigPayload, DeltaConfigPayload, DeltasCatchUpPayload, ApplyResult, AslHandshakeSuccess, PiiWireOptions, } from './sdk/core/config-sync';
@@ -1,6 +1,8 @@
1
1
  import { CacheAdapter, FeatureFlags } from '../core/helpers/types';
2
2
  import type { Transport } from '../core/transports/Transport';
3
3
  import { FlagValue } from '../core/evaluation/types';
4
+ import { type ConnectionShareOptions } from '../core/helpers/connectionShare';
5
+ import { RulesStore } from '../core/config-sync';
4
6
  type TransportMode = 'auto' | 'long-polling' | 'sse';
5
7
  export interface FlagClientOptions<C extends Record<string, any> = Record<string, any>> {
6
8
  apiKey: string;
@@ -9,6 +11,14 @@ export interface FlagClientOptions<C extends Record<string, any> = Record<string
9
11
  persistContext?: boolean;
10
12
  transport?: Transport<C, any>;
11
13
  transportMode?: TransportMode;
14
+ /**
15
+ * Enable SDK config-sync (local evaluation path).
16
+ * Performs X25519 ECDH on ASL handshake and derives a session MAC key used
17
+ * to verify signed `lease` / `fullConfig` / `delta` payloads.
18
+ * Default false — keeps the legacy evaluated-flags stream.
19
+ * When true, `shareConnection` defaults to false (rules are not shared across tabs yet).
20
+ */
21
+ configSync?: boolean;
12
22
  /**
13
23
  * Called when a non-fatal but noteworthy error occurs during or after initialization.
14
24
  * The client always resolves ready() regardless — use this to show a degraded/fallback UI.
@@ -28,7 +38,19 @@ export interface FlagClientOptions<C extends Record<string, any> = Record<string
28
38
  deferInitialization?: boolean;
29
39
  cacheAdapter?: CacheAdapter<C>;
30
40
  restEndpoint?: string;
41
+ /**
42
+ * SSE stream base (`…/evaluator/v2/flags`). Defaults to the stream host
43
+ * (`stream.flagmint.com` / `staging-stream…`). EventSource only — context
44
+ * POST uses the API host (see `flagsEndpoint` / `restEndpoint`).
45
+ */
31
46
  sseEndpoint?: string;
47
+ /**
48
+ * API base for `POST …/context` (`…/evaluator/v2/flags`). Defaults from
49
+ * `restEndpoint`. Override for self-hosted splits that do not match evaluate→v2/flags.
50
+ */
51
+ flagsEndpoint?: string;
52
+ /** Override the ASL handshake URL. Use with `sseEndpoint` / `restEndpoint` for self-hosted gateways. */
53
+ handshakeEndpoint?: string;
32
54
  debugLog?: boolean;
33
55
  env?: string;
34
56
  enableFlagmint: boolean;
@@ -37,6 +59,22 @@ export interface FlagClientOptions<C extends Record<string, any> = Record<string
37
59
  version: string;
38
60
  };
39
61
  EventSourceImpl?: any;
62
+ /**
63
+ * Share one streaming connection across same-origin documents (tabs / iframes)
64
+ * that use the same API key. Defaults to true in browsers with BroadcastChannel,
65
+ * false in Node (cluster workers each keep their own stream).
66
+ *
67
+ * Followers do not handshake or open EventSource. They receive flags from the
68
+ * leader and forward updateContext() to it. Do not enable this when two clients
69
+ * must evaluate different contexts at the same time on one page, or when the
70
+ * origin hosts untrusted documents that should not steer or read this stream.
71
+ */
72
+ shareConnection?: boolean;
73
+ /**
74
+ * Test/runtime hooks for the share hub (channel, storage, timers).
75
+ * Omit in production.
76
+ */
77
+ share?: ConnectionShareOptions;
40
78
  }
41
79
  type FlagUpdateCallback<T> = (flags: FeatureFlags<T>) => void;
42
80
  export declare class FlagClient<T = unknown, C extends Record<string, any> = Record<string, any>> {
@@ -49,8 +87,19 @@ export declare class FlagClient<T = unknown, C extends Record<string, any> = Rec
49
87
  private cacheTTL;
50
88
  private transport;
51
89
  private restEndpoint;
90
+ private eventsEndpoint;
52
91
  private aslHandshakeUrl;
53
92
  private sseEndpoint;
93
+ /** API host base for POST /context (stream host may be SSE-only). */
94
+ private flagsApiEndpoint;
95
+ private eventQueue;
96
+ private eventFlushTimer;
97
+ /** Coalesced getFlag hits waiting for EVAL_REPORT_FLUSH_MS. */
98
+ private evaluationReportQueue;
99
+ private evaluationReportTimer;
100
+ /** null = server has not sent a map yet; send and let ingest drop. */
101
+ private analyticsByFlag;
102
+ private contextUpdateSeq;
54
103
  private readyPromise;
55
104
  private resolveReady;
56
105
  private rejectReady;
@@ -64,6 +113,12 @@ export declare class FlagClient<T = unknown, C extends Record<string, any> = Rec
64
113
  private initializationOptions?;
65
114
  private isInitialized;
66
115
  private subscribers;
116
+ private shareHub;
117
+ private shareConnection;
118
+ private configSync;
119
+ private rulesStore;
120
+ /** Avoid stacking reconnects when many getFlag calls hit an expired lease. */
121
+ private leaseRenewInFlight;
67
122
  /**
68
123
  * Creates a new FlagClient instance.
69
124
  * @param options - Configuration options for the client.
@@ -73,8 +128,14 @@ export declare class FlagClient<T = unknown, C extends Record<string, any> = Rec
73
128
  * Initializes the client by loading cached flags and context, setting up the transport layer.
74
129
  */
75
130
  private initialize;
131
+ private bindTransport;
132
+ /**
133
+ * @returns true when this document is a follower and should skip opening a stream.
134
+ */
135
+ private joinConnectionShare;
136
+ private attachShareLeader;
76
137
  /**
77
- * Creates and initializes the transport layer used to receive feature flag
138
+ * Creates and initializes the transport layer used to receive feature flags
78
139
  * updates from the Flagmint platform.
79
140
  *
80
141
  * This method performs the authentication handshake to obtain a fresh session
@@ -119,14 +180,38 @@ export declare class FlagClient<T = unknown, C extends Record<string, any> = Rec
119
180
  subscribe(callback: FlagUpdateCallback<T>): () => void;
120
181
  /**
121
182
  * Get all flags.
183
+ * Config-sync: if the lease expired, fail-closed to defaults and reconnect.
122
184
  */
123
185
  getFlags(): FeatureFlags<T>;
124
186
  /**
125
187
  * Get a single flag value.
188
+ * When analytics is on for the flag, queues a debounced call-site evaluation
189
+ * report (dashboard Evaluations / unique users). Does not consume billing quota.
190
+ * Config-sync: expired lease → defaults only, then drop/reconnect (24h renew).
126
191
  */
127
192
  getFlag<K extends keyof FeatureFlags<T>>(key: K, fallback?: FeatureFlags<T>[K]): FeatureFlags<T>[K];
193
+ private queueEvaluationReport;
194
+ private flushEvaluationReports;
195
+ /**
196
+ * Report an application error that happened after this flag was served.
197
+ * Fire-and-forget: events are batched and never throw.
198
+ * No-ops when Analytics Tracking is off for that flag.
199
+ */
200
+ trackError(flagKey: string, error?: unknown, extra?: Record<string, unknown>): void;
201
+ /**
202
+ * Report a custom metric event attributed to the currently served variation.
203
+ * Fire-and-forget: events are batched and never throw.
204
+ * No-ops when Analytics Tracking is off for that flag.
205
+ */
206
+ track(flagKey: string, eventName: string, extra?: Record<string, unknown>): void;
207
+ /**
208
+ * Whether Analytics Tracking is on for this flag in the last streamed payload.
209
+ * `undefined` means the server has not sent a map yet (older API / long-polling).
210
+ */
211
+ isAnalyticsEnabled(flagKey: string): boolean | undefined;
128
212
  /**
129
213
  * Update the evaluation context.
214
+ * Config-sync: renews the lease stream first if expired (still evaluates locally).
130
215
  */
131
216
  updateContext(context: C): Promise<void>;
132
217
  /**
@@ -142,8 +227,8 @@ export declare class FlagClient<T = unknown, C extends Record<string, any> = Rec
142
227
  * become available (or until the specified timeout expires) before awaiting
143
228
  * completion of the underlying transport initialization.
144
229
  *
145
- * This method guarantees that transport or connection failures are surfaced,
146
- * even when feature flags have already been restored from cache.
230
+ * Transport and connection failures do not reject this promise. The client reports them through the `onError` option and resolves anyway, so the
231
+ * application can render a fallback or degraded UI.
147
232
  *
148
233
  * @param {number} [timeoutMs=3000] - The maximum amount of time, in
149
234
  * milliseconds, to wait for the initial feature flag payload before
@@ -152,8 +237,7 @@ export declare class FlagClient<T = unknown, C extends Record<string, any> = Rec
152
237
  * @returns {Promise<void>} A promise that resolves when the client is fully
153
238
  * initialized and ready to evaluate feature flags.
154
239
  *
155
- * @throws {Error} If client initialization or the underlying transport fails,
156
- * such as authentication, rate limiting, or connection errors.
240
+ *
157
241
  */
158
242
  ready(timeoutMs?: number): Promise<void>;
159
243
  /**
@@ -180,6 +264,18 @@ export declare class FlagClient<T = unknown, C extends Record<string, any> = Rec
180
264
  * flag payload is received or when the timeout expires, whichever occurs first.
181
265
  */
182
266
  private waitForFlags;
267
+ private updateAnalytics;
268
+ private queueApplicationEvent;
269
+ private flushApplicationEvents;
270
+ /**
271
+ * If a previously-ready config-sync lease just crossed expiresAt: fail-closed
272
+ * to defaults, persist, notify once, and reconnect for fullConfig.
273
+ * Bootstrapping or already-expired stores return without side effects so
274
+ * getFlag/getFlags cannot re-enter via subscriber updates.
275
+ *
276
+ * @param now Wall clock for expiry check
277
+ */
278
+ private ensureConfigSyncLeaseFresh;
183
279
  /**
184
280
  * Performs the initial authentication handshake with the Flagmint server
185
281
  * to obtain a fresh, single-use session identifier.
@@ -201,5 +297,17 @@ export declare class FlagClient<T = unknown, C extends Record<string, any> = Rec
201
297
  * @throws {Error} If the handshake response does not contain a valid session ID.
202
298
  */
203
299
  private fetchFreshSessionId;
300
+ /** Config-sync rules store (null when `configSync` is false). */
301
+ getRulesStore(): RulesStore | null;
302
+ /**
303
+ * Restore rules from localCache before the stream opens.
304
+ * Fresh lease → reconnect with sinceVersion; expired → fullConfig.
305
+ */
306
+ private hydrateRulesFromCache;
307
+ /** Persist rules after a successful (or expiry/gap) apply so reconnect can catch up. */
308
+ private persistRulesSnapshot;
309
+ private handleConfigSyncEvent;
310
+ private evaluateFromRulesStore;
311
+ private analyticsMapFromRulesStore;
204
312
  }
205
313
  export {};
@@ -3,6 +3,7 @@
3
3
  * using the ported `flagEvaluator` (spec = utils/flag-evaluator.ts).
4
4
  */
5
5
  import { type FlagEvaluationResult } from './flagEvaluator';
6
+ import { type PiiWireOptions } from './piiHash';
6
7
  import type { SdkFlagConfig, SdkSegment } from './types';
7
8
  /** ES2018-safe Map → record (avoids Object.fromEntries / ES2019 lib). */
8
9
  export declare function mapToRecord<V>(map: Map<string, V>): Record<string, V>;
@@ -16,11 +17,33 @@ export declare function mapToRecord<V>(map: Map<string, V>): Record<string, V>;
16
17
  * and organization is not dropped.
17
18
  */
18
19
  export declare function prepareContextForEvaluator(context: Record<string, unknown> | null | undefined): Record<string, unknown>;
20
+ /**
21
+ * Flatten prepared context, then hash PII fields so they match hashed rule
22
+ * values on the config-sync wire (FF-EU Phase B / SDK Phase C).
23
+ *
24
+ * @param context Evaluation or flat context
25
+ * @param pii PII wire options from the rules store (null/undefined = no-op)
26
+ */
27
+ export declare function prepareContextForLocalEval(context: Record<string, unknown> | null | undefined, pii?: PiiWireOptions | null): Record<string, unknown>;
19
28
  /**
20
29
  * Evaluate one SdkFlagConfig against context (server RuleEngine order).
30
+ *
31
+ * @param flag Compiled flag config
32
+ * @param context Evaluation context (plaintext PII is hashed when `pii` is set)
33
+ * @param segments Segment map for segment rules
34
+ * @param pii Optional PII wire options from the signed config
35
+ */
36
+ export declare function evaluateSdkFlag(flag: SdkFlagConfig, context: Record<string, unknown>, segments: Map<string, SdkSegment> | Record<string, SdkSegment>, pii?: PiiWireOptions | null): FlagEvaluationResult;
37
+ /**
38
+ * Evaluate every flag in a map/list against one context.
39
+ *
40
+ * @param flags Flag configs
41
+ * @param context Shared evaluation context
42
+ * @param segments Segment map
43
+ * @param options Fail-closed and PII wire options
21
44
  */
22
- export declare function evaluateSdkFlag(flag: SdkFlagConfig, context: Record<string, unknown>, segments: Map<string, SdkSegment> | Record<string, SdkSegment>): FlagEvaluationResult;
23
45
  export declare function evaluateAllSdkFlags<T = unknown>(flags: Map<string, SdkFlagConfig> | SdkFlagConfig[], context: Record<string, unknown>, segments: Map<string, SdkSegment> | Record<string, SdkSegment>, options?: {
24
46
  failClosedDefaultsOnly?: boolean;
47
+ pii?: PiiWireOptions | null;
25
48
  }): Record<string, T>;
26
49
  export { coerceType } from './flagEvaluator';
@@ -3,10 +3,12 @@ export type { AslClientKeyPair, AslDerivedMac } from './aslEcdh';
3
3
  export { canonicalizeForSigning, isConfigPayloadExpired, signConfigPayload, verifyConfigPayloadSignature, } from './signPayload';
4
4
  export { RulesStore, createEmptyRulesState, reduceRules, } from './rulesStore';
5
5
  export type { ApplyResult, RulesState } from './rulesStore';
6
- export type { ConfigSyncPayload, DeltaConfigPayload, DeltasCatchUpPayload, FullConfigPayload, LeasePayload, RulesCacheSnapshot, SdkFlagConfig, SdkSegment, SignableConfigBody, } from './types';
6
+ export type { ConfigSyncPayload, DeltaConfigPayload, DeltasCatchUpPayload, FullConfigPayload, LeasePayload, PiiWireMetadata, RulesCacheSnapshot, SdkFlagConfig, SdkSegment, SignableConfigBody, } from './types';
7
7
  export { performAslHandshake } from './handshake';
8
8
  export type { AslHandshakeSuccess } from './handshake';
9
- export { evaluateSdkFlag, evaluateAllSdkFlags, coerceType, prepareContextForEvaluator, mapToRecord, } from './evaluateSdkFlag';
9
+ export { evaluateSdkFlag, evaluateAllSdkFlags, coerceType, prepareContextForEvaluator, prepareContextForLocalEval, mapToRecord, } from './evaluateSdkFlag';
10
+ export { PII_VALUE_ENCODING, normalizePiiString, hashPiiValue, isPiiAttribute, piiWireOptionsFromPayload, applyPiiHashToFlatContext, } from './piiHash';
11
+ export type { PiiWireOptions } from './piiHash';
10
12
  export { evaluateRule, evaluateKillSwitch, evaluateOnVariation, evaluateFlagWithTargetingRules, applyRolloutStrategy, applyPercentageRollout, applyVariantRollout, applyGradualRollout, computeCurrentPercentage, defaultHash, getContextAttribute, } from './flagEvaluator';
11
13
  export type { ExpectedFlagType, FlagEvaluationResult, Condition, TargetingRule, Rollout, Variation, Segment, EvaluationDeps, } from './flagEvaluator';
12
14
  export { flattenContext, flattenEvaluationContext } from './flattenContext';
@@ -0,0 +1,53 @@
1
+ /** Must match FF-EU `PII_VALUE_ENCODING`. */
2
+ export declare const PII_VALUE_ENCODING: "hmac-v1";
3
+ /**
4
+ * Client PII wire options from signed `fullConfig` / `delta`.
5
+ */
6
+ export type PiiWireOptions = {
7
+ piiAttributes: string[];
8
+ piiSalt: string;
9
+ piiValueEncoding?: typeof PII_VALUE_ENCODING | string;
10
+ };
11
+ /**
12
+ * Normalize a string before PII hashing (trim + lowercase).
13
+ * Must match FF-EU `normalizePiiString`.
14
+ *
15
+ * @param value Raw attribute value
16
+ */
17
+ export declare function normalizePiiString(value: string): string;
18
+ /**
19
+ * HMAC-SHA256 hex digest of a normalized PII string.
20
+ * Must match FF-EU `hashPiiValue`.
21
+ *
22
+ * @param value Plaintext value
23
+ * @param salt Client-visible salt from the signed config
24
+ */
25
+ export declare function hashPiiValue(value: string, salt: string): string;
26
+ /**
27
+ * Whether a context / rule attribute key is PII.
28
+ * Matches bare keys (`email`) and kind-prefixed forms (`user.email`).
29
+ *
30
+ * @param attribute Attribute key from context or rule
31
+ * @param piiAttributes Keys from signed config
32
+ */
33
+ export declare function isPiiAttribute(attribute: string | undefined | null, piiAttributes: readonly string[]): boolean;
34
+ /**
35
+ * Parse PII wire options from a config-sync payload body.
36
+ *
37
+ * @param payload Signed fullConfig / delta fields
38
+ * @returns Options when salt + encoding are usable; otherwise null
39
+ */
40
+ export declare function piiWireOptionsFromPayload(payload: {
41
+ piiAttributes?: unknown;
42
+ piiSalt?: unknown;
43
+ piiValueEncoding?: unknown;
44
+ } | null | undefined): PiiWireOptions | null;
45
+ /**
46
+ * Hash PII string fields on a flat evaluation context so they match hashed
47
+ * rule values on the wire.
48
+ *
49
+ * @param context Flat context map (after prepare / flatten)
50
+ * @param options PII options from the rules store
51
+ * @returns New context object with PII strings hashed (or the input if no-op)
52
+ */
53
+ export declare function applyPiiHashToFlatContext(context: Record<string, unknown>, options: PiiWireOptions | null | undefined): Record<string, unknown>;
@@ -1,3 +1,4 @@
1
+ import { type PiiWireOptions } from './piiHash';
1
2
  import type { ConfigSyncPayload, DeltaConfigPayload, DeltasCatchUpPayload, FullConfigPayload, LeasePayload, RulesCacheSnapshot, SdkFlagConfig, SdkSegment } from './types';
2
3
  export type RulesState = {
3
4
  version: number;
@@ -8,6 +9,8 @@ export type RulesState = {
8
9
  ready: boolean;
9
10
  /** Set when a delta's fromVersion does not match — reconnect with fullConfig. */
10
11
  needsFullConfig: boolean;
12
+ /** PII hash options from the last fullConfig/delta (null when absent). */
13
+ piiWire: PiiWireOptions | null;
11
14
  };
12
15
  export type ApplyResult = {
13
16
  ok: true;
@@ -36,6 +39,10 @@ export declare class RulesStore {
36
39
  * Fail-closed readiness: lease not expired and store marked ready.
37
40
  */
38
41
  isReady(now?: number): boolean;
42
+ /**
43
+ * Mark the store fail-closed after wall-clock lease expiry.
44
+ * Next reconnect must request fullConfig (or a fresh lease + catch-up).
45
+ */
39
46
  markExpired(): void;
40
47
  getFlag(key: string): SdkFlagConfig | undefined;
41
48
  /**
@@ -33,6 +33,12 @@ export type LeasePayload = {
33
33
  expiresAt: number;
34
34
  signature: string;
35
35
  };
36
+ /** PII hashing metadata on client-bound config (FF-EU Phase B). */
37
+ export type PiiWireMetadata = {
38
+ piiAttributes?: string[];
39
+ piiSalt?: string;
40
+ piiValueEncoding?: string;
41
+ };
36
42
  export type FullConfigPayload = {
37
43
  type: 'fullConfig';
38
44
  version: number;
@@ -42,7 +48,7 @@ export type FullConfigPayload = {
42
48
  segments: Record<string, SdkSegment>;
43
49
  warnings?: string[];
44
50
  signature: string;
45
- };
51
+ } & PiiWireMetadata;
46
52
  export type DeltaConfigPayload = {
47
53
  type: 'delta';
48
54
  fromVersion: number;
@@ -53,7 +59,7 @@ export type DeltaConfigPayload = {
53
59
  deletes: string[];
54
60
  segments: Record<string, SdkSegment>;
55
61
  signature: string;
56
- };
62
+ } & PiiWireMetadata;
57
63
  export type DeltasCatchUpPayload = {
58
64
  type: 'deltas';
59
65
  fromVersion: number;
@@ -69,4 +75,8 @@ export type RulesCacheSnapshot = {
69
75
  expiresAt: number;
70
76
  flags: SdkFlagConfig[];
71
77
  segments: Record<string, SdkSegment>;
78
+ /** PII wire options so offline hydrate still hashes context correctly. */
79
+ piiAttributes?: string[];
80
+ piiSalt?: string;
81
+ piiValueEncoding?: string;
72
82
  };
@@ -1,12 +1,16 @@
1
1
  export declare const MAX_EVENT_BATCH = 20;
2
2
  export declare const EVENT_FLUSH_MS = 2000;
3
- export type ApplicationEventKind = 'error' | 'custom';
3
+ /** Debounce window for coalescing getFlag call-site evaluation reports. */
4
+ export declare const EVAL_REPORT_FLUSH_MS = 2000;
5
+ export type ApplicationEventKind = 'error' | 'custom' | 'evaluation';
4
6
  export interface ApplicationEvent {
5
7
  flagKey: string;
6
8
  kind: ApplicationEventKind;
7
9
  eventName?: string;
8
10
  variationValue?: unknown;
9
11
  userKey?: string;
12
+ /** Coalesced getFlag hits (evaluation kind only). */
13
+ count?: number;
10
14
  timestamp: string;
11
15
  extra?: Record<string, unknown>;
12
16
  }
@@ -1,4 +1,5 @@
1
1
  import { FeatureFlags } from '../../core/helpers/types';
2
+ import type { RulesCacheSnapshot } from '../../core/config-sync/types';
2
3
  export interface AsyncStorageAdapter {
3
4
  getItem(key: string): Promise<string | null>;
4
5
  setItem(key: string, value: string): Promise<void>;
@@ -9,3 +10,6 @@ export declare function loadCachedFlags<T>(apiKey: string, ttl: number): Promise
9
10
  export declare function saveCachedFlags<T>(apiKey: string, data: FeatureFlags<T>): Promise<void>;
10
11
  export declare function loadCachedContext<C>(apiKey: string): Promise<C | null>;
11
12
  export declare function saveCachedContext<C>(apiKey: string, context: C): Promise<void>;
13
+ export declare function loadCachedRulesSnapshot(apiKey: string): Promise<RulesCacheSnapshot | null>;
14
+ export declare function saveCachedRulesSnapshot(apiKey: string, snapshot: RulesCacheSnapshot): Promise<void>;
15
+ export declare function clearCachedRulesSnapshot(apiKey: string): Promise<void>;
@@ -1,4 +1,5 @@
1
1
  import { FeatureFlags } from '../../core/helpers/types';
2
+ import type { RulesCacheSnapshot } from '../../core/config-sync/types';
2
3
  export interface StorageAdapter {
3
4
  getItem(key: string): string | null;
4
5
  setItem(key: string, value: string): void;
@@ -9,3 +10,7 @@ export declare function loadCachedFlags<T>(apiKey: string, ttl: number): Feature
9
10
  export declare function saveCachedFlags<T>(apiKey: string, data: FeatureFlags<T>): void;
10
11
  export declare function loadCachedContext<C>(apiKey: string): C | null;
11
12
  export declare function saveCachedContext<C>(apiKey: string, context: C): void;
13
+ /** Config-sync rules localCache — no wall-clock TTL; lease `expiresAt` gates reuse. */
14
+ export declare function loadCachedRulesSnapshot(apiKey: string): RulesCacheSnapshot | null;
15
+ export declare function saveCachedRulesSnapshot(apiKey: string, snapshot: RulesCacheSnapshot): void;
16
+ export declare function clearCachedRulesSnapshot(apiKey: string): void;
@@ -1,7 +1,14 @@
1
+ import type { RulesCacheSnapshot } from '../../core/config-sync/types';
1
2
  export type FeatureFlags<T = unknown> = Record<string, T>;
2
3
  export interface CacheAdapter<C extends Record<string, any> = any> {
3
4
  loadFlags(apiKey: string, ttl: number): Promise<FeatureFlags> | FeatureFlags | null;
4
5
  saveFlags(apiKey: string, data: FeatureFlags): Promise<void> | void;
5
6
  loadContext(apiKey: string): Promise<C | null> | C | null;
6
7
  saveContext(apiKey: string, ctx: C): Promise<void> | void;
8
+ /**
9
+ * Config-sync localCache (optional). When omitted, rules are not persisted
10
+ * across reloads — every boot is a cold start (`fullConfig=true`).
11
+ */
12
+ loadRulesSnapshot?(apiKey: string): Promise<RulesCacheSnapshot | null> | RulesCacheSnapshot | null;
13
+ saveRulesSnapshot?(apiKey: string, snapshot: RulesCacheSnapshot): Promise<void> | void;
7
14
  }
@@ -13,6 +13,7 @@ export declare class LongPollingTransport<C, T> implements Transport<C, T> {
13
13
  private backoffMultiplier;
14
14
  private pollTimeoutId;
15
15
  private onUpdateCallback?;
16
+ private onAnalyticsUpdatedCallback?;
16
17
  private currentContext;
17
18
  private currentFlags;
18
19
  private consecutiveErrors;
@@ -23,7 +24,11 @@ export declare class LongPollingTransport<C, T> implements Transport<C, T> {
23
24
  private poll;
24
25
  private applyBackoff;
25
26
  private flagsChanged;
26
- fetchFlags(context: C): Promise<Record<string, T>>;
27
+ fetchFlags(context: C, options?: {
28
+ persist?: boolean;
29
+ }): Promise<Record<string, T>>;
30
+ private readQuotaPayload;
27
31
  onFlagsUpdated(callback: (flags: Record<string, T>) => void): void;
32
+ onAnalyticsUpdated(callback: (analytics: Record<string, boolean>) => void): void;
28
33
  destroy(): void;
29
34
  }
@@ -106,6 +106,13 @@ export declare class SseTransport<C, T> implements Transport<C, T> {
106
106
  onAnalyticsUpdated(callback: (analytics: Record<string, boolean>) => void): void;
107
107
  onError(callback: (error: Error) => void): void;
108
108
  destroy(): void;
109
+ /**
110
+ * Drop the live stream and reconnect (fresh ASL ticket).
111
+ * Used when the config-sync lease expires so the next open can request fullConfig.
112
+ *
113
+ * @param reason Lifecycle reason for debug logs
114
+ */
115
+ requestReconnect(reason?: string): void;
109
116
  /**
110
117
  * Opt-in SSE lifecycle lines (`FlagClient` `debugLog: true`).
111
118
  * Example: `[SseTransport] disconnected connectionId=… upMs=900012 reason=network_error retryInMs=1500`
@@ -1,7 +1,13 @@
1
1
  export interface Transport<C, T> {
2
2
  init(onUpdate?: (flags: Record<string, T>) => void): Promise<void>;
3
- fetchFlags(context: C): Promise<Record<string, T>>;
3
+ fetchFlags(context: C, options?: {
4
+ persist?: boolean;
5
+ }): Promise<Record<string, T>>;
4
6
  destroy(): void;
7
+ /** Optional: drop + reconnect (SSE). Used for config-sync lease renewal. */
8
+ requestReconnect?(reason?: string): void;
5
9
  onFlagsUpdated?: (callback: (flags: Record<string, T>) => void) => void;
10
+ onAnalyticsUpdated?: (callback: (analytics: Record<string, boolean>) => void) => void;
6
11
  onContextUpdated?: (callback: (context: C) => void) => void;
12
+ onError?: (callback: (error: Error) => void) => void;
7
13
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flagmint-js-sdk",
3
- "version": "2.2.0",
3
+ "version": "2.2.2-rc.0",
4
4
  "description": "Framework-agnostic JavaScript SDK for managing feature flags in Flagmint applications.",
5
5
  "author": "Flagmint Team",
6
6
  "license": "BSD 3-Clause",