@lunora/flags 1.0.0-alpha.5 → 1.0.0-alpha.50
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/LICENSE.md +6 -0
- package/README.md +5 -1
- package/dist/index.d.mts +87 -46
- package/dist/index.d.ts +87 -46
- package/dist/index.mjs +1 -2
- package/dist/packem_shared/createFlags-D8e_hTbh.mjs +1 -0
- package/dist/packem_shared/defineFlags-BZhoR_h7.mjs +1 -0
- package/dist/packem_shared/{types.d-BWygZiSc.d.mts → types.d-Bek9WZpB.d.mts} +40 -40
- package/dist/packem_shared/{types.d-BWygZiSc.d.ts → types.d-Bek9WZpB.d.ts} +40 -40
- package/dist/providers/env.d.mts +32 -32
- package/dist/providers/env.d.ts +32 -32
- package/dist/providers/env.mjs +1 -75
- package/dist/providers/flagship.d.mts +34 -25
- package/dist/providers/flagship.d.ts +34 -25
- package/dist/providers/flagship.mjs +1 -22
- package/dist/providers/memory.d.mts +31 -31
- package/dist/providers/memory.d.ts +31 -31
- package/dist/providers/memory.mjs +1 -16
- package/dist/web.mjs +1 -1
- package/package.json +4 -4
- package/dist/packem_shared/createFlags-CEbcdnqZ.mjs +0 -100
- package/dist/packem_shared/defineFlags-DAIO8cWQ.mjs +0 -15
package/dist/providers/env.d.mts
CHANGED
|
@@ -1,43 +1,43 @@
|
|
|
1
|
-
import { F as FlagsProviderFactory } from "../packem_shared/types.d-
|
|
1
|
+
import { F as FlagsProviderFactory } from "../packem_shared/types.d-Bek9WZpB.mjs";
|
|
2
2
|
import '@openfeature/server-sdk';
|
|
3
3
|
/** Options for {@link envProvider}. */
|
|
4
4
|
interface EnvProviderOptions {
|
|
5
5
|
/**
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
* Map a flag key to its Worker `env` variable name. Defaults to
|
|
7
|
+
* `prefix` + the key upper-snake-cased (`"dark-mode"` → `"FLAG_DARK_MODE"`).
|
|
8
|
+
* Provide this to override the whole derivation (the `prefix` is then unused).
|
|
9
|
+
*/
|
|
10
10
|
name?: (flagKey: string) => string;
|
|
11
11
|
/** Prefix prepended to the derived env variable name. Defaults to `"FLAG_"`. */
|
|
12
12
|
prefix?: string;
|
|
13
13
|
}
|
|
14
14
|
/**
|
|
15
|
-
* A zero-dependency OpenFeature provider that reads flags from the Worker `env`
|
|
16
|
-
* (plain `vars` and Secrets Store / `.dev.vars` values). Each flag key maps to an
|
|
17
|
-
* env variable (default `"dark-mode"` → `env.FLAG_DARK_MODE`); the string value is
|
|
18
|
-
* coerced to the read's type:
|
|
19
|
-
*
|
|
20
|
-
* - **boolean** — `true`/`1`/`on`/`yes` → `true`, `false`/`0`/`off`/`no` → `false` (case-insensitive), anything else is a parse error.
|
|
21
|
-
* - **number** — `Number(value)`; non-numeric is a parse error.
|
|
22
|
-
* - **string** — the raw value.
|
|
23
|
-
* - **object** — `JSON.parse(value)`; invalid JSON is a parse error.
|
|
24
|
-
*
|
|
25
|
-
* A key with no matching env variable falls back to the call's default (reason
|
|
26
|
-
* `DEFAULT`); a parse error also returns the default (reason `ERROR`) so a
|
|
27
|
-
* malformed value degrades the read rather than throwing.
|
|
28
|
-
*
|
|
29
|
-
* ```ts
|
|
30
|
-
* import { defineFlags } from "@lunora/flags";
|
|
31
|
-
* import { envProvider } from "@lunora/flags/providers/env";
|
|
32
|
-
*
|
|
33
|
-
* // wrangler.jsonc: { "vars": { "FLAG_DARK_MODE": "true", "FLAG_PAGE_SIZE": "25" } }
|
|
34
|
-
* export default defineFlags({ provider: envProvider() });
|
|
35
|
-
* // ctx.flags.boolean("dark-mode", false) → true
|
|
36
|
-
* // ctx.flags.number("page-size", 10) → 25
|
|
37
|
-
* ```
|
|
38
|
-
*
|
|
39
|
-
* Values are static per deployment (env is fixed for the isolate's lifetime), so
|
|
40
|
-
* this suits build-time/per-environment toggles rather than live targeting.
|
|
41
|
-
*/
|
|
15
|
+
* A zero-dependency OpenFeature provider that reads flags from the Worker `env`
|
|
16
|
+
* (plain `vars` and Secrets Store / `.dev.vars` values). Each flag key maps to an
|
|
17
|
+
* env variable (default `"dark-mode"` → `env.FLAG_DARK_MODE`); the string value is
|
|
18
|
+
* coerced to the read's type:
|
|
19
|
+
*
|
|
20
|
+
* - **boolean** — `true`/`1`/`on`/`yes` → `true`, `false`/`0`/`off`/`no` → `false` (case-insensitive), anything else is a parse error.
|
|
21
|
+
* - **number** — `Number(value)`; non-numeric is a parse error.
|
|
22
|
+
* - **string** — the raw value.
|
|
23
|
+
* - **object** — `JSON.parse(value)`; invalid JSON is a parse error.
|
|
24
|
+
*
|
|
25
|
+
* A key with no matching env variable falls back to the call's default (reason
|
|
26
|
+
* `DEFAULT`); a parse error also returns the default (reason `ERROR`) so a
|
|
27
|
+
* malformed value degrades the read rather than throwing.
|
|
28
|
+
*
|
|
29
|
+
* ```ts
|
|
30
|
+
* import { defineFlags } from "@lunora/flags";
|
|
31
|
+
* import { envProvider } from "@lunora/flags/providers/env";
|
|
32
|
+
*
|
|
33
|
+
* // wrangler.jsonc: { "vars": { "FLAG_DARK_MODE": "true", "FLAG_PAGE_SIZE": "25" } }
|
|
34
|
+
* export default defineFlags({ provider: envProvider() });
|
|
35
|
+
* // ctx.flags.boolean("dark-mode", false) → true
|
|
36
|
+
* // ctx.flags.number("page-size", 10) → 25
|
|
37
|
+
* ```
|
|
38
|
+
*
|
|
39
|
+
* Values are static per deployment (env is fixed for the isolate's lifetime), so
|
|
40
|
+
* this suits build-time/per-environment toggles rather than live targeting.
|
|
41
|
+
*/
|
|
42
42
|
declare const envProvider: (options?: EnvProviderOptions) => FlagsProviderFactory;
|
|
43
43
|
export { type EnvProviderOptions, envProvider };
|
package/dist/providers/env.d.ts
CHANGED
|
@@ -1,43 +1,43 @@
|
|
|
1
|
-
import { F as FlagsProviderFactory } from "../packem_shared/types.d-
|
|
1
|
+
import { F as FlagsProviderFactory } from "../packem_shared/types.d-Bek9WZpB.js";
|
|
2
2
|
import '@openfeature/server-sdk';
|
|
3
3
|
/** Options for {@link envProvider}. */
|
|
4
4
|
interface EnvProviderOptions {
|
|
5
5
|
/**
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
* Map a flag key to its Worker `env` variable name. Defaults to
|
|
7
|
+
* `prefix` + the key upper-snake-cased (`"dark-mode"` → `"FLAG_DARK_MODE"`).
|
|
8
|
+
* Provide this to override the whole derivation (the `prefix` is then unused).
|
|
9
|
+
*/
|
|
10
10
|
name?: (flagKey: string) => string;
|
|
11
11
|
/** Prefix prepended to the derived env variable name. Defaults to `"FLAG_"`. */
|
|
12
12
|
prefix?: string;
|
|
13
13
|
}
|
|
14
14
|
/**
|
|
15
|
-
* A zero-dependency OpenFeature provider that reads flags from the Worker `env`
|
|
16
|
-
* (plain `vars` and Secrets Store / `.dev.vars` values). Each flag key maps to an
|
|
17
|
-
* env variable (default `"dark-mode"` → `env.FLAG_DARK_MODE`); the string value is
|
|
18
|
-
* coerced to the read's type:
|
|
19
|
-
*
|
|
20
|
-
* - **boolean** — `true`/`1`/`on`/`yes` → `true`, `false`/`0`/`off`/`no` → `false` (case-insensitive), anything else is a parse error.
|
|
21
|
-
* - **number** — `Number(value)`; non-numeric is a parse error.
|
|
22
|
-
* - **string** — the raw value.
|
|
23
|
-
* - **object** — `JSON.parse(value)`; invalid JSON is a parse error.
|
|
24
|
-
*
|
|
25
|
-
* A key with no matching env variable falls back to the call's default (reason
|
|
26
|
-
* `DEFAULT`); a parse error also returns the default (reason `ERROR`) so a
|
|
27
|
-
* malformed value degrades the read rather than throwing.
|
|
28
|
-
*
|
|
29
|
-
* ```ts
|
|
30
|
-
* import { defineFlags } from "@lunora/flags";
|
|
31
|
-
* import { envProvider } from "@lunora/flags/providers/env";
|
|
32
|
-
*
|
|
33
|
-
* // wrangler.jsonc: { "vars": { "FLAG_DARK_MODE": "true", "FLAG_PAGE_SIZE": "25" } }
|
|
34
|
-
* export default defineFlags({ provider: envProvider() });
|
|
35
|
-
* // ctx.flags.boolean("dark-mode", false) → true
|
|
36
|
-
* // ctx.flags.number("page-size", 10) → 25
|
|
37
|
-
* ```
|
|
38
|
-
*
|
|
39
|
-
* Values are static per deployment (env is fixed for the isolate's lifetime), so
|
|
40
|
-
* this suits build-time/per-environment toggles rather than live targeting.
|
|
41
|
-
*/
|
|
15
|
+
* A zero-dependency OpenFeature provider that reads flags from the Worker `env`
|
|
16
|
+
* (plain `vars` and Secrets Store / `.dev.vars` values). Each flag key maps to an
|
|
17
|
+
* env variable (default `"dark-mode"` → `env.FLAG_DARK_MODE`); the string value is
|
|
18
|
+
* coerced to the read's type:
|
|
19
|
+
*
|
|
20
|
+
* - **boolean** — `true`/`1`/`on`/`yes` → `true`, `false`/`0`/`off`/`no` → `false` (case-insensitive), anything else is a parse error.
|
|
21
|
+
* - **number** — `Number(value)`; non-numeric is a parse error.
|
|
22
|
+
* - **string** — the raw value.
|
|
23
|
+
* - **object** — `JSON.parse(value)`; invalid JSON is a parse error.
|
|
24
|
+
*
|
|
25
|
+
* A key with no matching env variable falls back to the call's default (reason
|
|
26
|
+
* `DEFAULT`); a parse error also returns the default (reason `ERROR`) so a
|
|
27
|
+
* malformed value degrades the read rather than throwing.
|
|
28
|
+
*
|
|
29
|
+
* ```ts
|
|
30
|
+
* import { defineFlags } from "@lunora/flags";
|
|
31
|
+
* import { envProvider } from "@lunora/flags/providers/env";
|
|
32
|
+
*
|
|
33
|
+
* // wrangler.jsonc: { "vars": { "FLAG_DARK_MODE": "true", "FLAG_PAGE_SIZE": "25" } }
|
|
34
|
+
* export default defineFlags({ provider: envProvider() });
|
|
35
|
+
* // ctx.flags.boolean("dark-mode", false) → true
|
|
36
|
+
* // ctx.flags.number("page-size", 10) → 25
|
|
37
|
+
* ```
|
|
38
|
+
*
|
|
39
|
+
* Values are static per deployment (env is fixed for the isolate's lifetime), so
|
|
40
|
+
* this suits build-time/per-environment toggles rather than live targeting.
|
|
41
|
+
*/
|
|
42
42
|
declare const envProvider: (options?: EnvProviderOptions) => FlagsProviderFactory;
|
|
43
43
|
export { type EnvProviderOptions, envProvider };
|
package/dist/providers/env.mjs
CHANGED
|
@@ -1,75 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
const upperSnake = (flagKey) => flagKey.replaceAll(/[^a-z0-9]+/gi, "_").toUpperCase();
|
|
4
|
-
const TRUE_TOKENS = /* @__PURE__ */ new Set(["1", "on", "true", "yes"]);
|
|
5
|
-
const FALSE_TOKENS = /* @__PURE__ */ new Set(["0", "false", "no", "off"]);
|
|
6
|
-
const staticDetails = (value) => Promise.resolve({ reason: "STATIC", value });
|
|
7
|
-
const missing = (defaultValue) => Promise.resolve({ reason: "DEFAULT", value: defaultValue });
|
|
8
|
-
const parseError = (defaultValue, message) => Promise.resolve({ errorCode: ErrorCode.PARSE_ERROR, errorMessage: message, reason: "ERROR", value: defaultValue });
|
|
9
|
-
const envProvider = (options = {}) => {
|
|
10
|
-
const prefix = options.prefix ?? "FLAG_";
|
|
11
|
-
const nameOf = options.name ?? ((flagKey) => prefix + upperSnake(flagKey));
|
|
12
|
-
return (env) => {
|
|
13
|
-
const raw = (flagKey) => {
|
|
14
|
-
const value = env[nameOf(flagKey)];
|
|
15
|
-
if (value === void 0 || value === null) {
|
|
16
|
-
return void 0;
|
|
17
|
-
}
|
|
18
|
-
if (typeof value === "string") {
|
|
19
|
-
return value;
|
|
20
|
-
}
|
|
21
|
-
if (typeof value === "number" || typeof value === "boolean") {
|
|
22
|
-
return String(value);
|
|
23
|
-
}
|
|
24
|
-
return JSON.stringify(value);
|
|
25
|
-
};
|
|
26
|
-
return {
|
|
27
|
-
metadata: { name: "lunora-env" },
|
|
28
|
-
resolveBooleanEvaluation: (flagKey, defaultValue) => {
|
|
29
|
-
const value = raw(flagKey);
|
|
30
|
-
if (value === void 0) {
|
|
31
|
-
return missing(defaultValue);
|
|
32
|
-
}
|
|
33
|
-
const token = value.trim().toLowerCase();
|
|
34
|
-
if (TRUE_TOKENS.has(token)) {
|
|
35
|
-
return staticDetails(true);
|
|
36
|
-
}
|
|
37
|
-
if (FALSE_TOKENS.has(token)) {
|
|
38
|
-
return staticDetails(false);
|
|
39
|
-
}
|
|
40
|
-
return parseError(defaultValue, `env flag "${flagKey}" (${nameOf(flagKey)}) is not a boolean: "${value}"`);
|
|
41
|
-
},
|
|
42
|
-
resolveNumberEvaluation: (flagKey, defaultValue) => {
|
|
43
|
-
const value = raw(flagKey);
|
|
44
|
-
if (value === void 0) {
|
|
45
|
-
return missing(defaultValue);
|
|
46
|
-
}
|
|
47
|
-
const parsed = Number(value);
|
|
48
|
-
if (value.trim() === "" || Number.isNaN(parsed)) {
|
|
49
|
-
return parseError(defaultValue, `env flag "${flagKey}" (${nameOf(flagKey)}) is not a number: "${value}"`);
|
|
50
|
-
}
|
|
51
|
-
return staticDetails(parsed);
|
|
52
|
-
},
|
|
53
|
-
resolveObjectEvaluation: (flagKey, defaultValue) => {
|
|
54
|
-
const value = raw(flagKey);
|
|
55
|
-
if (value === void 0) {
|
|
56
|
-
return missing(defaultValue);
|
|
57
|
-
}
|
|
58
|
-
try {
|
|
59
|
-
return staticDetails(JSON.parse(value));
|
|
60
|
-
} catch (error) {
|
|
61
|
-
return parseError(
|
|
62
|
-
defaultValue,
|
|
63
|
-
`env flag "${flagKey}" (${nameOf(flagKey)}) is not valid JSON: ${error instanceof Error ? error.message : String(error)}`
|
|
64
|
-
);
|
|
65
|
-
}
|
|
66
|
-
},
|
|
67
|
-
resolveStringEvaluation: (flagKey, defaultValue) => {
|
|
68
|
-
const value = raw(flagKey);
|
|
69
|
-
return value === void 0 ? missing(defaultValue) : staticDetails(value);
|
|
70
|
-
}
|
|
71
|
-
};
|
|
72
|
-
};
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
export { envProvider };
|
|
1
|
+
import{ErrorCode as m}from"@openfeature/server-sdk";const f=o=>o.replaceAll(/[^a-z0-9]+/gi,"_").toUpperCase(),E=new Set(["1","on","true","yes"]),p=new Set(["0","false","no","off"]),s=o=>Promise.resolve({reason:"STATIC",value:o}),u=o=>Promise.resolve({reason:"DEFAULT",value:o}),l=(o,v)=>Promise.resolve({errorCode:m.PARSE_ERROR,errorMessage:v,reason:"ERROR",value:o}),d=(o={})=>{const v=o.prefix??"FLAG_",i=o.name??(c=>v+f(c));return c=>{const a=e=>{const r=c[i(e)];if(r!=null)return typeof r=="string"?r:typeof r=="number"||typeof r=="boolean"?String(r):JSON.stringify(r)};return{metadata:{name:"lunora-env"},resolveBooleanEvaluation:(e,r)=>{const n=a(e);if(n===void 0)return u(r);const t=n.trim().toLowerCase();return E.has(t)?s(!0):p.has(t)?s(!1):l(r,`env flag "${e}" (${i(e)}) value is not a recognized boolean`)},resolveNumberEvaluation:(e,r)=>{const n=a(e);if(n===void 0)return u(r);const t=Number(n);return n.trim()===""||Number.isNaN(t)?l(r,`env flag "${e}" (${i(e)}) value is not a number`):s(t)},resolveObjectEvaluation:(e,r)=>{const n=a(e);if(n===void 0)return u(r);try{return s(JSON.parse(n))}catch(t){return l(r,`env flag "${e}" (${i(e)}) is not valid JSON: ${t instanceof Error?t.message:String(t)}`)}},resolveStringEvaluation:(e,r)=>{const n=a(e);return n===void 0?u(r):s(n)}}}};export{d as envProvider};
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import { F as FlagsProviderFactory } from "../packem_shared/types.d-
|
|
1
|
+
import { F as FlagsProviderFactory } from "../packem_shared/types.d-Bek9WZpB.mjs";
|
|
2
2
|
import '@openfeature/server-sdk';
|
|
3
3
|
/**
|
|
4
|
-
* Flagship provider in **binding mode** (recommended for Workers): evaluations
|
|
5
|
-
* go through the wrangler binding — no HTTP, no auth token.
|
|
6
|
-
*/
|
|
4
|
+
* Flagship provider in **binding mode** (recommended for Workers): evaluations
|
|
5
|
+
* go through the wrangler binding — no HTTP, no auth token.
|
|
6
|
+
*/
|
|
7
7
|
interface FlagshipBindingOptions {
|
|
8
8
|
/**
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
* The name of the Flagship binding on the Worker `env` (e.g. `"FLAGS"`). The
|
|
10
|
+
* factory resolves `env[binding]` at request time. Configure a matching
|
|
11
|
+
* `flagship` binding in `wrangler.jsonc` with this binding name and your app id.
|
|
12
|
+
*/
|
|
13
13
|
binding: string;
|
|
14
14
|
/** Max cached entries when `cacheTtl` is set (default 1000). */
|
|
15
15
|
cacheMaxSize?: number;
|
|
@@ -19,16 +19,24 @@ interface FlagshipBindingOptions {
|
|
|
19
19
|
logging?: boolean;
|
|
20
20
|
}
|
|
21
21
|
/**
|
|
22
|
-
* Flagship provider in **HTTP mode** (non-binding Workers or other server
|
|
23
|
-
* runtimes): evaluations go to the Flagship API over HTTP.
|
|
24
|
-
*/
|
|
22
|
+
* Flagship provider in **HTTP mode** (non-binding Workers or other server
|
|
23
|
+
* runtimes): evaluations go to the Flagship API over HTTP.
|
|
24
|
+
*/
|
|
25
25
|
interface FlagshipHttpOptions {
|
|
26
26
|
/** Account id for multi-tenant routing (required with `appId`). */
|
|
27
27
|
accountId?: string;
|
|
28
28
|
/** Flagship app id; the SDK builds the evaluation URL (mutually exclusive with `endpoint`). */
|
|
29
29
|
appId?: string;
|
|
30
|
-
/**
|
|
31
|
-
|
|
30
|
+
/**
|
|
31
|
+
* Bearer token added as an `Authorization: Bearer` header to every request.
|
|
32
|
+
* Either the literal token, or a thunk resolved against the Worker `env` at
|
|
33
|
+
* construction (`(env) => env.FLAGSHIP_TOKEN`) so the secret never has to be
|
|
34
|
+
* inlined in source. A thunk resolving to anything but a non-empty string
|
|
35
|
+
* throws from the bind (`createFlags` logs it and evaluations fall back to
|
|
36
|
+
* their caller-supplied defaults): `Bearer undefined` would otherwise make
|
|
37
|
+
* every evaluation fall back with nothing said.
|
|
38
|
+
*/
|
|
39
|
+
authToken?: ((env: Record<string, unknown>) => unknown) | string;
|
|
32
40
|
/** Base URL override (only used with `appId`). */
|
|
33
41
|
baseUrl?: string;
|
|
34
42
|
cacheMaxSize?: number;
|
|
@@ -46,17 +54,18 @@ interface FlagshipHttpOptions {
|
|
|
46
54
|
/** Options for `flagshipProvider` — binding mode or HTTP mode. */
|
|
47
55
|
type FlagshipProviderOptions = FlagshipBindingOptions | FlagshipHttpOptions;
|
|
48
56
|
/**
|
|
49
|
-
* Builds a Cloudflare Flagship OpenFeature provider for `defineFlags({ provider })`.
|
|
50
|
-
* Flagship is Lunora's first-class default; the same `defineFlags` accepts any
|
|
51
|
-
* OpenFeature provider, so apps can swap it out without touching call sites.
|
|
52
|
-
*
|
|
53
|
-
* ```ts
|
|
54
|
-
* // Binding mode (recommended) — reads env.FLAGS at request time:
|
|
55
|
-
* flagshipProvider({ binding: "FLAGS" })
|
|
56
|
-
*
|
|
57
|
-
* // HTTP mode:
|
|
58
|
-
* flagshipProvider({ appId: "app-abc", accountId: "acct", authToken: "tok" })
|
|
59
|
-
*
|
|
60
|
-
|
|
57
|
+
* Builds a Cloudflare Flagship OpenFeature provider for `defineFlags({ provider })`.
|
|
58
|
+
* Flagship is Lunora's first-class default; the same `defineFlags` accepts any
|
|
59
|
+
* OpenFeature provider, so apps can swap it out without touching call sites.
|
|
60
|
+
*
|
|
61
|
+
* ```ts
|
|
62
|
+
* // Binding mode (recommended) — reads env.FLAGS at request time:
|
|
63
|
+
* flagshipProvider({ binding: "FLAGS" })
|
|
64
|
+
*
|
|
65
|
+
* // HTTP mode — the token is a literal, or a thunk read off the Worker env:
|
|
66
|
+
* flagshipProvider({ appId: "app-abc", accountId: "acct", authToken: "tok" })
|
|
67
|
+
* flagshipProvider({ appId: "app-abc", accountId: "acct", authToken: (env) => env.FLAGSHIP_TOKEN })
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
61
70
|
declare const flagshipProvider: (options: FlagshipProviderOptions) => FlagsProviderFactory;
|
|
62
71
|
export { type FlagshipBindingOptions, type FlagshipHttpOptions, type FlagshipProviderOptions, flagshipProvider };
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import { F as FlagsProviderFactory } from "../packem_shared/types.d-
|
|
1
|
+
import { F as FlagsProviderFactory } from "../packem_shared/types.d-Bek9WZpB.js";
|
|
2
2
|
import '@openfeature/server-sdk';
|
|
3
3
|
/**
|
|
4
|
-
* Flagship provider in **binding mode** (recommended for Workers): evaluations
|
|
5
|
-
* go through the wrangler binding — no HTTP, no auth token.
|
|
6
|
-
*/
|
|
4
|
+
* Flagship provider in **binding mode** (recommended for Workers): evaluations
|
|
5
|
+
* go through the wrangler binding — no HTTP, no auth token.
|
|
6
|
+
*/
|
|
7
7
|
interface FlagshipBindingOptions {
|
|
8
8
|
/**
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
* The name of the Flagship binding on the Worker `env` (e.g. `"FLAGS"`). The
|
|
10
|
+
* factory resolves `env[binding]` at request time. Configure a matching
|
|
11
|
+
* `flagship` binding in `wrangler.jsonc` with this binding name and your app id.
|
|
12
|
+
*/
|
|
13
13
|
binding: string;
|
|
14
14
|
/** Max cached entries when `cacheTtl` is set (default 1000). */
|
|
15
15
|
cacheMaxSize?: number;
|
|
@@ -19,16 +19,24 @@ interface FlagshipBindingOptions {
|
|
|
19
19
|
logging?: boolean;
|
|
20
20
|
}
|
|
21
21
|
/**
|
|
22
|
-
* Flagship provider in **HTTP mode** (non-binding Workers or other server
|
|
23
|
-
* runtimes): evaluations go to the Flagship API over HTTP.
|
|
24
|
-
*/
|
|
22
|
+
* Flagship provider in **HTTP mode** (non-binding Workers or other server
|
|
23
|
+
* runtimes): evaluations go to the Flagship API over HTTP.
|
|
24
|
+
*/
|
|
25
25
|
interface FlagshipHttpOptions {
|
|
26
26
|
/** Account id for multi-tenant routing (required with `appId`). */
|
|
27
27
|
accountId?: string;
|
|
28
28
|
/** Flagship app id; the SDK builds the evaluation URL (mutually exclusive with `endpoint`). */
|
|
29
29
|
appId?: string;
|
|
30
|
-
/**
|
|
31
|
-
|
|
30
|
+
/**
|
|
31
|
+
* Bearer token added as an `Authorization: Bearer` header to every request.
|
|
32
|
+
* Either the literal token, or a thunk resolved against the Worker `env` at
|
|
33
|
+
* construction (`(env) => env.FLAGSHIP_TOKEN`) so the secret never has to be
|
|
34
|
+
* inlined in source. A thunk resolving to anything but a non-empty string
|
|
35
|
+
* throws from the bind (`createFlags` logs it and evaluations fall back to
|
|
36
|
+
* their caller-supplied defaults): `Bearer undefined` would otherwise make
|
|
37
|
+
* every evaluation fall back with nothing said.
|
|
38
|
+
*/
|
|
39
|
+
authToken?: ((env: Record<string, unknown>) => unknown) | string;
|
|
32
40
|
/** Base URL override (only used with `appId`). */
|
|
33
41
|
baseUrl?: string;
|
|
34
42
|
cacheMaxSize?: number;
|
|
@@ -46,17 +54,18 @@ interface FlagshipHttpOptions {
|
|
|
46
54
|
/** Options for `flagshipProvider` — binding mode or HTTP mode. */
|
|
47
55
|
type FlagshipProviderOptions = FlagshipBindingOptions | FlagshipHttpOptions;
|
|
48
56
|
/**
|
|
49
|
-
* Builds a Cloudflare Flagship OpenFeature provider for `defineFlags({ provider })`.
|
|
50
|
-
* Flagship is Lunora's first-class default; the same `defineFlags` accepts any
|
|
51
|
-
* OpenFeature provider, so apps can swap it out without touching call sites.
|
|
52
|
-
*
|
|
53
|
-
* ```ts
|
|
54
|
-
* // Binding mode (recommended) — reads env.FLAGS at request time:
|
|
55
|
-
* flagshipProvider({ binding: "FLAGS" })
|
|
56
|
-
*
|
|
57
|
-
* // HTTP mode:
|
|
58
|
-
* flagshipProvider({ appId: "app-abc", accountId: "acct", authToken: "tok" })
|
|
59
|
-
*
|
|
60
|
-
|
|
57
|
+
* Builds a Cloudflare Flagship OpenFeature provider for `defineFlags({ provider })`.
|
|
58
|
+
* Flagship is Lunora's first-class default; the same `defineFlags` accepts any
|
|
59
|
+
* OpenFeature provider, so apps can swap it out without touching call sites.
|
|
60
|
+
*
|
|
61
|
+
* ```ts
|
|
62
|
+
* // Binding mode (recommended) — reads env.FLAGS at request time:
|
|
63
|
+
* flagshipProvider({ binding: "FLAGS" })
|
|
64
|
+
*
|
|
65
|
+
* // HTTP mode — the token is a literal, or a thunk read off the Worker env:
|
|
66
|
+
* flagshipProvider({ appId: "app-abc", accountId: "acct", authToken: "tok" })
|
|
67
|
+
* flagshipProvider({ appId: "app-abc", accountId: "acct", authToken: (env) => env.FLAGSHIP_TOKEN })
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
61
70
|
declare const flagshipProvider: (options: FlagshipProviderOptions) => FlagsProviderFactory;
|
|
62
71
|
export { type FlagshipBindingOptions, type FlagshipHttpOptions, type FlagshipProviderOptions, flagshipProvider };
|
|
@@ -1,22 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { LunoraError } from '@lunora/errors';
|
|
3
|
-
|
|
4
|
-
const isBindingOptions = (options) => "binding" in options && typeof options.binding === "string";
|
|
5
|
-
const flagshipProvider = (options) => {
|
|
6
|
-
if (isBindingOptions(options)) {
|
|
7
|
-
const { binding: bindingName, ...rest } = options;
|
|
8
|
-
return (env) => {
|
|
9
|
-
const binding = env[bindingName];
|
|
10
|
-
if (binding === void 0 || binding === null) {
|
|
11
|
-
throw new LunoraError(
|
|
12
|
-
"INTERNAL",
|
|
13
|
-
`flagshipProvider: no binding "${bindingName}" found on env. Add a \`flagship\` binding to wrangler.jsonc, e.g. { "flagship": [{ "binding": "${bindingName}", "app_id": "your-app-id" }] }.`
|
|
14
|
-
);
|
|
15
|
-
}
|
|
16
|
-
return new FlagshipServerProvider({ binding, ...rest });
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
return () => new FlagshipServerProvider(options);
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
export { flagshipProvider };
|
|
1
|
+
import{FlagshipServerProvider as a}from"@cloudflare/flagship/server";import{LunoraError as r}from"@lunora/errors";const h=e=>"binding"in e&&typeof e.binding=="string",g=e=>{if(h(e)){const{binding:n,...i}=e;return l=>{const o=l[n];if(o==null)throw new r("INTERNAL",`flagshipProvider: no binding "${n}" found on env. Add a \`flagship\` binding to wrangler.jsonc, e.g. { "flagship": [{ "binding": "${n}", "app_id": "your-app-id" }] }.`);return new a({binding:o,...i})}}const{appId:d,endpoint:s}=e;if(d===void 0&&s===void 0)throw new r("INTERNAL",'flagshipProvider: HTTP mode requires either `appId` (the SDK builds the evaluation URL) or `endpoint` (a full evaluation URL). Pass exactly one, or use binding mode: `flagshipProvider({ binding: "FLAGS" })`.');if(d!==void 0&&s!==void 0)throw new r("INTERNAL","flagshipProvider: `appId` and `endpoint` are mutually exclusive in HTTP mode — pass exactly one.");if(e.authToken==="")throw new r("INTERNAL","flagshipProvider: `authToken` is an empty string — omit it for an unauthenticated endpoint, or pass a thunk so the secret is read off the Worker env at request time.");const{authToken:t,...u}=e;return n=>{if(typeof t!="function")return new a({...u,...t===void 0?{}:{authToken:t}});const i=t(n);if(typeof i!="string"||i.length===0)throw new r("INTERNAL","flagshipProvider: `authToken` resolved to an empty or non-string value — check that the env var the thunk reads is set. An unset secret would send `Bearer undefined`, and every evaluation would silently fall back to its default.");return new a({...u,authToken:i})}};export{g as flagshipProvider};
|
|
@@ -1,37 +1,37 @@
|
|
|
1
1
|
import { JsonValue } from '@openfeature/server-sdk';
|
|
2
|
-
import { F as FlagsProviderFactory } from "../packem_shared/types.d-
|
|
2
|
+
import { F as FlagsProviderFactory } from "../packem_shared/types.d-Bek9WZpB.mjs";
|
|
3
3
|
/**
|
|
4
|
-
* The value a static memory flag can hold — any JSON value (OpenFeature boolean /
|
|
5
|
-
* number / string / structured object). Alias of `JsonValue` for naming clarity.
|
|
6
|
-
*/
|
|
4
|
+
* The value a static memory flag can hold — any JSON value (OpenFeature boolean /
|
|
5
|
+
* number / string / structured object). Alias of `JsonValue` for naming clarity.
|
|
6
|
+
*/
|
|
7
7
|
type MemoryFlagValue = JsonValue;
|
|
8
8
|
/**
|
|
9
|
-
* A static, in-memory OpenFeature provider for `defineFlags({ provider })`, built
|
|
10
|
-
* on OpenFeature's own in-memory provider (no extra install). Pass a plain
|
|
11
|
-
* `key → value` map; every read resolves the configured value (reason `STATIC`)
|
|
12
|
-
* or, for an unknown key, the call's default.
|
|
13
|
-
*
|
|
14
|
-
* Ideal for tests, local development, and apps with a handful of static flags
|
|
15
|
-
* checked into the repo — no external flag service, no binding.
|
|
16
|
-
*
|
|
17
|
-
* ```ts
|
|
18
|
-
* import { defineFlags } from "@lunora/flags";
|
|
19
|
-
* import { memoryProvider } from "@lunora/flags/providers/memory";
|
|
20
|
-
*
|
|
21
|
-
* export default defineFlags({
|
|
22
|
-
* provider: memoryProvider({
|
|
23
|
-
* "dark-mode": true,
|
|
24
|
-
* "page-size": 25,
|
|
25
|
-
* "homepage-hero": "control",
|
|
26
|
-
* "rollout": { percent: 10, regions: ["us", "eu"] },
|
|
27
|
-
* }),
|
|
28
|
-
* });
|
|
29
|
-
* ```
|
|
30
|
-
*
|
|
31
|
-
* Targeting (per-context variants) is out of scope for this static provider; for
|
|
32
|
-
* that, wire a real provider (e.g. `flagshipProvider`) or construct a
|
|
33
|
-
* `TypedInMemoryProvider` with `contextEvaluator`s directly via the `defineFlags`
|
|
34
|
-
* escape hatch (`provider: () => new TypedInMemoryProvider(config)`).
|
|
35
|
-
*/
|
|
9
|
+
* A static, in-memory OpenFeature provider for `defineFlags({ provider })`, built
|
|
10
|
+
* on OpenFeature's own in-memory provider (no extra install). Pass a plain
|
|
11
|
+
* `key → value` map; every read resolves the configured value (reason `STATIC`)
|
|
12
|
+
* or, for an unknown key, the call's default.
|
|
13
|
+
*
|
|
14
|
+
* Ideal for tests, local development, and apps with a handful of static flags
|
|
15
|
+
* checked into the repo — no external flag service, no binding.
|
|
16
|
+
*
|
|
17
|
+
* ```ts
|
|
18
|
+
* import { defineFlags } from "@lunora/flags";
|
|
19
|
+
* import { memoryProvider } from "@lunora/flags/providers/memory";
|
|
20
|
+
*
|
|
21
|
+
* export default defineFlags({
|
|
22
|
+
* provider: memoryProvider({
|
|
23
|
+
* "dark-mode": true,
|
|
24
|
+
* "page-size": 25,
|
|
25
|
+
* "homepage-hero": "control",
|
|
26
|
+
* "rollout": { percent: 10, regions: ["us", "eu"] },
|
|
27
|
+
* }),
|
|
28
|
+
* });
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* Targeting (per-context variants) is out of scope for this static provider; for
|
|
32
|
+
* that, wire a real provider (e.g. `flagshipProvider`) or construct a
|
|
33
|
+
* `TypedInMemoryProvider` with `contextEvaluator`s directly via the `defineFlags`
|
|
34
|
+
* escape hatch (`provider: () => new TypedInMemoryProvider(config)`).
|
|
35
|
+
*/
|
|
36
36
|
declare const memoryProvider: (flags: Record<string, MemoryFlagValue>) => FlagsProviderFactory;
|
|
37
37
|
export { type MemoryFlagValue, memoryProvider };
|
|
@@ -1,37 +1,37 @@
|
|
|
1
1
|
import { JsonValue } from '@openfeature/server-sdk';
|
|
2
|
-
import { F as FlagsProviderFactory } from "../packem_shared/types.d-
|
|
2
|
+
import { F as FlagsProviderFactory } from "../packem_shared/types.d-Bek9WZpB.js";
|
|
3
3
|
/**
|
|
4
|
-
* The value a static memory flag can hold — any JSON value (OpenFeature boolean /
|
|
5
|
-
* number / string / structured object). Alias of `JsonValue` for naming clarity.
|
|
6
|
-
*/
|
|
4
|
+
* The value a static memory flag can hold — any JSON value (OpenFeature boolean /
|
|
5
|
+
* number / string / structured object). Alias of `JsonValue` for naming clarity.
|
|
6
|
+
*/
|
|
7
7
|
type MemoryFlagValue = JsonValue;
|
|
8
8
|
/**
|
|
9
|
-
* A static, in-memory OpenFeature provider for `defineFlags({ provider })`, built
|
|
10
|
-
* on OpenFeature's own in-memory provider (no extra install). Pass a plain
|
|
11
|
-
* `key → value` map; every read resolves the configured value (reason `STATIC`)
|
|
12
|
-
* or, for an unknown key, the call's default.
|
|
13
|
-
*
|
|
14
|
-
* Ideal for tests, local development, and apps with a handful of static flags
|
|
15
|
-
* checked into the repo — no external flag service, no binding.
|
|
16
|
-
*
|
|
17
|
-
* ```ts
|
|
18
|
-
* import { defineFlags } from "@lunora/flags";
|
|
19
|
-
* import { memoryProvider } from "@lunora/flags/providers/memory";
|
|
20
|
-
*
|
|
21
|
-
* export default defineFlags({
|
|
22
|
-
* provider: memoryProvider({
|
|
23
|
-
* "dark-mode": true,
|
|
24
|
-
* "page-size": 25,
|
|
25
|
-
* "homepage-hero": "control",
|
|
26
|
-
* "rollout": { percent: 10, regions: ["us", "eu"] },
|
|
27
|
-
* }),
|
|
28
|
-
* });
|
|
29
|
-
* ```
|
|
30
|
-
*
|
|
31
|
-
* Targeting (per-context variants) is out of scope for this static provider; for
|
|
32
|
-
* that, wire a real provider (e.g. `flagshipProvider`) or construct a
|
|
33
|
-
* `TypedInMemoryProvider` with `contextEvaluator`s directly via the `defineFlags`
|
|
34
|
-
* escape hatch (`provider: () => new TypedInMemoryProvider(config)`).
|
|
35
|
-
*/
|
|
9
|
+
* A static, in-memory OpenFeature provider for `defineFlags({ provider })`, built
|
|
10
|
+
* on OpenFeature's own in-memory provider (no extra install). Pass a plain
|
|
11
|
+
* `key → value` map; every read resolves the configured value (reason `STATIC`)
|
|
12
|
+
* or, for an unknown key, the call's default.
|
|
13
|
+
*
|
|
14
|
+
* Ideal for tests, local development, and apps with a handful of static flags
|
|
15
|
+
* checked into the repo — no external flag service, no binding.
|
|
16
|
+
*
|
|
17
|
+
* ```ts
|
|
18
|
+
* import { defineFlags } from "@lunora/flags";
|
|
19
|
+
* import { memoryProvider } from "@lunora/flags/providers/memory";
|
|
20
|
+
*
|
|
21
|
+
* export default defineFlags({
|
|
22
|
+
* provider: memoryProvider({
|
|
23
|
+
* "dark-mode": true,
|
|
24
|
+
* "page-size": 25,
|
|
25
|
+
* "homepage-hero": "control",
|
|
26
|
+
* "rollout": { percent: 10, regions: ["us", "eu"] },
|
|
27
|
+
* }),
|
|
28
|
+
* });
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* Targeting (per-context variants) is out of scope for this static provider; for
|
|
32
|
+
* that, wire a real provider (e.g. `flagshipProvider`) or construct a
|
|
33
|
+
* `TypedInMemoryProvider` with `contextEvaluator`s directly via the `defineFlags`
|
|
34
|
+
* escape hatch (`provider: () => new TypedInMemoryProvider(config)`).
|
|
35
|
+
*/
|
|
36
36
|
declare const memoryProvider: (flags: Record<string, MemoryFlagValue>) => FlagsProviderFactory;
|
|
37
37
|
export { type MemoryFlagValue, memoryProvider };
|
|
@@ -1,16 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
const memoryProvider = (flags) => {
|
|
4
|
-
const configuration = {};
|
|
5
|
-
for (const [key, value] of Object.entries(flags)) {
|
|
6
|
-
configuration[key] = {
|
|
7
|
-
defaultVariant: "default",
|
|
8
|
-
disabled: false,
|
|
9
|
-
variants: { default: value }
|
|
10
|
-
};
|
|
11
|
-
}
|
|
12
|
-
const provider = new TypedInMemoryProvider(configuration);
|
|
13
|
-
return (_environment) => provider;
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
export { memoryProvider };
|
|
1
|
+
import{TypedInMemoryProvider as a}from"@openfeature/server-sdk";const d=o=>{const e={};for(const[r,n]of Object.entries(o))e[r]={defaultVariant:"default",disabled:!1,variants:{default:n}};const t=new a(e);return r=>t};export{d as memoryProvider};
|
package/dist/web.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
import{FlagshipClientProvider as i}from"@cloudflare/flagship/web";export{i as FlagshipClientProvider};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lunora/flags",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.50",
|
|
4
4
|
"description": "OpenFeature-based feature flags for Lunora — ctx.flags, useFlag, and a first-class Cloudflare Flagship provider with any OpenFeature provider pluggable",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cloudflare",
|
|
@@ -60,11 +60,11 @@
|
|
|
60
60
|
"access": "public"
|
|
61
61
|
},
|
|
62
62
|
"dependencies": {
|
|
63
|
-
"@lunora/errors": "1.0.0-alpha.
|
|
64
|
-
"@openfeature/server-sdk": "^1.
|
|
63
|
+
"@lunora/errors": "1.0.0-alpha.36",
|
|
64
|
+
"@openfeature/server-sdk": "^1.23.0"
|
|
65
65
|
},
|
|
66
66
|
"peerDependencies": {
|
|
67
|
-
"@cloudflare/flagship": "
|
|
67
|
+
"@cloudflare/flagship": ">=0.4.2 <1.0.0",
|
|
68
68
|
"@openfeature/web-sdk": "^1.9.0"
|
|
69
69
|
},
|
|
70
70
|
"peerDependenciesMeta": {
|