@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/LICENSE.md
CHANGED
|
@@ -103,3 +103,9 @@ Unless required by applicable law or agreed to in writing, software distributed
|
|
|
103
103
|
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
|
104
104
|
CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
|
105
105
|
specific language governing permissions and limitations under the License.
|
|
106
|
+
|
|
107
|
+
<!-- DEPENDENCIES -->
|
|
108
|
+
<!-- /DEPENDENCIES -->
|
|
109
|
+
|
|
110
|
+
<!-- TYPE_DEPENDENCIES -->
|
|
111
|
+
<!-- /TYPE_DEPENDENCIES -->
|
package/README.md
CHANGED
|
@@ -79,7 +79,11 @@ export const listPosts = query(async (ctx) => {
|
|
|
79
79
|
```
|
|
80
80
|
|
|
81
81
|
`ctx.flags` never throws: a missing flag, type mismatch, or provider error resolves
|
|
82
|
-
with the `defaultValue`.
|
|
82
|
+
with the `defaultValue`. It does not do so silently — a provider that fails to BIND
|
|
83
|
+
(a missing binding, an unset token, a throwing `initialize`) is logged through
|
|
84
|
+
`defineFlags({ logger })`, or on `console.error` when none is configured, since
|
|
85
|
+
otherwise a misconfigured deployment serves every flag at its checked-in default
|
|
86
|
+
with nothing to notice. The default `targetingKey` (from `identify`) is merged under
|
|
83
87
|
any per-call context, and identical evaluations within one request are memoized.
|
|
84
88
|
|
|
85
89
|
## Exports
|
package/dist/index.d.mts
CHANGED
|
@@ -1,56 +1,97 @@
|
|
|
1
|
-
import { a as FlagsConfig, b as FlagsDefinition, L as LunoraFlags } from "./packem_shared/types.d-
|
|
2
|
-
export type { c as FlagsAuth, F as FlagsProviderFactory } from "./packem_shared/types.d-
|
|
3
|
-
import {
|
|
1
|
+
import { a as FlagsConfig, b as FlagsDefinition, L as LunoraFlags } from "./packem_shared/types.d-Bek9WZpB.mjs";
|
|
2
|
+
export type { c as FlagsAuth, F as FlagsProviderFactory } from "./packem_shared/types.d-Bek9WZpB.mjs";
|
|
3
|
+
import { Provider } from '@openfeature/server-sdk';
|
|
4
4
|
export type { EvaluationContext, EvaluationDetails, Hook, JsonValue, Logger, Provider } from '@openfeature/server-sdk';
|
|
5
5
|
/**
|
|
6
|
-
* Declare the feature-flag provider for a Lunora app. Pure validation +
|
|
7
|
-
* branding — codegen discovers the default export of `lunora/flags.ts`, imports
|
|
8
|
-
* it into the generated worker, and wires `ctx.flags` from `provider` /
|
|
9
|
-
* `identify` (mirrors how `defineQueue` / `defineWorkflow` feed codegen).
|
|
10
|
-
*
|
|
11
|
-
* ```ts
|
|
12
|
-
* // lunora/flags.ts
|
|
13
|
-
* import { defineFlags } from "@lunora/flags";
|
|
14
|
-
* import { flagshipProvider } from "@lunora/flags/providers/flagship";
|
|
15
|
-
*
|
|
16
|
-
* export default defineFlags({
|
|
17
|
-
* provider: flagshipProvider({ binding: "FLAGS" }), // Cloudflare Flagship (binding mode)
|
|
18
|
-
* identify: (auth) => auth.userId ?? undefined, // default targetingKey
|
|
19
|
-
* });
|
|
20
|
-
* ```
|
|
21
|
-
*
|
|
22
|
-
* Any OpenFeature provider works — Flagship is just the first-class default:
|
|
23
|
-
*
|
|
24
|
-
* ```ts
|
|
25
|
-
* export default defineFlags({ provider: (env) => new SomeOpenFeatureProvider(env.SOME_KEY) });
|
|
26
|
-
* ```
|
|
27
|
-
*/
|
|
6
|
+
* Declare the feature-flag provider for a Lunora app. Pure validation +
|
|
7
|
+
* branding — codegen discovers the default export of `lunora/flags.ts`, imports
|
|
8
|
+
* it into the generated worker, and wires `ctx.flags` from `provider` /
|
|
9
|
+
* `identify` (mirrors how `defineQueue` / `defineWorkflow` feed codegen).
|
|
10
|
+
*
|
|
11
|
+
* ```ts
|
|
12
|
+
* // lunora/flags.ts
|
|
13
|
+
* import { defineFlags } from "@lunora/flags";
|
|
14
|
+
* import { flagshipProvider } from "@lunora/flags/providers/flagship";
|
|
15
|
+
*
|
|
16
|
+
* export default defineFlags({
|
|
17
|
+
* provider: flagshipProvider({ binding: "FLAGS" }), // Cloudflare Flagship (binding mode)
|
|
18
|
+
* identify: (auth) => auth.userId ?? undefined, // default targetingKey
|
|
19
|
+
* });
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* Any OpenFeature provider works — Flagship is just the first-class default:
|
|
23
|
+
*
|
|
24
|
+
* ```ts
|
|
25
|
+
* export default defineFlags({ provider: (env) => new SomeOpenFeatureProvider(env.SOME_KEY) });
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
28
|
declare const defineFlags: (config: FlagsConfig) => FlagsDefinition;
|
|
29
29
|
/** True when a value is a {@link defineFlags} result (the runtime brand check). */
|
|
30
30
|
declare const isFlagsDefinition: (value: unknown) => value is FlagsDefinition;
|
|
31
|
-
/**
|
|
31
|
+
/**
|
|
32
|
+
* Per-request extras for `createFlags`. Everything static — the provider
|
|
33
|
+
* factory, `hooks`, `logger` — is read from the {@link FlagsDefinition} itself,
|
|
34
|
+
* so this carries only what the definition cannot know: the test/config
|
|
35
|
+
* provider override and the request's targeting key.
|
|
36
|
+
*/
|
|
32
37
|
interface CreateFlagsOptions {
|
|
33
|
-
/** OpenFeature hooks applied when the provider is bound. */
|
|
34
|
-
hooks?: Hook[];
|
|
35
|
-
/** Logger for the OpenFeature client. */
|
|
36
|
-
logger?: Logger;
|
|
37
|
-
/** Lazily constructs the OpenFeature provider from `env` (invoked once per isolate). */
|
|
38
|
-
provider: () => Provider;
|
|
39
38
|
/**
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
39
|
+
* Overrides the definition's provider (codegen's `config.flags` test seam).
|
|
40
|
+
* Returning `undefined` falls back to `definition.provider(env)`.
|
|
41
|
+
*/
|
|
42
|
+
provider?: () => Provider | undefined;
|
|
43
|
+
/**
|
|
44
|
+
* Default `targetingKey` for every evaluation (from `defineFlags({ identify })`).
|
|
45
|
+
* May be a thunk — codegen passes one wrapping the user's `identify` — so a
|
|
46
|
+
* throwing `identify` fails open to no `targetingKey` rather than escaping
|
|
47
|
+
* ctx construction and taking down the whole request.
|
|
48
|
+
*/
|
|
45
49
|
targetingKey?: (() => string | undefined) | string;
|
|
46
50
|
}
|
|
47
51
|
/**
|
|
48
|
-
* Builds the `ctx.flags` facade for a single request.
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
52
|
+
* Builds the `ctx.flags` facade for a single request. The provider, `hooks`,
|
|
53
|
+
* and `logger` come from `definition`; the client bind is memoized per
|
|
54
|
+
* (`definition`, `env`) pair — both stable object identities for the isolate's
|
|
55
|
+
* lifetime — and each pair owns its own OpenFeature domain, so a pair always
|
|
56
|
+
* evaluates against the provider it was configured with instead of whichever
|
|
57
|
+
* bind happened first. The bind is additionally attempted at most ONCE per
|
|
58
|
+
* request, so a broken deployment costs one `initialize` and one log line
|
|
59
|
+
* rather than one per flag read. Evaluations resolve through the
|
|
60
|
+
* OpenFeature client (the SDK applies hooks and never throws — provider errors
|
|
61
|
+
* surface as the default value with an `errorCode`). The default `targetingKey`
|
|
62
|
+
* is merged under any per-call context, and identical evaluations within the
|
|
63
|
+
* request are memoized so repeated reads of the same flag are internally
|
|
64
|
+
* consistent and hit the provider once.
|
|
65
|
+
*/
|
|
66
|
+
declare const createFlags: (definition: FlagsDefinition, env: Record<string, unknown>, options?: CreateFlagsOptions) => LunoraFlags;
|
|
67
|
+
export { type CreateFlagsOptions, type FlagsConfig, type FlagsDefinition, type LunoraFlags, createFlags,
|
|
68
|
+
/**
|
|
69
|
+
* `@lunora/flags`
|
|
70
|
+
*
|
|
71
|
+
* OpenFeature-based feature flags for Lunora. `defineFlags` in `lunora/flags.ts`
|
|
72
|
+
* configures an OpenFeature provider (Cloudflare Flagship by default, any
|
|
73
|
+
* provider pluggable); codegen wires `ctx.flags` onto every handler ctx from it.
|
|
74
|
+
*
|
|
75
|
+
* - `@lunora/flags` — `defineFlags`, the `createFlags` ctx facade, and types.
|
|
76
|
+
* - `@lunora/flags/providers/flagship` — the first-class Cloudflare Flagship provider.
|
|
77
|
+
* - `@lunora/flags/providers/memory` — a static, in-memory provider (tests/local/simple apps, zero extra deps).
|
|
78
|
+
* - `@lunora/flags/providers/env` — reads flags from the Worker `env` vars/secrets (zero deps).
|
|
79
|
+
* - `@lunora/flags/web` — optional browser OpenFeature provider (escape hatch).
|
|
80
|
+
* @packageDocumentation
|
|
81
|
+
*/
|
|
82
|
+
defineFlags,
|
|
83
|
+
/**
|
|
84
|
+
* `@lunora/flags`
|
|
85
|
+
*
|
|
86
|
+
* OpenFeature-based feature flags for Lunora. `defineFlags` in `lunora/flags.ts`
|
|
87
|
+
* configures an OpenFeature provider (Cloudflare Flagship by default, any
|
|
88
|
+
* provider pluggable); codegen wires `ctx.flags` onto every handler ctx from it.
|
|
89
|
+
*
|
|
90
|
+
* - `@lunora/flags` — `defineFlags`, the `createFlags` ctx facade, and types.
|
|
91
|
+
* - `@lunora/flags/providers/flagship` — the first-class Cloudflare Flagship provider.
|
|
92
|
+
* - `@lunora/flags/providers/memory` — a static, in-memory provider (tests/local/simple apps, zero extra deps).
|
|
93
|
+
* - `@lunora/flags/providers/env` — reads flags from the Worker `env` vars/secrets (zero deps).
|
|
94
|
+
* - `@lunora/flags/web` — optional browser OpenFeature provider (escape hatch).
|
|
95
|
+
* @packageDocumentation
|
|
96
|
+
*/
|
|
97
|
+
isFlagsDefinition };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,56 +1,97 @@
|
|
|
1
|
-
import { a as FlagsConfig, b as FlagsDefinition, L as LunoraFlags } from "./packem_shared/types.d-
|
|
2
|
-
export type { c as FlagsAuth, F as FlagsProviderFactory } from "./packem_shared/types.d-
|
|
3
|
-
import {
|
|
1
|
+
import { a as FlagsConfig, b as FlagsDefinition, L as LunoraFlags } from "./packem_shared/types.d-Bek9WZpB.js";
|
|
2
|
+
export type { c as FlagsAuth, F as FlagsProviderFactory } from "./packem_shared/types.d-Bek9WZpB.js";
|
|
3
|
+
import { Provider } from '@openfeature/server-sdk';
|
|
4
4
|
export type { EvaluationContext, EvaluationDetails, Hook, JsonValue, Logger, Provider } from '@openfeature/server-sdk';
|
|
5
5
|
/**
|
|
6
|
-
* Declare the feature-flag provider for a Lunora app. Pure validation +
|
|
7
|
-
* branding — codegen discovers the default export of `lunora/flags.ts`, imports
|
|
8
|
-
* it into the generated worker, and wires `ctx.flags` from `provider` /
|
|
9
|
-
* `identify` (mirrors how `defineQueue` / `defineWorkflow` feed codegen).
|
|
10
|
-
*
|
|
11
|
-
* ```ts
|
|
12
|
-
* // lunora/flags.ts
|
|
13
|
-
* import { defineFlags } from "@lunora/flags";
|
|
14
|
-
* import { flagshipProvider } from "@lunora/flags/providers/flagship";
|
|
15
|
-
*
|
|
16
|
-
* export default defineFlags({
|
|
17
|
-
* provider: flagshipProvider({ binding: "FLAGS" }), // Cloudflare Flagship (binding mode)
|
|
18
|
-
* identify: (auth) => auth.userId ?? undefined, // default targetingKey
|
|
19
|
-
* });
|
|
20
|
-
* ```
|
|
21
|
-
*
|
|
22
|
-
* Any OpenFeature provider works — Flagship is just the first-class default:
|
|
23
|
-
*
|
|
24
|
-
* ```ts
|
|
25
|
-
* export default defineFlags({ provider: (env) => new SomeOpenFeatureProvider(env.SOME_KEY) });
|
|
26
|
-
* ```
|
|
27
|
-
*/
|
|
6
|
+
* Declare the feature-flag provider for a Lunora app. Pure validation +
|
|
7
|
+
* branding — codegen discovers the default export of `lunora/flags.ts`, imports
|
|
8
|
+
* it into the generated worker, and wires `ctx.flags` from `provider` /
|
|
9
|
+
* `identify` (mirrors how `defineQueue` / `defineWorkflow` feed codegen).
|
|
10
|
+
*
|
|
11
|
+
* ```ts
|
|
12
|
+
* // lunora/flags.ts
|
|
13
|
+
* import { defineFlags } from "@lunora/flags";
|
|
14
|
+
* import { flagshipProvider } from "@lunora/flags/providers/flagship";
|
|
15
|
+
*
|
|
16
|
+
* export default defineFlags({
|
|
17
|
+
* provider: flagshipProvider({ binding: "FLAGS" }), // Cloudflare Flagship (binding mode)
|
|
18
|
+
* identify: (auth) => auth.userId ?? undefined, // default targetingKey
|
|
19
|
+
* });
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* Any OpenFeature provider works — Flagship is just the first-class default:
|
|
23
|
+
*
|
|
24
|
+
* ```ts
|
|
25
|
+
* export default defineFlags({ provider: (env) => new SomeOpenFeatureProvider(env.SOME_KEY) });
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
28
|
declare const defineFlags: (config: FlagsConfig) => FlagsDefinition;
|
|
29
29
|
/** True when a value is a {@link defineFlags} result (the runtime brand check). */
|
|
30
30
|
declare const isFlagsDefinition: (value: unknown) => value is FlagsDefinition;
|
|
31
|
-
/**
|
|
31
|
+
/**
|
|
32
|
+
* Per-request extras for `createFlags`. Everything static — the provider
|
|
33
|
+
* factory, `hooks`, `logger` — is read from the {@link FlagsDefinition} itself,
|
|
34
|
+
* so this carries only what the definition cannot know: the test/config
|
|
35
|
+
* provider override and the request's targeting key.
|
|
36
|
+
*/
|
|
32
37
|
interface CreateFlagsOptions {
|
|
33
|
-
/** OpenFeature hooks applied when the provider is bound. */
|
|
34
|
-
hooks?: Hook[];
|
|
35
|
-
/** Logger for the OpenFeature client. */
|
|
36
|
-
logger?: Logger;
|
|
37
|
-
/** Lazily constructs the OpenFeature provider from `env` (invoked once per isolate). */
|
|
38
|
-
provider: () => Provider;
|
|
39
38
|
/**
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
39
|
+
* Overrides the definition's provider (codegen's `config.flags` test seam).
|
|
40
|
+
* Returning `undefined` falls back to `definition.provider(env)`.
|
|
41
|
+
*/
|
|
42
|
+
provider?: () => Provider | undefined;
|
|
43
|
+
/**
|
|
44
|
+
* Default `targetingKey` for every evaluation (from `defineFlags({ identify })`).
|
|
45
|
+
* May be a thunk — codegen passes one wrapping the user's `identify` — so a
|
|
46
|
+
* throwing `identify` fails open to no `targetingKey` rather than escaping
|
|
47
|
+
* ctx construction and taking down the whole request.
|
|
48
|
+
*/
|
|
45
49
|
targetingKey?: (() => string | undefined) | string;
|
|
46
50
|
}
|
|
47
51
|
/**
|
|
48
|
-
* Builds the `ctx.flags` facade for a single request.
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
52
|
+
* Builds the `ctx.flags` facade for a single request. The provider, `hooks`,
|
|
53
|
+
* and `logger` come from `definition`; the client bind is memoized per
|
|
54
|
+
* (`definition`, `env`) pair — both stable object identities for the isolate's
|
|
55
|
+
* lifetime — and each pair owns its own OpenFeature domain, so a pair always
|
|
56
|
+
* evaluates against the provider it was configured with instead of whichever
|
|
57
|
+
* bind happened first. The bind is additionally attempted at most ONCE per
|
|
58
|
+
* request, so a broken deployment costs one `initialize` and one log line
|
|
59
|
+
* rather than one per flag read. Evaluations resolve through the
|
|
60
|
+
* OpenFeature client (the SDK applies hooks and never throws — provider errors
|
|
61
|
+
* surface as the default value with an `errorCode`). The default `targetingKey`
|
|
62
|
+
* is merged under any per-call context, and identical evaluations within the
|
|
63
|
+
* request are memoized so repeated reads of the same flag are internally
|
|
64
|
+
* consistent and hit the provider once.
|
|
65
|
+
*/
|
|
66
|
+
declare const createFlags: (definition: FlagsDefinition, env: Record<string, unknown>, options?: CreateFlagsOptions) => LunoraFlags;
|
|
67
|
+
export { type CreateFlagsOptions, type FlagsConfig, type FlagsDefinition, type LunoraFlags, createFlags,
|
|
68
|
+
/**
|
|
69
|
+
* `@lunora/flags`
|
|
70
|
+
*
|
|
71
|
+
* OpenFeature-based feature flags for Lunora. `defineFlags` in `lunora/flags.ts`
|
|
72
|
+
* configures an OpenFeature provider (Cloudflare Flagship by default, any
|
|
73
|
+
* provider pluggable); codegen wires `ctx.flags` onto every handler ctx from it.
|
|
74
|
+
*
|
|
75
|
+
* - `@lunora/flags` — `defineFlags`, the `createFlags` ctx facade, and types.
|
|
76
|
+
* - `@lunora/flags/providers/flagship` — the first-class Cloudflare Flagship provider.
|
|
77
|
+
* - `@lunora/flags/providers/memory` — a static, in-memory provider (tests/local/simple apps, zero extra deps).
|
|
78
|
+
* - `@lunora/flags/providers/env` — reads flags from the Worker `env` vars/secrets (zero deps).
|
|
79
|
+
* - `@lunora/flags/web` — optional browser OpenFeature provider (escape hatch).
|
|
80
|
+
* @packageDocumentation
|
|
81
|
+
*/
|
|
82
|
+
defineFlags,
|
|
83
|
+
/**
|
|
84
|
+
* `@lunora/flags`
|
|
85
|
+
*
|
|
86
|
+
* OpenFeature-based feature flags for Lunora. `defineFlags` in `lunora/flags.ts`
|
|
87
|
+
* configures an OpenFeature provider (Cloudflare Flagship by default, any
|
|
88
|
+
* provider pluggable); codegen wires `ctx.flags` onto every handler ctx from it.
|
|
89
|
+
*
|
|
90
|
+
* - `@lunora/flags` — `defineFlags`, the `createFlags` ctx facade, and types.
|
|
91
|
+
* - `@lunora/flags/providers/flagship` — the first-class Cloudflare Flagship provider.
|
|
92
|
+
* - `@lunora/flags/providers/memory` — a static, in-memory provider (tests/local/simple apps, zero extra deps).
|
|
93
|
+
* - `@lunora/flags/providers/env` — reads flags from the Worker `env` vars/secrets (zero deps).
|
|
94
|
+
* - `@lunora/flags/web` — optional browser OpenFeature provider (escape hatch).
|
|
95
|
+
* @packageDocumentation
|
|
96
|
+
*/
|
|
97
|
+
isFlagsDefinition };
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
export { createFlags } from './packem_shared/createFlags-CEbcdnqZ.mjs';
|
|
1
|
+
import{defineFlags as o,isFlagsDefinition as r}from"./packem_shared/defineFlags-BZhoR_h7.mjs";import{createFlags as f}from"./packem_shared/createFlags-D8e_hTbh.mjs";export{f as createFlags,o as defineFlags,r as isFlagsDefinition};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{LunoraError as D}from"@lunora/errors";import{OpenFeature as E,ErrorCode as $}from"@openfeature/server-sdk";const A=/["\\\u0000-\u001F\uD800-\uDFFF]/,j=e=>A.test(e)?JSON.stringify(e):`"${e}"`,h=e=>{if(e===void 0)return"null";if(typeof e=="bigint")throw new TypeError("stableStringify: cannot use a bigint in a stable JSON cache key — pass it as a string, or use stableWireKey");if(typeof e=="number"){if(Number.isNaN(e))return"nan";if(e===1/0)return"inf";if(e===-1/0)return"-inf";if(Object.is(e,-0))return"-0"}if(typeof e=="string")return j(e);if(e===null||typeof e!="object")return JSON.stringify(e);if(Array.isArray(e)){let i="[";for(let l=0;l<e.length;l++)l>0&&(i+=","),i+=h(e[l]);return i+"]"}const c=Object.getPrototypeOf(e);if(c!==null&&c!==Object.prototype){const i=e.constructor?.name??"value";throw new TypeError(`stableStringify: cannot use a ${i} in a stable JSON cache key — only plain objects, arrays, and JSON primitives are supported (wire-typed values key via stableWireKey)`)}const a=e,s=Object.keys(a).sort();let n="{",u=!0;for(const i of s){const l=a[i];l!==void 0&&(u?u=!1:n+=",",n+=j(i),n+=":",n+=h(l))}return n+"}"},S="lunora";let O=new WeakMap,p=0;const C={},M=e=>Object.keys(e).length===0?C:e,T=e=>typeof e?.then=="function",W=(e,c)=>{const a=`@lunora/flags: could not bind the OpenFeature provider — every flag read falls back to its caller-supplied default until this is fixed: ${c instanceof Error?c.message:String(c)}`;if(e)try{const s=e.error(a);T(s)&&s.catch(()=>{});return}catch{}console.error(a)},J=(e,c,a)=>{const s=M(c);let n=O.get(e);n===void 0&&(n=new WeakMap,O.set(e,n));let u=n.get(s);if(u===void 0&&(p+=1,u={domain:p===1?S:`${S}-${String(p)}`},n.set(s,u)),u.client!==void 0)return u.client;const i=u,{domain:l}=i,{hooks:b,logger:d}=e,g=(async()=>{await E.setProviderAndWait(l,a());const t=E.getClient(l);return d&&t.setLogger(d),b&&b.length>0&&t.addHooks(...b),t})();return i.client=g,g.catch(t=>{i.client===g&&(i.client=void 0),W(d,t)}).catch(()=>{}),g},B=async()=>{O=new WeakMap,p=0,await E.clearProviders()},L=(e,c,a,s)=>{const n=h([e,c,a]);return Object.keys(s).length===0?`${n}:{}`:`${n}:${h(s)}`},P=(e,c,a,s,n)=>{switch(c){case"boolean":return e.getBooleanDetails(a,s,n);case"number":return e.getNumberDetails(a,s,n);case"object":return e.getObjectDetails(a,s,n);case"string":return e.getStringDetails(a,s,n);default:throw new D("INTERNAL",`createFlags: unknown flag type "${c}"`)}},_=(e,c,a={})=>{const{provider:s,targetingKey:n}=a,u=()=>s?.()??e.provider(c);let i;try{i=typeof n=="function"?n():n}catch{i=void 0}const l=new Map;let b;const d=()=>(b??=J(e,c,u),b),g=(t,r,o,f)=>{const k=i===void 0?{...f}:{targetingKey:i,...f},F=y=>({errorCode:$.GENERAL,errorMessage:y instanceof Error?y.message:String(y),flagKey:r,flagMetadata:{},reason:"ERROR",value:o}),w=()=>d().then(y=>P(y,t,r,o,k)).catch(F);let m;try{m=L(t,r,o,k)}catch{return w()}const v=l.get(m);if(v)return v;const N=w();return l.set(m,N),N};return{boolean:(t,r,o)=>g("boolean",t,r,o).then(f=>f.value),details:{boolean:(t,r,o)=>g("boolean",t,r,o),number:(t,r,o)=>g("number",t,r,o),object:(t,r,o)=>g("object",t,r,o),string:(t,r,o)=>g("string",t,r,o)},number:(t,r,o)=>g("number",t,r,o).then(f=>f.value),object:(t,r,o)=>g("object",t,r,o).then(f=>f.value),string:(t,r,o)=>g("string",t,r,o).then(f=>f.value)}};export{_ as createFlags,B as resetFlags};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const r=e=>{if(typeof e.provider!="function")throw new TypeError('defineFlags: `provider` must be a function `(env) => Provider` (e.g. flagshipProvider({ binding: "FLAGS" }))');if(e.identify!==void 0&&typeof e.identify!="function")throw new TypeError("defineFlags: `identify` must be a function `(auth) => string | undefined` when provided");if(e.hooks!==void 0&&!Array.isArray(e.hooks))throw new TypeError("defineFlags: `hooks` must be an array of OpenFeature hooks when provided");return{...e,isLunoraFlags:!0}},i=e=>typeof e=="object"&&e!==null&&e.isLunoraFlags===!0;export{r as defineFlags,i as isFlagsDefinition};
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { Hook, Logger, Provider, EvaluationContext, EvaluationDetails, JsonValue } from '@openfeature/server-sdk';
|
|
2
2
|
/**
|
|
3
|
-
* The flag-evaluation surface spliced onto every Lunora `ctx` as `ctx.flags`
|
|
4
|
-
* (query / mutation / action) by codegen when an app wires `@lunora/flags`.
|
|
5
|
-
*
|
|
6
|
-
* Every method resolves through the configured OpenFeature provider and **never
|
|
7
|
-
* throws** — on a missing flag, type mismatch, provider error, or
|
|
8
|
-
* misconfiguration it resolves with the supplied `defaultValue`. Use the
|
|
9
|
-
* `details.*` variants when you need the full {@link EvaluationDetails} (reason,
|
|
10
|
-
* variant, `errorCode`, `errorMessage`) alongside the value.
|
|
11
|
-
*
|
|
12
|
-
* The optional per-call `context` is merged on top of the default targeting
|
|
13
|
-
* context (the `targetingKey` derived from `defineFlags({ identify })`), so a
|
|
14
|
-
* call can add or override targeting attributes for that single evaluation.
|
|
15
|
-
*/
|
|
3
|
+
* The flag-evaluation surface spliced onto every Lunora `ctx` as `ctx.flags`
|
|
4
|
+
* (query / mutation / action) by codegen when an app wires `@lunora/flags`.
|
|
5
|
+
*
|
|
6
|
+
* Every method resolves through the configured OpenFeature provider and **never
|
|
7
|
+
* throws** — on a missing flag, type mismatch, provider error, or
|
|
8
|
+
* misconfiguration it resolves with the supplied `defaultValue`. Use the
|
|
9
|
+
* `details.*` variants when you need the full {@link EvaluationDetails} (reason,
|
|
10
|
+
* variant, `errorCode`, `errorMessage`) alongside the value.
|
|
11
|
+
*
|
|
12
|
+
* The optional per-call `context` is merged on top of the default targeting
|
|
13
|
+
* context (the `targetingKey` derived from `defineFlags({ identify })`), so a
|
|
14
|
+
* call can add or override targeting attributes for that single evaluation.
|
|
15
|
+
*/
|
|
16
16
|
interface LunoraFlags {
|
|
17
17
|
/** Resolve a boolean flag (feature on/off). */
|
|
18
18
|
boolean: (flagKey: string, defaultValue: boolean, context?: EvaluationContext) => Promise<boolean>;
|
|
@@ -31,11 +31,11 @@ interface LunoraFlags {
|
|
|
31
31
|
string: (flagKey: string, defaultValue: string, context?: EvaluationContext) => Promise<string>;
|
|
32
32
|
}
|
|
33
33
|
/**
|
|
34
|
-
* The resolved request auth handed to {@link FlagsConfig.identify} so an app can
|
|
35
|
-
* derive the default OpenFeature `targetingKey` (usually the authenticated user
|
|
36
|
-
* id). This mirrors the `auth` shape the runtime already threads into other ctx
|
|
37
|
-
* helpers, so `identify` never needs the full ctx.
|
|
38
|
-
*/
|
|
34
|
+
* The resolved request auth handed to {@link FlagsConfig.identify} so an app can
|
|
35
|
+
* derive the default OpenFeature `targetingKey` (usually the authenticated user
|
|
36
|
+
* id). This mirrors the `auth` shape the runtime already threads into other ctx
|
|
37
|
+
* helpers, so `identify` never needs the full ctx.
|
|
38
|
+
*/
|
|
39
39
|
interface FlagsAuth {
|
|
40
40
|
/** The verified identity claims for the request, or `null` when anonymous. */
|
|
41
41
|
identity: Record<string, unknown> | null;
|
|
@@ -43,41 +43,41 @@ interface FlagsAuth {
|
|
|
43
43
|
userId: string | null;
|
|
44
44
|
}
|
|
45
45
|
/**
|
|
46
|
-
* Builds the OpenFeature {@link Provider} for the request from the Worker `env`.
|
|
47
|
-
*
|
|
48
|
-
* Receiving `env` (rather than a constructed provider) is what lets the Flagship
|
|
49
|
-
* binding provider read `env.FLAGS` at request time — matching the existing
|
|
50
|
-
* `config.ai?.(env)` thunk pattern. Construction is memoized per DO isolate, so
|
|
51
|
-
* this factory is invoked once per isolate, not once per request.
|
|
52
|
-
*/
|
|
46
|
+
* Builds the OpenFeature {@link Provider} for the request from the Worker `env`.
|
|
47
|
+
*
|
|
48
|
+
* Receiving `env` (rather than a constructed provider) is what lets the Flagship
|
|
49
|
+
* binding provider read `env.FLAGS` at request time — matching the existing
|
|
50
|
+
* `config.ai?.(env)` thunk pattern. Construction is memoized per DO isolate, so
|
|
51
|
+
* this factory is invoked once per isolate, not once per request.
|
|
52
|
+
*/
|
|
53
53
|
type FlagsProviderFactory = (env: Record<string, unknown>) => Provider;
|
|
54
54
|
/** Options accepted by `defineFlags`. */
|
|
55
55
|
interface FlagsConfig {
|
|
56
56
|
/**
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
* OpenFeature hooks run on every evaluation (logging, telemetry, …). Applied
|
|
58
|
+
* to the Lunora flags client when the provider is bound.
|
|
59
|
+
*/
|
|
60
60
|
hooks?: Hook[];
|
|
61
61
|
/**
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
62
|
+
* Derives the default OpenFeature `targetingKey` from the request auth. Most
|
|
63
|
+
* apps return the user id: `identify: (auth) => auth.userId ?? undefined`.
|
|
64
|
+
* A per-call `context.targetingKey` still overrides this.
|
|
65
|
+
*/
|
|
66
66
|
identify?: (auth: FlagsAuth) => string | undefined;
|
|
67
67
|
/** Logger for the OpenFeature client (provider + SDK diagnostics). */
|
|
68
68
|
logger?: Logger;
|
|
69
69
|
/**
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
70
|
+
* The OpenFeature provider factory. Use `flagshipProvider(...)` from
|
|
71
|
+
* `@lunora/flags/providers/flagship`, or return any OpenFeature provider:
|
|
72
|
+
* `provider: (env) => new LaunchDarklyProvider(env.LD_KEY)`.
|
|
73
|
+
*/
|
|
74
74
|
provider: FlagsProviderFactory;
|
|
75
75
|
}
|
|
76
76
|
/**
|
|
77
|
-
* A branded {@link FlagsConfig} produced by `defineFlags`. This is the
|
|
78
|
-
* default export of `lunora/flags.ts`; codegen imports it into the generated
|
|
79
|
-
* worker and wires `ctx.flags` from its `provider` / `identify`.
|
|
80
|
-
*/
|
|
77
|
+
* A branded {@link FlagsConfig} produced by `defineFlags`. This is the
|
|
78
|
+
* default export of `lunora/flags.ts`; codegen imports it into the generated
|
|
79
|
+
* worker and wires `ctx.flags` from its `provider` / `identify`.
|
|
80
|
+
*/
|
|
81
81
|
interface FlagsDefinition extends FlagsConfig {
|
|
82
82
|
/** Runtime brand used by `isFlagsDefinition` and codegen discovery. */
|
|
83
83
|
readonly isLunoraFlags: true;
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { Hook, Logger, Provider, EvaluationContext, EvaluationDetails, JsonValue } from '@openfeature/server-sdk';
|
|
2
2
|
/**
|
|
3
|
-
* The flag-evaluation surface spliced onto every Lunora `ctx` as `ctx.flags`
|
|
4
|
-
* (query / mutation / action) by codegen when an app wires `@lunora/flags`.
|
|
5
|
-
*
|
|
6
|
-
* Every method resolves through the configured OpenFeature provider and **never
|
|
7
|
-
* throws** — on a missing flag, type mismatch, provider error, or
|
|
8
|
-
* misconfiguration it resolves with the supplied `defaultValue`. Use the
|
|
9
|
-
* `details.*` variants when you need the full {@link EvaluationDetails} (reason,
|
|
10
|
-
* variant, `errorCode`, `errorMessage`) alongside the value.
|
|
11
|
-
*
|
|
12
|
-
* The optional per-call `context` is merged on top of the default targeting
|
|
13
|
-
* context (the `targetingKey` derived from `defineFlags({ identify })`), so a
|
|
14
|
-
* call can add or override targeting attributes for that single evaluation.
|
|
15
|
-
*/
|
|
3
|
+
* The flag-evaluation surface spliced onto every Lunora `ctx` as `ctx.flags`
|
|
4
|
+
* (query / mutation / action) by codegen when an app wires `@lunora/flags`.
|
|
5
|
+
*
|
|
6
|
+
* Every method resolves through the configured OpenFeature provider and **never
|
|
7
|
+
* throws** — on a missing flag, type mismatch, provider error, or
|
|
8
|
+
* misconfiguration it resolves with the supplied `defaultValue`. Use the
|
|
9
|
+
* `details.*` variants when you need the full {@link EvaluationDetails} (reason,
|
|
10
|
+
* variant, `errorCode`, `errorMessage`) alongside the value.
|
|
11
|
+
*
|
|
12
|
+
* The optional per-call `context` is merged on top of the default targeting
|
|
13
|
+
* context (the `targetingKey` derived from `defineFlags({ identify })`), so a
|
|
14
|
+
* call can add or override targeting attributes for that single evaluation.
|
|
15
|
+
*/
|
|
16
16
|
interface LunoraFlags {
|
|
17
17
|
/** Resolve a boolean flag (feature on/off). */
|
|
18
18
|
boolean: (flagKey: string, defaultValue: boolean, context?: EvaluationContext) => Promise<boolean>;
|
|
@@ -31,11 +31,11 @@ interface LunoraFlags {
|
|
|
31
31
|
string: (flagKey: string, defaultValue: string, context?: EvaluationContext) => Promise<string>;
|
|
32
32
|
}
|
|
33
33
|
/**
|
|
34
|
-
* The resolved request auth handed to {@link FlagsConfig.identify} so an app can
|
|
35
|
-
* derive the default OpenFeature `targetingKey` (usually the authenticated user
|
|
36
|
-
* id). This mirrors the `auth` shape the runtime already threads into other ctx
|
|
37
|
-
* helpers, so `identify` never needs the full ctx.
|
|
38
|
-
*/
|
|
34
|
+
* The resolved request auth handed to {@link FlagsConfig.identify} so an app can
|
|
35
|
+
* derive the default OpenFeature `targetingKey` (usually the authenticated user
|
|
36
|
+
* id). This mirrors the `auth` shape the runtime already threads into other ctx
|
|
37
|
+
* helpers, so `identify` never needs the full ctx.
|
|
38
|
+
*/
|
|
39
39
|
interface FlagsAuth {
|
|
40
40
|
/** The verified identity claims for the request, or `null` when anonymous. */
|
|
41
41
|
identity: Record<string, unknown> | null;
|
|
@@ -43,41 +43,41 @@ interface FlagsAuth {
|
|
|
43
43
|
userId: string | null;
|
|
44
44
|
}
|
|
45
45
|
/**
|
|
46
|
-
* Builds the OpenFeature {@link Provider} for the request from the Worker `env`.
|
|
47
|
-
*
|
|
48
|
-
* Receiving `env` (rather than a constructed provider) is what lets the Flagship
|
|
49
|
-
* binding provider read `env.FLAGS` at request time — matching the existing
|
|
50
|
-
* `config.ai?.(env)` thunk pattern. Construction is memoized per DO isolate, so
|
|
51
|
-
* this factory is invoked once per isolate, not once per request.
|
|
52
|
-
*/
|
|
46
|
+
* Builds the OpenFeature {@link Provider} for the request from the Worker `env`.
|
|
47
|
+
*
|
|
48
|
+
* Receiving `env` (rather than a constructed provider) is what lets the Flagship
|
|
49
|
+
* binding provider read `env.FLAGS` at request time — matching the existing
|
|
50
|
+
* `config.ai?.(env)` thunk pattern. Construction is memoized per DO isolate, so
|
|
51
|
+
* this factory is invoked once per isolate, not once per request.
|
|
52
|
+
*/
|
|
53
53
|
type FlagsProviderFactory = (env: Record<string, unknown>) => Provider;
|
|
54
54
|
/** Options accepted by `defineFlags`. */
|
|
55
55
|
interface FlagsConfig {
|
|
56
56
|
/**
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
* OpenFeature hooks run on every evaluation (logging, telemetry, …). Applied
|
|
58
|
+
* to the Lunora flags client when the provider is bound.
|
|
59
|
+
*/
|
|
60
60
|
hooks?: Hook[];
|
|
61
61
|
/**
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
62
|
+
* Derives the default OpenFeature `targetingKey` from the request auth. Most
|
|
63
|
+
* apps return the user id: `identify: (auth) => auth.userId ?? undefined`.
|
|
64
|
+
* A per-call `context.targetingKey` still overrides this.
|
|
65
|
+
*/
|
|
66
66
|
identify?: (auth: FlagsAuth) => string | undefined;
|
|
67
67
|
/** Logger for the OpenFeature client (provider + SDK diagnostics). */
|
|
68
68
|
logger?: Logger;
|
|
69
69
|
/**
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
70
|
+
* The OpenFeature provider factory. Use `flagshipProvider(...)` from
|
|
71
|
+
* `@lunora/flags/providers/flagship`, or return any OpenFeature provider:
|
|
72
|
+
* `provider: (env) => new LaunchDarklyProvider(env.LD_KEY)`.
|
|
73
|
+
*/
|
|
74
74
|
provider: FlagsProviderFactory;
|
|
75
75
|
}
|
|
76
76
|
/**
|
|
77
|
-
* A branded {@link FlagsConfig} produced by `defineFlags`. This is the
|
|
78
|
-
* default export of `lunora/flags.ts`; codegen imports it into the generated
|
|
79
|
-
* worker and wires `ctx.flags` from its `provider` / `identify`.
|
|
80
|
-
*/
|
|
77
|
+
* A branded {@link FlagsConfig} produced by `defineFlags`. This is the
|
|
78
|
+
* default export of `lunora/flags.ts`; codegen imports it into the generated
|
|
79
|
+
* worker and wires `ctx.flags` from its `provider` / `identify`.
|
|
80
|
+
*/
|
|
81
81
|
interface FlagsDefinition extends FlagsConfig {
|
|
82
82
|
/** Runtime brand used by `isFlagsDefinition` and codegen discovery. */
|
|
83
83
|
readonly isLunoraFlags: true;
|