@quonfig/openfeature-node 0.0.5 → 0.0.7
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/README.md +21 -25
- package/dist/index.cjs +24 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +24 -5
- package/dist/index.js.map +1 -1
- package/package.json +8 -4
package/README.md
CHANGED
|
@@ -39,43 +39,39 @@ const timeout = await client.getNumberValue("request-timeout-ms", 5000);
|
|
|
39
39
|
const allowedPlans = await client.getObjectValue("allowed-plans", []);
|
|
40
40
|
|
|
41
41
|
// With evaluation context (per-request)
|
|
42
|
-
const isProFeatureEnabled = await client.getBooleanValue(
|
|
43
|
-
"
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
"user.plan": "pro",
|
|
48
|
-
"org.tier": "enterprise",
|
|
49
|
-
}
|
|
50
|
-
);
|
|
42
|
+
const isProFeatureEnabled = await client.getBooleanValue("pro-feature", false, {
|
|
43
|
+
targetingKey: "user-123", // maps to user.id by default
|
|
44
|
+
"user.plan": "pro",
|
|
45
|
+
"org.tier": "enterprise",
|
|
46
|
+
});
|
|
51
47
|
```
|
|
52
48
|
|
|
53
49
|
## Context mapping
|
|
54
50
|
|
|
55
|
-
OpenFeature context is flat; Quonfig context is nested by namespace. This provider
|
|
56
|
-
|
|
51
|
+
OpenFeature context is flat; Quonfig context is nested by namespace. This provider maps between them
|
|
52
|
+
using dot-notation:
|
|
57
53
|
|
|
58
|
-
| OpenFeature context key | Quonfig namespace | Quonfig property
|
|
59
|
-
|
|
60
|
-
| `targetingKey`
|
|
61
|
-
| `"user.email"`
|
|
62
|
-
| `"org.tier"`
|
|
63
|
-
| `"country"` (no dot)
|
|
64
|
-
| `"user.ip.address"`
|
|
54
|
+
| OpenFeature context key | Quonfig namespace | Quonfig property |
|
|
55
|
+
| ----------------------- | ----------------- | --------------------------------------------- |
|
|
56
|
+
| `targetingKey` | `user` | `id` (configurable via `targetingKeyMapping`) |
|
|
57
|
+
| `"user.email"` | `user` | `email` |
|
|
58
|
+
| `"org.tier"` | `org` | `tier` |
|
|
59
|
+
| `"country"` (no dot) | `""` (default) | `country` |
|
|
60
|
+
| `"user.ip.address"` | `user` | `ip.address` (first dot only) |
|
|
65
61
|
|
|
66
62
|
### Customizing targetingKey mapping
|
|
67
63
|
|
|
68
64
|
```typescript
|
|
69
65
|
const provider = new QuonfigProvider({
|
|
70
66
|
sdkKey: "qf_sk_...",
|
|
71
|
-
targetingKeyMapping: "account.id",
|
|
67
|
+
targetingKeyMapping: "account.id", // maps targetingKey to { account: { id: ... } }
|
|
72
68
|
});
|
|
73
69
|
```
|
|
74
70
|
|
|
75
71
|
## Accessing native SDK features
|
|
76
72
|
|
|
77
|
-
The `getClient()` escape hatch returns the underlying `@quonfig/node` client for
|
|
78
|
-
|
|
73
|
+
The `getClient()` escape hatch returns the underlying `@quonfig/node` client for features not
|
|
74
|
+
available in OpenFeature:
|
|
79
75
|
|
|
80
76
|
```typescript
|
|
81
77
|
const native = provider.getClient();
|
|
@@ -96,8 +92,8 @@ const rawConfig = native.rawConfig("my-flag");
|
|
|
96
92
|
|
|
97
93
|
## What you lose vs. the native SDK
|
|
98
94
|
|
|
99
|
-
OpenFeature is designed for feature flags, not general configuration. Some Quonfig
|
|
100
|
-
|
|
95
|
+
OpenFeature is designed for feature flags, not general configuration. Some Quonfig features require
|
|
96
|
+
the native `@quonfig/node` SDK:
|
|
101
97
|
|
|
102
98
|
1. **Log levels** -- `shouldLog()` and `logger()` are native-only.
|
|
103
99
|
2. **`string_list` configs** -- must be accessed via `getObjectValue()` and cast to `string[]`.
|
|
@@ -109,8 +105,8 @@ features require the native `@quonfig/node` SDK:
|
|
|
109
105
|
|
|
110
106
|
## Configuration changed events
|
|
111
107
|
|
|
112
|
-
The provider emits `ProviderEvents.ConfigurationChanged` when Quonfig pushes a
|
|
113
|
-
|
|
108
|
+
The provider emits `ProviderEvents.ConfigurationChanged` when Quonfig pushes a live config update
|
|
109
|
+
via SSE. Register a handler on the OpenFeature API or client:
|
|
114
110
|
|
|
115
111
|
```typescript
|
|
116
112
|
OpenFeature.addHandler(ProviderEvents.ConfigurationChanged, () => {
|
package/dist/index.cjs
CHANGED
|
@@ -114,23 +114,42 @@ var QuonfigProvider = class {
|
|
|
114
114
|
}
|
|
115
115
|
};
|
|
116
116
|
function toResolutionDetails(details, defaultValue) {
|
|
117
|
+
const common = {
|
|
118
|
+
variant: details.variant,
|
|
119
|
+
flagMetadata: details.flagMetadata
|
|
120
|
+
};
|
|
117
121
|
switch (details.reason) {
|
|
118
122
|
case "STATIC":
|
|
119
|
-
return {
|
|
123
|
+
return {
|
|
124
|
+
value: details.value,
|
|
125
|
+
reason: import_server_sdk.StandardResolutionReasons.STATIC,
|
|
126
|
+
...common
|
|
127
|
+
};
|
|
120
128
|
case "TARGETING_MATCH":
|
|
121
129
|
return {
|
|
122
130
|
value: details.value,
|
|
123
|
-
reason: import_server_sdk.StandardResolutionReasons.TARGETING_MATCH
|
|
131
|
+
reason: import_server_sdk.StandardResolutionReasons.TARGETING_MATCH,
|
|
132
|
+
...common
|
|
124
133
|
};
|
|
125
134
|
case "SPLIT":
|
|
126
|
-
return {
|
|
135
|
+
return {
|
|
136
|
+
value: details.value,
|
|
137
|
+
reason: import_server_sdk.StandardResolutionReasons.SPLIT,
|
|
138
|
+
...common
|
|
139
|
+
};
|
|
127
140
|
case "DEFAULT":
|
|
128
|
-
return {
|
|
141
|
+
return {
|
|
142
|
+
value: defaultValue,
|
|
143
|
+
reason: import_server_sdk.StandardResolutionReasons.DEFAULT,
|
|
144
|
+
...common
|
|
145
|
+
};
|
|
129
146
|
case "ERROR":
|
|
130
147
|
return {
|
|
131
148
|
value: defaultValue,
|
|
132
149
|
reason: import_server_sdk.StandardResolutionReasons.ERROR,
|
|
133
|
-
errorCode: toOFErrorCode(details.errorCode)
|
|
150
|
+
errorCode: toOFErrorCode(details.errorCode),
|
|
151
|
+
errorMessage: details.errorMessage,
|
|
152
|
+
...common
|
|
134
153
|
};
|
|
135
154
|
}
|
|
136
155
|
}
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/provider.ts","../src/context.ts","../src/errors.ts"],"sourcesContent":["export { QuonfigProvider } from \"./provider.js\";\nexport type { QuonfigProviderOptions } from \"./provider.js\";\nexport { mapContext } from \"./context.js\";\nexport { toErrorCode } from \"./errors.js\";\n","import {\n ErrorCode,\n EvaluationContext,\n JsonValue,\n Logger,\n OpenFeatureEventEmitter,\n Provider,\n ProviderEvents,\n ResolutionDetails,\n StandardResolutionReasons,\n} from \"@openfeature/server-sdk\";\nimport { EvaluationDetails, Quonfig, QuonfigOptions } from \"@quonfig/node\";\nimport { mapContext } from \"./context.js\";\n\nexport interface QuonfigProviderOptions extends Omit<QuonfigOptions, \"onConfigUpdate\"> {\n /**\n * Dot-notation path to map OpenFeature's `targetingKey` into Quonfig's nested context.\n * Defaults to \"user.id\".\n */\n targetingKeyMapping?: string;\n}\n\n/**\n * QuonfigProvider wraps the @quonfig/node native SDK and implements the\n * OpenFeature server-side Provider interface.\n *\n * Usage:\n * ```typescript\n * import { QuonfigProvider } from \"@quonfig/openfeature-node\";\n * import { OpenFeature } from \"@openfeature/server-sdk\";\n *\n * const provider = new QuonfigProvider({ sdkKey: \"qf_sk_...\" });\n * await OpenFeature.setProviderAndWait(provider);\n * const client = OpenFeature.getClient();\n * const enabled = await client.getBooleanValue(\"my-flag\", false);\n * ```\n */\nexport class QuonfigProvider implements Provider {\n readonly metadata = { name: \"quonfig\" } as const;\n readonly events = new OpenFeatureEventEmitter();\n readonly hooks = [];\n\n private readonly client: Quonfig;\n private readonly targetingKeyMapping: string;\n\n constructor(options: QuonfigProviderOptions) {\n this.targetingKeyMapping = options.targetingKeyMapping ?? \"user.id\";\n this.client = new Quonfig({\n ...options,\n onConfigUpdate: () => {\n this.events.emit(ProviderEvents.ConfigurationChanged, { flagsChanged: [] });\n },\n });\n }\n\n async initialize(_context?: EvaluationContext): Promise<void> {\n await this.client.init();\n }\n\n async shutdown(): Promise<void> {\n await this.client.close();\n }\n\n async resolveBooleanEvaluation(\n flagKey: string,\n defaultValue: boolean,\n context: EvaluationContext,\n _logger: Logger,\n ): Promise<ResolutionDetails<boolean>> {\n const mappedCtx = mapContext(context, this.targetingKeyMapping);\n const details = this.client.getBoolDetails(flagKey, mappedCtx);\n return toResolutionDetails(details, defaultValue);\n }\n\n async resolveStringEvaluation(\n flagKey: string,\n defaultValue: string,\n context: EvaluationContext,\n _logger: Logger,\n ): Promise<ResolutionDetails<string>> {\n const mappedCtx = mapContext(context, this.targetingKeyMapping);\n const details = this.client.getStringDetails(flagKey, mappedCtx);\n return toResolutionDetails(details, defaultValue);\n }\n\n async resolveNumberEvaluation(\n flagKey: string,\n defaultValue: number,\n context: EvaluationContext,\n _logger: Logger,\n ): Promise<ResolutionDetails<number>> {\n const mappedCtx = mapContext(context, this.targetingKeyMapping);\n const details = this.client.getNumberDetails(flagKey, mappedCtx);\n return toResolutionDetails(details, defaultValue);\n }\n\n async resolveObjectEvaluation<T extends JsonValue>(\n flagKey: string,\n defaultValue: T,\n context: EvaluationContext,\n _logger: Logger,\n ): Promise<ResolutionDetails<T>> {\n const mappedCtx = mapContext(context, this.targetingKeyMapping);\n // Try string_list first (returns string[]), fall back to JSON.\n const listDetails = this.client.getStringListDetails(flagKey, mappedCtx);\n if (\n listDetails.reason === \"STATIC\" ||\n listDetails.reason === \"TARGETING_MATCH\" ||\n listDetails.reason === \"SPLIT\"\n ) {\n return toResolutionDetails(\n listDetails as EvaluationDetails<unknown> as EvaluationDetails<T>,\n defaultValue,\n );\n }\n const jsonDetails = this.client.getJSONDetails(flagKey, mappedCtx);\n return toResolutionDetails(jsonDetails as EvaluationDetails<T>, defaultValue);\n }\n\n /**\n * Escape hatch: access the underlying @quonfig/node client for native-only features\n * (shouldLog, keys, rawConfig, etc.)\n */\n getClient(): Quonfig {\n return this.client;\n }\n}\n\n/**\n * Translate a Quonfig EvaluationDetails<T> into an OpenFeature ResolutionDetails<T>.\n *\n * Reason mapping (from the brief):\n * STATIC → StandardResolutionReasons.STATIC\n * TARGETING_MATCH → StandardResolutionReasons.TARGETING_MATCH\n * SPLIT → StandardResolutionReasons.SPLIT\n * DEFAULT → defaultValue + StandardResolutionReasons.DEFAULT\n * ERROR → defaultValue + ERROR + errorCode (FLAG_NOT_FOUND / TYPE_MISMATCH / GENERAL)\n */\nfunction toResolutionDetails<T>(\n details: EvaluationDetails<T>,\n defaultValue: T,\n): ResolutionDetails<T> {\n switch (details.reason) {\n case \"STATIC\":\n return { value: details.value as T, reason: StandardResolutionReasons.STATIC };\n case \"TARGETING_MATCH\":\n return {\n value: details.value as T,\n reason: StandardResolutionReasons.TARGETING_MATCH,\n };\n case \"SPLIT\":\n return { value: details.value as T, reason: StandardResolutionReasons.SPLIT };\n case \"DEFAULT\":\n return { value: defaultValue, reason: StandardResolutionReasons.DEFAULT };\n case \"ERROR\":\n return {\n value: defaultValue,\n reason: StandardResolutionReasons.ERROR,\n errorCode: toOFErrorCode(details.errorCode),\n };\n }\n}\n\nfunction toOFErrorCode(code: EvaluationDetails<unknown>[\"errorCode\"]): ErrorCode {\n switch (code) {\n case \"FLAG_NOT_FOUND\":\n return ErrorCode.FLAG_NOT_FOUND;\n case \"TYPE_MISMATCH\":\n return ErrorCode.TYPE_MISMATCH;\n case \"GENERAL\":\n default:\n return ErrorCode.GENERAL;\n }\n}\n","import type { EvaluationContext } from \"@openfeature/server-sdk\";\nimport type { Contexts, ContextValue } from \"@quonfig/node\";\n\n/**\n * Maps an OpenFeature flat EvaluationContext to Quonfig's nested Contexts format.\n *\n * Rules:\n * - `targetingKey` maps to the namespace+property specified by `targetingKeyMapping` (default: \"user.id\")\n * - Keys with a dot are split on the first dot: \"user.email\" -> namespace \"user\", key \"email\"\n * - Keys without a dot go to the default (empty-string) namespace: \"country\" -> { \"\": { country: ... } }\n * - Multi-dot keys split on first dot only: \"user.ip.address\" -> { user: { \"ip.address\": ... } }\n */\nexport function mapContext(\n ofContext: EvaluationContext,\n targetingKeyMapping = \"user.id\",\n): Contexts {\n const result: Record<string, Record<string, ContextValue>> = {};\n\n for (const [key, value] of Object.entries(ofContext)) {\n if (value === undefined) continue;\n\n // Cast to ContextValue -- OpenFeature allows arbitrary nesting but Quonfig\n // contexts accept primitives and string arrays. Callers should pass only\n // primitive or string[] values for keys they want evaluated.\n const ctxValue = value as ContextValue;\n\n if (key === \"targetingKey\") {\n const dotIdx = targetingKeyMapping.indexOf(\".\");\n const ns = dotIdx === -1 ? \"\" : targetingKeyMapping.slice(0, dotIdx);\n const prop = dotIdx === -1 ? targetingKeyMapping : targetingKeyMapping.slice(dotIdx + 1);\n result[ns] ??= {};\n result[ns][prop] = ctxValue;\n continue;\n }\n\n const dotIdx = key.indexOf(\".\");\n if (dotIdx === -1) {\n result[\"\"] ??= {};\n result[\"\"][key] = ctxValue;\n } else {\n const ns = key.slice(0, dotIdx);\n const prop = key.slice(dotIdx + 1);\n result[ns] ??= {};\n result[ns][prop] = ctxValue;\n }\n }\n\n return result;\n}\n","import { ErrorCode } from \"@openfeature/server-sdk\";\n\n/**\n * Maps a native SDK error to an OpenFeature ErrorCode.\n *\n * The native SDK throws Error instances with message strings. We map by inspecting\n * the lowercased message.\n */\nexport function toErrorCode(err: unknown): ErrorCode {\n const msg =\n err instanceof Error ? err.message.toLowerCase() : String(err).toLowerCase();\n\n if (\n msg.includes(\"not found\") ||\n msg.includes(\"flag not found\") ||\n msg.includes(\"no value found\") ||\n msg.includes(\"value found for key\")\n ) {\n return ErrorCode.FLAG_NOT_FOUND;\n }\n if (msg.includes(\"type mismatch\")) {\n return ErrorCode.TYPE_MISMATCH;\n }\n if (msg.includes(\"not initialized\") || msg.includes(\"provider not ready\")) {\n return ErrorCode.PROVIDER_NOT_READY;\n }\n return ErrorCode.GENERAL;\n}\n"],"mappings":"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","names":["dotIdx","import_server_sdk"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/provider.ts","../src/context.ts","../src/errors.ts"],"sourcesContent":["export { QuonfigProvider } from \"./provider.js\";\nexport type { QuonfigProviderOptions } from \"./provider.js\";\nexport { mapContext } from \"./context.js\";\nexport { toErrorCode } from \"./errors.js\";\n","import {\n ErrorCode,\n EvaluationContext,\n JsonValue,\n Logger,\n OpenFeatureEventEmitter,\n Provider,\n ProviderEvents,\n ResolutionDetails,\n StandardResolutionReasons,\n} from \"@openfeature/server-sdk\";\nimport { EvaluationDetails, Quonfig, QuonfigOptions } from \"@quonfig/node\";\nimport { mapContext } from \"./context.js\";\n\nexport interface QuonfigProviderOptions extends Omit<QuonfigOptions, \"onConfigUpdate\"> {\n /**\n * Dot-notation path to map OpenFeature's `targetingKey` into Quonfig's nested context.\n * Defaults to \"user.id\".\n */\n targetingKeyMapping?: string;\n}\n\n/**\n * QuonfigProvider wraps the @quonfig/node native SDK and implements the\n * OpenFeature server-side Provider interface.\n *\n * Usage:\n * ```typescript\n * import { QuonfigProvider } from \"@quonfig/openfeature-node\";\n * import { OpenFeature } from \"@openfeature/server-sdk\";\n *\n * const provider = new QuonfigProvider({ sdkKey: \"qf_sk_...\" });\n * await OpenFeature.setProviderAndWait(provider);\n * const client = OpenFeature.getClient();\n * const enabled = await client.getBooleanValue(\"my-flag\", false);\n * ```\n */\nexport class QuonfigProvider implements Provider {\n readonly metadata = { name: \"quonfig\" } as const;\n readonly events = new OpenFeatureEventEmitter();\n readonly hooks = [];\n\n private readonly client: Quonfig;\n private readonly targetingKeyMapping: string;\n\n constructor(options: QuonfigProviderOptions) {\n this.targetingKeyMapping = options.targetingKeyMapping ?? \"user.id\";\n this.client = new Quonfig({\n ...options,\n onConfigUpdate: () => {\n this.events.emit(ProviderEvents.ConfigurationChanged, { flagsChanged: [] });\n },\n });\n }\n\n async initialize(_context?: EvaluationContext): Promise<void> {\n await this.client.init();\n }\n\n async shutdown(): Promise<void> {\n await this.client.close();\n }\n\n async resolveBooleanEvaluation(\n flagKey: string,\n defaultValue: boolean,\n context: EvaluationContext,\n _logger: Logger\n ): Promise<ResolutionDetails<boolean>> {\n const mappedCtx = mapContext(context, this.targetingKeyMapping);\n const details = this.client.getBoolDetails(flagKey, mappedCtx);\n return toResolutionDetails(details, defaultValue);\n }\n\n async resolveStringEvaluation(\n flagKey: string,\n defaultValue: string,\n context: EvaluationContext,\n _logger: Logger\n ): Promise<ResolutionDetails<string>> {\n const mappedCtx = mapContext(context, this.targetingKeyMapping);\n const details = this.client.getStringDetails(flagKey, mappedCtx);\n return toResolutionDetails(details, defaultValue);\n }\n\n async resolveNumberEvaluation(\n flagKey: string,\n defaultValue: number,\n context: EvaluationContext,\n _logger: Logger\n ): Promise<ResolutionDetails<number>> {\n const mappedCtx = mapContext(context, this.targetingKeyMapping);\n const details = this.client.getNumberDetails(flagKey, mappedCtx);\n return toResolutionDetails(details, defaultValue);\n }\n\n async resolveObjectEvaluation<T extends JsonValue>(\n flagKey: string,\n defaultValue: T,\n context: EvaluationContext,\n _logger: Logger\n ): Promise<ResolutionDetails<T>> {\n const mappedCtx = mapContext(context, this.targetingKeyMapping);\n // Try string_list first (returns string[]), fall back to JSON.\n const listDetails = this.client.getStringListDetails(flagKey, mappedCtx);\n if (\n listDetails.reason === \"STATIC\" ||\n listDetails.reason === \"TARGETING_MATCH\" ||\n listDetails.reason === \"SPLIT\"\n ) {\n return toResolutionDetails(\n listDetails as EvaluationDetails<unknown> as EvaluationDetails<T>,\n defaultValue\n );\n }\n const jsonDetails = this.client.getJSONDetails(flagKey, mappedCtx);\n return toResolutionDetails(jsonDetails as EvaluationDetails<T>, defaultValue);\n }\n\n /**\n * Escape hatch: access the underlying @quonfig/node client for native-only features\n * (shouldLog, keys, rawConfig, etc.)\n */\n getClient(): Quonfig {\n return this.client;\n }\n}\n\n/**\n * Translate a Quonfig EvaluationDetails<T> into an OpenFeature ResolutionDetails<T>.\n *\n * Reason mapping (from the brief):\n * STATIC → StandardResolutionReasons.STATIC\n * TARGETING_MATCH → StandardResolutionReasons.TARGETING_MATCH\n * SPLIT → StandardResolutionReasons.SPLIT\n * DEFAULT → defaultValue + StandardResolutionReasons.DEFAULT\n * ERROR → defaultValue + ERROR + errorCode (FLAG_NOT_FOUND / TYPE_MISMATCH / GENERAL)\n */\nfunction toResolutionDetails<T>(\n details: EvaluationDetails<T>,\n defaultValue: T\n): ResolutionDetails<T> {\n const common = {\n variant: details.variant,\n flagMetadata: details.flagMetadata as ResolutionDetails<T>[\"flagMetadata\"],\n };\n switch (details.reason) {\n case \"STATIC\":\n return {\n value: details.value as T,\n reason: StandardResolutionReasons.STATIC,\n ...common,\n };\n case \"TARGETING_MATCH\":\n return {\n value: details.value as T,\n reason: StandardResolutionReasons.TARGETING_MATCH,\n ...common,\n };\n case \"SPLIT\":\n return {\n value: details.value as T,\n reason: StandardResolutionReasons.SPLIT,\n ...common,\n };\n case \"DEFAULT\":\n return {\n value: defaultValue,\n reason: StandardResolutionReasons.DEFAULT,\n ...common,\n };\n case \"ERROR\":\n return {\n value: defaultValue,\n reason: StandardResolutionReasons.ERROR,\n errorCode: toOFErrorCode(details.errorCode),\n errorMessage: details.errorMessage,\n ...common,\n };\n }\n}\n\nfunction toOFErrorCode(code: EvaluationDetails<unknown>[\"errorCode\"]): ErrorCode {\n switch (code) {\n case \"FLAG_NOT_FOUND\":\n return ErrorCode.FLAG_NOT_FOUND;\n case \"TYPE_MISMATCH\":\n return ErrorCode.TYPE_MISMATCH;\n case \"GENERAL\":\n default:\n return ErrorCode.GENERAL;\n }\n}\n","import type { EvaluationContext } from \"@openfeature/server-sdk\";\nimport type { Contexts, ContextValue } from \"@quonfig/node\";\n\n/**\n * Maps an OpenFeature flat EvaluationContext to Quonfig's nested Contexts format.\n *\n * Rules:\n * - `targetingKey` maps to the namespace+property specified by `targetingKeyMapping` (default: \"user.id\")\n * - Keys with a dot are split on the first dot: \"user.email\" -> namespace \"user\", key \"email\"\n * - Keys without a dot go to the default (empty-string) namespace: \"country\" -> { \"\": { country: ... } }\n * - Multi-dot keys split on first dot only: \"user.ip.address\" -> { user: { \"ip.address\": ... } }\n */\nexport function mapContext(\n ofContext: EvaluationContext,\n targetingKeyMapping = \"user.id\"\n): Contexts {\n const result: Record<string, Record<string, ContextValue>> = {};\n\n for (const [key, value] of Object.entries(ofContext)) {\n if (value === undefined) continue;\n\n // Cast to ContextValue -- OpenFeature allows arbitrary nesting but Quonfig\n // contexts accept primitives and string arrays. Callers should pass only\n // primitive or string[] values for keys they want evaluated.\n const ctxValue = value as ContextValue;\n\n if (key === \"targetingKey\") {\n const dotIdx = targetingKeyMapping.indexOf(\".\");\n const ns = dotIdx === -1 ? \"\" : targetingKeyMapping.slice(0, dotIdx);\n const prop = dotIdx === -1 ? targetingKeyMapping : targetingKeyMapping.slice(dotIdx + 1);\n result[ns] ??= {};\n result[ns][prop] = ctxValue;\n continue;\n }\n\n const dotIdx = key.indexOf(\".\");\n if (dotIdx === -1) {\n result[\"\"] ??= {};\n result[\"\"][key] = ctxValue;\n } else {\n const ns = key.slice(0, dotIdx);\n const prop = key.slice(dotIdx + 1);\n result[ns] ??= {};\n result[ns][prop] = ctxValue;\n }\n }\n\n return result;\n}\n","import { ErrorCode } from \"@openfeature/server-sdk\";\n\n/**\n * Maps a native SDK error to an OpenFeature ErrorCode.\n *\n * The native SDK throws Error instances with message strings. We map by inspecting\n * the lowercased message.\n */\nexport function toErrorCode(err: unknown): ErrorCode {\n const msg = err instanceof Error ? err.message.toLowerCase() : String(err).toLowerCase();\n\n if (\n msg.includes(\"not found\") ||\n msg.includes(\"flag not found\") ||\n msg.includes(\"no value found\") ||\n msg.includes(\"value found for key\")\n ) {\n return ErrorCode.FLAG_NOT_FOUND;\n }\n if (msg.includes(\"type mismatch\")) {\n return ErrorCode.TYPE_MISMATCH;\n }\n if (msg.includes(\"not initialized\") || msg.includes(\"provider not ready\")) {\n return ErrorCode.PROVIDER_NOT_READY;\n }\n return ErrorCode.GENERAL;\n}\n"],"mappings":"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","names":["dotIdx","import_server_sdk"]}
|
package/dist/index.js
CHANGED
|
@@ -91,23 +91,42 @@ var QuonfigProvider = class {
|
|
|
91
91
|
}
|
|
92
92
|
};
|
|
93
93
|
function toResolutionDetails(details, defaultValue) {
|
|
94
|
+
const common = {
|
|
95
|
+
variant: details.variant,
|
|
96
|
+
flagMetadata: details.flagMetadata
|
|
97
|
+
};
|
|
94
98
|
switch (details.reason) {
|
|
95
99
|
case "STATIC":
|
|
96
|
-
return {
|
|
100
|
+
return {
|
|
101
|
+
value: details.value,
|
|
102
|
+
reason: StandardResolutionReasons.STATIC,
|
|
103
|
+
...common
|
|
104
|
+
};
|
|
97
105
|
case "TARGETING_MATCH":
|
|
98
106
|
return {
|
|
99
107
|
value: details.value,
|
|
100
|
-
reason: StandardResolutionReasons.TARGETING_MATCH
|
|
108
|
+
reason: StandardResolutionReasons.TARGETING_MATCH,
|
|
109
|
+
...common
|
|
101
110
|
};
|
|
102
111
|
case "SPLIT":
|
|
103
|
-
return {
|
|
112
|
+
return {
|
|
113
|
+
value: details.value,
|
|
114
|
+
reason: StandardResolutionReasons.SPLIT,
|
|
115
|
+
...common
|
|
116
|
+
};
|
|
104
117
|
case "DEFAULT":
|
|
105
|
-
return {
|
|
118
|
+
return {
|
|
119
|
+
value: defaultValue,
|
|
120
|
+
reason: StandardResolutionReasons.DEFAULT,
|
|
121
|
+
...common
|
|
122
|
+
};
|
|
106
123
|
case "ERROR":
|
|
107
124
|
return {
|
|
108
125
|
value: defaultValue,
|
|
109
126
|
reason: StandardResolutionReasons.ERROR,
|
|
110
|
-
errorCode: toOFErrorCode(details.errorCode)
|
|
127
|
+
errorCode: toOFErrorCode(details.errorCode),
|
|
128
|
+
errorMessage: details.errorMessage,
|
|
129
|
+
...common
|
|
111
130
|
};
|
|
112
131
|
}
|
|
113
132
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/provider.ts","../src/context.ts","../src/errors.ts"],"sourcesContent":["import {\n ErrorCode,\n EvaluationContext,\n JsonValue,\n Logger,\n OpenFeatureEventEmitter,\n Provider,\n ProviderEvents,\n ResolutionDetails,\n StandardResolutionReasons,\n} from \"@openfeature/server-sdk\";\nimport { EvaluationDetails, Quonfig, QuonfigOptions } from \"@quonfig/node\";\nimport { mapContext } from \"./context.js\";\n\nexport interface QuonfigProviderOptions extends Omit<QuonfigOptions, \"onConfigUpdate\"> {\n /**\n * Dot-notation path to map OpenFeature's `targetingKey` into Quonfig's nested context.\n * Defaults to \"user.id\".\n */\n targetingKeyMapping?: string;\n}\n\n/**\n * QuonfigProvider wraps the @quonfig/node native SDK and implements the\n * OpenFeature server-side Provider interface.\n *\n * Usage:\n * ```typescript\n * import { QuonfigProvider } from \"@quonfig/openfeature-node\";\n * import { OpenFeature } from \"@openfeature/server-sdk\";\n *\n * const provider = new QuonfigProvider({ sdkKey: \"qf_sk_...\" });\n * await OpenFeature.setProviderAndWait(provider);\n * const client = OpenFeature.getClient();\n * const enabled = await client.getBooleanValue(\"my-flag\", false);\n * ```\n */\nexport class QuonfigProvider implements Provider {\n readonly metadata = { name: \"quonfig\" } as const;\n readonly events = new OpenFeatureEventEmitter();\n readonly hooks = [];\n\n private readonly client: Quonfig;\n private readonly targetingKeyMapping: string;\n\n constructor(options: QuonfigProviderOptions) {\n this.targetingKeyMapping = options.targetingKeyMapping ?? \"user.id\";\n this.client = new Quonfig({\n ...options,\n onConfigUpdate: () => {\n this.events.emit(ProviderEvents.ConfigurationChanged, { flagsChanged: [] });\n },\n });\n }\n\n async initialize(_context?: EvaluationContext): Promise<void> {\n await this.client.init();\n }\n\n async shutdown(): Promise<void> {\n await this.client.close();\n }\n\n async resolveBooleanEvaluation(\n flagKey: string,\n defaultValue: boolean,\n context: EvaluationContext,\n _logger: Logger,\n ): Promise<ResolutionDetails<boolean>> {\n const mappedCtx = mapContext(context, this.targetingKeyMapping);\n const details = this.client.getBoolDetails(flagKey, mappedCtx);\n return toResolutionDetails(details, defaultValue);\n }\n\n async resolveStringEvaluation(\n flagKey: string,\n defaultValue: string,\n context: EvaluationContext,\n _logger: Logger,\n ): Promise<ResolutionDetails<string>> {\n const mappedCtx = mapContext(context, this.targetingKeyMapping);\n const details = this.client.getStringDetails(flagKey, mappedCtx);\n return toResolutionDetails(details, defaultValue);\n }\n\n async resolveNumberEvaluation(\n flagKey: string,\n defaultValue: number,\n context: EvaluationContext,\n _logger: Logger,\n ): Promise<ResolutionDetails<number>> {\n const mappedCtx = mapContext(context, this.targetingKeyMapping);\n const details = this.client.getNumberDetails(flagKey, mappedCtx);\n return toResolutionDetails(details, defaultValue);\n }\n\n async resolveObjectEvaluation<T extends JsonValue>(\n flagKey: string,\n defaultValue: T,\n context: EvaluationContext,\n _logger: Logger,\n ): Promise<ResolutionDetails<T>> {\n const mappedCtx = mapContext(context, this.targetingKeyMapping);\n // Try string_list first (returns string[]), fall back to JSON.\n const listDetails = this.client.getStringListDetails(flagKey, mappedCtx);\n if (\n listDetails.reason === \"STATIC\" ||\n listDetails.reason === \"TARGETING_MATCH\" ||\n listDetails.reason === \"SPLIT\"\n ) {\n return toResolutionDetails(\n listDetails as EvaluationDetails<unknown> as EvaluationDetails<T>,\n defaultValue,\n );\n }\n const jsonDetails = this.client.getJSONDetails(flagKey, mappedCtx);\n return toResolutionDetails(jsonDetails as EvaluationDetails<T>, defaultValue);\n }\n\n /**\n * Escape hatch: access the underlying @quonfig/node client for native-only features\n * (shouldLog, keys, rawConfig, etc.)\n */\n getClient(): Quonfig {\n return this.client;\n }\n}\n\n/**\n * Translate a Quonfig EvaluationDetails<T> into an OpenFeature ResolutionDetails<T>.\n *\n * Reason mapping (from the brief):\n * STATIC → StandardResolutionReasons.STATIC\n * TARGETING_MATCH → StandardResolutionReasons.TARGETING_MATCH\n * SPLIT → StandardResolutionReasons.SPLIT\n * DEFAULT → defaultValue + StandardResolutionReasons.DEFAULT\n * ERROR → defaultValue + ERROR + errorCode (FLAG_NOT_FOUND / TYPE_MISMATCH / GENERAL)\n */\nfunction toResolutionDetails<T>(\n details: EvaluationDetails<T>,\n defaultValue: T,\n): ResolutionDetails<T> {\n switch (details.reason) {\n case \"STATIC\":\n return { value: details.value as T, reason: StandardResolutionReasons.STATIC };\n case \"TARGETING_MATCH\":\n return {\n value: details.value as T,\n reason: StandardResolutionReasons.TARGETING_MATCH,\n };\n case \"SPLIT\":\n return { value: details.value as T, reason: StandardResolutionReasons.SPLIT };\n case \"DEFAULT\":\n return { value: defaultValue, reason: StandardResolutionReasons.DEFAULT };\n case \"ERROR\":\n return {\n value: defaultValue,\n reason: StandardResolutionReasons.ERROR,\n errorCode: toOFErrorCode(details.errorCode),\n };\n }\n}\n\nfunction toOFErrorCode(code: EvaluationDetails<unknown>[\"errorCode\"]): ErrorCode {\n switch (code) {\n case \"FLAG_NOT_FOUND\":\n return ErrorCode.FLAG_NOT_FOUND;\n case \"TYPE_MISMATCH\":\n return ErrorCode.TYPE_MISMATCH;\n case \"GENERAL\":\n default:\n return ErrorCode.GENERAL;\n }\n}\n","import type { EvaluationContext } from \"@openfeature/server-sdk\";\nimport type { Contexts, ContextValue } from \"@quonfig/node\";\n\n/**\n * Maps an OpenFeature flat EvaluationContext to Quonfig's nested Contexts format.\n *\n * Rules:\n * - `targetingKey` maps to the namespace+property specified by `targetingKeyMapping` (default: \"user.id\")\n * - Keys with a dot are split on the first dot: \"user.email\" -> namespace \"user\", key \"email\"\n * - Keys without a dot go to the default (empty-string) namespace: \"country\" -> { \"\": { country: ... } }\n * - Multi-dot keys split on first dot only: \"user.ip.address\" -> { user: { \"ip.address\": ... } }\n */\nexport function mapContext(\n ofContext: EvaluationContext,\n targetingKeyMapping = \"user.id\",\n): Contexts {\n const result: Record<string, Record<string, ContextValue>> = {};\n\n for (const [key, value] of Object.entries(ofContext)) {\n if (value === undefined) continue;\n\n // Cast to ContextValue -- OpenFeature allows arbitrary nesting but Quonfig\n // contexts accept primitives and string arrays. Callers should pass only\n // primitive or string[] values for keys they want evaluated.\n const ctxValue = value as ContextValue;\n\n if (key === \"targetingKey\") {\n const dotIdx = targetingKeyMapping.indexOf(\".\");\n const ns = dotIdx === -1 ? \"\" : targetingKeyMapping.slice(0, dotIdx);\n const prop = dotIdx === -1 ? targetingKeyMapping : targetingKeyMapping.slice(dotIdx + 1);\n result[ns] ??= {};\n result[ns][prop] = ctxValue;\n continue;\n }\n\n const dotIdx = key.indexOf(\".\");\n if (dotIdx === -1) {\n result[\"\"] ??= {};\n result[\"\"][key] = ctxValue;\n } else {\n const ns = key.slice(0, dotIdx);\n const prop = key.slice(dotIdx + 1);\n result[ns] ??= {};\n result[ns][prop] = ctxValue;\n }\n }\n\n return result;\n}\n","import { ErrorCode } from \"@openfeature/server-sdk\";\n\n/**\n * Maps a native SDK error to an OpenFeature ErrorCode.\n *\n * The native SDK throws Error instances with message strings. We map by inspecting\n * the lowercased message.\n */\nexport function toErrorCode(err: unknown): ErrorCode {\n const msg =\n err instanceof Error ? err.message.toLowerCase() : String(err).toLowerCase();\n\n if (\n msg.includes(\"not found\") ||\n msg.includes(\"flag not found\") ||\n msg.includes(\"no value found\") ||\n msg.includes(\"value found for key\")\n ) {\n return ErrorCode.FLAG_NOT_FOUND;\n }\n if (msg.includes(\"type mismatch\")) {\n return ErrorCode.TYPE_MISMATCH;\n }\n if (msg.includes(\"not initialized\") || msg.includes(\"provider not ready\")) {\n return ErrorCode.PROVIDER_NOT_READY;\n }\n return ErrorCode.GENERAL;\n}\n"],"mappings":"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","names":["dotIdx","ErrorCode"]}
|
|
1
|
+
{"version":3,"sources":["../src/provider.ts","../src/context.ts","../src/errors.ts"],"sourcesContent":["import {\n ErrorCode,\n EvaluationContext,\n JsonValue,\n Logger,\n OpenFeatureEventEmitter,\n Provider,\n ProviderEvents,\n ResolutionDetails,\n StandardResolutionReasons,\n} from \"@openfeature/server-sdk\";\nimport { EvaluationDetails, Quonfig, QuonfigOptions } from \"@quonfig/node\";\nimport { mapContext } from \"./context.js\";\n\nexport interface QuonfigProviderOptions extends Omit<QuonfigOptions, \"onConfigUpdate\"> {\n /**\n * Dot-notation path to map OpenFeature's `targetingKey` into Quonfig's nested context.\n * Defaults to \"user.id\".\n */\n targetingKeyMapping?: string;\n}\n\n/**\n * QuonfigProvider wraps the @quonfig/node native SDK and implements the\n * OpenFeature server-side Provider interface.\n *\n * Usage:\n * ```typescript\n * import { QuonfigProvider } from \"@quonfig/openfeature-node\";\n * import { OpenFeature } from \"@openfeature/server-sdk\";\n *\n * const provider = new QuonfigProvider({ sdkKey: \"qf_sk_...\" });\n * await OpenFeature.setProviderAndWait(provider);\n * const client = OpenFeature.getClient();\n * const enabled = await client.getBooleanValue(\"my-flag\", false);\n * ```\n */\nexport class QuonfigProvider implements Provider {\n readonly metadata = { name: \"quonfig\" } as const;\n readonly events = new OpenFeatureEventEmitter();\n readonly hooks = [];\n\n private readonly client: Quonfig;\n private readonly targetingKeyMapping: string;\n\n constructor(options: QuonfigProviderOptions) {\n this.targetingKeyMapping = options.targetingKeyMapping ?? \"user.id\";\n this.client = new Quonfig({\n ...options,\n onConfigUpdate: () => {\n this.events.emit(ProviderEvents.ConfigurationChanged, { flagsChanged: [] });\n },\n });\n }\n\n async initialize(_context?: EvaluationContext): Promise<void> {\n await this.client.init();\n }\n\n async shutdown(): Promise<void> {\n await this.client.close();\n }\n\n async resolveBooleanEvaluation(\n flagKey: string,\n defaultValue: boolean,\n context: EvaluationContext,\n _logger: Logger\n ): Promise<ResolutionDetails<boolean>> {\n const mappedCtx = mapContext(context, this.targetingKeyMapping);\n const details = this.client.getBoolDetails(flagKey, mappedCtx);\n return toResolutionDetails(details, defaultValue);\n }\n\n async resolveStringEvaluation(\n flagKey: string,\n defaultValue: string,\n context: EvaluationContext,\n _logger: Logger\n ): Promise<ResolutionDetails<string>> {\n const mappedCtx = mapContext(context, this.targetingKeyMapping);\n const details = this.client.getStringDetails(flagKey, mappedCtx);\n return toResolutionDetails(details, defaultValue);\n }\n\n async resolveNumberEvaluation(\n flagKey: string,\n defaultValue: number,\n context: EvaluationContext,\n _logger: Logger\n ): Promise<ResolutionDetails<number>> {\n const mappedCtx = mapContext(context, this.targetingKeyMapping);\n const details = this.client.getNumberDetails(flagKey, mappedCtx);\n return toResolutionDetails(details, defaultValue);\n }\n\n async resolveObjectEvaluation<T extends JsonValue>(\n flagKey: string,\n defaultValue: T,\n context: EvaluationContext,\n _logger: Logger\n ): Promise<ResolutionDetails<T>> {\n const mappedCtx = mapContext(context, this.targetingKeyMapping);\n // Try string_list first (returns string[]), fall back to JSON.\n const listDetails = this.client.getStringListDetails(flagKey, mappedCtx);\n if (\n listDetails.reason === \"STATIC\" ||\n listDetails.reason === \"TARGETING_MATCH\" ||\n listDetails.reason === \"SPLIT\"\n ) {\n return toResolutionDetails(\n listDetails as EvaluationDetails<unknown> as EvaluationDetails<T>,\n defaultValue\n );\n }\n const jsonDetails = this.client.getJSONDetails(flagKey, mappedCtx);\n return toResolutionDetails(jsonDetails as EvaluationDetails<T>, defaultValue);\n }\n\n /**\n * Escape hatch: access the underlying @quonfig/node client for native-only features\n * (shouldLog, keys, rawConfig, etc.)\n */\n getClient(): Quonfig {\n return this.client;\n }\n}\n\n/**\n * Translate a Quonfig EvaluationDetails<T> into an OpenFeature ResolutionDetails<T>.\n *\n * Reason mapping (from the brief):\n * STATIC → StandardResolutionReasons.STATIC\n * TARGETING_MATCH → StandardResolutionReasons.TARGETING_MATCH\n * SPLIT → StandardResolutionReasons.SPLIT\n * DEFAULT → defaultValue + StandardResolutionReasons.DEFAULT\n * ERROR → defaultValue + ERROR + errorCode (FLAG_NOT_FOUND / TYPE_MISMATCH / GENERAL)\n */\nfunction toResolutionDetails<T>(\n details: EvaluationDetails<T>,\n defaultValue: T\n): ResolutionDetails<T> {\n const common = {\n variant: details.variant,\n flagMetadata: details.flagMetadata as ResolutionDetails<T>[\"flagMetadata\"],\n };\n switch (details.reason) {\n case \"STATIC\":\n return {\n value: details.value as T,\n reason: StandardResolutionReasons.STATIC,\n ...common,\n };\n case \"TARGETING_MATCH\":\n return {\n value: details.value as T,\n reason: StandardResolutionReasons.TARGETING_MATCH,\n ...common,\n };\n case \"SPLIT\":\n return {\n value: details.value as T,\n reason: StandardResolutionReasons.SPLIT,\n ...common,\n };\n case \"DEFAULT\":\n return {\n value: defaultValue,\n reason: StandardResolutionReasons.DEFAULT,\n ...common,\n };\n case \"ERROR\":\n return {\n value: defaultValue,\n reason: StandardResolutionReasons.ERROR,\n errorCode: toOFErrorCode(details.errorCode),\n errorMessage: details.errorMessage,\n ...common,\n };\n }\n}\n\nfunction toOFErrorCode(code: EvaluationDetails<unknown>[\"errorCode\"]): ErrorCode {\n switch (code) {\n case \"FLAG_NOT_FOUND\":\n return ErrorCode.FLAG_NOT_FOUND;\n case \"TYPE_MISMATCH\":\n return ErrorCode.TYPE_MISMATCH;\n case \"GENERAL\":\n default:\n return ErrorCode.GENERAL;\n }\n}\n","import type { EvaluationContext } from \"@openfeature/server-sdk\";\nimport type { Contexts, ContextValue } from \"@quonfig/node\";\n\n/**\n * Maps an OpenFeature flat EvaluationContext to Quonfig's nested Contexts format.\n *\n * Rules:\n * - `targetingKey` maps to the namespace+property specified by `targetingKeyMapping` (default: \"user.id\")\n * - Keys with a dot are split on the first dot: \"user.email\" -> namespace \"user\", key \"email\"\n * - Keys without a dot go to the default (empty-string) namespace: \"country\" -> { \"\": { country: ... } }\n * - Multi-dot keys split on first dot only: \"user.ip.address\" -> { user: { \"ip.address\": ... } }\n */\nexport function mapContext(\n ofContext: EvaluationContext,\n targetingKeyMapping = \"user.id\"\n): Contexts {\n const result: Record<string, Record<string, ContextValue>> = {};\n\n for (const [key, value] of Object.entries(ofContext)) {\n if (value === undefined) continue;\n\n // Cast to ContextValue -- OpenFeature allows arbitrary nesting but Quonfig\n // contexts accept primitives and string arrays. Callers should pass only\n // primitive or string[] values for keys they want evaluated.\n const ctxValue = value as ContextValue;\n\n if (key === \"targetingKey\") {\n const dotIdx = targetingKeyMapping.indexOf(\".\");\n const ns = dotIdx === -1 ? \"\" : targetingKeyMapping.slice(0, dotIdx);\n const prop = dotIdx === -1 ? targetingKeyMapping : targetingKeyMapping.slice(dotIdx + 1);\n result[ns] ??= {};\n result[ns][prop] = ctxValue;\n continue;\n }\n\n const dotIdx = key.indexOf(\".\");\n if (dotIdx === -1) {\n result[\"\"] ??= {};\n result[\"\"][key] = ctxValue;\n } else {\n const ns = key.slice(0, dotIdx);\n const prop = key.slice(dotIdx + 1);\n result[ns] ??= {};\n result[ns][prop] = ctxValue;\n }\n }\n\n return result;\n}\n","import { ErrorCode } from \"@openfeature/server-sdk\";\n\n/**\n * Maps a native SDK error to an OpenFeature ErrorCode.\n *\n * The native SDK throws Error instances with message strings. We map by inspecting\n * the lowercased message.\n */\nexport function toErrorCode(err: unknown): ErrorCode {\n const msg = err instanceof Error ? err.message.toLowerCase() : String(err).toLowerCase();\n\n if (\n msg.includes(\"not found\") ||\n msg.includes(\"flag not found\") ||\n msg.includes(\"no value found\") ||\n msg.includes(\"value found for key\")\n ) {\n return ErrorCode.FLAG_NOT_FOUND;\n }\n if (msg.includes(\"type mismatch\")) {\n return ErrorCode.TYPE_MISMATCH;\n }\n if (msg.includes(\"not initialized\") || msg.includes(\"provider not ready\")) {\n return ErrorCode.PROVIDER_NOT_READY;\n }\n return ErrorCode.GENERAL;\n}\n"],"mappings":"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","names":["dotIdx","ErrorCode"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quonfig/openfeature-node",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.7",
|
|
4
4
|
"description": "OpenFeature provider for Quonfig -- Node.js",
|
|
5
5
|
"main": "dist/index.cjs",
|
|
6
6
|
"module": "dist/index.js",
|
|
@@ -19,16 +19,20 @@
|
|
|
19
19
|
"test": "vitest run --exclude test/conformance/**",
|
|
20
20
|
"test:conformance": "vitest run test/conformance",
|
|
21
21
|
"lint": "tsc --noEmit",
|
|
22
|
-
"prepublishOnly": "npm run build"
|
|
22
|
+
"prepublishOnly": "npm run build",
|
|
23
|
+
"prettier": "prettier . -l",
|
|
24
|
+
"prettier:fix": "prettier --write .",
|
|
25
|
+
"version": "prettier --write CHANGELOG.md README.md && git add CHANGELOG.md README.md"
|
|
23
26
|
},
|
|
24
27
|
"peerDependencies": {
|
|
25
|
-
"@quonfig/node": ">=0.0.
|
|
28
|
+
"@quonfig/node": ">=0.0.28",
|
|
26
29
|
"@openfeature/server-sdk": ">=1.0.0"
|
|
27
30
|
},
|
|
28
31
|
"devDependencies": {
|
|
29
|
-
"@quonfig/node": "^0.0.
|
|
32
|
+
"@quonfig/node": "^0.0.28",
|
|
30
33
|
"@openfeature/server-sdk": "^1.7.0",
|
|
31
34
|
"@types/node": "^20.11.0",
|
|
35
|
+
"prettier": "^3.0.0",
|
|
32
36
|
"tsup": "^8.0.0",
|
|
33
37
|
"typescript": "^5.3.0",
|
|
34
38
|
"vitest": "^1.0.0"
|