@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.
@@ -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
- return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
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
- if (!getRandomValues) {
572
- if (typeof crypto === "undefined" || !crypto.getRandomValues) {
573
- throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");
574
- }
575
- getRandomValues = crypto.getRandomValues.bind(crypto);
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 getRandomValues(rnds8);
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.4.0";
832
- var anonymousIdKey = "schematicId";
833
- var Schematic = class {
834
- additionalHeaders = {};
835
- apiKey;
836
- apiUrl = "https://api.schematichq.com";
837
- conn = null;
838
- context = {};
839
- debugEnabled = false;
840
- offlineEnabled = false;
841
- eventQueue;
842
- contextDependentEventQueue;
843
- eventUrl = "https://c.schematichq.com";
844
- flagCheckListeners = {};
845
- flagValueListeners = {};
846
- isPending = true;
847
- isPendingListeners = /* @__PURE__ */ new Set();
848
- planListeners = /* @__PURE__ */ new Set();
849
- storage;
850
- useWebSocket = false;
851
- checks = {};
852
- featureUsageEventMap = {};
853
- planChecks = {};
854
- webSocketUrl = "wss://api.schematichq.com";
855
- webSocketConnectionTimeout = 1e4;
856
- webSocketReconnect = true;
857
- webSocketMaxReconnectAttempts = 7;
858
- // Max attempts after connection disrupted
859
- webSocketMaxConnectionAttempts = 3;
860
- // Max attempts for initial connection
861
- webSocketInitialRetryDelay = 1e3;
862
- webSocketMaxRetryDelay = 3e4;
863
- wsReconnectAttempts = 0;
864
- wsReconnectTimer = null;
865
- wsIntentionalDisconnect = false;
866
- currentWebSocket = null;
867
- isConnecting = false;
868
- maxEventQueueSize = 100;
869
- // Prevent memory issues with very long network outages
870
- maxEventRetries = 5;
871
- // Maximum retry attempts for failed events
872
- eventRetryInitialDelay = 1e3;
873
- // Initial retry delay in ms
874
- eventRetryMaxDelay = 3e4;
875
- // Maximum retry delay in ms
876
- retryTimer = null;
877
- flagValueDefaults = {};
878
- flagCheckDefaults = {};
879
- fallbackCheckCache = {};
880
- constructor(apiKey, options) {
881
- this.apiKey = apiKey;
882
- this.eventQueue = [];
883
- this.contextDependentEventQueue = [];
884
- this.useWebSocket = options?.useWebSocket ?? false;
885
- this.debugEnabled = options?.debug ?? false;
886
- this.offlineEnabled = options?.offline ?? false;
887
- if (typeof window !== "undefined" && typeof window.location !== "undefined") {
888
- const params = new URLSearchParams(window.location.search);
889
- const debugParam = params.get("schematic_debug");
890
- if (debugParam !== null && (debugParam === "" || debugParam === "true" || debugParam === "1")) {
891
- this.debugEnabled = true;
892
- }
893
- const offlineParam = params.get("schematic_offline");
894
- if (offlineParam !== null && (offlineParam === "" || offlineParam === "true" || offlineParam === "1")) {
895
- this.offlineEnabled = true;
896
- this.debugEnabled = true;
897
- }
898
- }
899
- if (this.offlineEnabled && options?.debug !== false) {
900
- this.debugEnabled = true;
901
- }
902
- if (this.offlineEnabled) {
903
- this.setIsPending(false);
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
- if (options?.apiUrl !== void 0) {
920
- this.apiUrl = options.apiUrl;
921
- }
922
- if (options?.eventUrl !== void 0) {
923
- this.eventUrl = options.eventUrl;
924
- }
925
- if (options?.webSocketUrl !== void 0) {
926
- this.webSocketUrl = options.webSocketUrl;
927
- }
928
- if (options?.webSocketConnectionTimeout !== void 0) {
929
- this.webSocketConnectionTimeout = options.webSocketConnectionTimeout;
930
- }
931
- if (options?.webSocketReconnect !== void 0) {
932
- this.webSocketReconnect = options.webSocketReconnect;
933
- }
934
- if (options?.webSocketMaxReconnectAttempts !== void 0) {
935
- this.webSocketMaxReconnectAttempts = options.webSocketMaxReconnectAttempts;
936
- }
937
- if (options?.webSocketInitialRetryDelay !== void 0) {
938
- this.webSocketInitialRetryDelay = options.webSocketInitialRetryDelay;
939
- }
940
- if (options?.webSocketMaxRetryDelay !== void 0) {
941
- this.webSocketMaxRetryDelay = options.webSocketMaxRetryDelay;
942
- }
943
- if (options?.maxEventQueueSize !== void 0) {
944
- this.maxEventQueueSize = options.maxEventQueueSize;
945
- }
946
- if (options?.maxEventRetries !== void 0) {
947
- this.maxEventRetries = options.maxEventRetries;
948
- }
949
- if (options?.eventRetryInitialDelay !== void 0) {
950
- this.eventRetryInitialDelay = options.eventRetryInitialDelay;
951
- }
952
- if (options?.eventRetryMaxDelay !== void 0) {
953
- this.eventRetryMaxDelay = options.eventRetryMaxDelay;
954
- }
955
- if (options?.flagValueDefaults !== void 0) {
956
- this.flagValueDefaults = options.flagValueDefaults;
957
- }
958
- if (options?.flagCheckDefaults !== void 0) {
959
- this.flagCheckDefaults = options.flagCheckDefaults;
960
- }
961
- if (typeof window !== "undefined" && window?.addEventListener) {
962
- window.addEventListener("beforeunload", () => {
963
- this.flushEventQueue();
964
- this.flushContextDependentEventQueue();
965
- });
966
- if (this.useWebSocket) {
967
- window.addEventListener("offline", () => {
968
- this.debug("Browser went offline, closing WebSocket connection");
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.apiUrl}/flags/${key}/check`;
1938
+ const requestUrl = `${this._apiUrl}/flags/${key}/check`;
1071
1939
  return fetch(requestUrl, {
1072
1940
  method: "POST",
1073
1941
  headers: {
1074
- ...this.additionalHeaders ?? {},
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.apiUrl}/flags/check`;
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.additionalHeaders ?? {},
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
- this.setIsPending(true);
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.additionalHeaders ?? {},
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 (!isResolved && !this.wsIntentionalDisconnect && this.webSocketReconnect) {
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.additionalHeaders["X-Schematic-Client-Version"] ?? `schematic-js@${version}`;
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 React, { createContext, useEffect, useMemo, useRef } from "react";
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.4.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 = createContext(
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 = useRef({
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 = useMemo(() => {
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
- useEffect(() => {
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 = useMemo(
4298
+ const contextValue = useMemo5(
2307
4299
  () => ({
2308
4300
  client
2309
4301
  }),
2310
4302
  [client]
2311
4303
  );
2312
- return /* @__PURE__ */ jsx(SchematicContext.Provider, { value: contextValue, children });
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 = React.useContext(SchematicContext);
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 useMemo2, useSyncExternalStore, useCallback } from "react";
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 useMemo2(() => {
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 useMemo2(
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 = useCallback(
4367
+ const track = useCallback2(
2346
4368
  (...args) => client.track(...args),
2347
4369
  [client]
2348
4370
  );
2349
- const identify = useCallback(
4371
+ const identify = useCallback2(
2350
4372
  (...args) => client.identify(...args),
2351
4373
  [client]
2352
4374
  );
2353
- return useMemo2(() => ({ track, identify }), [track, identify]);
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 = useCallback(
4380
+ const subscribe = useCallback2(
2359
4381
  (callback) => client.addFlagValueListener(key, callback),
2360
4382
  [client, key]
2361
4383
  );
2362
- const getSnapshot = useCallback(() => {
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 useSyncExternalStore(subscribe, getSnapshot, () => fallback);
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 = useMemo2(
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 = useCallback(
4401
+ const subscribe = useCallback2(
2380
4402
  (callback) => client.addFlagCheckListener(key, callback),
2381
4403
  [client, key]
2382
4404
  );
2383
- const getSnapshot = useCallback(() => {
4405
+ const getSnapshot = useCallback2(() => {
2384
4406
  const check = client.getFlagCheck(key);
2385
4407
  return check ?? fallbackCheck;
2386
4408
  }, [client, key, fallbackCheck]);
2387
- return useSyncExternalStore(subscribe, getSnapshot, () => fallbackCheck);
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 = useMemo2(
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 = useCallback(
4423
+ const subscribe = useCallback2(
2402
4424
  (callback) => client.addPlanListener(callback),
2403
4425
  [client]
2404
4426
  );
2405
- const getSnapshot = useCallback(() => {
4427
+ const getSnapshot = useCallback2(() => {
2406
4428
  const plan = client.getPlan();
2407
4429
  return plan ?? fallbackPlan;
2408
4430
  }, [client, fallbackPlan]);
2409
- return useSyncExternalStore(subscribe, getSnapshot, () => fallbackPlan);
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 = useCallback(
4463
+ const subscribe = useCallback2(
2414
4464
  (callback) => client.addIsPendingListener(callback),
2415
4465
  [client]
2416
4466
  );
2417
- const getSnapshot = useCallback(() => client.getIsPending(), [client]);
2418
- return useSyncExternalStore(subscribe, getSnapshot, () => true);
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
- useSchematicPlan
4495
+ useSchematicLocale,
4496
+ useSchematicPlan,
4497
+ useSchematicStrings,
4498
+ useSchematicTranslate
2433
4499
  };
2434
4500
  /*! Bundled license information:
2435
4501