@schematichq/schematic-js 1.2.1 → 1.2.3

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.
@@ -572,6 +572,7 @@ __export(index_exports, {
572
572
  CheckFlagResponseFromJSON: () => CheckFlagResponseFromJSON,
573
573
  CheckFlagReturnFromJSON: () => CheckFlagReturnFromJSON,
574
574
  CheckFlagsResponseFromJSON: () => CheckFlagsResponseFromJSON,
575
+ EventBodyFlagCheckToJSON: () => EventBodyFlagCheckToJSON,
575
576
  RuleType: () => RuleType,
576
577
  Schematic: () => Schematic,
577
578
  UsagePeriod: () => UsagePeriod
@@ -646,6 +647,7 @@ function CheckFlagResponseDataFromJSONTyped(json, ignoreDiscriminator) {
646
647
  error: json["error"] == null ? void 0 : json["error"],
647
648
  featureAllocation: json["feature_allocation"] == null ? void 0 : json["feature_allocation"],
648
649
  featureUsage: json["feature_usage"] == null ? void 0 : json["feature_usage"],
650
+ featureUsageEvent: json["feature_usage_event"] == null ? void 0 : json["feature_usage_event"],
649
651
  featureUsagePeriod: json["feature_usage_period"] == null ? void 0 : json["feature_usage_period"],
650
652
  featureUsageResetAt: json["feature_usage_reset_at"] == null ? void 0 : new Date(json["feature_usage_reset_at"]),
651
653
  flag: json["flag"],
@@ -658,6 +660,28 @@ function CheckFlagResponseDataFromJSONTyped(json, ignoreDiscriminator) {
658
660
  };
659
661
  }
660
662
 
663
+ // src/types/api/models/EventBodyFlagCheck.ts
664
+ function EventBodyFlagCheckToJSON(json) {
665
+ return EventBodyFlagCheckToJSONTyped(json, false);
666
+ }
667
+ function EventBodyFlagCheckToJSONTyped(value, ignoreDiscriminator = false) {
668
+ if (value == null) {
669
+ return value;
670
+ }
671
+ return {
672
+ company_id: value["companyId"],
673
+ error: value["error"],
674
+ flag_id: value["flagId"],
675
+ flag_key: value["flagKey"],
676
+ reason: value["reason"],
677
+ req_company: value["reqCompany"],
678
+ req_user: value["reqUser"],
679
+ rule_id: value["ruleId"],
680
+ user_id: value["userId"],
681
+ value: value["value"]
682
+ };
683
+ }
684
+
661
685
  // src/types/api/models/CheckFlagResponse.ts
662
686
  function CheckFlagResponseFromJSON(json) {
663
687
  return CheckFlagResponseFromJSONTyped(json, false);
@@ -723,6 +747,7 @@ var CheckFlagReturnFromJSON = (json) => {
723
747
  error,
724
748
  featureAllocation,
725
749
  featureUsage,
750
+ featureUsageEvent,
726
751
  featureUsagePeriod,
727
752
  featureUsageResetAt,
728
753
  flag,
@@ -742,6 +767,7 @@ var CheckFlagReturnFromJSON = (json) => {
742
767
  error: error == null ? void 0 : error,
743
768
  featureAllocation: featureAllocation == null ? void 0 : featureAllocation,
744
769
  featureUsage: featureUsage == null ? void 0 : featureUsage,
770
+ featureUsageEvent: featureUsageEvent === null ? void 0 : featureUsageEvent,
745
771
  featureUsagePeriod: featureUsagePeriod == null ? void 0 : featureUsagePeriod,
746
772
  featureUsageResetAt: featureUsageResetAt == null ? void 0 : featureUsageResetAt,
747
773
  flag,
@@ -771,7 +797,7 @@ function contextString(context) {
771
797
  }
772
798
 
773
799
  // src/version.ts
774
- var version = "1.2.1";
800
+ var version = "1.2.3";
775
801
 
776
802
  // src/index.ts
777
803
  var anonymousIdKey = "schematicId";
@@ -781,6 +807,8 @@ var Schematic = class {
781
807
  apiUrl = "https://api.schematichq.com";
782
808
  conn = null;
783
809
  context = {};
810
+ debugEnabled = false;
811
+ offlineEnabled = false;
784
812
  eventQueue;
785
813
  eventUrl = "https://c.schematichq.com";
786
814
  flagCheckListeners = {};
@@ -790,11 +818,32 @@ var Schematic = class {
790
818
  storage;
791
819
  useWebSocket = false;
792
820
  checks = {};
821
+ featureUsageEventMap = {};
793
822
  webSocketUrl = "wss://api.schematichq.com";
794
823
  constructor(apiKey, options) {
795
824
  this.apiKey = apiKey;
796
825
  this.eventQueue = [];
797
826
  this.useWebSocket = options?.useWebSocket ?? false;
827
+ this.debugEnabled = options?.debug ?? false;
828
+ this.offlineEnabled = options?.offline ?? false;
829
+ if (typeof window !== "undefined" && typeof window.location !== "undefined") {
830
+ const params = new URLSearchParams(window.location.search);
831
+ const debugParam = params.get("schematic_debug");
832
+ if (debugParam !== null && (debugParam === "" || debugParam === "true" || debugParam === "1")) {
833
+ this.debugEnabled = true;
834
+ }
835
+ const offlineParam = params.get("schematic_offline");
836
+ if (offlineParam !== null && (offlineParam === "" || offlineParam === "true" || offlineParam === "1")) {
837
+ this.offlineEnabled = true;
838
+ this.debugEnabled = true;
839
+ }
840
+ }
841
+ if (this.offlineEnabled && options?.debug !== false) {
842
+ this.debugEnabled = true;
843
+ }
844
+ if (this.offlineEnabled) {
845
+ this.setIsPending(false);
846
+ }
798
847
  this.additionalHeaders = {
799
848
  "X-Schematic-Client-Version": `schematic-js@${version}`,
800
849
  ...options?.additionalHeaders ?? {}
@@ -818,6 +867,13 @@ var Schematic = class {
818
867
  this.flushEventQueue();
819
868
  });
820
869
  }
870
+ if (this.offlineEnabled) {
871
+ this.debug(
872
+ "Initialized with offline mode enabled - no network requests will be made"
873
+ );
874
+ } else if (this.debugEnabled) {
875
+ this.debug("Initialized with debug mode enabled");
876
+ }
821
877
  }
822
878
  /**
823
879
  * Get value for a single flag.
@@ -830,6 +886,14 @@ var Schematic = class {
830
886
  const { fallback = false, key } = options;
831
887
  const context = options.context || this.context;
832
888
  const contextStr = contextString(context);
889
+ this.debug(`checkFlag: ${key}`, { context, fallback });
890
+ if (this.isOffline()) {
891
+ this.debug(`checkFlag offline result: ${key}`, {
892
+ value: fallback,
893
+ offlineMode: true
894
+ });
895
+ return fallback;
896
+ }
833
897
  if (!this.useWebSocket) {
834
898
  const requestUrl = `${this.apiUrl}/flags/${key}/check`;
835
899
  return fetch(requestUrl, {
@@ -846,17 +910,35 @@ var Schematic = class {
846
910
  }
847
911
  return response.json();
848
912
  }).then((response) => {
849
- return CheckFlagResponseFromJSON(response).data.value;
913
+ const parsedResponse = CheckFlagResponseFromJSON(response);
914
+ this.debug(`checkFlag result: ${key}`, parsedResponse);
915
+ const result = CheckFlagReturnFromJSON(parsedResponse.data);
916
+ if (typeof result.featureUsageEvent === "string") {
917
+ this.updateFeatureUsageEventMap(result);
918
+ }
919
+ this.submitFlagCheckEvent(key, result, context);
920
+ return result.value;
850
921
  }).catch((error) => {
851
922
  console.error("There was a problem with the fetch operation:", error);
923
+ const errorResult = {
924
+ flag: key,
925
+ value: fallback,
926
+ reason: "API request failed",
927
+ error: error instanceof Error ? error.message : String(error)
928
+ };
929
+ this.submitFlagCheckEvent(key, errorResult, context);
852
930
  return fallback;
853
931
  });
854
932
  }
855
933
  try {
856
934
  const existingVals = this.checks[contextStr];
857
- if (this.conn && typeof existingVals !== "undefined" && typeof existingVals[key] !== "undefined") {
935
+ if (this.conn !== null && typeof existingVals !== "undefined" && typeof existingVals[key] !== "undefined") {
936
+ this.debug(`checkFlag cached result: ${key}`, existingVals[key]);
858
937
  return existingVals[key].value;
859
938
  }
939
+ if (this.isOffline()) {
940
+ return fallback;
941
+ }
860
942
  try {
861
943
  await this.setContext(context);
862
944
  } catch (error) {
@@ -867,16 +949,74 @@ var Schematic = class {
867
949
  return this.fallbackToRest(key, context, fallback);
868
950
  }
869
951
  const contextVals = this.checks[contextStr] ?? {};
870
- return contextVals[key]?.value ?? fallback;
952
+ const flagCheck = contextVals[key];
953
+ const result = flagCheck?.value ?? fallback;
954
+ this.debug(
955
+ `checkFlag WebSocket result: ${key}`,
956
+ typeof flagCheck !== "undefined" ? flagCheck : { value: fallback, fallbackUsed: true }
957
+ );
958
+ if (typeof flagCheck !== "undefined") {
959
+ this.submitFlagCheckEvent(key, flagCheck, context);
960
+ }
961
+ return result;
871
962
  } catch (error) {
872
963
  console.error("Unexpected error in checkFlag:", error);
964
+ const errorResult = {
965
+ flag: key,
966
+ value: fallback,
967
+ reason: "Unexpected error in flag check",
968
+ error: error instanceof Error ? error.message : String(error)
969
+ };
970
+ this.submitFlagCheckEvent(key, errorResult, context);
873
971
  return fallback;
874
972
  }
875
973
  }
974
+ /**
975
+ * Helper function to log debug messages
976
+ * Only logs if debug mode is enabled
977
+ */
978
+ debug(message, ...args) {
979
+ if (this.debugEnabled) {
980
+ console.log(`[Schematic] ${message}`, ...args);
981
+ }
982
+ }
983
+ /**
984
+ * Helper function to check if client is in offline mode
985
+ */
986
+ isOffline() {
987
+ return this.offlineEnabled;
988
+ }
989
+ /**
990
+ * Submit a flag check event
991
+ * Records data about a flag check for analytics
992
+ */
993
+ submitFlagCheckEvent(flagKey, result, context) {
994
+ const eventBody = {
995
+ flagKey,
996
+ value: result.value,
997
+ reason: result.reason,
998
+ flagId: result.flagId,
999
+ ruleId: result.ruleId,
1000
+ companyId: result.companyId,
1001
+ userId: result.userId,
1002
+ error: result.error,
1003
+ reqCompany: context.company,
1004
+ reqUser: context.user
1005
+ };
1006
+ this.debug(`submitting flag check event:`, eventBody);
1007
+ return this.handleEvent("flag_check", EventBodyFlagCheckToJSON(eventBody));
1008
+ }
876
1009
  /**
877
1010
  * Helper method for falling back to REST API when WebSocket connection fails
878
1011
  */
879
1012
  async fallbackToRest(key, context, fallback) {
1013
+ if (this.isOffline()) {
1014
+ this.debug(`fallbackToRest offline result: ${key}`, {
1015
+ value: fallback,
1016
+ offlineMode: true
1017
+ });
1018
+ return fallback;
1019
+ }
880
1020
  try {
881
1021
  const requestUrl = `${this.apiUrl}/flags/${key}/check`;
882
1022
  const response = await fetch(requestUrl, {
@@ -891,19 +1031,39 @@ var Schematic = class {
891
1031
  if (!response.ok) {
892
1032
  throw new Error("Network response was not ok");
893
1033
  }
894
- const data = CheckFlagResponseFromJSON(await response.json());
895
- return data?.data?.value ?? false;
1034
+ const responseJson = await response.json();
1035
+ const data = CheckFlagResponseFromJSON(responseJson);
1036
+ this.debug(`fallbackToRest result: ${key}`, data);
1037
+ const result = CheckFlagReturnFromJSON(data.data);
1038
+ if (typeof result.featureUsageEvent === "string") {
1039
+ this.updateFeatureUsageEventMap(result);
1040
+ }
1041
+ this.submitFlagCheckEvent(key, result, context);
1042
+ return result.value;
896
1043
  } catch (error) {
897
1044
  console.error("REST API call failed, using fallback value:", error);
1045
+ const errorResult = {
1046
+ flag: key,
1047
+ value: fallback,
1048
+ reason: "API request failed (fallback)",
1049
+ error: error instanceof Error ? error.message : String(error)
1050
+ };
1051
+ this.submitFlagCheckEvent(key, errorResult, context);
898
1052
  return fallback;
899
1053
  }
900
1054
  }
901
1055
  /**
902
1056
  * Make an API call to fetch all flag values for a given context.
903
1057
  * Recommended for use in REST mode only.
1058
+ * In offline mode, returns an empty object.
904
1059
  */
905
1060
  checkFlags = async (context) => {
906
1061
  context = context || this.context;
1062
+ this.debug(`checkFlags`, { context });
1063
+ if (this.isOffline()) {
1064
+ this.debug(`checkFlags offline result: returning empty object`);
1065
+ return {};
1066
+ }
907
1067
  const requestUrl = `${this.apiUrl}/flags/check`;
908
1068
  const requestBody = JSON.stringify(context);
909
1069
  return fetch(requestUrl, {
@@ -921,6 +1081,7 @@ var Schematic = class {
921
1081
  return response.json();
922
1082
  }).then((responseJson) => {
923
1083
  const resp = CheckFlagsResponseFromJSON(responseJson);
1084
+ this.debug(`checkFlags result:`, resp);
924
1085
  return (resp?.data?.flags ?? []).reduce(
925
1086
  (accum, flag) => {
926
1087
  accum[flag.flag] = flag.value;
@@ -939,6 +1100,7 @@ var Schematic = class {
939
1100
  * send an identify event to the Schematic API which will upsert a user and company.
940
1101
  */
941
1102
  identify = (body) => {
1103
+ this.debug(`identify:`, body);
942
1104
  try {
943
1105
  this.setContext({
944
1106
  company: body.company?.keys,
@@ -956,10 +1118,12 @@ var Schematic = class {
956
1118
  * 2. Send the context to the server
957
1119
  * 3. Wait for initial flag values to be returned
958
1120
  * The promise resolves when initial flag values are received.
1121
+ * In offline mode, this will just set the context locally without connecting.
959
1122
  */
960
1123
  setContext = async (context) => {
961
- if (!this.useWebSocket) {
1124
+ if (this.isOffline() || !this.useWebSocket) {
962
1125
  this.context = context;
1126
+ this.setIsPending(false);
963
1127
  return Promise.resolve();
964
1128
  }
965
1129
  try {
@@ -977,14 +1141,67 @@ var Schematic = class {
977
1141
  /**
978
1142
  * Send a track event
979
1143
  * Track usage for a company and/or user.
1144
+ * Optimistically updates feature usage flags if tracking a featureUsageEvent.
980
1145
  */
981
1146
  track = (body) => {
982
- const { company, user, event, traits } = body;
983
- return this.handleEvent("track", {
1147
+ const { company, user, event, traits, quantity = 1 } = body;
1148
+ const trackData = {
984
1149
  company: company ?? this.context.company,
985
1150
  event,
986
1151
  traits: traits ?? {},
987
- user: user ?? this.context.user
1152
+ user: user ?? this.context.user,
1153
+ quantity
1154
+ };
1155
+ this.debug(`track:`, trackData);
1156
+ if (event in this.featureUsageEventMap) {
1157
+ this.optimisticallyUpdateFeatureUsage(event, quantity);
1158
+ }
1159
+ return this.handleEvent("track", trackData);
1160
+ };
1161
+ /**
1162
+ * Optimistically update feature usage flags associated with a tracked event
1163
+ * This updates flags in memory with updated usage counts and value/featureUsageExceeded flags
1164
+ * before the network request completes
1165
+ */
1166
+ optimisticallyUpdateFeatureUsage = (eventName, quantity = 1) => {
1167
+ const flagsForEvent = this.featureUsageEventMap[eventName];
1168
+ if (flagsForEvent === void 0 || flagsForEvent === null) return;
1169
+ this.debug(
1170
+ `Optimistically updating feature usage for event: ${eventName}`,
1171
+ { quantity }
1172
+ );
1173
+ Object.entries(flagsForEvent).forEach(([flagKey, check]) => {
1174
+ if (check === void 0) return;
1175
+ const updatedCheck = { ...check };
1176
+ if (typeof updatedCheck.featureUsage === "number") {
1177
+ updatedCheck.featureUsage += quantity;
1178
+ if (typeof updatedCheck.featureAllocation === "number") {
1179
+ const wasExceeded = updatedCheck.featureUsageExceeded === true;
1180
+ const nowExceeded = updatedCheck.featureUsage >= updatedCheck.featureAllocation;
1181
+ if (nowExceeded !== wasExceeded) {
1182
+ updatedCheck.featureUsageExceeded = nowExceeded;
1183
+ if (nowExceeded) {
1184
+ updatedCheck.value = false;
1185
+ }
1186
+ this.debug(`Usage limit status changed for flag: ${flagKey}`, {
1187
+ was: wasExceeded ? "exceeded" : "within limits",
1188
+ now: nowExceeded ? "exceeded" : "within limits",
1189
+ featureUsage: updatedCheck.featureUsage,
1190
+ featureAllocation: updatedCheck.featureAllocation,
1191
+ value: updatedCheck.value
1192
+ });
1193
+ }
1194
+ }
1195
+ if (this.featureUsageEventMap[eventName] !== void 0) {
1196
+ this.featureUsageEventMap[eventName][flagKey] = updatedCheck;
1197
+ }
1198
+ const contextStr = contextString(this.context);
1199
+ if (this.checks[contextStr] !== void 0 && this.checks[contextStr] !== null) {
1200
+ this.checks[contextStr][flagKey] = updatedCheck;
1201
+ }
1202
+ this.notifyFlagCheckListeners(flagKey, updatedCheck);
1203
+ this.notifyFlagValueListeners(flagKey, updatedCheck.value);
1204
+ }
988
1205
  });
989
1206
  };
990
1207
  /**
@@ -1028,8 +1245,13 @@ var Schematic = class {
1028
1245
  sendEvent = async (event) => {
1029
1246
  const captureUrl = `${this.eventUrl}/e`;
1030
1247
  const payload = JSON.stringify(event);
1248
+ this.debug(`sending event:`, { url: captureUrl, event });
1249
+ if (this.isOffline()) {
1250
+ this.debug(`event not sent (offline mode):`, { event });
1251
+ return Promise.resolve();
1252
+ }
1031
1253
  try {
1032
- await fetch(captureUrl, {
1254
+ const response = await fetch(captureUrl, {
1033
1255
  method: "POST",
1034
1256
  headers: {
1035
1257
  ...this.additionalHeaders ?? {},
@@ -1037,6 +1259,10 @@ var Schematic = class {
1037
1259
  },
1038
1260
  body: payload
1039
1261
  });
1262
+ this.debug(`event sent:`, {
1263
+ status: response.status,
1264
+ statusText: response.statusText
1265
+ });
1040
1266
  } catch (error) {
1041
1267
  console.error("Error sending Schematic event: ", error);
1042
1268
  }
@@ -1051,8 +1277,13 @@ var Schematic = class {
1051
1277
  */
1052
1278
  /**
1053
1279
  * If using websocket mode, close the connection when done.
1280
+ * In offline mode, this is a no-op.
1054
1281
  */
1055
1282
  cleanup = async () => {
1283
+ if (this.isOffline()) {
1284
+ this.debug("cleanup: skipped (offline mode)");
1285
+ return Promise.resolve();
1286
+ }
1056
1287
  if (this.conn) {
1057
1288
  try {
1058
1289
  const socket = await this.conn;
@@ -1066,16 +1297,26 @@ var Schematic = class {
1066
1297
  };
1067
1298
  // Open a websocket connection
1068
1299
  wsConnect = () => {
1300
+ if (this.isOffline()) {
1301
+ this.debug("wsConnect: skipped (offline mode)");
1302
+ return Promise.reject(
1303
+ new Error("WebSocket connection skipped in offline mode")
1304
+ );
1305
+ }
1069
1306
  return new Promise((resolve, reject) => {
1070
1307
  const wsUrl = `${this.webSocketUrl}/flags/bootstrap?apiKey=${this.apiKey}`;
1308
+ this.debug(`connecting to WebSocket:`, wsUrl);
1071
1309
  const webSocket = new WebSocket(wsUrl);
1072
1310
  webSocket.onopen = () => {
1311
+ this.debug(`WebSocket connection opened`);
1073
1312
  resolve(webSocket);
1074
1313
  };
1075
1314
  webSocket.onerror = (error) => {
1315
+ this.debug(`WebSocket connection error:`, error);
1076
1316
  reject(error);
1077
1317
  };
1078
1318
  webSocket.onclose = () => {
1319
+ this.debug(`WebSocket connection closed`);
1079
1320
  this.conn = null;
1080
1321
  };
1081
1322
  });
@@ -1083,21 +1324,44 @@ var Schematic = class {
1083
1324
  // Send a message on the websocket indicating interest in a particular evaluation context
1084
1325
  // and wait for the initial set of flag values to be returned
1085
1326
  wsSendMessage = (socket, context) => {
1327
+ if (this.isOffline()) {
1328
+ this.debug("wsSendMessage: skipped (offline mode)");
1329
+ this.setIsPending(false);
1330
+ return Promise.resolve();
1331
+ }
1086
1332
  return new Promise((resolve, reject) => {
1087
1333
  if (contextString(context) == contextString(this.context)) {
1334
+ this.debug(`WebSocket context unchanged, skipping update`);
1088
1335
  return resolve(this.setIsPending(false));
1089
1336
  }
1337
+ this.debug(`WebSocket context updated:`, context);
1090
1338
  this.context = context;
1091
1339
  const sendMessage = () => {
1092
1340
  let resolved = false;
1093
1341
  const messageHandler = (event) => {
1094
1342
  const message = JSON.parse(event.data);
1343
+ this.debug(`WebSocket message received:`, message);
1095
1344
  if (!(contextString(context) in this.checks)) {
1096
1345
  this.checks[contextString(context)] = {};
1097
1346
  }
1098
1347
  (message.flags ?? []).forEach((flag) => {
1099
1348
  const flagCheck = CheckFlagReturnFromJSON(flag);
1100
- this.checks[contextString(context)][flagCheck.flag] = flagCheck;
1349
+ const contextStr = contextString(context);
1350
+ if (this.checks[contextStr] === void 0) {
1351
+ this.checks[contextStr] = {};
1352
+ }
1353
+ this.checks[contextStr][flagCheck.flag] = flagCheck;
1354
+ this.debug(`WebSocket flag update:`, {
1355
+ flag: flagCheck.flag,
1356
+ value: flagCheck.value,
1357
+ flagCheck
1358
+ });
1359
+ if (typeof flagCheck.featureUsageEvent === "string") {
1360
+ this.updateFeatureUsageEventMap(flagCheck);
1361
+ }
1362
+ if (this.flagCheckListeners[flag.flag]?.size > 0 || this.flagValueListeners[flag.flag]?.size > 0) {
1363
+ this.submitFlagCheckEvent(flagCheck.flag, flagCheck, context);
1364
+ }
1101
1365
  this.notifyFlagCheckListeners(flag.flag, flagCheck);
1102
1366
  this.notifyFlagValueListeners(flag.flag, flagCheck.value);
1103
1367
  });
@@ -1108,19 +1372,23 @@ var Schematic = class {
1108
1372
  }
1109
1373
  };
1110
1374
  socket.addEventListener("message", messageHandler);
1111
- socket.send(
1112
- JSON.stringify({
1113
- apiKey: this.apiKey,
1114
- clientVersion: `schematic-js@${version}`,
1115
- data: context
1116
- })
1117
- );
1375
+ const clientVersion = this.additionalHeaders["X-Schematic-Client-Version"] ?? `schematic-js@${version}`;
1376
+ const messagePayload = {
1377
+ apiKey: this.apiKey,
1378
+ clientVersion,
1379
+ data: context
1380
+ };
1381
+ this.debug(`WebSocket sending message:`, messagePayload);
1382
+ socket.send(JSON.stringify(messagePayload));
1118
1383
  };
1119
1384
  if (socket.readyState === WebSocket.OPEN) {
1385
+ this.debug(`WebSocket already open, sending message`);
1120
1386
  sendMessage();
1121
1387
  } else if (socket.readyState === WebSocket.CONNECTING) {
1388
+ this.debug(`WebSocket connecting, waiting for open to send message`);
1122
1389
  socket.addEventListener("open", sendMessage);
1123
1390
  } else {
1391
+ this.debug(`WebSocket is closed, cannot send message`);
1124
1392
  reject("WebSocket is not open or connecting");
1125
1393
  }
1126
1394
  });
@@ -1177,10 +1445,40 @@ var Schematic = class {
1177
1445
  };
1178
1446
  notifyFlagCheckListeners = (flagKey, check) => {
1179
1447
  const listeners = this.flagCheckListeners?.[flagKey] ?? [];
1448
+ if (listeners.size > 0) {
1449
+ this.debug(
1450
+ `Notifying ${listeners.size} flag check listeners for ${flagKey}`,
1451
+ check
1452
+ );
1453
+ }
1454
+ if (typeof check.featureUsageEvent === "string") {
1455
+ this.updateFeatureUsageEventMap(check);
1456
+ }
1180
1457
  listeners.forEach((listener) => notifyFlagCheckListener(listener, check));
1181
1458
  };
1459
+ /** Add or update a CheckFlagReturn in the featureUsageEventMap */
1460
+ updateFeatureUsageEventMap = (check) => {
1461
+ if (typeof check.featureUsageEvent !== "string") return;
1462
+ const eventName = check.featureUsageEvent;
1463
+ if (this.featureUsageEventMap[eventName] === void 0 || this.featureUsageEventMap[eventName] === null) {
1464
+ this.featureUsageEventMap[eventName] = {};
1465
+ }
1466
+ if (this.featureUsageEventMap[eventName] !== void 0) {
1467
+ this.featureUsageEventMap[eventName][check.flag] = check;
1468
+ }
1469
+ this.debug(
1470
+ `Updated featureUsageEventMap for event: ${eventName}, flag: ${check.flag}`,
1471
+ check
1472
+ );
1473
+ };
1182
1474
  notifyFlagValueListeners = (flagKey, value) => {
1183
1475
  const listeners = this.flagValueListeners?.[flagKey] ?? [];
1476
+ if (listeners.size > 0) {
1477
+ this.debug(
1478
+ `Notifying ${listeners.size} flag value listeners for ${flagKey}`,
1479
+ { value }
1480
+ );
1481
+ }
1184
1482
  listeners.forEach((listener) => notifyFlagValueListener(listener, value));
1185
1483
  };
1186
1484
  };