@schematichq/schematic-react 1.0.2 → 1.0.3
Sign up to get free protection for your applications and to get access to all the features.
- package/dist/schematic-react.cjs.js +110 -36
- package/dist/schematic-react.esm.js +111 -37
- package/package.json +2 -2
@@ -667,37 +667,89 @@ var Schematic = class {
|
|
667
667
|
});
|
668
668
|
}
|
669
669
|
}
|
670
|
-
|
671
|
-
|
670
|
+
/**
|
671
|
+
* Get value for a single flag.
|
672
|
+
* In WebSocket mode, returns cached values if connection is active, otherwise establishes
|
673
|
+
* new connection and then returns the requestedvalue. Falls back to REST API if WebSocket
|
674
|
+
* connection fails.
|
675
|
+
* In REST mode, makes an API call for each check.
|
676
|
+
*/
|
672
677
|
async checkFlag(options) {
|
673
678
|
const { fallback = false, key } = options;
|
674
679
|
const context = options.context || this.context;
|
675
|
-
|
676
|
-
|
680
|
+
const contextStr = contextString(context);
|
681
|
+
if (!this.useWebSocket) {
|
682
|
+
const requestUrl = `${this.apiUrl}/flags/${key}/check`;
|
683
|
+
return fetch(requestUrl, {
|
684
|
+
method: "POST",
|
685
|
+
headers: {
|
686
|
+
...this.additionalHeaders ?? {},
|
687
|
+
"Content-Type": "application/json;charset=UTF-8",
|
688
|
+
"X-Schematic-Api-Key": this.apiKey
|
689
|
+
},
|
690
|
+
body: JSON.stringify(context)
|
691
|
+
}).then((response) => {
|
692
|
+
if (!response.ok) {
|
693
|
+
throw new Error("Network response was not ok");
|
694
|
+
}
|
695
|
+
return response.json();
|
696
|
+
}).then((data) => {
|
697
|
+
return data.data.value;
|
698
|
+
}).catch((error) => {
|
699
|
+
console.error("There was a problem with the fetch operation:", error);
|
700
|
+
return fallback;
|
701
|
+
});
|
702
|
+
}
|
703
|
+
try {
|
704
|
+
const existingVals = this.values[contextStr];
|
705
|
+
if (this.conn && typeof existingVals !== "undefined" && typeof existingVals[key] !== "undefined") {
|
706
|
+
return existingVals[key];
|
707
|
+
}
|
708
|
+
try {
|
709
|
+
await this.setContext(context);
|
710
|
+
} catch (error) {
|
711
|
+
console.error(
|
712
|
+
"WebSocket connection failed, falling back to REST:",
|
713
|
+
error
|
714
|
+
);
|
715
|
+
return this.fallbackToRest(key, context, fallback);
|
716
|
+
}
|
717
|
+
const contextVals = this.values[contextStr] ?? {};
|
677
718
|
return typeof contextVals[key] === "undefined" ? fallback : contextVals[key];
|
719
|
+
} catch (error) {
|
720
|
+
console.error("Unexpected error in checkFlag:", error);
|
721
|
+
return fallback;
|
678
722
|
}
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
723
|
+
}
|
724
|
+
/**
|
725
|
+
* Helper method for falling back to REST API when WebSocket connection fails
|
726
|
+
*/
|
727
|
+
async fallbackToRest(key, context, fallback) {
|
728
|
+
try {
|
729
|
+
const requestUrl = `${this.apiUrl}/flags/${key}/check`;
|
730
|
+
const response = await fetch(requestUrl, {
|
731
|
+
method: "POST",
|
732
|
+
headers: {
|
733
|
+
...this.additionalHeaders ?? {},
|
734
|
+
"Content-Type": "application/json;charset=UTF-8",
|
735
|
+
"X-Schematic-Api-Key": this.apiKey
|
736
|
+
},
|
737
|
+
body: JSON.stringify(context)
|
738
|
+
});
|
689
739
|
if (!response.ok) {
|
690
740
|
throw new Error("Network response was not ok");
|
691
741
|
}
|
692
|
-
|
693
|
-
}).then((data) => {
|
742
|
+
const data = await response.json();
|
694
743
|
return data.data.value;
|
695
|
-
}
|
696
|
-
console.error("
|
744
|
+
} catch (error) {
|
745
|
+
console.error("REST API call failed, using fallback value:", error);
|
697
746
|
return fallback;
|
698
|
-
}
|
747
|
+
}
|
699
748
|
}
|
700
|
-
|
749
|
+
/**
|
750
|
+
* Make an API call to fetch all flag values for a given context.
|
751
|
+
* Recommended for use in REST mode only.
|
752
|
+
*/
|
701
753
|
checkFlags = async (context) => {
|
702
754
|
context = context || this.context;
|
703
755
|
const requestUrl = `${this.apiUrl}/flags/check`;
|
@@ -728,18 +780,30 @@ var Schematic = class {
|
|
728
780
|
return false;
|
729
781
|
});
|
730
782
|
};
|
731
|
-
|
783
|
+
/**
|
784
|
+
* Send an identify event.
|
785
|
+
* This will set the context for subsequent flag evaluation and events, and will also
|
786
|
+
* send an identify event to the Schematic API which will upsert a user and company.
|
787
|
+
*/
|
732
788
|
identify = (body) => {
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
|
789
|
+
try {
|
790
|
+
this.setContext({
|
791
|
+
company: body.company?.keys,
|
792
|
+
user: body.keys
|
793
|
+
});
|
794
|
+
} catch (error) {
|
795
|
+
console.error("Error setting context:", error);
|
796
|
+
}
|
737
797
|
return this.handleEvent("identify", body);
|
738
798
|
};
|
739
|
-
|
740
|
-
|
741
|
-
|
742
|
-
|
799
|
+
/**
|
800
|
+
* Set the flag evaluation context.
|
801
|
+
* In WebSocket mode, this will:
|
802
|
+
* 1. Open a websocket connection if not already open
|
803
|
+
* 2. Send the context to the server
|
804
|
+
* 3. Wait for initial flag values to be returned
|
805
|
+
* The promise resolves when initial flag values are received.
|
806
|
+
*/
|
743
807
|
setContext = async (context) => {
|
744
808
|
if (!this.useWebSocket) {
|
745
809
|
this.context = context;
|
@@ -753,10 +817,14 @@ var Schematic = class {
|
|
753
817
|
const socket = await this.conn;
|
754
818
|
await this.wsSendMessage(socket, context);
|
755
819
|
} catch (error) {
|
756
|
-
console.error("
|
820
|
+
console.error("Failed to establish WebSocket connection:", error);
|
821
|
+
throw error;
|
757
822
|
}
|
758
823
|
};
|
759
|
-
|
824
|
+
/**
|
825
|
+
* Send a track event
|
826
|
+
* Track usage for a company and/or user.
|
827
|
+
*/
|
760
828
|
track = (body) => {
|
761
829
|
const { company, user, event, traits } = body;
|
762
830
|
return this.handleEvent("track", {
|
@@ -828,6 +896,9 @@ var Schematic = class {
|
|
828
896
|
/**
|
829
897
|
* Websocket management
|
830
898
|
*/
|
899
|
+
/**
|
900
|
+
* If using websocket mode, close the connection when done.
|
901
|
+
*/
|
831
902
|
cleanup = async () => {
|
832
903
|
if (this.conn) {
|
833
904
|
try {
|
@@ -965,16 +1036,19 @@ var SchematicProvider = ({
|
|
965
1036
|
publishableKey,
|
966
1037
|
...clientOpts
|
967
1038
|
}) => {
|
1039
|
+
const initialOptsRef = (0, import_react.useRef)({
|
1040
|
+
publishableKey,
|
1041
|
+
useWebSocket: clientOpts.useWebSocket ?? true,
|
1042
|
+
...clientOpts
|
1043
|
+
});
|
968
1044
|
const client = (0, import_react.useMemo)(() => {
|
969
|
-
const { useWebSocket = true } = clientOpts;
|
970
1045
|
if (providedClient) {
|
971
1046
|
return providedClient;
|
972
1047
|
}
|
973
|
-
return new Schematic(publishableKey, {
|
974
|
-
|
975
|
-
...clientOpts
|
1048
|
+
return new Schematic(initialOptsRef.current.publishableKey, {
|
1049
|
+
...initialOptsRef.current
|
976
1050
|
});
|
977
|
-
}, [providedClient
|
1051
|
+
}, [providedClient]);
|
978
1052
|
(0, import_react.useEffect)(() => {
|
979
1053
|
return () => {
|
980
1054
|
if (!providedClient) {
|
@@ -625,37 +625,89 @@ var Schematic = class {
|
|
625
625
|
});
|
626
626
|
}
|
627
627
|
}
|
628
|
-
|
629
|
-
|
628
|
+
/**
|
629
|
+
* Get value for a single flag.
|
630
|
+
* In WebSocket mode, returns cached values if connection is active, otherwise establishes
|
631
|
+
* new connection and then returns the requestedvalue. Falls back to REST API if WebSocket
|
632
|
+
* connection fails.
|
633
|
+
* In REST mode, makes an API call for each check.
|
634
|
+
*/
|
630
635
|
async checkFlag(options) {
|
631
636
|
const { fallback = false, key } = options;
|
632
637
|
const context = options.context || this.context;
|
633
|
-
|
634
|
-
|
638
|
+
const contextStr = contextString(context);
|
639
|
+
if (!this.useWebSocket) {
|
640
|
+
const requestUrl = `${this.apiUrl}/flags/${key}/check`;
|
641
|
+
return fetch(requestUrl, {
|
642
|
+
method: "POST",
|
643
|
+
headers: {
|
644
|
+
...this.additionalHeaders ?? {},
|
645
|
+
"Content-Type": "application/json;charset=UTF-8",
|
646
|
+
"X-Schematic-Api-Key": this.apiKey
|
647
|
+
},
|
648
|
+
body: JSON.stringify(context)
|
649
|
+
}).then((response) => {
|
650
|
+
if (!response.ok) {
|
651
|
+
throw new Error("Network response was not ok");
|
652
|
+
}
|
653
|
+
return response.json();
|
654
|
+
}).then((data) => {
|
655
|
+
return data.data.value;
|
656
|
+
}).catch((error) => {
|
657
|
+
console.error("There was a problem with the fetch operation:", error);
|
658
|
+
return fallback;
|
659
|
+
});
|
660
|
+
}
|
661
|
+
try {
|
662
|
+
const existingVals = this.values[contextStr];
|
663
|
+
if (this.conn && typeof existingVals !== "undefined" && typeof existingVals[key] !== "undefined") {
|
664
|
+
return existingVals[key];
|
665
|
+
}
|
666
|
+
try {
|
667
|
+
await this.setContext(context);
|
668
|
+
} catch (error) {
|
669
|
+
console.error(
|
670
|
+
"WebSocket connection failed, falling back to REST:",
|
671
|
+
error
|
672
|
+
);
|
673
|
+
return this.fallbackToRest(key, context, fallback);
|
674
|
+
}
|
675
|
+
const contextVals = this.values[contextStr] ?? {};
|
635
676
|
return typeof contextVals[key] === "undefined" ? fallback : contextVals[key];
|
677
|
+
} catch (error) {
|
678
|
+
console.error("Unexpected error in checkFlag:", error);
|
679
|
+
return fallback;
|
636
680
|
}
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
681
|
+
}
|
682
|
+
/**
|
683
|
+
* Helper method for falling back to REST API when WebSocket connection fails
|
684
|
+
*/
|
685
|
+
async fallbackToRest(key, context, fallback) {
|
686
|
+
try {
|
687
|
+
const requestUrl = `${this.apiUrl}/flags/${key}/check`;
|
688
|
+
const response = await fetch(requestUrl, {
|
689
|
+
method: "POST",
|
690
|
+
headers: {
|
691
|
+
...this.additionalHeaders ?? {},
|
692
|
+
"Content-Type": "application/json;charset=UTF-8",
|
693
|
+
"X-Schematic-Api-Key": this.apiKey
|
694
|
+
},
|
695
|
+
body: JSON.stringify(context)
|
696
|
+
});
|
647
697
|
if (!response.ok) {
|
648
698
|
throw new Error("Network response was not ok");
|
649
699
|
}
|
650
|
-
|
651
|
-
}).then((data) => {
|
700
|
+
const data = await response.json();
|
652
701
|
return data.data.value;
|
653
|
-
}
|
654
|
-
console.error("
|
702
|
+
} catch (error) {
|
703
|
+
console.error("REST API call failed, using fallback value:", error);
|
655
704
|
return fallback;
|
656
|
-
}
|
705
|
+
}
|
657
706
|
}
|
658
|
-
|
707
|
+
/**
|
708
|
+
* Make an API call to fetch all flag values for a given context.
|
709
|
+
* Recommended for use in REST mode only.
|
710
|
+
*/
|
659
711
|
checkFlags = async (context) => {
|
660
712
|
context = context || this.context;
|
661
713
|
const requestUrl = `${this.apiUrl}/flags/check`;
|
@@ -686,18 +738,30 @@ var Schematic = class {
|
|
686
738
|
return false;
|
687
739
|
});
|
688
740
|
};
|
689
|
-
|
741
|
+
/**
|
742
|
+
* Send an identify event.
|
743
|
+
* This will set the context for subsequent flag evaluation and events, and will also
|
744
|
+
* send an identify event to the Schematic API which will upsert a user and company.
|
745
|
+
*/
|
690
746
|
identify = (body) => {
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
|
747
|
+
try {
|
748
|
+
this.setContext({
|
749
|
+
company: body.company?.keys,
|
750
|
+
user: body.keys
|
751
|
+
});
|
752
|
+
} catch (error) {
|
753
|
+
console.error("Error setting context:", error);
|
754
|
+
}
|
695
755
|
return this.handleEvent("identify", body);
|
696
756
|
};
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
|
757
|
+
/**
|
758
|
+
* Set the flag evaluation context.
|
759
|
+
* In WebSocket mode, this will:
|
760
|
+
* 1. Open a websocket connection if not already open
|
761
|
+
* 2. Send the context to the server
|
762
|
+
* 3. Wait for initial flag values to be returned
|
763
|
+
* The promise resolves when initial flag values are received.
|
764
|
+
*/
|
701
765
|
setContext = async (context) => {
|
702
766
|
if (!this.useWebSocket) {
|
703
767
|
this.context = context;
|
@@ -711,10 +775,14 @@ var Schematic = class {
|
|
711
775
|
const socket = await this.conn;
|
712
776
|
await this.wsSendMessage(socket, context);
|
713
777
|
} catch (error) {
|
714
|
-
console.error("
|
778
|
+
console.error("Failed to establish WebSocket connection:", error);
|
779
|
+
throw error;
|
715
780
|
}
|
716
781
|
};
|
717
|
-
|
782
|
+
/**
|
783
|
+
* Send a track event
|
784
|
+
* Track usage for a company and/or user.
|
785
|
+
*/
|
718
786
|
track = (body) => {
|
719
787
|
const { company, user, event, traits } = body;
|
720
788
|
return this.handleEvent("track", {
|
@@ -786,6 +854,9 @@ var Schematic = class {
|
|
786
854
|
/**
|
787
855
|
* Websocket management
|
788
856
|
*/
|
857
|
+
/**
|
858
|
+
* If using websocket mode, close the connection when done.
|
859
|
+
*/
|
789
860
|
cleanup = async () => {
|
790
861
|
if (this.conn) {
|
791
862
|
try {
|
@@ -912,7 +983,7 @@ var notifyListener = (listener, value) => {
|
|
912
983
|
};
|
913
984
|
|
914
985
|
// src/context/schematic.tsx
|
915
|
-
import React, { createContext, useEffect, useMemo } from "react";
|
986
|
+
import React, { createContext, useEffect, useMemo, useRef } from "react";
|
916
987
|
import { jsx } from "react/jsx-runtime";
|
917
988
|
var SchematicContext = createContext(
|
918
989
|
null
|
@@ -923,16 +994,19 @@ var SchematicProvider = ({
|
|
923
994
|
publishableKey,
|
924
995
|
...clientOpts
|
925
996
|
}) => {
|
997
|
+
const initialOptsRef = useRef({
|
998
|
+
publishableKey,
|
999
|
+
useWebSocket: clientOpts.useWebSocket ?? true,
|
1000
|
+
...clientOpts
|
1001
|
+
});
|
926
1002
|
const client = useMemo(() => {
|
927
|
-
const { useWebSocket = true } = clientOpts;
|
928
1003
|
if (providedClient) {
|
929
1004
|
return providedClient;
|
930
1005
|
}
|
931
|
-
return new Schematic(publishableKey, {
|
932
|
-
|
933
|
-
...clientOpts
|
1006
|
+
return new Schematic(initialOptsRef.current.publishableKey, {
|
1007
|
+
...initialOptsRef.current
|
934
1008
|
});
|
935
|
-
}, [providedClient
|
1009
|
+
}, [providedClient]);
|
936
1010
|
useEffect(() => {
|
937
1011
|
return () => {
|
938
1012
|
if (!providedClient) {
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@schematichq/schematic-react",
|
3
|
-
"version": "1.0.
|
3
|
+
"version": "1.0.3",
|
4
4
|
"main": "dist/schematic-react.cjs.js",
|
5
5
|
"module": "dist/schematic-react.esm.js",
|
6
6
|
"types": "dist/schematic-react.d.ts",
|
@@ -40,7 +40,7 @@
|
|
40
40
|
"esbuild-jest": "^0.5.0",
|
41
41
|
"eslint": "^8.57.1",
|
42
42
|
"eslint-plugin-import": "^2.30.0",
|
43
|
-
"eslint-plugin-react-hooks": "^
|
43
|
+
"eslint-plugin-react-hooks": "^5.0.0",
|
44
44
|
"jest": "^29.7.0",
|
45
45
|
"jest-environment-jsdom": "^29.7.0",
|
46
46
|
"jest-esbuild": "^0.3.0",
|