@lunora/flags 1.0.0-alpha.31 → 1.0.0-alpha.33

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/dist/index.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { a as FlagsConfig, b as FlagsDefinition, L as LunoraFlags } from "./packem_shared/types.d-Bek9WZpB.mjs";
2
2
  export type { c as FlagsAuth, F as FlagsProviderFactory } from "./packem_shared/types.d-Bek9WZpB.mjs";
3
- import { Hook, Logger, Provider } from '@openfeature/server-sdk';
3
+ import { Provider } from '@openfeature/server-sdk';
4
4
  export type { EvaluationContext, EvaluationDetails, Hook, JsonValue, Logger, Provider } from '@openfeature/server-sdk';
5
5
  /**
6
6
  * Declare the feature-flag provider for a Lunora app. Pure validation +
@@ -28,14 +28,18 @@ export type { EvaluationContext, EvaluationDetails, Hook, JsonValue, Logger, Pro
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
- /** Options for `createFlags`. Built by codegen from `defineFlags(...)`. */
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;
38
+ /**
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;
39
43
  /**
40
44
  * Default `targetingKey` for every evaluation (from `defineFlags({ identify })`).
41
45
  * May be a thunk — codegen passes one wrapping the user's `identify` — so a
@@ -45,14 +49,19 @@ interface CreateFlagsOptions {
45
49
  targetingKey?: (() => string | undefined) | string;
46
50
  }
47
51
  /**
48
- * Builds the `ctx.flags` facade for a single request. Evaluations resolve
49
- * through the OpenFeature client (the SDK applies hooks and never throws —
50
- * provider errors surface as the default value with an `errorCode`). The default
51
- * `targetingKey` is merged under any per-call context, and identical evaluations
52
- * within the request are memoized so repeated reads of the same flag are
53
- * internally consistent and hit the provider once.
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. Evaluations resolve through the
58
+ * OpenFeature client (the SDK applies hooks and never throws — provider errors
59
+ * surface as the default value with an `errorCode`). The default `targetingKey`
60
+ * is merged under any per-call context, and identical evaluations within the
61
+ * request are memoized so repeated reads of the same flag are internally
62
+ * consistent and hit the provider once.
54
63
  */
55
- declare const createFlags: (options: CreateFlagsOptions) => LunoraFlags;
64
+ declare const createFlags: (definition: FlagsDefinition, env: Record<string, unknown>, options?: CreateFlagsOptions) => LunoraFlags;
56
65
  export { type CreateFlagsOptions, type FlagsConfig, type FlagsDefinition, type LunoraFlags, createFlags,
57
66
  /**
58
67
  * `@lunora/flags`
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { a as FlagsConfig, b as FlagsDefinition, L as LunoraFlags } from "./packem_shared/types.d-Bek9WZpB.js";
2
2
  export type { c as FlagsAuth, F as FlagsProviderFactory } from "./packem_shared/types.d-Bek9WZpB.js";
3
- import { Hook, Logger, Provider } from '@openfeature/server-sdk';
3
+ import { Provider } from '@openfeature/server-sdk';
4
4
  export type { EvaluationContext, EvaluationDetails, Hook, JsonValue, Logger, Provider } from '@openfeature/server-sdk';
5
5
  /**
6
6
  * Declare the feature-flag provider for a Lunora app. Pure validation +
@@ -28,14 +28,18 @@ export type { EvaluationContext, EvaluationDetails, Hook, JsonValue, Logger, Pro
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
- /** Options for `createFlags`. Built by codegen from `defineFlags(...)`. */
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;
38
+ /**
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;
39
43
  /**
40
44
  * Default `targetingKey` for every evaluation (from `defineFlags({ identify })`).
41
45
  * May be a thunk — codegen passes one wrapping the user's `identify` — so a
@@ -45,14 +49,19 @@ interface CreateFlagsOptions {
45
49
  targetingKey?: (() => string | undefined) | string;
46
50
  }
47
51
  /**
48
- * Builds the `ctx.flags` facade for a single request. Evaluations resolve
49
- * through the OpenFeature client (the SDK applies hooks and never throws —
50
- * provider errors surface as the default value with an `errorCode`). The default
51
- * `targetingKey` is merged under any per-call context, and identical evaluations
52
- * within the request are memoized so repeated reads of the same flag are
53
- * internally consistent and hit the provider once.
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. Evaluations resolve through the
58
+ * OpenFeature client (the SDK applies hooks and never throws — provider errors
59
+ * surface as the default value with an `errorCode`). The default `targetingKey`
60
+ * is merged under any per-call context, and identical evaluations within the
61
+ * request are memoized so repeated reads of the same flag are internally
62
+ * consistent and hit the provider once.
54
63
  */
55
- declare const createFlags: (options: CreateFlagsOptions) => LunoraFlags;
64
+ declare const createFlags: (definition: FlagsDefinition, env: Record<string, unknown>, options?: CreateFlagsOptions) => LunoraFlags;
56
65
  export { type CreateFlagsOptions, type FlagsConfig, type FlagsDefinition, type LunoraFlags, createFlags,
57
66
  /**
58
67
  * `@lunora/flags`
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import{defineFlags as o,isFlagsDefinition as r}from"./packem_shared/defineFlags-BZhoR_h7.mjs";import{createFlags as f}from"./packem_shared/createFlags-D2NuikJs.mjs";export{f as createFlags,o as defineFlags,r as isFlagsDefinition};
1
+ import{defineFlags as o,isFlagsDefinition as r}from"./packem_shared/defineFlags-BZhoR_h7.mjs";import{createFlags as f}from"./packem_shared/createFlags-C4kEEC0F.mjs";export{f as createFlags,o as defineFlags,r as isFlagsDefinition};
@@ -0,0 +1 @@
1
+ import{LunoraError as E}from"@lunora/errors";import{OpenFeature as m,ErrorCode as S}from"@openfeature/server-sdk";const $=(e,i)=>e<i?-1:e>i?1:0,f=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(e===null||typeof e!="object")return JSON.stringify(e);if(Array.isArray(e))return`[${e.map(s=>f(s)).join(",")}]`;const i=Object.getPrototypeOf(e);if(i!==null&&i!==Object.prototype){const s=e.constructor?.name??"value";throw new TypeError(`stableStringify: cannot use a ${s} in a stable JSON cache key — only plain objects, arrays, and JSON primitives are supported (wire-typed values key via stableWireKey)`)}const a=e,c=Object.keys(a).toSorted($),o=[];for(const s of c){const g=a[s];g!==void 0&&o.push(`${JSON.stringify(s)}:${f(g)}`)}return`{${o.join(",")}}`},v="lunora";let h=new WeakMap,p=0;const M={},A=e=>Object.keys(e).length===0?M:e,C=(e,i,a)=>{const c=A(i);let o=h.get(e);o===void 0&&(o=new WeakMap,h.set(e,o));let s=o.get(c);if(s===void 0&&(p+=1,s={domain:p===1?v:`${v}-${String(p)}`},o.set(c,s)),s.client!==void 0)return s.client;const g=s,{domain:u}=g,{hooks:b,logger:n}=e,t=(async()=>{await m.setProviderAndWait(u,a());const r=m.getClient(u);return n&&r.setLogger(n),b&&b.length>0&&r.addHooks(...b),r})();return g.client=t,t.catch(()=>{g.client===t&&(g.client=void 0)}),t},J=async()=>{h=new WeakMap,p=0,await m.clearProviders()},D=(e,i,a,c)=>{const o=f([e,i,a]);return Object.keys(c).length===0?`${o}:{}`:`${o}:${f(c)}`},F=(e,i,a,c,o)=>{switch(i){case"boolean":return e.getBooleanDetails(a,c,o);case"number":return e.getNumberDetails(a,c,o);case"object":return e.getObjectDetails(a,c,o);case"string":return e.getStringDetails(a,c,o);default:throw new E("INTERNAL",`createFlags: unknown flag type "${i}"`)}},L=(e,i,a={})=>{const{provider:c,targetingKey:o}=a,s=()=>c?.()??e.provider(i);let g;try{g=typeof o=="function"?o():o}catch{g=void 0}const u=new Map,b=(n,t,r,y)=>{const O=g===void 0?{...y}:{targetingKey:g,...y},N=l=>({errorCode:S.GENERAL,errorMessage:l instanceof Error?l.message:String(l),flagKey:t,flagMetadata:{},reason:"ERROR",value:r}),j=()=>C(e,i,s).then(l=>F(l,n,t,r,O)).catch(N);let d;try{d=D(n,t,r,O)}catch{return j()}const w=u.get(d);if(w)return w;const k=j();return u.set(d,k),k};return{boolean:(n,t,r)=>b("boolean",n,t,r).then(y=>y.value),details:{boolean:(n,t,r)=>b("boolean",n,t,r),number:(n,t,r)=>b("number",n,t,r),object:(n,t,r)=>b("object",n,t,r),string:(n,t,r)=>b("string",n,t,r)},number:(n,t,r)=>b("number",n,t,r).then(y=>y.value),object:(n,t,r)=>b("object",n,t,r).then(y=>y.value),string:(n,t,r)=>b("string",n,t,r).then(y=>y.value)}};export{L as createFlags,J as resetFlags};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/flags",
3
- "version": "1.0.0-alpha.31",
3
+ "version": "1.0.0-alpha.33",
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",
@@ -1 +0,0 @@
1
- import{LunoraError as S}from"@lunora/errors";import{OpenFeature as f,ErrorCode as N}from"@openfeature/server-sdk";const k=(e,o)=>e<o?-1:e>o?1:0,p=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(e===null||typeof e!="object")return JSON.stringify(e);if(Array.isArray(e))return`[${e.map(c=>p(c)).join(",")}]`;const o=Object.getPrototypeOf(e);if(o!==null&&o!==Object.prototype){const c=e.constructor?.name??"value";throw new TypeError(`stableStringify: cannot use a ${c} 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).toSorted(k),i=[];for(const c of s){const y=a[c];y!==void 0&&i.push(`${JSON.stringify(c)}:${p(y)}`)}return`{${i.join(",")}}`},v="lunora";let u;const E=({hooks:e,logger:o,provider:a})=>(u===void 0&&(u=(async()=>{await f.setProviderAndWait(v,a());const s=f.getClient(v);return o&&s.setLogger(o),e&&e.length>0&&s.addHooks(...e),s})(),u.catch(()=>{u=void 0})),u),J=async()=>{u=void 0,await f.clearProviders()},$=(e,o,a,s)=>{const i=p([e,o,a]);return Object.keys(s).length===0?`${i}:{}`:`${i}:${p(s)}`},A=(e,o,a,s,i)=>{switch(o){case"boolean":return e.getBooleanDetails(a,s,i);case"number":return e.getNumberDetails(a,s,i);case"object":return e.getObjectDetails(a,s,i);case"string":return e.getStringDetails(a,s,i);default:throw new S("INTERNAL",`createFlags: unknown flag type "${o}"`)}},R=e=>{const{hooks:o,logger:a,provider:s,targetingKey:i}=e;let c;try{c=typeof i=="function"?i():i}catch{c=void 0}const y=new Map,b=(n,t,r,g)=>{const m=c===void 0?{...g}:{targetingKey:c,...g},w=l=>({errorCode:N.GENERAL,errorMessage:l instanceof Error?l.message:String(l),flagKey:t,flagMetadata:{},reason:"ERROR",value:r}),h=()=>E({hooks:o,logger:a,provider:s}).then(l=>A(l,n,t,r,m)).catch(w);let d;try{d=$(n,t,r,m)}catch{return h()}const j=y.get(d);if(j)return j;const O=h();return y.set(d,O),O};return{boolean:(n,t,r)=>b("boolean",n,t,r).then(g=>g.value),details:{boolean:(n,t,r)=>b("boolean",n,t,r),number:(n,t,r)=>b("number",n,t,r),object:(n,t,r)=>b("object",n,t,r),string:(n,t,r)=>b("string",n,t,r)},number:(n,t,r)=>b("number",n,t,r).then(g=>g.value),object:(n,t,r)=>b("object",n,t,r).then(g=>g.value),string:(n,t,r)=>b("string",n,t,r).then(g=>g.value)}};export{R as createFlags,J as resetFlags};