@envflags/js-sdk 0.1.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/README.md ADDED
@@ -0,0 +1,83 @@
1
+ # `@envflags/js-sdk`
2
+
3
+ The reference envflags SDK for browsers, Node.js, and Workers. It downloads an
4
+ immutable flag payload, keeps it in memory, and evaluates flags synchronously.
5
+
6
+ ```ts
7
+ import { createClient } from '@envflags/js-sdk'
8
+
9
+ const flags = await createClient('client_…').initialize()
10
+ flags.setContext({ key: 'user-123', country: 'BE' })
11
+
12
+ const enabled = flags.boolVariation('new-checkout', false)
13
+
14
+ flags.close()
15
+ ```
16
+
17
+ In a browser session, call `setContext` once and omit context on later reads. On
18
+ the server, skip `setContext` and pass context as the last argument so one
19
+ client can evaluate many users. Context is optional: flags that do not depend on
20
+ who is asking still evaluate off/fallthrough without it.
21
+
22
+ ```ts
23
+ flags.boolVariation('new-checkout', false, { key: 'user-123', country: 'BE' })
24
+ ```
25
+
26
+ By default the client polls every 30 seconds and fetches a new payload only when
27
+ its version changes. Set `pollIntervalMs: false` to refresh manually. Supply a
28
+ previously persisted payload as `bootstrap` to remain available when the first
29
+ network request fails. Once initialized, refresh failures keep the last-known
30
+ payload active. Typed variation methods always fall back to the caller's default
31
+ when a flag is missing or has the wrong value type.
32
+
33
+ ## React
34
+
35
+ Define the application's flags once with any
36
+ [Standard Schema](https://standardschema.dev/) validator. The resulting API
37
+ knows every flag key, return type, and fallback value. Remote values are
38
+ validated before they reach application code, including structured JSON flags.
39
+
40
+ ```tsx
41
+ import { flag } from '@envflags/js-sdk'
42
+ import { defineFlags } from '@envflags/js-sdk/react'
43
+ import { z } from 'zod'
44
+
45
+ const envflags = defineFlags({
46
+ 'new-checkout': flag(z.boolean(), false),
47
+ 'checkout-config': flag(
48
+ z.object({
49
+ layout: z.enum(['compact', 'comfortable']),
50
+ maximumItems: z.number().int().positive(),
51
+ }),
52
+ { layout: 'comfortable', maximumItems: 20 },
53
+ ),
54
+ })
55
+
56
+ const client = envflags.createClient('client_…')
57
+
58
+ function App() {
59
+ return (
60
+ <envflags.EnvflagsProvider client={client} context={{ key: 'user-123' }}>
61
+ <Checkout />
62
+ </envflags.EnvflagsProvider>
63
+ )
64
+ }
65
+
66
+ function Checkout() {
67
+ const enabled = envflags.useFlag('new-checkout') // boolean
68
+ const config = envflags.useFlag('checkout-config') // fully typed object
69
+ return enabled ? <NewCheckout /> : <OldCheckout />
70
+ }
71
+ ```
72
+
73
+ Schemas must validate synchronously. Flags can contain booleans, numbers,
74
+ strings, `null`, arrays, or objects. Defaults are validated immediately. An
75
+ invalid remote variation reports through the client's `onError` callback and
76
+ resolves to its registered default. `useFlag` accepts an optional second
77
+ argument to override the provider context for one evaluation.
78
+
79
+ The provider initializes its client and re-renders consumers when a new payload
80
+ arrives. It does not close the client when it unmounts because the client may be
81
+ shared; call `client.close()` when the application is finished with it. The
82
+ low-level `variation` and typed variation methods remain available for dynamic
83
+ flag keys and gradual migration.
@@ -0,0 +1,31 @@
1
+ import type { EvaluationContext, EvaluationPayload, JsonValue } from './types';
2
+ type Fetch = typeof globalThis.fetch;
3
+ export type EnvflagsClientOptions = {
4
+ baseUrl?: string;
5
+ bootstrap?: EvaluationPayload;
6
+ fetch?: Fetch;
7
+ onError?: (error: Error) => void;
8
+ pollIntervalMs?: number | false;
9
+ };
10
+ export declare class EnvflagsClient {
11
+ #private;
12
+ constructor(sdkKey: string, options?: EnvflagsClientOptions);
13
+ get initialized(): boolean;
14
+ get version(): number | undefined;
15
+ /** Subscribe to payload changes. Intended for UI framework bindings. */
16
+ subscribe: (listener: () => void) => () => void;
17
+ /** Return a stable value suitable for React's useSyncExternalStore. */
18
+ getSnapshot: () => number;
19
+ protected reportError(error: Error): void;
20
+ initialize(): Promise<this>;
21
+ refresh(): Promise<boolean>;
22
+ close(): void;
23
+ setContext(context: EvaluationContext): this;
24
+ variation<T extends JsonValue>(flagKey: string, defaultValue: T, context?: EvaluationContext): T;
25
+ boolVariation(flagKey: string, defaultValue: boolean, context?: EvaluationContext): boolean;
26
+ stringVariation(flagKey: string, defaultValue: string, context?: EvaluationContext): string;
27
+ numberVariation(flagKey: string, defaultValue: number, context?: EvaluationContext): number;
28
+ jsonVariation<T extends JsonValue>(flagKey: string, defaultValue: T, context?: EvaluationContext): T;
29
+ }
30
+ export declare function createClient(sdkKey: string, options?: EnvflagsClientOptions): EnvflagsClient;
31
+ export {};
@@ -0,0 +1,3 @@
1
+ import type { EvaluationContext, EvaluationPayload, JsonValue } from './types';
2
+ export declare function bucket(flagKey: string, salt: string, bucketBy: string): number;
3
+ export declare function evaluate<T extends JsonValue>(payload: EvaluationPayload, flagKey: string, context: EvaluationContext | undefined, defaultValue: T): JsonValue;
@@ -0,0 +1,5 @@
1
+ export { createClient, EnvflagsClient, type EnvflagsClientOptions, } from './client';
2
+ export { bucket, evaluate } from './evaluate';
3
+ export { flag, type FlagDefinition, type FlagDefinitions, type FlagValue, type StandardSchemaIssue, type StandardSchemaOptions, type StandardSchemaOutput, type StandardSchemaPathSegment, type StandardSchemaProps, type StandardSchemaResult, type StandardSchemaTypes, type StandardSchemaV1, } from './schema';
4
+ export { sha1 } from './sha1';
5
+ export type { Clause, ClauseOperator, EvaluationContext, EvaluationPayload, Flag, FlagKind, JsonPrimitive, JsonValue, RolloutEntry, Rule, Segment, Target, VariationSelector, } from './types';
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ import { a as createClient, c as sha1, i as EnvflagsClient, n as flag, o as bucket, s as evaluate } from "./shared.js";
2
+ export { EnvflagsClient, bucket, createClient, evaluate, flag, sha1 };
@@ -0,0 +1,38 @@
1
+ import { type ReactNode } from 'react';
2
+ import type { EnvflagsClient, EnvflagsClientOptions } from './client';
3
+ import { type DefinedEnvflagsClient, type FlagDefinitions, type FlagValue } from './schema';
4
+ import type { EvaluationContext, JsonValue } from './types';
5
+ type EnvflagsReactContext = {
6
+ client: EnvflagsClient;
7
+ evaluationContext: EvaluationContext;
8
+ };
9
+ export type EnvflagsProviderProps = {
10
+ children?: ReactNode;
11
+ client: EnvflagsClient;
12
+ context: EvaluationContext;
13
+ };
14
+ /** @deprecated Prefer the typed provider returned by defineFlags(). */
15
+ export declare function EnvflagsProvider({ children, client, context, }: EnvflagsProviderProps): import("react").FunctionComponentElement<import("react").ProviderProps<EnvflagsReactContext | undefined>>;
16
+ /** @deprecated Prefer the schema-validated useFlag() returned by defineFlags(). */
17
+ export declare function useVariation<T extends JsonValue>(flagKey: string, defaultValue: T, context?: EvaluationContext): T;
18
+ /** @deprecated Prefer the schema-validated useFlag() returned by defineFlags(). */
19
+ export declare function useBooleanFlag(flagKey: string, defaultValue: boolean, context?: EvaluationContext): boolean;
20
+ type DefinedReactContext<Definitions extends FlagDefinitions> = {
21
+ client: DefinedEnvflagsClient<Definitions>;
22
+ evaluationContext: EvaluationContext;
23
+ };
24
+ export type DefinedEnvflagsProviderProps<Definitions extends FlagDefinitions> = {
25
+ children?: ReactNode;
26
+ client: DefinedEnvflagsClient<Definitions>;
27
+ context: EvaluationContext;
28
+ };
29
+ /**
30
+ * Bind Standard Schema flag definitions to a typed client, provider, and hook.
31
+ * Call this once at module scope and export the returned API.
32
+ */
33
+ export declare function defineFlags<const Definitions extends FlagDefinitions>(definitions: Definitions): {
34
+ createClient: (sdkKey: string, options?: EnvflagsClientOptions) => DefinedEnvflagsClient<Definitions>;
35
+ EnvflagsProvider: ({ children, client, context, }: DefinedEnvflagsProviderProps<Definitions>) => import("react").FunctionComponentElement<import("react").ProviderProps<DefinedReactContext<Definitions> | undefined>>;
36
+ useFlag: <Key extends Extract<keyof Definitions, string>>(flagKey: Key, context?: EvaluationContext) => FlagValue<Definitions, Key>;
37
+ };
38
+ export {};
package/dist/react.js ADDED
@@ -0,0 +1,67 @@
1
+ import { r as prepareFlagDefinitions, t as createDefinedClient } from "./shared.js";
2
+ import { createContext, createElement, useContext, useEffect, useMemo, useSyncExternalStore } from "react";
3
+ //#region src/react.ts
4
+ var Context = createContext(void 0);
5
+ /** @deprecated Prefer the typed provider returned by defineFlags(). */
6
+ function EnvflagsProvider({ children, client, context }) {
7
+ useEffect(() => {
8
+ client.initialize().catch(() => void 0);
9
+ }, [client]);
10
+ const value = useMemo(() => ({
11
+ client,
12
+ evaluationContext: context
13
+ }), [client, context]);
14
+ return createElement(Context.Provider, { value }, children);
15
+ }
16
+ function useEnvflags() {
17
+ const value = useContext(Context);
18
+ if (!value) throw new Error("envflags hooks must be used inside an EnvflagsProvider");
19
+ useSyncExternalStore(value.client.subscribe, value.client.getSnapshot, value.client.getSnapshot);
20
+ return value;
21
+ }
22
+ /** @deprecated Prefer the schema-validated useFlag() returned by defineFlags(). */
23
+ function useVariation(flagKey, defaultValue, context) {
24
+ const envflags = useEnvflags();
25
+ return envflags.client.variation(flagKey, defaultValue, context ?? envflags.evaluationContext);
26
+ }
27
+ /** @deprecated Prefer the schema-validated useFlag() returned by defineFlags(). */
28
+ function useBooleanFlag(flagKey, defaultValue, context) {
29
+ const envflags = useEnvflags();
30
+ return envflags.client.boolVariation(flagKey, defaultValue, context ?? envflags.evaluationContext);
31
+ }
32
+ /**
33
+ * Bind Standard Schema flag definitions to a typed client, provider, and hook.
34
+ * Call this once at module scope and export the returned API.
35
+ */
36
+ function defineFlags(definitions) {
37
+ const prepared = prepareFlagDefinitions(definitions);
38
+ const DefinedContext = createContext(void 0);
39
+ function createClient(sdkKey, options = {}) {
40
+ return createDefinedClient(prepared, sdkKey, options);
41
+ }
42
+ function DefinedProvider({ children, client, context }) {
43
+ useEffect(() => {
44
+ client.initialize().catch(() => void 0);
45
+ }, [client]);
46
+ const value = useMemo(() => ({
47
+ client,
48
+ evaluationContext: context
49
+ }), [client, context]);
50
+ return createElement(DefinedContext.Provider, { value }, children);
51
+ }
52
+ function useFlag(flagKey, context) {
53
+ const value = useContext(DefinedContext);
54
+ if (!value) throw new Error("useFlag must be used inside its EnvflagsProvider");
55
+ useSyncExternalStore(value.client.subscribe, value.client.getSnapshot, value.client.getSnapshot);
56
+ return value.client.flag(flagKey, context ?? value.evaluationContext);
57
+ }
58
+ return {
59
+ createClient,
60
+ EnvflagsProvider: DefinedProvider,
61
+ useFlag
62
+ };
63
+ }
64
+ //#endregion
65
+ export { EnvflagsProvider, defineFlags, useBooleanFlag, useVariation };
66
+
67
+ //# sourceMappingURL=react.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"react.js","names":[],"sources":["../src/react.ts"],"sourcesContent":["import {\n type ReactNode,\n createContext,\n createElement,\n useContext,\n useEffect,\n useMemo,\n useSyncExternalStore,\n} from 'react'\n\nimport type { EnvflagsClient, EnvflagsClientOptions } from './client'\nimport {\n type DefinedEnvflagsClient,\n type FlagDefinitions,\n type FlagValue,\n createDefinedClient,\n prepareFlagDefinitions,\n} from './schema'\nimport type { EvaluationContext, JsonValue } from './types'\n\ntype EnvflagsReactContext = {\n client: EnvflagsClient\n evaluationContext: EvaluationContext\n}\n\nconst Context = createContext<EnvflagsReactContext | undefined>(undefined)\n\nexport type EnvflagsProviderProps = {\n children?: ReactNode\n client: EnvflagsClient\n context: EvaluationContext\n}\n\n/** @deprecated Prefer the typed provider returned by defineFlags(). */\nexport function EnvflagsProvider({\n children,\n client,\n context,\n}: EnvflagsProviderProps) {\n useEffect(() => {\n // Failed initialization leaves hooks on their caller-supplied defaults.\n // The client's onError callback remains the reporting mechanism.\n void client.initialize().catch(() => undefined)\n }, [client])\n\n const value = useMemo(\n () => ({ client, evaluationContext: context }),\n [client, context],\n )\n\n return createElement(Context.Provider, { value }, children)\n}\n\nfunction useEnvflags() {\n const value = useContext(Context)\n if (!value) {\n throw new Error('envflags hooks must be used inside an EnvflagsProvider')\n }\n\n useSyncExternalStore(\n value.client.subscribe,\n value.client.getSnapshot,\n value.client.getSnapshot,\n )\n return value\n}\n\n/** @deprecated Prefer the schema-validated useFlag() returned by defineFlags(). */\nexport function useVariation<T extends JsonValue>(\n flagKey: string,\n defaultValue: T,\n context?: EvaluationContext,\n) {\n const envflags = useEnvflags()\n return envflags.client.variation(\n flagKey,\n defaultValue,\n context ?? envflags.evaluationContext,\n )\n}\n\n/** @deprecated Prefer the schema-validated useFlag() returned by defineFlags(). */\nexport function useBooleanFlag(\n flagKey: string,\n defaultValue: boolean,\n context?: EvaluationContext,\n) {\n const envflags = useEnvflags()\n return envflags.client.boolVariation(\n flagKey,\n defaultValue,\n context ?? envflags.evaluationContext,\n )\n}\n\ntype DefinedReactContext<Definitions extends FlagDefinitions> = {\n client: DefinedEnvflagsClient<Definitions>\n evaluationContext: EvaluationContext\n}\n\nexport type DefinedEnvflagsProviderProps<Definitions extends FlagDefinitions> =\n {\n children?: ReactNode\n client: DefinedEnvflagsClient<Definitions>\n context: EvaluationContext\n }\n\n/**\n * Bind Standard Schema flag definitions to a typed client, provider, and hook.\n * Call this once at module scope and export the returned API.\n */\nexport function defineFlags<const Definitions extends FlagDefinitions>(\n definitions: Definitions,\n) {\n const prepared = prepareFlagDefinitions(definitions)\n const DefinedContext = createContext<\n DefinedReactContext<Definitions> | undefined\n >(undefined)\n\n function createClient(sdkKey: string, options: EnvflagsClientOptions = {}) {\n return createDefinedClient(prepared, sdkKey, options)\n }\n\n function DefinedProvider({\n children,\n client,\n context,\n }: DefinedEnvflagsProviderProps<Definitions>) {\n useEffect(() => {\n void client.initialize().catch(() => undefined)\n }, [client])\n\n const value = useMemo(\n () => ({ client, evaluationContext: context }),\n [client, context],\n )\n\n return createElement(DefinedContext.Provider, { value }, children)\n }\n\n function useFlag<Key extends Extract<keyof Definitions, string>>(\n flagKey: Key,\n context?: EvaluationContext,\n ): FlagValue<Definitions, Key> {\n const value = useContext(DefinedContext)\n if (!value) {\n throw new Error('useFlag must be used inside its EnvflagsProvider')\n }\n\n useSyncExternalStore(\n value.client.subscribe,\n value.client.getSnapshot,\n value.client.getSnapshot,\n )\n return value.client.flag(flagKey, context ?? value.evaluationContext)\n }\n\n return {\n createClient,\n EnvflagsProvider: DefinedProvider,\n useFlag,\n }\n}\n"],"mappings":";;;AAyBA,IAAM,UAAU,cAAgD,KAAA,CAAS;;AASzE,SAAgB,iBAAiB,EAC/B,UACA,QACA,WACwB;CACxB,gBAAgB;EAGd,OAAY,WAAW,CAAC,CAAC,YAAY,KAAA,CAAS;CAChD,GAAG,CAAC,MAAM,CAAC;CAEX,MAAM,QAAQ,eACL;EAAE;EAAQ,mBAAmB;CAAQ,IAC5C,CAAC,QAAQ,OAAO,CAClB;CAEA,OAAO,cAAc,QAAQ,UAAU,EAAE,MAAM,GAAG,QAAQ;AAC5D;AAEA,SAAS,cAAc;CACrB,MAAM,QAAQ,WAAW,OAAO;CAChC,IAAI,CAAC,OACH,MAAM,IAAI,MAAM,wDAAwD;CAG1E,qBACE,MAAM,OAAO,WACb,MAAM,OAAO,aACb,MAAM,OAAO,WACf;CACA,OAAO;AACT;;AAGA,SAAgB,aACd,SACA,cACA,SACA;CACA,MAAM,WAAW,YAAY;CAC7B,OAAO,SAAS,OAAO,UACrB,SACA,cACA,WAAW,SAAS,iBACtB;AACF;;AAGA,SAAgB,eACd,SACA,cACA,SACA;CACA,MAAM,WAAW,YAAY;CAC7B,OAAO,SAAS,OAAO,cACrB,SACA,cACA,WAAW,SAAS,iBACtB;AACF;;;;;AAkBA,SAAgB,YACd,aACA;CACA,MAAM,WAAW,uBAAuB,WAAW;CACnD,MAAM,iBAAiB,cAErB,KAAA,CAAS;CAEX,SAAS,aAAa,QAAgB,UAAiC,CAAC,GAAG;EACzE,OAAO,oBAAoB,UAAU,QAAQ,OAAO;CACtD;CAEA,SAAS,gBAAgB,EACvB,UACA,QACA,WAC4C;EAC5C,gBAAgB;GACd,OAAY,WAAW,CAAC,CAAC,YAAY,KAAA,CAAS;EAChD,GAAG,CAAC,MAAM,CAAC;EAEX,MAAM,QAAQ,eACL;GAAE;GAAQ,mBAAmB;EAAQ,IAC5C,CAAC,QAAQ,OAAO,CAClB;EAEA,OAAO,cAAc,eAAe,UAAU,EAAE,MAAM,GAAG,QAAQ;CACnE;CAEA,SAAS,QACP,SACA,SAC6B;EAC7B,MAAM,QAAQ,WAAW,cAAc;EACvC,IAAI,CAAC,OACH,MAAM,IAAI,MAAM,kDAAkD;EAGpE,qBACE,MAAM,OAAO,WACb,MAAM,OAAO,aACb,MAAM,OAAO,WACf;EACA,OAAO,MAAM,OAAO,KAAK,SAAS,WAAW,MAAM,iBAAiB;CACtE;CAEA,OAAO;EACL;EACA,kBAAkB;EAClB;CACF;AACF"}
@@ -0,0 +1,57 @@
1
+ import { EnvflagsClient, type EnvflagsClientOptions } from './client';
2
+ import type { EvaluationContext, JsonValue } from './types';
3
+ /** The library-neutral Standard Schema v1 validation contract. */
4
+ export interface StandardSchemaV1<Input = unknown, Output = Input> {
5
+ readonly '~standard': StandardSchemaProps<Input, Output>;
6
+ }
7
+ export interface StandardSchemaProps<Input = unknown, Output = Input> {
8
+ readonly version: 1;
9
+ readonly vendor: string;
10
+ readonly validate: (value: unknown, options?: StandardSchemaOptions) => StandardSchemaResult<Output> | Promise<StandardSchemaResult<Output>>;
11
+ readonly types?: StandardSchemaTypes<Input, Output>;
12
+ }
13
+ export interface StandardSchemaOptions {
14
+ readonly libraryOptions?: Record<string, unknown>;
15
+ }
16
+ export interface StandardSchemaTypes<Input = unknown, Output = Input> {
17
+ readonly input: Input;
18
+ readonly output: Output;
19
+ }
20
+ export type StandardSchemaIssue = {
21
+ readonly message: string;
22
+ readonly path?: ReadonlyArray<PropertyKey | StandardSchemaPathSegment>;
23
+ };
24
+ export type StandardSchemaPathSegment = {
25
+ readonly key: PropertyKey;
26
+ };
27
+ export type StandardSchemaResult<Output> = {
28
+ readonly value: Output;
29
+ readonly issues?: undefined;
30
+ } | {
31
+ readonly issues: ReadonlyArray<StandardSchemaIssue>;
32
+ };
33
+ export type StandardSchemaOutput<Schema extends StandardSchemaV1> = NonNullable<Schema['~standard']['types']>['output'];
34
+ export type FlagDefinition<Schema extends StandardSchemaV1 = StandardSchemaV1> = {
35
+ readonly schema: Schema;
36
+ readonly defaultValue: StandardSchemaOutput<Schema>;
37
+ };
38
+ export type FlagDefinitions = Record<string, FlagDefinition>;
39
+ type FlagKey<Definitions extends FlagDefinitions> = Extract<keyof Definitions, string>;
40
+ export type FlagValue<Definitions extends FlagDefinitions, Key extends FlagKey<Definitions>> = StandardSchemaOutput<Definitions[Key]['schema']>;
41
+ type PreparedDefinition = {
42
+ schema: StandardSchemaV1<unknown, JsonValue>;
43
+ defaultValue: JsonValue;
44
+ };
45
+ export type PreparedFlagDefinitions<Definitions extends FlagDefinitions> = {
46
+ readonly definitions: Readonly<Record<FlagKey<Definitions>, PreparedDefinition>>;
47
+ };
48
+ /** Define one flag's runtime schema and offline/invalid-value fallback. */
49
+ export declare function flag<Schema extends StandardSchemaV1>(schema: Schema, defaultValue: StandardSchemaOutput<Schema>): FlagDefinition<Schema>;
50
+ export declare function prepareFlagDefinitions<Definitions extends FlagDefinitions>(definitions: Definitions): PreparedFlagDefinitions<Definitions>;
51
+ export declare class DefinedEnvflagsClient<Definitions extends FlagDefinitions> extends EnvflagsClient {
52
+ #private;
53
+ constructor(sdkKey: string, options: EnvflagsClientOptions, prepared: PreparedFlagDefinitions<Definitions>);
54
+ flag<Key extends FlagKey<Definitions>>(flagKey: Key, context?: EvaluationContext): FlagValue<Definitions, Key>;
55
+ }
56
+ export declare function createDefinedClient<Definitions extends FlagDefinitions>(prepared: PreparedFlagDefinitions<Definitions>, sdkKey: string, options?: EnvflagsClientOptions): DefinedEnvflagsClient<Definitions>;
57
+ export {};
package/dist/sha1.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ /** A small synchronous SHA-1 implementation so flag evaluation stays sync. */
2
+ export declare function sha1(value: string): string;
package/dist/shared.js ADDED
@@ -0,0 +1,444 @@
1
+ //#region src/sha1.ts
2
+ function rotateLeft(value, bits) {
3
+ return (value << bits | value >>> 32 - bits) >>> 0;
4
+ }
5
+ function add(...values) {
6
+ return values.reduce((sum, value) => sum + value >>> 0, 0);
7
+ }
8
+ /** A small synchronous SHA-1 implementation so flag evaluation stays sync. */
9
+ function sha1(value) {
10
+ const input = new TextEncoder().encode(value);
11
+ const paddedLength = Math.ceil((input.length + 9) / 64) * 64;
12
+ const bytes = new Uint8Array(paddedLength);
13
+ bytes.set(input);
14
+ bytes[input.length] = 128;
15
+ const bitLength = BigInt(input.length) * 8n;
16
+ const view = new DataView(bytes.buffer);
17
+ view.setUint32(paddedLength - 8, Number(bitLength >> 32n), false);
18
+ view.setUint32(paddedLength - 4, Number(bitLength & 4294967295n), false);
19
+ let h0 = 1732584193;
20
+ let h1 = 4023233417;
21
+ let h2 = 2562383102;
22
+ let h3 = 271733878;
23
+ let h4 = 3285377520;
24
+ const words = /* @__PURE__ */ new Uint32Array(80);
25
+ for (let offset = 0; offset < paddedLength; offset += 64) {
26
+ for (let index = 0; index < 16; index += 1) words[index] = view.getUint32(offset + index * 4, false);
27
+ for (let index = 16; index < 80; index += 1) words[index] = rotateLeft(words[index - 3] ^ words[index - 8] ^ words[index - 14] ^ words[index - 16], 1);
28
+ let a = h0;
29
+ let b = h1;
30
+ let c = h2;
31
+ let d = h3;
32
+ let e = h4;
33
+ for (let index = 0; index < 80; index += 1) {
34
+ let f;
35
+ let k;
36
+ if (index < 20) {
37
+ f = b & c | ~b & d;
38
+ k = 1518500249;
39
+ } else if (index < 40) {
40
+ f = b ^ c ^ d;
41
+ k = 1859775393;
42
+ } else if (index < 60) {
43
+ f = b & c | b & d | c & d;
44
+ k = 2400959708;
45
+ } else {
46
+ f = b ^ c ^ d;
47
+ k = 3395469782;
48
+ }
49
+ const temporary = add(rotateLeft(a, 5), f, e, k, words[index]);
50
+ e = d;
51
+ d = c;
52
+ c = rotateLeft(b, 30);
53
+ b = a;
54
+ a = temporary;
55
+ }
56
+ h0 = add(h0, a);
57
+ h1 = add(h1, b);
58
+ h2 = add(h2, c);
59
+ h3 = add(h3, d);
60
+ h4 = add(h4, e);
61
+ }
62
+ return [
63
+ h0,
64
+ h1,
65
+ h2,
66
+ h3,
67
+ h4
68
+ ].map((word) => word.toString(16).padStart(8, "0")).join("");
69
+ }
70
+ //#endregion
71
+ //#region src/evaluate.ts
72
+ var BUCKET_DENOMINATOR = Number(1152921504606846975n);
73
+ function hasAttribute(context, attribute) {
74
+ return Object.prototype.hasOwnProperty.call(context, attribute);
75
+ }
76
+ function scalarEquals(left, right) {
77
+ return (left === null || typeof left === "boolean" || typeof left === "number" || typeof left === "string") && left === right;
78
+ }
79
+ function matchesIn(attribute, values) {
80
+ return (Array.isArray(attribute) ? attribute : [attribute]).some((item) => values.some((value) => scalarEquals(item, value)));
81
+ }
82
+ function matchesStrings(attribute, values, predicate) {
83
+ if (typeof attribute !== "string") return false;
84
+ return values.some((value) => typeof value === "string" && predicate(attribute, value));
85
+ }
86
+ function matchesNumbers(attribute, values, predicate) {
87
+ if (typeof attribute !== "number" || !Number.isFinite(attribute)) return false;
88
+ return values.some((value) => typeof value === "number" && Number.isFinite(value) && predicate(attribute, value));
89
+ }
90
+ function instant(value) {
91
+ if (typeof value === "number") return Number.isFinite(value) ? value : void 0;
92
+ if (typeof value !== "string" || !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/.test(value)) return;
93
+ const parsed = Date.parse(value);
94
+ return Number.isFinite(parsed) ? parsed : void 0;
95
+ }
96
+ function matchesDates(attribute, values, predicate) {
97
+ const attributeInstant = instant(attribute);
98
+ if (attributeInstant === void 0) return false;
99
+ return values.some((value) => {
100
+ const valueInstant = instant(value);
101
+ return valueInstant !== void 0 && predicate(attributeInstant, valueInstant);
102
+ });
103
+ }
104
+ function matchesSegment(segment, context, payload, visiting) {
105
+ const key = context.key;
106
+ if (typeof key === "string" && key.length > 0) {
107
+ if (segment.excluded?.includes(key)) return false;
108
+ if (segment.included?.includes(key)) return true;
109
+ }
110
+ return (segment.rules ?? []).some((rule) => rule.clauses.every((clause) => matchesClause(clause, context, payload, visiting)));
111
+ }
112
+ function matchesSegments(values, context, payload, visiting) {
113
+ return values.some((value) => {
114
+ if (typeof value !== "string" || visiting.has(value)) return false;
115
+ const segment = payload.segments[value];
116
+ if (!segment) return false;
117
+ const nestedVisiting = new Set(visiting);
118
+ nestedVisiting.add(value);
119
+ return matchesSegment(segment, context, payload, nestedVisiting);
120
+ });
121
+ }
122
+ function matchesClause(clause, context, payload, visiting) {
123
+ if (!hasAttribute(context, clause.attribute)) return false;
124
+ const attribute = context[clause.attribute];
125
+ switch (clause.op) {
126
+ case "in": return matchesIn(attribute, clause.values);
127
+ case "notIn": return !matchesIn(attribute, clause.values);
128
+ case "startsWith": return matchesStrings(attribute, clause.values, (left, right) => left.startsWith(right));
129
+ case "endsWith": return matchesStrings(attribute, clause.values, (left, right) => left.endsWith(right));
130
+ case "contains": return matchesStrings(attribute, clause.values, (left, right) => left.includes(right));
131
+ case "matches": return matchesStrings(attribute, clause.values, (left, right) => {
132
+ try {
133
+ return new RegExp(right).test(left);
134
+ } catch {
135
+ return false;
136
+ }
137
+ });
138
+ case "lessThan": return matchesNumbers(attribute, clause.values, (left, right) => left < right);
139
+ case "greaterThan": return matchesNumbers(attribute, clause.values, (left, right) => left > right);
140
+ case "before": return matchesDates(attribute, clause.values, (left, right) => left < right);
141
+ case "after": return matchesDates(attribute, clause.values, (left, right) => left > right);
142
+ case "segmentMatch": return matchesSegments(clause.values, context, payload, visiting);
143
+ }
144
+ }
145
+ function bucket(flagKey, salt, bucketBy) {
146
+ const digest = sha1(`${flagKey}.${salt}.${bucketBy}`);
147
+ const integer = BigInt(`0x${digest.slice(0, 15)}`);
148
+ return Number(integer) / BUCKET_DENOMINATOR;
149
+ }
150
+ function selectVariation(selector, flagKey, context, payload) {
151
+ if ("variation" in selector && selector.variation !== void 0) return selector.variation;
152
+ const rollout = selector.rollout;
153
+ if (!rollout?.length) return void 0;
154
+ const bucketAttribute = selector.bucketBy ?? "key";
155
+ if (!hasAttribute(context, bucketAttribute)) return rollout.at(-1)?.variation;
156
+ const bucketBy = String(context[bucketAttribute]);
157
+ const contextBucket = bucket(flagKey, payload.salt, bucketBy);
158
+ let accumulatedWeight = 0;
159
+ for (const entry of rollout) {
160
+ accumulatedWeight += entry.weight / 1e5;
161
+ if (contextBucket < accumulatedWeight) return entry.variation;
162
+ }
163
+ return rollout.at(-1)?.variation;
164
+ }
165
+ function evaluate(payload, flagKey, context, defaultValue) {
166
+ const flag = payload.flags[flagKey];
167
+ if (!flag) return defaultValue;
168
+ const resolved = context ?? {};
169
+ const key = resolved.key;
170
+ let variation;
171
+ if (!flag.on) variation = flag.offVariation;
172
+ else {
173
+ variation = flag.targets.find((target) => typeof key === "string" && key.length > 0 && target.values.includes(key))?.variation;
174
+ if (variation === void 0) {
175
+ const rule = flag.rules.find((candidate) => candidate.clauses.every((clause) => matchesClause(clause, resolved, payload, /* @__PURE__ */ new Set())));
176
+ if (rule) variation = selectVariation(rule, flagKey, resolved, payload);
177
+ }
178
+ variation ??= selectVariation(flag.fallthrough, flagKey, resolved, payload);
179
+ }
180
+ return variation === void 0 || !(variation in flag.variations) ? defaultValue : flag.variations[variation];
181
+ }
182
+ //#endregion
183
+ //#region src/client.ts
184
+ var DEFAULT_BASE_URL = "https://envflags.com";
185
+ var DEFAULT_POLL_INTERVAL_MS = 3e4;
186
+ function isRecord(value) {
187
+ return typeof value === "object" && value !== null && !Array.isArray(value);
188
+ }
189
+ function parsePollResponse(value) {
190
+ if (!isRecord(value) || typeof value.envId !== "string" || !Number.isSafeInteger(value.version) || value.version < 0 || typeof value.etag !== "string" || typeof value.payloadUrl !== "string") throw new Error("The envflags poll response is invalid");
191
+ return value;
192
+ }
193
+ function parsePayload(value) {
194
+ if (!isRecord(value) || typeof value.envId !== "string" || !Number.isSafeInteger(value.version) || value.version < 0 || typeof value.salt !== "string" || !isRecord(value.flags) || !isRecord(value.segments)) throw new Error("The envflags payload is invalid");
195
+ return value;
196
+ }
197
+ function responseError(action, response) {
198
+ return /* @__PURE__ */ new Error(`Unable to ${action}: envflags returned ${response.status} ${response.statusText}`);
199
+ }
200
+ var EnvflagsClient = class {
201
+ #baseUrl;
202
+ #fetch;
203
+ #onError;
204
+ #pollIntervalMs;
205
+ #sdkKey;
206
+ #context;
207
+ #etag;
208
+ #payload;
209
+ #revision = 0;
210
+ #refreshPromise;
211
+ #subscribers = /* @__PURE__ */ new Set();
212
+ #timer;
213
+ constructor(sdkKey, options = {}) {
214
+ const normalizedSdkKey = sdkKey.trim();
215
+ if (!normalizedSdkKey) throw new Error("An envflags SDK key is required");
216
+ if (options.pollIntervalMs !== void 0 && options.pollIntervalMs !== false && (!Number.isFinite(options.pollIntervalMs) || options.pollIntervalMs < 0)) throw new Error("pollIntervalMs must be a non-negative finite number");
217
+ this.#sdkKey = normalizedSdkKey;
218
+ this.#baseUrl = new URL(options.baseUrl ?? DEFAULT_BASE_URL);
219
+ this.#fetch = options.fetch ?? globalThis.fetch.bind(globalThis);
220
+ this.#onError = options.onError ?? (() => void 0);
221
+ this.#pollIntervalMs = options.pollIntervalMs === void 0 ? DEFAULT_POLL_INTERVAL_MS : options.pollIntervalMs;
222
+ if (options.bootstrap) {
223
+ this.#payload = parsePayload(options.bootstrap);
224
+ this.#revision = 1;
225
+ }
226
+ }
227
+ get initialized() {
228
+ return this.#payload !== void 0;
229
+ }
230
+ get version() {
231
+ return this.#payload?.version;
232
+ }
233
+ /** Subscribe to payload changes. Intended for UI framework bindings. */
234
+ subscribe = (listener) => {
235
+ this.#subscribers.add(listener);
236
+ return () => {
237
+ this.#subscribers.delete(listener);
238
+ };
239
+ };
240
+ /** Return a stable value suitable for React's useSyncExternalStore. */
241
+ getSnapshot = () => this.#revision;
242
+ reportError(error) {
243
+ this.#onError(error);
244
+ }
245
+ async initialize() {
246
+ try {
247
+ await this.refresh();
248
+ } catch (cause) {
249
+ const error = cause instanceof Error ? cause : /* @__PURE__ */ new Error("Unable to initialize envflags");
250
+ this.reportError(error);
251
+ if (!this.#payload) throw error;
252
+ }
253
+ this.#startPolling();
254
+ return this;
255
+ }
256
+ refresh() {
257
+ if (!this.#refreshPromise) this.#refreshPromise = this.#performRefresh().finally(() => {
258
+ this.#refreshPromise = void 0;
259
+ });
260
+ return this.#refreshPromise;
261
+ }
262
+ close() {
263
+ if (this.#timer !== void 0) clearInterval(this.#timer);
264
+ this.#timer = void 0;
265
+ }
266
+ setContext(context) {
267
+ this.#context = context;
268
+ return this;
269
+ }
270
+ variation(flagKey, defaultValue, context) {
271
+ if (!this.#payload) return defaultValue;
272
+ return evaluate(this.#payload, flagKey, context ?? this.#context, defaultValue);
273
+ }
274
+ boolVariation(flagKey, defaultValue, context) {
275
+ const value = this.variation(flagKey, defaultValue, context);
276
+ return typeof value === "boolean" ? value : defaultValue;
277
+ }
278
+ stringVariation(flagKey, defaultValue, context) {
279
+ const value = this.variation(flagKey, defaultValue, context);
280
+ return typeof value === "string" ? value : defaultValue;
281
+ }
282
+ numberVariation(flagKey, defaultValue, context) {
283
+ const value = this.variation(flagKey, defaultValue, context);
284
+ return typeof value === "number" && Number.isFinite(value) ? value : defaultValue;
285
+ }
286
+ jsonVariation(flagKey, defaultValue, context) {
287
+ return this.variation(flagKey, defaultValue, context);
288
+ }
289
+ async #performRefresh() {
290
+ const headers = new Headers({
291
+ accept: "application/json",
292
+ authorization: `Bearer ${this.#sdkKey}`
293
+ });
294
+ if (this.#etag) headers.set("if-none-match", this.#etag);
295
+ const pollUrl = new URL("/sdk/poll", this.#baseUrl);
296
+ const pollResponse = await this.#fetch(pollUrl, { headers });
297
+ if (pollResponse.status === 304) return false;
298
+ if (!pollResponse.ok) throw responseError("poll for flag updates", pollResponse);
299
+ const pointer = parsePollResponse(await pollResponse.json());
300
+ if (this.#payload?.envId === pointer.envId && this.#payload.version === pointer.version) {
301
+ this.#etag = pointer.etag;
302
+ return false;
303
+ }
304
+ const payloadUrl = new URL(pointer.payloadUrl, this.#baseUrl);
305
+ const payloadResponse = await this.#fetch(payloadUrl, { headers: {
306
+ accept: "application/json",
307
+ authorization: `Bearer ${this.#sdkKey}`
308
+ } });
309
+ if (!payloadResponse.ok) throw responseError("fetch the flag payload", payloadResponse);
310
+ const payload = parsePayload(await payloadResponse.json());
311
+ if (payload.envId !== pointer.envId || payload.version !== pointer.version) throw new Error("The envflags payload does not match its version pointer");
312
+ this.#payload = payload;
313
+ this.#etag = pointer.etag;
314
+ this.#revision += 1;
315
+ for (const subscriber of this.#subscribers) subscriber();
316
+ return true;
317
+ }
318
+ #startPolling() {
319
+ if (this.#timer !== void 0 || this.#pollIntervalMs === false || this.#pollIntervalMs <= 0) return;
320
+ this.#timer = setInterval(() => {
321
+ this.refresh().catch((cause) => {
322
+ this.reportError(cause instanceof Error ? cause : /* @__PURE__ */ new Error("Unable to refresh envflags"));
323
+ });
324
+ }, this.#pollIntervalMs);
325
+ }
326
+ };
327
+ function createClient(sdkKey, options = {}) {
328
+ return new EnvflagsClient(sdkKey, options);
329
+ }
330
+ //#endregion
331
+ //#region src/schema.ts
332
+ /** Define one flag's runtime schema and offline/invalid-value fallback. */
333
+ function flag(schema, defaultValue) {
334
+ return {
335
+ schema,
336
+ defaultValue
337
+ };
338
+ }
339
+ function isPromise(value) {
340
+ return typeof value === "object" && value !== null && "then" in value && typeof value.then === "function";
341
+ }
342
+ function isJsonValue(value, ancestors = /* @__PURE__ */ new Set()) {
343
+ if (value === null || typeof value === "boolean" || typeof value === "string") return true;
344
+ if (typeof value === "number") return Number.isFinite(value);
345
+ if (typeof value !== "object") return false;
346
+ if (ancestors.has(value)) return false;
347
+ const nestedAncestors = new Set(ancestors);
348
+ nestedAncestors.add(value);
349
+ if (Array.isArray(value)) return value.every((item) => isJsonValue(item, nestedAncestors));
350
+ const prototype = Object.getPrototypeOf(value);
351
+ if (prototype !== Object.prototype && prototype !== null) return false;
352
+ return Object.values(value).every((item) => isJsonValue(item, nestedAncestors));
353
+ }
354
+ function jsonEquals(left, right) {
355
+ if (left === right) return true;
356
+ if (Array.isArray(left) || Array.isArray(right)) return Array.isArray(left) && Array.isArray(right) && left.length === right.length && left.every((value, index) => jsonEquals(value, right[index]));
357
+ if (left === null || right === null) return false;
358
+ if (typeof left !== "object" || typeof right !== "object") return false;
359
+ const leftKeys = Object.keys(left);
360
+ const rightKeys = Object.keys(right);
361
+ return leftKeys.length === rightKeys.length && leftKeys.every((key) => key in right && jsonEquals(left[key], right[key]));
362
+ }
363
+ function formatIssues(issues) {
364
+ return issues.map((issue) => issue.message).join("; ") || "validation failed";
365
+ }
366
+ function validate(flagKey, schema, value) {
367
+ let result;
368
+ try {
369
+ result = schema["~standard"].validate(value);
370
+ } catch (cause) {
371
+ return {
372
+ ok: false,
373
+ message: cause instanceof Error ? cause.message : "validation threw"
374
+ };
375
+ }
376
+ if (isPromise(result)) return {
377
+ ok: false,
378
+ message: `Flag "${flagKey}" uses an asynchronous schema; flag schemas must validate synchronously`
379
+ };
380
+ if (result.issues) return {
381
+ ok: false,
382
+ message: formatIssues(result.issues)
383
+ };
384
+ if (!isJsonValue(result.value)) return {
385
+ ok: false,
386
+ message: `Flag "${flagKey}" schema produced a non-JSON value`
387
+ };
388
+ return {
389
+ ok: true,
390
+ value: result.value
391
+ };
392
+ }
393
+ function prepareFlagDefinitions(definitions) {
394
+ const prepared = {};
395
+ for (const [flagKey, definition] of Object.entries(definitions)) {
396
+ const schema = definition.schema;
397
+ if (schema?.["~standard"]?.version !== 1) throw new Error(`Flag "${flagKey}" must use a Standard Schema v1 schema`);
398
+ const result = validate(flagKey, schema, definition.defaultValue);
399
+ if (!result.ok) throw new Error(`Invalid default for flag "${flagKey}": ${result.message}`);
400
+ const repeated = validate(flagKey, schema, result.value);
401
+ if (!repeated.ok || !jsonEquals(result.value, repeated.value)) throw new Error(`Invalid default for flag "${flagKey}": schema output must remain stable when validated again`);
402
+ prepared[flagKey] = {
403
+ schema,
404
+ defaultValue: result.value
405
+ };
406
+ }
407
+ return { definitions: Object.freeze(prepared) };
408
+ }
409
+ var DefinedEnvflagsClient = class extends EnvflagsClient {
410
+ #prepared;
411
+ #cacheRevision = -1;
412
+ #validationCache = /* @__PURE__ */ new Map();
413
+ constructor(sdkKey, options, prepared) {
414
+ super(sdkKey, options);
415
+ this.#prepared = prepared;
416
+ }
417
+ flag(flagKey, context) {
418
+ const definition = this.#prepared.definitions[flagKey];
419
+ const candidate = this.variation(flagKey, definition.defaultValue, context);
420
+ if (this.#cacheRevision !== this.getSnapshot()) {
421
+ this.#cacheRevision = this.getSnapshot();
422
+ this.#validationCache.clear();
423
+ }
424
+ let flagCache = this.#validationCache.get(flagKey);
425
+ if (!flagCache) {
426
+ flagCache = /* @__PURE__ */ new Map();
427
+ this.#validationCache.set(flagKey, flagCache);
428
+ }
429
+ let result = flagCache.get(candidate);
430
+ if (!result) {
431
+ result = validate(flagKey, definition.schema, candidate);
432
+ flagCache.set(candidate, result);
433
+ if (!result.ok) this.reportError(/* @__PURE__ */ new Error(`Invalid value for flag "${flagKey}": ${result.message}; using its default`));
434
+ }
435
+ return result.ok ? result.value : definition.defaultValue;
436
+ }
437
+ };
438
+ function createDefinedClient(prepared, sdkKey, options = {}) {
439
+ return new DefinedEnvflagsClient(sdkKey, options, prepared);
440
+ }
441
+ //#endregion
442
+ export { createClient as a, sha1 as c, EnvflagsClient as i, flag as n, bucket as o, prepareFlagDefinitions as r, evaluate as s, createDefinedClient as t };
443
+
444
+ //# sourceMappingURL=shared.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.js","names":["#baseUrl","#fetch","#onError","#pollIntervalMs","#sdkKey","#payload","#revision","#subscribers","#startPolling","#refreshPromise","#performRefresh","#timer","#context","#etag","#prepared","#cacheRevision","#validationCache"],"sources":["../src/sha1.ts","../src/evaluate.ts","../src/client.ts","../src/schema.ts"],"sourcesContent":["function rotateLeft(value: number, bits: number) {\n return ((value << bits) | (value >>> (32 - bits))) >>> 0\n}\n\nfunction add(...values: number[]) {\n return values.reduce((sum, value) => (sum + value) >>> 0, 0)\n}\n\n/** A small synchronous SHA-1 implementation so flag evaluation stays sync. */\nexport function sha1(value: string) {\n const input = new TextEncoder().encode(value)\n const paddedLength = Math.ceil((input.length + 9) / 64) * 64\n const bytes = new Uint8Array(paddedLength)\n bytes.set(input)\n bytes[input.length] = 0x80\n\n const bitLength = BigInt(input.length) * 8n\n const view = new DataView(bytes.buffer)\n view.setUint32(paddedLength - 8, Number(bitLength >> 32n), false)\n view.setUint32(paddedLength - 4, Number(bitLength & 0xffffffffn), false)\n\n let h0 = 0x67452301\n let h1 = 0xefcdab89\n let h2 = 0x98badcfe\n let h3 = 0x10325476\n let h4 = 0xc3d2e1f0\n const words = new Uint32Array(80)\n\n for (let offset = 0; offset < paddedLength; offset += 64) {\n for (let index = 0; index < 16; index += 1) {\n words[index] = view.getUint32(offset + index * 4, false)\n }\n for (let index = 16; index < 80; index += 1) {\n words[index] = rotateLeft(\n words[index - 3]! ^\n words[index - 8]! ^\n words[index - 14]! ^\n words[index - 16]!,\n 1,\n )\n }\n\n let a = h0\n let b = h1\n let c = h2\n let d = h3\n let e = h4\n\n for (let index = 0; index < 80; index += 1) {\n let f: number\n let k: number\n\n if (index < 20) {\n f = (b & c) | (~b & d)\n k = 0x5a827999\n } else if (index < 40) {\n f = b ^ c ^ d\n k = 0x6ed9eba1\n } else if (index < 60) {\n f = (b & c) | (b & d) | (c & d)\n k = 0x8f1bbcdc\n } else {\n f = b ^ c ^ d\n k = 0xca62c1d6\n }\n\n const temporary = add(rotateLeft(a, 5), f, e, k, words[index]!)\n e = d\n d = c\n c = rotateLeft(b, 30)\n b = a\n a = temporary\n }\n\n h0 = add(h0, a)\n h1 = add(h1, b)\n h2 = add(h2, c)\n h3 = add(h3, d)\n h4 = add(h4, e)\n }\n\n return [h0, h1, h2, h3, h4]\n .map(word => word.toString(16).padStart(8, '0'))\n .join('')\n}\n","import { sha1 } from './sha1'\nimport type {\n Clause,\n EvaluationContext,\n EvaluationPayload,\n JsonValue,\n Segment,\n VariationSelector,\n} from './types'\n\nconst BUCKET_DENOMINATOR = Number(0x0fff_ffff_ffff_ffffn)\n\nfunction hasAttribute(context: EvaluationContext, attribute: string) {\n return Object.prototype.hasOwnProperty.call(context, attribute)\n}\n\nfunction scalarEquals(left: unknown, right: JsonValue) {\n return (\n (left === null ||\n typeof left === 'boolean' ||\n typeof left === 'number' ||\n typeof left === 'string') &&\n left === right\n )\n}\n\nfunction matchesIn(attribute: unknown, values: JsonValue[]) {\n const attributes = Array.isArray(attribute) ? attribute : [attribute]\n return attributes.some(item =>\n values.some(value => scalarEquals(item, value)),\n )\n}\n\nfunction matchesStrings(\n attribute: unknown,\n values: JsonValue[],\n predicate: (attribute: string, value: string) => boolean,\n) {\n if (typeof attribute !== 'string') return false\n return values.some(\n value => typeof value === 'string' && predicate(attribute, value),\n )\n}\n\nfunction matchesNumbers(\n attribute: unknown,\n values: JsonValue[],\n predicate: (attribute: number, value: number) => boolean,\n) {\n if (typeof attribute !== 'number' || !Number.isFinite(attribute)) return false\n return values.some(\n value =>\n typeof value === 'number' &&\n Number.isFinite(value) &&\n predicate(attribute, value),\n )\n}\n\nfunction instant(value: unknown) {\n if (typeof value === 'number')\n return Number.isFinite(value) ? value : undefined\n if (\n typeof value !== 'string' ||\n !/^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?(?:Z|[+-]\\d{2}:\\d{2})$/.test(\n value,\n )\n ) {\n return undefined\n }\n\n const parsed = Date.parse(value)\n return Number.isFinite(parsed) ? parsed : undefined\n}\n\nfunction matchesDates(\n attribute: unknown,\n values: JsonValue[],\n predicate: (attribute: number, value: number) => boolean,\n) {\n const attributeInstant = instant(attribute)\n if (attributeInstant === undefined) return false\n\n return values.some(value => {\n const valueInstant = instant(value)\n return (\n valueInstant !== undefined && predicate(attributeInstant, valueInstant)\n )\n })\n}\n\nfunction matchesSegment(\n segment: Segment,\n context: EvaluationContext,\n payload: EvaluationPayload,\n visiting: Set<string>,\n) {\n const key = context.key\n if (typeof key === 'string' && key.length > 0) {\n if (segment.excluded?.includes(key)) return false\n if (segment.included?.includes(key)) return true\n }\n\n return (segment.rules ?? []).some(rule =>\n rule.clauses.every(clause =>\n matchesClause(clause, context, payload, visiting),\n ),\n )\n}\n\nfunction matchesSegments(\n values: JsonValue[],\n context: EvaluationContext,\n payload: EvaluationPayload,\n visiting: Set<string>,\n) {\n return values.some(value => {\n if (typeof value !== 'string' || visiting.has(value)) return false\n const segment = payload.segments[value]\n if (!segment) return false\n\n const nestedVisiting = new Set(visiting)\n nestedVisiting.add(value)\n return matchesSegment(segment, context, payload, nestedVisiting)\n })\n}\n\nfunction matchesClause(\n clause: Clause,\n context: EvaluationContext,\n payload: EvaluationPayload,\n visiting: Set<string>,\n): boolean {\n if (!hasAttribute(context, clause.attribute)) return false\n const attribute = context[clause.attribute]\n\n switch (clause.op) {\n case 'in':\n return matchesIn(attribute, clause.values)\n case 'notIn':\n return !matchesIn(attribute, clause.values)\n case 'startsWith':\n return matchesStrings(attribute, clause.values, (left, right) =>\n left.startsWith(right),\n )\n case 'endsWith':\n return matchesStrings(attribute, clause.values, (left, right) =>\n left.endsWith(right),\n )\n case 'contains':\n return matchesStrings(attribute, clause.values, (left, right) =>\n left.includes(right),\n )\n case 'matches':\n return matchesStrings(attribute, clause.values, (left, right) => {\n try {\n return new RegExp(right).test(left)\n } catch {\n return false\n }\n })\n case 'lessThan':\n return matchesNumbers(\n attribute,\n clause.values,\n (left, right) => left < right,\n )\n case 'greaterThan':\n return matchesNumbers(\n attribute,\n clause.values,\n (left, right) => left > right,\n )\n case 'before':\n return matchesDates(\n attribute,\n clause.values,\n (left, right) => left < right,\n )\n case 'after':\n return matchesDates(\n attribute,\n clause.values,\n (left, right) => left > right,\n )\n case 'segmentMatch':\n return matchesSegments(clause.values, context, payload, visiting)\n }\n}\n\nexport function bucket(flagKey: string, salt: string, bucketBy: string) {\n const digest = sha1(`${flagKey}.${salt}.${bucketBy}`)\n const integer = BigInt(`0x${digest.slice(0, 15)}`)\n return Number(integer) / BUCKET_DENOMINATOR\n}\n\nfunction selectVariation(\n selector: VariationSelector,\n flagKey: string,\n context: EvaluationContext,\n payload: EvaluationPayload,\n) {\n if ('variation' in selector && selector.variation !== undefined) {\n return selector.variation\n }\n\n const rollout = selector.rollout\n if (!rollout?.length) return undefined\n\n const bucketAttribute = selector.bucketBy ?? 'key'\n if (!hasAttribute(context, bucketAttribute)) {\n return rollout.at(-1)?.variation\n }\n\n const bucketBy = String(context[bucketAttribute])\n const contextBucket = bucket(flagKey, payload.salt, bucketBy)\n let accumulatedWeight = 0\n\n for (const entry of rollout) {\n accumulatedWeight += entry.weight / 100_000\n if (contextBucket < accumulatedWeight) return entry.variation\n }\n\n return rollout.at(-1)?.variation\n}\n\nexport function evaluate<T extends JsonValue>(\n payload: EvaluationPayload,\n flagKey: string,\n context: EvaluationContext | undefined,\n defaultValue: T,\n): JsonValue {\n const flag = payload.flags[flagKey]\n if (!flag) return defaultValue\n\n const resolved = context ?? {}\n const key = resolved.key\n\n let variation: number | undefined\n\n if (!flag.on) {\n variation = flag.offVariation\n } else {\n variation = flag.targets.find(\n target =>\n typeof key === 'string' &&\n key.length > 0 &&\n target.values.includes(key),\n )?.variation\n\n if (variation === undefined) {\n const rule = flag.rules.find(candidate =>\n candidate.clauses.every(clause =>\n matchesClause(clause, resolved, payload, new Set()),\n ),\n )\n if (rule) variation = selectVariation(rule, flagKey, resolved, payload)\n }\n\n variation ??= selectVariation(flag.fallthrough, flagKey, resolved, payload)\n }\n\n return variation === undefined || !(variation in flag.variations)\n ? defaultValue\n : flag.variations[variation]!\n}\n","import { evaluate } from './evaluate'\nimport type { EvaluationContext, EvaluationPayload, JsonValue } from './types'\n\nconst DEFAULT_BASE_URL = 'https://envflags.com'\nconst DEFAULT_POLL_INTERVAL_MS = 30_000\n\ntype Fetch = typeof globalThis.fetch\n\nexport type EnvflagsClientOptions = {\n baseUrl?: string\n bootstrap?: EvaluationPayload\n fetch?: Fetch\n onError?: (error: Error) => void\n pollIntervalMs?: number | false\n}\n\ntype PollResponse = {\n envId: string\n version: number\n etag: string\n payloadUrl: string\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction parsePollResponse(value: unknown): PollResponse {\n if (\n !isRecord(value) ||\n typeof value.envId !== 'string' ||\n !Number.isSafeInteger(value.version) ||\n (value.version as number) < 0 ||\n typeof value.etag !== 'string' ||\n typeof value.payloadUrl !== 'string'\n ) {\n throw new Error('The envflags poll response is invalid')\n }\n\n return value as PollResponse\n}\n\nfunction parsePayload(value: unknown): EvaluationPayload {\n if (\n !isRecord(value) ||\n typeof value.envId !== 'string' ||\n !Number.isSafeInteger(value.version) ||\n (value.version as number) < 0 ||\n typeof value.salt !== 'string' ||\n !isRecord(value.flags) ||\n !isRecord(value.segments)\n ) {\n throw new Error('The envflags payload is invalid')\n }\n\n return value as EvaluationPayload\n}\n\nfunction responseError(action: string, response: Response) {\n return new Error(\n `Unable to ${action}: envflags returned ${response.status} ${response.statusText}`,\n )\n}\n\nexport class EnvflagsClient {\n readonly #baseUrl: URL\n readonly #fetch: Fetch\n readonly #onError: (error: Error) => void\n readonly #pollIntervalMs: number | false\n readonly #sdkKey: string\n #context?: EvaluationContext\n #etag?: string\n #payload?: EvaluationPayload\n #revision = 0\n #refreshPromise?: Promise<boolean>\n #subscribers = new Set<() => void>()\n #timer?: ReturnType<typeof setInterval>\n\n constructor(sdkKey: string, options: EnvflagsClientOptions = {}) {\n const normalizedSdkKey = sdkKey.trim()\n if (!normalizedSdkKey) throw new Error('An envflags SDK key is required')\n if (\n options.pollIntervalMs !== undefined &&\n options.pollIntervalMs !== false &&\n (!Number.isFinite(options.pollIntervalMs) || options.pollIntervalMs < 0)\n ) {\n throw new Error('pollIntervalMs must be a non-negative finite number')\n }\n\n this.#sdkKey = normalizedSdkKey\n this.#baseUrl = new URL(options.baseUrl ?? DEFAULT_BASE_URL)\n this.#fetch = options.fetch ?? globalThis.fetch.bind(globalThis)\n this.#onError = options.onError ?? (() => undefined)\n this.#pollIntervalMs =\n options.pollIntervalMs === undefined\n ? DEFAULT_POLL_INTERVAL_MS\n : options.pollIntervalMs\n if (options.bootstrap) {\n this.#payload = parsePayload(options.bootstrap)\n this.#revision = 1\n }\n }\n\n get initialized() {\n return this.#payload !== undefined\n }\n\n get version() {\n return this.#payload?.version\n }\n\n /** Subscribe to payload changes. Intended for UI framework bindings. */\n subscribe = (listener: () => void) => {\n this.#subscribers.add(listener)\n return () => {\n this.#subscribers.delete(listener)\n }\n }\n\n /** Return a stable value suitable for React's useSyncExternalStore. */\n getSnapshot = () => this.#revision\n\n protected reportError(error: Error) {\n this.#onError(error)\n }\n\n async initialize() {\n try {\n await this.refresh()\n } catch (cause) {\n const error =\n cause instanceof Error\n ? cause\n : new Error('Unable to initialize envflags')\n this.reportError(error)\n if (!this.#payload) throw error\n }\n\n this.#startPolling()\n return this\n }\n\n refresh() {\n if (!this.#refreshPromise) {\n this.#refreshPromise = this.#performRefresh().finally(() => {\n this.#refreshPromise = undefined\n })\n }\n\n return this.#refreshPromise\n }\n\n close() {\n if (this.#timer !== undefined) clearInterval(this.#timer)\n this.#timer = undefined\n }\n\n setContext(context: EvaluationContext) {\n this.#context = context\n return this\n }\n\n variation<T extends JsonValue>(\n flagKey: string,\n defaultValue: T,\n context?: EvaluationContext,\n ): T {\n if (!this.#payload) return defaultValue\n return evaluate(\n this.#payload,\n flagKey,\n context ?? this.#context,\n defaultValue,\n ) as T\n }\n\n boolVariation(\n flagKey: string,\n defaultValue: boolean,\n context?: EvaluationContext,\n ) {\n const value = this.variation(flagKey, defaultValue, context)\n return typeof value === 'boolean' ? value : defaultValue\n }\n\n stringVariation(\n flagKey: string,\n defaultValue: string,\n context?: EvaluationContext,\n ) {\n const value = this.variation(flagKey, defaultValue, context)\n return typeof value === 'string' ? value : defaultValue\n }\n\n numberVariation(\n flagKey: string,\n defaultValue: number,\n context?: EvaluationContext,\n ) {\n const value = this.variation(flagKey, defaultValue, context)\n return typeof value === 'number' && Number.isFinite(value)\n ? value\n : defaultValue\n }\n\n jsonVariation<T extends JsonValue>(\n flagKey: string,\n defaultValue: T,\n context?: EvaluationContext,\n ) {\n return this.variation(flagKey, defaultValue, context)\n }\n\n async #performRefresh() {\n const headers = new Headers({\n accept: 'application/json',\n authorization: `Bearer ${this.#sdkKey}`,\n })\n if (this.#etag) headers.set('if-none-match', this.#etag)\n\n const pollUrl = new URL('/sdk/poll', this.#baseUrl)\n const pollResponse = await this.#fetch(pollUrl, { headers })\n if (pollResponse.status === 304) return false\n if (!pollResponse.ok)\n throw responseError('poll for flag updates', pollResponse)\n\n const pointer = parsePollResponse(await pollResponse.json())\n if (\n this.#payload?.envId === pointer.envId &&\n this.#payload.version === pointer.version\n ) {\n this.#etag = pointer.etag\n return false\n }\n\n const payloadUrl = new URL(pointer.payloadUrl, this.#baseUrl)\n const payloadResponse = await this.#fetch(payloadUrl, {\n headers: {\n accept: 'application/json',\n authorization: `Bearer ${this.#sdkKey}`,\n },\n })\n if (!payloadResponse.ok) {\n throw responseError('fetch the flag payload', payloadResponse)\n }\n\n const payload = parsePayload(await payloadResponse.json())\n if (\n payload.envId !== pointer.envId ||\n payload.version !== pointer.version\n ) {\n throw new Error('The envflags payload does not match its version pointer')\n }\n\n this.#payload = payload\n this.#etag = pointer.etag\n this.#revision += 1\n for (const subscriber of this.#subscribers) subscriber()\n return true\n }\n\n #startPolling() {\n if (\n this.#timer !== undefined ||\n this.#pollIntervalMs === false ||\n this.#pollIntervalMs <= 0\n ) {\n return\n }\n\n this.#timer = setInterval(() => {\n void this.refresh().catch(cause => {\n this.reportError(\n cause instanceof Error\n ? cause\n : new Error('Unable to refresh envflags'),\n )\n })\n }, this.#pollIntervalMs)\n }\n}\n\nexport function createClient(\n sdkKey: string,\n options: EnvflagsClientOptions = {},\n) {\n return new EnvflagsClient(sdkKey, options)\n}\n","import { EnvflagsClient, type EnvflagsClientOptions } from './client'\nimport type { EvaluationContext, JsonValue } from './types'\n\n/** The library-neutral Standard Schema v1 validation contract. */\nexport interface StandardSchemaV1<Input = unknown, Output = Input> {\n readonly '~standard': StandardSchemaProps<Input, Output>\n}\n\nexport interface StandardSchemaProps<Input = unknown, Output = Input> {\n readonly version: 1\n readonly vendor: string\n readonly validate: (\n value: unknown,\n options?: StandardSchemaOptions,\n ) => StandardSchemaResult<Output> | Promise<StandardSchemaResult<Output>>\n readonly types?: StandardSchemaTypes<Input, Output>\n}\n\nexport interface StandardSchemaOptions {\n readonly libraryOptions?: Record<string, unknown>\n}\n\nexport interface StandardSchemaTypes<Input = unknown, Output = Input> {\n readonly input: Input\n readonly output: Output\n}\n\nexport type StandardSchemaIssue = {\n readonly message: string\n readonly path?: ReadonlyArray<PropertyKey | StandardSchemaPathSegment>\n}\n\nexport type StandardSchemaPathSegment = {\n readonly key: PropertyKey\n}\n\nexport type StandardSchemaResult<Output> =\n | { readonly value: Output; readonly issues?: undefined }\n | { readonly issues: ReadonlyArray<StandardSchemaIssue> }\n\nexport type StandardSchemaOutput<Schema extends StandardSchemaV1> = NonNullable<\n Schema['~standard']['types']\n>['output']\n\nexport type FlagDefinition<Schema extends StandardSchemaV1 = StandardSchemaV1> =\n {\n readonly schema: Schema\n readonly defaultValue: StandardSchemaOutput<Schema>\n }\n\nexport type FlagDefinitions = Record<string, FlagDefinition>\n\ntype FlagKey<Definitions extends FlagDefinitions> = Extract<\n keyof Definitions,\n string\n>\n\nexport type FlagValue<\n Definitions extends FlagDefinitions,\n Key extends FlagKey<Definitions>,\n> = StandardSchemaOutput<Definitions[Key]['schema']>\n\ntype ValidationResult =\n { ok: true; value: JsonValue } | { ok: false; message: string }\n\ntype PreparedDefinition = {\n schema: StandardSchemaV1<unknown, JsonValue>\n defaultValue: JsonValue\n}\n\nexport type PreparedFlagDefinitions<Definitions extends FlagDefinitions> = {\n readonly definitions: Readonly<\n Record<FlagKey<Definitions>, PreparedDefinition>\n >\n}\n\n/** Define one flag's runtime schema and offline/invalid-value fallback. */\nexport function flag<Schema extends StandardSchemaV1>(\n schema: Schema,\n defaultValue: StandardSchemaOutput<Schema>,\n): FlagDefinition<Schema> {\n return { schema, defaultValue }\n}\n\nfunction isPromise<T>(value: unknown): value is Promise<T> {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'then' in value &&\n typeof value.then === 'function'\n )\n}\n\nfunction isJsonValue(\n value: unknown,\n ancestors = new Set<object>(),\n): value is JsonValue {\n if (\n value === null ||\n typeof value === 'boolean' ||\n typeof value === 'string'\n ) {\n return true\n }\n if (typeof value === 'number') return Number.isFinite(value)\n if (typeof value !== 'object') return false\n if (ancestors.has(value)) return false\n\n const nestedAncestors = new Set(ancestors)\n nestedAncestors.add(value)\n if (Array.isArray(value)) {\n return value.every(item => isJsonValue(item, nestedAncestors))\n }\n\n const prototype = Object.getPrototypeOf(value)\n if (prototype !== Object.prototype && prototype !== null) return false\n return Object.values(value).every(item => isJsonValue(item, nestedAncestors))\n}\n\nfunction jsonEquals(left: JsonValue, right: JsonValue): boolean {\n if (left === right) return true\n if (Array.isArray(left) || Array.isArray(right)) {\n return (\n Array.isArray(left) &&\n Array.isArray(right) &&\n left.length === right.length &&\n left.every((value, index) => jsonEquals(value, right[index]!))\n )\n }\n if (left === null || right === null) return false\n if (typeof left !== 'object' || typeof right !== 'object') return false\n\n const leftKeys = Object.keys(left)\n const rightKeys = Object.keys(right)\n return (\n leftKeys.length === rightKeys.length &&\n leftKeys.every(key => key in right && jsonEquals(left[key]!, right[key]!))\n )\n}\n\nfunction formatIssues(issues: ReadonlyArray<StandardSchemaIssue>) {\n return issues.map(issue => issue.message).join('; ') || 'validation failed'\n}\n\nfunction validate(\n flagKey: string,\n schema: StandardSchemaV1<unknown, JsonValue>,\n value: unknown,\n): ValidationResult {\n let result:\n StandardSchemaResult<JsonValue> | Promise<StandardSchemaResult<JsonValue>>\n try {\n result = schema['~standard'].validate(value)\n } catch (cause) {\n return {\n ok: false,\n message: cause instanceof Error ? cause.message : 'validation threw',\n }\n }\n\n if (isPromise(result)) {\n return {\n ok: false,\n message: `Flag \"${flagKey}\" uses an asynchronous schema; flag schemas must validate synchronously`,\n }\n }\n if (result.issues) {\n return { ok: false, message: formatIssues(result.issues) }\n }\n if (!isJsonValue(result.value)) {\n return {\n ok: false,\n message: `Flag \"${flagKey}\" schema produced a non-JSON value`,\n }\n }\n\n return { ok: true, value: result.value }\n}\n\nexport function prepareFlagDefinitions<Definitions extends FlagDefinitions>(\n definitions: Definitions,\n): PreparedFlagDefinitions<Definitions> {\n const prepared: Record<string, PreparedDefinition> = {}\n\n for (const [flagKey, definition] of Object.entries(definitions)) {\n const schema = definition.schema as StandardSchemaV1<unknown, JsonValue>\n if (schema?.['~standard']?.version !== 1) {\n throw new Error(`Flag \"${flagKey}\" must use a Standard Schema v1 schema`)\n }\n\n const result = validate(flagKey, schema, definition.defaultValue)\n if (!result.ok) {\n throw new Error(\n `Invalid default for flag \"${flagKey}\": ${result.message}`,\n )\n }\n\n const repeated = validate(flagKey, schema, result.value)\n if (!repeated.ok || !jsonEquals(result.value, repeated.value)) {\n throw new Error(\n `Invalid default for flag \"${flagKey}\": schema output must remain stable when validated again`,\n )\n }\n\n prepared[flagKey] = { schema, defaultValue: result.value }\n }\n\n return {\n definitions: Object.freeze(prepared) as Record<\n FlagKey<Definitions>,\n PreparedDefinition\n >,\n }\n}\n\nexport class DefinedEnvflagsClient<\n Definitions extends FlagDefinitions,\n> extends EnvflagsClient {\n readonly #prepared: PreparedFlagDefinitions<Definitions>\n #cacheRevision = -1\n #validationCache = new Map<string, Map<JsonValue, ValidationResult>>()\n\n constructor(\n sdkKey: string,\n options: EnvflagsClientOptions,\n prepared: PreparedFlagDefinitions<Definitions>,\n ) {\n super(sdkKey, options)\n this.#prepared = prepared\n }\n\n flag<Key extends FlagKey<Definitions>>(\n flagKey: Key,\n context?: EvaluationContext,\n ): FlagValue<Definitions, Key> {\n const definition = this.#prepared.definitions[flagKey]\n const candidate = this.variation(flagKey, definition.defaultValue, context)\n\n if (this.#cacheRevision !== this.getSnapshot()) {\n this.#cacheRevision = this.getSnapshot()\n this.#validationCache.clear()\n }\n\n let flagCache = this.#validationCache.get(flagKey)\n if (!flagCache) {\n flagCache = new Map()\n this.#validationCache.set(flagKey, flagCache)\n }\n\n let result = flagCache.get(candidate)\n if (!result) {\n result = validate(flagKey, definition.schema, candidate)\n flagCache.set(candidate, result)\n if (!result.ok) {\n this.reportError(\n new Error(\n `Invalid value for flag \"${flagKey}\": ${result.message}; using its default`,\n ),\n )\n }\n }\n\n return (result.ok ? result.value : definition.defaultValue) as FlagValue<\n Definitions,\n Key\n >\n }\n}\n\nexport function createDefinedClient<Definitions extends FlagDefinitions>(\n prepared: PreparedFlagDefinitions<Definitions>,\n sdkKey: string,\n options: EnvflagsClientOptions = {},\n) {\n return new DefinedEnvflagsClient(sdkKey, options, prepared)\n}\n"],"mappings":"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"}
@@ -0,0 +1,60 @@
1
+ export type JsonPrimitive = boolean | null | number | string;
2
+ export type JsonValue = JsonPrimitive | JsonValue[] | {
3
+ [key: string]: JsonValue;
4
+ };
5
+ export type FlagKind = 'boolean' | 'json' | 'number' | 'string';
6
+ export type EvaluationContext = {
7
+ key?: string;
8
+ [attribute: string]: unknown;
9
+ };
10
+ export type ClauseOperator = 'after' | 'before' | 'contains' | 'endsWith' | 'greaterThan' | 'in' | 'lessThan' | 'matches' | 'notIn' | 'segmentMatch' | 'startsWith';
11
+ export type Clause = {
12
+ attribute: string;
13
+ op: ClauseOperator;
14
+ values: JsonValue[];
15
+ };
16
+ export type RolloutEntry = {
17
+ variation: number;
18
+ weight: number;
19
+ };
20
+ export type VariationSelector = {
21
+ variation: number;
22
+ rollout?: never;
23
+ bucketBy?: never;
24
+ } | {
25
+ variation?: never;
26
+ rollout: RolloutEntry[];
27
+ bucketBy?: string;
28
+ };
29
+ export type Rule = VariationSelector & {
30
+ clauses: Clause[];
31
+ };
32
+ export type Target = {
33
+ variation: number;
34
+ values: string[];
35
+ };
36
+ export type Flag = {
37
+ kind: FlagKind;
38
+ on: boolean;
39
+ variations: JsonValue[];
40
+ offVariation: number;
41
+ fallthrough: VariationSelector;
42
+ targets: Target[];
43
+ rules: Rule[];
44
+ clientSideAvailable: boolean;
45
+ value?: JsonValue;
46
+ };
47
+ export type Segment = {
48
+ included?: string[];
49
+ excluded?: string[];
50
+ rules?: Array<{
51
+ clauses: Clause[];
52
+ }>;
53
+ };
54
+ export type EvaluationPayload = {
55
+ envId: string;
56
+ version: number;
57
+ salt: string;
58
+ flags: Record<string, Flag>;
59
+ segments: Record<string, Segment>;
60
+ };
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@envflags/js-sdk",
3
+ "version": "0.1.0",
4
+ "description": "JavaScript/TypeScript SDK for envflags",
5
+ "homepage": "https://envflags.com",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/envflags/envflags.git",
9
+ "directory": "sdks/js"
10
+ },
11
+ "type": "module",
12
+ "sideEffects": false,
13
+ "files": [
14
+ "dist"
15
+ ],
16
+ "main": "./dist/index.js",
17
+ "types": "./dist/index.d.ts",
18
+ "exports": {
19
+ ".": {
20
+ "types": "./dist/index.d.ts",
21
+ "import": "./dist/index.js"
22
+ },
23
+ "./react": {
24
+ "types": "./dist/react.d.ts",
25
+ "import": "./dist/react.js"
26
+ }
27
+ },
28
+ "publishConfig": {
29
+ "access": "public"
30
+ },
31
+ "scripts": {
32
+ "build": "vite build && tsc -p tsconfig.build.json",
33
+ "prepublishOnly": "bun run build"
34
+ },
35
+ "peerDependencies": {
36
+ "react": ">=18"
37
+ },
38
+ "peerDependenciesMeta": {
39
+ "react": {
40
+ "optional": true
41
+ }
42
+ },
43
+ "devDependencies": {
44
+ "typescript": "^6.0.0",
45
+ "vite": "^8.2.1"
46
+ }
47
+ }