@infaris/flags 0.0.0-stage → 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.
@@ -0,0 +1,17 @@
1
+ import * as react from 'react';
2
+ import { ReactNode } from 'react';
3
+ import { BrowserFlags } from './client.cjs';
4
+ import { C as ConfigValue, E as EvaluatedFlags } from './types-DMIusvFt.cjs';
5
+
6
+ declare function FlagsProvider({ client, children }: {
7
+ client: BrowserFlags;
8
+ children?: ReactNode;
9
+ }): react.FunctionComponentElement<react.ProviderProps<BrowserFlags | null>>;
10
+ /** One flag; `defaultValue` until Hosting answered, or for a flag the publishable key may not see. */
11
+ declare function useFlag(key: string, defaultValue?: boolean): boolean;
12
+ /** Every client-visible flag and value. */
13
+ declare function useFlags(): EvaluatedFlags;
14
+ /** One client-visible Edge Config value. */
15
+ declare function useConfig<T extends ConfigValue = ConfigValue>(key: string, defaultValue?: T): T | undefined;
16
+
17
+ export { FlagsProvider, useConfig, useFlag, useFlags };
@@ -0,0 +1,17 @@
1
+ import * as react from 'react';
2
+ import { ReactNode } from 'react';
3
+ import { BrowserFlags } from './client.js';
4
+ import { C as ConfigValue, E as EvaluatedFlags } from './types-DMIusvFt.js';
5
+
6
+ declare function FlagsProvider({ client, children }: {
7
+ client: BrowserFlags;
8
+ children?: ReactNode;
9
+ }): react.FunctionComponentElement<react.ProviderProps<BrowserFlags | null>>;
10
+ /** One flag; `defaultValue` until Hosting answered, or for a flag the publishable key may not see. */
11
+ declare function useFlag(key: string, defaultValue?: boolean): boolean;
12
+ /** Every client-visible flag and value. */
13
+ declare function useFlags(): EvaluatedFlags;
14
+ /** One client-visible Edge Config value. */
15
+ declare function useConfig<T extends ConfigValue = ConfigValue>(key: string, defaultValue?: T): T | undefined;
16
+
17
+ export { FlagsProvider, useConfig, useFlag, useFlags };
package/dist/react.js ADDED
@@ -0,0 +1,34 @@
1
+ import "./chunk-TRTQSARU.js";
2
+
3
+ // src/react.ts
4
+ import { createContext, createElement, useContext, useSyncExternalStore } from "react";
5
+ var FlagsContext = createContext(null);
6
+ function FlagsProvider({ client, children }) {
7
+ return createElement(FlagsContext.Provider, { value: client }, children);
8
+ }
9
+ function useFlagsState() {
10
+ const client = useContext(FlagsContext);
11
+ if (!client) throw new Error("useFlag needs a <FlagsProvider client={\u2026}> above it");
12
+ return useSyncExternalStore(
13
+ (onChange) => client.subscribe(onChange),
14
+ () => client.snapshot(),
15
+ () => client.snapshot()
16
+ );
17
+ }
18
+ function useFlag(key, defaultValue = false) {
19
+ const state = useFlagsState();
20
+ return Object.prototype.hasOwnProperty.call(state.flags, key) ? state.flags[key] : defaultValue;
21
+ }
22
+ function useFlags() {
23
+ return useFlagsState();
24
+ }
25
+ function useConfig(key, defaultValue) {
26
+ const state = useFlagsState();
27
+ return Object.prototype.hasOwnProperty.call(state.items, key) ? state.items[key] : defaultValue;
28
+ }
29
+ export {
30
+ FlagsProvider,
31
+ useConfig,
32
+ useFlag,
33
+ useFlags
34
+ };
@@ -0,0 +1,86 @@
1
+ /** Common base of the SDK-specific errors. Existing error names and constructors remain supported. */
2
+ declare class InfarisError extends Error {
3
+ static [Symbol.hasInstance](value: unknown): boolean;
4
+ readonly code: string;
5
+ readonly status: number | null | undefined;
6
+ constructor(message: string, code?: string, status?: number | null | undefined);
7
+ }
8
+
9
+ /**
10
+ * Evaluatie van een feature flag, zonder afhankelijkheden: dezelfde code draait in de hosting-api (beheer-API en de
11
+ * publieke sleutel) en in `@infaris/flags` (`packages/infaris-flags/src/evaluate.ts` is een kopie; de test daar
12
+ * vergelijkt beide bestanden, zodat een gebruiker op de server en in de browser in dezelfde groep valt).
13
+ *
14
+ * Volgorde: kill-switch → omgeving uit → regels (de eerste die past wint) → uitrol. De uitrol is vast per gebruiker:
15
+ * `fnv1a32("<flag>:<userId>") % 10000` tegen `percentage × 100`. Wie bij 10% meedoet, doet bij 25% nog steeds mee.
16
+ */
17
+ type EvalEnvironment = 'production' | 'preview' | 'development';
18
+ type EvalAttribute = 'userId' | 'email' | 'emailDomain' | 'org' | 'role';
19
+ type EvalReason = 'killed' | 'off' | 'rule' | 'rollout' | 'fallthrough' | 'no_user';
20
+ interface EvalRule {
21
+ attribute: EvalAttribute;
22
+ operator: 'in' | 'not_in' | 'ends_with';
23
+ values: readonly string[];
24
+ serve: boolean;
25
+ }
26
+ interface EvalFlag {
27
+ key: string;
28
+ killed: boolean;
29
+ environments: Partial<Record<EvalEnvironment, {
30
+ enabled: boolean;
31
+ rollout: number | null;
32
+ rules: readonly EvalRule[];
33
+ }>>;
34
+ }
35
+ interface EvalContext {
36
+ userId?: string;
37
+ email?: string;
38
+ org?: string;
39
+ role?: string;
40
+ }
41
+ /** FNV-1a (32 bit) over de UTF-8-bytes. Snel, synchroon en overal gelijk (ook in edge-runtimes zonder `node:crypto`). */
42
+ declare function fnv1a32(input: string): number;
43
+ /** De groep (0 tot 9999) van een gebruiker voor een flag. */
44
+ declare function rolloutBucket(flagKey: string, userId: string): number;
45
+ /** Een ontbrekend kenmerk past bij geen enkele regel, ook niet bij `not_in`. */
46
+ declare function ruleMatches(rule: EvalRule, ctx: EvalContext): boolean;
47
+ declare function evaluateFlag(flag: EvalFlag, environment: EvalEnvironment, ctx: EvalContext): {
48
+ value: boolean;
49
+ reason: EvalReason;
50
+ };
51
+
52
+ type FlagEnvironment = EvalEnvironment;
53
+ type FlagReason = EvalReason;
54
+ /** Who is looking. On the server free to fill in; in the browser targeting comes from the Infaris Auth token only. */
55
+ type FlagContext = EvalContext;
56
+ /** `fetch` as far as this package uses it (also fits Bun's and Node's). */
57
+ type FetchLike = (input: string, init?: RequestInit) => Promise<Response>;
58
+ type ConfigValue = string | number | boolean | null | ConfigValue[] | {
59
+ [key: string]: ConfigValue;
60
+ };
61
+ interface FlagDefinition extends EvalFlag {
62
+ description: string;
63
+ clientVisible: boolean;
64
+ }
65
+ /** What the secret key gets from `GET /flags/v1/<ref>/snapshot`. */
66
+ interface FlagsSnapshot {
67
+ ref: string;
68
+ version: number;
69
+ flags: FlagDefinition[];
70
+ items: Record<string, ConfigValue>;
71
+ }
72
+ /** What `POST /flags/v1/<ref>/evaluate` returns: only client-visible flags and values for a publishable key. */
73
+ interface EvaluatedFlags {
74
+ version: number;
75
+ flags: Record<string, boolean>;
76
+ items: Record<string, ConfigValue>;
77
+ }
78
+ declare class FlagsError extends InfarisError {
79
+ readonly status: number | null;
80
+ readonly code: string;
81
+ constructor(message: string, status: number | null, code: string);
82
+ }
83
+ /** Environment of this process: `INFARIS_ENV`, then `VERCEL_ENV`, then `NODE_ENV` (`production` stays production). */
84
+ declare function detectEnvironment(env?: Record<string, string | undefined> | undefined): FlagEnvironment;
85
+
86
+ export { type ConfigValue as C, type EvaluatedFlags as E, type FlagContext as F, InfarisError as I, type FlagEnvironment as a, type FetchLike as b, type FlagReason as c, type FlagDefinition as d, FlagsError as e, type FlagsSnapshot as f, detectEnvironment as g, evaluateFlag as h, fnv1a32 as i, ruleMatches as j, rolloutBucket as r };
@@ -0,0 +1,86 @@
1
+ /** Common base of the SDK-specific errors. Existing error names and constructors remain supported. */
2
+ declare class InfarisError extends Error {
3
+ static [Symbol.hasInstance](value: unknown): boolean;
4
+ readonly code: string;
5
+ readonly status: number | null | undefined;
6
+ constructor(message: string, code?: string, status?: number | null | undefined);
7
+ }
8
+
9
+ /**
10
+ * Evaluatie van een feature flag, zonder afhankelijkheden: dezelfde code draait in de hosting-api (beheer-API en de
11
+ * publieke sleutel) en in `@infaris/flags` (`packages/infaris-flags/src/evaluate.ts` is een kopie; de test daar
12
+ * vergelijkt beide bestanden, zodat een gebruiker op de server en in de browser in dezelfde groep valt).
13
+ *
14
+ * Volgorde: kill-switch → omgeving uit → regels (de eerste die past wint) → uitrol. De uitrol is vast per gebruiker:
15
+ * `fnv1a32("<flag>:<userId>") % 10000` tegen `percentage × 100`. Wie bij 10% meedoet, doet bij 25% nog steeds mee.
16
+ */
17
+ type EvalEnvironment = 'production' | 'preview' | 'development';
18
+ type EvalAttribute = 'userId' | 'email' | 'emailDomain' | 'org' | 'role';
19
+ type EvalReason = 'killed' | 'off' | 'rule' | 'rollout' | 'fallthrough' | 'no_user';
20
+ interface EvalRule {
21
+ attribute: EvalAttribute;
22
+ operator: 'in' | 'not_in' | 'ends_with';
23
+ values: readonly string[];
24
+ serve: boolean;
25
+ }
26
+ interface EvalFlag {
27
+ key: string;
28
+ killed: boolean;
29
+ environments: Partial<Record<EvalEnvironment, {
30
+ enabled: boolean;
31
+ rollout: number | null;
32
+ rules: readonly EvalRule[];
33
+ }>>;
34
+ }
35
+ interface EvalContext {
36
+ userId?: string;
37
+ email?: string;
38
+ org?: string;
39
+ role?: string;
40
+ }
41
+ /** FNV-1a (32 bit) over de UTF-8-bytes. Snel, synchroon en overal gelijk (ook in edge-runtimes zonder `node:crypto`). */
42
+ declare function fnv1a32(input: string): number;
43
+ /** De groep (0 tot 9999) van een gebruiker voor een flag. */
44
+ declare function rolloutBucket(flagKey: string, userId: string): number;
45
+ /** Een ontbrekend kenmerk past bij geen enkele regel, ook niet bij `not_in`. */
46
+ declare function ruleMatches(rule: EvalRule, ctx: EvalContext): boolean;
47
+ declare function evaluateFlag(flag: EvalFlag, environment: EvalEnvironment, ctx: EvalContext): {
48
+ value: boolean;
49
+ reason: EvalReason;
50
+ };
51
+
52
+ type FlagEnvironment = EvalEnvironment;
53
+ type FlagReason = EvalReason;
54
+ /** Who is looking. On the server free to fill in; in the browser targeting comes from the Infaris Auth token only. */
55
+ type FlagContext = EvalContext;
56
+ /** `fetch` as far as this package uses it (also fits Bun's and Node's). */
57
+ type FetchLike = (input: string, init?: RequestInit) => Promise<Response>;
58
+ type ConfigValue = string | number | boolean | null | ConfigValue[] | {
59
+ [key: string]: ConfigValue;
60
+ };
61
+ interface FlagDefinition extends EvalFlag {
62
+ description: string;
63
+ clientVisible: boolean;
64
+ }
65
+ /** What the secret key gets from `GET /flags/v1/<ref>/snapshot`. */
66
+ interface FlagsSnapshot {
67
+ ref: string;
68
+ version: number;
69
+ flags: FlagDefinition[];
70
+ items: Record<string, ConfigValue>;
71
+ }
72
+ /** What `POST /flags/v1/<ref>/evaluate` returns: only client-visible flags and values for a publishable key. */
73
+ interface EvaluatedFlags {
74
+ version: number;
75
+ flags: Record<string, boolean>;
76
+ items: Record<string, ConfigValue>;
77
+ }
78
+ declare class FlagsError extends InfarisError {
79
+ readonly status: number | null;
80
+ readonly code: string;
81
+ constructor(message: string, status: number | null, code: string);
82
+ }
83
+ /** Environment of this process: `INFARIS_ENV`, then `VERCEL_ENV`, then `NODE_ENV` (`production` stays production). */
84
+ declare function detectEnvironment(env?: Record<string, string | undefined> | undefined): FlagEnvironment;
85
+
86
+ export { type ConfigValue as C, type EvaluatedFlags as E, type FlagContext as F, InfarisError as I, type FlagEnvironment as a, type FetchLike as b, type FlagReason as c, type FlagDefinition as d, FlagsError as e, type FlagsSnapshot as f, detectEnvironment as g, evaluateFlag as h, fnv1a32 as i, ruleMatches as j, rolloutBucket as r };
package/package.json CHANGED
@@ -1,6 +1,105 @@
1
1
  {
2
2
  "name": "@infaris/flags",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "description": "Edge Config and feature flags for Hosting by Infaris: an in-memory client for servers and edge runtimes with push invalidation, a browser client, a React hook and an adapter for the Vercel flags SDK. Bundles the shared Infaris error contract.",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "import": {
12
+ "types": "./dist/index.d.ts",
13
+ "default": "./dist/index.js"
14
+ },
15
+ "require": {
16
+ "types": "./dist/index.d.cts",
17
+ "default": "./dist/index.cjs"
18
+ }
19
+ },
20
+ "./client": {
21
+ "import": {
22
+ "types": "./dist/client.d.ts",
23
+ "default": "./dist/client.js"
24
+ },
25
+ "require": {
26
+ "types": "./dist/client.d.cts",
27
+ "default": "./dist/client.cjs"
28
+ }
29
+ },
30
+ "./react": {
31
+ "import": {
32
+ "types": "./dist/react.d.ts",
33
+ "default": "./dist/react.js"
34
+ },
35
+ "require": {
36
+ "types": "./dist/react.d.cts",
37
+ "default": "./dist/react.cjs"
38
+ }
39
+ },
40
+ "./adapter": {
41
+ "import": {
42
+ "types": "./dist/adapter.d.ts",
43
+ "default": "./dist/adapter.js"
44
+ },
45
+ "require": {
46
+ "types": "./dist/adapter.d.cts",
47
+ "default": "./dist/adapter.cjs"
48
+ }
49
+ }
50
+ },
51
+ "files": [
52
+ "dist",
53
+ "README.md",
54
+ "LICENSE"
55
+ ],
56
+ "sideEffects": false,
57
+ "scripts": {
58
+ "build": "tsup",
59
+ "prepublishOnly": "bun run build",
60
+ "typecheck": "tsgo --noEmit",
61
+ "typecheck:legacy": "tsc --noEmit",
62
+ "test": "bun test src",
63
+ "lint": "echo \"(no lint configured for flags)\""
64
+ },
65
+ "devDependencies": {
66
+ "@happy-dom/global-registrator": "^21.0.4",
67
+ "@types/bun": "^1.3.14",
68
+ "@types/react": "^19.3.0",
69
+ "@types/react-dom": "^19.3.0",
70
+ "react": "^19.3.0",
71
+ "react-dom": "^19.3.0",
72
+ "typescript": "5.9.3",
73
+ "tsup": "^8.5.1",
74
+ "@infaris/hosting-sdk": "0.1.0"
75
+ },
76
+ "keywords": [
77
+ "infaris",
78
+ "feature-flags",
79
+ "edge-config",
80
+ "flags",
81
+ "rollout",
82
+ "react"
83
+ ],
84
+ "license": "MIT",
85
+ "repository": {
86
+ "type": "git",
87
+ "url": "https://github.com/InfarisHQ/monorepo.git",
88
+ "directory": "packages/infaris-flags"
89
+ },
90
+ "publishConfig": {
91
+ "access": "public",
92
+ "registry": "https://registry.npmjs.org"
93
+ },
94
+ "peerDependencies": {
95
+ "react": ">=18"
96
+ },
97
+ "peerDependenciesMeta": {
98
+ "react": {
99
+ "optional": true
100
+ }
101
+ },
102
+ "engines": {
103
+ "node": ">=22.12.0"
104
+ }
105
+ }