@lunora/flags 1.0.0-alpha.29 → 1.0.0-alpha.30

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
@@ -53,4 +53,34 @@ interface CreateFlagsOptions {
53
53
  * internally consistent and hit the provider once.
54
54
  */
55
55
  declare const createFlags: (options: CreateFlagsOptions) => LunoraFlags;
56
- export { type CreateFlagsOptions, type FlagsConfig, type FlagsDefinition, type LunoraFlags, createFlags, defineFlags, isFlagsDefinition };
56
+ export { type CreateFlagsOptions, type FlagsConfig, type FlagsDefinition, type LunoraFlags, createFlags,
57
+ /**
58
+ * `@lunora/flags`
59
+ *
60
+ * OpenFeature-based feature flags for Lunora. `defineFlags` in `lunora/flags.ts`
61
+ * configures an OpenFeature provider (Cloudflare Flagship by default, any
62
+ * provider pluggable); codegen wires `ctx.flags` onto every handler ctx from it.
63
+ *
64
+ * - `@lunora/flags` — `defineFlags`, the `createFlags` ctx facade, and types.
65
+ * - `@lunora/flags/providers/flagship` — the first-class Cloudflare Flagship provider.
66
+ * - `@lunora/flags/providers/memory` — a static, in-memory provider (tests/local/simple apps, zero extra deps).
67
+ * - `@lunora/flags/providers/env` — reads flags from the Worker `env` vars/secrets (zero deps).
68
+ * - `@lunora/flags/web` — optional browser OpenFeature provider (escape hatch).
69
+ * @packageDocumentation
70
+ */
71
+ defineFlags,
72
+ /**
73
+ * `@lunora/flags`
74
+ *
75
+ * OpenFeature-based feature flags for Lunora. `defineFlags` in `lunora/flags.ts`
76
+ * configures an OpenFeature provider (Cloudflare Flagship by default, any
77
+ * provider pluggable); codegen wires `ctx.flags` onto every handler ctx from it.
78
+ *
79
+ * - `@lunora/flags` — `defineFlags`, the `createFlags` ctx facade, and types.
80
+ * - `@lunora/flags/providers/flagship` — the first-class Cloudflare Flagship provider.
81
+ * - `@lunora/flags/providers/memory` — a static, in-memory provider (tests/local/simple apps, zero extra deps).
82
+ * - `@lunora/flags/providers/env` — reads flags from the Worker `env` vars/secrets (zero deps).
83
+ * - `@lunora/flags/web` — optional browser OpenFeature provider (escape hatch).
84
+ * @packageDocumentation
85
+ */
86
+ isFlagsDefinition };
package/dist/index.d.ts CHANGED
@@ -53,4 +53,34 @@ interface CreateFlagsOptions {
53
53
  * internally consistent and hit the provider once.
54
54
  */
55
55
  declare const createFlags: (options: CreateFlagsOptions) => LunoraFlags;
56
- export { type CreateFlagsOptions, type FlagsConfig, type FlagsDefinition, type LunoraFlags, createFlags, defineFlags, isFlagsDefinition };
56
+ export { type CreateFlagsOptions, type FlagsConfig, type FlagsDefinition, type LunoraFlags, createFlags,
57
+ /**
58
+ * `@lunora/flags`
59
+ *
60
+ * OpenFeature-based feature flags for Lunora. `defineFlags` in `lunora/flags.ts`
61
+ * configures an OpenFeature provider (Cloudflare Flagship by default, any
62
+ * provider pluggable); codegen wires `ctx.flags` onto every handler ctx from it.
63
+ *
64
+ * - `@lunora/flags` — `defineFlags`, the `createFlags` ctx facade, and types.
65
+ * - `@lunora/flags/providers/flagship` — the first-class Cloudflare Flagship provider.
66
+ * - `@lunora/flags/providers/memory` — a static, in-memory provider (tests/local/simple apps, zero extra deps).
67
+ * - `@lunora/flags/providers/env` — reads flags from the Worker `env` vars/secrets (zero deps).
68
+ * - `@lunora/flags/web` — optional browser OpenFeature provider (escape hatch).
69
+ * @packageDocumentation
70
+ */
71
+ defineFlags,
72
+ /**
73
+ * `@lunora/flags`
74
+ *
75
+ * OpenFeature-based feature flags for Lunora. `defineFlags` in `lunora/flags.ts`
76
+ * configures an OpenFeature provider (Cloudflare Flagship by default, any
77
+ * provider pluggable); codegen wires `ctx.flags` onto every handler ctx from it.
78
+ *
79
+ * - `@lunora/flags` — `defineFlags`, the `createFlags` ctx facade, and types.
80
+ * - `@lunora/flags/providers/flagship` — the first-class Cloudflare Flagship provider.
81
+ * - `@lunora/flags/providers/memory` — a static, in-memory provider (tests/local/simple apps, zero extra deps).
82
+ * - `@lunora/flags/providers/env` — reads flags from the Worker `env` vars/secrets (zero deps).
83
+ * - `@lunora/flags/web` — optional browser OpenFeature provider (escape hatch).
84
+ * @packageDocumentation
85
+ */
86
+ isFlagsDefinition };
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import{defineFlags as o,isFlagsDefinition as r}from"./packem_shared/defineFlags-BO1q5hLr.mjs";import{createFlags as f}from"./packem_shared/createFlags-KrGb1nlE.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-D2NuikJs.mjs";export{f as createFlags,o as defineFlags,r as isFlagsDefinition};
@@ -0,0 +1 @@
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};
@@ -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 +1 @@
1
- import{ErrorCode as c}from"@openfeature/server-sdk";const m=o=>o.replaceAll(/[^a-z0-9]+/gi,"_").toUpperCase(),g=new Set(["1","on","true","yes"]),d=new Set(["0","false","no","off"]),a=o=>Promise.resolve({reason:"STATIC",value:o}),l=o=>Promise.resolve({reason:"DEFAULT",value:o}),f=(o,u)=>Promise.resolve({errorCode:c.PARSE_ERROR,errorMessage:u,reason:"ERROR",value:o}),E=(o={})=>{const u=o.prefix??"FLAG_",s=o.name??(v=>u+m(v));return v=>{const i=r=>{const e=v[s(r)];if(e!=null)return typeof e=="string"?e:typeof e=="number"||typeof e=="boolean"?String(e):JSON.stringify(e)};return{metadata:{name:"lunora-env"},resolveBooleanEvaluation:(r,e)=>{const n=i(r);if(n===void 0)return l(e);const t=n.trim().toLowerCase();return g.has(t)?a(!0):d.has(t)?a(!1):f(e,`env flag "${r}" (${s(r)}) value is not a recognized boolean`)},resolveNumberEvaluation:(r,e)=>{const n=i(r);if(n===void 0)return l(e);const t=Number(n);return n.trim()===""||Number.isNaN(t)?f(e,`env flag "${r}" (${s(r)}) value is not a number`):a(t)},resolveObjectEvaluation:(r,e)=>{const n=i(r);if(n===void 0)return l(e);try{return a(JSON.parse(n))}catch(t){return f(e,`env flag "${r}" (${s(r)}) is not valid JSON: ${t instanceof Error?t.message:String(t)}`)}},resolveStringEvaluation:(r,e)=>{const n=i(r);return n===void 0?l(e):a(n)}}}};export{E as 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 +1 @@
1
- import{FlagshipServerProvider as a}from"@cloudflare/flagship/server";import{LunoraError as e}from"@lunora/errors";const s=n=>"binding"in n&&typeof n.binding=="string",f=n=>{if(s(n)){const{binding:i,...t}=n;return p=>{const d=p[i];if(d==null)throw new e("INTERNAL",`flagshipProvider: no binding "${i}" found on env. Add a \`flagship\` binding to wrangler.jsonc, e.g. { "flagship": [{ "binding": "${i}", "app_id": "your-app-id" }] }.`);return new a({binding:d,...t})}}const{appId:o,endpoint:r}=n;if(o===void 0&&r===void 0)throw new e("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(o!==void 0&&r!==void 0)throw new e("INTERNAL","flagshipProvider: `appId` and `endpoint` are mutually exclusive in HTTP mode — pass exactly one.");return()=>new a(n)};export{f as flagshipProvider};
1
+ import{FlagshipServerProvider as a}from"@cloudflare/flagship/server";import{LunoraError as r}from"@lunora/errors";const g=i=>"binding"in i&&typeof i.binding=="string",f=i=>{if(g(i)){const{binding:n,...t}=i;return p=>{const e=p[n];if(e==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:e,...t})}}const{appId:d,endpoint:o}=i;if(d===void 0&&o===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&&o!==void 0)throw new r("INTERNAL","flagshipProvider: `appId` and `endpoint` are mutually exclusive in HTTP mode — pass exactly one.");return()=>new a(i)};export{f as flagshipProvider};
@@ -1 +1 @@
1
- import{TypedInMemoryProvider as n}from"@openfeature/server-sdk";const d=t=>{const e={};for(const[r,a]of Object.entries(t))e[r]={defaultVariant:"default",disabled:!1,variants:{default:a}};const o=new n(e);return r=>o};export{d as 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/flags",
3
- "version": "1.0.0-alpha.29",
3
+ "version": "1.0.0-alpha.30",
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",
@@ -61,10 +61,10 @@
61
61
  },
62
62
  "dependencies": {
63
63
  "@lunora/errors": "1.0.0-alpha.21",
64
- "@openfeature/server-sdk": "^1.22.0"
64
+ "@openfeature/server-sdk": "^1.23.0"
65
65
  },
66
66
  "peerDependencies": {
67
- "@cloudflare/flagship": "^0.4.2",
67
+ "@cloudflare/flagship": ">=0.4.2 <1.0.0",
68
68
  "@openfeature/web-sdk": "^1.9.0"
69
69
  },
70
70
  "peerDependenciesMeta": {
@@ -1 +0,0 @@
1
- import{LunoraError as k}from"@lunora/errors";import{OpenFeature as f,ErrorCode as E}from"@openfeature/server-sdk";const S=(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 i=e,a=Object.keys(i).toSorted(S),s=[];for(const c of a){const b=i[c];b!==void 0&&s.push(`${JSON.stringify(c)}:${p(b)}`)}return`{${s.join(",")}}`},O="lunora";let u;const N=({hooks:e,logger:o,provider:i})=>(u===void 0&&(u=(async()=>{await f.setProviderAndWait(O,i());const a=f.getClient(O);return o&&a.setLogger(o),e&&e.length>0&&a.addHooks(...e),a})(),u.catch(()=>{u=void 0})),u),K=async()=>{u=void 0,await f.clearProviders()},$=(e,o,i,a)=>{const s=p([e,o,i]);return Object.keys(a).length===0?`${s}:{}`:`${s}:${p(a)}`},A=(e,o,i,a,s)=>{switch(o){case"boolean":return e.getBooleanDetails(i,a,s);case"number":return e.getNumberDetails(i,a,s);case"object":return e.getObjectDetails(i,a,s);case"string":return e.getStringDetails(i,a,s);default:throw new k("INTERNAL",`createFlags: unknown flag type "${o}"`)}},L=e=>{const{hooks:o,logger:i,provider:a,targetingKey:s}=e;let c;try{c=typeof s=="function"?s():s}catch{c=void 0}const b=new Map,g=(n,t,r,l)=>{const h=c===void 0?{...l}:{targetingKey:c,...l},w=y=>({errorCode:E.GENERAL,errorMessage:y instanceof Error?y.message:String(y),flagKey:t,flagMetadata:{},reason:"ERROR",value:r}),v=()=>N({hooks:o,logger:i,provider:a}).then(y=>A(y,n,t,r,h)).catch(w);let d;try{d=$(n,t,r,h)}catch{return v()}const j=b.get(d);if(j)return j;const m=v();return b.set(d,m),m};return{boolean:(n,t,r)=>g("boolean",n,t,r).then(l=>l.value),details:{boolean:(n,t,r)=>g("boolean",n,t,r),number:(n,t,r)=>g("number",n,t,r),object:(n,t,r)=>g("object",n,t,r),string:(n,t,r)=>g("string",n,t,r)},number:(n,t,r)=>g("number",n,t,r).then(l=>l.value),object:(n,t,r)=>g("object",n,t,r).then(l=>l.value),string:(n,t,r)=>g("string",n,t,r).then(l=>l.value)}};export{L as createFlags,K as resetFlags};
@@ -1 +0,0 @@
1
- const e=i=>{if(typeof i.provider!="function")throw new TypeError('defineFlags: `provider` must be a function `(env) => Provider` (e.g. flagshipProvider({ binding: "FLAGS" }))');if(i.identify!==void 0&&typeof i.identify!="function")throw new TypeError("defineFlags: `identify` must be a function `(auth) => string | undefined` when provided");if(i.hooks!==void 0&&!Array.isArray(i.hooks))throw new TypeError("defineFlags: `hooks` must be an array of OpenFeature hooks when provided");return{...i,isLunoraFlags:!0}},r=i=>typeof i=="object"&&i!==null&&i.isLunoraFlags===!0;export{e as defineFlags,r as isFlagsDefinition};