@openfeature/flagd-provider 0.4.0 → 0.5.1

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 CHANGED
@@ -1,8 +1,10 @@
1
- # NodeJS flagd Provider for OpenFeature
1
+ # Server-side JavaScript flagd Provider for OpenFeature
2
2
 
3
3
  ![Experimental](https://img.shields.io/badge/experimental-breaking%20changes%20allowed-yellow)
4
4
 
5
- Flagd is a simple command line tool for fetching and presenting feature flags to services. It is designed to conform to OpenFeature schema for flag definitions. This repository and package provides the client side code for interacting with it via the Open-Feature Node SDK.
5
+ Flagd is a simple daemon for evaluating feature flags.
6
+ It is designed to conform to OpenFeature schema for flag definitions.
7
+ This repository and package provides the client code for interacting with it via the OpenFeature server-side JavaScript SDK.
6
8
 
7
9
  ## Installation
8
10
 
@@ -10,29 +12,43 @@ Flagd is a simple command line tool for fetching and presenting feature flags to
10
12
  $ npm install @openfeature/flagd-provider
11
13
  ```
12
14
 
13
- ## Building
14
-
15
- Run `nx package providers-flagd` to build the library.
16
-
17
- > NOTE: [Buf](https://docs.buf.build/installation) must be installed to build locally.
15
+ ## Usage
18
16
 
19
- ## Running unit tests
17
+ The `FlagdProvider` supports multiple configuration options that determine now the SDK communicates with flagd.
18
+ Options can be defined in the constructor or as environment variables, with constructor options having the highest precedence.
20
19
 
21
- Run `nx test providers-flagd` to execute the unit tests via [Jest](https://jestjs.io).
20
+ ### Available options
22
21
 
23
- ## Usage
22
+ | Option name | Environment variable name | Type | Default |
23
+ | ----------- | ------------------------- | ------- | --------- |
24
+ | host | FLAGD_HOST | string | localhost |
25
+ | port | FLAGD_PORT | number | 8013 |
26
+ | tls | FLAGD_TLS | boolean | false |
27
+ | socketPath | FLAGD_SOCKET_PATH | string | - |
24
28
 
25
- The `FlagdProvider` client constructor takes a single optional argument with 3 fields, their default values correspond to the default arguments supplied to the flagd server:
29
+ ### Example using TCP
26
30
 
27
31
  ```
28
32
  OpenFeature.setProvider(new FlagdProvider({
29
- service: 'grpc',
30
33
  host: 'localhost',
31
34
  port: 8013,
32
35
  }))
33
36
  ```
34
37
 
35
- **service**: "http" | "grpc" _(defaults to http)_
36
- **host**: string _(defaults to "localhost")_
37
- **port**: number _(defaults to 8013)_
38
- **protocol**: "http" | "https" _(defaults to http - only active for http service)_
38
+ ### Example using a Unix socket
39
+
40
+ ```
41
+ OpenFeature.setProvider(new FlagdProvider({
42
+ socketPath: "/tmp/flagd.socks",
43
+ }))
44
+ ```
45
+
46
+ ## Building
47
+
48
+ Run `nx package providers-flagd` to build the library.
49
+
50
+ > NOTE: [Buf](https://docs.buf.build/installation) must be installed to build locally.
51
+
52
+ ## Running unit tests
53
+
54
+ Run `nx test providers-flagd` to execute the unit tests via [Jest](https://jestjs.io).
package/index.cjs CHANGED
@@ -2,12 +2,9 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var nodejsSdk = require('@openfeature/nodejs-sdk');
6
- var axios = require('axios');
7
- var grpcTransport = require('@protobuf-ts/grpc-transport');
8
5
  var grpc = require('@grpc/grpc-js');
9
-
10
- function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
6
+ var jsSdk = require('@openfeature/js-sdk');
7
+ var grpcTransport = require('@protobuf-ts/grpc-transport');
11
8
 
12
9
  function _interopNamespace(e) {
13
10
  if (e && e.__esModule) return e;
@@ -27,7 +24,6 @@ function _interopNamespace(e) {
27
24
  return Object.freeze(n);
28
25
  }
29
26
 
30
- var axios__default = /*#__PURE__*/_interopDefaultLegacy(axios);
31
27
  var grpc__namespace = /*#__PURE__*/_interopNamespace(grpc);
32
28
 
33
29
  var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
@@ -198,9 +194,9 @@ var engineUserAgent = getBuiltIn$3('navigator', 'userAgent') || '';
198
194
  var global$a = global$c;
199
195
  var userAgent = engineUserAgent;
200
196
 
201
- var process = global$a.process;
197
+ var process$1 = global$a.process;
202
198
  var Deno = global$a.Deno;
203
- var versions = process && process.versions || Deno && Deno.version;
199
+ var versions = process$1 && process$1.versions || Deno && Deno.version;
204
200
  var v8 = versions && versions.v8;
205
201
  var match, version;
206
202
 
@@ -1078,6 +1074,8 @@ $$2({ target: 'Object', stat: true, arity: 2, forced: Object.assign !== assign }
1078
1074
  assign: assign
1079
1075
  });
1080
1076
 
1077
+ const DEFAULT_CONFIG={host:"localhost",port:8013,tls:!1};var ENV_VAR;(function(a){a.FLAGD_HOST="FLAGD_HOST",a.FLAGD_PORT="FLAGD_PORT",a.FLAGD_TLS="FLAGD_TLS",a.FLAGD_SOCKET_PATH="FLAGD_SOCKET_PATH";})(ENV_VAR||(ENV_VAR={}));const getEnvVarConfig=()=>{var a;return Object.assign(Object.assign(Object.assign(Object.assign({},process.env[ENV_VAR.FLAGD_HOST]&&{host:process.env[ENV_VAR.FLAGD_HOST]}),+process.env[ENV_VAR.FLAGD_PORT]&&{port:+process.env[ENV_VAR.FLAGD_PORT]}),process.env[ENV_VAR.FLAGD_TLS]&&{tls:"true"===(null===(a=process.env[ENV_VAR.FLAGD_TLS])||void 0===a?void 0:a.toLowerCase())}),process.env[ENV_VAR.FLAGD_SOCKET_PATH]&&{socketPath:process.env[ENV_VAR.FLAGD_SOCKET_PATH]})};function getConfig(a={}){return Object.assign(Object.assign(Object.assign({},DEFAULT_CONFIG),getEnvVarConfig()),a)}
1078
+
1081
1079
  /******************************************************************************
1082
1080
  Copyright (c) Microsoft Corporation.
1083
1081
 
@@ -1103,10 +1101,6 @@ function __awaiter(thisArg, _arguments, P, generator) {
1103
1101
  });
1104
1102
  }
1105
1103
 
1106
- class HTTPService{constructor(a){this.url=`${a.protocol}://${a.host}:${a.port}`;}resolveBoolean(a,b,c){return __awaiter(this,void 0,void 0,function*(){try{const d=yield axios__default["default"].post(`${this.url}/flags/${encodeURIComponent(a)}/resolve/boolean`,c);return checkResponse(d,"boolean")?{value:d.data.value,reason:d.data.reason,variant:d.data.variant}:{value:b,reason:nodejsSdk.StandardResolutionReasons.ERROR,errorCode:nodejsSdk.ErrorCode.PARSE_ERROR}}catch(a){return {reason:nodejsSdk.StandardResolutionReasons.ERROR,errorCode:getErrorCode(a),value:b}}})}resolveNumber(a,b,c){return __awaiter(this,void 0,void 0,function*(){try{const d=yield axios__default["default"].post(/**
1107
- * JavaScript numbers are always 64-bit floating point
1108
- */`${this.url}/flags/${encodeURIComponent(a)}/resolve/float`,c);return checkResponse(d,"number")?{value:d.data.value,reason:d.data.reason,variant:d.data.variant}:{value:b,reason:nodejsSdk.StandardResolutionReasons.ERROR,errorCode:nodejsSdk.ErrorCode.PARSE_ERROR}}catch(a){return {reason:nodejsSdk.StandardResolutionReasons.ERROR,errorCode:getErrorCode(a),value:b}}})}resolveString(a,b,c){return __awaiter(this,void 0,void 0,function*(){try{const d=yield axios__default["default"].post(`${this.url}/flags/${encodeURIComponent(a)}/resolve/string`,c);return checkResponse(d,"string")?{value:d.data.value,reason:d.data.reason,variant:d.data.variant}:{value:b,reason:nodejsSdk.StandardResolutionReasons.ERROR,errorCode:nodejsSdk.ErrorCode.PARSE_ERROR}}catch(a){return {reason:nodejsSdk.StandardResolutionReasons.ERROR,errorCode:getErrorCode(a),value:b}}})}resolveObject(a,b,c){return __awaiter(this,void 0,void 0,function*(){try{const d=yield axios__default["default"].post(`${this.url}/flags/${encodeURIComponent(a)}/resolve/object`,c);return checkResponse(d,"object")?{value:d.data.value,reason:d.data.reason,variant:d.data.variant}:{value:b,reason:nodejsSdk.StandardResolutionReasons.ERROR,errorCode:nodejsSdk.ErrorCode.PARSE_ERROR}}catch(a){return {reason:nodejsSdk.StandardResolutionReasons.ERROR,errorCode:getErrorCode(a),value:b}}})}}function getErrorCode(a){var b;const c=null===(b=null===a||void 0===a?void 0:a.response)||void 0===b?void 0:b.status;let d=nodejsSdk.StandardResolutionReasons.UNKNOWN;return null!=c&&(404==c?d=nodejsSdk.ErrorCode.FLAG_NOT_FOUND:400==c&&(d=nodejsSdk.ErrorCode.TYPE_MISMATCH)),d}function checkResponse(a,b){return !!(a.data&&typeof a.data.value===b&&"string"==typeof a.data.variant&&"string"==typeof a.data.reason)}
1109
-
1110
1104
  var objectDefineProperties = {};
1111
1105
 
1112
1106
  var DESCRIPTORS$1 = descriptors;
@@ -4806,8 +4800,10 @@ class ResolveObjectResponse$Type extends MessageType{constructor(){super("schema
4806
4800
  * @generated from protobuf rpc: ResolveObject(schema.v1.ResolveObjectRequest) returns (schema.v1.ResolveObjectResponse);
4807
4801
  */resolveObject(a,b){const c=this.methods[4],d=this._transport.mergeOptions(b);return stackIntercept("unary",this._transport,c,d,a)}}
4808
4802
 
4809
- class GRPCService{constructor(a){const{host:b,port:c}=a;this.client=new ServiceClient(new grpcTransport.GrpcTransport({host:`${b}:${c}`,channelCredentials:grpc__namespace.credentials.createInsecure()}));}resolveBoolean(a,b,c){var d;return __awaiter(this,void 0,void 0,function*(){try{const{response:b}=yield this.client.resolveBoolean({flagKey:a,context:Struct.fromJsonString(JSON.stringify(c))});return {value:b.value,reason:b.reason,variant:b.variant}}catch(a){return {reason:nodejsSdk.StandardResolutionReasons.ERROR,errorCode:null!==(d=null===a||void 0===a?void 0:a.code)&&void 0!==d?d:nodejsSdk.StandardResolutionReasons.UNKNOWN,value:b}}})}resolveString(a,b,c){var d;return __awaiter(this,void 0,void 0,function*(){try{const{response:b}=yield this.client.resolveString({flagKey:a,context:Struct.fromJsonString(JSON.stringify(c))});return {value:b.value,reason:b.reason,variant:b.variant}}catch(a){return {reason:nodejsSdk.StandardResolutionReasons.ERROR,errorCode:null!==(d=null===a||void 0===a?void 0:a.code)&&void 0!==d?d:nodejsSdk.StandardResolutionReasons.UNKNOWN,value:b}}})}resolveNumber(a,b,c){var d;return __awaiter(this,void 0,void 0,function*(){try{const{response:b}=yield this.client.resolveFloat({flagKey:a,context:Struct.fromJsonString(JSON.stringify(c))});return {value:b.value,reason:b.reason,variant:b.variant}}catch(a){return {reason:nodejsSdk.StandardResolutionReasons.ERROR,errorCode:null!==(d=null===a||void 0===a?void 0:a.code)&&void 0!==d?d:nodejsSdk.StandardResolutionReasons.UNKNOWN,value:b}}})}resolveObject(a,b,c){var d;return __awaiter(this,void 0,void 0,function*(){try{const{response:b}=yield this.client.resolveObject({flagKey:a,context:Struct.fromJsonString(JSON.stringify(c))});return {value:b.value,reason:b.reason,variant:b.variant}}catch(a){return {reason:nodejsSdk.StandardResolutionReasons.ERROR,errorCode:null!==(d=null===a||void 0===a?void 0:a.code)&&void 0!==d?d:nodejsSdk.StandardResolutionReasons.UNKNOWN,value:b}}})}}
4803
+ const Codes={InvalidArgument:"INVALID_ARGUMENT",NotFound:"NOT_FOUND",DataLoss:"DATA_LOSS",Unavailable:"UNAVAILABLE"};class GRPCService{constructor(a,b){this.onFulfilled=a=>a,this.onRejected=a=>{// map the errors
4804
+ switch(null===a||void 0===a?void 0:a.code){case Codes.DataLoss:throw new jsSdk.ParseError(a.message);case Codes.InvalidArgument:throw new jsSdk.TypeMismatchError(a.message);case Codes.NotFound:throw new jsSdk.FlagNotFoundError(a.message);case Codes.Unavailable:throw new jsSdk.FlagNotFoundError(a.message);default:throw a;}};const{host:c,port:d,tls:e,socketPath:f}=a;this.client=b?b:new ServiceClient(new grpcTransport.GrpcTransport({host:f?`unix://${f}`:`${c}:${d}`,channelCredentials:e?grpc__namespace.credentials.createSsl():grpc__namespace.credentials.createInsecure()}));}resolveBoolean(a,b,c){return __awaiter(this,void 0,void 0,function*(){const{response:d}=yield this.client.resolveBoolean({flagKey:a,context:this.convertContext(b,c)}).then(this.onFulfilled,this.onRejected);return {value:d.value,reason:d.reason,variant:d.variant}})}resolveString(a,b,c){return __awaiter(this,void 0,void 0,function*(){const{response:d}=yield this.client.resolveString({flagKey:a,context:this.convertContext(b,c)}).then(this.onFulfilled,this.onRejected);return {value:d.value,reason:d.reason,variant:d.variant}})}resolveNumber(a,b,c){return __awaiter(this,void 0,void 0,function*(){const{response:d}=yield this.client.resolveFloat({flagKey:a,context:this.convertContext(b,c)}).then(this.onFulfilled,this.onRejected);return {value:d.value,reason:d.reason,variant:d.variant}})}resolveObject(a,b,c){return __awaiter(this,void 0,void 0,function*(){const{response:d}=yield this.client.resolveObject({flagKey:a,context:this.convertContext(b,c)}).then(this.onFulfilled,this.onRejected);if(d.value!==void 0)return {value:Struct.toJson(d.value),reason:d.reason,variant:d.variant};throw new jsSdk.ParseError("Object value undefined or missing.")})}convertContext(a,b){try{// stringify to remove invalid js props
4805
+ return Struct.fromJsonString(JSON.stringify(a))}catch(a){const c=a;throw b.error(`${"Error serializing context."}: ${null===c||void 0===c?void 0:c.message}`),b.error(null===c||void 0===c?void 0:c.stack),new jsSdk.ParseError("Error serializing context.")}}}
4810
4806
 
4811
- class FlagdProvider{constructor(a){this.metadata={name:"flagD Provider"};const{service:b,host:c,port:d,protocol:e}=Object.assign({service:"http",host:"localhost",port:8013,protocol:"http"},a);this.service="http"===b?new HTTPService({host:c,port:d,protocol:e}):new GRPCService({host:c,port:d});}resolveBooleanEvaluation(a,b,c){return this.service.resolveBoolean(a,b,c)}resolveStringEvaluation(a,b,c){return this.service.resolveString(a,b,c)}resolveNumberEvaluation(a,b,c){return this.service.resolveNumber(a,b,c)}resolveObjectEvaluation(a,b,c){return this.service.resolveObject(a,b,c)}}
4807
+ class FlagdProvider{constructor(a,b){this.metadata={name:"flagd Provider"},this.logRejected=(a,b,c)=>{throw c.error(`Error resolving flag ${b}: ${null===a||void 0===a?void 0:a.message}`),c.error(null===a||void 0===a?void 0:a.stack),a},this._service=b?b:new GRPCService(getConfig(a));}resolveBooleanEvaluation(a,b,c,d){return this._service.resolveBoolean(a,c,d).catch(b=>this.logRejected(b,a,d))}resolveStringEvaluation(a,b,c,d){return this._service.resolveString(a,c,d).catch(b=>this.logRejected(b,a,d))}resolveNumberEvaluation(a,b,c,d){return this._service.resolveNumber(a,c,d).catch(b=>this.logRejected(b,a,d))}resolveObjectEvaluation(a,b,c,d){return this._service.resolveObject(a,c,d).catch(b=>this.logRejected(b,a,d))}}
4812
4808
 
4813
4809
  exports.FlagdProvider = FlagdProvider;
package/index.js CHANGED
@@ -1,7 +1,6 @@
1
- import { StandardResolutionReasons, ErrorCode } from '@openfeature/nodejs-sdk';
2
- import axios from 'axios';
3
- import { GrpcTransport } from '@protobuf-ts/grpc-transport';
4
1
  import * as grpc from '@grpc/grpc-js';
2
+ import { FlagNotFoundError, TypeMismatchError, ParseError } from '@openfeature/js-sdk';
3
+ import { GrpcTransport } from '@protobuf-ts/grpc-transport';
5
4
 
6
5
  var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
7
6
 
@@ -171,9 +170,9 @@ var engineUserAgent = getBuiltIn$3('navigator', 'userAgent') || '';
171
170
  var global$a = global$c;
172
171
  var userAgent = engineUserAgent;
173
172
 
174
- var process = global$a.process;
173
+ var process$1 = global$a.process;
175
174
  var Deno = global$a.Deno;
176
- var versions = process && process.versions || Deno && Deno.version;
175
+ var versions = process$1 && process$1.versions || Deno && Deno.version;
177
176
  var v8 = versions && versions.v8;
178
177
  var match, version;
179
178
 
@@ -1051,6 +1050,8 @@ $$2({ target: 'Object', stat: true, arity: 2, forced: Object.assign !== assign }
1051
1050
  assign: assign
1052
1051
  });
1053
1052
 
1053
+ const DEFAULT_CONFIG={host:"localhost",port:8013,tls:!1};var ENV_VAR;(function(a){a.FLAGD_HOST="FLAGD_HOST",a.FLAGD_PORT="FLAGD_PORT",a.FLAGD_TLS="FLAGD_TLS",a.FLAGD_SOCKET_PATH="FLAGD_SOCKET_PATH";})(ENV_VAR||(ENV_VAR={}));const getEnvVarConfig=()=>{var a;return Object.assign(Object.assign(Object.assign(Object.assign({},process.env[ENV_VAR.FLAGD_HOST]&&{host:process.env[ENV_VAR.FLAGD_HOST]}),+process.env[ENV_VAR.FLAGD_PORT]&&{port:+process.env[ENV_VAR.FLAGD_PORT]}),process.env[ENV_VAR.FLAGD_TLS]&&{tls:"true"===(null===(a=process.env[ENV_VAR.FLAGD_TLS])||void 0===a?void 0:a.toLowerCase())}),process.env[ENV_VAR.FLAGD_SOCKET_PATH]&&{socketPath:process.env[ENV_VAR.FLAGD_SOCKET_PATH]})};function getConfig(a={}){return Object.assign(Object.assign(Object.assign({},DEFAULT_CONFIG),getEnvVarConfig()),a)}
1054
+
1054
1055
  /******************************************************************************
1055
1056
  Copyright (c) Microsoft Corporation.
1056
1057
 
@@ -1076,10 +1077,6 @@ function __awaiter(thisArg, _arguments, P, generator) {
1076
1077
  });
1077
1078
  }
1078
1079
 
1079
- class HTTPService{constructor(a){this.url=`${a.protocol}://${a.host}:${a.port}`;}resolveBoolean(a,b,c){return __awaiter(this,void 0,void 0,function*(){try{const d=yield axios.post(`${this.url}/flags/${encodeURIComponent(a)}/resolve/boolean`,c);return checkResponse(d,"boolean")?{value:d.data.value,reason:d.data.reason,variant:d.data.variant}:{value:b,reason:StandardResolutionReasons.ERROR,errorCode:ErrorCode.PARSE_ERROR}}catch(a){return {reason:StandardResolutionReasons.ERROR,errorCode:getErrorCode(a),value:b}}})}resolveNumber(a,b,c){return __awaiter(this,void 0,void 0,function*(){try{const d=yield axios.post(/**
1080
- * JavaScript numbers are always 64-bit floating point
1081
- */`${this.url}/flags/${encodeURIComponent(a)}/resolve/float`,c);return checkResponse(d,"number")?{value:d.data.value,reason:d.data.reason,variant:d.data.variant}:{value:b,reason:StandardResolutionReasons.ERROR,errorCode:ErrorCode.PARSE_ERROR}}catch(a){return {reason:StandardResolutionReasons.ERROR,errorCode:getErrorCode(a),value:b}}})}resolveString(a,b,c){return __awaiter(this,void 0,void 0,function*(){try{const d=yield axios.post(`${this.url}/flags/${encodeURIComponent(a)}/resolve/string`,c);return checkResponse(d,"string")?{value:d.data.value,reason:d.data.reason,variant:d.data.variant}:{value:b,reason:StandardResolutionReasons.ERROR,errorCode:ErrorCode.PARSE_ERROR}}catch(a){return {reason:StandardResolutionReasons.ERROR,errorCode:getErrorCode(a),value:b}}})}resolveObject(a,b,c){return __awaiter(this,void 0,void 0,function*(){try{const d=yield axios.post(`${this.url}/flags/${encodeURIComponent(a)}/resolve/object`,c);return checkResponse(d,"object")?{value:d.data.value,reason:d.data.reason,variant:d.data.variant}:{value:b,reason:StandardResolutionReasons.ERROR,errorCode:ErrorCode.PARSE_ERROR}}catch(a){return {reason:StandardResolutionReasons.ERROR,errorCode:getErrorCode(a),value:b}}})}}function getErrorCode(a){var b;const c=null===(b=null===a||void 0===a?void 0:a.response)||void 0===b?void 0:b.status;let d=StandardResolutionReasons.UNKNOWN;return null!=c&&(404==c?d=ErrorCode.FLAG_NOT_FOUND:400==c&&(d=ErrorCode.TYPE_MISMATCH)),d}function checkResponse(a,b){return !!(a.data&&typeof a.data.value===b&&"string"==typeof a.data.variant&&"string"==typeof a.data.reason)}
1082
-
1083
1080
  var objectDefineProperties = {};
1084
1081
 
1085
1082
  var DESCRIPTORS$1 = descriptors;
@@ -4779,8 +4776,10 @@ class ResolveObjectResponse$Type extends MessageType{constructor(){super("schema
4779
4776
  * @generated from protobuf rpc: ResolveObject(schema.v1.ResolveObjectRequest) returns (schema.v1.ResolveObjectResponse);
4780
4777
  */resolveObject(a,b){const c=this.methods[4],d=this._transport.mergeOptions(b);return stackIntercept("unary",this._transport,c,d,a)}}
4781
4778
 
4782
- class GRPCService{constructor(a){const{host:b,port:c}=a;this.client=new ServiceClient(new GrpcTransport({host:`${b}:${c}`,channelCredentials:grpc.credentials.createInsecure()}));}resolveBoolean(a,b,c){var d;return __awaiter(this,void 0,void 0,function*(){try{const{response:b}=yield this.client.resolveBoolean({flagKey:a,context:Struct.fromJsonString(JSON.stringify(c))});return {value:b.value,reason:b.reason,variant:b.variant}}catch(a){return {reason:StandardResolutionReasons.ERROR,errorCode:null!==(d=null===a||void 0===a?void 0:a.code)&&void 0!==d?d:StandardResolutionReasons.UNKNOWN,value:b}}})}resolveString(a,b,c){var d;return __awaiter(this,void 0,void 0,function*(){try{const{response:b}=yield this.client.resolveString({flagKey:a,context:Struct.fromJsonString(JSON.stringify(c))});return {value:b.value,reason:b.reason,variant:b.variant}}catch(a){return {reason:StandardResolutionReasons.ERROR,errorCode:null!==(d=null===a||void 0===a?void 0:a.code)&&void 0!==d?d:StandardResolutionReasons.UNKNOWN,value:b}}})}resolveNumber(a,b,c){var d;return __awaiter(this,void 0,void 0,function*(){try{const{response:b}=yield this.client.resolveFloat({flagKey:a,context:Struct.fromJsonString(JSON.stringify(c))});return {value:b.value,reason:b.reason,variant:b.variant}}catch(a){return {reason:StandardResolutionReasons.ERROR,errorCode:null!==(d=null===a||void 0===a?void 0:a.code)&&void 0!==d?d:StandardResolutionReasons.UNKNOWN,value:b}}})}resolveObject(a,b,c){var d;return __awaiter(this,void 0,void 0,function*(){try{const{response:b}=yield this.client.resolveObject({flagKey:a,context:Struct.fromJsonString(JSON.stringify(c))});return {value:b.value,reason:b.reason,variant:b.variant}}catch(a){return {reason:StandardResolutionReasons.ERROR,errorCode:null!==(d=null===a||void 0===a?void 0:a.code)&&void 0!==d?d:StandardResolutionReasons.UNKNOWN,value:b}}})}}
4779
+ const Codes={InvalidArgument:"INVALID_ARGUMENT",NotFound:"NOT_FOUND",DataLoss:"DATA_LOSS",Unavailable:"UNAVAILABLE"};class GRPCService{constructor(a,b){this.onFulfilled=a=>a,this.onRejected=a=>{// map the errors
4780
+ switch(null===a||void 0===a?void 0:a.code){case Codes.DataLoss:throw new ParseError(a.message);case Codes.InvalidArgument:throw new TypeMismatchError(a.message);case Codes.NotFound:throw new FlagNotFoundError(a.message);case Codes.Unavailable:throw new FlagNotFoundError(a.message);default:throw a;}};const{host:c,port:d,tls:e,socketPath:f}=a;this.client=b?b:new ServiceClient(new GrpcTransport({host:f?`unix://${f}`:`${c}:${d}`,channelCredentials:e?grpc.credentials.createSsl():grpc.credentials.createInsecure()}));}resolveBoolean(a,b,c){return __awaiter(this,void 0,void 0,function*(){const{response:d}=yield this.client.resolveBoolean({flagKey:a,context:this.convertContext(b,c)}).then(this.onFulfilled,this.onRejected);return {value:d.value,reason:d.reason,variant:d.variant}})}resolveString(a,b,c){return __awaiter(this,void 0,void 0,function*(){const{response:d}=yield this.client.resolveString({flagKey:a,context:this.convertContext(b,c)}).then(this.onFulfilled,this.onRejected);return {value:d.value,reason:d.reason,variant:d.variant}})}resolveNumber(a,b,c){return __awaiter(this,void 0,void 0,function*(){const{response:d}=yield this.client.resolveFloat({flagKey:a,context:this.convertContext(b,c)}).then(this.onFulfilled,this.onRejected);return {value:d.value,reason:d.reason,variant:d.variant}})}resolveObject(a,b,c){return __awaiter(this,void 0,void 0,function*(){const{response:d}=yield this.client.resolveObject({flagKey:a,context:this.convertContext(b,c)}).then(this.onFulfilled,this.onRejected);if(d.value!==void 0)return {value:Struct.toJson(d.value),reason:d.reason,variant:d.variant};throw new ParseError("Object value undefined or missing.")})}convertContext(a,b){try{// stringify to remove invalid js props
4781
+ return Struct.fromJsonString(JSON.stringify(a))}catch(a){const c=a;throw b.error(`${"Error serializing context."}: ${null===c||void 0===c?void 0:c.message}`),b.error(null===c||void 0===c?void 0:c.stack),new ParseError("Error serializing context.")}}}
4783
4782
 
4784
- class FlagdProvider{constructor(a){this.metadata={name:"flagD Provider"};const{service:b,host:c,port:d,protocol:e}=Object.assign({service:"http",host:"localhost",port:8013,protocol:"http"},a);this.service="http"===b?new HTTPService({host:c,port:d,protocol:e}):new GRPCService({host:c,port:d});}resolveBooleanEvaluation(a,b,c){return this.service.resolveBoolean(a,b,c)}resolveStringEvaluation(a,b,c){return this.service.resolveString(a,b,c)}resolveNumberEvaluation(a,b,c){return this.service.resolveNumber(a,b,c)}resolveObjectEvaluation(a,b,c){return this.service.resolveObject(a,b,c)}}
4783
+ class FlagdProvider{constructor(a,b){this.metadata={name:"flagd Provider"},this.logRejected=(a,b,c)=>{throw c.error(`Error resolving flag ${b}: ${null===a||void 0===a?void 0:a.message}`),c.error(null===a||void 0===a?void 0:a.stack),a},this._service=b?b:new GRPCService(getConfig(a));}resolveBooleanEvaluation(a,b,c,d){return this._service.resolveBoolean(a,c,d).catch(b=>this.logRejected(b,a,d))}resolveStringEvaluation(a,b,c,d){return this._service.resolveString(a,c,d).catch(b=>this.logRejected(b,a,d))}resolveNumberEvaluation(a,b,c,d){return this._service.resolveNumber(a,c,d).catch(b=>this.logRejected(b,a,d))}resolveObjectEvaluation(a,b,c,d){return this._service.resolveObject(a,c,d).catch(b=>this.logRejected(b,a,d))}}
4785
4784
 
4786
4785
  export { FlagdProvider };
@@ -0,0 +1,33 @@
1
+ export interface Config {
2
+ /**
3
+ * The domain name or IP address of flagd.
4
+ *
5
+ * @default localhost
6
+ */
7
+ host: string;
8
+ /**
9
+ * The port flagd is listen on.
10
+ *
11
+ * @default 8013
12
+ */
13
+ port: number;
14
+ /**
15
+ * Determines if TLS should be used.
16
+ *
17
+ * @default false
18
+ */
19
+ tls: boolean;
20
+ /**
21
+ * When set, a unix socket connection is used.
22
+ *
23
+ * @example "/tmp/flagd.socks"
24
+ */
25
+ socketPath?: string;
26
+ }
27
+ export declare type FlagdProviderOptions = Partial<Config>;
28
+ export declare function getConfig(options?: FlagdProviderOptions): {
29
+ host: string;
30
+ port: number;
31
+ tls: boolean;
32
+ socketPath?: string | undefined;
33
+ };
@@ -1,18 +1,15 @@
1
- import { EvaluationContext, Provider, ResolutionDetails } from '@openfeature/nodejs-sdk';
2
- export interface FlagdProviderOptions {
3
- service?: 'grpc' | 'http';
4
- host?: string;
5
- port?: number;
6
- protocol?: 'http' | 'https';
7
- }
1
+ import { EvaluationContext, JsonValue, Logger, Provider, ResolutionDetails } from '@openfeature/js-sdk';
2
+ import { FlagdProviderOptions } from './configuration';
3
+ import { Service } from './service/service';
8
4
  export declare class FlagdProvider implements Provider {
9
5
  metadata: {
10
6
  name: string;
11
7
  };
12
- private readonly service;
13
- constructor(options?: FlagdProviderOptions);
14
- resolveBooleanEvaluation(flagKey: string, defaultValue: boolean, transformedContext: EvaluationContext): Promise<ResolutionDetails<boolean>>;
15
- resolveStringEvaluation(flagKey: string, defaultValue: string, transformedContext: EvaluationContext): Promise<ResolutionDetails<string>>;
16
- resolveNumberEvaluation(flagKey: string, defaultValue: number, transformedContext: EvaluationContext): Promise<ResolutionDetails<number>>;
17
- resolveObjectEvaluation<U extends object>(flagKey: string, defaultValue: U, transformedContext: EvaluationContext): Promise<ResolutionDetails<U>>;
8
+ private readonly _service;
9
+ constructor(options?: FlagdProviderOptions, service?: Service);
10
+ resolveBooleanEvaluation(flagKey: string, _: boolean, transformedContext: EvaluationContext, logger: Logger): Promise<ResolutionDetails<boolean>>;
11
+ resolveStringEvaluation(flagKey: string, _: string, transformedContext: EvaluationContext, logger: Logger): Promise<ResolutionDetails<string>>;
12
+ resolveNumberEvaluation(flagKey: string, _: number, transformedContext: EvaluationContext, logger: Logger): Promise<ResolutionDetails<number>>;
13
+ resolveObjectEvaluation<T extends JsonValue>(flagKey: string, _: T, transformedContext: EvaluationContext, logger: Logger): Promise<ResolutionDetails<T>>;
14
+ logRejected: (err: Error, flagKey: string, logger: Logger) => never;
18
15
  }
@@ -1,16 +1,21 @@
1
- import { EvaluationContext, ResolutionDetails } from '@openfeature/nodejs-sdk';
1
+ import { EvaluationContext, JsonValue, Logger, ResolutionDetails } from '@openfeature/js-sdk';
2
2
  import { ServiceClient } from '../../../proto/ts/schema/v1/schema.client';
3
- import { Service } from '../Service';
4
- interface GRPCServiceOptions {
5
- host: string;
6
- port: number;
7
- }
3
+ import { Config } from '../../configuration';
4
+ import { Service } from '../service';
5
+ export declare const Codes: {
6
+ readonly InvalidArgument: "INVALID_ARGUMENT";
7
+ readonly NotFound: "NOT_FOUND";
8
+ readonly DataLoss: "DATA_LOSS";
9
+ readonly Unavailable: "UNAVAILABLE";
10
+ };
8
11
  export declare class GRPCService implements Service {
9
12
  client: ServiceClient;
10
- constructor(options: GRPCServiceOptions);
11
- resolveBoolean(flagKey: string, defaultValue: boolean, context: EvaluationContext): Promise<ResolutionDetails<boolean>>;
12
- resolveString(flagKey: string, defaultValue: string, context: EvaluationContext): Promise<ResolutionDetails<string>>;
13
- resolveNumber(flagKey: string, defaultValue: number, context: EvaluationContext): Promise<ResolutionDetails<number>>;
14
- resolveObject<T extends object>(flagKey: string, defaultValue: T, context: EvaluationContext): Promise<ResolutionDetails<T>>;
13
+ constructor(config: Config, client?: ServiceClient);
14
+ resolveBoolean(flagKey: string, context: EvaluationContext, logger: Logger): Promise<ResolutionDetails<boolean>>;
15
+ resolveString(flagKey: string, context: EvaluationContext, logger: Logger): Promise<ResolutionDetails<string>>;
16
+ resolveNumber(flagKey: string, context: EvaluationContext, logger: Logger): Promise<ResolutionDetails<number>>;
17
+ resolveObject<T extends JsonValue>(flagKey: string, context: EvaluationContext, logger: Logger): Promise<ResolutionDetails<T>>;
18
+ private convertContext;
19
+ private onFulfilled;
20
+ private onRejected;
15
21
  }
16
- export {};
@@ -0,0 +1,7 @@
1
+ import { EvaluationContext, JsonValue, Logger, ResolutionDetails } from '@openfeature/js-sdk';
2
+ export interface Service {
3
+ resolveBoolean(flagKey: string, context: EvaluationContext, logger: Logger): Promise<ResolutionDetails<boolean>>;
4
+ resolveString(flagKey: string, context: EvaluationContext, logger: Logger): Promise<ResolutionDetails<string>>;
5
+ resolveNumber(flagKey: string, context: EvaluationContext, logger: Logger): Promise<ResolutionDetails<number>>;
6
+ resolveObject<T extends JsonValue>(flagKey: string, context: EvaluationContext, logger: Logger): Promise<ResolutionDetails<T>>;
7
+ }
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@openfeature/flagd-provider",
3
- "version": "0.4.0",
3
+ "version": "0.5.1",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "publish-if-not-exists": "cp $NPM_CONFIG_USERCONFIG .npmrc && if [ \"$(npm show $npm_package_name@$npm_package_version version)\" = \"$(npm run current-version -s)\" ]; then echo 'already published, skipping'; else npm publish --access public; fi",
7
7
  "current-version": "echo $npm_package_version"
8
8
  },
9
9
  "peerDependencies": {
10
- "@openfeature/nodejs-sdk": "^0.2.0"
10
+ "@openfeature/js-sdk": "^0.4.0"
11
11
  },
12
12
  "module": "./index.js",
13
13
  "main": "./index.cjs",
@@ -20,7 +20,6 @@
20
20
  }
21
21
  },
22
22
  "dependencies": {
23
- "axios": "^0.27.2",
24
23
  "@protobuf-ts/grpc-transport": "^2.7.0",
25
24
  "@grpc/grpc-js": "^1.6.7"
26
25
  }
@@ -1,7 +0,0 @@
1
- import { EvaluationContext, ResolutionDetails } from '@openfeature/nodejs-sdk';
2
- export interface Service {
3
- resolveBoolean(flagKey: string, defaultValue: boolean, context: EvaluationContext): Promise<ResolutionDetails<boolean>>;
4
- resolveString(flagKey: string, defaultValue: string, context: EvaluationContext): Promise<ResolutionDetails<string>>;
5
- resolveNumber(flagKey: string, defaultValue: number, context: EvaluationContext): Promise<ResolutionDetails<number>>;
6
- resolveObject<T extends object>(flagKey: string, defaultValue: T, context: EvaluationContext): Promise<ResolutionDetails<T>>;
7
- }
@@ -1,16 +0,0 @@
1
- import { EvaluationContext, ResolutionDetails } from '@openfeature/nodejs-sdk';
2
- import { Service } from '../Service';
3
- interface HTTPServiceOptions {
4
- host: string;
5
- port: number;
6
- protocol: string;
7
- }
8
- export declare class HTTPService implements Service {
9
- private url;
10
- constructor(options: HTTPServiceOptions);
11
- resolveBoolean(flagKey: string, defaultValue: boolean, context: EvaluationContext): Promise<ResolutionDetails<boolean>>;
12
- resolveNumber(flagKey: string, defaultValue: number, context: EvaluationContext): Promise<ResolutionDetails<number>>;
13
- resolveString(flagKey: string, defaultValue: string, context: EvaluationContext): Promise<ResolutionDetails<string>>;
14
- resolveObject<T extends object>(flagKey: string, defaultValue: T, context: EvaluationContext): Promise<ResolutionDetails<T>>;
15
- }
16
- export {};