@flipt-io/flipt-client-js 0.0.2 → 0.0.4
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 +122 -34
- package/dist/browser/flipt_engine_wasm_js_bg.wasm +0 -0
- package/dist/browser/index.cjs +1 -2
- package/dist/browser/index.mjs +1 -2
- package/dist/flipt_engine_wasm_js_bg.wasm +0 -0
- package/dist/node/flipt_engine_wasm_js_bg.wasm +0 -0
- package/dist/node/index.cjs +1 -2
- package/dist/node/index.mjs +1 -2
- package/dist/{browser/flipt_engine_wasm_js.js → slim/index.cjs} +284 -40
- package/dist/{node/flipt_engine_wasm_js.js → slim/index.mjs} +279 -39
- package/dist/types/slim/index.d.ts +31 -0
- package/package.json +17 -8
- package/dist/browser/flipt_engine_wasm_js.d.ts +0 -53
- package/dist/browser/flipt_engine_wasm_js_bg.wasm.d.ts +0 -18
- package/dist/browser/index.cjs.map +0 -1
- package/dist/browser/index.mjs.map +0 -1
- package/dist/node/flipt_engine_wasm_js.d.ts +0 -53
- package/dist/node/flipt_engine_wasm_js_bg.wasm.d.ts +0 -18
- package/dist/node/index.cjs.map +0 -1
- package/dist/node/index.mjs.map +0 -1
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
1
4
|
let wasm;
|
|
2
5
|
|
|
3
6
|
let WASM_VECTOR_LEN = 0;
|
|
@@ -91,8 +94,7 @@ function handleError(f, args) {
|
|
|
91
94
|
|
|
92
95
|
const cachedTextDecoder = (typeof TextDecoder !== 'undefined' ? new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }) : { decode: () => { throw Error('TextDecoder not available') } } );
|
|
93
96
|
|
|
94
|
-
if (typeof TextDecoder !== 'undefined') { cachedTextDecoder.decode(); }
|
|
95
|
-
|
|
97
|
+
if (typeof TextDecoder !== 'undefined') { cachedTextDecoder.decode(); }
|
|
96
98
|
function getStringFromWasm0(ptr, len) {
|
|
97
99
|
ptr = ptr >>> 0;
|
|
98
100
|
return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len));
|
|
@@ -177,7 +179,7 @@ const EngineFinalization = (typeof FinalizationRegistry === 'undefined')
|
|
|
177
179
|
? { register: () => {}, unregister: () => {} }
|
|
178
180
|
: new FinalizationRegistry(ptr => wasm.__wbg_engine_free(ptr >>> 0, 1));
|
|
179
181
|
|
|
180
|
-
|
|
182
|
+
class Engine {
|
|
181
183
|
|
|
182
184
|
__destroy_into_raw() {
|
|
183
185
|
const ptr = this.__wbg_ptr;
|
|
@@ -479,7 +481,6 @@ function __wbg_get_imports() {
|
|
|
479
481
|
table.set(offset + 1, null);
|
|
480
482
|
table.set(offset + 2, true);
|
|
481
483
|
table.set(offset + 3, false);
|
|
482
|
-
;
|
|
483
484
|
};
|
|
484
485
|
imports.wbg.__wbindgen_is_function = function(arg0) {
|
|
485
486
|
const ret = typeof(arg0) === 'function';
|
|
@@ -535,10 +536,6 @@ function __wbg_get_imports() {
|
|
|
535
536
|
return imports;
|
|
536
537
|
}
|
|
537
538
|
|
|
538
|
-
function __wbg_init_memory(imports, memory) {
|
|
539
|
-
|
|
540
|
-
}
|
|
541
|
-
|
|
542
539
|
function __wbg_finalize_init(instance, module) {
|
|
543
540
|
wasm = instance.exports;
|
|
544
541
|
__wbg_init.__wbindgen_wasm_module = module;
|
|
@@ -550,45 +547,20 @@ function __wbg_finalize_init(instance, module) {
|
|
|
550
547
|
return wasm;
|
|
551
548
|
}
|
|
552
549
|
|
|
553
|
-
function initSync(module) {
|
|
554
|
-
if (wasm !== undefined) return wasm;
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
if (typeof module !== 'undefined') {
|
|
558
|
-
if (Object.getPrototypeOf(module) === Object.prototype) {
|
|
559
|
-
({module} = module)
|
|
560
|
-
} else {
|
|
561
|
-
console.warn('using deprecated parameters for `initSync()`; pass a single object instead')
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
const imports = __wbg_get_imports();
|
|
566
|
-
|
|
567
|
-
__wbg_init_memory(imports);
|
|
568
|
-
|
|
569
|
-
if (!(module instanceof WebAssembly.Module)) {
|
|
570
|
-
module = new WebAssembly.Module(module);
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
const instance = new WebAssembly.Instance(module, imports);
|
|
574
|
-
|
|
575
|
-
return __wbg_finalize_init(instance, module);
|
|
576
|
-
}
|
|
577
|
-
|
|
578
550
|
async function __wbg_init(module_or_path) {
|
|
579
551
|
if (wasm !== undefined) return wasm;
|
|
580
552
|
|
|
581
553
|
|
|
582
554
|
if (typeof module_or_path !== 'undefined') {
|
|
583
555
|
if (Object.getPrototypeOf(module_or_path) === Object.prototype) {
|
|
584
|
-
({module_or_path} = module_or_path)
|
|
556
|
+
({module_or_path} = module_or_path);
|
|
585
557
|
} else {
|
|
586
|
-
console.warn('using deprecated parameters for the initialization function; pass a single object instead')
|
|
558
|
+
console.warn('using deprecated parameters for the initialization function; pass a single object instead');
|
|
587
559
|
}
|
|
588
560
|
}
|
|
589
561
|
|
|
590
562
|
if (typeof module_or_path === 'undefined') {
|
|
591
|
-
module_or_path = new URL('flipt_engine_wasm_js_bg.wasm',
|
|
563
|
+
module_or_path = new URL('flipt_engine_wasm_js_bg.wasm', (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
|
|
592
564
|
}
|
|
593
565
|
const imports = __wbg_get_imports();
|
|
594
566
|
|
|
@@ -596,12 +568,284 @@ async function __wbg_init(module_or_path) {
|
|
|
596
568
|
module_or_path = fetch(module_or_path);
|
|
597
569
|
}
|
|
598
570
|
|
|
599
|
-
__wbg_init_memory(imports);
|
|
600
|
-
|
|
601
571
|
const { instance, module } = await __wbg_load(await module_or_path, imports);
|
|
602
572
|
|
|
603
573
|
return __wbg_finalize_init(instance, module);
|
|
604
574
|
}
|
|
605
575
|
|
|
606
|
-
|
|
607
|
-
|
|
576
|
+
/**
|
|
577
|
+
* Defines the strategy to handle errors during the flags snapshot update calls.
|
|
578
|
+
*/
|
|
579
|
+
exports.ErrorStrategy = void 0;
|
|
580
|
+
(function (ErrorStrategy) {
|
|
581
|
+
/** The client will throw an error if the flag state cannot be fetched. This is the default behavior. */
|
|
582
|
+
ErrorStrategy["Fail"] = "fail";
|
|
583
|
+
/** The client will maintain the last known good state and use that state for evaluation in case of an error. */
|
|
584
|
+
ErrorStrategy["Fallback"] = "fallback";
|
|
585
|
+
})(exports.ErrorStrategy || (exports.ErrorStrategy = {}));
|
|
586
|
+
|
|
587
|
+
function snakeToCamel(str) {
|
|
588
|
+
return str.replace(/([-_][a-z])/g, (group) => group.toUpperCase().replace('-', '').replace('_', ''));
|
|
589
|
+
}
|
|
590
|
+
function camelToSnake(str) {
|
|
591
|
+
return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
|
|
592
|
+
}
|
|
593
|
+
function deserialize(data) {
|
|
594
|
+
const result = {};
|
|
595
|
+
for (const key in data) {
|
|
596
|
+
if (Object.prototype.hasOwnProperty.call(data, key)) {
|
|
597
|
+
const camelKey = snakeToCamel(key);
|
|
598
|
+
result[camelKey] = data[key];
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
return result;
|
|
602
|
+
}
|
|
603
|
+
function serialize(data) {
|
|
604
|
+
const result = {};
|
|
605
|
+
for (const key in data) {
|
|
606
|
+
if (Object.prototype.hasOwnProperty.call(data, key)) {
|
|
607
|
+
const snakeKey = camelToSnake(key);
|
|
608
|
+
result[snakeKey] = data[key];
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
return result;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
class BaseFliptClient {
|
|
615
|
+
constructor(engine, fetcher) {
|
|
616
|
+
this.engine = engine;
|
|
617
|
+
this.fetcher = fetcher;
|
|
618
|
+
}
|
|
619
|
+
/**
|
|
620
|
+
* Store etag from response for next requests
|
|
621
|
+
*/
|
|
622
|
+
storeEtag(resp) {
|
|
623
|
+
const etag = resp.headers.get('etag');
|
|
624
|
+
if (etag) {
|
|
625
|
+
this.etag = etag;
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
/**
|
|
629
|
+
* Refresh the flags snapshot
|
|
630
|
+
* @returns true if snapshot changed
|
|
631
|
+
*/
|
|
632
|
+
async refresh() {
|
|
633
|
+
try {
|
|
634
|
+
const opts = { etag: this.etag };
|
|
635
|
+
const resp = await this.fetcher(opts);
|
|
636
|
+
const etag = resp.headers.get('etag');
|
|
637
|
+
if (this.etag && this.etag === etag) {
|
|
638
|
+
return false;
|
|
639
|
+
}
|
|
640
|
+
this.storeEtag(resp);
|
|
641
|
+
const data = await resp.json();
|
|
642
|
+
this.engine.snapshot(data);
|
|
643
|
+
return true;
|
|
644
|
+
}
|
|
645
|
+
catch (error) {
|
|
646
|
+
if (this.errorStrategy === exports.ErrorStrategy.Fail) {
|
|
647
|
+
throw error;
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
return false;
|
|
651
|
+
}
|
|
652
|
+
/**
|
|
653
|
+
* Evaluate a variant flag
|
|
654
|
+
*/
|
|
655
|
+
evaluateVariant({ flagKey, entityId, context }) {
|
|
656
|
+
if (!flagKey || flagKey.trim() === '') {
|
|
657
|
+
throw new Error('flagKey cannot be empty');
|
|
658
|
+
}
|
|
659
|
+
if (!entityId || entityId.trim() === '') {
|
|
660
|
+
throw new Error('entityId cannot be empty');
|
|
661
|
+
}
|
|
662
|
+
const evaluationRequest = {
|
|
663
|
+
flagKey,
|
|
664
|
+
entityId,
|
|
665
|
+
context
|
|
666
|
+
};
|
|
667
|
+
const result = this.engine.evaluate_variant(serialize(evaluationRequest));
|
|
668
|
+
if (result === null) {
|
|
669
|
+
throw new Error('Failed to evaluate variant');
|
|
670
|
+
}
|
|
671
|
+
const variantResult = deserialize(result);
|
|
672
|
+
if (variantResult.status === 'failure') {
|
|
673
|
+
throw new Error(variantResult.errorMessage);
|
|
674
|
+
}
|
|
675
|
+
if (!variantResult.result) {
|
|
676
|
+
throw new Error('Failed to evaluate variant');
|
|
677
|
+
}
|
|
678
|
+
return deserialize(variantResult.result);
|
|
679
|
+
}
|
|
680
|
+
/**
|
|
681
|
+
* Evaluate a boolean flag
|
|
682
|
+
*/
|
|
683
|
+
evaluateBoolean({ flagKey, entityId, context }) {
|
|
684
|
+
if (!flagKey || flagKey.trim() === '') {
|
|
685
|
+
throw new Error('flagKey cannot be empty');
|
|
686
|
+
}
|
|
687
|
+
if (!entityId || entityId.trim() === '') {
|
|
688
|
+
throw new Error('entityId cannot be empty');
|
|
689
|
+
}
|
|
690
|
+
const evaluationRequest = {
|
|
691
|
+
flagKey,
|
|
692
|
+
entityId,
|
|
693
|
+
context
|
|
694
|
+
};
|
|
695
|
+
const result = this.engine.evaluate_boolean(serialize(evaluationRequest));
|
|
696
|
+
if (result === null) {
|
|
697
|
+
throw new Error('Failed to evaluate boolean');
|
|
698
|
+
}
|
|
699
|
+
const booleanResult = deserialize(result);
|
|
700
|
+
if (booleanResult.status === 'failure') {
|
|
701
|
+
throw new Error(booleanResult.errorMessage);
|
|
702
|
+
}
|
|
703
|
+
if (!booleanResult.result) {
|
|
704
|
+
throw new Error('Failed to evaluate boolean');
|
|
705
|
+
}
|
|
706
|
+
return deserialize(booleanResult.result);
|
|
707
|
+
}
|
|
708
|
+
/**
|
|
709
|
+
* Evaluate a batch of flag requests
|
|
710
|
+
*/
|
|
711
|
+
evaluateBatch(requests) {
|
|
712
|
+
const serializedRequests = requests.map(serialize);
|
|
713
|
+
const batchResult = this.engine.evaluate_batch(serializedRequests);
|
|
714
|
+
if (batchResult === null) {
|
|
715
|
+
throw new Error('Failed to evaluate batch');
|
|
716
|
+
}
|
|
717
|
+
if (batchResult.status === 'failure') {
|
|
718
|
+
throw new Error(batchResult.errorMessage);
|
|
719
|
+
}
|
|
720
|
+
if (!batchResult.result) {
|
|
721
|
+
throw new Error('Failed to evaluate batch');
|
|
722
|
+
}
|
|
723
|
+
const responses = batchResult.result.responses
|
|
724
|
+
.map((response) => {
|
|
725
|
+
if (response.type === 'BOOLEAN_EVALUATION_RESPONSE_TYPE') {
|
|
726
|
+
const booleanResponse = deserialize(
|
|
727
|
+
// @ts-ignore
|
|
728
|
+
response.boolean_evaluation_response);
|
|
729
|
+
return {
|
|
730
|
+
booleanEvaluationResponse: booleanResponse,
|
|
731
|
+
type: 'BOOLEAN_EVALUATION_RESPONSE_TYPE'
|
|
732
|
+
};
|
|
733
|
+
}
|
|
734
|
+
if (response.type === 'VARIANT_EVALUATION_RESPONSE_TYPE') {
|
|
735
|
+
const variantResponse = deserialize(
|
|
736
|
+
// @ts-ignore
|
|
737
|
+
response.variant_evaluation_response);
|
|
738
|
+
return {
|
|
739
|
+
variantEvaluationResponse: variantResponse,
|
|
740
|
+
type: 'VARIANT_EVALUATION_RESPONSE_TYPE'
|
|
741
|
+
};
|
|
742
|
+
}
|
|
743
|
+
if (response.type === 'ERROR_EVALUATION_RESPONSE_TYPE') {
|
|
744
|
+
const errorResponse = deserialize(
|
|
745
|
+
// @ts-ignore
|
|
746
|
+
response.error_evaluation_response);
|
|
747
|
+
return {
|
|
748
|
+
errorEvaluationResponse: errorResponse,
|
|
749
|
+
type: 'ERROR_EVALUATION_RESPONSE_TYPE'
|
|
750
|
+
};
|
|
751
|
+
}
|
|
752
|
+
return undefined;
|
|
753
|
+
})
|
|
754
|
+
.filter((response) => response !== undefined);
|
|
755
|
+
return {
|
|
756
|
+
responses,
|
|
757
|
+
requestDurationMillis: batchResult.result.requestDurationMillis
|
|
758
|
+
};
|
|
759
|
+
}
|
|
760
|
+
listFlags() {
|
|
761
|
+
const listFlagsResult = this.engine.list_flags();
|
|
762
|
+
if (listFlagsResult === null) {
|
|
763
|
+
throw new Error('Failed to list flags');
|
|
764
|
+
}
|
|
765
|
+
const flags = deserialize(listFlagsResult);
|
|
766
|
+
if (flags.status === 'failure') {
|
|
767
|
+
throw new Error(flags.errorMessage);
|
|
768
|
+
}
|
|
769
|
+
if (!flags.result) {
|
|
770
|
+
throw new Error('Failed to list flags');
|
|
771
|
+
}
|
|
772
|
+
return flags.result.map((deserialize));
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
class FliptClient extends BaseFliptClient {
|
|
777
|
+
/**
|
|
778
|
+
* Initialize the client
|
|
779
|
+
* @param options - optional client options
|
|
780
|
+
* @param wasmOptions - options for loading WASM
|
|
781
|
+
* @returns {Promise<FliptClient>}
|
|
782
|
+
*/
|
|
783
|
+
static async init(options = {
|
|
784
|
+
namespace: 'default',
|
|
785
|
+
url: 'http://localhost:8080',
|
|
786
|
+
reference: '',
|
|
787
|
+
updateInterval: 120,
|
|
788
|
+
errorStrategy: exports.ErrorStrategy.Fail
|
|
789
|
+
}, wasmOptions) {
|
|
790
|
+
if (!wasmOptions || !wasmOptions.wasm) {
|
|
791
|
+
throw new Error('WASM module must be provided in slim mode. Use the standard client or provide a wasm module.');
|
|
792
|
+
}
|
|
793
|
+
const namespace = options.namespace ?? 'default';
|
|
794
|
+
let url = options.url ?? 'http://localhost:8080';
|
|
795
|
+
url = url.replace(/\/$/, '');
|
|
796
|
+
url = `${url}/internal/v1/evaluation/snapshot/namespace/${namespace}`;
|
|
797
|
+
if (options.reference) {
|
|
798
|
+
url = `${url}?reference=${options.reference}`;
|
|
799
|
+
}
|
|
800
|
+
const headers = {
|
|
801
|
+
Accept: 'application/json',
|
|
802
|
+
'x-flipt-accept-server-version': '1.47.0'
|
|
803
|
+
};
|
|
804
|
+
if (options.authentication) {
|
|
805
|
+
if ('clientToken' in options.authentication) {
|
|
806
|
+
headers['Authorization'] =
|
|
807
|
+
`Bearer ${options.authentication.clientToken}`;
|
|
808
|
+
}
|
|
809
|
+
else if ('jwtToken' in options.authentication) {
|
|
810
|
+
headers['Authorization'] = `JWT ${options.authentication.jwtToken}`;
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
let fetcher = options.fetcher;
|
|
814
|
+
if (!fetcher) {
|
|
815
|
+
fetcher = async (opts) => {
|
|
816
|
+
if (opts && opts.etag) {
|
|
817
|
+
headers['If-None-Match'] = opts.etag;
|
|
818
|
+
}
|
|
819
|
+
const resp = await fetch(url, {
|
|
820
|
+
method: 'GET',
|
|
821
|
+
headers
|
|
822
|
+
});
|
|
823
|
+
if (!resp.ok && resp.status !== 304) {
|
|
824
|
+
throw new Error(`Failed to fetch data: ${resp.statusText}`);
|
|
825
|
+
}
|
|
826
|
+
return resp;
|
|
827
|
+
};
|
|
828
|
+
}
|
|
829
|
+
// Initialize WASM engine with the provided WASM module
|
|
830
|
+
await __wbg_init(wasmOptions.wasm);
|
|
831
|
+
if (!fetcher) {
|
|
832
|
+
throw new Error('Failed to initialize fetcher');
|
|
833
|
+
}
|
|
834
|
+
// handle case if they pass in a custom fetcher that doesn't throw on non-2xx status codes
|
|
835
|
+
const resp = await fetcher();
|
|
836
|
+
if (!resp.ok) {
|
|
837
|
+
throw new Error(`Failed to fetch data: ${resp.statusText}`);
|
|
838
|
+
}
|
|
839
|
+
const data = await resp.json();
|
|
840
|
+
const engine = new Engine(namespace);
|
|
841
|
+
engine.snapshot(data);
|
|
842
|
+
// Create client instance
|
|
843
|
+
const client = new FliptClient(engine, fetcher);
|
|
844
|
+
client.storeEtag(resp);
|
|
845
|
+
client.errorStrategy = options.errorStrategy;
|
|
846
|
+
return client;
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
exports.BaseFliptClient = BaseFliptClient;
|
|
851
|
+
exports.FliptClient = FliptClient;
|