@schematichq/schematic-react 1.4.1 → 1.6.0
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 +72 -1
- package/dist/schematic-react.cjs.js +2266 -200
- package/dist/schematic-react.d.ts +847 -22
- package/dist/schematic-react.esm.js +2267 -201
- package/package.json +27 -31
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// node_modules/@schematichq/schematic-js/dist/schematic.esm.js
|
|
1
|
+
// node_modules/.pnpm/@schematichq+schematic-js@1.7.0/node_modules/@schematichq/schematic-js/dist/schematic.esm.js
|
|
2
2
|
var __create = Object.create;
|
|
3
3
|
var __defProp = Object.defineProperty;
|
|
4
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -6,7 +6,15 @@ var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
|
6
6
|
var __getProtoOf = Object.getPrototypeOf;
|
|
7
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
8
|
var __commonJS = (cb, mod) => function __require() {
|
|
9
|
-
|
|
9
|
+
try {
|
|
10
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
11
|
+
} catch (e) {
|
|
12
|
+
throw mod = 0, e;
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
var __export = (target, all) => {
|
|
16
|
+
for (var name in all)
|
|
17
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
18
|
};
|
|
11
19
|
var __copyProps = (to, from, except, desc) => {
|
|
12
20
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
@@ -25,7 +33,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
25
33
|
mod
|
|
26
34
|
));
|
|
27
35
|
var require_browser_polyfill = __commonJS({
|
|
28
|
-
"node_modules/cross-fetch/dist/browser-polyfill.js"(exports) {
|
|
36
|
+
"node_modules/.pnpm/cross-fetch@4.1.0/node_modules/cross-fetch/dist/browser-polyfill.js"(exports) {
|
|
29
37
|
(function(self2) {
|
|
30
38
|
var irrelevant = (function(exports2) {
|
|
31
39
|
var g = typeof globalThis !== "undefined" && globalThis || typeof self2 !== "undefined" && self2 || // eslint-disable-next-line no-undef
|
|
@@ -565,19 +573,16 @@ for (let i = 0; i < 256; ++i) {
|
|
|
565
573
|
function unsafeStringify(arr, offset = 0) {
|
|
566
574
|
return (byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + "-" + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + "-" + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + "-" + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + "-" + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]]).toLowerCase();
|
|
567
575
|
}
|
|
568
|
-
var getRandomValues;
|
|
569
576
|
var rnds8 = new Uint8Array(16);
|
|
570
577
|
function rng() {
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
578
|
+
return crypto.getRandomValues(rnds8);
|
|
579
|
+
}
|
|
580
|
+
function v4(options, buf, offset) {
|
|
581
|
+
if (!buf && !options && crypto.randomUUID) {
|
|
582
|
+
return crypto.randomUUID();
|
|
576
583
|
}
|
|
577
|
-
return
|
|
584
|
+
return _v4(options, buf, offset);
|
|
578
585
|
}
|
|
579
|
-
var randomUUID = typeof crypto !== "undefined" && crypto.randomUUID && crypto.randomUUID.bind(crypto);
|
|
580
|
-
var native_default = { randomUUID };
|
|
581
586
|
function _v4(options, buf, offset) {
|
|
582
587
|
options = options || {};
|
|
583
588
|
const rnds = options.random ?? options.rng?.() ?? rng();
|
|
@@ -598,12 +603,6 @@ function _v4(options, buf, offset) {
|
|
|
598
603
|
}
|
|
599
604
|
return unsafeStringify(rnds);
|
|
600
605
|
}
|
|
601
|
-
function v4(options, buf, offset) {
|
|
602
|
-
if (native_default.randomUUID && !buf && !options) {
|
|
603
|
-
return native_default.randomUUID();
|
|
604
|
-
}
|
|
605
|
-
return _v4(options, buf, offset);
|
|
606
|
-
}
|
|
607
606
|
var v4_default = v4;
|
|
608
607
|
var import_polyfill = __toESM(require_browser_polyfill());
|
|
609
608
|
function EntitlementValueTypeFromJSON(json) {
|
|
@@ -612,6 +611,30 @@ function EntitlementValueTypeFromJSON(json) {
|
|
|
612
611
|
function EntitlementValueTypeFromJSONTyped(json, ignoreDiscriminator) {
|
|
613
612
|
return json;
|
|
614
613
|
}
|
|
614
|
+
function MetricPeriodMonthResetFromJSON(json) {
|
|
615
|
+
return MetricPeriodMonthResetFromJSONTyped(json, false);
|
|
616
|
+
}
|
|
617
|
+
function MetricPeriodMonthResetFromJSONTyped(json, ignoreDiscriminator) {
|
|
618
|
+
return json;
|
|
619
|
+
}
|
|
620
|
+
function WarningTierFromJSON(json) {
|
|
621
|
+
return WarningTierFromJSONTyped(json, false);
|
|
622
|
+
}
|
|
623
|
+
function WarningTierFromJSONTyped(json, ignoreDiscriminator) {
|
|
624
|
+
if (json == null) {
|
|
625
|
+
return json;
|
|
626
|
+
}
|
|
627
|
+
return {
|
|
628
|
+
key: json["key"],
|
|
629
|
+
value: json["value"]
|
|
630
|
+
};
|
|
631
|
+
}
|
|
632
|
+
function MetricPeriodFromJSON(json) {
|
|
633
|
+
return MetricPeriodFromJSONTyped(json, false);
|
|
634
|
+
}
|
|
635
|
+
function MetricPeriodFromJSONTyped(json, ignoreDiscriminator) {
|
|
636
|
+
return json;
|
|
637
|
+
}
|
|
615
638
|
function FeatureEntitlementFromJSON(json) {
|
|
616
639
|
return FeatureEntitlementFromJSONTyped(json, false);
|
|
617
640
|
}
|
|
@@ -621,21 +644,32 @@ function FeatureEntitlementFromJSONTyped(json, ignoreDiscriminator) {
|
|
|
621
644
|
}
|
|
622
645
|
return {
|
|
623
646
|
allocation: json["allocation"] == null ? void 0 : json["allocation"],
|
|
647
|
+
consumptionRate: json["consumption_rate"] == null ? void 0 : json["consumption_rate"],
|
|
624
648
|
creditId: json["credit_id"] == null ? void 0 : json["credit_id"],
|
|
625
649
|
creditRemaining: json["credit_remaining"] == null ? void 0 : json["credit_remaining"],
|
|
650
|
+
creditReserved: json["credit_reserved"] == null ? void 0 : json["credit_reserved"],
|
|
651
|
+
creditSettled: json["credit_settled"] == null ? void 0 : json["credit_settled"],
|
|
626
652
|
creditTotal: json["credit_total"] == null ? void 0 : json["credit_total"],
|
|
627
653
|
creditUsed: json["credit_used"] == null ? void 0 : json["credit_used"],
|
|
628
654
|
eventName: json["event_name"] == null ? void 0 : json["event_name"],
|
|
655
|
+
eventSubtype: json["event_subtype"] == null ? void 0 : json["event_subtype"],
|
|
629
656
|
featureId: json["feature_id"],
|
|
630
657
|
featureKey: json["feature_key"],
|
|
631
|
-
metricPeriod: json["metric_period"] == null ? void 0 : json["metric_period"],
|
|
658
|
+
metricPeriod: json["metric_period"] == null ? void 0 : MetricPeriodFromJSON(json["metric_period"]),
|
|
632
659
|
metricResetAt: json["metric_reset_at"] == null ? void 0 : new Date(json["metric_reset_at"]),
|
|
633
|
-
monthReset: json["month_reset"] == null ? void 0 : json["month_reset"],
|
|
660
|
+
monthReset: json["month_reset"] == null ? void 0 : MetricPeriodMonthResetFromJSON(json["month_reset"]),
|
|
634
661
|
softLimit: json["soft_limit"] == null ? void 0 : json["soft_limit"],
|
|
635
662
|
usage: json["usage"] == null ? void 0 : json["usage"],
|
|
636
|
-
valueType: EntitlementValueTypeFromJSON(json["value_type"])
|
|
663
|
+
valueType: EntitlementValueTypeFromJSON(json["value_type"]),
|
|
664
|
+
warningTiers: json["warning_tiers"] == null ? void 0 : json["warning_tiers"].map(WarningTierFromJSON)
|
|
637
665
|
};
|
|
638
666
|
}
|
|
667
|
+
function RuleTypeFromJSON(json) {
|
|
668
|
+
return RuleTypeFromJSONTyped(json, false);
|
|
669
|
+
}
|
|
670
|
+
function RuleTypeFromJSONTyped(json, ignoreDiscriminator) {
|
|
671
|
+
return json;
|
|
672
|
+
}
|
|
639
673
|
function CheckFlagResponseDataFromJSON(json) {
|
|
640
674
|
return CheckFlagResponseDataFromJSONTyped(json, false);
|
|
641
675
|
}
|
|
@@ -650,13 +684,13 @@ function CheckFlagResponseDataFromJSONTyped(json, ignoreDiscriminator) {
|
|
|
650
684
|
featureAllocation: json["feature_allocation"] == null ? void 0 : json["feature_allocation"],
|
|
651
685
|
featureUsage: json["feature_usage"] == null ? void 0 : json["feature_usage"],
|
|
652
686
|
featureUsageEvent: json["feature_usage_event"] == null ? void 0 : json["feature_usage_event"],
|
|
653
|
-
featureUsagePeriod: json["feature_usage_period"] == null ? void 0 : json["feature_usage_period"],
|
|
687
|
+
featureUsagePeriod: json["feature_usage_period"] == null ? void 0 : MetricPeriodFromJSON(json["feature_usage_period"]),
|
|
654
688
|
featureUsageResetAt: json["feature_usage_reset_at"] == null ? void 0 : new Date(json["feature_usage_reset_at"]),
|
|
655
689
|
flag: json["flag"],
|
|
656
690
|
flagId: json["flag_id"] == null ? void 0 : json["flag_id"],
|
|
657
691
|
reason: json["reason"],
|
|
658
692
|
ruleId: json["rule_id"] == null ? void 0 : json["rule_id"],
|
|
659
|
-
ruleType: json["rule_type"] == null ? void 0 : json["rule_type"],
|
|
693
|
+
ruleType: json["rule_type"] == null ? void 0 : RuleTypeFromJSON(json["rule_type"]),
|
|
660
694
|
userId: json["user_id"] == null ? void 0 : json["user_id"],
|
|
661
695
|
value: json["value"]
|
|
662
696
|
};
|
|
@@ -673,6 +707,268 @@ function CheckFlagResponseFromJSONTyped(json, ignoreDiscriminator) {
|
|
|
673
707
|
params: json["params"]
|
|
674
708
|
};
|
|
675
709
|
}
|
|
710
|
+
var BASE_PATH = "https://api.schematichq.com".replace(/\/+$/, "");
|
|
711
|
+
var Configuration = class {
|
|
712
|
+
constructor(configuration = {}) {
|
|
713
|
+
this.configuration = configuration;
|
|
714
|
+
}
|
|
715
|
+
configuration;
|
|
716
|
+
set config(configuration) {
|
|
717
|
+
this.configuration = configuration;
|
|
718
|
+
}
|
|
719
|
+
get basePath() {
|
|
720
|
+
return this.configuration.basePath != null ? this.configuration.basePath : BASE_PATH;
|
|
721
|
+
}
|
|
722
|
+
get fetchApi() {
|
|
723
|
+
return this.configuration.fetchApi;
|
|
724
|
+
}
|
|
725
|
+
get middleware() {
|
|
726
|
+
return this.configuration.middleware || [];
|
|
727
|
+
}
|
|
728
|
+
get queryParamsStringify() {
|
|
729
|
+
return this.configuration.queryParamsStringify || querystring;
|
|
730
|
+
}
|
|
731
|
+
get username() {
|
|
732
|
+
return this.configuration.username;
|
|
733
|
+
}
|
|
734
|
+
get password() {
|
|
735
|
+
return this.configuration.password;
|
|
736
|
+
}
|
|
737
|
+
get apiKey() {
|
|
738
|
+
const apiKey = this.configuration.apiKey;
|
|
739
|
+
if (apiKey) {
|
|
740
|
+
return typeof apiKey === "function" ? apiKey : () => apiKey;
|
|
741
|
+
}
|
|
742
|
+
return void 0;
|
|
743
|
+
}
|
|
744
|
+
get accessToken() {
|
|
745
|
+
const accessToken = this.configuration.accessToken;
|
|
746
|
+
if (accessToken) {
|
|
747
|
+
return typeof accessToken === "function" ? accessToken : async () => accessToken;
|
|
748
|
+
}
|
|
749
|
+
return void 0;
|
|
750
|
+
}
|
|
751
|
+
get headers() {
|
|
752
|
+
return this.configuration.headers;
|
|
753
|
+
}
|
|
754
|
+
get credentials() {
|
|
755
|
+
return this.configuration.credentials;
|
|
756
|
+
}
|
|
757
|
+
};
|
|
758
|
+
var DefaultConfig = new Configuration();
|
|
759
|
+
var BaseAPI = class _BaseAPI {
|
|
760
|
+
constructor(configuration = DefaultConfig) {
|
|
761
|
+
this.configuration = configuration;
|
|
762
|
+
this.middleware = configuration.middleware;
|
|
763
|
+
}
|
|
764
|
+
configuration;
|
|
765
|
+
static jsonRegex = new RegExp(
|
|
766
|
+
"^(:?application/json|[^;/ ]+/[^;/ ]+[+]json)[ ]*(:?;.*)?$",
|
|
767
|
+
"i"
|
|
768
|
+
);
|
|
769
|
+
middleware;
|
|
770
|
+
withMiddleware(...middlewares) {
|
|
771
|
+
const next = this.clone();
|
|
772
|
+
next.middleware = next.middleware.concat(...middlewares);
|
|
773
|
+
return next;
|
|
774
|
+
}
|
|
775
|
+
withPreMiddleware(...preMiddlewares) {
|
|
776
|
+
const middlewares = preMiddlewares.map((pre) => ({ pre }));
|
|
777
|
+
return this.withMiddleware(...middlewares);
|
|
778
|
+
}
|
|
779
|
+
withPostMiddleware(...postMiddlewares) {
|
|
780
|
+
const middlewares = postMiddlewares.map((post) => ({ post }));
|
|
781
|
+
return this.withMiddleware(...middlewares);
|
|
782
|
+
}
|
|
783
|
+
/**
|
|
784
|
+
* Check if the given MIME is a JSON MIME.
|
|
785
|
+
* JSON MIME examples:
|
|
786
|
+
* application/json
|
|
787
|
+
* application/json; charset=UTF8
|
|
788
|
+
* APPLICATION/JSON
|
|
789
|
+
* application/vnd.company+json
|
|
790
|
+
* @param mime - MIME (Multipurpose Internet Mail Extensions)
|
|
791
|
+
* @return True if the given MIME is JSON, false otherwise.
|
|
792
|
+
*/
|
|
793
|
+
isJsonMime(mime) {
|
|
794
|
+
if (!mime) {
|
|
795
|
+
return false;
|
|
796
|
+
}
|
|
797
|
+
return _BaseAPI.jsonRegex.test(mime);
|
|
798
|
+
}
|
|
799
|
+
async request(context, initOverrides) {
|
|
800
|
+
const { url, init } = await this.createFetchParams(context, initOverrides);
|
|
801
|
+
const response = await this.fetchApi(url, init);
|
|
802
|
+
if (response && response.status >= 200 && response.status < 300) {
|
|
803
|
+
return response;
|
|
804
|
+
}
|
|
805
|
+
throw new ResponseError(response, "Response returned an error code");
|
|
806
|
+
}
|
|
807
|
+
async createFetchParams(context, initOverrides) {
|
|
808
|
+
let url = this.configuration.basePath + context.path;
|
|
809
|
+
if (context.query !== void 0 && Object.keys(context.query).length !== 0) {
|
|
810
|
+
url += "?" + this.configuration.queryParamsStringify(context.query);
|
|
811
|
+
}
|
|
812
|
+
const headers = Object.assign(
|
|
813
|
+
{},
|
|
814
|
+
this.configuration.headers,
|
|
815
|
+
context.headers
|
|
816
|
+
);
|
|
817
|
+
Object.keys(headers).forEach(
|
|
818
|
+
(key) => headers[key] === void 0 ? delete headers[key] : {}
|
|
819
|
+
);
|
|
820
|
+
const initOverrideFn = typeof initOverrides === "function" ? initOverrides : async () => initOverrides;
|
|
821
|
+
const initParams = {
|
|
822
|
+
method: context.method,
|
|
823
|
+
headers,
|
|
824
|
+
body: context.body,
|
|
825
|
+
credentials: this.configuration.credentials
|
|
826
|
+
};
|
|
827
|
+
const overriddenInit = {
|
|
828
|
+
...initParams,
|
|
829
|
+
...await initOverrideFn({
|
|
830
|
+
init: initParams,
|
|
831
|
+
context
|
|
832
|
+
})
|
|
833
|
+
};
|
|
834
|
+
let body;
|
|
835
|
+
if (isFormData(overriddenInit.body) || overriddenInit.body instanceof URLSearchParams || isBlob(overriddenInit.body)) {
|
|
836
|
+
body = overriddenInit.body;
|
|
837
|
+
} else if (this.isJsonMime(headers["Content-Type"])) {
|
|
838
|
+
body = JSON.stringify(overriddenInit.body);
|
|
839
|
+
} else {
|
|
840
|
+
body = overriddenInit.body;
|
|
841
|
+
}
|
|
842
|
+
const init = {
|
|
843
|
+
...overriddenInit,
|
|
844
|
+
body
|
|
845
|
+
};
|
|
846
|
+
return { url, init };
|
|
847
|
+
}
|
|
848
|
+
fetchApi = async (url, init) => {
|
|
849
|
+
let fetchParams = { url, init };
|
|
850
|
+
for (const middleware of this.middleware) {
|
|
851
|
+
if (middleware.pre) {
|
|
852
|
+
fetchParams = await middleware.pre({
|
|
853
|
+
fetch: this.fetchApi,
|
|
854
|
+
...fetchParams
|
|
855
|
+
}) || fetchParams;
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
let response = void 0;
|
|
859
|
+
try {
|
|
860
|
+
response = await (this.configuration.fetchApi || fetch)(
|
|
861
|
+
fetchParams.url,
|
|
862
|
+
fetchParams.init
|
|
863
|
+
);
|
|
864
|
+
} catch (e) {
|
|
865
|
+
for (const middleware of this.middleware) {
|
|
866
|
+
if (middleware.onError) {
|
|
867
|
+
response = await middleware.onError({
|
|
868
|
+
fetch: this.fetchApi,
|
|
869
|
+
url: fetchParams.url,
|
|
870
|
+
init: fetchParams.init,
|
|
871
|
+
error: e,
|
|
872
|
+
response: response ? response.clone() : void 0
|
|
873
|
+
}) || response;
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
if (response === void 0) {
|
|
877
|
+
if (e instanceof Error) {
|
|
878
|
+
throw new FetchError(
|
|
879
|
+
e,
|
|
880
|
+
"The request failed and the interceptors did not return an alternative response"
|
|
881
|
+
);
|
|
882
|
+
} else {
|
|
883
|
+
throw e;
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
for (const middleware of this.middleware) {
|
|
888
|
+
if (middleware.post) {
|
|
889
|
+
response = await middleware.post({
|
|
890
|
+
fetch: this.fetchApi,
|
|
891
|
+
url: fetchParams.url,
|
|
892
|
+
init: fetchParams.init,
|
|
893
|
+
response: response.clone()
|
|
894
|
+
}) || response;
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
return response;
|
|
898
|
+
};
|
|
899
|
+
/**
|
|
900
|
+
* Create a shallow clone of `this` by constructing a new instance
|
|
901
|
+
* and then shallow cloning data members.
|
|
902
|
+
*/
|
|
903
|
+
clone() {
|
|
904
|
+
const constructor = this.constructor;
|
|
905
|
+
const next = new constructor(this.configuration);
|
|
906
|
+
next.middleware = this.middleware.slice();
|
|
907
|
+
return next;
|
|
908
|
+
}
|
|
909
|
+
};
|
|
910
|
+
function isBlob(value) {
|
|
911
|
+
return typeof Blob !== "undefined" && value instanceof Blob;
|
|
912
|
+
}
|
|
913
|
+
function isFormData(value) {
|
|
914
|
+
return typeof FormData !== "undefined" && value instanceof FormData;
|
|
915
|
+
}
|
|
916
|
+
var ResponseError = class extends Error {
|
|
917
|
+
constructor(response, msg) {
|
|
918
|
+
super(msg);
|
|
919
|
+
this.response = response;
|
|
920
|
+
}
|
|
921
|
+
response;
|
|
922
|
+
name = "ResponseError";
|
|
923
|
+
};
|
|
924
|
+
var FetchError = class extends Error {
|
|
925
|
+
constructor(cause, msg) {
|
|
926
|
+
super(msg);
|
|
927
|
+
this.cause = cause;
|
|
928
|
+
}
|
|
929
|
+
cause;
|
|
930
|
+
name = "FetchError";
|
|
931
|
+
};
|
|
932
|
+
function querystring(params, prefix = "") {
|
|
933
|
+
return Object.keys(params).map((key) => querystringSingleKey(key, params[key], prefix)).filter((part) => part.length > 0).join("&");
|
|
934
|
+
}
|
|
935
|
+
function querystringSingleKey(key, value, keyPrefix = "") {
|
|
936
|
+
const fullKey = keyPrefix + (keyPrefix.length ? `[${key}]` : key);
|
|
937
|
+
if (value instanceof Array) {
|
|
938
|
+
const multiValue = value.map((singleValue) => encodeURIComponent(String(singleValue))).join(`&${encodeURIComponent(fullKey)}=`);
|
|
939
|
+
return `${encodeURIComponent(fullKey)}=${multiValue}`;
|
|
940
|
+
}
|
|
941
|
+
if (value instanceof Set) {
|
|
942
|
+
const valueAsArray = Array.from(value);
|
|
943
|
+
return querystringSingleKey(key, valueAsArray, keyPrefix);
|
|
944
|
+
}
|
|
945
|
+
if (value instanceof Date) {
|
|
946
|
+
return `${encodeURIComponent(fullKey)}=${encodeURIComponent(value.toISOString())}`;
|
|
947
|
+
}
|
|
948
|
+
if (value instanceof Object) {
|
|
949
|
+
return querystring(value, fullKey);
|
|
950
|
+
}
|
|
951
|
+
return `${encodeURIComponent(fullKey)}=${encodeURIComponent(String(value))}`;
|
|
952
|
+
}
|
|
953
|
+
function mapValues(data, fn) {
|
|
954
|
+
return Object.keys(data).reduce(
|
|
955
|
+
(acc, key) => ({ ...acc, [key]: fn(data[key]) }),
|
|
956
|
+
{}
|
|
957
|
+
);
|
|
958
|
+
}
|
|
959
|
+
function CompanyCreditBalanceFromJSON(json) {
|
|
960
|
+
return CompanyCreditBalanceFromJSONTyped(json, false);
|
|
961
|
+
}
|
|
962
|
+
function CompanyCreditBalanceFromJSONTyped(json, ignoreDiscriminator) {
|
|
963
|
+
if (json == null) {
|
|
964
|
+
return json;
|
|
965
|
+
}
|
|
966
|
+
return {
|
|
967
|
+
remaining: json["remaining"],
|
|
968
|
+
reserved: json["reserved"],
|
|
969
|
+
settled: json["settled"]
|
|
970
|
+
};
|
|
971
|
+
}
|
|
676
972
|
var TrialStatus = {
|
|
677
973
|
Active: "active",
|
|
678
974
|
Converted: "converted",
|
|
@@ -706,6 +1002,7 @@ function CheckFlagsResponseDataFromJSONTyped(json, ignoreDiscriminator) {
|
|
|
706
1002
|
return json;
|
|
707
1003
|
}
|
|
708
1004
|
return {
|
|
1005
|
+
creditBalances: json["credit_balances"] == null ? void 0 : mapValues(json["credit_balances"], CompanyCreditBalanceFromJSON),
|
|
709
1006
|
flags: json["flags"].map(CheckFlagResponseDataFromJSON),
|
|
710
1007
|
plan: json["plan"] == null ? void 0 : DatastreamCompanyPlanFromJSON(json["plan"])
|
|
711
1008
|
};
|
|
@@ -734,6 +1031,7 @@ function EventBodyFlagCheckToJSONTyped(value, ignoreDiscriminator = false) {
|
|
|
734
1031
|
error: value["error"],
|
|
735
1032
|
flag_id: value["flagId"],
|
|
736
1033
|
flag_key: value["flagKey"],
|
|
1034
|
+
preflight: value["preflight"],
|
|
737
1035
|
reason: value["reason"],
|
|
738
1036
|
req_company: value["reqCompany"],
|
|
739
1037
|
req_user: value["reqUser"],
|
|
@@ -788,7 +1086,10 @@ var CheckFlagReturnFromJSON = (json) => {
|
|
|
788
1086
|
return {
|
|
789
1087
|
featureUsageExceeded,
|
|
790
1088
|
companyId: companyId == null ? void 0 : companyId,
|
|
1089
|
+
creditId: entitlement?.creditId == null ? void 0 : entitlement.creditId,
|
|
791
1090
|
creditRemaining: entitlement?.creditRemaining == null ? void 0 : entitlement.creditRemaining,
|
|
1091
|
+
creditReserved: entitlement?.creditReserved == null ? void 0 : entitlement.creditReserved,
|
|
1092
|
+
creditSettled: entitlement?.creditSettled == null ? void 0 : entitlement.creditSettled,
|
|
792
1093
|
error: error == null ? void 0 : error,
|
|
793
1094
|
featureAllocation: resolvedAllocation == null ? void 0 : resolvedAllocation,
|
|
794
1095
|
featureUsage: resolvedUsage == null ? void 0 : resolvedUsage,
|
|
@@ -814,6 +1115,22 @@ var CheckPlanReturnFromJSON = (json) => {
|
|
|
814
1115
|
trialStatus: trialStatus == null ? void 0 : trialStatus
|
|
815
1116
|
};
|
|
816
1117
|
};
|
|
1118
|
+
var CreditBalancesFromJSON = (json) => {
|
|
1119
|
+
if (json == null || typeof json !== "object") {
|
|
1120
|
+
return {};
|
|
1121
|
+
}
|
|
1122
|
+
const balances = {};
|
|
1123
|
+
for (const [creditId, raw] of Object.entries(json)) {
|
|
1124
|
+
if (raw == null || typeof raw !== "object") continue;
|
|
1125
|
+
const { remaining, reserved, settled } = CompanyCreditBalanceFromJSON(raw);
|
|
1126
|
+
if (typeof remaining !== "number" || typeof reserved !== "number" || typeof settled !== "number") {
|
|
1127
|
+
continue;
|
|
1128
|
+
}
|
|
1129
|
+
balances[creditId] = { remaining, reserved, settled };
|
|
1130
|
+
}
|
|
1131
|
+
return balances;
|
|
1132
|
+
};
|
|
1133
|
+
var cacheVersion = "bb56e75d";
|
|
817
1134
|
function contextString(context) {
|
|
818
1135
|
const sortedContext = Object.keys(context).reduce((acc, key) => {
|
|
819
1136
|
const sortedKeys = Object.keys(
|
|
@@ -828,144 +1145,689 @@ function contextString(context) {
|
|
|
828
1145
|
}, {});
|
|
829
1146
|
return JSON.stringify(sortedContext);
|
|
830
1147
|
}
|
|
831
|
-
var version = "1.
|
|
832
|
-
var
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
}
|
|
905
|
-
this.additionalHeaders = {
|
|
906
|
-
"X-Schematic-Client-Version": `schematic-js@${version}`,
|
|
907
|
-
...options?.additionalHeaders ?? {}
|
|
908
|
-
};
|
|
909
|
-
if (options?.storage) {
|
|
910
|
-
this.storage = options.storage;
|
|
911
|
-
} else {
|
|
912
|
-
try {
|
|
913
|
-
if (typeof localStorage !== "undefined") {
|
|
914
|
-
this.storage = localStorage;
|
|
915
|
-
}
|
|
916
|
-
} catch {
|
|
1148
|
+
var version = "1.7.0";
|
|
1149
|
+
var models_exports = {};
|
|
1150
|
+
__export(models_exports, {
|
|
1151
|
+
ApiErrorFromJSON: () => ApiErrorFromJSON,
|
|
1152
|
+
ApiErrorFromJSONTyped: () => ApiErrorFromJSONTyped,
|
|
1153
|
+
ApiErrorToJSON: () => ApiErrorToJSON,
|
|
1154
|
+
ApiErrorToJSONTyped: () => ApiErrorToJSONTyped,
|
|
1155
|
+
CompanyInvoiceResponseDataFromJSON: () => CompanyInvoiceResponseDataFromJSON,
|
|
1156
|
+
CompanyInvoiceResponseDataFromJSONTyped: () => CompanyInvoiceResponseDataFromJSONTyped,
|
|
1157
|
+
CompanyInvoiceResponseDataToJSON: () => CompanyInvoiceResponseDataToJSON,
|
|
1158
|
+
CompanyInvoiceResponseDataToJSONTyped: () => CompanyInvoiceResponseDataToJSONTyped,
|
|
1159
|
+
CompanyInvoicesResponseDataFromJSON: () => CompanyInvoicesResponseDataFromJSON,
|
|
1160
|
+
CompanyInvoicesResponseDataFromJSONTyped: () => CompanyInvoicesResponseDataFromJSONTyped,
|
|
1161
|
+
CompanyInvoicesResponseDataToJSON: () => CompanyInvoicesResponseDataToJSON,
|
|
1162
|
+
CompanyInvoicesResponseDataToJSONTyped: () => CompanyInvoicesResponseDataToJSONTyped,
|
|
1163
|
+
GetCompanyInvoicesParamsFromJSON: () => GetCompanyInvoicesParamsFromJSON,
|
|
1164
|
+
GetCompanyInvoicesParamsFromJSONTyped: () => GetCompanyInvoicesParamsFromJSONTyped,
|
|
1165
|
+
GetCompanyInvoicesParamsToJSON: () => GetCompanyInvoicesParamsToJSON,
|
|
1166
|
+
GetCompanyInvoicesParamsToJSONTyped: () => GetCompanyInvoicesParamsToJSONTyped,
|
|
1167
|
+
GetCompanyInvoicesResponseFromJSON: () => GetCompanyInvoicesResponseFromJSON,
|
|
1168
|
+
GetCompanyInvoicesResponseFromJSONTyped: () => GetCompanyInvoicesResponseFromJSONTyped,
|
|
1169
|
+
GetCompanyInvoicesResponseToJSON: () => GetCompanyInvoicesResponseToJSON,
|
|
1170
|
+
GetCompanyInvoicesResponseToJSONTyped: () => GetCompanyInvoicesResponseToJSONTyped,
|
|
1171
|
+
InvoiceStatus: () => InvoiceStatus,
|
|
1172
|
+
InvoiceStatusFromJSON: () => InvoiceStatusFromJSON,
|
|
1173
|
+
InvoiceStatusFromJSONTyped: () => InvoiceStatusFromJSONTyped,
|
|
1174
|
+
InvoiceStatusToJSON: () => InvoiceStatusToJSON,
|
|
1175
|
+
InvoiceStatusToJSONTyped: () => InvoiceStatusToJSONTyped,
|
|
1176
|
+
instanceOfApiError: () => instanceOfApiError,
|
|
1177
|
+
instanceOfCompanyInvoiceResponseData: () => instanceOfCompanyInvoiceResponseData,
|
|
1178
|
+
instanceOfCompanyInvoicesResponseData: () => instanceOfCompanyInvoicesResponseData,
|
|
1179
|
+
instanceOfGetCompanyInvoicesParams: () => instanceOfGetCompanyInvoicesParams,
|
|
1180
|
+
instanceOfGetCompanyInvoicesResponse: () => instanceOfGetCompanyInvoicesResponse,
|
|
1181
|
+
instanceOfInvoiceStatus: () => instanceOfInvoiceStatus
|
|
1182
|
+
});
|
|
1183
|
+
function instanceOfApiError(value) {
|
|
1184
|
+
if (!("error" in value) || value["error"] === void 0) return false;
|
|
1185
|
+
return true;
|
|
1186
|
+
}
|
|
1187
|
+
function ApiErrorFromJSON(json) {
|
|
1188
|
+
return ApiErrorFromJSONTyped(json, false);
|
|
1189
|
+
}
|
|
1190
|
+
function ApiErrorFromJSONTyped(json, ignoreDiscriminator) {
|
|
1191
|
+
if (json == null) {
|
|
1192
|
+
return json;
|
|
1193
|
+
}
|
|
1194
|
+
return {
|
|
1195
|
+
error: json["error"]
|
|
1196
|
+
};
|
|
1197
|
+
}
|
|
1198
|
+
function ApiErrorToJSON(json) {
|
|
1199
|
+
return ApiErrorToJSONTyped(json, false);
|
|
1200
|
+
}
|
|
1201
|
+
function ApiErrorToJSONTyped(value, ignoreDiscriminator = false) {
|
|
1202
|
+
if (value == null) {
|
|
1203
|
+
return value;
|
|
1204
|
+
}
|
|
1205
|
+
return {
|
|
1206
|
+
error: value["error"]
|
|
1207
|
+
};
|
|
1208
|
+
}
|
|
1209
|
+
var InvoiceStatus = {
|
|
1210
|
+
Draft: "draft",
|
|
1211
|
+
Open: "open",
|
|
1212
|
+
Paid: "paid",
|
|
1213
|
+
Uncollectible: "uncollectible",
|
|
1214
|
+
Void: "void"
|
|
1215
|
+
};
|
|
1216
|
+
function instanceOfInvoiceStatus(value) {
|
|
1217
|
+
for (const key in InvoiceStatus) {
|
|
1218
|
+
if (Object.prototype.hasOwnProperty.call(InvoiceStatus, key)) {
|
|
1219
|
+
if (InvoiceStatus[key] === value) {
|
|
1220
|
+
return true;
|
|
917
1221
|
}
|
|
918
1222
|
}
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
1223
|
+
}
|
|
1224
|
+
return false;
|
|
1225
|
+
}
|
|
1226
|
+
function InvoiceStatusFromJSON(json) {
|
|
1227
|
+
return InvoiceStatusFromJSONTyped(json, false);
|
|
1228
|
+
}
|
|
1229
|
+
function InvoiceStatusFromJSONTyped(json, ignoreDiscriminator) {
|
|
1230
|
+
return json;
|
|
1231
|
+
}
|
|
1232
|
+
function InvoiceStatusToJSON(value) {
|
|
1233
|
+
return value;
|
|
1234
|
+
}
|
|
1235
|
+
function InvoiceStatusToJSONTyped(value, ignoreDiscriminator) {
|
|
1236
|
+
return value;
|
|
1237
|
+
}
|
|
1238
|
+
function instanceOfCompanyInvoiceResponseData(value) {
|
|
1239
|
+
if (!("amountDue" in value) || value["amountDue"] === void 0) return false;
|
|
1240
|
+
if (!("createdAt" in value) || value["createdAt"] === void 0) return false;
|
|
1241
|
+
if (!("currency" in value) || value["currency"] === void 0) return false;
|
|
1242
|
+
if (!("id" in value) || value["id"] === void 0) return false;
|
|
1243
|
+
return true;
|
|
1244
|
+
}
|
|
1245
|
+
function CompanyInvoiceResponseDataFromJSON(json) {
|
|
1246
|
+
return CompanyInvoiceResponseDataFromJSONTyped(json, false);
|
|
1247
|
+
}
|
|
1248
|
+
function CompanyInvoiceResponseDataFromJSONTyped(json, ignoreDiscriminator) {
|
|
1249
|
+
if (json == null) {
|
|
1250
|
+
return json;
|
|
1251
|
+
}
|
|
1252
|
+
return {
|
|
1253
|
+
amountDue: json["amount_due"],
|
|
1254
|
+
createdAt: new Date(json["created_at"]),
|
|
1255
|
+
currency: json["currency"],
|
|
1256
|
+
dueDate: json["due_date"] == null ? void 0 : new Date(json["due_date"]),
|
|
1257
|
+
id: json["id"],
|
|
1258
|
+
status: json["status"] == null ? void 0 : InvoiceStatusFromJSON(json["status"]),
|
|
1259
|
+
url: json["url"] == null ? void 0 : json["url"]
|
|
1260
|
+
};
|
|
1261
|
+
}
|
|
1262
|
+
function CompanyInvoiceResponseDataToJSON(json) {
|
|
1263
|
+
return CompanyInvoiceResponseDataToJSONTyped(json, false);
|
|
1264
|
+
}
|
|
1265
|
+
function CompanyInvoiceResponseDataToJSONTyped(value, ignoreDiscriminator = false) {
|
|
1266
|
+
if (value == null) {
|
|
1267
|
+
return value;
|
|
1268
|
+
}
|
|
1269
|
+
return {
|
|
1270
|
+
amount_due: value["amountDue"],
|
|
1271
|
+
created_at: value["createdAt"].toISOString(),
|
|
1272
|
+
currency: value["currency"],
|
|
1273
|
+
due_date: value["dueDate"] == null ? void 0 : value["dueDate"].toISOString(),
|
|
1274
|
+
id: value["id"],
|
|
1275
|
+
status: InvoiceStatusToJSON(value["status"]),
|
|
1276
|
+
url: value["url"]
|
|
1277
|
+
};
|
|
1278
|
+
}
|
|
1279
|
+
function instanceOfCompanyInvoicesResponseData(value) {
|
|
1280
|
+
if (!("count" in value) || value["count"] === void 0) return false;
|
|
1281
|
+
if (!("invoices" in value) || value["invoices"] === void 0) return false;
|
|
1282
|
+
return true;
|
|
1283
|
+
}
|
|
1284
|
+
function CompanyInvoicesResponseDataFromJSON(json) {
|
|
1285
|
+
return CompanyInvoicesResponseDataFromJSONTyped(json, false);
|
|
1286
|
+
}
|
|
1287
|
+
function CompanyInvoicesResponseDataFromJSONTyped(json, ignoreDiscriminator) {
|
|
1288
|
+
if (json == null) {
|
|
1289
|
+
return json;
|
|
1290
|
+
}
|
|
1291
|
+
return {
|
|
1292
|
+
count: json["count"],
|
|
1293
|
+
invoices: json["invoices"].map(
|
|
1294
|
+
CompanyInvoiceResponseDataFromJSON
|
|
1295
|
+
)
|
|
1296
|
+
};
|
|
1297
|
+
}
|
|
1298
|
+
function CompanyInvoicesResponseDataToJSON(json) {
|
|
1299
|
+
return CompanyInvoicesResponseDataToJSONTyped(json, false);
|
|
1300
|
+
}
|
|
1301
|
+
function CompanyInvoicesResponseDataToJSONTyped(value, ignoreDiscriminator = false) {
|
|
1302
|
+
if (value == null) {
|
|
1303
|
+
return value;
|
|
1304
|
+
}
|
|
1305
|
+
return {
|
|
1306
|
+
count: value["count"],
|
|
1307
|
+
invoices: value["invoices"].map(
|
|
1308
|
+
CompanyInvoiceResponseDataToJSON
|
|
1309
|
+
)
|
|
1310
|
+
};
|
|
1311
|
+
}
|
|
1312
|
+
function instanceOfGetCompanyInvoicesParams(value) {
|
|
1313
|
+
return true;
|
|
1314
|
+
}
|
|
1315
|
+
function GetCompanyInvoicesParamsFromJSON(json) {
|
|
1316
|
+
return GetCompanyInvoicesParamsFromJSONTyped(json, false);
|
|
1317
|
+
}
|
|
1318
|
+
function GetCompanyInvoicesParamsFromJSONTyped(json, ignoreDiscriminator) {
|
|
1319
|
+
if (json == null) {
|
|
1320
|
+
return json;
|
|
1321
|
+
}
|
|
1322
|
+
return {
|
|
1323
|
+
includePending: json["include_pending"] == null ? void 0 : json["include_pending"],
|
|
1324
|
+
limit: json["limit"] == null ? void 0 : json["limit"],
|
|
1325
|
+
offset: json["offset"] == null ? void 0 : json["offset"]
|
|
1326
|
+
};
|
|
1327
|
+
}
|
|
1328
|
+
function GetCompanyInvoicesParamsToJSON(json) {
|
|
1329
|
+
return GetCompanyInvoicesParamsToJSONTyped(json, false);
|
|
1330
|
+
}
|
|
1331
|
+
function GetCompanyInvoicesParamsToJSONTyped(value, ignoreDiscriminator = false) {
|
|
1332
|
+
if (value == null) {
|
|
1333
|
+
return value;
|
|
1334
|
+
}
|
|
1335
|
+
return {
|
|
1336
|
+
include_pending: value["includePending"],
|
|
1337
|
+
limit: value["limit"],
|
|
1338
|
+
offset: value["offset"]
|
|
1339
|
+
};
|
|
1340
|
+
}
|
|
1341
|
+
function instanceOfGetCompanyInvoicesResponse(value) {
|
|
1342
|
+
if (!("data" in value) || value["data"] === void 0) return false;
|
|
1343
|
+
if (!("params" in value) || value["params"] === void 0) return false;
|
|
1344
|
+
return true;
|
|
1345
|
+
}
|
|
1346
|
+
function GetCompanyInvoicesResponseFromJSON(json) {
|
|
1347
|
+
return GetCompanyInvoicesResponseFromJSONTyped(json, false);
|
|
1348
|
+
}
|
|
1349
|
+
function GetCompanyInvoicesResponseFromJSONTyped(json, ignoreDiscriminator) {
|
|
1350
|
+
if (json == null) {
|
|
1351
|
+
return json;
|
|
1352
|
+
}
|
|
1353
|
+
return {
|
|
1354
|
+
data: CompanyInvoicesResponseDataFromJSON(json["data"]),
|
|
1355
|
+
params: GetCompanyInvoicesParamsFromJSON(json["params"])
|
|
1356
|
+
};
|
|
1357
|
+
}
|
|
1358
|
+
function GetCompanyInvoicesResponseToJSON(json) {
|
|
1359
|
+
return GetCompanyInvoicesResponseToJSONTyped(json, false);
|
|
1360
|
+
}
|
|
1361
|
+
function GetCompanyInvoicesResponseToJSONTyped(value, ignoreDiscriminator = false) {
|
|
1362
|
+
if (value == null) {
|
|
1363
|
+
return value;
|
|
1364
|
+
}
|
|
1365
|
+
return {
|
|
1366
|
+
data: CompanyInvoicesResponseDataToJSON(value["data"]),
|
|
1367
|
+
params: GetCompanyInvoicesParamsToJSON(value["params"])
|
|
1368
|
+
};
|
|
1369
|
+
}
|
|
1370
|
+
var DEFAULT_INVOICE_QUERY = {};
|
|
1371
|
+
function normalizeInvoiceQuery(query) {
|
|
1372
|
+
const { includePending, ...rest } = query;
|
|
1373
|
+
return includePending === true ? { ...rest, includePending: true } : rest;
|
|
1374
|
+
}
|
|
1375
|
+
var SchematicApiError = class extends Error {
|
|
1376
|
+
constructor(status, path, body) {
|
|
1377
|
+
super(
|
|
1378
|
+
typeof body === "object" && body !== null && "error" in body ? String(body.error) : `Request to ${path} failed with status ${status}`
|
|
1379
|
+
);
|
|
1380
|
+
this.status = status;
|
|
1381
|
+
this.path = path;
|
|
1382
|
+
this.body = body;
|
|
1383
|
+
this.name = "SchematicApiError";
|
|
1384
|
+
}
|
|
1385
|
+
status;
|
|
1386
|
+
path;
|
|
1387
|
+
body;
|
|
1388
|
+
};
|
|
1389
|
+
var DEFAULT_API_URL = "https://api.schematichq.com";
|
|
1390
|
+
var TOKEN_CACHE_SIZE = 10;
|
|
1391
|
+
function sessionKey(session) {
|
|
1392
|
+
return JSON.stringify([session.company, session.user ?? null]);
|
|
1393
|
+
}
|
|
1394
|
+
var SchematicSession = class {
|
|
1395
|
+
_apiUrl;
|
|
1396
|
+
_headers;
|
|
1397
|
+
_fetch;
|
|
1398
|
+
_session;
|
|
1399
|
+
_tokens = /* @__PURE__ */ new Map();
|
|
1400
|
+
_resolving = /* @__PURE__ */ new Map();
|
|
1401
|
+
/**
|
|
1402
|
+
* Bumped when the session's identity changes, and only then — the object
|
|
1403
|
+
* itself is replaced on every render. Anything asking "is this still the
|
|
1404
|
+
* session I started under?" counts generations rather than comparing.
|
|
1405
|
+
*/
|
|
1406
|
+
_generation = 0;
|
|
1407
|
+
_listeners = /* @__PURE__ */ new Set();
|
|
1408
|
+
constructor(options = {}) {
|
|
1409
|
+
this._session = options.session;
|
|
1410
|
+
this._apiUrl = (options.apiUrl ?? DEFAULT_API_URL).replace(/\/$/, "");
|
|
1411
|
+
this._headers = options.additionalHeaders ?? {};
|
|
1412
|
+
this._fetch = options.fetch ?? ((input, init) => globalThis.fetch(input, init));
|
|
1413
|
+
}
|
|
1414
|
+
get key() {
|
|
1415
|
+
return this._session === null || this._session === void 0 ? void 0 : sessionKey(this._session);
|
|
1416
|
+
}
|
|
1417
|
+
get status() {
|
|
1418
|
+
if (this._session === void 0) {
|
|
1419
|
+
return "pending";
|
|
1420
|
+
}
|
|
1421
|
+
return this._session === null ? "ended" : "active";
|
|
1422
|
+
}
|
|
1423
|
+
/** Only a statement that changes which pair is read counts as a change. */
|
|
1424
|
+
set(session) {
|
|
1425
|
+
if (session === void 0) {
|
|
1426
|
+
return;
|
|
1427
|
+
}
|
|
1428
|
+
const previous = this._session;
|
|
1429
|
+
this._session = session;
|
|
1430
|
+
if (session === null) {
|
|
1431
|
+
if (previous === null) {
|
|
1432
|
+
return;
|
|
1433
|
+
}
|
|
1434
|
+
this._tokens.clear();
|
|
1435
|
+
this._resolving.clear();
|
|
1436
|
+
this._generation += 1;
|
|
1437
|
+
this._emit({ type: "ended" });
|
|
1438
|
+
return;
|
|
1439
|
+
}
|
|
1440
|
+
if (previous !== null && previous !== void 0) {
|
|
1441
|
+
if (previous.company === session.company && (previous.user ?? null) === (session.user ?? null)) {
|
|
1442
|
+
return;
|
|
1443
|
+
}
|
|
1444
|
+
this._generation += 1;
|
|
1445
|
+
this._emit({ type: "changed" });
|
|
1446
|
+
return;
|
|
1447
|
+
}
|
|
1448
|
+
this._generation += 1;
|
|
1449
|
+
this._emit({ type: "started" });
|
|
1450
|
+
}
|
|
1451
|
+
onChange(listener) {
|
|
1452
|
+
this._listeners.add(listener);
|
|
1453
|
+
return () => {
|
|
1454
|
+
this._listeners.delete(listener);
|
|
1455
|
+
};
|
|
1456
|
+
}
|
|
1457
|
+
_emit(event) {
|
|
1458
|
+
for (const listener of this._listeners) {
|
|
1459
|
+
listener(event);
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
_cached(key) {
|
|
1463
|
+
const cached = this._tokens.get(key);
|
|
1464
|
+
if (cached === void 0) {
|
|
1465
|
+
return void 0;
|
|
1466
|
+
}
|
|
1467
|
+
if (cached.expiresAt !== void 0 && cached.expiresAt.getTime() <= Date.now()) {
|
|
1468
|
+
this._tokens.delete(key);
|
|
1469
|
+
return void 0;
|
|
1470
|
+
}
|
|
1471
|
+
this._tokens.delete(key);
|
|
1472
|
+
this._tokens.set(key, cached);
|
|
1473
|
+
return cached;
|
|
1474
|
+
}
|
|
1475
|
+
_cache(key, resolved) {
|
|
1476
|
+
this._tokens.delete(key);
|
|
1477
|
+
this._tokens.set(key, resolved);
|
|
1478
|
+
while (this._tokens.size > TOKEN_CACHE_SIZE) {
|
|
1479
|
+
const oldest = this._tokens.keys().next();
|
|
1480
|
+
if (oldest.done === true) {
|
|
1481
|
+
break;
|
|
1482
|
+
}
|
|
1483
|
+
this._tokens.delete(oldest.value);
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
/**
|
|
1487
|
+
* The credential for a session, minted if this one is not held. `force`
|
|
1488
|
+
* retires what is held first: the caller is a request the API has just
|
|
1489
|
+
* rejected.
|
|
1490
|
+
*/
|
|
1491
|
+
resolveToken(session, force) {
|
|
1492
|
+
const { token } = session;
|
|
1493
|
+
if (typeof token === "string") {
|
|
1494
|
+
return Promise.resolve(token);
|
|
1495
|
+
}
|
|
1496
|
+
const key = sessionKey(session);
|
|
1497
|
+
if (!force) {
|
|
1498
|
+
const cached = this._cached(key);
|
|
1499
|
+
if (cached !== void 0) {
|
|
1500
|
+
return Promise.resolve(cached.token);
|
|
1501
|
+
}
|
|
1502
|
+
} else {
|
|
1503
|
+
this._tokens.delete(key);
|
|
1504
|
+
}
|
|
1505
|
+
const inflight = this._resolving.get(key);
|
|
1506
|
+
if (inflight !== void 0 && inflight.sessionGeneration === this._generation && (!force || inflight.forced)) {
|
|
1507
|
+
return inflight.promise;
|
|
1508
|
+
}
|
|
1509
|
+
const provider = token;
|
|
1510
|
+
const sessionGeneration = this._generation;
|
|
1511
|
+
const owns = () => this._resolving.get(key)?.promise === promise;
|
|
1512
|
+
const promise = Promise.resolve().then(() => provider()).then((result) => {
|
|
1513
|
+
const { resolved, stamped } = asResolvedToken(result, session);
|
|
1514
|
+
if (owns() && (this._generation === sessionGeneration || stamped)) {
|
|
1515
|
+
this._cache(key, resolved);
|
|
1516
|
+
}
|
|
1517
|
+
return resolved.token;
|
|
1518
|
+
}).finally(() => {
|
|
1519
|
+
if (owns()) {
|
|
1520
|
+
this._resolving.delete(key);
|
|
1521
|
+
}
|
|
1522
|
+
});
|
|
1523
|
+
this._resolving.set(key, { promise, forced: force, sessionGeneration });
|
|
1524
|
+
return promise;
|
|
1525
|
+
}
|
|
1526
|
+
/**
|
|
1527
|
+
* Sends a request under the active session's credential, refreshing once
|
|
1528
|
+
* and retrying if the API rejects it. Rejects rather than answering when
|
|
1529
|
+
* the session changes while the request is in flight: the rows would
|
|
1530
|
+
* otherwise come back as the answer to a question asked for another pair.
|
|
1531
|
+
*/
|
|
1532
|
+
async request(path, options = {}) {
|
|
1533
|
+
const { method = "GET", body } = options;
|
|
1534
|
+
const send = async (credential2) => this._fetch(`${this._apiUrl}${path}`, {
|
|
1535
|
+
method,
|
|
1536
|
+
headers: {
|
|
1537
|
+
"Accept": "application/json",
|
|
1538
|
+
...body === void 0 ? {} : { "Content-Type": "application/json" },
|
|
1539
|
+
...this._headers,
|
|
1540
|
+
"X-Schematic-Api-Key": credential2
|
|
1541
|
+
},
|
|
1542
|
+
...body === void 0 ? {} : { body: JSON.stringify(body) }
|
|
1543
|
+
});
|
|
1544
|
+
const session = this._session;
|
|
1545
|
+
if (session === null) {
|
|
1546
|
+
throw new Error("There is no session to read data for.");
|
|
1547
|
+
}
|
|
1548
|
+
if (session === void 0) {
|
|
1549
|
+
throw new Error("A session is required to read data.");
|
|
1550
|
+
}
|
|
1551
|
+
const provider = session.token;
|
|
1552
|
+
const generation = this._generation;
|
|
1553
|
+
const stillOurs = async (response2) => {
|
|
1554
|
+
if (this._generation === generation) {
|
|
1555
|
+
return;
|
|
1556
|
+
}
|
|
1557
|
+
if (response2 !== void 0) {
|
|
1558
|
+
await discardBody(response2);
|
|
1559
|
+
}
|
|
1560
|
+
throw new Error("The session changed while this request was in flight.");
|
|
1561
|
+
};
|
|
1562
|
+
const credential = await this.resolveToken(session, false);
|
|
1563
|
+
if (this._generation !== generation) {
|
|
1564
|
+
throw new Error("The session changed before this request was sent.");
|
|
1565
|
+
}
|
|
1566
|
+
let response = await send(credential);
|
|
1567
|
+
await stillOurs(response);
|
|
1568
|
+
if (response.status === 401 && typeof provider === "function" && this._generation === generation) {
|
|
1569
|
+
const status = response.status;
|
|
1570
|
+
const rejected = await readBody(response);
|
|
1571
|
+
let retried = false;
|
|
1572
|
+
const current = this._session;
|
|
1573
|
+
if (this._generation === generation && current !== null && current !== void 0) {
|
|
1574
|
+
const refreshed = await this.resolveToken(current, true);
|
|
1575
|
+
if (this._generation === generation) {
|
|
1576
|
+
response = await send(refreshed);
|
|
1577
|
+
await stillOurs(response);
|
|
1578
|
+
retried = true;
|
|
1579
|
+
}
|
|
1580
|
+
}
|
|
1581
|
+
if (!retried) {
|
|
1582
|
+
throw new SchematicApiError(status, path, rejected);
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
const parsed = await readBody(response);
|
|
1586
|
+
await stillOurs();
|
|
1587
|
+
if (!response.ok) {
|
|
1588
|
+
throw new SchematicApiError(response.status, path, parsed);
|
|
1589
|
+
}
|
|
1590
|
+
return parsed;
|
|
1591
|
+
}
|
|
1592
|
+
};
|
|
1593
|
+
function asResolvedToken(result, session) {
|
|
1594
|
+
if (typeof result === "string") {
|
|
1595
|
+
return { resolved: { token: result }, stamped: false };
|
|
1596
|
+
}
|
|
1597
|
+
const token = result?.token;
|
|
1598
|
+
if (typeof token !== "string" || token === "") {
|
|
1599
|
+
throw new Error("The access token provider did not return a token.");
|
|
1600
|
+
}
|
|
1601
|
+
if (result.company !== void 0 && result.company !== session.company || result.user !== void 0 && session.user !== void 0 && result.user !== session.user) {
|
|
1602
|
+
throw new Error(
|
|
1603
|
+
"The access token provider returned a token for a different session."
|
|
1604
|
+
);
|
|
1605
|
+
}
|
|
1606
|
+
const stamped = result.company !== void 0 && (session.user === void 0 || result.user !== void 0);
|
|
1607
|
+
return { resolved: { token, expiresAt: asDate(result.expiresAt) }, stamped };
|
|
1608
|
+
}
|
|
1609
|
+
function asDate(value) {
|
|
1610
|
+
if (value === void 0 || value === null) {
|
|
1611
|
+
return void 0;
|
|
1612
|
+
}
|
|
1613
|
+
const date = value instanceof Date ? value : new Date(value);
|
|
1614
|
+
return Number.isNaN(date.getTime()) ? void 0 : date;
|
|
1615
|
+
}
|
|
1616
|
+
async function discardBody(response) {
|
|
1617
|
+
try {
|
|
1618
|
+
await response.body?.cancel();
|
|
1619
|
+
} catch {
|
|
1620
|
+
}
|
|
1621
|
+
}
|
|
1622
|
+
async function readBody(response) {
|
|
1623
|
+
const text = await response.text();
|
|
1624
|
+
if (text === "") {
|
|
1625
|
+
return null;
|
|
1626
|
+
}
|
|
1627
|
+
try {
|
|
1628
|
+
return JSON.parse(text);
|
|
1629
|
+
} catch {
|
|
1630
|
+
return text;
|
|
1631
|
+
}
|
|
1632
|
+
}
|
|
1633
|
+
var INVOICE_PAGE_SIZE = 12;
|
|
1634
|
+
var INVOICE_MAX_PAGE_SIZE = 250;
|
|
1635
|
+
var SchematicBillingClient = class {
|
|
1636
|
+
/**
|
|
1637
|
+
* Public so a second client over the same surface can be constructed on
|
|
1638
|
+
* this one rather than minting tokens of its own.
|
|
1639
|
+
*/
|
|
1640
|
+
session;
|
|
1641
|
+
constructor(options = {}) {
|
|
1642
|
+
this.session = options instanceof SchematicSession ? options : new SchematicSession(options);
|
|
1643
|
+
}
|
|
1644
|
+
get sessionKey() {
|
|
1645
|
+
return this.session.key;
|
|
1646
|
+
}
|
|
1647
|
+
get sessionStatus() {
|
|
1648
|
+
return this.session.status;
|
|
1649
|
+
}
|
|
1650
|
+
setSession(session) {
|
|
1651
|
+
this.session.set(session);
|
|
1652
|
+
}
|
|
1653
|
+
onSessionChange(listener) {
|
|
1654
|
+
return this.session.onChange(listener);
|
|
1655
|
+
}
|
|
1656
|
+
fetchInvoices(params) {
|
|
1657
|
+
const query = new URLSearchParams({
|
|
1658
|
+
limit: String(Math.min(params.limit, INVOICE_MAX_PAGE_SIZE)),
|
|
1659
|
+
offset: String(params.offset)
|
|
1660
|
+
});
|
|
1661
|
+
if (params.includePending !== void 0) {
|
|
1662
|
+
query.set("include_pending", String(params.includePending));
|
|
1663
|
+
}
|
|
1664
|
+
const path = `/company/invoices?${query}`;
|
|
1665
|
+
return this.session.request(path).then((body) => {
|
|
1666
|
+
if (body === null || typeof body !== "object" || !("data" in body)) {
|
|
1667
|
+
throw new Error(`Malformed response from ${path}`);
|
|
1668
|
+
}
|
|
1669
|
+
const data = body.data;
|
|
1670
|
+
if (data == null || data.invoices == null) {
|
|
1671
|
+
return { invoices: [], count: 0 };
|
|
1672
|
+
}
|
|
1673
|
+
const decoded = GetCompanyInvoicesResponseFromJSON(body).data;
|
|
1674
|
+
return { invoices: decoded.invoices, count: decoded.count };
|
|
1675
|
+
});
|
|
1676
|
+
}
|
|
1677
|
+
};
|
|
1678
|
+
var anonymousIdKey = "schematicId";
|
|
1679
|
+
var flagStateCachePrefix = "schematicCache";
|
|
1680
|
+
var flagStateCacheMaxContexts = 10;
|
|
1681
|
+
var flagStateCacheDefaultMaxAgeMs = 7 * 24 * 60 * 60 * 1e3;
|
|
1682
|
+
var emptyCreditBalances = Object.freeze({});
|
|
1683
|
+
var Schematic = class {
|
|
1684
|
+
_additionalHeaders = {};
|
|
1685
|
+
apiKey;
|
|
1686
|
+
_apiUrl = "https://api.schematichq.com";
|
|
1687
|
+
conn = null;
|
|
1688
|
+
context = {};
|
|
1689
|
+
debugEnabled = false;
|
|
1690
|
+
offlineEnabled = false;
|
|
1691
|
+
eventQueue;
|
|
1692
|
+
contextDependentEventQueue;
|
|
1693
|
+
eventUrl = "https://c.schematichq.com";
|
|
1694
|
+
flagCheckListeners = {};
|
|
1695
|
+
flagValueListeners = {};
|
|
1696
|
+
isPending = true;
|
|
1697
|
+
isPendingListeners = /* @__PURE__ */ new Set();
|
|
1698
|
+
planListeners = /* @__PURE__ */ new Set();
|
|
1699
|
+
creditBalanceListeners = /* @__PURE__ */ new Set();
|
|
1700
|
+
storage;
|
|
1701
|
+
persistFlagState = true;
|
|
1702
|
+
flagStateCacheKey;
|
|
1703
|
+
flagStateCacheMaxAgeMs = flagStateCacheDefaultMaxAgeMs;
|
|
1704
|
+
cachedFlagState = null;
|
|
1705
|
+
useWebSocket = false;
|
|
1706
|
+
checks = {};
|
|
1707
|
+
featureUsageEventMap = {};
|
|
1708
|
+
planChecks = {};
|
|
1709
|
+
creditBalances = {};
|
|
1710
|
+
webSocketUrl = "wss://api.schematichq.com";
|
|
1711
|
+
webSocketConnectionTimeout = 1e4;
|
|
1712
|
+
webSocketReconnect = true;
|
|
1713
|
+
webSocketMaxReconnectAttempts = 7;
|
|
1714
|
+
// Max attempts after connection disrupted
|
|
1715
|
+
webSocketMaxConnectionAttempts = 3;
|
|
1716
|
+
// Max attempts for initial connection
|
|
1717
|
+
webSocketInitialRetryDelay = 1e3;
|
|
1718
|
+
webSocketMaxRetryDelay = 3e4;
|
|
1719
|
+
wsReconnectAttempts = 0;
|
|
1720
|
+
wsReconnectTimer = null;
|
|
1721
|
+
wsIntentionalDisconnect = false;
|
|
1722
|
+
currentWebSocket = null;
|
|
1723
|
+
isConnecting = false;
|
|
1724
|
+
maxEventQueueSize = 100;
|
|
1725
|
+
// Prevent memory issues with very long network outages
|
|
1726
|
+
maxEventRetries = 5;
|
|
1727
|
+
// Maximum retry attempts for failed events
|
|
1728
|
+
eventRetryInitialDelay = 1e3;
|
|
1729
|
+
// Initial retry delay in ms
|
|
1730
|
+
eventRetryMaxDelay = 3e4;
|
|
1731
|
+
// Maximum retry delay in ms
|
|
1732
|
+
retryTimer = null;
|
|
1733
|
+
flagValueDefaults = {};
|
|
1734
|
+
flagCheckDefaults = {};
|
|
1735
|
+
fallbackCheckCache = {};
|
|
1736
|
+
constructor(apiKey, options) {
|
|
1737
|
+
this.apiKey = apiKey;
|
|
1738
|
+
this.flagStateCacheKey = `${flagStateCachePrefix}:${apiKey}`;
|
|
1739
|
+
this.eventQueue = [];
|
|
1740
|
+
this.contextDependentEventQueue = [];
|
|
1741
|
+
this.useWebSocket = options?.useWebSocket ?? false;
|
|
1742
|
+
this.debugEnabled = options?.debug ?? false;
|
|
1743
|
+
this.offlineEnabled = options?.offline ?? false;
|
|
1744
|
+
this.persistFlagState = options?.persistFlagState ?? true;
|
|
1745
|
+
if (typeof options?.flagStateCacheMaxAgeMs === "number" && options.flagStateCacheMaxAgeMs > 0) {
|
|
1746
|
+
this.flagStateCacheMaxAgeMs = options.flagStateCacheMaxAgeMs;
|
|
1747
|
+
}
|
|
1748
|
+
if (typeof window !== "undefined" && typeof window.location !== "undefined") {
|
|
1749
|
+
const params = new URLSearchParams(window.location.search);
|
|
1750
|
+
const debugParam = params.get("schematic_debug");
|
|
1751
|
+
if (debugParam !== null && (debugParam === "" || debugParam === "true" || debugParam === "1")) {
|
|
1752
|
+
this.debugEnabled = true;
|
|
1753
|
+
}
|
|
1754
|
+
const offlineParam = params.get("schematic_offline");
|
|
1755
|
+
if (offlineParam !== null && (offlineParam === "" || offlineParam === "true" || offlineParam === "1")) {
|
|
1756
|
+
this.offlineEnabled = true;
|
|
1757
|
+
this.debugEnabled = true;
|
|
1758
|
+
}
|
|
1759
|
+
}
|
|
1760
|
+
if (this.offlineEnabled && options?.debug !== false) {
|
|
1761
|
+
this.debugEnabled = true;
|
|
1762
|
+
}
|
|
1763
|
+
if (this.offlineEnabled) {
|
|
1764
|
+
this.setIsPending(false);
|
|
1765
|
+
}
|
|
1766
|
+
this._additionalHeaders = {
|
|
1767
|
+
"X-Schematic-Client-Version": `schematic-js@${version}`,
|
|
1768
|
+
...options?.additionalHeaders ?? {}
|
|
1769
|
+
};
|
|
1770
|
+
if (options?.storage) {
|
|
1771
|
+
this.storage = options.storage;
|
|
1772
|
+
} else {
|
|
1773
|
+
try {
|
|
1774
|
+
if (typeof localStorage !== "undefined") {
|
|
1775
|
+
this.storage = localStorage;
|
|
1776
|
+
}
|
|
1777
|
+
} catch {
|
|
1778
|
+
}
|
|
1779
|
+
}
|
|
1780
|
+
this.hydrateFlagStateFromCache();
|
|
1781
|
+
if (options?.apiUrl !== void 0) {
|
|
1782
|
+
this._apiUrl = options.apiUrl;
|
|
1783
|
+
}
|
|
1784
|
+
if (options?.eventUrl !== void 0) {
|
|
1785
|
+
this.eventUrl = options.eventUrl;
|
|
1786
|
+
}
|
|
1787
|
+
if (options?.webSocketUrl !== void 0) {
|
|
1788
|
+
this.webSocketUrl = options.webSocketUrl;
|
|
1789
|
+
}
|
|
1790
|
+
if (options?.webSocketConnectionTimeout !== void 0) {
|
|
1791
|
+
this.webSocketConnectionTimeout = options.webSocketConnectionTimeout;
|
|
1792
|
+
}
|
|
1793
|
+
if (options?.webSocketReconnect !== void 0) {
|
|
1794
|
+
this.webSocketReconnect = options.webSocketReconnect;
|
|
1795
|
+
}
|
|
1796
|
+
if (options?.webSocketMaxReconnectAttempts !== void 0) {
|
|
1797
|
+
this.webSocketMaxReconnectAttempts = options.webSocketMaxReconnectAttempts;
|
|
1798
|
+
}
|
|
1799
|
+
if (options?.webSocketInitialRetryDelay !== void 0) {
|
|
1800
|
+
this.webSocketInitialRetryDelay = options.webSocketInitialRetryDelay;
|
|
1801
|
+
}
|
|
1802
|
+
if (options?.webSocketMaxRetryDelay !== void 0) {
|
|
1803
|
+
this.webSocketMaxRetryDelay = options.webSocketMaxRetryDelay;
|
|
1804
|
+
}
|
|
1805
|
+
if (options?.maxEventQueueSize !== void 0) {
|
|
1806
|
+
this.maxEventQueueSize = options.maxEventQueueSize;
|
|
1807
|
+
}
|
|
1808
|
+
if (options?.maxEventRetries !== void 0) {
|
|
1809
|
+
this.maxEventRetries = options.maxEventRetries;
|
|
1810
|
+
}
|
|
1811
|
+
if (options?.eventRetryInitialDelay !== void 0) {
|
|
1812
|
+
this.eventRetryInitialDelay = options.eventRetryInitialDelay;
|
|
1813
|
+
}
|
|
1814
|
+
if (options?.eventRetryMaxDelay !== void 0) {
|
|
1815
|
+
this.eventRetryMaxDelay = options.eventRetryMaxDelay;
|
|
1816
|
+
}
|
|
1817
|
+
if (options?.flagValueDefaults !== void 0) {
|
|
1818
|
+
this.flagValueDefaults = options.flagValueDefaults;
|
|
1819
|
+
}
|
|
1820
|
+
if (options?.flagCheckDefaults !== void 0) {
|
|
1821
|
+
this.flagCheckDefaults = options.flagCheckDefaults;
|
|
1822
|
+
}
|
|
1823
|
+
if (typeof window !== "undefined" && window?.addEventListener) {
|
|
1824
|
+
window.addEventListener("beforeunload", () => {
|
|
1825
|
+
this.flushEventQueue();
|
|
1826
|
+
this.flushContextDependentEventQueue();
|
|
1827
|
+
});
|
|
1828
|
+
if (this.useWebSocket) {
|
|
1829
|
+
window.addEventListener("offline", () => {
|
|
1830
|
+
this.debug("Browser went offline, closing WebSocket connection");
|
|
969
1831
|
this.handleNetworkOffline();
|
|
970
1832
|
});
|
|
971
1833
|
window.addEventListener("online", () => {
|
|
@@ -982,6 +1844,12 @@ var Schematic = class {
|
|
|
982
1844
|
this.debug("Initialized with debug mode enabled");
|
|
983
1845
|
}
|
|
984
1846
|
}
|
|
1847
|
+
get apiUrl() {
|
|
1848
|
+
return this._apiUrl;
|
|
1849
|
+
}
|
|
1850
|
+
get additionalHeaders() {
|
|
1851
|
+
return { ...this._additionalHeaders };
|
|
1852
|
+
}
|
|
985
1853
|
/**
|
|
986
1854
|
* Resolve fallback value according to priority order:
|
|
987
1855
|
* 1. Callsite fallback value (if provided)
|
|
@@ -1067,11 +1935,11 @@ var Schematic = class {
|
|
|
1067
1935
|
return resolvedFallbackResult.value;
|
|
1068
1936
|
}
|
|
1069
1937
|
if (!this.useWebSocket) {
|
|
1070
|
-
const requestUrl = `${this.
|
|
1938
|
+
const requestUrl = `${this._apiUrl}/flags/${key}/check`;
|
|
1071
1939
|
return fetch(requestUrl, {
|
|
1072
1940
|
method: "POST",
|
|
1073
1941
|
headers: {
|
|
1074
|
-
...this.
|
|
1942
|
+
...this._additionalHeaders ?? {},
|
|
1075
1943
|
"Content-Type": "application/json;charset=UTF-8",
|
|
1076
1944
|
"X-Schematic-Api-Key": this.apiKey
|
|
1077
1945
|
},
|
|
@@ -1209,6 +2077,29 @@ var Schematic = class {
|
|
|
1209
2077
|
});
|
|
1210
2078
|
this.notifyPlanListeners(plan);
|
|
1211
2079
|
}
|
|
2080
|
+
if (message.credit_balances !== void 0 && message.credit_balances !== null) {
|
|
2081
|
+
const contextStr = contextString(context);
|
|
2082
|
+
const updates = CreditBalancesFromJSON(message.credit_balances);
|
|
2083
|
+
const previous = this.creditBalances[contextStr] ?? {};
|
|
2084
|
+
let changed = false;
|
|
2085
|
+
const merged = { ...previous };
|
|
2086
|
+
for (const [creditId, next] of Object.entries(updates)) {
|
|
2087
|
+
const current = previous[creditId];
|
|
2088
|
+
if (current === void 0 || current.remaining !== next.remaining || current.reserved !== next.reserved || current.settled !== next.settled) {
|
|
2089
|
+
merged[creditId] = next;
|
|
2090
|
+
changed = true;
|
|
2091
|
+
}
|
|
2092
|
+
}
|
|
2093
|
+
if (changed) {
|
|
2094
|
+
this.creditBalances[contextStr] = merged;
|
|
2095
|
+
this.debug(
|
|
2096
|
+
`WebSocket credit balance update received. Notifying listeners`,
|
|
2097
|
+
{ creditBalances: merged }
|
|
2098
|
+
);
|
|
2099
|
+
this.notifyCreditBalanceListeners(merged);
|
|
2100
|
+
}
|
|
2101
|
+
}
|
|
2102
|
+
this.persistContextToCache(contextString(context));
|
|
1212
2103
|
this.flushContextDependentEventQueue();
|
|
1213
2104
|
this.setIsPending(false);
|
|
1214
2105
|
};
|
|
@@ -1251,12 +2142,12 @@ var Schematic = class {
|
|
|
1251
2142
|
this.debug(`checkFlags offline result: returning empty object`);
|
|
1252
2143
|
return {};
|
|
1253
2144
|
}
|
|
1254
|
-
const requestUrl = `${this.
|
|
2145
|
+
const requestUrl = `${this._apiUrl}/flags/check`;
|
|
1255
2146
|
const requestBody = JSON.stringify(context);
|
|
1256
2147
|
return fetch(requestUrl, {
|
|
1257
2148
|
method: "POST",
|
|
1258
2149
|
headers: {
|
|
1259
|
-
...this.
|
|
2150
|
+
...this._additionalHeaders ?? {},
|
|
1260
2151
|
"Content-Type": "application/json;charset=UTF-8",
|
|
1261
2152
|
"X-Schematic-Api-Key": this.apiKey
|
|
1262
2153
|
},
|
|
@@ -1316,7 +2207,32 @@ var Schematic = class {
|
|
|
1316
2207
|
return;
|
|
1317
2208
|
}
|
|
1318
2209
|
try {
|
|
1319
|
-
|
|
2210
|
+
const newContextStr = contextString(context);
|
|
2211
|
+
const cacheHit = this.hasCachedValuesForContext(newContextStr);
|
|
2212
|
+
if (cacheHit) {
|
|
2213
|
+
this.debug(
|
|
2214
|
+
`setContext: cache hit for context, pre-resolving from cached values`
|
|
2215
|
+
);
|
|
2216
|
+
this.context = context;
|
|
2217
|
+
this.flushContextDependentEventQueue();
|
|
2218
|
+
this.setIsPending(false);
|
|
2219
|
+
const cachedChecks = this.checks[newContextStr] ?? {};
|
|
2220
|
+
for (const [flagKey, check] of Object.entries(cachedChecks)) {
|
|
2221
|
+
if (check === void 0) continue;
|
|
2222
|
+
this.notifyFlagCheckListeners(flagKey, check);
|
|
2223
|
+
this.notifyFlagValueListeners(flagKey, check.value);
|
|
2224
|
+
}
|
|
2225
|
+
const cachedPlan = this.planChecks[newContextStr];
|
|
2226
|
+
if (cachedPlan !== void 0) {
|
|
2227
|
+
this.notifyPlanListeners(cachedPlan);
|
|
2228
|
+
}
|
|
2229
|
+
const cachedCreditBalances = this.creditBalances[newContextStr];
|
|
2230
|
+
if (cachedCreditBalances !== void 0) {
|
|
2231
|
+
this.notifyCreditBalanceListeners(cachedCreditBalances);
|
|
2232
|
+
}
|
|
2233
|
+
} else {
|
|
2234
|
+
this.setIsPending(true);
|
|
2235
|
+
}
|
|
1320
2236
|
if (!this.conn) {
|
|
1321
2237
|
if (this.isConnecting) {
|
|
1322
2238
|
this.debug(
|
|
@@ -1327,7 +2243,7 @@ var Schematic = class {
|
|
|
1327
2243
|
}
|
|
1328
2244
|
if (this.conn !== null) {
|
|
1329
2245
|
const socket2 = await this.conn;
|
|
1330
|
-
await this.wsSendMessage(socket2, context);
|
|
2246
|
+
await this.wsSendMessage(socket2, context, cacheHit);
|
|
1331
2247
|
return;
|
|
1332
2248
|
}
|
|
1333
2249
|
}
|
|
@@ -1343,7 +2259,7 @@ var Schematic = class {
|
|
|
1343
2259
|
this.conn = this.wsConnect();
|
|
1344
2260
|
const socket2 = await this.conn;
|
|
1345
2261
|
this.isConnecting = false;
|
|
1346
|
-
await this.wsSendMessage(socket2, context);
|
|
2262
|
+
await this.wsSendMessage(socket2, context, cacheHit);
|
|
1347
2263
|
return;
|
|
1348
2264
|
} catch (error) {
|
|
1349
2265
|
this.isConnecting = false;
|
|
@@ -1351,7 +2267,7 @@ var Schematic = class {
|
|
|
1351
2267
|
}
|
|
1352
2268
|
}
|
|
1353
2269
|
const socket = await this.conn;
|
|
1354
|
-
await this.wsSendMessage(socket, context);
|
|
2270
|
+
await this.wsSendMessage(socket, context, cacheHit);
|
|
1355
2271
|
} catch (error) {
|
|
1356
2272
|
console.warn("Failed to establish WebSocket connection:", error);
|
|
1357
2273
|
this.context = context;
|
|
@@ -1411,10 +2327,12 @@ var Schematic = class {
|
|
|
1411
2327
|
`Optimistically updating feature usage for event: ${eventName}`,
|
|
1412
2328
|
{ quantity }
|
|
1413
2329
|
);
|
|
2330
|
+
let dirty = false;
|
|
1414
2331
|
Object.entries(flagsForEvent).forEach(([flagKey, check]) => {
|
|
1415
2332
|
if (check === void 0) return;
|
|
1416
2333
|
const updatedCheck = { ...check };
|
|
1417
2334
|
if (typeof updatedCheck.featureUsage === "number") {
|
|
2335
|
+
dirty = true;
|
|
1418
2336
|
updatedCheck.featureUsage += quantity;
|
|
1419
2337
|
if (typeof updatedCheck.featureAllocation === "number") {
|
|
1420
2338
|
const wasExceeded = updatedCheck.featureUsageExceeded === true;
|
|
@@ -1444,6 +2362,9 @@ var Schematic = class {
|
|
|
1444
2362
|
this.notifyFlagValueListeners(flagKey, updatedCheck.value);
|
|
1445
2363
|
}
|
|
1446
2364
|
});
|
|
2365
|
+
if (dirty) {
|
|
2366
|
+
this.persistContextToCache(contextString(this.context));
|
|
2367
|
+
}
|
|
1447
2368
|
};
|
|
1448
2369
|
/**
|
|
1449
2370
|
* Event processing
|
|
@@ -1546,6 +2467,206 @@ var Schematic = class {
|
|
|
1546
2467
|
this.storage.setItem(anonymousIdKey, generatedAnonymousId);
|
|
1547
2468
|
return generatedAnonymousId;
|
|
1548
2469
|
};
|
|
2470
|
+
hasCachedValuesForContext = (contextStr) => {
|
|
2471
|
+
const cachedChecks = this.checks[contextStr];
|
|
2472
|
+
if (cachedChecks !== void 0 && Object.keys(cachedChecks).length > 0) {
|
|
2473
|
+
return true;
|
|
2474
|
+
}
|
|
2475
|
+
if (this.planChecks[contextStr] !== void 0) {
|
|
2476
|
+
return true;
|
|
2477
|
+
}
|
|
2478
|
+
const cachedCreditBalances = this.creditBalances[contextStr];
|
|
2479
|
+
return cachedCreditBalances !== void 0 && Object.keys(cachedCreditBalances).length > 0;
|
|
2480
|
+
};
|
|
2481
|
+
readFlagStateCache = () => {
|
|
2482
|
+
if (!this.persistFlagState || this.storage === void 0) {
|
|
2483
|
+
return null;
|
|
2484
|
+
}
|
|
2485
|
+
let raw;
|
|
2486
|
+
try {
|
|
2487
|
+
raw = this.storage.getItem(this.flagStateCacheKey);
|
|
2488
|
+
} catch (error) {
|
|
2489
|
+
this.debug("Failed to read flag state cache:", error);
|
|
2490
|
+
return null;
|
|
2491
|
+
}
|
|
2492
|
+
if (raw === null || raw === void 0) {
|
|
2493
|
+
return null;
|
|
2494
|
+
}
|
|
2495
|
+
let parsed;
|
|
2496
|
+
try {
|
|
2497
|
+
parsed = typeof raw === "string" ? JSON.parse(raw) : raw;
|
|
2498
|
+
} catch (error) {
|
|
2499
|
+
this.debug("Flag state cache is not valid JSON, ignoring:", error);
|
|
2500
|
+
return null;
|
|
2501
|
+
}
|
|
2502
|
+
if (parsed === null || typeof parsed !== "object" || parsed.version !== cacheVersion || typeof parsed.contexts !== "object" || parsed.contexts === null) {
|
|
2503
|
+
this.debug(
|
|
2504
|
+
`Flag state cache invalid or version mismatch (expected ${cacheVersion}), ignoring`,
|
|
2505
|
+
{
|
|
2506
|
+
parsedType: typeof parsed,
|
|
2507
|
+
version: parsed?.version
|
|
2508
|
+
}
|
|
2509
|
+
);
|
|
2510
|
+
return null;
|
|
2511
|
+
}
|
|
2512
|
+
return parsed;
|
|
2513
|
+
};
|
|
2514
|
+
reviveCachedCheck = (raw) => {
|
|
2515
|
+
if (raw === null || typeof raw !== "object") return null;
|
|
2516
|
+
const obj = raw;
|
|
2517
|
+
if (typeof obj.flag !== "string" || typeof obj.value !== "boolean" || typeof obj.reason !== "string") {
|
|
2518
|
+
return null;
|
|
2519
|
+
}
|
|
2520
|
+
const revived = { ...obj };
|
|
2521
|
+
if (typeof revived.featureUsageResetAt === "string") {
|
|
2522
|
+
const d = new Date(revived.featureUsageResetAt);
|
|
2523
|
+
revived.featureUsageResetAt = isNaN(d.getTime()) ? void 0 : d;
|
|
2524
|
+
}
|
|
2525
|
+
return revived;
|
|
2526
|
+
};
|
|
2527
|
+
reviveCachedPlan = (raw) => {
|
|
2528
|
+
if (raw === null || typeof raw !== "object") return null;
|
|
2529
|
+
const obj = raw;
|
|
2530
|
+
if (typeof obj.id !== "string" || typeof obj.name !== "string") {
|
|
2531
|
+
return null;
|
|
2532
|
+
}
|
|
2533
|
+
const revived = { ...obj };
|
|
2534
|
+
if (typeof revived.trialEndDate === "string") {
|
|
2535
|
+
const d = new Date(revived.trialEndDate);
|
|
2536
|
+
revived.trialEndDate = isNaN(d.getTime()) ? void 0 : d;
|
|
2537
|
+
}
|
|
2538
|
+
return revived;
|
|
2539
|
+
};
|
|
2540
|
+
reviveCachedCreditBalances = (raw) => {
|
|
2541
|
+
if (raw === null || typeof raw !== "object") return null;
|
|
2542
|
+
const revived = {};
|
|
2543
|
+
for (const [creditId, value] of Object.entries(
|
|
2544
|
+
raw
|
|
2545
|
+
)) {
|
|
2546
|
+
if (value === null || typeof value !== "object") continue;
|
|
2547
|
+
const obj = value;
|
|
2548
|
+
if (typeof obj.remaining !== "number" || typeof obj.reserved !== "number" || typeof obj.settled !== "number") {
|
|
2549
|
+
continue;
|
|
2550
|
+
}
|
|
2551
|
+
revived[creditId] = {
|
|
2552
|
+
remaining: obj.remaining,
|
|
2553
|
+
reserved: obj.reserved,
|
|
2554
|
+
settled: obj.settled
|
|
2555
|
+
};
|
|
2556
|
+
}
|
|
2557
|
+
return Object.keys(revived).length > 0 ? revived : null;
|
|
2558
|
+
};
|
|
2559
|
+
hydrateFlagStateFromCache = () => {
|
|
2560
|
+
const parsed = this.readFlagStateCache();
|
|
2561
|
+
if (parsed === null) return;
|
|
2562
|
+
const cutoff = Date.now() - this.flagStateCacheMaxAgeMs;
|
|
2563
|
+
const fresh = {};
|
|
2564
|
+
let contextsLoaded = 0;
|
|
2565
|
+
let contextsExpired = 0;
|
|
2566
|
+
for (const [contextStr, entry] of Object.entries(parsed.contexts)) {
|
|
2567
|
+
if (entry === null || typeof entry !== "object") continue;
|
|
2568
|
+
if (typeof entry.updatedAt !== "number") continue;
|
|
2569
|
+
if (entry.updatedAt < cutoff) {
|
|
2570
|
+
contextsExpired += 1;
|
|
2571
|
+
continue;
|
|
2572
|
+
}
|
|
2573
|
+
const revivedChecks = {};
|
|
2574
|
+
if (entry.checks !== null && typeof entry.checks === "object") {
|
|
2575
|
+
for (const [flagKey, rawCheck] of Object.entries(entry.checks)) {
|
|
2576
|
+
const revived = this.reviveCachedCheck(rawCheck);
|
|
2577
|
+
if (revived === null || revived.flag !== flagKey) continue;
|
|
2578
|
+
revivedChecks[flagKey] = revived;
|
|
2579
|
+
if (typeof revived.featureUsageEvent === "string") {
|
|
2580
|
+
this.updateFeatureUsageEventMap(revived);
|
|
2581
|
+
}
|
|
2582
|
+
}
|
|
2583
|
+
if (Object.keys(revivedChecks).length > 0) {
|
|
2584
|
+
this.checks[contextStr] = revivedChecks;
|
|
2585
|
+
}
|
|
2586
|
+
}
|
|
2587
|
+
let revivedPlan;
|
|
2588
|
+
if (entry.plan !== void 0 && entry.plan !== null) {
|
|
2589
|
+
const p = this.reviveCachedPlan(entry.plan);
|
|
2590
|
+
if (p !== null) {
|
|
2591
|
+
revivedPlan = p;
|
|
2592
|
+
this.planChecks[contextStr] = p;
|
|
2593
|
+
}
|
|
2594
|
+
}
|
|
2595
|
+
let revivedCreditBalances;
|
|
2596
|
+
if (entry.creditBalances !== void 0 && entry.creditBalances !== null) {
|
|
2597
|
+
const balances = this.reviveCachedCreditBalances(entry.creditBalances);
|
|
2598
|
+
if (balances !== null) {
|
|
2599
|
+
revivedCreditBalances = balances;
|
|
2600
|
+
this.creditBalances[contextStr] = balances;
|
|
2601
|
+
}
|
|
2602
|
+
}
|
|
2603
|
+
fresh[contextStr] = {
|
|
2604
|
+
checks: revivedChecks,
|
|
2605
|
+
plan: revivedPlan,
|
|
2606
|
+
creditBalances: revivedCreditBalances,
|
|
2607
|
+
updatedAt: entry.updatedAt
|
|
2608
|
+
};
|
|
2609
|
+
contextsLoaded += 1;
|
|
2610
|
+
}
|
|
2611
|
+
this.cachedFlagState = { version: cacheVersion, contexts: fresh };
|
|
2612
|
+
this.debug(
|
|
2613
|
+
`Hydrated flag state cache: ${contextsLoaded} loaded, ${contextsExpired} expired`
|
|
2614
|
+
);
|
|
2615
|
+
};
|
|
2616
|
+
persistContextToCache = (contextStr) => {
|
|
2617
|
+
const checksForContext = this.checks[contextStr];
|
|
2618
|
+
const planForContext = this.planChecks[contextStr];
|
|
2619
|
+
const creditBalancesForContext = this.creditBalances[contextStr];
|
|
2620
|
+
if (checksForContext === void 0 && planForContext === void 0 && creditBalancesForContext === void 0) {
|
|
2621
|
+
return;
|
|
2622
|
+
}
|
|
2623
|
+
if (this.cachedFlagState === null) {
|
|
2624
|
+
this.cachedFlagState = (this.persistFlagState ? this.readFlagStateCache() : null) ?? {
|
|
2625
|
+
version: cacheVersion,
|
|
2626
|
+
contexts: {}
|
|
2627
|
+
};
|
|
2628
|
+
}
|
|
2629
|
+
const cleanChecks = {};
|
|
2630
|
+
if (checksForContext !== void 0) {
|
|
2631
|
+
for (const [flagKey, check] of Object.entries(checksForContext)) {
|
|
2632
|
+
if (check !== void 0) cleanChecks[flagKey] = check;
|
|
2633
|
+
}
|
|
2634
|
+
}
|
|
2635
|
+
this.cachedFlagState.contexts[contextStr] = {
|
|
2636
|
+
checks: cleanChecks,
|
|
2637
|
+
plan: planForContext,
|
|
2638
|
+
creditBalances: creditBalancesForContext,
|
|
2639
|
+
updatedAt: Date.now()
|
|
2640
|
+
};
|
|
2641
|
+
const entries = Object.entries(this.cachedFlagState.contexts);
|
|
2642
|
+
if (entries.length > flagStateCacheMaxContexts) {
|
|
2643
|
+
entries.sort((a, b) => b[1].updatedAt - a[1].updatedAt);
|
|
2644
|
+
const survivors = entries.slice(0, flagStateCacheMaxContexts);
|
|
2645
|
+
const survivorKeys = new Set(survivors.map(([k]) => k));
|
|
2646
|
+
for (const key of Object.keys(this.checks)) {
|
|
2647
|
+
if (!survivorKeys.has(key)) delete this.checks[key];
|
|
2648
|
+
}
|
|
2649
|
+
for (const key of Object.keys(this.planChecks)) {
|
|
2650
|
+
if (!survivorKeys.has(key)) delete this.planChecks[key];
|
|
2651
|
+
}
|
|
2652
|
+
for (const key of Object.keys(this.creditBalances)) {
|
|
2653
|
+
if (!survivorKeys.has(key)) delete this.creditBalances[key];
|
|
2654
|
+
}
|
|
2655
|
+
this.cachedFlagState = {
|
|
2656
|
+
version: cacheVersion,
|
|
2657
|
+
contexts: Object.fromEntries(survivors)
|
|
2658
|
+
};
|
|
2659
|
+
}
|
|
2660
|
+
if (!this.persistFlagState || this.storage === void 0) return;
|
|
2661
|
+
try {
|
|
2662
|
+
this.storage.setItem(
|
|
2663
|
+
this.flagStateCacheKey,
|
|
2664
|
+
JSON.stringify(this.cachedFlagState)
|
|
2665
|
+
);
|
|
2666
|
+
} catch (error) {
|
|
2667
|
+
this.debug("Failed to write flag state cache:", error);
|
|
2668
|
+
}
|
|
2669
|
+
};
|
|
1549
2670
|
handleEvent = (eventType, eventBody) => {
|
|
1550
2671
|
const event = {
|
|
1551
2672
|
api_key: this.apiKey,
|
|
@@ -1573,7 +2694,7 @@ var Schematic = class {
|
|
|
1573
2694
|
const response = await fetch(captureUrl, {
|
|
1574
2695
|
method: "POST",
|
|
1575
2696
|
headers: {
|
|
1576
|
-
...this.
|
|
2697
|
+
...this._additionalHeaders ?? {},
|
|
1577
2698
|
"Content-Type": "application/json;charset=UTF-8"
|
|
1578
2699
|
},
|
|
1579
2700
|
body: payload
|
|
@@ -1806,6 +2927,9 @@ var Schematic = class {
|
|
|
1806
2927
|
if (this.conn !== null) {
|
|
1807
2928
|
try {
|
|
1808
2929
|
const socket = await this.conn;
|
|
2930
|
+
if (this.currentWebSocket === socket) {
|
|
2931
|
+
this.currentWebSocket = null;
|
|
2932
|
+
}
|
|
1809
2933
|
if (socket.readyState === WebSocket.OPEN || socket.readyState === WebSocket.CONNECTING) {
|
|
1810
2934
|
socket.close();
|
|
1811
2935
|
}
|
|
@@ -1971,6 +3095,7 @@ var Schematic = class {
|
|
|
1971
3095
|
this.debug(`Creating WebSocket connection ${connectionId} to ${wsUrl}`);
|
|
1972
3096
|
let timeoutId = null;
|
|
1973
3097
|
let isResolved = false;
|
|
3098
|
+
let didOpen = false;
|
|
1974
3099
|
timeoutId = setTimeout(() => {
|
|
1975
3100
|
if (!isResolved) {
|
|
1976
3101
|
isResolved = true;
|
|
@@ -1984,6 +3109,7 @@ var Schematic = class {
|
|
|
1984
3109
|
webSocket.onopen = () => {
|
|
1985
3110
|
if (isResolved) return;
|
|
1986
3111
|
isResolved = true;
|
|
3112
|
+
didOpen = true;
|
|
1987
3113
|
if (timeoutId !== null) {
|
|
1988
3114
|
clearTimeout(timeoutId);
|
|
1989
3115
|
}
|
|
@@ -2009,11 +3135,12 @@ var Schematic = class {
|
|
|
2009
3135
|
}
|
|
2010
3136
|
this.debug(`WebSocket connection ${connectionId} closed`);
|
|
2011
3137
|
this.conn = null;
|
|
3138
|
+
const wasCurrent = this.currentWebSocket === webSocket;
|
|
2012
3139
|
if (this.currentWebSocket === webSocket) {
|
|
2013
3140
|
this.currentWebSocket = null;
|
|
2014
3141
|
this.isConnecting = false;
|
|
2015
3142
|
}
|
|
2016
|
-
if (
|
|
3143
|
+
if (wasCurrent && didOpen && !this.wsIntentionalDisconnect && this.webSocketReconnect) {
|
|
2017
3144
|
this.attemptReconnect();
|
|
2018
3145
|
}
|
|
2019
3146
|
};
|
|
@@ -2063,7 +3190,7 @@ var Schematic = class {
|
|
|
2063
3190
|
}
|
|
2064
3191
|
});
|
|
2065
3192
|
this.currentWebSocket = socket;
|
|
2066
|
-
const clientVersion = this.
|
|
3193
|
+
const clientVersion = this._additionalHeaders["X-Schematic-Client-Version"] ?? `schematic-js@${version}`;
|
|
2067
3194
|
const messagePayload = {
|
|
2068
3195
|
apiKey: this.apiKey,
|
|
2069
3196
|
clientVersion,
|
|
@@ -2109,6 +3236,16 @@ var Schematic = class {
|
|
|
2109
3236
|
const plan = this.planChecks[contextStr];
|
|
2110
3237
|
return plan;
|
|
2111
3238
|
};
|
|
3239
|
+
/** Get the company's lease-aware credit balances for the current context, keyed by credit ID */
|
|
3240
|
+
getCreditBalances = () => {
|
|
3241
|
+
const contextStr = contextString(this.context);
|
|
3242
|
+
return this.creditBalances[contextStr] ?? emptyCreditBalances;
|
|
3243
|
+
};
|
|
3244
|
+
/** Get the company's lease-aware balance for a single credit type in the current context */
|
|
3245
|
+
getCreditBalance = (creditId) => {
|
|
3246
|
+
const contextStr = contextString(this.context);
|
|
3247
|
+
return this.creditBalances[contextStr]?.[creditId];
|
|
3248
|
+
};
|
|
2112
3249
|
// flag checks state
|
|
2113
3250
|
getFlagCheck = (flagKey) => {
|
|
2114
3251
|
const contextStr = contextString(this.context);
|
|
@@ -2168,6 +3305,13 @@ var Schematic = class {
|
|
|
2168
3305
|
this.planListeners.delete(listener);
|
|
2169
3306
|
};
|
|
2170
3307
|
};
|
|
3308
|
+
/** Register an event listener that will be notified with the company's credit balances (keyed by credit ID) whenever they change */
|
|
3309
|
+
addCreditBalanceListener = (listener) => {
|
|
3310
|
+
this.creditBalanceListeners.add(listener);
|
|
3311
|
+
return () => {
|
|
3312
|
+
this.creditBalanceListeners.delete(listener);
|
|
3313
|
+
};
|
|
3314
|
+
};
|
|
2171
3315
|
notifyFlagCheckListeners = (flagKey, check) => {
|
|
2172
3316
|
const listeners = this.flagCheckListeners?.[flagKey] ?? [];
|
|
2173
3317
|
if (listeners.size > 0) {
|
|
@@ -2231,6 +3375,17 @@ var Schematic = class {
|
|
|
2231
3375
|
});
|
|
2232
3376
|
});
|
|
2233
3377
|
};
|
|
3378
|
+
notifyCreditBalanceListeners = (value) => {
|
|
3379
|
+
const listeners = this.creditBalanceListeners ?? [];
|
|
3380
|
+
if (listeners.size > 0) {
|
|
3381
|
+
this.debug(`Notifying ${listeners.size} credit balance listeners`, {
|
|
3382
|
+
value
|
|
3383
|
+
});
|
|
3384
|
+
}
|
|
3385
|
+
listeners.forEach(
|
|
3386
|
+
(listener) => notifyCreditBalanceListener(listener, value)
|
|
3387
|
+
);
|
|
3388
|
+
};
|
|
2234
3389
|
};
|
|
2235
3390
|
var notifyPendingListener = (listener, value) => {
|
|
2236
3391
|
if (listener.length > 0) {
|
|
@@ -2260,25 +3415,862 @@ var notifyPlanListener = (listener, value) => {
|
|
|
2260
3415
|
listener();
|
|
2261
3416
|
}
|
|
2262
3417
|
};
|
|
3418
|
+
var notifyCreditBalanceListener = (listener, value) => {
|
|
3419
|
+
if (listener.length > 0) {
|
|
3420
|
+
listener(value);
|
|
3421
|
+
} else {
|
|
3422
|
+
listener();
|
|
3423
|
+
}
|
|
3424
|
+
};
|
|
2263
3425
|
|
|
2264
3426
|
// src/context/schematic.tsx
|
|
2265
|
-
import
|
|
3427
|
+
import React4, { createContext as createContext3, useEffect as useEffect2, useMemo as useMemo5, useRef as useRef3 } from "react";
|
|
3428
|
+
|
|
3429
|
+
// src/billing/contract/index.ts
|
|
3430
|
+
function normalizeInvoiceQuery2(query) {
|
|
3431
|
+
return normalizeInvoiceQuery(query).includePending === true ? { includePending: true } : {};
|
|
3432
|
+
}
|
|
3433
|
+
|
|
3434
|
+
// src/billing/store.ts
|
|
3435
|
+
var Resource = class {
|
|
3436
|
+
constructor(_fetcher, initial, _readiness = () => "ready") {
|
|
3437
|
+
this._fetcher = _fetcher;
|
|
3438
|
+
this._readiness = _readiness;
|
|
3439
|
+
this._loaded = initial !== void 0;
|
|
3440
|
+
this._snapshot = {
|
|
3441
|
+
data: initial,
|
|
3442
|
+
error: void 0,
|
|
3443
|
+
isPending: initial === void 0
|
|
3444
|
+
};
|
|
3445
|
+
}
|
|
3446
|
+
_fetcher;
|
|
3447
|
+
_readiness;
|
|
3448
|
+
_snapshot;
|
|
3449
|
+
_listeners = /* @__PURE__ */ new Set();
|
|
3450
|
+
_inflight;
|
|
3451
|
+
_extending;
|
|
3452
|
+
/** A refetch asked for mid-load; runs once that load lands. */
|
|
3453
|
+
_queued;
|
|
3454
|
+
_generation = 0;
|
|
3455
|
+
_loaded = false;
|
|
3456
|
+
get snapshot() {
|
|
3457
|
+
return this._snapshot;
|
|
3458
|
+
}
|
|
3459
|
+
get subscriberCount() {
|
|
3460
|
+
return this._listeners.size;
|
|
3461
|
+
}
|
|
3462
|
+
getSnapshot = () => this._snapshot;
|
|
3463
|
+
subscribe = (listener) => {
|
|
3464
|
+
this._listeners.add(listener);
|
|
3465
|
+
if (!this._loaded && this._inflight === void 0 && this._snapshot.error === void 0) {
|
|
3466
|
+
void this.load();
|
|
3467
|
+
}
|
|
3468
|
+
return () => {
|
|
3469
|
+
this._listeners.delete(listener);
|
|
3470
|
+
};
|
|
3471
|
+
};
|
|
3472
|
+
load() {
|
|
3473
|
+
if (this._inflight !== void 0) {
|
|
3474
|
+
return this._inflight;
|
|
3475
|
+
}
|
|
3476
|
+
const readiness = this._readiness();
|
|
3477
|
+
if (readiness !== "ready") {
|
|
3478
|
+
if (readiness === "never" && this._snapshot.isPending) {
|
|
3479
|
+
this._set({ ...this._snapshot, isPending: false });
|
|
3480
|
+
}
|
|
3481
|
+
return Promise.resolve();
|
|
3482
|
+
}
|
|
3483
|
+
const generation = ++this._generation;
|
|
3484
|
+
this._set({ ...this._snapshot, error: void 0, isPending: true });
|
|
3485
|
+
const run = Promise.resolve().then(() => this._fetcher()).then(
|
|
3486
|
+
(data) => {
|
|
3487
|
+
if (generation !== this._generation) return;
|
|
3488
|
+
this._loaded = true;
|
|
3489
|
+
this._set({ data, error: void 0, isPending: false });
|
|
3490
|
+
},
|
|
3491
|
+
(cause) => {
|
|
3492
|
+
if (generation !== this._generation) return;
|
|
3493
|
+
const error = cause instanceof Error ? cause : new Error(String(cause));
|
|
3494
|
+
this._set({ ...this._snapshot, error, isPending: false });
|
|
3495
|
+
}
|
|
3496
|
+
).finally(() => {
|
|
3497
|
+
if (generation === this._generation) {
|
|
3498
|
+
this._inflight = void 0;
|
|
3499
|
+
}
|
|
3500
|
+
});
|
|
3501
|
+
this._inflight = run;
|
|
3502
|
+
return run;
|
|
3503
|
+
}
|
|
3504
|
+
/**
|
|
3505
|
+
* Reloads; `data` holds its current value until the response lands. Asked
|
|
3506
|
+
* for mid-load, it runs again after: that request was built before
|
|
3507
|
+
* whatever prompted this one. Refetches queued on one load share the
|
|
3508
|
+
* re-run; a reset, seed, or clear in the meantime supersedes it.
|
|
3509
|
+
*/
|
|
3510
|
+
refetch() {
|
|
3511
|
+
if (this._inflight === void 0) {
|
|
3512
|
+
return this.load();
|
|
3513
|
+
}
|
|
3514
|
+
if (this._queued === void 0) {
|
|
3515
|
+
const generation = this._generation;
|
|
3516
|
+
const queued = this._inflight.then(() => {
|
|
3517
|
+
if (this._queued === queued) {
|
|
3518
|
+
this._queued = void 0;
|
|
3519
|
+
}
|
|
3520
|
+
if (generation !== this._generation) {
|
|
3521
|
+
return;
|
|
3522
|
+
}
|
|
3523
|
+
return this.load();
|
|
3524
|
+
});
|
|
3525
|
+
this._queued = queued;
|
|
3526
|
+
}
|
|
3527
|
+
return this._queued;
|
|
3528
|
+
}
|
|
3529
|
+
/**
|
|
3530
|
+
* Pages: `fetcher` receives the current data and returns what replaces it.
|
|
3531
|
+
* Never rejects — a caller that wants the failure reads `snapshot.error`,
|
|
3532
|
+
* and `data` keeps what was already fetched.
|
|
3533
|
+
*
|
|
3534
|
+
* A load already in flight wins: it is about to refresh the window this
|
|
3535
|
+
* would have appended to.
|
|
3536
|
+
*/
|
|
3537
|
+
extend(fetcher) {
|
|
3538
|
+
const current = this._snapshot.data;
|
|
3539
|
+
if (current === void 0) {
|
|
3540
|
+
return Promise.resolve();
|
|
3541
|
+
}
|
|
3542
|
+
if (this._readiness() !== "ready") {
|
|
3543
|
+
return Promise.resolve();
|
|
3544
|
+
}
|
|
3545
|
+
if (this._extending !== void 0) {
|
|
3546
|
+
return this._extending;
|
|
3547
|
+
}
|
|
3548
|
+
if (this._inflight !== void 0) {
|
|
3549
|
+
return this._inflight;
|
|
3550
|
+
}
|
|
3551
|
+
const generation = ++this._generation;
|
|
3552
|
+
this._set({ ...this._snapshot, error: void 0, isPending: true });
|
|
3553
|
+
const run = Promise.resolve().then(() => fetcher(current)).then(
|
|
3554
|
+
(data) => {
|
|
3555
|
+
if (generation !== this._generation) return;
|
|
3556
|
+
this._set({ data, error: void 0, isPending: false });
|
|
3557
|
+
},
|
|
3558
|
+
(cause) => {
|
|
3559
|
+
if (generation !== this._generation) return;
|
|
3560
|
+
const error = cause instanceof Error ? cause : new Error(String(cause));
|
|
3561
|
+
this._set({ ...this._snapshot, error, isPending: false });
|
|
3562
|
+
}
|
|
3563
|
+
).finally(() => {
|
|
3564
|
+
if (this._extending === run) {
|
|
3565
|
+
this._extending = void 0;
|
|
3566
|
+
}
|
|
3567
|
+
});
|
|
3568
|
+
this._extending = run;
|
|
3569
|
+
return run;
|
|
3570
|
+
}
|
|
3571
|
+
seed(data) {
|
|
3572
|
+
this._generation++;
|
|
3573
|
+
this._inflight = void 0;
|
|
3574
|
+
this._extending = void 0;
|
|
3575
|
+
this._queued = void 0;
|
|
3576
|
+
this._loaded = true;
|
|
3577
|
+
this._set({ data, error: void 0, isPending: false });
|
|
3578
|
+
}
|
|
3579
|
+
update(fn) {
|
|
3580
|
+
if (this._snapshot.data === void 0) return;
|
|
3581
|
+
this._generation++;
|
|
3582
|
+
this._inflight = void 0;
|
|
3583
|
+
this._extending = void 0;
|
|
3584
|
+
this._queued = void 0;
|
|
3585
|
+
this._set({ ...this._snapshot, data: fn(this._snapshot.data) });
|
|
3586
|
+
}
|
|
3587
|
+
/**
|
|
3588
|
+
* Forgets everything and stays empty — no data, no error, not pending.
|
|
3589
|
+
* What a resource holds when there is no session to hold it for: a session
|
|
3590
|
+
* ending is not a failure and must not surface as one.
|
|
3591
|
+
*/
|
|
3592
|
+
clear() {
|
|
3593
|
+
this._generation++;
|
|
3594
|
+
this._inflight = void 0;
|
|
3595
|
+
this._extending = void 0;
|
|
3596
|
+
this._queued = void 0;
|
|
3597
|
+
this._loaded = false;
|
|
3598
|
+
this._set({ data: void 0, error: void 0, isPending: false });
|
|
3599
|
+
}
|
|
3600
|
+
/** Forgets everything; reloads immediately if anyone is subscribed. */
|
|
3601
|
+
reset() {
|
|
3602
|
+
this._generation++;
|
|
3603
|
+
this._inflight = void 0;
|
|
3604
|
+
this._extending = void 0;
|
|
3605
|
+
this._queued = void 0;
|
|
3606
|
+
this._loaded = false;
|
|
3607
|
+
const readiness = this._readiness();
|
|
3608
|
+
const willLoad = this._listeners.size > 0 && readiness === "ready";
|
|
3609
|
+
this._set({
|
|
3610
|
+
data: void 0,
|
|
3611
|
+
error: void 0,
|
|
3612
|
+
isPending: willLoad || readiness === "waiting"
|
|
3613
|
+
});
|
|
3614
|
+
if (willLoad) {
|
|
3615
|
+
void this.load();
|
|
3616
|
+
}
|
|
3617
|
+
}
|
|
3618
|
+
_set(next) {
|
|
3619
|
+
this._snapshot = next;
|
|
3620
|
+
for (const listener of this._listeners) {
|
|
3621
|
+
listener();
|
|
3622
|
+
}
|
|
3623
|
+
}
|
|
3624
|
+
};
|
|
3625
|
+
function hashKey(params) {
|
|
3626
|
+
return JSON.stringify(canonical(params));
|
|
3627
|
+
}
|
|
3628
|
+
function canonical(value) {
|
|
3629
|
+
if (Array.isArray(value)) {
|
|
3630
|
+
return value.map(canonical);
|
|
3631
|
+
}
|
|
3632
|
+
if (value instanceof Date) {
|
|
3633
|
+
const time = value.getTime();
|
|
3634
|
+
return Number.isNaN(time) ? "Invalid Date" : value.toISOString();
|
|
3635
|
+
}
|
|
3636
|
+
if (value !== null && typeof value === "object") {
|
|
3637
|
+
const out = {};
|
|
3638
|
+
for (const key of Object.keys(value).sort()) {
|
|
3639
|
+
const v = value[key];
|
|
3640
|
+
if (v !== void 0) {
|
|
3641
|
+
out[key] = canonical(v);
|
|
3642
|
+
}
|
|
3643
|
+
}
|
|
3644
|
+
return out;
|
|
3645
|
+
}
|
|
3646
|
+
return value ?? null;
|
|
3647
|
+
}
|
|
3648
|
+
var DEFAULT_EVICTION = { maxIdle: 4 };
|
|
3649
|
+
var KeyedResource = class {
|
|
3650
|
+
constructor(_fetcher, options = {}) {
|
|
3651
|
+
this._fetcher = _fetcher;
|
|
3652
|
+
this._readiness = options.readiness ?? (() => "ready");
|
|
3653
|
+
this._hash = options.hash ?? hashKey;
|
|
3654
|
+
this._eviction = {
|
|
3655
|
+
maxIdle: options.eviction?.maxIdle ?? DEFAULT_EVICTION.maxIdle
|
|
3656
|
+
};
|
|
3657
|
+
}
|
|
3658
|
+
_fetcher;
|
|
3659
|
+
_entries = /* @__PURE__ */ new Map();
|
|
3660
|
+
_hash;
|
|
3661
|
+
_eviction;
|
|
3662
|
+
_readiness;
|
|
3663
|
+
hash(params) {
|
|
3664
|
+
return this._hash(params);
|
|
3665
|
+
}
|
|
3666
|
+
get size() {
|
|
3667
|
+
return this._entries.size;
|
|
3668
|
+
}
|
|
3669
|
+
entries() {
|
|
3670
|
+
return Array.from(this._entries, ([key, e]) => ({ key, ...e }));
|
|
3671
|
+
}
|
|
3672
|
+
get(params) {
|
|
3673
|
+
const key = this._hash(params);
|
|
3674
|
+
const existing = this._entries.get(key);
|
|
3675
|
+
if (existing !== void 0) {
|
|
3676
|
+
this._entries.delete(key);
|
|
3677
|
+
this._entries.set(key, existing);
|
|
3678
|
+
return existing.resource;
|
|
3679
|
+
}
|
|
3680
|
+
const resource = new Resource(
|
|
3681
|
+
() => this._fetcher(params, resource.snapshot.data),
|
|
3682
|
+
void 0,
|
|
3683
|
+
this._readiness
|
|
3684
|
+
);
|
|
3685
|
+
this._entries.set(key, { params, resource });
|
|
3686
|
+
return resource;
|
|
3687
|
+
}
|
|
3688
|
+
has(params) {
|
|
3689
|
+
return this._entries.has(this._hash(params));
|
|
3690
|
+
}
|
|
3691
|
+
subscribe(params, listener) {
|
|
3692
|
+
const resource = this.get(params);
|
|
3693
|
+
const unsubscribe = resource.subscribe(listener);
|
|
3694
|
+
return () => {
|
|
3695
|
+
unsubscribe();
|
|
3696
|
+
this._evict();
|
|
3697
|
+
};
|
|
3698
|
+
}
|
|
3699
|
+
seed(params, data) {
|
|
3700
|
+
this.get(params).seed(data);
|
|
3701
|
+
}
|
|
3702
|
+
evict(params) {
|
|
3703
|
+
return this._entries.delete(this._hash(params));
|
|
3704
|
+
}
|
|
3705
|
+
resetAll() {
|
|
3706
|
+
for (const [key, { resource }] of Array.from(this._entries)) {
|
|
3707
|
+
if (resource.subscriberCount === 0) {
|
|
3708
|
+
this._entries.delete(key);
|
|
3709
|
+
} else {
|
|
3710
|
+
resource.reset();
|
|
3711
|
+
}
|
|
3712
|
+
}
|
|
3713
|
+
}
|
|
3714
|
+
resumeAll() {
|
|
3715
|
+
for (const { resource } of this._entries.values()) {
|
|
3716
|
+
if (resource.subscriberCount === 0) {
|
|
3717
|
+
continue;
|
|
3718
|
+
}
|
|
3719
|
+
const { data, error } = resource.snapshot;
|
|
3720
|
+
if (data === void 0 || error !== void 0) {
|
|
3721
|
+
void resource.load();
|
|
3722
|
+
}
|
|
3723
|
+
}
|
|
3724
|
+
}
|
|
3725
|
+
clearAll() {
|
|
3726
|
+
for (const [key, { resource }] of Array.from(this._entries)) {
|
|
3727
|
+
if (resource.subscriberCount === 0) {
|
|
3728
|
+
this._entries.delete(key);
|
|
3729
|
+
} else {
|
|
3730
|
+
resource.clear();
|
|
3731
|
+
}
|
|
3732
|
+
}
|
|
3733
|
+
}
|
|
3734
|
+
invalidateAll() {
|
|
3735
|
+
for (const { resource } of this._entries.values()) {
|
|
3736
|
+
if (resource.snapshot.data !== void 0) {
|
|
3737
|
+
void resource.refetch();
|
|
3738
|
+
}
|
|
3739
|
+
}
|
|
3740
|
+
}
|
|
3741
|
+
_evict() {
|
|
3742
|
+
const idle = Array.from(this._entries).filter(
|
|
3743
|
+
([, e]) => e.resource.subscriberCount === 0
|
|
3744
|
+
);
|
|
3745
|
+
let excess = idle.length - this._eviction.maxIdle;
|
|
3746
|
+
for (const [key] of idle) {
|
|
3747
|
+
if (excess <= 0) break;
|
|
3748
|
+
this._entries.delete(key);
|
|
3749
|
+
excess -= 1;
|
|
3750
|
+
}
|
|
3751
|
+
}
|
|
3752
|
+
};
|
|
3753
|
+
|
|
3754
|
+
// src/billing/client.ts
|
|
3755
|
+
function claimOf(client) {
|
|
3756
|
+
return { status: client.sessionStatus, key: client.sessionKey };
|
|
3757
|
+
}
|
|
3758
|
+
function claimFrom(session) {
|
|
3759
|
+
if (session === void 0) {
|
|
3760
|
+
return void 0;
|
|
3761
|
+
}
|
|
3762
|
+
return session === null ? { status: "ended", key: void 0 } : { status: "active", key: sessionKey(session) };
|
|
3763
|
+
}
|
|
3764
|
+
function judgeSeed(seedKey, claim) {
|
|
3765
|
+
if (claim.status === "ended") {
|
|
3766
|
+
return "drop";
|
|
3767
|
+
}
|
|
3768
|
+
if (seedKey === void 0) {
|
|
3769
|
+
return "adopt";
|
|
3770
|
+
}
|
|
3771
|
+
if (claim.status === "pending") {
|
|
3772
|
+
return "hold";
|
|
3773
|
+
}
|
|
3774
|
+
return seedKey === claim.key ? "adopt" : "drop";
|
|
3775
|
+
}
|
|
3776
|
+
var READINESS = {
|
|
3777
|
+
pending: "waiting",
|
|
3778
|
+
active: "ready",
|
|
3779
|
+
ended: "never"
|
|
3780
|
+
};
|
|
3781
|
+
var BillingStore = class {
|
|
3782
|
+
constructor(_client, initialData = {}, options = {}) {
|
|
3783
|
+
this._client = _client;
|
|
3784
|
+
this._pageSize = options.pageSize ?? INVOICE_PAGE_SIZE;
|
|
3785
|
+
this.invoices = new KeyedResource(
|
|
3786
|
+
(query, current) => this._reloadWindow(
|
|
3787
|
+
query,
|
|
3788
|
+
Math.max(this._pageSize, current?.invoices.length ?? 0)
|
|
3789
|
+
),
|
|
3790
|
+
// Waiting until connected; then a session still pending stays
|
|
3791
|
+
// pending, and one that has ended settles empty rather than recording
|
|
3792
|
+
// the client's refusal as an error.
|
|
3793
|
+
{
|
|
3794
|
+
readiness: () => this._connected ? READINESS[this._client.sessionStatus] : "waiting"
|
|
3795
|
+
}
|
|
3796
|
+
);
|
|
3797
|
+
if (initialData.invoices !== void 0) {
|
|
3798
|
+
this._held = {
|
|
3799
|
+
// Normalized the way the hook normalizes, or the caller asking for
|
|
3800
|
+
// it fetches the same page again.
|
|
3801
|
+
params: normalizeInvoiceQuery2(
|
|
3802
|
+
initialData.params?.invoices ?? DEFAULT_INVOICE_QUERY
|
|
3803
|
+
),
|
|
3804
|
+
data: initialData.invoices,
|
|
3805
|
+
key: initialData.sessionKey
|
|
3806
|
+
};
|
|
3807
|
+
this._settleSeed(claimFrom(options.session) ?? claimOf(this._client));
|
|
3808
|
+
}
|
|
3809
|
+
}
|
|
3810
|
+
_client;
|
|
3811
|
+
invoices;
|
|
3812
|
+
_pageSize;
|
|
3813
|
+
_unsubscribe;
|
|
3814
|
+
_connected = false;
|
|
3815
|
+
_seeded = false;
|
|
3816
|
+
/** The session that prefetch was for, where it said. */
|
|
3817
|
+
_seedKey;
|
|
3818
|
+
/** A stamped prefetch waiting for a session to check it against. */
|
|
3819
|
+
_held;
|
|
3820
|
+
/**
|
|
3821
|
+
* Not done in the constructor: the provider arms it from an effect, and
|
|
3822
|
+
* React runs an effect, its cleanup, then the effect again under
|
|
3823
|
+
* StrictMode. A subscription made once at construction is torn down by
|
|
3824
|
+
* that cleanup and never comes back, leaving the store deaf to every
|
|
3825
|
+
* session change for the rest of the page.
|
|
3826
|
+
*
|
|
3827
|
+
* Also what opens the store: subscribers who arrived first have been
|
|
3828
|
+
* waiting, and this is the moment the session is known to be installed.
|
|
3829
|
+
*/
|
|
3830
|
+
connect() {
|
|
3831
|
+
this._unsubscribe?.();
|
|
3832
|
+
const unsubscribe = this._client.onSessionChange?.((event) => {
|
|
3833
|
+
if (event.type === "ended") {
|
|
3834
|
+
this._held = void 0;
|
|
3835
|
+
this._seeded = false;
|
|
3836
|
+
this.clearAll();
|
|
3837
|
+
} else if (event.type === "changed") {
|
|
3838
|
+
this._held = void 0;
|
|
3839
|
+
this._seeded = false;
|
|
3840
|
+
this.resetAll();
|
|
3841
|
+
} else {
|
|
3842
|
+
this._onStarted();
|
|
3843
|
+
}
|
|
3844
|
+
});
|
|
3845
|
+
this._unsubscribe = unsubscribe;
|
|
3846
|
+
if (!this._connected) {
|
|
3847
|
+
this._connected = true;
|
|
3848
|
+
this._settleSeed(claimOf(this._client));
|
|
3849
|
+
this.resumeAll();
|
|
3850
|
+
}
|
|
3851
|
+
return () => {
|
|
3852
|
+
unsubscribe?.();
|
|
3853
|
+
if (this._unsubscribe === unsubscribe) {
|
|
3854
|
+
this._unsubscribe = void 0;
|
|
3855
|
+
}
|
|
3856
|
+
};
|
|
3857
|
+
}
|
|
3858
|
+
_onStarted() {
|
|
3859
|
+
const claim = claimOf(this._client);
|
|
3860
|
+
this._settleSeed(claim);
|
|
3861
|
+
if (this._seeded && judgeSeed(this._seedKey, claim) !== "adopt") {
|
|
3862
|
+
this._seeded = false;
|
|
3863
|
+
this.resetAll();
|
|
3864
|
+
return;
|
|
3865
|
+
}
|
|
3866
|
+
this._seeded = false;
|
|
3867
|
+
this.resumeAll();
|
|
3868
|
+
}
|
|
3869
|
+
/** Adopts, keeps holding, or drops the prefetch, by what `claim` says. */
|
|
3870
|
+
_settleSeed(claim) {
|
|
3871
|
+
const held = this._held;
|
|
3872
|
+
if (held === void 0) {
|
|
3873
|
+
return;
|
|
3874
|
+
}
|
|
3875
|
+
const verdict = judgeSeed(held.key, claim);
|
|
3876
|
+
if (verdict === "hold") {
|
|
3877
|
+
return;
|
|
3878
|
+
}
|
|
3879
|
+
this._held = void 0;
|
|
3880
|
+
if (verdict === "adopt") {
|
|
3881
|
+
this.invoices.seed(held.params, held.data);
|
|
3882
|
+
this._seedKey = held.key;
|
|
3883
|
+
this._seeded = true;
|
|
3884
|
+
}
|
|
3885
|
+
}
|
|
3886
|
+
resource(name) {
|
|
3887
|
+
return this[name];
|
|
3888
|
+
}
|
|
3889
|
+
/** Forgets every resource; subscribed ones reload, idle ones are dropped. */
|
|
3890
|
+
resetAll() {
|
|
3891
|
+
for (const name of RESOURCE_NAMES) {
|
|
3892
|
+
this[name].resetAll();
|
|
3893
|
+
}
|
|
3894
|
+
}
|
|
3895
|
+
/** Forgets every resource and leaves them empty; nothing reloads. */
|
|
3896
|
+
clearAll() {
|
|
3897
|
+
for (const name of RESOURCE_NAMES) {
|
|
3898
|
+
this[name].clearAll();
|
|
3899
|
+
}
|
|
3900
|
+
}
|
|
3901
|
+
/** Loads every resource that is subscribed and has nothing yet. */
|
|
3902
|
+
resumeAll() {
|
|
3903
|
+
for (const name of RESOURCE_NAMES) {
|
|
3904
|
+
this[name].resumeAll();
|
|
3905
|
+
}
|
|
3906
|
+
}
|
|
3907
|
+
/** Reloads every resource that has been loaded, keeping its current data. */
|
|
3908
|
+
invalidateAll() {
|
|
3909
|
+
for (const name of RESOURCE_NAMES) {
|
|
3910
|
+
this[name].invalidateAll();
|
|
3911
|
+
}
|
|
3912
|
+
}
|
|
3913
|
+
/**
|
|
3914
|
+
* Appends the next page to the invoices list for `query`, when there is
|
|
3915
|
+
* one. A failure records the error on the resource and keeps the rows
|
|
3916
|
+
* already fetched; the returned promise never rejects.
|
|
3917
|
+
*/
|
|
3918
|
+
loadMoreInvoices(query = DEFAULT_INVOICE_QUERY) {
|
|
3919
|
+
const resource = this.invoices.get(normalizeInvoiceQuery2(query));
|
|
3920
|
+
if (resource.snapshot.data?.hasMore !== true) {
|
|
3921
|
+
return Promise.resolve();
|
|
3922
|
+
}
|
|
3923
|
+
return resource.extend(async (current) => {
|
|
3924
|
+
const page = await this._fetchInvoices(
|
|
3925
|
+
normalizeInvoiceQuery2(query),
|
|
3926
|
+
current.invoices.length,
|
|
3927
|
+
this._pageSize
|
|
3928
|
+
);
|
|
3929
|
+
const invoices = [...current.invoices, ...page.invoices];
|
|
3930
|
+
return {
|
|
3931
|
+
invoices,
|
|
3932
|
+
count: page.count,
|
|
3933
|
+
// From the count the page came back with, not the one the list was
|
|
3934
|
+
// built on: an invoice finalized between the two requests changes
|
|
3935
|
+
// what is left to load. An empty page ends the list whatever the
|
|
3936
|
+
// count says — the rows and the count are two queries, and a count
|
|
3937
|
+
// that outruns the rows would otherwise leave `hasMore` true for
|
|
3938
|
+
// good, with every further page fetching nothing.
|
|
3939
|
+
hasMore: page.invoices.length > 0 && invoices.length < page.count
|
|
3940
|
+
};
|
|
3941
|
+
});
|
|
3942
|
+
}
|
|
3943
|
+
dispose() {
|
|
3944
|
+
this._unsubscribe?.();
|
|
3945
|
+
}
|
|
3946
|
+
/**
|
|
3947
|
+
* Re-reads the window already loaded, in as many requests as the API's
|
|
3948
|
+
* page cap needs. A window paged past that cap still comes back whole:
|
|
3949
|
+
* asking for it in one request would be a 400, and asking for only the
|
|
3950
|
+
* first page would drop the rest.
|
|
3951
|
+
*/
|
|
3952
|
+
async _reloadWindow(query, window2) {
|
|
3953
|
+
const invoices = [];
|
|
3954
|
+
let count = 0;
|
|
3955
|
+
while (invoices.length < window2) {
|
|
3956
|
+
const page = await this._fetchInvoices(
|
|
3957
|
+
query,
|
|
3958
|
+
invoices.length,
|
|
3959
|
+
Math.min(INVOICE_MAX_PAGE_SIZE, window2 - invoices.length)
|
|
3960
|
+
);
|
|
3961
|
+
count = page.count;
|
|
3962
|
+
invoices.push(...page.invoices);
|
|
3963
|
+
if (page.invoices.length === 0 || invoices.length >= count) {
|
|
3964
|
+
break;
|
|
3965
|
+
}
|
|
3966
|
+
}
|
|
3967
|
+
return {
|
|
3968
|
+
invoices,
|
|
3969
|
+
count,
|
|
3970
|
+
hasMore: invoices.length > 0 && invoices.length < count
|
|
3971
|
+
};
|
|
3972
|
+
}
|
|
3973
|
+
/**
|
|
3974
|
+
* Returns what the server said and nothing more: whether there is more to
|
|
3975
|
+
* load depends on the whole window, which only the caller assembling it
|
|
3976
|
+
* knows — `_reloadWindow` across its requests, and `loadMoreInvoices`
|
|
3977
|
+
* across the rows already loaded.
|
|
3978
|
+
*/
|
|
3979
|
+
_fetchInvoices(query, offset, limit) {
|
|
3980
|
+
return this._client.fetchInvoices({ ...query, limit, offset });
|
|
3981
|
+
}
|
|
3982
|
+
};
|
|
3983
|
+
var RESOURCE_NAMES = ["invoices"];
|
|
3984
|
+
|
|
3985
|
+
// src/billing/context.tsx
|
|
3986
|
+
import { createContext as createContext2, useContext as useContext2, useMemo as useMemo2 } from "react";
|
|
3987
|
+
|
|
3988
|
+
// src/i18n/context.tsx
|
|
3989
|
+
import { createContext, useContext, useMemo } from "react";
|
|
3990
|
+
import { jsx } from "react/jsx-runtime";
|
|
3991
|
+
var EMPTY = Object.freeze({});
|
|
3992
|
+
var SchematicI18nContext = createContext(EMPTY);
|
|
3993
|
+
function SchematicI18nProvider({
|
|
3994
|
+
children,
|
|
3995
|
+
locale,
|
|
3996
|
+
onMissingString,
|
|
3997
|
+
strings,
|
|
3998
|
+
translate
|
|
3999
|
+
}) {
|
|
4000
|
+
const parent = useContext(SchematicI18nContext);
|
|
4001
|
+
const value = useMemo(
|
|
4002
|
+
() => ({
|
|
4003
|
+
locale: locale ?? parent.locale,
|
|
4004
|
+
translate: translate ?? parent.translate,
|
|
4005
|
+
strings: strings === void 0 ? parent.strings : { ...parent.strings, ...strings },
|
|
4006
|
+
onMissingString: onMissingString ?? parent.onMissingString
|
|
4007
|
+
}),
|
|
4008
|
+
[locale, onMissingString, parent, strings, translate]
|
|
4009
|
+
);
|
|
4010
|
+
return /* @__PURE__ */ jsx(SchematicI18nContext.Provider, { value, children });
|
|
4011
|
+
}
|
|
4012
|
+
function useSchematicI18n() {
|
|
4013
|
+
return useContext(SchematicI18nContext);
|
|
4014
|
+
}
|
|
4015
|
+
function useSchematicLocale() {
|
|
4016
|
+
return useContext(SchematicI18nContext).locale;
|
|
4017
|
+
}
|
|
4018
|
+
function useSchematicTranslate() {
|
|
4019
|
+
return useContext(SchematicI18nContext).translate;
|
|
4020
|
+
}
|
|
4021
|
+
function useSchematicStrings() {
|
|
4022
|
+
return useContext(SchematicI18nContext).strings;
|
|
4023
|
+
}
|
|
4024
|
+
|
|
4025
|
+
// src/billing/context.tsx
|
|
4026
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
4027
|
+
var BillingDataContext = createContext2(
|
|
4028
|
+
void 0
|
|
4029
|
+
);
|
|
4030
|
+
var MISSING_BILLING_SOURCE_MESSAGE = "Schematic billing hooks need a SchematicProvider with a session, billingClient, or initialData, or a BillingDataProvider.";
|
|
4031
|
+
var missingSourceError = new Error(MISSING_BILLING_SOURCE_MESSAGE);
|
|
4032
|
+
var missingHandle = {
|
|
4033
|
+
data: void 0,
|
|
4034
|
+
error: missingSourceError,
|
|
4035
|
+
isPending: false,
|
|
4036
|
+
refetch: () => {
|
|
4037
|
+
}
|
|
4038
|
+
};
|
|
4039
|
+
var missingBillingSource = {
|
|
4040
|
+
subscribe: () => () => {
|
|
4041
|
+
},
|
|
4042
|
+
handle: () => missingHandle,
|
|
4043
|
+
loadMoreInvoices: () => Promise.resolve(),
|
|
4044
|
+
invalidateAll: () => {
|
|
4045
|
+
}
|
|
4046
|
+
};
|
|
4047
|
+
function useBillingDataSource() {
|
|
4048
|
+
return useContext2(BillingDataContext) ?? missingBillingSource;
|
|
4049
|
+
}
|
|
4050
|
+
function BillingDataProvider({
|
|
4051
|
+
children,
|
|
4052
|
+
data,
|
|
4053
|
+
locale,
|
|
4054
|
+
onLoadMoreInvoices,
|
|
4055
|
+
onMissingString,
|
|
4056
|
+
onRefetch,
|
|
4057
|
+
status,
|
|
4058
|
+
strings,
|
|
4059
|
+
translate
|
|
4060
|
+
}) {
|
|
4061
|
+
const source = useMemo2(() => {
|
|
4062
|
+
const handles = /* @__PURE__ */ new Map();
|
|
4063
|
+
return {
|
|
4064
|
+
subscribe: () => () => {
|
|
4065
|
+
},
|
|
4066
|
+
handle: (name) => {
|
|
4067
|
+
const cached = handles.get(name);
|
|
4068
|
+
if (cached !== void 0) {
|
|
4069
|
+
return cached;
|
|
4070
|
+
}
|
|
4071
|
+
const value = data[name];
|
|
4072
|
+
const override = status?.[name];
|
|
4073
|
+
const handle = {
|
|
4074
|
+
data: value,
|
|
4075
|
+
error: override?.error,
|
|
4076
|
+
isPending: override?.isPending ?? value === void 0,
|
|
4077
|
+
refetch: () => onRefetch?.(name)
|
|
4078
|
+
};
|
|
4079
|
+
handles.set(name, handle);
|
|
4080
|
+
return handle;
|
|
4081
|
+
},
|
|
4082
|
+
loadMoreInvoices: (query) => {
|
|
4083
|
+
onLoadMoreInvoices?.(query);
|
|
4084
|
+
return Promise.resolve();
|
|
4085
|
+
},
|
|
4086
|
+
invalidateAll: () => {
|
|
4087
|
+
}
|
|
4088
|
+
};
|
|
4089
|
+
}, [data, onLoadMoreInvoices, onRefetch, status]);
|
|
4090
|
+
return /* @__PURE__ */ jsx2(BillingDataContext.Provider, { value: source, children: /* @__PURE__ */ jsx2(
|
|
4091
|
+
SchematicI18nProvider,
|
|
4092
|
+
{
|
|
4093
|
+
locale,
|
|
4094
|
+
strings,
|
|
4095
|
+
translate,
|
|
4096
|
+
onMissingString,
|
|
4097
|
+
children
|
|
4098
|
+
}
|
|
4099
|
+
) });
|
|
4100
|
+
}
|
|
4101
|
+
|
|
4102
|
+
// src/billing/hooks.ts
|
|
4103
|
+
import { useCallback, useMemo as useMemo3, useRef, useSyncExternalStore } from "react";
|
|
4104
|
+
function useStableParams(params) {
|
|
4105
|
+
const key = hashKey(params);
|
|
4106
|
+
const ref = useRef({ key, params });
|
|
4107
|
+
if (ref.current.key !== key) {
|
|
4108
|
+
ref.current = { key, params };
|
|
4109
|
+
}
|
|
4110
|
+
return ref.current.params;
|
|
4111
|
+
}
|
|
4112
|
+
function useBillingResource(name, params) {
|
|
4113
|
+
const source = useBillingDataSource();
|
|
4114
|
+
const subscribe = useCallback(
|
|
4115
|
+
(listener) => source.subscribe(name, params, listener),
|
|
4116
|
+
[name, params, source]
|
|
4117
|
+
);
|
|
4118
|
+
const getSnapshot = useCallback(
|
|
4119
|
+
() => source.handle(name, params),
|
|
4120
|
+
[name, params, source]
|
|
4121
|
+
);
|
|
4122
|
+
return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
4123
|
+
}
|
|
4124
|
+
function useInvoices(query = DEFAULT_INVOICE_QUERY) {
|
|
4125
|
+
const source = useBillingDataSource();
|
|
4126
|
+
const params = useStableParams(normalizeInvoiceQuery2(query));
|
|
4127
|
+
const handle = useBillingResource("invoices", params);
|
|
4128
|
+
const loadMore = useCallback(
|
|
4129
|
+
() => source.loadMoreInvoices(params),
|
|
4130
|
+
[params, source]
|
|
4131
|
+
);
|
|
4132
|
+
return useMemo3(() => ({ ...handle, loadMore }), [handle, loadMore]);
|
|
4133
|
+
}
|
|
4134
|
+
function useInvalidateBillingData() {
|
|
4135
|
+
return useBillingDataSource().invalidateAll;
|
|
4136
|
+
}
|
|
4137
|
+
|
|
4138
|
+
// src/billing/provider.tsx
|
|
4139
|
+
import { useEffect, useMemo as useMemo4, useRef as useRef2 } from "react";
|
|
4140
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
4141
|
+
var SESSION_REPLACED_MESSAGE = "BillingProvider is moving its billingClient from one session to another. A client holds one session: if another provider shares this client, each will keep installing its own and show the other's data. Give each provider its own client.";
|
|
4142
|
+
function BillingProvider({
|
|
4143
|
+
billingClient,
|
|
4144
|
+
children,
|
|
4145
|
+
initialData,
|
|
4146
|
+
locale,
|
|
4147
|
+
onMissingString,
|
|
4148
|
+
session,
|
|
4149
|
+
strings,
|
|
4150
|
+
translate
|
|
4151
|
+
}) {
|
|
4152
|
+
const initialRef = useRef2(initialData);
|
|
4153
|
+
const install = () => {
|
|
4154
|
+
billingClient?.setSession?.(session);
|
|
4155
|
+
};
|
|
4156
|
+
useEffect(() => {
|
|
4157
|
+
const claim = billingClient?.sessionKey;
|
|
4158
|
+
if (claim !== void 0 && session !== null && session !== void 0 && claim !== sessionKey(session)) {
|
|
4159
|
+
console.warn(SESSION_REPLACED_MESSAGE);
|
|
4160
|
+
}
|
|
4161
|
+
}, []);
|
|
4162
|
+
useEffect(install);
|
|
4163
|
+
const seedSpent = useRef2(false);
|
|
4164
|
+
const store = useMemo4(
|
|
4165
|
+
() => billingClient === void 0 ? void 0 : new BillingStore(
|
|
4166
|
+
billingClient,
|
|
4167
|
+
seedSpent.current ? void 0 : initialRef.current,
|
|
4168
|
+
{
|
|
4169
|
+
// What `install` is about to say, so a stamped prefetch is
|
|
4170
|
+
// judged now rather than after the effect.
|
|
4171
|
+
session
|
|
4172
|
+
}
|
|
4173
|
+
),
|
|
4174
|
+
// `session` is read only when the store is built: the client learns
|
|
4175
|
+
// later sessions through `install`, and the store through the client.
|
|
4176
|
+
[billingClient]
|
|
4177
|
+
);
|
|
4178
|
+
useEffect(() => {
|
|
4179
|
+
if (store !== void 0) {
|
|
4180
|
+
seedSpent.current = true;
|
|
4181
|
+
}
|
|
4182
|
+
}, [store]);
|
|
4183
|
+
useEffect(() => store?.connect(), [store]);
|
|
4184
|
+
const source = useMemo4(() => {
|
|
4185
|
+
if (store === void 0) {
|
|
4186
|
+
return initialRef.current === void 0 ? void 0 : staticSource(initialRef.current);
|
|
4187
|
+
}
|
|
4188
|
+
const handles = /* @__PURE__ */ new WeakMap();
|
|
4189
|
+
const handle = (name, params) => {
|
|
4190
|
+
const resource = store.resource(name).get(params);
|
|
4191
|
+
const snapshot = resource.getSnapshot();
|
|
4192
|
+
const cached = handles.get(resource);
|
|
4193
|
+
if (cached !== void 0 && cached.snapshot === snapshot) {
|
|
4194
|
+
return cached.handle;
|
|
4195
|
+
}
|
|
4196
|
+
const next = {
|
|
4197
|
+
...snapshot,
|
|
4198
|
+
refetch: () => void resource.refetch()
|
|
4199
|
+
};
|
|
4200
|
+
handles.set(resource, { snapshot, handle: next });
|
|
4201
|
+
return next;
|
|
4202
|
+
};
|
|
4203
|
+
return {
|
|
4204
|
+
subscribe: (name, params, listener) => store.resource(name).subscribe(params, listener),
|
|
4205
|
+
handle,
|
|
4206
|
+
loadMoreInvoices: (query) => store.loadMoreInvoices(query),
|
|
4207
|
+
invalidateAll: () => store.invalidateAll()
|
|
4208
|
+
};
|
|
4209
|
+
}, [store]);
|
|
4210
|
+
const localized = /* @__PURE__ */ jsx3(
|
|
4211
|
+
SchematicI18nProvider,
|
|
4212
|
+
{
|
|
4213
|
+
locale,
|
|
4214
|
+
strings,
|
|
4215
|
+
translate,
|
|
4216
|
+
onMissingString,
|
|
4217
|
+
children
|
|
4218
|
+
}
|
|
4219
|
+
);
|
|
4220
|
+
if (source === void 0) {
|
|
4221
|
+
return localized;
|
|
4222
|
+
}
|
|
4223
|
+
return /* @__PURE__ */ jsx3(BillingDataContext.Provider, { value: source, children: localized });
|
|
4224
|
+
}
|
|
4225
|
+
function staticSource(data) {
|
|
4226
|
+
const handles = /* @__PURE__ */ new Map();
|
|
4227
|
+
return {
|
|
4228
|
+
subscribe: () => () => {
|
|
4229
|
+
},
|
|
4230
|
+
handle: (name) => {
|
|
4231
|
+
const cached = handles.get(name);
|
|
4232
|
+
if (cached !== void 0) {
|
|
4233
|
+
return cached;
|
|
4234
|
+
}
|
|
4235
|
+
const value = data[name];
|
|
4236
|
+
const next = {
|
|
4237
|
+
data: value,
|
|
4238
|
+
error: void 0,
|
|
4239
|
+
isPending: value === void 0,
|
|
4240
|
+
refetch: () => {
|
|
4241
|
+
}
|
|
4242
|
+
};
|
|
4243
|
+
handles.set(name, next);
|
|
4244
|
+
return next;
|
|
4245
|
+
},
|
|
4246
|
+
loadMoreInvoices: () => Promise.resolve(),
|
|
4247
|
+
invalidateAll: () => {
|
|
4248
|
+
}
|
|
4249
|
+
};
|
|
4250
|
+
}
|
|
2266
4251
|
|
|
2267
4252
|
// src/version.ts
|
|
2268
|
-
var version2 = "1.
|
|
4253
|
+
var version2 = "1.6.0";
|
|
2269
4254
|
|
|
2270
4255
|
// src/context/schematic.tsx
|
|
2271
|
-
import { jsx } from "react/jsx-runtime";
|
|
2272
|
-
var SchematicContext =
|
|
4256
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
4257
|
+
var SchematicContext = createContext3(
|
|
2273
4258
|
null
|
|
2274
4259
|
);
|
|
2275
4260
|
var SchematicProvider = ({
|
|
4261
|
+
session,
|
|
4262
|
+
billingClient,
|
|
2276
4263
|
children,
|
|
2277
4264
|
client: providedClient,
|
|
4265
|
+
initialData,
|
|
4266
|
+
locale,
|
|
4267
|
+
onMissingString,
|
|
2278
4268
|
publishableKey,
|
|
4269
|
+
strings,
|
|
4270
|
+
translate,
|
|
2279
4271
|
...clientOpts
|
|
2280
4272
|
}) => {
|
|
2281
|
-
const initialOptsRef =
|
|
4273
|
+
const initialOptsRef = useRef3({
|
|
2282
4274
|
publishableKey,
|
|
2283
4275
|
useWebSocket: true,
|
|
2284
4276
|
additionalHeaders: {
|
|
@@ -2286,7 +4278,7 @@ var SchematicProvider = ({
|
|
|
2286
4278
|
},
|
|
2287
4279
|
...clientOpts
|
|
2288
4280
|
});
|
|
2289
|
-
const client =
|
|
4281
|
+
const client = useMemo5(() => {
|
|
2290
4282
|
if (providedClient) {
|
|
2291
4283
|
return providedClient;
|
|
2292
4284
|
}
|
|
@@ -2294,7 +4286,7 @@ var SchematicProvider = ({
|
|
|
2294
4286
|
...initialOptsRef.current
|
|
2295
4287
|
});
|
|
2296
4288
|
}, [providedClient]);
|
|
2297
|
-
|
|
4289
|
+
useEffect2(() => {
|
|
2298
4290
|
return () => {
|
|
2299
4291
|
if (!providedClient) {
|
|
2300
4292
|
client.cleanup().catch((error) => {
|
|
@@ -2303,16 +4295,46 @@ var SchematicProvider = ({
|
|
|
2303
4295
|
}
|
|
2304
4296
|
};
|
|
2305
4297
|
}, [client, providedClient]);
|
|
2306
|
-
const contextValue =
|
|
4298
|
+
const contextValue = useMemo5(
|
|
2307
4299
|
() => ({
|
|
2308
4300
|
client
|
|
2309
4301
|
}),
|
|
2310
4302
|
[client]
|
|
2311
4303
|
);
|
|
2312
|
-
|
|
4304
|
+
const { apiUrl, additionalHeaders } = initialOptsRef.current;
|
|
4305
|
+
const providedApiUrl = providedClient?.apiUrl;
|
|
4306
|
+
const resolvedBillingClient = useMemo5(
|
|
4307
|
+
() => billingClient ?? new SchematicBillingClient({
|
|
4308
|
+
session,
|
|
4309
|
+
apiUrl: apiUrl ?? providedApiUrl,
|
|
4310
|
+
// Read as they stand when a client is built: headers do not change
|
|
4311
|
+
// where the token goes, so they are no reason to build one.
|
|
4312
|
+
additionalHeaders: {
|
|
4313
|
+
...providedClient?.additionalHeaders,
|
|
4314
|
+
...additionalHeaders
|
|
4315
|
+
}
|
|
4316
|
+
}),
|
|
4317
|
+
// `session` is deliberately absent: `BillingProvider` forwards it through
|
|
4318
|
+
// `setSession`, so a token change resets the billing resources without
|
|
4319
|
+
// rebuilding the client.
|
|
4320
|
+
[additionalHeaders, apiUrl, billingClient, providedApiUrl]
|
|
4321
|
+
);
|
|
4322
|
+
return /* @__PURE__ */ jsx4(SchematicContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx4(
|
|
4323
|
+
BillingProvider,
|
|
4324
|
+
{
|
|
4325
|
+
billingClient: resolvedBillingClient,
|
|
4326
|
+
initialData,
|
|
4327
|
+
locale,
|
|
4328
|
+
session,
|
|
4329
|
+
strings,
|
|
4330
|
+
translate,
|
|
4331
|
+
onMissingString,
|
|
4332
|
+
children
|
|
4333
|
+
}
|
|
4334
|
+
) });
|
|
2313
4335
|
};
|
|
2314
4336
|
var useSchematic = () => {
|
|
2315
|
-
const context =
|
|
4337
|
+
const context = React4.useContext(SchematicContext);
|
|
2316
4338
|
if (context === null) {
|
|
2317
4339
|
throw new Error("useSchematic must be used within a SchematicProvider");
|
|
2318
4340
|
}
|
|
@@ -2320,11 +4342,11 @@ var useSchematic = () => {
|
|
|
2320
4342
|
};
|
|
2321
4343
|
|
|
2322
4344
|
// src/hooks/schematic.ts
|
|
2323
|
-
import { useMemo as
|
|
4345
|
+
import { useMemo as useMemo6, useSyncExternalStore as useSyncExternalStore2, useCallback as useCallback2 } from "react";
|
|
2324
4346
|
var useSchematicClient = (opts) => {
|
|
2325
4347
|
const schematic = useSchematic();
|
|
2326
4348
|
const { client } = opts ?? {};
|
|
2327
|
-
return
|
|
4349
|
+
return useMemo6(() => {
|
|
2328
4350
|
if (client) {
|
|
2329
4351
|
return client;
|
|
2330
4352
|
}
|
|
@@ -2333,7 +4355,7 @@ var useSchematicClient = (opts) => {
|
|
|
2333
4355
|
};
|
|
2334
4356
|
var useSchematicContext = (opts) => {
|
|
2335
4357
|
const client = useSchematicClient(opts);
|
|
2336
|
-
return
|
|
4358
|
+
return useMemo6(
|
|
2337
4359
|
() => ({
|
|
2338
4360
|
setContext: client.setContext.bind(client)
|
|
2339
4361
|
}),
|
|
@@ -2342,33 +4364,33 @@ var useSchematicContext = (opts) => {
|
|
|
2342
4364
|
};
|
|
2343
4365
|
var useSchematicEvents = (opts) => {
|
|
2344
4366
|
const client = useSchematicClient(opts);
|
|
2345
|
-
const track =
|
|
4367
|
+
const track = useCallback2(
|
|
2346
4368
|
(...args) => client.track(...args),
|
|
2347
4369
|
[client]
|
|
2348
4370
|
);
|
|
2349
|
-
const identify =
|
|
4371
|
+
const identify = useCallback2(
|
|
2350
4372
|
(...args) => client.identify(...args),
|
|
2351
4373
|
[client]
|
|
2352
4374
|
);
|
|
2353
|
-
return
|
|
4375
|
+
return useMemo6(() => ({ track, identify }), [track, identify]);
|
|
2354
4376
|
};
|
|
2355
4377
|
var useSchematicFlag = (key, opts) => {
|
|
2356
4378
|
const client = useSchematicClient(opts);
|
|
2357
4379
|
const fallback = opts?.fallback ?? false;
|
|
2358
|
-
const subscribe =
|
|
4380
|
+
const subscribe = useCallback2(
|
|
2359
4381
|
(callback) => client.addFlagValueListener(key, callback),
|
|
2360
4382
|
[client, key]
|
|
2361
4383
|
);
|
|
2362
|
-
const getSnapshot =
|
|
4384
|
+
const getSnapshot = useCallback2(() => {
|
|
2363
4385
|
const value = client.getFlagValue(key);
|
|
2364
4386
|
return typeof value === "undefined" ? fallback : value;
|
|
2365
4387
|
}, [client, key, fallback]);
|
|
2366
|
-
return
|
|
4388
|
+
return useSyncExternalStore2(subscribe, getSnapshot, () => fallback);
|
|
2367
4389
|
};
|
|
2368
4390
|
var useSchematicEntitlement = (key, opts) => {
|
|
2369
4391
|
const client = useSchematicClient(opts);
|
|
2370
4392
|
const fallback = opts?.fallback ?? false;
|
|
2371
|
-
const fallbackCheck =
|
|
4393
|
+
const fallbackCheck = useMemo6(
|
|
2372
4394
|
() => ({
|
|
2373
4395
|
flag: key,
|
|
2374
4396
|
reason: "Fallback",
|
|
@@ -2376,20 +4398,20 @@ var useSchematicEntitlement = (key, opts) => {
|
|
|
2376
4398
|
}),
|
|
2377
4399
|
[key, fallback]
|
|
2378
4400
|
);
|
|
2379
|
-
const subscribe =
|
|
4401
|
+
const subscribe = useCallback2(
|
|
2380
4402
|
(callback) => client.addFlagCheckListener(key, callback),
|
|
2381
4403
|
[client, key]
|
|
2382
4404
|
);
|
|
2383
|
-
const getSnapshot =
|
|
4405
|
+
const getSnapshot = useCallback2(() => {
|
|
2384
4406
|
const check = client.getFlagCheck(key);
|
|
2385
4407
|
return check ?? fallbackCheck;
|
|
2386
4408
|
}, [client, key, fallbackCheck]);
|
|
2387
|
-
return
|
|
4409
|
+
return useSyncExternalStore2(subscribe, getSnapshot, () => fallbackCheck);
|
|
2388
4410
|
};
|
|
2389
4411
|
var useSchematicPlan = (opts) => {
|
|
2390
4412
|
const client = useSchematicClient(opts);
|
|
2391
4413
|
const fallback = opts?.fallback;
|
|
2392
|
-
const fallbackPlan =
|
|
4414
|
+
const fallbackPlan = useMemo6(
|
|
2393
4415
|
() => fallback,
|
|
2394
4416
|
[
|
|
2395
4417
|
fallback?.id,
|
|
@@ -2398,38 +4420,82 @@ var useSchematicPlan = (opts) => {
|
|
|
2398
4420
|
fallback?.trialStatus
|
|
2399
4421
|
]
|
|
2400
4422
|
);
|
|
2401
|
-
const subscribe =
|
|
4423
|
+
const subscribe = useCallback2(
|
|
2402
4424
|
(callback) => client.addPlanListener(callback),
|
|
2403
4425
|
[client]
|
|
2404
4426
|
);
|
|
2405
|
-
const getSnapshot =
|
|
4427
|
+
const getSnapshot = useCallback2(() => {
|
|
2406
4428
|
const plan = client.getPlan();
|
|
2407
4429
|
return plan ?? fallbackPlan;
|
|
2408
4430
|
}, [client, fallbackPlan]);
|
|
2409
|
-
return
|
|
4431
|
+
return useSyncExternalStore2(subscribe, getSnapshot, () => fallbackPlan);
|
|
4432
|
+
};
|
|
4433
|
+
var useSchematicCreditBalance = (creditId, opts) => {
|
|
4434
|
+
const client = useSchematicClient(opts);
|
|
4435
|
+
const subscribe = useCallback2(
|
|
4436
|
+
(callback) => client.addCreditBalanceListener(callback),
|
|
4437
|
+
[client]
|
|
4438
|
+
);
|
|
4439
|
+
const getSnapshot = useCallback2(
|
|
4440
|
+
() => creditId === void 0 ? void 0 : client.getCreditBalance(creditId),
|
|
4441
|
+
[client, creditId]
|
|
4442
|
+
);
|
|
4443
|
+
const balance = useSyncExternalStore2(subscribe, getSnapshot, () => void 0);
|
|
4444
|
+
const isPendingSubscribe = useCallback2(
|
|
4445
|
+
(callback) => client.addIsPendingListener(callback),
|
|
4446
|
+
[client]
|
|
4447
|
+
);
|
|
4448
|
+
const isPending = useSyncExternalStore2(
|
|
4449
|
+
isPendingSubscribe,
|
|
4450
|
+
() => client.getIsPending(),
|
|
4451
|
+
() => true
|
|
4452
|
+
);
|
|
4453
|
+
return useMemo6(
|
|
4454
|
+
() => ({
|
|
4455
|
+
balance: balance?.settled ?? 0,
|
|
4456
|
+
isLoading: balance === void 0 && isPending
|
|
4457
|
+
}),
|
|
4458
|
+
[balance, isPending]
|
|
4459
|
+
);
|
|
2410
4460
|
};
|
|
2411
4461
|
var useSchematicIsPending = (opts) => {
|
|
2412
4462
|
const client = useSchematicClient(opts);
|
|
2413
|
-
const subscribe =
|
|
4463
|
+
const subscribe = useCallback2(
|
|
2414
4464
|
(callback) => client.addIsPendingListener(callback),
|
|
2415
4465
|
[client]
|
|
2416
4466
|
);
|
|
2417
|
-
const getSnapshot =
|
|
2418
|
-
return
|
|
4467
|
+
const getSnapshot = useCallback2(() => client.getIsPending(), [client]);
|
|
4468
|
+
return useSyncExternalStore2(subscribe, getSnapshot, () => true);
|
|
2419
4469
|
};
|
|
2420
4470
|
export {
|
|
4471
|
+
BillingDataProvider,
|
|
4472
|
+
BillingProvider,
|
|
4473
|
+
DEFAULT_INVOICE_QUERY,
|
|
4474
|
+
INVOICE_PAGE_SIZE,
|
|
4475
|
+
InvoiceStatus,
|
|
2421
4476
|
RuleType,
|
|
2422
4477
|
Schematic,
|
|
4478
|
+
SchematicI18nContext,
|
|
4479
|
+
SchematicI18nProvider,
|
|
2423
4480
|
SchematicProvider,
|
|
2424
4481
|
TrialStatus,
|
|
2425
4482
|
UsagePeriod,
|
|
4483
|
+
normalizeInvoiceQuery2 as normalizeInvoiceQuery,
|
|
4484
|
+
useBillingDataSource,
|
|
4485
|
+
useInvalidateBillingData,
|
|
4486
|
+
useInvoices,
|
|
2426
4487
|
useSchematic,
|
|
2427
4488
|
useSchematicContext,
|
|
4489
|
+
useSchematicCreditBalance,
|
|
2428
4490
|
useSchematicEntitlement,
|
|
2429
4491
|
useSchematicEvents,
|
|
2430
4492
|
useSchematicFlag,
|
|
4493
|
+
useSchematicI18n,
|
|
2431
4494
|
useSchematicIsPending,
|
|
2432
|
-
|
|
4495
|
+
useSchematicLocale,
|
|
4496
|
+
useSchematicPlan,
|
|
4497
|
+
useSchematicStrings,
|
|
4498
|
+
useSchematicTranslate
|
|
2433
4499
|
};
|
|
2434
4500
|
/*! Bundled license information:
|
|
2435
4501
|
|