@featureflip/edge 0.0.0-stage → 2.14.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 (37) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +88 -2
  3. package/dist/edge-sdk/src/client.d.ts +35 -0
  4. package/dist/edge-sdk/src/client.d.ts.map +1 -0
  5. package/dist/edge-sdk/src/config.d.ts +42 -0
  6. package/dist/edge-sdk/src/config.d.ts.map +1 -0
  7. package/dist/edge-sdk/src/index.d.ts +7 -0
  8. package/dist/edge-sdk/src/index.d.ts.map +1 -0
  9. package/dist/edge-sdk/src/snapshot.d.ts +61 -0
  10. package/dist/edge-sdk/src/snapshot.d.ts.map +1 -0
  11. package/dist/edge-sdk/src/store.d.ts +21 -0
  12. package/dist/edge-sdk/src/store.d.ts.map +1 -0
  13. package/dist/index.d.ts +2 -0
  14. package/dist/index.mjs +947 -0
  15. package/dist/js-sdk/src/core/evaluator.d.ts +37 -0
  16. package/dist/js-sdk/src/core/evaluator.d.ts.map +1 -0
  17. package/dist/js-sdk/src/core/events.d.ts +77 -0
  18. package/dist/js-sdk/src/core/events.d.ts.map +1 -0
  19. package/dist/js-sdk/src/core/identity.d.ts +13 -0
  20. package/dist/js-sdk/src/core/identity.d.ts.map +1 -0
  21. package/dist/js-sdk/src/core/resolve-detail.d.ts +42 -0
  22. package/dist/js-sdk/src/core/resolve-detail.d.ts.map +1 -0
  23. package/dist/js-sdk/src/core/shared-core.d.ts +170 -0
  24. package/dist/js-sdk/src/core/shared-core.d.ts.map +1 -0
  25. package/dist/js-sdk/src/core/store.d.ts +94 -0
  26. package/dist/js-sdk/src/core/store.d.ts.map +1 -0
  27. package/dist/js-sdk/src/core/types.d.ts +128 -0
  28. package/dist/js-sdk/src/core/types.d.ts.map +1 -0
  29. package/dist/js-sdk/src/core/unevaluable.d.ts +28 -0
  30. package/dist/js-sdk/src/core/unevaluable.d.ts.map +1 -0
  31. package/dist/js-sdk/src/core/validate-snapshot.d.ts +38 -0
  32. package/dist/js-sdk/src/core/validate-snapshot.d.ts.map +1 -0
  33. package/dist/js-sdk/src/platform/browser.d.ts +5 -0
  34. package/dist/js-sdk/src/platform/browser.d.ts.map +1 -0
  35. package/dist/js-sdk/src/platform/types.d.ts +31 -0
  36. package/dist/js-sdk/src/platform/types.d.ts.map +1 -0
  37. package/package.json +56 -4
@@ -0,0 +1,37 @@
1
+ import { ConditionDto, ConditionGroupDto, ConditionLogic, EvaluationContext, EvaluationDetail, FlagDto, ServeConfigDto, SegmentDto } from './types.js';
2
+ export declare const MAX_PREREQUISITE_DEPTH = 10;
3
+ /**
4
+ * Compute deterministic bucket (0-99) for a value.
5
+ * Uses MD5 hashing for consistency with Python and C# SDKs.
6
+ * Formula: readUInt32LE(md5(salt:value).slice(0, 4)) % 100
7
+ */
8
+ export declare function computeBucket(salt: string, value: string, md5: (input: string) => Uint8Array): number;
9
+ declare function evaluateCondition(condition: ConditionDto, context: EvaluationContext): boolean;
10
+ declare function evaluateConditions(conditions: ConditionDto[], logic: ConditionLogic, context: EvaluationContext): boolean;
11
+ declare function evaluateConditionGroups(groups: ConditionGroupDto[], context: EvaluationContext): boolean;
12
+ declare function resolveServe(serve: ServeConfigDto, context: EvaluationContext, md5: (input: string) => Uint8Array): string;
13
+ export interface EvaluatorDeps {
14
+ md5: (input: string) => Uint8Array;
15
+ getSegment?: (key: string) => SegmentDto | undefined;
16
+ }
17
+ /**
18
+ * Evaluate a flag against a context. Pure function, no I/O.
19
+ *
20
+ * @param flag The flag to evaluate.
21
+ * @param context The evaluation context.
22
+ * @param deps Evaluator dependencies (md5, getSegment).
23
+ * @param allFlags Map of all flags in the environment, keyed by flag key.
24
+ * Required for prerequisite resolution; pass `{}` if the flag
25
+ * has no prerequisites.
26
+ */
27
+ export declare function evaluate(flag: FlagDto, context: EvaluationContext, deps: EvaluatorDeps, allFlags?: Record<string, FlagDto>): EvaluationDetail;
28
+ /**
29
+ * Evaluate a flag, sharing a memoisation map with other concurrent
30
+ * evaluations (e.g. a batch "evaluate all" pass).
31
+ *
32
+ * Use this when evaluating multiple flags in one sweep so that shared
33
+ * prerequisite flags are only evaluated once.
34
+ */
35
+ export declare function evaluateWithSharedMemo(flag: FlagDto, context: EvaluationContext, deps: EvaluatorDeps, allFlags: Record<string, FlagDto>, memo: Map<string, EvaluationDetail>): EvaluationDetail;
36
+ export { evaluateCondition, evaluateConditions, evaluateConditionGroups, resolveServe, };
37
+ //# sourceMappingURL=evaluator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"evaluator.d.ts","sourceRoot":"","sources":["../../../../../js-sdk/src/core/evaluator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EACZ,iBAAiB,EACjB,cAAc,EACd,iBAAiB,EACjB,gBAAgB,EAChB,OAAO,EACP,cAAc,EACd,UAAU,EACX,MAAM,YAAY,CAAC;AAEpB,eAAO,MAAM,sBAAsB,KAAK,CAAC;AASzC;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,UAAU,GACjC,MAAM,CAUR;AAokBD,iBAAS,iBAAiB,CACxB,SAAS,EAAE,YAAY,EACvB,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAkDT;AAED,iBAAS,kBAAkB,CACzB,UAAU,EAAE,YAAY,EAAE,EAC1B,KAAK,EAAE,cAAc,EACrB,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAOT;AAED,iBAAS,uBAAuB,CAC9B,MAAM,EAAE,iBAAiB,EAAE,EAC3B,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAOT;AAED,iBAAS,YAAY,CACnB,KAAK,EAAE,cAAc,EACrB,OAAO,EAAE,iBAAiB,EAC1B,GAAG,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,UAAU,GACjC,MAAM,CA6CR;AAED,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,UAAU,CAAC;IACnC,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,UAAU,GAAG,SAAS,CAAC;CACtD;AAED;;;;;;;;;GASG;AACH,wBAAgB,QAAQ,CACtB,IAAI,EAAE,OAAO,EACb,OAAO,EAAE,iBAAiB,EAC1B,IAAI,EAAE,aAAa,EACnB,QAAQ,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GACrC,gBAAgB,CAGlB;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,OAAO,EACb,OAAO,EAAE,iBAAiB,EAC1B,IAAI,EAAE,aAAa,EACnB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,GAClC,gBAAgB,CAElB;AA6ID,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,uBAAuB,EACvB,YAAY,GACb,CAAC"}
@@ -0,0 +1,77 @@
1
+ import { SdkEventDto, RecordEventsRequest } from './types.js';
2
+ export interface EventSender {
3
+ sendEvents(request: RecordEventsRequest): Promise<void>;
4
+ }
5
+ /**
6
+ * Upper bound on buffered events.
7
+ *
8
+ * Only reachable once the events endpoint fails for long enough that re-queued
9
+ * batches pile up. Past the bound the OLDEST events are shed, which caps memory
10
+ * and keeps the freshest analytics — and means a long outage sheds the stale
11
+ * re-queued batches rather than starving new events.
12
+ */
13
+ export declare const DEFAULT_MAX_QUEUE_SIZE = 10000;
14
+ /**
15
+ * Raised when the events endpoint answers with a non-success status.
16
+ *
17
+ * `fetch` does not reject on an HTTP error status, so without an explicit check
18
+ * a 503 was indistinguishable from success and the batch was discarded as
19
+ * "sent" — no log, no counter, nothing (#2456).
20
+ */
21
+ export declare class EventSendError extends Error {
22
+ readonly status: number;
23
+ constructor(status: number);
24
+ /**
25
+ * Whether the same batch could succeed if sent again: any 5xx (the production
26
+ * edge answers this endpoint with a 503 at a low constant rate) and 429, where
27
+ * the server is explicitly asking the caller to come back later.
28
+ */
29
+ get retryable(): boolean;
30
+ }
31
+ export declare class EventProcessor {
32
+ private readonly sender;
33
+ private readonly flushInterval;
34
+ private readonly options;
35
+ private queue;
36
+ private flushTimer;
37
+ private closed;
38
+ private flushPromise;
39
+ private readonly flushBatchSize;
40
+ private readonly maxQueueSize;
41
+ /**
42
+ * Epoch ms before which the batch-size trigger must not start another flush.
43
+ *
44
+ * A re-queued batch leaves the queue at or above `flushBatchSize`, so without this
45
+ * gate every subsequent `enqueue` would start another flush — turning a failing
46
+ * endpoint into one request per event, which is worse for the server than the
47
+ * dropping it replaces. The interval timer is the retry vehicle; this only
48
+ * suppresses the size trigger between its ticks. (Concurrent starts are already
49
+ * coalesced by `flushPromise`; this handles the gap AFTER a failed flush settles.)
50
+ */
51
+ private nextAutoFlushAt;
52
+ /**
53
+ * @param options.flushOnFullBatch Default true: `enqueue` starts a flush once a
54
+ * batch is full. A host where only an awaited or `waitUntil`-held promise is
55
+ * guaranteed to finish (the edge SDK) turns it off, so no send is ever
56
+ * started that nobody waits on; its explicit `flush()` still sends in
57
+ * batches of `flushBatchSize`.
58
+ * @param options.maxBatchesPerFlush Default unlimited: one `flush()` drains the
59
+ * queue. A host with a per-invocation subrequest budget (the edge SDK) caps
60
+ * the POSTs one flush may make; whatever is left waits for a later flush.
61
+ */
62
+ constructor(sender: EventSender, flushInterval: number, flushBatchSize: number, maxQueueSize?: number, options?: {
63
+ flushOnFullBatch?: boolean;
64
+ maxBatchesPerFlush?: number;
65
+ });
66
+ /** Events waiting to be sent, not counting a batch currently in flight. */
67
+ get size(): number;
68
+ start(): void;
69
+ enqueue(event: SdkEventDto): void;
70
+ flush(): Promise<void>;
71
+ /** Returns a batch that failed to send to the front of the queue. */
72
+ private requeue;
73
+ /** Sheds oldest-first until the queue fits the bound. */
74
+ private trimToBound;
75
+ close(): Promise<void>;
76
+ }
77
+ //# sourceMappingURL=events.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../../../../js-sdk/src/core/events.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEnE,MAAM,WAAW,WAAW;IAC1B,UAAU,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACzD;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,QAAS,CAAC;AAE7C;;;;;;GAMG;AACH,qBAAa,cAAe,SAAQ,KAAK;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM;gBAAd,MAAM,EAAE,MAAM;IAKnC;;;;OAIG;IACH,IAAI,SAAS,IAAI,OAAO,CAEvB;CACF;AAsCD,qBAAa,cAAc;IAgCvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,aAAa;IAG9B,OAAO,CAAC,QAAQ,CAAC,OAAO;IAnC1B,OAAO,CAAC,KAAK,CAAqB;IAClC,OAAO,CAAC,UAAU,CAA+C;IACjE,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,YAAY,CAA8B;IAClD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IAExC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IAEtC;;;;;;;;;OASG;IACH,OAAO,CAAC,eAAe,CAAK;IAE5B;;;;;;;;;OASG;gBAEgB,MAAM,EAAE,WAAW,EACnB,aAAa,EAAE,MAAM,EACtC,cAAc,EAAE,MAAM,EACtB,YAAY,GAAE,MAA+B,EAC5B,OAAO,GAAE;QAAE,gBAAgB,CAAC,EAAE,OAAO,CAAC;QAAC,kBAAkB,CAAC,EAAE,MAAM,CAAA;KAAO;IAU5F,2EAA2E;IAC3E,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,KAAK,IAAI,IAAI;IAOb,OAAO,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI;IAa3B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAyC5B,qEAAqE;IACrE,OAAO,CAAC,OAAO;IAOf,yDAAyD;IACzD,OAAO,CAAC,WAAW;IASb,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAY7B"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * The token every request carries so the Evaluation API can count SDK usage by
3
+ * name and version (#4144, contract in #4138). The version is inlined from
4
+ * package.json at build time (see globals.d.ts), never written here (#2141).
5
+ */
6
+ export declare const SDK_TOKEN: string;
7
+ /**
8
+ * The SDK token followed by each wrapper token, in order. A wrapper that is not a
9
+ * valid product token is dropped with a warning: identity metadata must never stop
10
+ * the client starting. Filtered defensively because JS callers bypass the types.
11
+ */
12
+ export declare function buildIdentity(wrappers: readonly unknown[] | undefined): string;
13
+ //# sourceMappingURL=identity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../../../../../js-sdk/src/core/identity.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,eAAO,MAAM,SAAS,QAAsC,CAAC;AAK7D;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,SAAS,OAAO,EAAE,GAAG,SAAS,GAAG,MAAM,CAY9E"}
@@ -0,0 +1,42 @@
1
+ import { EvaluationContext, EvaluationDetail, FlagDto, SegmentDto } from './types.js';
2
+ /**
3
+ * The runtime type a typed accessor requires of the value a flag serves, as
4
+ * reported by `typeof`. Used to detect a type-mismatched read (#2281), which
5
+ * TypeScript cannot catch on its own: its type parameters are erased, so
6
+ * `boolVariation` would otherwise hand back whatever the flag actually served.
7
+ */
8
+ export type ExpectedValueType = 'boolean' | 'string' | 'number';
9
+ /** What `resolveDetail` needs from its host: hashing, segment lookup, an error hook. */
10
+ export interface ResolveDeps {
11
+ md5(input: string): Uint8Array;
12
+ getSegment(key: string): SegmentDto | undefined;
13
+ /**
14
+ * Told about an evaluation that threw, before it degrades to the default with
15
+ * reason `Error`. Optional: the JS core has never surfaced these and does not
16
+ * pass it; the edge SDK logs them.
17
+ */
18
+ onError?(err: unknown): void;
19
+ }
20
+ /**
21
+ * Turn a flag lookup into the `EvaluationDetail` a variation call returns: the
22
+ * evaluator's result plus every degradation the SDK applies on top of it. Pure,
23
+ * never throws, records nothing — the caller owns analytics and inspectors.
24
+ *
25
+ * Shared by `SharedFeatureflipCore.variationDetail` and `@featureflip/edge`, so
26
+ * the two cannot drift on what a missing flag, a malformed config or a
27
+ * type-mismatched read resolves to.
28
+ *
29
+ * @internal Not exported from any public entry point.
30
+ *
31
+ * @param expected The `typeof` result the caller's typed accessor requires, or
32
+ * undefined for the generic/JSON accessors. Those take no runtime type — a
33
+ * TypeScript type parameter is erased — so there is nothing to check them
34
+ * against and they are left unchecked.
35
+ * @param allFlags Every flag, for prerequisite lookups, or a function returning
36
+ * them. A function is called inside the guard, so a store that throws while
37
+ * building the map degrades to `Error` like any other evaluation failure.
38
+ * @param memo A memo shared across a batch of evaluations (e.g. a bootstrap
39
+ * sweep) so shared prerequisites are evaluated once. Omit for a single read.
40
+ */
41
+ export declare function resolveDetail<T>(flag: FlagDto | undefined, context: EvaluationContext, defaultValue: T, deps: ResolveDeps, allFlags: Record<string, FlagDto> | (() => Record<string, FlagDto>), expected?: ExpectedValueType, memo?: Map<string, EvaluationDetail>): EvaluationDetail<T>;
42
+ //# sourceMappingURL=resolve-detail.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-detail.d.ts","sourceRoot":"","sources":["../../../../../js-sdk/src/core/resolve-detail.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,iBAAiB,EACjB,gBAAgB,EAEhB,OAAO,EACP,UAAU,EACX,MAAM,YAAY,CAAC;AAEpB;;;;;GAKG;AACH,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEhE,wFAAwF;AACxF,MAAM,WAAW,WAAW;IAC1B,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CAAC;IAC/B,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAAC;IAChD;;;;OAIG;IACH,OAAO,CAAC,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAAC;CAC9B;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAC7B,IAAI,EAAE,OAAO,GAAG,SAAS,EACzB,OAAO,EAAE,iBAAiB,EAC1B,YAAY,EAAE,CAAC,EACf,IAAI,EAAE,WAAW,EACjB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,MAAM,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACnE,QAAQ,CAAC,EAAE,iBAAiB,EAC5B,IAAI,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,GACnC,gBAAgB,CAAC,CAAC,CAAC,CAqDrB"}
@@ -0,0 +1,170 @@
1
+ import { FeatureflipConfig, ResolvedConfig } from '../config.js';
2
+ import { ExpectedValueType } from './resolve-detail.js';
3
+ import { EvaluationContext, EvaluationDetail, FlagDto, FlagUpdateListener } from './types.js';
4
+ import { Platform } from '../platform/types.js';
5
+ /**
6
+ * Resolve the built-in user id for an analytics event from either the
7
+ * canonical `user_id` field or its `userId` camelCase alias — mirroring the
8
+ * two-way alias the evaluator applies for rollout bucketing. Without this the
9
+ * event's `userId` is silently null whenever a caller passes a `userId`-keyed
10
+ * context (a documented-valid shape), leaving analytics unattributed.
11
+ */
12
+ export declare function resolveEventUserId(context: EvaluationContext): string | undefined;
13
+ export type { ExpectedValueType } from './resolve-detail.js';
14
+ /**
15
+ * Internal shared core owning all expensive resources of a FeatureflipClient:
16
+ * platform, HTTP fetches, event processor, SSE connection, polling timer.
17
+ *
18
+ * Refcounted: multiple FeatureflipClient handles can share one core, and the
19
+ * real shutdown runs only when the last handle is disposed. Constructed either
20
+ * by the static factory in `client.ts` or directly by `createForTesting`.
21
+ *
22
+ * JS is single-threaded, so the refcount is a plain number — no atomic
23
+ * primitives or locking required.
24
+ *
25
+ * @internal
26
+ */
27
+ export declare class SharedFeatureflipCore {
28
+ readonly config: ResolvedConfig;
29
+ private readonly store;
30
+ private readonly events;
31
+ private readonly platform;
32
+ private readonly inspectors;
33
+ /** `featureflip-js/<version>` plus wrapper tokens, built once (#4144). */
34
+ private readonly identity;
35
+ private readonly updateListeners;
36
+ private initialized;
37
+ private initPromise;
38
+ private eventSource;
39
+ private pollTimer;
40
+ private closed;
41
+ private streamRetryCount;
42
+ private streamRetryTimer;
43
+ /**
44
+ * True between arming the polling fallback and the next delivered `sync`. Gates
45
+ * the reap so a poller the caller configured (`streaming: false`) is never
46
+ * mistaken for one this SDK armed to cover an outage.
47
+ */
48
+ private pollingIsStreamFallback;
49
+ /** Number of outstanding handles (including the one returned by the factory). */
50
+ private refCount;
51
+ /** Set by the factory after inserting into the owning map so Shutdown can remove itself. */
52
+ private owningMap;
53
+ private owningKey;
54
+ constructor(config: FeatureflipConfig, platform: Platform);
55
+ /**
56
+ * Test-only constructor. Bypasses network: accepts pre-populated flag DTOs
57
+ * and marks the core as initialized. Used by FeatureflipClient.forTesting.
58
+ */
59
+ static createForTesting(flags: FlagDto[]): SharedFeatureflipCore;
60
+ /** Ref count for diagnostics (test-only). */
61
+ get debugRefCount(): number;
62
+ /** Whether the core's shared shutdown has already run (refcount reached zero). */
63
+ get isShutDown(): boolean;
64
+ get isInitialized(): boolean;
65
+ /**
66
+ * Atomically increments the refcount if the core is still alive.
67
+ * Returns false if the core has already shut down — caller must construct a new one.
68
+ */
69
+ tryAcquire(): boolean;
70
+ /**
71
+ * Decrements the refcount. When it reaches zero, runs the real shutdown
72
+ * exactly once. Over-release is a no-op — the guard prevents re-entry.
73
+ */
74
+ release(): Promise<void>;
75
+ /**
76
+ * Called by the factory after successfully inserting this core into the
77
+ * owning map so Shutdown can remove itself from the map.
78
+ */
79
+ setOwningMap(map: Map<string, SharedFeatureflipCore>, key: string): void;
80
+ /**
81
+ * Subscribe to flag-configuration updates. Returns an unsubscribe function.
82
+ * Listeners live on the shared core, so they fire for updates regardless of
83
+ * which handle triggered them; handles remove their own on close.
84
+ */
85
+ onUpdate(listener: FlagUpdateListener): () => void;
86
+ offUpdate(listener: FlagUpdateListener): void;
87
+ /**
88
+ * Fire the registered update listeners. A throwing listener is isolated: it
89
+ * neither breaks the data-source callback nor stops the remaining listeners.
90
+ */
91
+ private notifyUpdateListeners;
92
+ /** Wait for the core to finish loading initial flag data. */
93
+ waitForInitialization(): Promise<void>;
94
+ /**
95
+ * @param expected The `typeof` result the caller's typed accessor requires, or
96
+ * undefined for the generic/JSON accessors. Those take no runtime type — a
97
+ * TypeScript type parameter is erased — so there is nothing to check them
98
+ * against and they are left unchecked.
99
+ */
100
+ variationDetail<T>(key: string, context: EvaluationContext, defaultValue: T, expected?: ExpectedValueType): EvaluationDetail<T>;
101
+ evaluateFlag<T>(key: string, context: EvaluationContext, defaultValue: T, expected?: ExpectedValueType): T;
102
+ track(eventKey: string, context: EvaluationContext, metadata?: Record<string, unknown>): void;
103
+ identify(context: EvaluationContext): void;
104
+ flush(): Promise<void>;
105
+ /**
106
+ * Runs the real shutdown exactly once. Closes the SSE connection, clears
107
+ * timers, flushes events, and removes the entry from the owning map.
108
+ * Safe to call more than once — subsequent calls are no-ops.
109
+ */
110
+ private shutdown;
111
+ private initialize;
112
+ private fetchFlags;
113
+ /**
114
+ * Apply a validated snapshot, minus any entity carrying an enum value this build
115
+ * cannot evaluate (#2402).
116
+ *
117
+ * Split from the two call sites rather than inlined at each so polling and streaming
118
+ * cannot drift — treating the two transports as one payload shape is exactly what
119
+ * #2279 taught, and a guard applied to only one of them is the same bug with a
120
+ * narrower trigger.
121
+ */
122
+ private applySnapshot;
123
+ private startDataSource;
124
+ private startStreaming;
125
+ /**
126
+ * Capped exponential reconnect backoff, jittered to [d/2, d] at every level.
127
+ *
128
+ * The jitter is load-bearing on the FIRST reconnect, not only the escalating
129
+ * ones: these drops are fleet-wide — one edge event severs every stream at once
130
+ * (#2457) — so every client re-enters here at the same failure count together,
131
+ * and a constant there republishes the drop's own synchronisation as a reconnect
132
+ * spike one delay later (#2508). Unbounded retrying (#3071) makes that permanent
133
+ * rather than one-off, which is why this SDK can no longer be the fleet's one
134
+ * un-jittered backoff. The band's lower bound is strictly positive, so a clean
135
+ * sever still cannot busy-loop.
136
+ */
137
+ private streamReconnectDelay;
138
+ /**
139
+ * Retires a polling fallback armed to cover an SSE outage, leaving a poller the
140
+ * caller asked for (`streaming: false`) alone.
141
+ */
142
+ private stopStreamFallbackPolling;
143
+ private startPolling;
144
+ /**
145
+ * Emit a diagnostic for a wire-contract violation, and only for that.
146
+ *
147
+ * Network and JSON-syntax failures are deliberately left silent: they self-heal on
148
+ * the next poll or reconnect, and warning on each would flood the console of any
149
+ * client that is briefly offline. A MalformedPayloadError means the server and SDK
150
+ * disagree about the shape of the data — that never self-heals and must be visible
151
+ * ("never swallow silently", packages/CLAUDE.md).
152
+ */
153
+ private warnIfMalformed;
154
+ private fetchSingleFlag;
155
+ /**
156
+ * Fire the registered evaluation inspectors. Called once per variation call
157
+ * on every exit path of variationDetail. A throwing inspector is isolated:
158
+ * it neither breaks evaluation nor stops the remaining inspectors.
159
+ */
160
+ private notifyInspectors;
161
+ private recordEvaluation;
162
+ private headers;
163
+ }
164
+ /**
165
+ * Structural comparison of resolved configs. Used to warn when a later
166
+ * `get()` call passes options that meaningfully differ from the cached
167
+ * instance's options. sdkKey is excluded (it's the cache key itself).
168
+ */
169
+ export declare function resolvedConfigsEqual(a: ResolvedConfig, b: ResolvedConfig): boolean;
170
+ //# sourceMappingURL=shared-core.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared-core.d.ts","sourceRoot":"","sources":["../../../../../js-sdk/src/core/shared-core.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,94 @@
1
+ import { FlagDto, SegmentDto } from './types.js';
2
+ /**
3
+ * Called with the keys of every flag affected by a single store mutation.
4
+ * Batched: one call per mutation, not one per key, so a poll that changes 40
5
+ * flags notifies once. Never called with an empty array.
6
+ */
7
+ export type FlagChangeListener = (keys: string[]) => void;
8
+ export declare class FlagStore {
9
+ private flags;
10
+ private segments;
11
+ private listeners;
12
+ private version;
13
+ /**
14
+ * Whether a snapshot has been loaded. The first `init` establishes the
15
+ * baseline rather than reporting every flag as changed — a cold load is not
16
+ * a configuration change.
17
+ */
18
+ private hasSnapshot;
19
+ getFlag(key: string): FlagDto | undefined;
20
+ getSegment(key: string): SegmentDto | undefined;
21
+ getAllFlags(): FlagDto[];
22
+ getVersion(): number;
23
+ /**
24
+ * Replace the whole snapshot. Used by the initial fetch, every poll tick, and
25
+ * the SSE `sync` event, so it diffs against the previous snapshot and reports
26
+ * only what actually changed — notifying every flag on every poll would make
27
+ * the public update hook useless.
28
+ */
29
+ init(flags: FlagDto[], segments: SegmentDto[], version: number): void;
30
+ /**
31
+ * Apply a single flag delta (SSE `flag.created` / `flag.updated`).
32
+ *
33
+ * Rejects only a *strictly older* config. Equal versions must be applied:
34
+ * the wire version is second-granular, so two edits to one flag inside the
35
+ * same wall-clock second carry an identical version, and treating equal as
36
+ * stale discarded the second edit outright. With streaming on (the default)
37
+ * polling is disabled, so no later snapshot corrected it and the store
38
+ * evaluated against the pre-edit config until an SSE `sync` or reconnect.
39
+ *
40
+ * Re-applying an identical config is harmless; dropping a real one is not.
41
+ */
42
+ upsert(flag: FlagDto): void;
43
+ delete(key: string): void;
44
+ /**
45
+ * `key` plus every flag transitively depending on it via a prerequisite.
46
+ *
47
+ * The single-key delta paths (SSE `flag.created` / `flag.updated` /
48
+ * `flag.deleted`) need the same fan-out as a full snapshot diff: the event
49
+ * names one flag, but each of its dependents changes evaluated value too.
50
+ *
51
+ * Reads the post-mutation store, so an upserted flag is included, and a
52
+ * deleted flag's dependents — which still carry the now-dangling
53
+ * prerequisite row — remain reachable.
54
+ */
55
+ private withPrerequisiteDependents;
56
+ onChange(listener: FlagChangeListener): () => void;
57
+ /**
58
+ * Keys of the flags affected by replacing the current snapshot with the given
59
+ * one. A flag counts as affected when it is added, removed, its own config
60
+ * changed, one of the segments its rules reference changed, or one of the
61
+ * flags it names as a prerequisite changed — the latter two alter evaluation
62
+ * outcomes without touching the flag's own config, so without them an SSE
63
+ * `segment.updated` (which refetches the whole snapshot) would report nothing
64
+ * and a toggled prerequisite would never reach its dependents.
65
+ *
66
+ * Compares config content, not `version`. The wire version is deliberately
67
+ * second-granular — eval-api divides its internal epoch-millisecond version
68
+ * by 1000 because published SDKs declare 32-bit version fields — so it is not
69
+ * a sufficient change signal here. Two edits to one flag inside the same
70
+ * wall-clock second carry an identical version, and if a snapshot boundary
71
+ * falls between them the later config lands in the store while a version-only
72
+ * diff reports nothing changed, leaving a consumer that caches per key stale
73
+ * until the next unrelated edit (#2088). `upsert` has no such problem — an SSE
74
+ * delta is its own signal — so only full snapshots need this.
75
+ *
76
+ * Serializing covers every field automatically, so a field added to a DTO is
77
+ * included without anyone remembering to extend a comparison. Bounded by one
78
+ * pass over a snapshot the caller just parsed off the wire.
79
+ *
80
+ * It is key-order sensitive. Snapshot-to-snapshot that is safe — both sides
81
+ * come from the same batch endpoint. It is *not* guaranteed when `previous`
82
+ * was written by `upsert` from the single-flag endpoint, which could order or
83
+ * project its keys differently; the first poll after a delta would then report
84
+ * that key as changed. Harmless and self-limiting — the map is replaced with
85
+ * the batch DTO, so it cannot repeat — and it fails toward a redundant
86
+ * notification rather than a missed one, which is the direction this whole
87
+ * diff is trying to protect.
88
+ *
89
+ * Must be called before the maps are replaced.
90
+ */
91
+ private diffSnapshot;
92
+ private notifyListeners;
93
+ }
94
+ //# sourceMappingURL=store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../../../../js-sdk/src/core/store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAEtD;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;AAE1D,qBAAa,SAAS;IACpB,OAAO,CAAC,KAAK,CAA8B;IAC3C,OAAO,CAAC,QAAQ,CAAiC;IACjD,OAAO,CAAC,SAAS,CAA4B;IAC7C,OAAO,CAAC,OAAO,CAAK;IAEpB;;;;OAIG;IACH,OAAO,CAAC,WAAW,CAAS;IAE5B,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS;IAIzC,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS;IAI/C,WAAW,IAAI,OAAO,EAAE;IAIxB,UAAU,IAAI,MAAM;IAIpB;;;;;OAKG;IACH,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IA2BrE;;;;;;;;;;;OAWG;IACH,MAAM,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI;IAS3B,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAMzB;;;;;;;;;;OAUG;IACH,OAAO,CAAC,0BAA0B;IAMlC,QAAQ,CAAC,QAAQ,EAAE,kBAAkB,GAAG,MAAM,IAAI;IAQlD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAiCG;IACH,OAAO,CAAC,YAAY;IAsDpB,OAAO,CAAC,eAAe;CASxB"}
@@ -0,0 +1,128 @@
1
+ export type FlagType = 'Boolean' | 'String' | 'Number' | 'Json';
2
+ export type ServeType = 'Fixed' | 'Rollout';
3
+ export type ConditionLogic = 'And' | 'Or';
4
+ export type SdkEventType = 'Evaluation' | 'Impression' | 'Identify' | 'Custom';
5
+ export type EvaluationReason = 'RuleMatch' | 'Fallthrough' | 'FlagDisabled' | 'FlagNotFound' | 'PrerequisiteFailed' | 'Error';
6
+ export type ConditionOperator = 'Equals' | 'NotEquals' | 'In' | 'NotIn' | 'Contains' | 'NotContains' | 'StartsWith' | 'EndsWith' | 'MatchesRegex' | 'GreaterThan' | 'GreaterThanOrEqual' | 'LessThan' | 'LessThanOrEqual' | 'Before' | 'After' | 'SemverEquals' | 'SemverGreaterThan' | 'SemverGreaterThanOrEqual' | 'SemverLessThan' | 'SemverLessThanOrEqual';
7
+ export interface VariationDto {
8
+ key: string;
9
+ value: unknown;
10
+ }
11
+ export interface ConditionDto {
12
+ attribute: string;
13
+ operator: ConditionOperator;
14
+ values: string[];
15
+ negate: boolean;
16
+ }
17
+ export interface WeightedVariationDto {
18
+ key: string;
19
+ weight: number;
20
+ }
21
+ export interface ServeConfigDto {
22
+ type: ServeType;
23
+ variation?: string;
24
+ bucketBy?: string;
25
+ salt?: string;
26
+ variations?: WeightedVariationDto[];
27
+ }
28
+ export interface ConditionGroupDto {
29
+ operator: ConditionLogic;
30
+ conditions: ConditionDto[];
31
+ }
32
+ export interface RuleDto {
33
+ id: string;
34
+ priority: number;
35
+ conditionGroups: ConditionGroupDto[];
36
+ serve: ServeConfigDto;
37
+ segmentKey?: string;
38
+ }
39
+ export interface Prerequisite {
40
+ prerequisiteFlagKey: string;
41
+ expectedVariationKey: string;
42
+ }
43
+ export interface FlagDto {
44
+ key: string;
45
+ version: number;
46
+ type: FlagType;
47
+ enabled: boolean;
48
+ variations: VariationDto[];
49
+ rules: RuleDto[];
50
+ fallthrough: ServeConfigDto;
51
+ offVariation: string;
52
+ prerequisites?: Prerequisite[];
53
+ /**
54
+ * Whether the flag may be exposed to client-side code. Absent on payloads from
55
+ * servers that predate the field; treat absent as not visible.
56
+ */
57
+ clientSideVisible?: boolean;
58
+ }
59
+ export interface SegmentDto {
60
+ key: string;
61
+ version: number;
62
+ conditions: ConditionDto[];
63
+ conditionLogic: ConditionLogic;
64
+ }
65
+ export interface GetFlagsResponse {
66
+ environment: string;
67
+ version: number;
68
+ flags: FlagDto[];
69
+ segments: SegmentDto[];
70
+ }
71
+ export interface EvaluationDetail<T = unknown> {
72
+ value: T;
73
+ variationKey?: string;
74
+ reason: EvaluationReason;
75
+ ruleId?: string;
76
+ prerequisiteKey?: string;
77
+ }
78
+ export type EvaluationContext = Record<string, unknown>;
79
+ /**
80
+ * The object passed to each registered evaluation inspector. Fired once per
81
+ * variation call, synchronously, during evaluation. `context` is a shallow copy
82
+ * of the caller's context — treat it as read-only. Optional fields are set only
83
+ * for their corresponding reason (`ruleId` on `RuleMatch`, `prerequisiteKey` on
84
+ * `PrerequisiteFailed`).
85
+ */
86
+ export interface EvaluationEvent {
87
+ flagKey: string;
88
+ context: EvaluationContext;
89
+ value: unknown;
90
+ variationKey?: string;
91
+ reason: EvaluationReason;
92
+ ruleId?: string;
93
+ prerequisiteKey?: string;
94
+ timestamp: string;
95
+ }
96
+ /** An in-process observer invoked on every flag evaluation. Return value ignored. */
97
+ export type EvaluationInspector = (event: EvaluationEvent) => void;
98
+ /** Events a client can be subscribed to via `on`/`off`. */
99
+ export type FeatureflipEvent = 'update';
100
+ /**
101
+ * Called when flag configuration changes after the initial load, with the keys
102
+ * of the affected flags. Batched: one call per update, however many flags it
103
+ * touched. Never called with an empty array, and never for the initial load.
104
+ *
105
+ * A flag is reported when it is added, removed, its definition changed, or a
106
+ * segment its rules reference changed. Return value ignored.
107
+ */
108
+ export type FlagUpdateListener = (keys: string[]) => void;
109
+ export interface SdkEventDto {
110
+ type: SdkEventType;
111
+ flagKey: string;
112
+ userId?: string;
113
+ variation?: string;
114
+ timestamp: string;
115
+ metadata?: Record<string, unknown>;
116
+ prerequisiteKey?: string;
117
+ }
118
+ export interface RecordEventsRequest {
119
+ events: SdkEventDto[];
120
+ }
121
+ export interface StreamFlagUpdatedEvent {
122
+ key: string;
123
+ version: number;
124
+ }
125
+ export interface StreamPingEvent {
126
+ timestamp: string;
127
+ }
128
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../js-sdk/src/core/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ import { FlagDto, GetFlagsResponse, SegmentDto } from './types.js';
2
+ /**
3
+ * Why this flag cannot be evaluated, or `null` if it can.
4
+ *
5
+ * Returns a human-readable reason rather than a boolean so the caller's diagnostic can
6
+ * name the field and the value actually received — the one detail an operator
7
+ * debugging a vanished flag needs.
8
+ */
9
+ export declare function unevaluableFlagReason(flag: FlagDto): string | null;
10
+ /** Why this segment cannot be evaluated, or `null` if it can. */
11
+ export declare function unevaluableSegmentReason(segment: SegmentDto): string | null;
12
+ export interface DroppedEntity {
13
+ kind: 'flag' | 'segment';
14
+ key: string;
15
+ reason: string;
16
+ }
17
+ export interface FilteredSnapshot {
18
+ flags: FlagDto[];
19
+ segments: SegmentDto[];
20
+ dropped: DroppedEntity[];
21
+ }
22
+ /**
23
+ * Split a validated snapshot into the entities this SDK can evaluate and the ones it
24
+ * cannot. Never throws: an unknown enum name is a forward-compatibility event, not a
25
+ * contract violation, and the healthy remainder must still apply.
26
+ */
27
+ export declare function dropUnevaluableEntities(data: GetFlagsResponse): FilteredSnapshot;
28
+ //# sourceMappingURL=unevaluable.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unevaluable.d.ts","sourceRoot":"","sources":["../../../../../js-sdk/src/core/unevaluable.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,OAAO,EACP,gBAAgB,EAChB,UAAU,EAEX,MAAM,YAAY,CAAC;AAqDpB;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAgBlE;AAED,iEAAiE;AACjE,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI,CAK3E;AASD,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,OAAO,EAAE,CAAC;IACjB,QAAQ,EAAE,UAAU,EAAE,CAAC;IACvB,OAAO,EAAE,aAAa,EAAE,CAAC;CAC1B;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,gBAAgB,GAAG,gBAAgB,CAgBhF"}