@envflags/js-sdk 0.1.1 → 0.2.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 +61 -36
- package/dist/client.d.ts +4 -29
- package/dist/index.js +2 -1
- package/dist/react.d.ts +4 -4
- package/dist/react.js +1 -1
- package/dist/react.js.map +1 -1
- package/dist/runtime.d.ts +28 -0
- package/dist/server.d.ts +15 -0
- package/dist/server.js +37 -0
- package/dist/server.js.map +1 -0
- package/dist/shared.js +3 -135
- package/dist/shared.js.map +1 -1
- package/dist/shared2.js +122 -0
- package/dist/shared2.js.map +1 -0
- package/package.json +21 -5
package/README.md
CHANGED
|
@@ -3,32 +3,47 @@
|
|
|
3
3
|
The reference envflags SDK for browsers, Node.js, and Workers. It downloads an
|
|
4
4
|
immutable flag payload, keeps it in memory, and evaluates flags synchronously.
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
Use the **client** entry in browsers and front-end bundles. It fetches once at
|
|
7
|
+
`initialize()` and has no background polling. Use the **server** entry in
|
|
8
|
+
Node.js, Workers, and other long-lived processes that should poll for updates.
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
```ts
|
|
11
|
+
import { createClient } from "@envflags/js-sdk";
|
|
11
12
|
|
|
12
|
-
const
|
|
13
|
+
const flags = await createClient("client_…").initialize();
|
|
14
|
+
flags.setContext({ key: "user-123", country: "BE" });
|
|
13
15
|
|
|
14
|
-
flags.
|
|
16
|
+
const enabled = flags.boolVariation("new-checkout", false);
|
|
15
17
|
```
|
|
16
18
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
`@envflags/js-sdk/client` is the same module. Call `setContext` once and omit
|
|
20
|
+
context on later reads. Call `refresh()` when the application should check for
|
|
21
|
+
a newer payload; the client never schedules that itself. Supply a previously
|
|
22
|
+
persisted payload as `bootstrap` to remain available when the first network
|
|
23
|
+
request fails, including when your backend inlines a client payload into the page.
|
|
24
|
+
Once initialized, refresh failures keep the last-known payload active. Typed
|
|
25
|
+
variation methods always fall back to the caller's default when a flag is
|
|
26
|
+
missing or has the wrong value type. Context is optional: flags that do not
|
|
27
|
+
depend on who is asking still evaluate off/fallthrough without it.
|
|
28
|
+
|
|
29
|
+
## Server
|
|
21
30
|
|
|
22
31
|
```ts
|
|
23
|
-
|
|
32
|
+
import { createServer } from "@envflags/js-sdk/server";
|
|
33
|
+
|
|
34
|
+
const flags = await createServer("server_…").initialize();
|
|
35
|
+
const enabled = flags.boolVariation("new-checkout", false, {
|
|
36
|
+
key: "user-123",
|
|
37
|
+
country: "BE",
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
flags.close();
|
|
24
41
|
```
|
|
25
42
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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.
|
|
43
|
+
Skip `setContext` and pass context as the last argument so one process can
|
|
44
|
+
evaluate many users. The server polls every 30 seconds and fetches a new
|
|
45
|
+
payload only when its version changes. Set `pollIntervalMs: false` to refresh
|
|
46
|
+
manually. Call `close()` to stop the timer.
|
|
32
47
|
|
|
33
48
|
## React
|
|
34
49
|
|
|
@@ -38,35 +53,35 @@ knows every flag key, return type, and fallback value. Remote values are
|
|
|
38
53
|
validated before they reach application code, including structured JSON flags.
|
|
39
54
|
|
|
40
55
|
```tsx
|
|
41
|
-
import { flag } from
|
|
42
|
-
import { defineFlags } from
|
|
43
|
-
import { z } from
|
|
56
|
+
import { flag } from "@envflags/js-sdk";
|
|
57
|
+
import { defineFlags } from "@envflags/js-sdk/react";
|
|
58
|
+
import { z } from "zod";
|
|
44
59
|
|
|
45
60
|
const envflags = defineFlags({
|
|
46
|
-
|
|
47
|
-
|
|
61
|
+
"new-checkout": flag(z.boolean(), false),
|
|
62
|
+
"checkout-config": flag(
|
|
48
63
|
z.object({
|
|
49
|
-
layout: z.enum([
|
|
64
|
+
layout: z.enum(["compact", "comfortable"]),
|
|
50
65
|
maximumItems: z.number().int().positive(),
|
|
51
66
|
}),
|
|
52
|
-
{ layout:
|
|
67
|
+
{ layout: "comfortable", maximumItems: 20 },
|
|
53
68
|
),
|
|
54
|
-
})
|
|
69
|
+
});
|
|
55
70
|
|
|
56
|
-
const client = envflags.createClient(
|
|
71
|
+
const client = envflags.createClient("client_…");
|
|
57
72
|
|
|
58
73
|
function App() {
|
|
59
74
|
return (
|
|
60
|
-
<envflags.EnvflagsProvider client={client} context={{ key:
|
|
75
|
+
<envflags.EnvflagsProvider client={client} context={{ key: "user-123" }}>
|
|
61
76
|
<Checkout />
|
|
62
77
|
</envflags.EnvflagsProvider>
|
|
63
|
-
)
|
|
78
|
+
);
|
|
64
79
|
}
|
|
65
80
|
|
|
66
81
|
function Checkout() {
|
|
67
|
-
const enabled = envflags.useFlag(
|
|
68
|
-
const config = envflags.useFlag(
|
|
69
|
-
return enabled ? <NewCheckout /> : <OldCheckout
|
|
82
|
+
const enabled = envflags.useFlag("new-checkout"); // boolean
|
|
83
|
+
const config = envflags.useFlag("checkout-config"); // fully typed object
|
|
84
|
+
return enabled ? <NewCheckout /> : <OldCheckout />;
|
|
70
85
|
}
|
|
71
86
|
```
|
|
72
87
|
|
|
@@ -76,8 +91,18 @@ invalid remote variation reports through the client's `onError` callback and
|
|
|
76
91
|
resolves to its registered default. `useFlag` accepts an optional second
|
|
77
92
|
argument to override the provider context for one evaluation.
|
|
78
93
|
|
|
79
|
-
The provider initializes its client and re-renders consumers when a new
|
|
80
|
-
arrives
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
94
|
+
The provider initializes its client and re-renders consumers when a new
|
|
95
|
+
payload arrives, including after an explicit `refresh()`. Prefer bootstrapping
|
|
96
|
+
the client from your server so the browser does not need its own fetch. The
|
|
97
|
+
provider does not dispose the client when it unmounts because the client may
|
|
98
|
+
be shared.
|
|
99
|
+
|
|
100
|
+
The low-level `variation` and typed variation methods remain available for
|
|
101
|
+
dynamic flag keys and gradual migration.
|
|
102
|
+
|
|
103
|
+
## Publishing
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
npm login
|
|
107
|
+
npm publish
|
|
108
|
+
```
|
package/dist/client.d.ts
CHANGED
|
@@ -1,31 +1,6 @@
|
|
|
1
|
-
import
|
|
2
|
-
type
|
|
3
|
-
|
|
4
|
-
|
|
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;
|
|
1
|
+
import { EnvflagsRuntime, type EnvflagsRuntimeOptions } from "./runtime";
|
|
2
|
+
export type EnvflagsClientOptions = EnvflagsRuntimeOptions;
|
|
3
|
+
/** Browser and frontend SDK. Fetches a payload once; never polls. */
|
|
4
|
+
export declare class EnvflagsClient extends EnvflagsRuntime {
|
|
29
5
|
}
|
|
30
6
|
export declare function createClient(sdkKey: string, options?: EnvflagsClientOptions): EnvflagsClient;
|
|
31
|
-
export {};
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { i as sha1, n as bucket, r as evaluate } from "./shared.js";
|
|
2
|
+
import { a as createClient, i as EnvflagsClient, n as flag } from "./shared2.js";
|
|
2
3
|
export { EnvflagsClient, bucket, createClient, evaluate, flag, sha1 };
|
package/dist/react.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { type ReactNode } from
|
|
2
|
-
import type { EnvflagsClient, EnvflagsClientOptions } from
|
|
3
|
-
import { type DefinedEnvflagsClient, type FlagDefinitions, type FlagValue } from
|
|
4
|
-
import type { EvaluationContext, JsonValue } from
|
|
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
5
|
type EnvflagsReactContext = {
|
|
6
6
|
client: EnvflagsClient;
|
|
7
7
|
evaluationContext: EvaluationContext;
|
package/dist/react.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { r as prepareFlagDefinitions, t as createDefinedClient } from "./
|
|
1
|
+
import { r as prepareFlagDefinitions, t as createDefinedClient } from "./shared2.js";
|
|
2
2
|
import { createContext, createElement, useContext, useEffect, useMemo, useSyncExternalStore } from "react";
|
|
3
3
|
//#region src/react.ts
|
|
4
4
|
var Context = createContext(void 0);
|
package/dist/react.js.map
CHANGED
|
@@ -1 +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
|
|
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,28 @@
|
|
|
1
|
+
import type { EvaluationContext, EvaluationPayload, JsonValue } from "./types";
|
|
2
|
+
type Fetch = typeof globalThis.fetch;
|
|
3
|
+
export type EnvflagsRuntimeOptions = {
|
|
4
|
+
baseUrl?: string;
|
|
5
|
+
bootstrap?: EvaluationPayload;
|
|
6
|
+
fetch?: Fetch;
|
|
7
|
+
onError?: (error: Error) => void;
|
|
8
|
+
};
|
|
9
|
+
export declare class EnvflagsRuntime {
|
|
10
|
+
#private;
|
|
11
|
+
constructor(sdkKey: string, options?: EnvflagsRuntimeOptions);
|
|
12
|
+
get initialized(): boolean;
|
|
13
|
+
get version(): number | undefined;
|
|
14
|
+
/** Subscribe to payload changes. Intended for UI framework bindings. */
|
|
15
|
+
subscribe: (listener: () => void) => () => void;
|
|
16
|
+
/** Return a stable value suitable for React's useSyncExternalStore. */
|
|
17
|
+
getSnapshot: () => number;
|
|
18
|
+
protected reportError(error: Error): void;
|
|
19
|
+
initialize(): Promise<this>;
|
|
20
|
+
refresh(): Promise<boolean>;
|
|
21
|
+
setContext(context: EvaluationContext): this;
|
|
22
|
+
variation<T extends JsonValue>(flagKey: string, defaultValue: T, context?: EvaluationContext): T;
|
|
23
|
+
boolVariation(flagKey: string, defaultValue: boolean, context?: EvaluationContext): boolean;
|
|
24
|
+
stringVariation(flagKey: string, defaultValue: string, context?: EvaluationContext): string;
|
|
25
|
+
numberVariation(flagKey: string, defaultValue: number, context?: EvaluationContext): number;
|
|
26
|
+
jsonVariation<T extends JsonValue>(flagKey: string, defaultValue: T, context?: EvaluationContext): T;
|
|
27
|
+
}
|
|
28
|
+
export {};
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { EnvflagsRuntime, type EnvflagsRuntimeOptions } from "./runtime";
|
|
2
|
+
export type EnvflagsServerOptions = EnvflagsRuntimeOptions & {
|
|
3
|
+
pollIntervalMs?: number | false;
|
|
4
|
+
};
|
|
5
|
+
/** Node, Workers, and other long-lived processes. Polls for payload updates. */
|
|
6
|
+
export declare class EnvflagsServer extends EnvflagsRuntime {
|
|
7
|
+
#private;
|
|
8
|
+
constructor(sdkKey: string, options?: EnvflagsServerOptions);
|
|
9
|
+
initialize(): Promise<this>;
|
|
10
|
+
close(): void;
|
|
11
|
+
}
|
|
12
|
+
export declare function createServer(sdkKey: string, options?: EnvflagsServerOptions): EnvflagsServer;
|
|
13
|
+
export { bucket, evaluate } from "./evaluate";
|
|
14
|
+
export { sha1 } from "./sha1";
|
|
15
|
+
export type { Clause, ClauseOperator, EvaluationContext, EvaluationPayload, Flag, FlagKind, JsonPrimitive, JsonValue, RolloutEntry, Rule, Segment, Target, VariationSelector, } from "./types";
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { i as sha1, n as bucket, r as evaluate, t as EnvflagsRuntime } from "./shared.js";
|
|
2
|
+
//#region src/server.ts
|
|
3
|
+
var DEFAULT_POLL_INTERVAL_MS = 3e4;
|
|
4
|
+
/** Node, Workers, and other long-lived processes. Polls for payload updates. */
|
|
5
|
+
var EnvflagsServer = class extends EnvflagsRuntime {
|
|
6
|
+
#pollIntervalMs;
|
|
7
|
+
#timer;
|
|
8
|
+
constructor(sdkKey, options = {}) {
|
|
9
|
+
super(sdkKey, options);
|
|
10
|
+
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");
|
|
11
|
+
this.#pollIntervalMs = options.pollIntervalMs === void 0 ? DEFAULT_POLL_INTERVAL_MS : options.pollIntervalMs;
|
|
12
|
+
}
|
|
13
|
+
async initialize() {
|
|
14
|
+
await super.initialize();
|
|
15
|
+
this.#startPolling();
|
|
16
|
+
return this;
|
|
17
|
+
}
|
|
18
|
+
close() {
|
|
19
|
+
if (this.#timer !== void 0) clearInterval(this.#timer);
|
|
20
|
+
this.#timer = void 0;
|
|
21
|
+
}
|
|
22
|
+
#startPolling() {
|
|
23
|
+
if (this.#timer !== void 0 || this.#pollIntervalMs === false || this.#pollIntervalMs <= 0) return;
|
|
24
|
+
this.#timer = setInterval(() => {
|
|
25
|
+
this.refresh().catch((cause) => {
|
|
26
|
+
this.reportError(cause instanceof Error ? cause : /* @__PURE__ */ new Error("Unable to refresh envflags"));
|
|
27
|
+
});
|
|
28
|
+
}, this.#pollIntervalMs);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
function createServer(sdkKey, options = {}) {
|
|
32
|
+
return new EnvflagsServer(sdkKey, options);
|
|
33
|
+
}
|
|
34
|
+
//#endregion
|
|
35
|
+
export { EnvflagsServer, bucket, createServer, evaluate, sha1 };
|
|
36
|
+
|
|
37
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","names":[],"sources":["../src/server.ts"],"sourcesContent":["import {\n EnvflagsRuntime,\n type EnvflagsRuntimeOptions,\n} from \"./runtime\";\n\nconst DEFAULT_POLL_INTERVAL_MS = 30_000;\n\nexport type EnvflagsServerOptions = EnvflagsRuntimeOptions & {\n pollIntervalMs?: number | false;\n};\n\n/** Node, Workers, and other long-lived processes. Polls for payload updates. */\nexport class EnvflagsServer extends EnvflagsRuntime {\n readonly #pollIntervalMs: number | false;\n #timer?: ReturnType<typeof setInterval>;\n\n constructor(sdkKey: string, options: EnvflagsServerOptions = {}) {\n super(sdkKey, options);\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.#pollIntervalMs =\n options.pollIntervalMs === undefined\n ? DEFAULT_POLL_INTERVAL_MS\n : options.pollIntervalMs;\n }\n\n async initialize() {\n await super.initialize();\n this.#startPolling();\n return this;\n }\n\n close() {\n if (this.#timer !== undefined) clearInterval(this.#timer);\n this.#timer = undefined;\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 createServer(\n sdkKey: string,\n options: EnvflagsServerOptions = {},\n) {\n return new EnvflagsServer(sdkKey, options);\n}\n\nexport { bucket, evaluate } from \"./evaluate\";\nexport { sha1 } from \"./sha1\";\nexport type {\n Clause,\n ClauseOperator,\n EvaluationContext,\n EvaluationPayload,\n Flag,\n FlagKind,\n JsonPrimitive,\n JsonValue,\n RolloutEntry,\n Rule,\n Segment,\n Target,\n VariationSelector,\n} from \"./types\";\n"],"mappings":";;AAKA,IAAM,2BAA2B;;AAOjC,IAAa,iBAAb,cAAoC,gBAAgB;CAClD;CACA;CAEA,YAAY,QAAgB,UAAiC,CAAC,GAAG;EAC/D,MAAM,QAAQ,OAAO;EACrB,IACE,QAAQ,mBAAmB,KAAA,KAC3B,QAAQ,mBAAmB,UAC1B,CAAC,OAAO,SAAS,QAAQ,cAAc,KAAK,QAAQ,iBAAiB,IAEtE,MAAM,IAAI,MAAM,qDAAqD;EAGvE,KAAK,kBACH,QAAQ,mBAAmB,KAAA,IACvB,2BACA,QAAQ;CAChB;CAEA,MAAM,aAAa;EACjB,MAAM,MAAM,WAAW;EACvB,KAAK,cAAc;EACnB,OAAO;CACT;CAEA,QAAQ;EACN,IAAI,KAAK,WAAW,KAAA,GAAW,cAAc,KAAK,MAAM;EACxD,KAAK,SAAS,KAAA;CAChB;CAEA,gBAAgB;EACd,IACE,KAAK,WAAW,KAAA,KAChB,KAAK,oBAAoB,SACzB,KAAK,mBAAmB,GAExB;EAGF,KAAK,SAAS,kBAAkB;GAC9B,KAAU,QAAQ,CAAC,CAAC,OAAO,UAAU;IACnC,KAAK,YACH,iBAAiB,QACb,wBACA,IAAI,MAAM,4BAA4B,CAC5C;GACF,CAAC;EACH,GAAG,KAAK,eAAe;CACzB;AACF;AAEA,SAAgB,aACd,QACA,UAAiC,CAAC,GAClC;CACA,OAAO,IAAI,eAAe,QAAQ,OAAO;AAC3C"}
|
package/dist/shared.js
CHANGED
|
@@ -184,9 +184,8 @@ function evaluate(payload, flagKey, context, defaultValue) {
|
|
|
184
184
|
return variation === void 0 || !(variation in flag.variations) ? defaultValue : flag.variations[variation];
|
|
185
185
|
}
|
|
186
186
|
//#endregion
|
|
187
|
-
//#region src/
|
|
187
|
+
//#region src/runtime.ts
|
|
188
188
|
var DEFAULT_BASE_URL = "https://envflags.com";
|
|
189
|
-
var DEFAULT_POLL_INTERVAL_MS = 3e4;
|
|
190
189
|
function isRecord(value) {
|
|
191
190
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
192
191
|
}
|
|
@@ -201,11 +200,10 @@ function parsePayload(value) {
|
|
|
201
200
|
function responseError(action, response) {
|
|
202
201
|
return /* @__PURE__ */ new Error(`Unable to ${action}: envflags returned ${response.status} ${response.statusText}`);
|
|
203
202
|
}
|
|
204
|
-
var
|
|
203
|
+
var EnvflagsRuntime = class {
|
|
205
204
|
#baseUrl;
|
|
206
205
|
#fetch;
|
|
207
206
|
#onError;
|
|
208
|
-
#pollIntervalMs;
|
|
209
207
|
#sdkKey;
|
|
210
208
|
#context;
|
|
211
209
|
#etag;
|
|
@@ -213,16 +211,13 @@ var EnvflagsClient = class {
|
|
|
213
211
|
#revision = 0;
|
|
214
212
|
#refreshPromise;
|
|
215
213
|
#subscribers = /* @__PURE__ */ new Set();
|
|
216
|
-
#timer;
|
|
217
214
|
constructor(sdkKey, options = {}) {
|
|
218
215
|
const normalizedSdkKey = sdkKey.trim();
|
|
219
216
|
if (!normalizedSdkKey) throw new Error("An envflags SDK key is required");
|
|
220
|
-
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");
|
|
221
217
|
this.#sdkKey = normalizedSdkKey;
|
|
222
218
|
this.#baseUrl = new URL(options.baseUrl ?? DEFAULT_BASE_URL);
|
|
223
219
|
this.#fetch = options.fetch ?? globalThis.fetch.bind(globalThis);
|
|
224
220
|
this.#onError = options.onError ?? (() => void 0);
|
|
225
|
-
this.#pollIntervalMs = options.pollIntervalMs === void 0 ? DEFAULT_POLL_INTERVAL_MS : options.pollIntervalMs;
|
|
226
221
|
if (options.bootstrap) {
|
|
227
222
|
this.#payload = parsePayload(options.bootstrap);
|
|
228
223
|
this.#revision = 1;
|
|
@@ -254,7 +249,6 @@ var EnvflagsClient = class {
|
|
|
254
249
|
this.reportError(error);
|
|
255
250
|
if (!this.#payload) throw error;
|
|
256
251
|
}
|
|
257
|
-
this.#startPolling();
|
|
258
252
|
return this;
|
|
259
253
|
}
|
|
260
254
|
refresh() {
|
|
@@ -263,10 +257,6 @@ var EnvflagsClient = class {
|
|
|
263
257
|
});
|
|
264
258
|
return this.#refreshPromise;
|
|
265
259
|
}
|
|
266
|
-
close() {
|
|
267
|
-
if (this.#timer !== void 0) clearInterval(this.#timer);
|
|
268
|
-
this.#timer = void 0;
|
|
269
|
-
}
|
|
270
260
|
setContext(context) {
|
|
271
261
|
this.#context = context;
|
|
272
262
|
return this;
|
|
@@ -319,130 +309,8 @@ var EnvflagsClient = class {
|
|
|
319
309
|
for (const subscriber of this.#subscribers) subscriber();
|
|
320
310
|
return true;
|
|
321
311
|
}
|
|
322
|
-
#startPolling() {
|
|
323
|
-
if (this.#timer !== void 0 || this.#pollIntervalMs === false || this.#pollIntervalMs <= 0) return;
|
|
324
|
-
this.#timer = setInterval(() => {
|
|
325
|
-
this.refresh().catch((cause) => {
|
|
326
|
-
this.reportError(cause instanceof Error ? cause : /* @__PURE__ */ new Error("Unable to refresh envflags"));
|
|
327
|
-
});
|
|
328
|
-
}, this.#pollIntervalMs);
|
|
329
|
-
}
|
|
330
312
|
};
|
|
331
|
-
function createClient(sdkKey, options = {}) {
|
|
332
|
-
return new EnvflagsClient(sdkKey, options);
|
|
333
|
-
}
|
|
334
|
-
//#endregion
|
|
335
|
-
//#region src/schema.ts
|
|
336
|
-
/** Define one flag's runtime schema and offline/invalid-value fallback. */
|
|
337
|
-
function flag(schema, defaultValue) {
|
|
338
|
-
return {
|
|
339
|
-
schema,
|
|
340
|
-
defaultValue
|
|
341
|
-
};
|
|
342
|
-
}
|
|
343
|
-
function isPromise(value) {
|
|
344
|
-
return typeof value === "object" && value !== null && "then" in value && typeof value.then === "function";
|
|
345
|
-
}
|
|
346
|
-
function isJsonValue(value, ancestors = /* @__PURE__ */ new Set()) {
|
|
347
|
-
if (value === null || typeof value === "boolean" || typeof value === "string") return true;
|
|
348
|
-
if (typeof value === "number") return Number.isFinite(value);
|
|
349
|
-
if (typeof value !== "object") return false;
|
|
350
|
-
if (ancestors.has(value)) return false;
|
|
351
|
-
const nestedAncestors = new Set(ancestors);
|
|
352
|
-
nestedAncestors.add(value);
|
|
353
|
-
if (Array.isArray(value)) return value.every((item) => isJsonValue(item, nestedAncestors));
|
|
354
|
-
const prototype = Object.getPrototypeOf(value);
|
|
355
|
-
if (prototype !== Object.prototype && prototype !== null) return false;
|
|
356
|
-
return Object.values(value).every((item) => isJsonValue(item, nestedAncestors));
|
|
357
|
-
}
|
|
358
|
-
function jsonEquals(left, right) {
|
|
359
|
-
if (left === right) return true;
|
|
360
|
-
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]));
|
|
361
|
-
if (left === null || right === null) return false;
|
|
362
|
-
if (typeof left !== "object" || typeof right !== "object") return false;
|
|
363
|
-
const leftKeys = Object.keys(left);
|
|
364
|
-
const rightKeys = Object.keys(right);
|
|
365
|
-
return leftKeys.length === rightKeys.length && leftKeys.every((key) => key in right && jsonEquals(left[key], right[key]));
|
|
366
|
-
}
|
|
367
|
-
function formatIssues(issues) {
|
|
368
|
-
return issues.map((issue) => issue.message).join("; ") || "validation failed";
|
|
369
|
-
}
|
|
370
|
-
function validate(flagKey, schema, value) {
|
|
371
|
-
let result;
|
|
372
|
-
try {
|
|
373
|
-
result = schema["~standard"].validate(value);
|
|
374
|
-
} catch (cause) {
|
|
375
|
-
return {
|
|
376
|
-
ok: false,
|
|
377
|
-
message: cause instanceof Error ? cause.message : "validation threw"
|
|
378
|
-
};
|
|
379
|
-
}
|
|
380
|
-
if (isPromise(result)) return {
|
|
381
|
-
ok: false,
|
|
382
|
-
message: `Flag "${flagKey}" uses an asynchronous schema; flag schemas must validate synchronously`
|
|
383
|
-
};
|
|
384
|
-
if (result.issues) return {
|
|
385
|
-
ok: false,
|
|
386
|
-
message: formatIssues(result.issues)
|
|
387
|
-
};
|
|
388
|
-
if (!isJsonValue(result.value)) return {
|
|
389
|
-
ok: false,
|
|
390
|
-
message: `Flag "${flagKey}" schema produced a non-JSON value`
|
|
391
|
-
};
|
|
392
|
-
return {
|
|
393
|
-
ok: true,
|
|
394
|
-
value: result.value
|
|
395
|
-
};
|
|
396
|
-
}
|
|
397
|
-
function prepareFlagDefinitions(definitions) {
|
|
398
|
-
const prepared = {};
|
|
399
|
-
for (const [flagKey, definition] of Object.entries(definitions)) {
|
|
400
|
-
const schema = definition.schema;
|
|
401
|
-
if (schema?.["~standard"]?.version !== 1) throw new Error(`Flag "${flagKey}" must use a Standard Schema v1 schema`);
|
|
402
|
-
const result = validate(flagKey, schema, definition.defaultValue);
|
|
403
|
-
if (!result.ok) throw new Error(`Invalid default for flag "${flagKey}": ${result.message}`);
|
|
404
|
-
const repeated = validate(flagKey, schema, result.value);
|
|
405
|
-
if (!repeated.ok || !jsonEquals(result.value, repeated.value)) throw new Error(`Invalid default for flag "${flagKey}": schema output must remain stable when validated again`);
|
|
406
|
-
prepared[flagKey] = {
|
|
407
|
-
schema,
|
|
408
|
-
defaultValue: result.value
|
|
409
|
-
};
|
|
410
|
-
}
|
|
411
|
-
return { definitions: Object.freeze(prepared) };
|
|
412
|
-
}
|
|
413
|
-
var DefinedEnvflagsClient = class extends EnvflagsClient {
|
|
414
|
-
#prepared;
|
|
415
|
-
#cacheRevision = -1;
|
|
416
|
-
#validationCache = /* @__PURE__ */ new Map();
|
|
417
|
-
constructor(sdkKey, options, prepared) {
|
|
418
|
-
super(sdkKey, options);
|
|
419
|
-
this.#prepared = prepared;
|
|
420
|
-
}
|
|
421
|
-
flag(flagKey, context) {
|
|
422
|
-
const definition = this.#prepared.definitions[flagKey];
|
|
423
|
-
const candidate = this.variation(flagKey, definition.defaultValue, context);
|
|
424
|
-
if (this.#cacheRevision !== this.getSnapshot()) {
|
|
425
|
-
this.#cacheRevision = this.getSnapshot();
|
|
426
|
-
this.#validationCache.clear();
|
|
427
|
-
}
|
|
428
|
-
let flagCache = this.#validationCache.get(flagKey);
|
|
429
|
-
if (!flagCache) {
|
|
430
|
-
flagCache = /* @__PURE__ */ new Map();
|
|
431
|
-
this.#validationCache.set(flagKey, flagCache);
|
|
432
|
-
}
|
|
433
|
-
let result = flagCache.get(candidate);
|
|
434
|
-
if (!result) {
|
|
435
|
-
result = validate(flagKey, definition.schema, candidate);
|
|
436
|
-
flagCache.set(candidate, result);
|
|
437
|
-
if (!result.ok) this.reportError(/* @__PURE__ */ new Error(`Invalid value for flag "${flagKey}": ${result.message}; using its default`));
|
|
438
|
-
}
|
|
439
|
-
return result.ok ? result.value : definition.defaultValue;
|
|
440
|
-
}
|
|
441
|
-
};
|
|
442
|
-
function createDefinedClient(prepared, sdkKey, options = {}) {
|
|
443
|
-
return new DefinedEnvflagsClient(sdkKey, options, prepared);
|
|
444
|
-
}
|
|
445
313
|
//#endregion
|
|
446
|
-
export {
|
|
314
|
+
export { sha1 as i, bucket as n, evaluate as r, EnvflagsRuntime as t };
|
|
447
315
|
|
|
448
316
|
//# sourceMappingURL=shared.js.map
|
package/dist/shared.js.map
CHANGED
|
@@ -1 +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 matchesRegex(attribute: unknown, values: JsonValue[], flags = '') {\n return matchesStrings(attribute, values, (left, right) => {\n try {\n return new RegExp(right, flags).test(left)\n } catch {\n return false\n }\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 matchesRegex(attribute, clause.values)\n case 'matchesIgnoreCase':\n return matchesRegex(attribute, clause.values, 'i')\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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"}
|
|
1
|
+
{"version":3,"file":"shared.js","names":[],"sources":["../src/sha1.ts","../src/evaluate.ts","../src/runtime.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 matchesRegex(attribute: unknown, values: JsonValue[], flags = '') {\n return matchesStrings(attribute, values, (left, right) => {\n try {\n return new RegExp(right, flags).test(left)\n } catch {\n return false\n }\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 matchesRegex(attribute, clause.values)\n case 'matchesIgnoreCase':\n return matchesRegex(attribute, clause.values, 'i')\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\";\n\ntype Fetch = typeof globalThis.fetch;\n\nexport type EnvflagsRuntimeOptions = {\n baseUrl?: string;\n bootstrap?: EvaluationPayload;\n fetch?: Fetch;\n onError?: (error: Error) => void;\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 EnvflagsRuntime {\n readonly #baseUrl: URL;\n readonly #fetch: Fetch;\n readonly #onError: (error: Error) => void;\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\n constructor(sdkKey: string, options: EnvflagsRuntimeOptions = {}) {\n const normalizedSdkKey = sdkKey.trim();\n if (!normalizedSdkKey) throw new Error(\"An envflags SDK key is required\");\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 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 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 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(\n \"The envflags payload does not match its version pointer\",\n );\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"],"mappings":"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"}
|
package/dist/shared2.js
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { t as EnvflagsRuntime } from "./shared.js";
|
|
2
|
+
//#region src/client.ts
|
|
3
|
+
/** Browser and frontend SDK. Fetches a payload once; never polls. */
|
|
4
|
+
var EnvflagsClient = class extends EnvflagsRuntime {};
|
|
5
|
+
function createClient(sdkKey, options = {}) {
|
|
6
|
+
return new EnvflagsClient(sdkKey, options);
|
|
7
|
+
}
|
|
8
|
+
//#endregion
|
|
9
|
+
//#region src/schema.ts
|
|
10
|
+
/** Define one flag's runtime schema and offline/invalid-value fallback. */
|
|
11
|
+
function flag(schema, defaultValue) {
|
|
12
|
+
return {
|
|
13
|
+
schema,
|
|
14
|
+
defaultValue
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function isPromise(value) {
|
|
18
|
+
return typeof value === "object" && value !== null && "then" in value && typeof value.then === "function";
|
|
19
|
+
}
|
|
20
|
+
function isJsonValue(value, ancestors = /* @__PURE__ */ new Set()) {
|
|
21
|
+
if (value === null || typeof value === "boolean" || typeof value === "string") return true;
|
|
22
|
+
if (typeof value === "number") return Number.isFinite(value);
|
|
23
|
+
if (typeof value !== "object") return false;
|
|
24
|
+
if (ancestors.has(value)) return false;
|
|
25
|
+
const nestedAncestors = new Set(ancestors);
|
|
26
|
+
nestedAncestors.add(value);
|
|
27
|
+
if (Array.isArray(value)) return value.every((item) => isJsonValue(item, nestedAncestors));
|
|
28
|
+
const prototype = Object.getPrototypeOf(value);
|
|
29
|
+
if (prototype !== Object.prototype && prototype !== null) return false;
|
|
30
|
+
return Object.values(value).every((item) => isJsonValue(item, nestedAncestors));
|
|
31
|
+
}
|
|
32
|
+
function jsonEquals(left, right) {
|
|
33
|
+
if (left === right) return true;
|
|
34
|
+
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]));
|
|
35
|
+
if (left === null || right === null) return false;
|
|
36
|
+
if (typeof left !== "object" || typeof right !== "object") return false;
|
|
37
|
+
const leftKeys = Object.keys(left);
|
|
38
|
+
const rightKeys = Object.keys(right);
|
|
39
|
+
return leftKeys.length === rightKeys.length && leftKeys.every((key) => key in right && jsonEquals(left[key], right[key]));
|
|
40
|
+
}
|
|
41
|
+
function formatIssues(issues) {
|
|
42
|
+
return issues.map((issue) => issue.message).join("; ") || "validation failed";
|
|
43
|
+
}
|
|
44
|
+
function validate(flagKey, schema, value) {
|
|
45
|
+
let result;
|
|
46
|
+
try {
|
|
47
|
+
result = schema["~standard"].validate(value);
|
|
48
|
+
} catch (cause) {
|
|
49
|
+
return {
|
|
50
|
+
ok: false,
|
|
51
|
+
message: cause instanceof Error ? cause.message : "validation threw"
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
if (isPromise(result)) return {
|
|
55
|
+
ok: false,
|
|
56
|
+
message: `Flag "${flagKey}" uses an asynchronous schema; flag schemas must validate synchronously`
|
|
57
|
+
};
|
|
58
|
+
if (result.issues) return {
|
|
59
|
+
ok: false,
|
|
60
|
+
message: formatIssues(result.issues)
|
|
61
|
+
};
|
|
62
|
+
if (!isJsonValue(result.value)) return {
|
|
63
|
+
ok: false,
|
|
64
|
+
message: `Flag "${flagKey}" schema produced a non-JSON value`
|
|
65
|
+
};
|
|
66
|
+
return {
|
|
67
|
+
ok: true,
|
|
68
|
+
value: result.value
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
function prepareFlagDefinitions(definitions) {
|
|
72
|
+
const prepared = {};
|
|
73
|
+
for (const [flagKey, definition] of Object.entries(definitions)) {
|
|
74
|
+
const schema = definition.schema;
|
|
75
|
+
if (schema?.["~standard"]?.version !== 1) throw new Error(`Flag "${flagKey}" must use a Standard Schema v1 schema`);
|
|
76
|
+
const result = validate(flagKey, schema, definition.defaultValue);
|
|
77
|
+
if (!result.ok) throw new Error(`Invalid default for flag "${flagKey}": ${result.message}`);
|
|
78
|
+
const repeated = validate(flagKey, schema, result.value);
|
|
79
|
+
if (!repeated.ok || !jsonEquals(result.value, repeated.value)) throw new Error(`Invalid default for flag "${flagKey}": schema output must remain stable when validated again`);
|
|
80
|
+
prepared[flagKey] = {
|
|
81
|
+
schema,
|
|
82
|
+
defaultValue: result.value
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
return { definitions: Object.freeze(prepared) };
|
|
86
|
+
}
|
|
87
|
+
var DefinedEnvflagsClient = class extends EnvflagsClient {
|
|
88
|
+
#prepared;
|
|
89
|
+
#cacheRevision = -1;
|
|
90
|
+
#validationCache = /* @__PURE__ */ new Map();
|
|
91
|
+
constructor(sdkKey, options, prepared) {
|
|
92
|
+
super(sdkKey, options);
|
|
93
|
+
this.#prepared = prepared;
|
|
94
|
+
}
|
|
95
|
+
flag(flagKey, context) {
|
|
96
|
+
const definition = this.#prepared.definitions[flagKey];
|
|
97
|
+
const candidate = this.variation(flagKey, definition.defaultValue, context);
|
|
98
|
+
if (this.#cacheRevision !== this.getSnapshot()) {
|
|
99
|
+
this.#cacheRevision = this.getSnapshot();
|
|
100
|
+
this.#validationCache.clear();
|
|
101
|
+
}
|
|
102
|
+
let flagCache = this.#validationCache.get(flagKey);
|
|
103
|
+
if (!flagCache) {
|
|
104
|
+
flagCache = /* @__PURE__ */ new Map();
|
|
105
|
+
this.#validationCache.set(flagKey, flagCache);
|
|
106
|
+
}
|
|
107
|
+
let result = flagCache.get(candidate);
|
|
108
|
+
if (!result) {
|
|
109
|
+
result = validate(flagKey, definition.schema, candidate);
|
|
110
|
+
flagCache.set(candidate, result);
|
|
111
|
+
if (!result.ok) this.reportError(/* @__PURE__ */ new Error(`Invalid value for flag "${flagKey}": ${result.message}; using its default`));
|
|
112
|
+
}
|
|
113
|
+
return result.ok ? result.value : definition.defaultValue;
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
function createDefinedClient(prepared, sdkKey, options = {}) {
|
|
117
|
+
return new DefinedEnvflagsClient(sdkKey, options, prepared);
|
|
118
|
+
}
|
|
119
|
+
//#endregion
|
|
120
|
+
export { createClient as a, EnvflagsClient as i, flag as n, prepareFlagDefinitions as r, createDefinedClient as t };
|
|
121
|
+
|
|
122
|
+
//# sourceMappingURL=shared2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shared2.js","names":[],"sources":["../src/client.ts","../src/schema.ts"],"sourcesContent":["import {\n EnvflagsRuntime,\n type EnvflagsRuntimeOptions,\n} from \"./runtime\";\n\nexport type EnvflagsClientOptions = EnvflagsRuntimeOptions;\n\n/** Browser and frontend SDK. Fetches a payload once; never polls. */\nexport class EnvflagsClient extends EnvflagsRuntime {}\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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"}
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@envflags/js-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "JavaScript/TypeScript SDK for envflags",
|
|
5
5
|
"homepage": "https://envflags.com",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
|
-
"url": "git+https://github.com/envflags/
|
|
9
|
-
"directory": "
|
|
8
|
+
"url": "git+https://github.com/envflags/sdks.git",
|
|
9
|
+
"directory": "js"
|
|
10
10
|
},
|
|
11
11
|
"type": "module",
|
|
12
12
|
"sideEffects": false,
|
|
@@ -20,6 +20,14 @@
|
|
|
20
20
|
"types": "./dist/index.d.ts",
|
|
21
21
|
"import": "./dist/index.js"
|
|
22
22
|
},
|
|
23
|
+
"./client": {
|
|
24
|
+
"types": "./dist/index.d.ts",
|
|
25
|
+
"import": "./dist/index.js"
|
|
26
|
+
},
|
|
27
|
+
"./server": {
|
|
28
|
+
"types": "./dist/server.d.ts",
|
|
29
|
+
"import": "./dist/server.js"
|
|
30
|
+
},
|
|
23
31
|
"./react": {
|
|
24
32
|
"types": "./dist/react.d.ts",
|
|
25
33
|
"import": "./dist/react.js"
|
|
@@ -30,7 +38,9 @@
|
|
|
30
38
|
},
|
|
31
39
|
"scripts": {
|
|
32
40
|
"build": "vite build && tsc -p tsconfig.build.json",
|
|
33
|
-
"prepublishOnly": "bun run build"
|
|
41
|
+
"prepublishOnly": "bun run build",
|
|
42
|
+
"test": "vitest run",
|
|
43
|
+
"test:watch": "vitest"
|
|
34
44
|
},
|
|
35
45
|
"peerDependencies": {
|
|
36
46
|
"react": ">=18"
|
|
@@ -41,7 +51,13 @@
|
|
|
41
51
|
}
|
|
42
52
|
},
|
|
43
53
|
"devDependencies": {
|
|
54
|
+
"@types/react": "^19.2.18",
|
|
55
|
+
"@types/react-dom": "^19.2.4",
|
|
56
|
+
"react": "^19.2.8",
|
|
57
|
+
"react-dom": "^19.2.8",
|
|
44
58
|
"typescript": "^6.0.0",
|
|
45
|
-
"vite": "^8.2.1"
|
|
59
|
+
"vite": "^8.2.1",
|
|
60
|
+
"vitest": "^4.1.10",
|
|
61
|
+
"zod": "^4.4.3"
|
|
46
62
|
}
|
|
47
63
|
}
|