@schematichq/schematic-js 1.4.0 → 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.
@@ -5,7 +5,15 @@ var __getOwnPropNames = Object.getOwnPropertyNames;
5
5
  var __getProtoOf = Object.getPrototypeOf;
6
6
  var __hasOwnProp = Object.prototype.hasOwnProperty;
7
7
  var __commonJS = (cb, mod) => function __require() {
8
- return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
8
+ try {
9
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
10
+ } catch (e) {
11
+ throw mod = 0, e;
12
+ }
13
+ };
14
+ var __export = (target, all) => {
15
+ for (var name in all)
16
+ __defProp(target, name, { get: all[name], enumerable: true });
9
17
  };
10
18
  var __copyProps = (to, from, except, desc) => {
11
19
  if (from && typeof from === "object" || typeof from === "function") {
@@ -24,9 +32,9 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
24
32
  mod
25
33
  ));
26
34
 
27
- // node_modules/cross-fetch/dist/browser-polyfill.js
35
+ // node_modules/.pnpm/cross-fetch@4.1.0/node_modules/cross-fetch/dist/browser-polyfill.js
28
36
  var require_browser_polyfill = __commonJS({
29
- "node_modules/cross-fetch/dist/browser-polyfill.js"(exports) {
37
+ "node_modules/.pnpm/cross-fetch@4.1.0/node_modules/cross-fetch/dist/browser-polyfill.js"(exports) {
30
38
  (function(self2) {
31
39
  var irrelevant = (function(exports2) {
32
40
  var g = typeof globalThis !== "undefined" && globalThis || typeof self2 !== "undefined" && self2 || // eslint-disable-next-line no-undef
@@ -560,7 +568,7 @@ var require_browser_polyfill = __commonJS({
560
568
  }
561
569
  });
562
570
 
563
- // node_modules/uuid/dist/stringify.js
571
+ // node_modules/.pnpm/uuid@14.0.2/node_modules/uuid/dist/stringify.js
564
572
  var byteToHex = [];
565
573
  for (let i = 0; i < 256; ++i) {
566
574
  byteToHex.push((i + 256).toString(16).slice(1));
@@ -569,24 +577,19 @@ function unsafeStringify(arr, offset = 0) {
569
577
  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();
570
578
  }
571
579
 
572
- // node_modules/uuid/dist/rng.js
573
- var getRandomValues;
580
+ // node_modules/.pnpm/uuid@14.0.2/node_modules/uuid/dist/rng.js
574
581
  var rnds8 = new Uint8Array(16);
575
582
  function rng() {
576
- if (!getRandomValues) {
577
- if (typeof crypto === "undefined" || !crypto.getRandomValues) {
578
- throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");
579
- }
580
- getRandomValues = crypto.getRandomValues.bind(crypto);
581
- }
582
- return getRandomValues(rnds8);
583
+ return crypto.getRandomValues(rnds8);
583
584
  }
584
585
 
585
- // node_modules/uuid/dist/native.js
586
- var randomUUID = typeof crypto !== "undefined" && crypto.randomUUID && crypto.randomUUID.bind(crypto);
587
- var native_default = { randomUUID };
588
-
589
- // node_modules/uuid/dist/v4.js
586
+ // node_modules/.pnpm/uuid@14.0.2/node_modules/uuid/dist/v4.js
587
+ function v4(options, buf, offset) {
588
+ if (!buf && !options && crypto.randomUUID) {
589
+ return crypto.randomUUID();
590
+ }
591
+ return _v4(options, buf, offset);
592
+ }
590
593
  function _v4(options, buf, offset) {
591
594
  options = options || {};
592
595
  const rnds = options.random ?? options.rng?.() ?? rng();
@@ -607,12 +610,6 @@ function _v4(options, buf, offset) {
607
610
  }
608
611
  return unsafeStringify(rnds);
609
612
  }
610
- function v4(options, buf, offset) {
611
- if (native_default.randomUUID && !buf && !options) {
612
- return native_default.randomUUID();
613
- }
614
- return _v4(options, buf, offset);
615
- }
616
613
  var v4_default = v4;
617
614
 
618
615
  // src/index.ts
@@ -626,6 +623,36 @@ function EntitlementValueTypeFromJSONTyped(json, ignoreDiscriminator) {
626
623
  return json;
627
624
  }
628
625
 
626
+ // src/types/api/models/MetricPeriodMonthReset.ts
627
+ function MetricPeriodMonthResetFromJSON(json) {
628
+ return MetricPeriodMonthResetFromJSONTyped(json, false);
629
+ }
630
+ function MetricPeriodMonthResetFromJSONTyped(json, ignoreDiscriminator) {
631
+ return json;
632
+ }
633
+
634
+ // src/types/api/models/WarningTier.ts
635
+ function WarningTierFromJSON(json) {
636
+ return WarningTierFromJSONTyped(json, false);
637
+ }
638
+ function WarningTierFromJSONTyped(json, ignoreDiscriminator) {
639
+ if (json == null) {
640
+ return json;
641
+ }
642
+ return {
643
+ key: json["key"],
644
+ value: json["value"]
645
+ };
646
+ }
647
+
648
+ // src/types/api/models/MetricPeriod.ts
649
+ function MetricPeriodFromJSON(json) {
650
+ return MetricPeriodFromJSONTyped(json, false);
651
+ }
652
+ function MetricPeriodFromJSONTyped(json, ignoreDiscriminator) {
653
+ return json;
654
+ }
655
+
629
656
  // src/types/api/models/FeatureEntitlement.ts
630
657
  function FeatureEntitlementFromJSON(json) {
631
658
  return FeatureEntitlementFromJSONTyped(json, false);
@@ -636,22 +663,35 @@ function FeatureEntitlementFromJSONTyped(json, ignoreDiscriminator) {
636
663
  }
637
664
  return {
638
665
  allocation: json["allocation"] == null ? void 0 : json["allocation"],
666
+ consumptionRate: json["consumption_rate"] == null ? void 0 : json["consumption_rate"],
639
667
  creditId: json["credit_id"] == null ? void 0 : json["credit_id"],
640
668
  creditRemaining: json["credit_remaining"] == null ? void 0 : json["credit_remaining"],
669
+ creditReserved: json["credit_reserved"] == null ? void 0 : json["credit_reserved"],
670
+ creditSettled: json["credit_settled"] == null ? void 0 : json["credit_settled"],
641
671
  creditTotal: json["credit_total"] == null ? void 0 : json["credit_total"],
642
672
  creditUsed: json["credit_used"] == null ? void 0 : json["credit_used"],
643
673
  eventName: json["event_name"] == null ? void 0 : json["event_name"],
674
+ eventSubtype: json["event_subtype"] == null ? void 0 : json["event_subtype"],
644
675
  featureId: json["feature_id"],
645
676
  featureKey: json["feature_key"],
646
- metricPeriod: json["metric_period"] == null ? void 0 : json["metric_period"],
677
+ metricPeriod: json["metric_period"] == null ? void 0 : MetricPeriodFromJSON(json["metric_period"]),
647
678
  metricResetAt: json["metric_reset_at"] == null ? void 0 : new Date(json["metric_reset_at"]),
648
- monthReset: json["month_reset"] == null ? void 0 : json["month_reset"],
679
+ monthReset: json["month_reset"] == null ? void 0 : MetricPeriodMonthResetFromJSON(json["month_reset"]),
649
680
  softLimit: json["soft_limit"] == null ? void 0 : json["soft_limit"],
650
681
  usage: json["usage"] == null ? void 0 : json["usage"],
651
- valueType: EntitlementValueTypeFromJSON(json["value_type"])
682
+ valueType: EntitlementValueTypeFromJSON(json["value_type"]),
683
+ warningTiers: json["warning_tiers"] == null ? void 0 : json["warning_tiers"].map(WarningTierFromJSON)
652
684
  };
653
685
  }
654
686
 
687
+ // src/types/api/models/RuleType.ts
688
+ function RuleTypeFromJSON(json) {
689
+ return RuleTypeFromJSONTyped(json, false);
690
+ }
691
+ function RuleTypeFromJSONTyped(json, ignoreDiscriminator) {
692
+ return json;
693
+ }
694
+
655
695
  // src/types/api/models/CheckFlagResponseData.ts
656
696
  function CheckFlagResponseDataFromJSON(json) {
657
697
  return CheckFlagResponseDataFromJSONTyped(json, false);
@@ -667,13 +707,13 @@ function CheckFlagResponseDataFromJSONTyped(json, ignoreDiscriminator) {
667
707
  featureAllocation: json["feature_allocation"] == null ? void 0 : json["feature_allocation"],
668
708
  featureUsage: json["feature_usage"] == null ? void 0 : json["feature_usage"],
669
709
  featureUsageEvent: json["feature_usage_event"] == null ? void 0 : json["feature_usage_event"],
670
- featureUsagePeriod: json["feature_usage_period"] == null ? void 0 : json["feature_usage_period"],
710
+ featureUsagePeriod: json["feature_usage_period"] == null ? void 0 : MetricPeriodFromJSON(json["feature_usage_period"]),
671
711
  featureUsageResetAt: json["feature_usage_reset_at"] == null ? void 0 : new Date(json["feature_usage_reset_at"]),
672
712
  flag: json["flag"],
673
713
  flagId: json["flag_id"] == null ? void 0 : json["flag_id"],
674
714
  reason: json["reason"],
675
715
  ruleId: json["rule_id"] == null ? void 0 : json["rule_id"],
676
- ruleType: json["rule_type"] == null ? void 0 : json["rule_type"],
716
+ ruleType: json["rule_type"] == null ? void 0 : RuleTypeFromJSON(json["rule_type"]),
677
717
  userId: json["user_id"] == null ? void 0 : json["user_id"],
678
718
  value: json["value"]
679
719
  };
@@ -693,6 +733,272 @@ function CheckFlagResponseFromJSONTyped(json, ignoreDiscriminator) {
693
733
  };
694
734
  }
695
735
 
736
+ // src/types/api/runtime.ts
737
+ var BASE_PATH = "https://api.schematichq.com".replace(/\/+$/, "");
738
+ var Configuration = class {
739
+ constructor(configuration = {}) {
740
+ this.configuration = configuration;
741
+ }
742
+ configuration;
743
+ set config(configuration) {
744
+ this.configuration = configuration;
745
+ }
746
+ get basePath() {
747
+ return this.configuration.basePath != null ? this.configuration.basePath : BASE_PATH;
748
+ }
749
+ get fetchApi() {
750
+ return this.configuration.fetchApi;
751
+ }
752
+ get middleware() {
753
+ return this.configuration.middleware || [];
754
+ }
755
+ get queryParamsStringify() {
756
+ return this.configuration.queryParamsStringify || querystring;
757
+ }
758
+ get username() {
759
+ return this.configuration.username;
760
+ }
761
+ get password() {
762
+ return this.configuration.password;
763
+ }
764
+ get apiKey() {
765
+ const apiKey = this.configuration.apiKey;
766
+ if (apiKey) {
767
+ return typeof apiKey === "function" ? apiKey : () => apiKey;
768
+ }
769
+ return void 0;
770
+ }
771
+ get accessToken() {
772
+ const accessToken = this.configuration.accessToken;
773
+ if (accessToken) {
774
+ return typeof accessToken === "function" ? accessToken : async () => accessToken;
775
+ }
776
+ return void 0;
777
+ }
778
+ get headers() {
779
+ return this.configuration.headers;
780
+ }
781
+ get credentials() {
782
+ return this.configuration.credentials;
783
+ }
784
+ };
785
+ var DefaultConfig = new Configuration();
786
+ var BaseAPI = class _BaseAPI {
787
+ constructor(configuration = DefaultConfig) {
788
+ this.configuration = configuration;
789
+ this.middleware = configuration.middleware;
790
+ }
791
+ configuration;
792
+ static jsonRegex = new RegExp(
793
+ "^(:?application/json|[^;/ ]+/[^;/ ]+[+]json)[ ]*(:?;.*)?$",
794
+ "i"
795
+ );
796
+ middleware;
797
+ withMiddleware(...middlewares) {
798
+ const next = this.clone();
799
+ next.middleware = next.middleware.concat(...middlewares);
800
+ return next;
801
+ }
802
+ withPreMiddleware(...preMiddlewares) {
803
+ const middlewares = preMiddlewares.map((pre) => ({ pre }));
804
+ return this.withMiddleware(...middlewares);
805
+ }
806
+ withPostMiddleware(...postMiddlewares) {
807
+ const middlewares = postMiddlewares.map((post) => ({ post }));
808
+ return this.withMiddleware(...middlewares);
809
+ }
810
+ /**
811
+ * Check if the given MIME is a JSON MIME.
812
+ * JSON MIME examples:
813
+ * application/json
814
+ * application/json; charset=UTF8
815
+ * APPLICATION/JSON
816
+ * application/vnd.company+json
817
+ * @param mime - MIME (Multipurpose Internet Mail Extensions)
818
+ * @return True if the given MIME is JSON, false otherwise.
819
+ */
820
+ isJsonMime(mime) {
821
+ if (!mime) {
822
+ return false;
823
+ }
824
+ return _BaseAPI.jsonRegex.test(mime);
825
+ }
826
+ async request(context, initOverrides) {
827
+ const { url, init } = await this.createFetchParams(context, initOverrides);
828
+ const response = await this.fetchApi(url, init);
829
+ if (response && response.status >= 200 && response.status < 300) {
830
+ return response;
831
+ }
832
+ throw new ResponseError(response, "Response returned an error code");
833
+ }
834
+ async createFetchParams(context, initOverrides) {
835
+ let url = this.configuration.basePath + context.path;
836
+ if (context.query !== void 0 && Object.keys(context.query).length !== 0) {
837
+ url += "?" + this.configuration.queryParamsStringify(context.query);
838
+ }
839
+ const headers = Object.assign(
840
+ {},
841
+ this.configuration.headers,
842
+ context.headers
843
+ );
844
+ Object.keys(headers).forEach(
845
+ (key) => headers[key] === void 0 ? delete headers[key] : {}
846
+ );
847
+ const initOverrideFn = typeof initOverrides === "function" ? initOverrides : async () => initOverrides;
848
+ const initParams = {
849
+ method: context.method,
850
+ headers,
851
+ body: context.body,
852
+ credentials: this.configuration.credentials
853
+ };
854
+ const overriddenInit = {
855
+ ...initParams,
856
+ ...await initOverrideFn({
857
+ init: initParams,
858
+ context
859
+ })
860
+ };
861
+ let body;
862
+ if (isFormData(overriddenInit.body) || overriddenInit.body instanceof URLSearchParams || isBlob(overriddenInit.body)) {
863
+ body = overriddenInit.body;
864
+ } else if (this.isJsonMime(headers["Content-Type"])) {
865
+ body = JSON.stringify(overriddenInit.body);
866
+ } else {
867
+ body = overriddenInit.body;
868
+ }
869
+ const init = {
870
+ ...overriddenInit,
871
+ body
872
+ };
873
+ return { url, init };
874
+ }
875
+ fetchApi = async (url, init) => {
876
+ let fetchParams = { url, init };
877
+ for (const middleware of this.middleware) {
878
+ if (middleware.pre) {
879
+ fetchParams = await middleware.pre({
880
+ fetch: this.fetchApi,
881
+ ...fetchParams
882
+ }) || fetchParams;
883
+ }
884
+ }
885
+ let response = void 0;
886
+ try {
887
+ response = await (this.configuration.fetchApi || fetch)(
888
+ fetchParams.url,
889
+ fetchParams.init
890
+ );
891
+ } catch (e) {
892
+ for (const middleware of this.middleware) {
893
+ if (middleware.onError) {
894
+ response = await middleware.onError({
895
+ fetch: this.fetchApi,
896
+ url: fetchParams.url,
897
+ init: fetchParams.init,
898
+ error: e,
899
+ response: response ? response.clone() : void 0
900
+ }) || response;
901
+ }
902
+ }
903
+ if (response === void 0) {
904
+ if (e instanceof Error) {
905
+ throw new FetchError(
906
+ e,
907
+ "The request failed and the interceptors did not return an alternative response"
908
+ );
909
+ } else {
910
+ throw e;
911
+ }
912
+ }
913
+ }
914
+ for (const middleware of this.middleware) {
915
+ if (middleware.post) {
916
+ response = await middleware.post({
917
+ fetch: this.fetchApi,
918
+ url: fetchParams.url,
919
+ init: fetchParams.init,
920
+ response: response.clone()
921
+ }) || response;
922
+ }
923
+ }
924
+ return response;
925
+ };
926
+ /**
927
+ * Create a shallow clone of `this` by constructing a new instance
928
+ * and then shallow cloning data members.
929
+ */
930
+ clone() {
931
+ const constructor = this.constructor;
932
+ const next = new constructor(this.configuration);
933
+ next.middleware = this.middleware.slice();
934
+ return next;
935
+ }
936
+ };
937
+ function isBlob(value) {
938
+ return typeof Blob !== "undefined" && value instanceof Blob;
939
+ }
940
+ function isFormData(value) {
941
+ return typeof FormData !== "undefined" && value instanceof FormData;
942
+ }
943
+ var ResponseError = class extends Error {
944
+ constructor(response, msg) {
945
+ super(msg);
946
+ this.response = response;
947
+ }
948
+ response;
949
+ name = "ResponseError";
950
+ };
951
+ var FetchError = class extends Error {
952
+ constructor(cause, msg) {
953
+ super(msg);
954
+ this.cause = cause;
955
+ }
956
+ cause;
957
+ name = "FetchError";
958
+ };
959
+ function querystring(params, prefix = "") {
960
+ return Object.keys(params).map((key) => querystringSingleKey(key, params[key], prefix)).filter((part) => part.length > 0).join("&");
961
+ }
962
+ function querystringSingleKey(key, value, keyPrefix = "") {
963
+ const fullKey = keyPrefix + (keyPrefix.length ? `[${key}]` : key);
964
+ if (value instanceof Array) {
965
+ const multiValue = value.map((singleValue) => encodeURIComponent(String(singleValue))).join(`&${encodeURIComponent(fullKey)}=`);
966
+ return `${encodeURIComponent(fullKey)}=${multiValue}`;
967
+ }
968
+ if (value instanceof Set) {
969
+ const valueAsArray = Array.from(value);
970
+ return querystringSingleKey(key, valueAsArray, keyPrefix);
971
+ }
972
+ if (value instanceof Date) {
973
+ return `${encodeURIComponent(fullKey)}=${encodeURIComponent(value.toISOString())}`;
974
+ }
975
+ if (value instanceof Object) {
976
+ return querystring(value, fullKey);
977
+ }
978
+ return `${encodeURIComponent(fullKey)}=${encodeURIComponent(String(value))}`;
979
+ }
980
+ function mapValues(data, fn) {
981
+ return Object.keys(data).reduce(
982
+ (acc, key) => ({ ...acc, [key]: fn(data[key]) }),
983
+ {}
984
+ );
985
+ }
986
+
987
+ // src/types/api/models/CompanyCreditBalance.ts
988
+ function CompanyCreditBalanceFromJSON(json) {
989
+ return CompanyCreditBalanceFromJSONTyped(json, false);
990
+ }
991
+ function CompanyCreditBalanceFromJSONTyped(json, ignoreDiscriminator) {
992
+ if (json == null) {
993
+ return json;
994
+ }
995
+ return {
996
+ remaining: json["remaining"],
997
+ reserved: json["reserved"],
998
+ settled: json["settled"]
999
+ };
1000
+ }
1001
+
696
1002
  // src/types/api/models/TrialStatus.ts
697
1003
  var TrialStatus = {
698
1004
  Active: "active",
@@ -731,6 +1037,7 @@ function CheckFlagsResponseDataFromJSONTyped(json, ignoreDiscriminator) {
731
1037
  return json;
732
1038
  }
733
1039
  return {
1040
+ creditBalances: json["credit_balances"] == null ? void 0 : mapValues(json["credit_balances"], CompanyCreditBalanceFromJSON),
734
1041
  flags: json["flags"].map(CheckFlagResponseDataFromJSON),
735
1042
  plan: json["plan"] == null ? void 0 : DatastreamCompanyPlanFromJSON(json["plan"])
736
1043
  };
@@ -763,6 +1070,7 @@ function EventBodyFlagCheckToJSONTyped(value, ignoreDiscriminator = false) {
763
1070
  error: value["error"],
764
1071
  flag_id: value["flagId"],
765
1072
  flag_key: value["flagKey"],
1073
+ preflight: value["preflight"],
766
1074
  reason: value["reason"],
767
1075
  req_company: value["reqCompany"],
768
1076
  req_user: value["reqUser"],
@@ -819,7 +1127,10 @@ var CheckFlagReturnFromJSON = (json) => {
819
1127
  return {
820
1128
  featureUsageExceeded,
821
1129
  companyId: companyId == null ? void 0 : companyId,
1130
+ creditId: entitlement?.creditId == null ? void 0 : entitlement.creditId,
822
1131
  creditRemaining: entitlement?.creditRemaining == null ? void 0 : entitlement.creditRemaining,
1132
+ creditReserved: entitlement?.creditReserved == null ? void 0 : entitlement.creditReserved,
1133
+ creditSettled: entitlement?.creditSettled == null ? void 0 : entitlement.creditSettled,
823
1134
  error: error == null ? void 0 : error,
824
1135
  featureAllocation: resolvedAllocation == null ? void 0 : resolvedAllocation,
825
1136
  featureUsage: resolvedUsage == null ? void 0 : resolvedUsage,
@@ -845,6 +1156,24 @@ var CheckPlanReturnFromJSON = (json) => {
845
1156
  trialStatus: trialStatus == null ? void 0 : trialStatus
846
1157
  };
847
1158
  };
1159
+ var CreditBalancesFromJSON = (json) => {
1160
+ if (json == null || typeof json !== "object") {
1161
+ return {};
1162
+ }
1163
+ const balances = {};
1164
+ for (const [creditId, raw] of Object.entries(json)) {
1165
+ if (raw == null || typeof raw !== "object") continue;
1166
+ const { remaining, reserved, settled } = CompanyCreditBalanceFromJSON(raw);
1167
+ if (typeof remaining !== "number" || typeof reserved !== "number" || typeof settled !== "number") {
1168
+ continue;
1169
+ }
1170
+ balances[creditId] = { remaining, reserved, settled };
1171
+ }
1172
+ return balances;
1173
+ };
1174
+
1175
+ // src/cacheVersion.ts
1176
+ var cacheVersion = "bb56e75d";
848
1177
 
849
1178
  // src/utils.ts
850
1179
  function contextString(context) {
@@ -863,10 +1192,597 @@ function contextString(context) {
863
1192
  }
864
1193
 
865
1194
  // src/version.ts
866
- var version = "1.4.0";
1195
+ var version = "1.6.0";
1196
+
1197
+ // src/billing/api/generated/models/index.ts
1198
+ var models_exports = {};
1199
+ __export(models_exports, {
1200
+ ApiErrorFromJSON: () => ApiErrorFromJSON,
1201
+ ApiErrorFromJSONTyped: () => ApiErrorFromJSONTyped,
1202
+ ApiErrorToJSON: () => ApiErrorToJSON,
1203
+ ApiErrorToJSONTyped: () => ApiErrorToJSONTyped,
1204
+ CompanyInvoiceResponseDataFromJSON: () => CompanyInvoiceResponseDataFromJSON,
1205
+ CompanyInvoiceResponseDataFromJSONTyped: () => CompanyInvoiceResponseDataFromJSONTyped,
1206
+ CompanyInvoiceResponseDataToJSON: () => CompanyInvoiceResponseDataToJSON,
1207
+ CompanyInvoiceResponseDataToJSONTyped: () => CompanyInvoiceResponseDataToJSONTyped,
1208
+ CompanyInvoicesResponseDataFromJSON: () => CompanyInvoicesResponseDataFromJSON,
1209
+ CompanyInvoicesResponseDataFromJSONTyped: () => CompanyInvoicesResponseDataFromJSONTyped,
1210
+ CompanyInvoicesResponseDataToJSON: () => CompanyInvoicesResponseDataToJSON,
1211
+ CompanyInvoicesResponseDataToJSONTyped: () => CompanyInvoicesResponseDataToJSONTyped,
1212
+ GetCompanyInvoicesParamsFromJSON: () => GetCompanyInvoicesParamsFromJSON,
1213
+ GetCompanyInvoicesParamsFromJSONTyped: () => GetCompanyInvoicesParamsFromJSONTyped,
1214
+ GetCompanyInvoicesParamsToJSON: () => GetCompanyInvoicesParamsToJSON,
1215
+ GetCompanyInvoicesParamsToJSONTyped: () => GetCompanyInvoicesParamsToJSONTyped,
1216
+ GetCompanyInvoicesResponseFromJSON: () => GetCompanyInvoicesResponseFromJSON,
1217
+ GetCompanyInvoicesResponseFromJSONTyped: () => GetCompanyInvoicesResponseFromJSONTyped,
1218
+ GetCompanyInvoicesResponseToJSON: () => GetCompanyInvoicesResponseToJSON,
1219
+ GetCompanyInvoicesResponseToJSONTyped: () => GetCompanyInvoicesResponseToJSONTyped,
1220
+ InvoiceStatus: () => InvoiceStatus,
1221
+ InvoiceStatusFromJSON: () => InvoiceStatusFromJSON,
1222
+ InvoiceStatusFromJSONTyped: () => InvoiceStatusFromJSONTyped,
1223
+ InvoiceStatusToJSON: () => InvoiceStatusToJSON,
1224
+ InvoiceStatusToJSONTyped: () => InvoiceStatusToJSONTyped,
1225
+ instanceOfApiError: () => instanceOfApiError,
1226
+ instanceOfCompanyInvoiceResponseData: () => instanceOfCompanyInvoiceResponseData,
1227
+ instanceOfCompanyInvoicesResponseData: () => instanceOfCompanyInvoicesResponseData,
1228
+ instanceOfGetCompanyInvoicesParams: () => instanceOfGetCompanyInvoicesParams,
1229
+ instanceOfGetCompanyInvoicesResponse: () => instanceOfGetCompanyInvoicesResponse,
1230
+ instanceOfInvoiceStatus: () => instanceOfInvoiceStatus
1231
+ });
1232
+
1233
+ // src/billing/api/generated/models/ApiError.ts
1234
+ function instanceOfApiError(value) {
1235
+ if (!("error" in value) || value["error"] === void 0) return false;
1236
+ return true;
1237
+ }
1238
+ function ApiErrorFromJSON(json) {
1239
+ return ApiErrorFromJSONTyped(json, false);
1240
+ }
1241
+ function ApiErrorFromJSONTyped(json, ignoreDiscriminator) {
1242
+ if (json == null) {
1243
+ return json;
1244
+ }
1245
+ return {
1246
+ error: json["error"]
1247
+ };
1248
+ }
1249
+ function ApiErrorToJSON(json) {
1250
+ return ApiErrorToJSONTyped(json, false);
1251
+ }
1252
+ function ApiErrorToJSONTyped(value, ignoreDiscriminator = false) {
1253
+ if (value == null) {
1254
+ return value;
1255
+ }
1256
+ return {
1257
+ error: value["error"]
1258
+ };
1259
+ }
1260
+
1261
+ // src/billing/api/generated/models/InvoiceStatus.ts
1262
+ var InvoiceStatus = {
1263
+ Draft: "draft",
1264
+ Open: "open",
1265
+ Paid: "paid",
1266
+ Uncollectible: "uncollectible",
1267
+ Void: "void"
1268
+ };
1269
+ function instanceOfInvoiceStatus(value) {
1270
+ for (const key in InvoiceStatus) {
1271
+ if (Object.prototype.hasOwnProperty.call(InvoiceStatus, key)) {
1272
+ if (InvoiceStatus[key] === value) {
1273
+ return true;
1274
+ }
1275
+ }
1276
+ }
1277
+ return false;
1278
+ }
1279
+ function InvoiceStatusFromJSON(json) {
1280
+ return InvoiceStatusFromJSONTyped(json, false);
1281
+ }
1282
+ function InvoiceStatusFromJSONTyped(json, ignoreDiscriminator) {
1283
+ return json;
1284
+ }
1285
+ function InvoiceStatusToJSON(value) {
1286
+ return value;
1287
+ }
1288
+ function InvoiceStatusToJSONTyped(value, ignoreDiscriminator) {
1289
+ return value;
1290
+ }
1291
+
1292
+ // src/billing/api/generated/models/CompanyInvoiceResponseData.ts
1293
+ function instanceOfCompanyInvoiceResponseData(value) {
1294
+ if (!("amountDue" in value) || value["amountDue"] === void 0) return false;
1295
+ if (!("createdAt" in value) || value["createdAt"] === void 0) return false;
1296
+ if (!("currency" in value) || value["currency"] === void 0) return false;
1297
+ if (!("id" in value) || value["id"] === void 0) return false;
1298
+ return true;
1299
+ }
1300
+ function CompanyInvoiceResponseDataFromJSON(json) {
1301
+ return CompanyInvoiceResponseDataFromJSONTyped(json, false);
1302
+ }
1303
+ function CompanyInvoiceResponseDataFromJSONTyped(json, ignoreDiscriminator) {
1304
+ if (json == null) {
1305
+ return json;
1306
+ }
1307
+ return {
1308
+ amountDue: json["amount_due"],
1309
+ createdAt: new Date(json["created_at"]),
1310
+ currency: json["currency"],
1311
+ dueDate: json["due_date"] == null ? void 0 : new Date(json["due_date"]),
1312
+ id: json["id"],
1313
+ status: json["status"] == null ? void 0 : InvoiceStatusFromJSON(json["status"]),
1314
+ url: json["url"] == null ? void 0 : json["url"]
1315
+ };
1316
+ }
1317
+ function CompanyInvoiceResponseDataToJSON(json) {
1318
+ return CompanyInvoiceResponseDataToJSONTyped(json, false);
1319
+ }
1320
+ function CompanyInvoiceResponseDataToJSONTyped(value, ignoreDiscriminator = false) {
1321
+ if (value == null) {
1322
+ return value;
1323
+ }
1324
+ return {
1325
+ amount_due: value["amountDue"],
1326
+ created_at: value["createdAt"].toISOString(),
1327
+ currency: value["currency"],
1328
+ due_date: value["dueDate"] == null ? void 0 : value["dueDate"].toISOString(),
1329
+ id: value["id"],
1330
+ status: InvoiceStatusToJSON(value["status"]),
1331
+ url: value["url"]
1332
+ };
1333
+ }
1334
+
1335
+ // src/billing/api/generated/models/CompanyInvoicesResponseData.ts
1336
+ function instanceOfCompanyInvoicesResponseData(value) {
1337
+ if (!("count" in value) || value["count"] === void 0) return false;
1338
+ if (!("invoices" in value) || value["invoices"] === void 0) return false;
1339
+ return true;
1340
+ }
1341
+ function CompanyInvoicesResponseDataFromJSON(json) {
1342
+ return CompanyInvoicesResponseDataFromJSONTyped(json, false);
1343
+ }
1344
+ function CompanyInvoicesResponseDataFromJSONTyped(json, ignoreDiscriminator) {
1345
+ if (json == null) {
1346
+ return json;
1347
+ }
1348
+ return {
1349
+ count: json["count"],
1350
+ invoices: json["invoices"].map(
1351
+ CompanyInvoiceResponseDataFromJSON
1352
+ )
1353
+ };
1354
+ }
1355
+ function CompanyInvoicesResponseDataToJSON(json) {
1356
+ return CompanyInvoicesResponseDataToJSONTyped(json, false);
1357
+ }
1358
+ function CompanyInvoicesResponseDataToJSONTyped(value, ignoreDiscriminator = false) {
1359
+ if (value == null) {
1360
+ return value;
1361
+ }
1362
+ return {
1363
+ count: value["count"],
1364
+ invoices: value["invoices"].map(
1365
+ CompanyInvoiceResponseDataToJSON
1366
+ )
1367
+ };
1368
+ }
1369
+
1370
+ // src/billing/api/generated/models/GetCompanyInvoicesParams.ts
1371
+ function instanceOfGetCompanyInvoicesParams(value) {
1372
+ return true;
1373
+ }
1374
+ function GetCompanyInvoicesParamsFromJSON(json) {
1375
+ return GetCompanyInvoicesParamsFromJSONTyped(json, false);
1376
+ }
1377
+ function GetCompanyInvoicesParamsFromJSONTyped(json, ignoreDiscriminator) {
1378
+ if (json == null) {
1379
+ return json;
1380
+ }
1381
+ return {
1382
+ includePending: json["include_pending"] == null ? void 0 : json["include_pending"],
1383
+ limit: json["limit"] == null ? void 0 : json["limit"],
1384
+ offset: json["offset"] == null ? void 0 : json["offset"]
1385
+ };
1386
+ }
1387
+ function GetCompanyInvoicesParamsToJSON(json) {
1388
+ return GetCompanyInvoicesParamsToJSONTyped(json, false);
1389
+ }
1390
+ function GetCompanyInvoicesParamsToJSONTyped(value, ignoreDiscriminator = false) {
1391
+ if (value == null) {
1392
+ return value;
1393
+ }
1394
+ return {
1395
+ include_pending: value["includePending"],
1396
+ limit: value["limit"],
1397
+ offset: value["offset"]
1398
+ };
1399
+ }
1400
+
1401
+ // src/billing/api/generated/models/GetCompanyInvoicesResponse.ts
1402
+ function instanceOfGetCompanyInvoicesResponse(value) {
1403
+ if (!("data" in value) || value["data"] === void 0) return false;
1404
+ if (!("params" in value) || value["params"] === void 0) return false;
1405
+ return true;
1406
+ }
1407
+ function GetCompanyInvoicesResponseFromJSON(json) {
1408
+ return GetCompanyInvoicesResponseFromJSONTyped(json, false);
1409
+ }
1410
+ function GetCompanyInvoicesResponseFromJSONTyped(json, ignoreDiscriminator) {
1411
+ if (json == null) {
1412
+ return json;
1413
+ }
1414
+ return {
1415
+ data: CompanyInvoicesResponseDataFromJSON(json["data"]),
1416
+ params: GetCompanyInvoicesParamsFromJSON(json["params"])
1417
+ };
1418
+ }
1419
+ function GetCompanyInvoicesResponseToJSON(json) {
1420
+ return GetCompanyInvoicesResponseToJSONTyped(json, false);
1421
+ }
1422
+ function GetCompanyInvoicesResponseToJSONTyped(value, ignoreDiscriminator = false) {
1423
+ if (value == null) {
1424
+ return value;
1425
+ }
1426
+ return {
1427
+ data: CompanyInvoicesResponseDataToJSON(value["data"]),
1428
+ params: GetCompanyInvoicesParamsToJSON(value["params"])
1429
+ };
1430
+ }
1431
+
1432
+ // src/billing/contract/invoices.ts
1433
+ var DEFAULT_INVOICE_QUERY = {};
1434
+ function normalizeInvoiceQuery(query) {
1435
+ const { includePending, ...rest } = query;
1436
+ return includePending === true ? { ...rest, includePending: true } : rest;
1437
+ }
1438
+
1439
+ // src/billing/session.ts
1440
+ var SchematicApiError = class extends Error {
1441
+ constructor(status, path, body) {
1442
+ super(
1443
+ typeof body === "object" && body !== null && "error" in body ? String(body.error) : `Request to ${path} failed with status ${status}`
1444
+ );
1445
+ this.status = status;
1446
+ this.path = path;
1447
+ this.body = body;
1448
+ this.name = "SchematicApiError";
1449
+ }
1450
+ status;
1451
+ path;
1452
+ body;
1453
+ };
1454
+ var DEFAULT_API_URL = "https://api.schematichq.com";
1455
+ var TOKEN_CACHE_SIZE = 10;
1456
+ function sessionKey(session) {
1457
+ return JSON.stringify([session.company, session.user ?? null]);
1458
+ }
1459
+ var SchematicSession = class {
1460
+ _apiUrl;
1461
+ _headers;
1462
+ _fetch;
1463
+ _session;
1464
+ _tokens = /* @__PURE__ */ new Map();
1465
+ _resolving = /* @__PURE__ */ new Map();
1466
+ /**
1467
+ * Bumped when the session's identity changes, and only then — the object
1468
+ * itself is replaced on every render. Anything asking "is this still the
1469
+ * session I started under?" counts generations rather than comparing.
1470
+ */
1471
+ _generation = 0;
1472
+ _listeners = /* @__PURE__ */ new Set();
1473
+ constructor(options = {}) {
1474
+ this._session = options.session;
1475
+ this._apiUrl = (options.apiUrl ?? DEFAULT_API_URL).replace(/\/$/, "");
1476
+ this._headers = options.additionalHeaders ?? {};
1477
+ this._fetch = options.fetch ?? ((input, init) => globalThis.fetch(input, init));
1478
+ }
1479
+ get key() {
1480
+ return this._session === null || this._session === void 0 ? void 0 : sessionKey(this._session);
1481
+ }
1482
+ get status() {
1483
+ if (this._session === void 0) {
1484
+ return "pending";
1485
+ }
1486
+ return this._session === null ? "ended" : "active";
1487
+ }
1488
+ /** Only a statement that changes which pair is read counts as a change. */
1489
+ set(session) {
1490
+ if (session === void 0) {
1491
+ return;
1492
+ }
1493
+ const previous = this._session;
1494
+ this._session = session;
1495
+ if (session === null) {
1496
+ if (previous === null) {
1497
+ return;
1498
+ }
1499
+ this._tokens.clear();
1500
+ this._resolving.clear();
1501
+ this._generation += 1;
1502
+ this._emit({ type: "ended" });
1503
+ return;
1504
+ }
1505
+ if (previous !== null && previous !== void 0) {
1506
+ if (previous.company === session.company && (previous.user ?? null) === (session.user ?? null)) {
1507
+ return;
1508
+ }
1509
+ this._generation += 1;
1510
+ this._emit({ type: "changed" });
1511
+ return;
1512
+ }
1513
+ this._generation += 1;
1514
+ this._emit({ type: "started" });
1515
+ }
1516
+ onChange(listener) {
1517
+ this._listeners.add(listener);
1518
+ return () => {
1519
+ this._listeners.delete(listener);
1520
+ };
1521
+ }
1522
+ _emit(event) {
1523
+ for (const listener of this._listeners) {
1524
+ listener(event);
1525
+ }
1526
+ }
1527
+ _cached(key) {
1528
+ const cached = this._tokens.get(key);
1529
+ if (cached === void 0) {
1530
+ return void 0;
1531
+ }
1532
+ if (cached.expiresAt !== void 0 && cached.expiresAt.getTime() <= Date.now()) {
1533
+ this._tokens.delete(key);
1534
+ return void 0;
1535
+ }
1536
+ this._tokens.delete(key);
1537
+ this._tokens.set(key, cached);
1538
+ return cached;
1539
+ }
1540
+ _cache(key, resolved) {
1541
+ this._tokens.delete(key);
1542
+ this._tokens.set(key, resolved);
1543
+ while (this._tokens.size > TOKEN_CACHE_SIZE) {
1544
+ const oldest = this._tokens.keys().next();
1545
+ if (oldest.done === true) {
1546
+ break;
1547
+ }
1548
+ this._tokens.delete(oldest.value);
1549
+ }
1550
+ }
1551
+ /**
1552
+ * The credential for a session, minted if this one is not held. `force`
1553
+ * retires what is held first: the caller is a request the API has just
1554
+ * rejected.
1555
+ */
1556
+ resolveToken(session, force) {
1557
+ const { token } = session;
1558
+ if (typeof token === "string") {
1559
+ return Promise.resolve(token);
1560
+ }
1561
+ const key = sessionKey(session);
1562
+ if (!force) {
1563
+ const cached = this._cached(key);
1564
+ if (cached !== void 0) {
1565
+ return Promise.resolve(cached.token);
1566
+ }
1567
+ } else {
1568
+ this._tokens.delete(key);
1569
+ }
1570
+ const inflight = this._resolving.get(key);
1571
+ if (inflight !== void 0 && inflight.sessionGeneration === this._generation && (!force || inflight.forced)) {
1572
+ return inflight.promise;
1573
+ }
1574
+ const provider = token;
1575
+ const sessionGeneration = this._generation;
1576
+ const owns = () => this._resolving.get(key)?.promise === promise;
1577
+ const promise = Promise.resolve().then(() => provider()).then((result) => {
1578
+ const { resolved, stamped } = asResolvedToken(result, session);
1579
+ if (owns() && (this._generation === sessionGeneration || stamped)) {
1580
+ this._cache(key, resolved);
1581
+ }
1582
+ return resolved.token;
1583
+ }).finally(() => {
1584
+ if (owns()) {
1585
+ this._resolving.delete(key);
1586
+ }
1587
+ });
1588
+ this._resolving.set(key, { promise, forced: force, sessionGeneration });
1589
+ return promise;
1590
+ }
1591
+ /**
1592
+ * Sends a request under the active session's credential, refreshing once
1593
+ * and retrying if the API rejects it. Rejects rather than answering when
1594
+ * the session changes while the request is in flight: the rows would
1595
+ * otherwise come back as the answer to a question asked for another pair.
1596
+ */
1597
+ async request(path, options = {}) {
1598
+ const { method = "GET", body } = options;
1599
+ const send = async (credential2) => this._fetch(`${this._apiUrl}${path}`, {
1600
+ method,
1601
+ headers: {
1602
+ "Accept": "application/json",
1603
+ ...body === void 0 ? {} : { "Content-Type": "application/json" },
1604
+ ...this._headers,
1605
+ "X-Schematic-Api-Key": credential2
1606
+ },
1607
+ ...body === void 0 ? {} : { body: JSON.stringify(body) }
1608
+ });
1609
+ const session = this._session;
1610
+ if (session === null) {
1611
+ throw new Error("There is no session to read data for.");
1612
+ }
1613
+ if (session === void 0) {
1614
+ throw new Error("A session is required to read data.");
1615
+ }
1616
+ const provider = session.token;
1617
+ const generation = this._generation;
1618
+ const stillOurs = async (response2) => {
1619
+ if (this._generation === generation) {
1620
+ return;
1621
+ }
1622
+ if (response2 !== void 0) {
1623
+ await discardBody(response2);
1624
+ }
1625
+ throw new Error("The session changed while this request was in flight.");
1626
+ };
1627
+ const credential = await this.resolveToken(session, false);
1628
+ if (this._generation !== generation) {
1629
+ throw new Error("The session changed before this request was sent.");
1630
+ }
1631
+ let response = await send(credential);
1632
+ await stillOurs(response);
1633
+ if (response.status === 401 && typeof provider === "function" && this._generation === generation) {
1634
+ const status = response.status;
1635
+ const rejected = await readBody(response);
1636
+ let retried = false;
1637
+ const current = this._session;
1638
+ if (this._generation === generation && current !== null && current !== void 0) {
1639
+ const refreshed = await this.resolveToken(current, true);
1640
+ if (this._generation === generation) {
1641
+ response = await send(refreshed);
1642
+ await stillOurs(response);
1643
+ retried = true;
1644
+ }
1645
+ }
1646
+ if (!retried) {
1647
+ throw new SchematicApiError(status, path, rejected);
1648
+ }
1649
+ }
1650
+ const parsed = await readBody(response);
1651
+ await stillOurs();
1652
+ if (!response.ok) {
1653
+ throw new SchematicApiError(response.status, path, parsed);
1654
+ }
1655
+ return parsed;
1656
+ }
1657
+ };
1658
+ function asResolvedToken(result, session) {
1659
+ if (typeof result === "string") {
1660
+ return { resolved: { token: result }, stamped: false };
1661
+ }
1662
+ const token = result?.token;
1663
+ if (typeof token !== "string" || token === "") {
1664
+ throw new Error("The access token provider did not return a token.");
1665
+ }
1666
+ if (result.company !== void 0 && result.company !== session.company || result.user !== void 0 && session.user !== void 0 && result.user !== session.user) {
1667
+ throw new Error(
1668
+ "The access token provider returned a token for a different session."
1669
+ );
1670
+ }
1671
+ const stamped = result.company !== void 0 && (session.user === void 0 || result.user !== void 0);
1672
+ return { resolved: { token, expiresAt: asDate(result.expiresAt) }, stamped };
1673
+ }
1674
+ function asDate(value) {
1675
+ if (value === void 0 || value === null) {
1676
+ return void 0;
1677
+ }
1678
+ const date = value instanceof Date ? value : new Date(value);
1679
+ return Number.isNaN(date.getTime()) ? void 0 : date;
1680
+ }
1681
+ async function discardBody(response) {
1682
+ try {
1683
+ await response.body?.cancel();
1684
+ } catch {
1685
+ }
1686
+ }
1687
+ async function readBody(response) {
1688
+ const text = await response.text();
1689
+ if (text === "") {
1690
+ return null;
1691
+ }
1692
+ try {
1693
+ return JSON.parse(text);
1694
+ } catch {
1695
+ return text;
1696
+ }
1697
+ }
1698
+
1699
+ // src/billing/client.ts
1700
+ var INVOICE_PAGE_SIZE = 12;
1701
+ var INVOICE_MAX_PAGE_SIZE = 250;
1702
+ var SchematicBillingClient = class {
1703
+ /**
1704
+ * Public so a second client over the same surface can be constructed on
1705
+ * this one rather than minting tokens of its own.
1706
+ */
1707
+ session;
1708
+ constructor(options = {}) {
1709
+ this.session = options instanceof SchematicSession ? options : new SchematicSession(options);
1710
+ }
1711
+ get sessionKey() {
1712
+ return this.session.key;
1713
+ }
1714
+ get sessionStatus() {
1715
+ return this.session.status;
1716
+ }
1717
+ setSession(session) {
1718
+ this.session.set(session);
1719
+ }
1720
+ onSessionChange(listener) {
1721
+ return this.session.onChange(listener);
1722
+ }
1723
+ fetchInvoices(params) {
1724
+ const query = new URLSearchParams({
1725
+ limit: String(Math.min(params.limit, INVOICE_MAX_PAGE_SIZE)),
1726
+ offset: String(params.offset)
1727
+ });
1728
+ if (params.includePending !== void 0) {
1729
+ query.set("include_pending", String(params.includePending));
1730
+ }
1731
+ const path = `/company/invoices?${query}`;
1732
+ return this.session.request(path).then((body) => {
1733
+ if (body === null || typeof body !== "object" || !("data" in body)) {
1734
+ throw new Error(`Malformed response from ${path}`);
1735
+ }
1736
+ const data = body.data;
1737
+ if (data == null || data.invoices == null) {
1738
+ return { invoices: [], count: 0 };
1739
+ }
1740
+ const decoded = GetCompanyInvoicesResponseFromJSON(body).data;
1741
+ return { invoices: decoded.invoices, count: decoded.count };
1742
+ });
1743
+ }
1744
+ };
1745
+ async function fetchBillingData(client, options = {}) {
1746
+ const wanted = options.names ?? ["invoices"];
1747
+ const data = {};
1748
+ const invoices = options.invoices === void 0 ? void 0 : normalizeInvoiceQuery(options.invoices);
1749
+ if (invoices !== void 0) {
1750
+ data.params = { invoices };
1751
+ }
1752
+ data.sessionKey = client.sessionKey;
1753
+ await Promise.all(
1754
+ wanted.map(async (name) => {
1755
+ try {
1756
+ switch (name) {
1757
+ case "invoices": {
1758
+ const page = await client.fetchInvoices({
1759
+ ...invoices ?? {},
1760
+ limit: INVOICE_PAGE_SIZE,
1761
+ offset: 0
1762
+ });
1763
+ data.invoices = {
1764
+ invoices: page.invoices,
1765
+ count: page.count,
1766
+ // Two queries on the server: a count that outran the rows
1767
+ // would seed a page that pages nothing.
1768
+ hasMore: page.invoices.length > 0 && page.invoices.length < page.count
1769
+ };
1770
+ break;
1771
+ }
1772
+ }
1773
+ } catch {
1774
+ }
1775
+ })
1776
+ );
1777
+ return data;
1778
+ }
867
1779
 
868
1780
  // src/index.ts
869
1781
  var anonymousIdKey = "schematicId";
1782
+ var flagStateCachePrefix = "schematicCache";
1783
+ var flagStateCacheMaxContexts = 10;
1784
+ var flagStateCacheDefaultMaxAgeMs = 7 * 24 * 60 * 60 * 1e3;
1785
+ var emptyCreditBalances = Object.freeze({});
870
1786
  var Schematic = class {
871
1787
  additionalHeaders = {};
872
1788
  apiKey;
@@ -883,11 +1799,17 @@ var Schematic = class {
883
1799
  isPending = true;
884
1800
  isPendingListeners = /* @__PURE__ */ new Set();
885
1801
  planListeners = /* @__PURE__ */ new Set();
1802
+ creditBalanceListeners = /* @__PURE__ */ new Set();
886
1803
  storage;
1804
+ persistFlagState = true;
1805
+ flagStateCacheKey;
1806
+ flagStateCacheMaxAgeMs = flagStateCacheDefaultMaxAgeMs;
1807
+ cachedFlagState = null;
887
1808
  useWebSocket = false;
888
1809
  checks = {};
889
1810
  featureUsageEventMap = {};
890
1811
  planChecks = {};
1812
+ creditBalances = {};
891
1813
  webSocketUrl = "wss://api.schematichq.com";
892
1814
  webSocketConnectionTimeout = 1e4;
893
1815
  webSocketReconnect = true;
@@ -916,11 +1838,16 @@ var Schematic = class {
916
1838
  fallbackCheckCache = {};
917
1839
  constructor(apiKey, options) {
918
1840
  this.apiKey = apiKey;
1841
+ this.flagStateCacheKey = `${flagStateCachePrefix}:${apiKey}`;
919
1842
  this.eventQueue = [];
920
1843
  this.contextDependentEventQueue = [];
921
1844
  this.useWebSocket = options?.useWebSocket ?? false;
922
1845
  this.debugEnabled = options?.debug ?? false;
923
1846
  this.offlineEnabled = options?.offline ?? false;
1847
+ this.persistFlagState = options?.persistFlagState ?? true;
1848
+ if (typeof options?.flagStateCacheMaxAgeMs === "number" && options.flagStateCacheMaxAgeMs > 0) {
1849
+ this.flagStateCacheMaxAgeMs = options.flagStateCacheMaxAgeMs;
1850
+ }
924
1851
  if (typeof window !== "undefined" && typeof window.location !== "undefined") {
925
1852
  const params = new URLSearchParams(window.location.search);
926
1853
  const debugParam = params.get("schematic_debug");
@@ -953,6 +1880,7 @@ var Schematic = class {
953
1880
  } catch {
954
1881
  }
955
1882
  }
1883
+ this.hydrateFlagStateFromCache();
956
1884
  if (options?.apiUrl !== void 0) {
957
1885
  this.apiUrl = options.apiUrl;
958
1886
  }
@@ -1246,6 +2174,29 @@ var Schematic = class {
1246
2174
  });
1247
2175
  this.notifyPlanListeners(plan);
1248
2176
  }
2177
+ if (message.credit_balances !== void 0 && message.credit_balances !== null) {
2178
+ const contextStr = contextString(context);
2179
+ const updates = CreditBalancesFromJSON(message.credit_balances);
2180
+ const previous = this.creditBalances[contextStr] ?? {};
2181
+ let changed = false;
2182
+ const merged = { ...previous };
2183
+ for (const [creditId, next] of Object.entries(updates)) {
2184
+ const current = previous[creditId];
2185
+ if (current === void 0 || current.remaining !== next.remaining || current.reserved !== next.reserved || current.settled !== next.settled) {
2186
+ merged[creditId] = next;
2187
+ changed = true;
2188
+ }
2189
+ }
2190
+ if (changed) {
2191
+ this.creditBalances[contextStr] = merged;
2192
+ this.debug(
2193
+ `WebSocket credit balance update received. Notifying listeners`,
2194
+ { creditBalances: merged }
2195
+ );
2196
+ this.notifyCreditBalanceListeners(merged);
2197
+ }
2198
+ }
2199
+ this.persistContextToCache(contextString(context));
1249
2200
  this.flushContextDependentEventQueue();
1250
2201
  this.setIsPending(false);
1251
2202
  };
@@ -1353,7 +2304,32 @@ var Schematic = class {
1353
2304
  return;
1354
2305
  }
1355
2306
  try {
1356
- this.setIsPending(true);
2307
+ const newContextStr = contextString(context);
2308
+ const cacheHit = this.hasCachedValuesForContext(newContextStr);
2309
+ if (cacheHit) {
2310
+ this.debug(
2311
+ `setContext: cache hit for context, pre-resolving from cached values`
2312
+ );
2313
+ this.context = context;
2314
+ this.flushContextDependentEventQueue();
2315
+ this.setIsPending(false);
2316
+ const cachedChecks = this.checks[newContextStr] ?? {};
2317
+ for (const [flagKey, check] of Object.entries(cachedChecks)) {
2318
+ if (check === void 0) continue;
2319
+ this.notifyFlagCheckListeners(flagKey, check);
2320
+ this.notifyFlagValueListeners(flagKey, check.value);
2321
+ }
2322
+ const cachedPlan = this.planChecks[newContextStr];
2323
+ if (cachedPlan !== void 0) {
2324
+ this.notifyPlanListeners(cachedPlan);
2325
+ }
2326
+ const cachedCreditBalances = this.creditBalances[newContextStr];
2327
+ if (cachedCreditBalances !== void 0) {
2328
+ this.notifyCreditBalanceListeners(cachedCreditBalances);
2329
+ }
2330
+ } else {
2331
+ this.setIsPending(true);
2332
+ }
1357
2333
  if (!this.conn) {
1358
2334
  if (this.isConnecting) {
1359
2335
  this.debug(
@@ -1364,7 +2340,7 @@ var Schematic = class {
1364
2340
  }
1365
2341
  if (this.conn !== null) {
1366
2342
  const socket2 = await this.conn;
1367
- await this.wsSendMessage(socket2, context);
2343
+ await this.wsSendMessage(socket2, context, cacheHit);
1368
2344
  return;
1369
2345
  }
1370
2346
  }
@@ -1380,7 +2356,7 @@ var Schematic = class {
1380
2356
  this.conn = this.wsConnect();
1381
2357
  const socket2 = await this.conn;
1382
2358
  this.isConnecting = false;
1383
- await this.wsSendMessage(socket2, context);
2359
+ await this.wsSendMessage(socket2, context, cacheHit);
1384
2360
  return;
1385
2361
  } catch (error) {
1386
2362
  this.isConnecting = false;
@@ -1388,7 +2364,7 @@ var Schematic = class {
1388
2364
  }
1389
2365
  }
1390
2366
  const socket = await this.conn;
1391
- await this.wsSendMessage(socket, context);
2367
+ await this.wsSendMessage(socket, context, cacheHit);
1392
2368
  } catch (error) {
1393
2369
  console.warn("Failed to establish WebSocket connection:", error);
1394
2370
  this.context = context;
@@ -1448,10 +2424,12 @@ var Schematic = class {
1448
2424
  `Optimistically updating feature usage for event: ${eventName}`,
1449
2425
  { quantity }
1450
2426
  );
2427
+ let dirty = false;
1451
2428
  Object.entries(flagsForEvent).forEach(([flagKey, check]) => {
1452
2429
  if (check === void 0) return;
1453
2430
  const updatedCheck = { ...check };
1454
2431
  if (typeof updatedCheck.featureUsage === "number") {
2432
+ dirty = true;
1455
2433
  updatedCheck.featureUsage += quantity;
1456
2434
  if (typeof updatedCheck.featureAllocation === "number") {
1457
2435
  const wasExceeded = updatedCheck.featureUsageExceeded === true;
@@ -1481,6 +2459,9 @@ var Schematic = class {
1481
2459
  this.notifyFlagValueListeners(flagKey, updatedCheck.value);
1482
2460
  }
1483
2461
  });
2462
+ if (dirty) {
2463
+ this.persistContextToCache(contextString(this.context));
2464
+ }
1484
2465
  };
1485
2466
  /**
1486
2467
  * Event processing
@@ -1583,6 +2564,206 @@ var Schematic = class {
1583
2564
  this.storage.setItem(anonymousIdKey, generatedAnonymousId);
1584
2565
  return generatedAnonymousId;
1585
2566
  };
2567
+ hasCachedValuesForContext = (contextStr) => {
2568
+ const cachedChecks = this.checks[contextStr];
2569
+ if (cachedChecks !== void 0 && Object.keys(cachedChecks).length > 0) {
2570
+ return true;
2571
+ }
2572
+ if (this.planChecks[contextStr] !== void 0) {
2573
+ return true;
2574
+ }
2575
+ const cachedCreditBalances = this.creditBalances[contextStr];
2576
+ return cachedCreditBalances !== void 0 && Object.keys(cachedCreditBalances).length > 0;
2577
+ };
2578
+ readFlagStateCache = () => {
2579
+ if (!this.persistFlagState || this.storage === void 0) {
2580
+ return null;
2581
+ }
2582
+ let raw;
2583
+ try {
2584
+ raw = this.storage.getItem(this.flagStateCacheKey);
2585
+ } catch (error) {
2586
+ this.debug("Failed to read flag state cache:", error);
2587
+ return null;
2588
+ }
2589
+ if (raw === null || raw === void 0) {
2590
+ return null;
2591
+ }
2592
+ let parsed;
2593
+ try {
2594
+ parsed = typeof raw === "string" ? JSON.parse(raw) : raw;
2595
+ } catch (error) {
2596
+ this.debug("Flag state cache is not valid JSON, ignoring:", error);
2597
+ return null;
2598
+ }
2599
+ if (parsed === null || typeof parsed !== "object" || parsed.version !== cacheVersion || typeof parsed.contexts !== "object" || parsed.contexts === null) {
2600
+ this.debug(
2601
+ `Flag state cache invalid or version mismatch (expected ${cacheVersion}), ignoring`,
2602
+ {
2603
+ parsedType: typeof parsed,
2604
+ version: parsed?.version
2605
+ }
2606
+ );
2607
+ return null;
2608
+ }
2609
+ return parsed;
2610
+ };
2611
+ reviveCachedCheck = (raw) => {
2612
+ if (raw === null || typeof raw !== "object") return null;
2613
+ const obj = raw;
2614
+ if (typeof obj.flag !== "string" || typeof obj.value !== "boolean" || typeof obj.reason !== "string") {
2615
+ return null;
2616
+ }
2617
+ const revived = { ...obj };
2618
+ if (typeof revived.featureUsageResetAt === "string") {
2619
+ const d = new Date(revived.featureUsageResetAt);
2620
+ revived.featureUsageResetAt = isNaN(d.getTime()) ? void 0 : d;
2621
+ }
2622
+ return revived;
2623
+ };
2624
+ reviveCachedPlan = (raw) => {
2625
+ if (raw === null || typeof raw !== "object") return null;
2626
+ const obj = raw;
2627
+ if (typeof obj.id !== "string" || typeof obj.name !== "string") {
2628
+ return null;
2629
+ }
2630
+ const revived = { ...obj };
2631
+ if (typeof revived.trialEndDate === "string") {
2632
+ const d = new Date(revived.trialEndDate);
2633
+ revived.trialEndDate = isNaN(d.getTime()) ? void 0 : d;
2634
+ }
2635
+ return revived;
2636
+ };
2637
+ reviveCachedCreditBalances = (raw) => {
2638
+ if (raw === null || typeof raw !== "object") return null;
2639
+ const revived = {};
2640
+ for (const [creditId, value] of Object.entries(
2641
+ raw
2642
+ )) {
2643
+ if (value === null || typeof value !== "object") continue;
2644
+ const obj = value;
2645
+ if (typeof obj.remaining !== "number" || typeof obj.reserved !== "number" || typeof obj.settled !== "number") {
2646
+ continue;
2647
+ }
2648
+ revived[creditId] = {
2649
+ remaining: obj.remaining,
2650
+ reserved: obj.reserved,
2651
+ settled: obj.settled
2652
+ };
2653
+ }
2654
+ return Object.keys(revived).length > 0 ? revived : null;
2655
+ };
2656
+ hydrateFlagStateFromCache = () => {
2657
+ const parsed = this.readFlagStateCache();
2658
+ if (parsed === null) return;
2659
+ const cutoff = Date.now() - this.flagStateCacheMaxAgeMs;
2660
+ const fresh = {};
2661
+ let contextsLoaded = 0;
2662
+ let contextsExpired = 0;
2663
+ for (const [contextStr, entry] of Object.entries(parsed.contexts)) {
2664
+ if (entry === null || typeof entry !== "object") continue;
2665
+ if (typeof entry.updatedAt !== "number") continue;
2666
+ if (entry.updatedAt < cutoff) {
2667
+ contextsExpired += 1;
2668
+ continue;
2669
+ }
2670
+ const revivedChecks = {};
2671
+ if (entry.checks !== null && typeof entry.checks === "object") {
2672
+ for (const [flagKey, rawCheck] of Object.entries(entry.checks)) {
2673
+ const revived = this.reviveCachedCheck(rawCheck);
2674
+ if (revived === null || revived.flag !== flagKey) continue;
2675
+ revivedChecks[flagKey] = revived;
2676
+ if (typeof revived.featureUsageEvent === "string") {
2677
+ this.updateFeatureUsageEventMap(revived);
2678
+ }
2679
+ }
2680
+ if (Object.keys(revivedChecks).length > 0) {
2681
+ this.checks[contextStr] = revivedChecks;
2682
+ }
2683
+ }
2684
+ let revivedPlan;
2685
+ if (entry.plan !== void 0 && entry.plan !== null) {
2686
+ const p = this.reviveCachedPlan(entry.plan);
2687
+ if (p !== null) {
2688
+ revivedPlan = p;
2689
+ this.planChecks[contextStr] = p;
2690
+ }
2691
+ }
2692
+ let revivedCreditBalances;
2693
+ if (entry.creditBalances !== void 0 && entry.creditBalances !== null) {
2694
+ const balances = this.reviveCachedCreditBalances(entry.creditBalances);
2695
+ if (balances !== null) {
2696
+ revivedCreditBalances = balances;
2697
+ this.creditBalances[contextStr] = balances;
2698
+ }
2699
+ }
2700
+ fresh[contextStr] = {
2701
+ checks: revivedChecks,
2702
+ plan: revivedPlan,
2703
+ creditBalances: revivedCreditBalances,
2704
+ updatedAt: entry.updatedAt
2705
+ };
2706
+ contextsLoaded += 1;
2707
+ }
2708
+ this.cachedFlagState = { version: cacheVersion, contexts: fresh };
2709
+ this.debug(
2710
+ `Hydrated flag state cache: ${contextsLoaded} loaded, ${contextsExpired} expired`
2711
+ );
2712
+ };
2713
+ persistContextToCache = (contextStr) => {
2714
+ const checksForContext = this.checks[contextStr];
2715
+ const planForContext = this.planChecks[contextStr];
2716
+ const creditBalancesForContext = this.creditBalances[contextStr];
2717
+ if (checksForContext === void 0 && planForContext === void 0 && creditBalancesForContext === void 0) {
2718
+ return;
2719
+ }
2720
+ if (this.cachedFlagState === null) {
2721
+ this.cachedFlagState = (this.persistFlagState ? this.readFlagStateCache() : null) ?? {
2722
+ version: cacheVersion,
2723
+ contexts: {}
2724
+ };
2725
+ }
2726
+ const cleanChecks = {};
2727
+ if (checksForContext !== void 0) {
2728
+ for (const [flagKey, check] of Object.entries(checksForContext)) {
2729
+ if (check !== void 0) cleanChecks[flagKey] = check;
2730
+ }
2731
+ }
2732
+ this.cachedFlagState.contexts[contextStr] = {
2733
+ checks: cleanChecks,
2734
+ plan: planForContext,
2735
+ creditBalances: creditBalancesForContext,
2736
+ updatedAt: Date.now()
2737
+ };
2738
+ const entries = Object.entries(this.cachedFlagState.contexts);
2739
+ if (entries.length > flagStateCacheMaxContexts) {
2740
+ entries.sort((a, b) => b[1].updatedAt - a[1].updatedAt);
2741
+ const survivors = entries.slice(0, flagStateCacheMaxContexts);
2742
+ const survivorKeys = new Set(survivors.map(([k]) => k));
2743
+ for (const key of Object.keys(this.checks)) {
2744
+ if (!survivorKeys.has(key)) delete this.checks[key];
2745
+ }
2746
+ for (const key of Object.keys(this.planChecks)) {
2747
+ if (!survivorKeys.has(key)) delete this.planChecks[key];
2748
+ }
2749
+ for (const key of Object.keys(this.creditBalances)) {
2750
+ if (!survivorKeys.has(key)) delete this.creditBalances[key];
2751
+ }
2752
+ this.cachedFlagState = {
2753
+ version: cacheVersion,
2754
+ contexts: Object.fromEntries(survivors)
2755
+ };
2756
+ }
2757
+ if (!this.persistFlagState || this.storage === void 0) return;
2758
+ try {
2759
+ this.storage.setItem(
2760
+ this.flagStateCacheKey,
2761
+ JSON.stringify(this.cachedFlagState)
2762
+ );
2763
+ } catch (error) {
2764
+ this.debug("Failed to write flag state cache:", error);
2765
+ }
2766
+ };
1586
2767
  handleEvent = (eventType, eventBody) => {
1587
2768
  const event = {
1588
2769
  api_key: this.apiKey,
@@ -1843,6 +3024,9 @@ var Schematic = class {
1843
3024
  if (this.conn !== null) {
1844
3025
  try {
1845
3026
  const socket = await this.conn;
3027
+ if (this.currentWebSocket === socket) {
3028
+ this.currentWebSocket = null;
3029
+ }
1846
3030
  if (socket.readyState === WebSocket.OPEN || socket.readyState === WebSocket.CONNECTING) {
1847
3031
  socket.close();
1848
3032
  }
@@ -2008,6 +3192,7 @@ var Schematic = class {
2008
3192
  this.debug(`Creating WebSocket connection ${connectionId} to ${wsUrl}`);
2009
3193
  let timeoutId = null;
2010
3194
  let isResolved = false;
3195
+ let didOpen = false;
2011
3196
  timeoutId = setTimeout(() => {
2012
3197
  if (!isResolved) {
2013
3198
  isResolved = true;
@@ -2021,6 +3206,7 @@ var Schematic = class {
2021
3206
  webSocket.onopen = () => {
2022
3207
  if (isResolved) return;
2023
3208
  isResolved = true;
3209
+ didOpen = true;
2024
3210
  if (timeoutId !== null) {
2025
3211
  clearTimeout(timeoutId);
2026
3212
  }
@@ -2046,11 +3232,12 @@ var Schematic = class {
2046
3232
  }
2047
3233
  this.debug(`WebSocket connection ${connectionId} closed`);
2048
3234
  this.conn = null;
3235
+ const wasCurrent = this.currentWebSocket === webSocket;
2049
3236
  if (this.currentWebSocket === webSocket) {
2050
3237
  this.currentWebSocket = null;
2051
3238
  this.isConnecting = false;
2052
3239
  }
2053
- if (!isResolved && !this.wsIntentionalDisconnect && this.webSocketReconnect) {
3240
+ if (wasCurrent && didOpen && !this.wsIntentionalDisconnect && this.webSocketReconnect) {
2054
3241
  this.attemptReconnect();
2055
3242
  }
2056
3243
  };
@@ -2146,6 +3333,16 @@ var Schematic = class {
2146
3333
  const plan = this.planChecks[contextStr];
2147
3334
  return plan;
2148
3335
  };
3336
+ /** Get the company's lease-aware credit balances for the current context, keyed by credit ID */
3337
+ getCreditBalances = () => {
3338
+ const contextStr = contextString(this.context);
3339
+ return this.creditBalances[contextStr] ?? emptyCreditBalances;
3340
+ };
3341
+ /** Get the company's lease-aware balance for a single credit type in the current context */
3342
+ getCreditBalance = (creditId) => {
3343
+ const contextStr = contextString(this.context);
3344
+ return this.creditBalances[contextStr]?.[creditId];
3345
+ };
2149
3346
  // flag checks state
2150
3347
  getFlagCheck = (flagKey) => {
2151
3348
  const contextStr = contextString(this.context);
@@ -2205,6 +3402,13 @@ var Schematic = class {
2205
3402
  this.planListeners.delete(listener);
2206
3403
  };
2207
3404
  };
3405
+ /** Register an event listener that will be notified with the company's credit balances (keyed by credit ID) whenever they change */
3406
+ addCreditBalanceListener = (listener) => {
3407
+ this.creditBalanceListeners.add(listener);
3408
+ return () => {
3409
+ this.creditBalanceListeners.delete(listener);
3410
+ };
3411
+ };
2208
3412
  notifyFlagCheckListeners = (flagKey, check) => {
2209
3413
  const listeners = this.flagCheckListeners?.[flagKey] ?? [];
2210
3414
  if (listeners.size > 0) {
@@ -2268,6 +3472,17 @@ var Schematic = class {
2268
3472
  });
2269
3473
  });
2270
3474
  };
3475
+ notifyCreditBalanceListeners = (value) => {
3476
+ const listeners = this.creditBalanceListeners ?? [];
3477
+ if (listeners.size > 0) {
3478
+ this.debug(`Notifying ${listeners.size} credit balance listeners`, {
3479
+ value
3480
+ });
3481
+ }
3482
+ listeners.forEach(
3483
+ (listener) => notifyCreditBalanceListener(listener, value)
3484
+ );
3485
+ };
2271
3486
  };
2272
3487
  var notifyPendingListener = (listener, value) => {
2273
3488
  if (listener.length > 0) {
@@ -2297,16 +3512,36 @@ var notifyPlanListener = (listener, value) => {
2297
3512
  listener();
2298
3513
  }
2299
3514
  };
3515
+ var notifyCreditBalanceListener = (listener, value) => {
3516
+ if (listener.length > 0) {
3517
+ listener(value);
3518
+ } else {
3519
+ listener();
3520
+ }
3521
+ };
2300
3522
  export {
2301
3523
  CheckFlagResponseFromJSON,
2302
3524
  CheckFlagReturnFromJSON,
2303
3525
  CheckFlagsResponseFromJSON,
2304
3526
  CheckPlanReturnFromJSON,
3527
+ CreditBalancesFromJSON,
3528
+ DEFAULT_INVOICE_QUERY,
2305
3529
  DatastreamCompanyPlanFromJSON,
2306
3530
  EventBodyFlagCheckToJSON,
3531
+ INVOICE_MAX_PAGE_SIZE,
3532
+ INVOICE_PAGE_SIZE,
3533
+ InvoiceStatus,
2307
3534
  RuleType,
2308
3535
  Schematic,
3536
+ SchematicApiError,
3537
+ SchematicBillingClient,
3538
+ SchematicSession,
3539
+ TOKEN_CACHE_SIZE,
2309
3540
  TrialStatus,
2310
- UsagePeriod
3541
+ UsagePeriod,
3542
+ models_exports as billingApi,
3543
+ fetchBillingData,
3544
+ normalizeInvoiceQuery,
3545
+ sessionKey
2311
3546
  };
2312
3547
  /* @preserve */