@schematichq/schematic-react 1.2.5 → 1.2.6

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.
@@ -72,20 +72,20 @@ var __toESM2 = (mod, isNodeMode, target) => (target = mod != null ? __create2(__
72
72
  var require_browser_polyfill = __commonJS({
73
73
  "node_modules/cross-fetch/dist/browser-polyfill.js"(exports) {
74
74
  (function(self2) {
75
- var irrelevant = function(exports2) {
75
+ var irrelevant = (function(exports2) {
76
76
  var g = typeof globalThis !== "undefined" && globalThis || typeof self2 !== "undefined" && self2 || // eslint-disable-next-line no-undef
77
77
  typeof global !== "undefined" && global || {};
78
78
  var support = {
79
79
  searchParams: "URLSearchParams" in g,
80
80
  iterable: "Symbol" in g && "iterator" in Symbol,
81
- blob: "FileReader" in g && "Blob" in g && function() {
81
+ blob: "FileReader" in g && "Blob" in g && (function() {
82
82
  try {
83
83
  new Blob();
84
84
  return true;
85
85
  } catch (e) {
86
86
  return false;
87
87
  }
88
- }(),
88
+ })(),
89
89
  formData: "FormData" in g,
90
90
  arrayBuffer: "ArrayBuffer" in g
91
91
  };
@@ -387,12 +387,12 @@ var require_browser_polyfill = __commonJS({
387
387
  }
388
388
  this.method = normalizeMethod(options.method || this.method || "GET");
389
389
  this.mode = options.mode || this.mode || null;
390
- this.signal = options.signal || this.signal || function() {
390
+ this.signal = options.signal || this.signal || (function() {
391
391
  if ("AbortController" in g) {
392
392
  var ctrl = new AbortController();
393
393
  return ctrl.signal;
394
394
  }
395
- }();
395
+ })();
396
396
  this.referrer = null;
397
397
  if ((this.method === "GET" || this.method === "HEAD") && body) {
398
398
  throw new TypeError("Body not allowed for GET or HEAD requests");
@@ -599,7 +599,7 @@ var require_browser_polyfill = __commonJS({
599
599
  exports2.Response = Response;
600
600
  exports2.fetch = fetch2;
601
601
  return exports2;
602
- }({});
602
+ })({});
603
603
  })(typeof self !== "undefined" ? self : exports);
604
604
  }
605
605
  });
@@ -623,10 +623,7 @@ function rng() {
623
623
  }
624
624
  var randomUUID = typeof crypto !== "undefined" && crypto.randomUUID && crypto.randomUUID.bind(crypto);
625
625
  var native_default = { randomUUID };
626
- function v4(options, buf, offset) {
627
- if (native_default.randomUUID && !buf && !options) {
628
- return native_default.randomUUID();
629
- }
626
+ function _v4(options, buf, offset) {
630
627
  options = options || {};
631
628
  const rnds = options.random ?? options.rng?.() ?? rng();
632
629
  if (rnds.length < 16) {
@@ -646,6 +643,12 @@ function v4(options, buf, offset) {
646
643
  }
647
644
  return unsafeStringify(rnds);
648
645
  }
646
+ function v4(options, buf, offset) {
647
+ if (native_default.randomUUID && !buf && !options) {
648
+ return native_default.randomUUID();
649
+ }
650
+ return _v4(options, buf, offset);
651
+ }
649
652
  var v4_default = v4;
650
653
  var import_polyfill = __toESM2(require_browser_polyfill());
651
654
  function CheckFlagResponseDataFromJSON(json) {
@@ -796,7 +799,7 @@ function contextString(context) {
796
799
  }, {});
797
800
  return JSON.stringify(sortedContext);
798
801
  }
799
- var version = "1.2.3";
802
+ var version = "1.2.6";
800
803
  var anonymousIdKey = "schematicId";
801
804
  var Schematic = class {
802
805
  additionalHeaders = {};
@@ -807,6 +810,7 @@ var Schematic = class {
807
810
  debugEnabled = false;
808
811
  offlineEnabled = false;
809
812
  eventQueue;
813
+ contextDependentEventQueue;
810
814
  eventUrl = "https://c.schematichq.com";
811
815
  flagCheckListeners = {};
812
816
  flagValueListeners = {};
@@ -820,6 +824,7 @@ var Schematic = class {
820
824
  constructor(apiKey, options) {
821
825
  this.apiKey = apiKey;
822
826
  this.eventQueue = [];
827
+ this.contextDependentEventQueue = [];
823
828
  this.useWebSocket = options?.useWebSocket ?? false;
824
829
  this.debugEnabled = options?.debug ?? false;
825
830
  this.offlineEnabled = options?.offline ?? false;
@@ -862,6 +867,7 @@ var Schematic = class {
862
867
  if (typeof window !== "undefined" && window?.addEventListener) {
863
868
  window.addEventListener("beforeunload", () => {
864
869
  this.flushEventQueue();
870
+ this.flushContextDependentEventQueue();
865
871
  });
866
872
  }
867
873
  if (this.offlineEnabled) {
@@ -1120,6 +1126,7 @@ var Schematic = class {
1120
1126
  setContext = async (context) => {
1121
1127
  if (this.isOffline() || !this.useWebSocket) {
1122
1128
  this.context = context;
1129
+ this.flushContextDependentEventQueue();
1123
1130
  this.setIsPending(false);
1124
1131
  return Promise.resolve();
1125
1132
  }
@@ -1142,6 +1149,25 @@ var Schematic = class {
1142
1149
  */
1143
1150
  track = (body) => {
1144
1151
  const { company, user, event, traits, quantity = 1 } = body;
1152
+ if (!this.hasContext(company, user)) {
1153
+ this.debug(`track: queuing event "${event}" until context is available`);
1154
+ const queuedEvent = {
1155
+ api_key: this.apiKey,
1156
+ body: {
1157
+ company,
1158
+ event,
1159
+ traits: traits ?? {},
1160
+ user,
1161
+ quantity
1162
+ },
1163
+ sent_at: (/* @__PURE__ */ new Date()).toISOString(),
1164
+ tracker_event_id: v4_default(),
1165
+ tracker_user_id: this.getAnonymousId(),
1166
+ type: "track"
1167
+ };
1168
+ this.contextDependentEventQueue.push(queuedEvent);
1169
+ return Promise.resolve();
1170
+ }
1145
1171
  const trackData = {
1146
1172
  company: company ?? this.context.company,
1147
1173
  event,
@@ -1204,6 +1230,38 @@ var Schematic = class {
1204
1230
  /**
1205
1231
  * Event processing
1206
1232
  */
1233
+ hasContext = (company, user) => {
1234
+ const hasProvidedContext = company !== void 0 && company !== null && Object.keys(company).length > 0 || user !== void 0 && user !== null && Object.keys(user).length > 0;
1235
+ const hasInstanceContext = this.context.company !== void 0 && this.context.company !== null && Object.keys(this.context.company).length > 0 || this.context.user !== void 0 && this.context.user !== null && Object.keys(this.context.user).length > 0;
1236
+ return hasProvidedContext || hasInstanceContext;
1237
+ };
1238
+ flushContextDependentEventQueue = () => {
1239
+ this.debug(
1240
+ `flushing ${this.contextDependentEventQueue.length} context-dependent events`
1241
+ );
1242
+ while (this.contextDependentEventQueue.length > 0) {
1243
+ const event = this.contextDependentEventQueue.shift();
1244
+ if (event) {
1245
+ if (event.type === "track" && typeof event.body === "object" && event.body !== null) {
1246
+ const trackBody = event.body;
1247
+ const updatedBody = {
1248
+ ...trackBody,
1249
+ company: trackBody.company ?? this.context.company,
1250
+ user: trackBody.user ?? this.context.user
1251
+ };
1252
+ const updatedEvent = {
1253
+ ...event,
1254
+ body: updatedBody,
1255
+ sent_at: (/* @__PURE__ */ new Date()).toISOString()
1256
+ // Update timestamp to actual send time
1257
+ };
1258
+ this.sendEvent(updatedEvent);
1259
+ } else {
1260
+ this.sendEvent(event);
1261
+ }
1262
+ }
1263
+ }
1264
+ };
1207
1265
  flushEventQueue = () => {
1208
1266
  while (this.eventQueue.length > 0) {
1209
1267
  const event = this.eventQueue.shift();
@@ -1233,7 +1291,7 @@ var Schematic = class {
1233
1291
  tracker_user_id: this.getAnonymousId(),
1234
1292
  type: eventType
1235
1293
  };
1236
- if (document?.hidden) {
1294
+ if (typeof document !== "undefined" && document?.hidden) {
1237
1295
  return this.storeEvent(event);
1238
1296
  } else {
1239
1297
  return this.sendEvent(event);
@@ -1362,6 +1420,7 @@ var Schematic = class {
1362
1420
  this.notifyFlagCheckListeners(flag.flag, flagCheck);
1363
1421
  this.notifyFlagValueListeners(flag.flag, flagCheck.value);
1364
1422
  });
1423
+ this.flushContextDependentEventQueue();
1365
1424
  this.setIsPending(false);
1366
1425
  if (!resolved) {
1367
1426
  resolved = true;
@@ -1505,7 +1564,7 @@ var notifyFlagValueListener = (listener, value) => {
1505
1564
  var import_react = __toESM(require("react"));
1506
1565
 
1507
1566
  // src/version.ts
1508
- var version2 = "1.2.5";
1567
+ var version2 = "1.2.6";
1509
1568
 
1510
1569
  // src/context/schematic.tsx
1511
1570
  var import_jsx_runtime = require("react/jsx-runtime");
@@ -11,6 +11,7 @@ import { Schematic } from '@schematichq/schematic-js';
11
11
  import { SchematicContext } from '@schematichq/schematic-js';
12
12
  import * as SchematicJS from '@schematichq/schematic-js';
13
13
  import { SchematicOptions } from '@schematichq/schematic-js';
14
+ import { StoragePersister } from '@schematichq/schematic-js';
14
15
  import { Traits } from '@schematichq/schematic-js';
15
16
  import { UsagePeriod } from '@schematichq/schematic-js';
16
17
 
@@ -62,6 +63,8 @@ declare type SchematicProviderPropsWithPublishableKey = BaseSchematicProviderPro
62
63
  publishableKey: string;
63
64
  };
64
65
 
66
+ export { StoragePersister }
67
+
65
68
  export { Traits }
66
69
 
67
70
  export { UsagePeriod }
@@ -27,20 +27,20 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
27
27
  var require_browser_polyfill = __commonJS({
28
28
  "node_modules/cross-fetch/dist/browser-polyfill.js"(exports) {
29
29
  (function(self2) {
30
- var irrelevant = function(exports2) {
30
+ var irrelevant = (function(exports2) {
31
31
  var g = typeof globalThis !== "undefined" && globalThis || typeof self2 !== "undefined" && self2 || // eslint-disable-next-line no-undef
32
32
  typeof global !== "undefined" && global || {};
33
33
  var support = {
34
34
  searchParams: "URLSearchParams" in g,
35
35
  iterable: "Symbol" in g && "iterator" in Symbol,
36
- blob: "FileReader" in g && "Blob" in g && function() {
36
+ blob: "FileReader" in g && "Blob" in g && (function() {
37
37
  try {
38
38
  new Blob();
39
39
  return true;
40
40
  } catch (e) {
41
41
  return false;
42
42
  }
43
- }(),
43
+ })(),
44
44
  formData: "FormData" in g,
45
45
  arrayBuffer: "ArrayBuffer" in g
46
46
  };
@@ -342,12 +342,12 @@ var require_browser_polyfill = __commonJS({
342
342
  }
343
343
  this.method = normalizeMethod(options.method || this.method || "GET");
344
344
  this.mode = options.mode || this.mode || null;
345
- this.signal = options.signal || this.signal || function() {
345
+ this.signal = options.signal || this.signal || (function() {
346
346
  if ("AbortController" in g) {
347
347
  var ctrl = new AbortController();
348
348
  return ctrl.signal;
349
349
  }
350
- }();
350
+ })();
351
351
  this.referrer = null;
352
352
  if ((this.method === "GET" || this.method === "HEAD") && body) {
353
353
  throw new TypeError("Body not allowed for GET or HEAD requests");
@@ -554,7 +554,7 @@ var require_browser_polyfill = __commonJS({
554
554
  exports2.Response = Response;
555
555
  exports2.fetch = fetch2;
556
556
  return exports2;
557
- }({});
557
+ })({});
558
558
  })(typeof self !== "undefined" ? self : exports);
559
559
  }
560
560
  });
@@ -578,10 +578,7 @@ function rng() {
578
578
  }
579
579
  var randomUUID = typeof crypto !== "undefined" && crypto.randomUUID && crypto.randomUUID.bind(crypto);
580
580
  var native_default = { randomUUID };
581
- function v4(options, buf, offset) {
582
- if (native_default.randomUUID && !buf && !options) {
583
- return native_default.randomUUID();
584
- }
581
+ function _v4(options, buf, offset) {
585
582
  options = options || {};
586
583
  const rnds = options.random ?? options.rng?.() ?? rng();
587
584
  if (rnds.length < 16) {
@@ -601,6 +598,12 @@ function v4(options, buf, offset) {
601
598
  }
602
599
  return unsafeStringify(rnds);
603
600
  }
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
+ }
604
607
  var v4_default = v4;
605
608
  var import_polyfill = __toESM(require_browser_polyfill());
606
609
  function CheckFlagResponseDataFromJSON(json) {
@@ -751,7 +754,7 @@ function contextString(context) {
751
754
  }, {});
752
755
  return JSON.stringify(sortedContext);
753
756
  }
754
- var version = "1.2.3";
757
+ var version = "1.2.6";
755
758
  var anonymousIdKey = "schematicId";
756
759
  var Schematic = class {
757
760
  additionalHeaders = {};
@@ -762,6 +765,7 @@ var Schematic = class {
762
765
  debugEnabled = false;
763
766
  offlineEnabled = false;
764
767
  eventQueue;
768
+ contextDependentEventQueue;
765
769
  eventUrl = "https://c.schematichq.com";
766
770
  flagCheckListeners = {};
767
771
  flagValueListeners = {};
@@ -775,6 +779,7 @@ var Schematic = class {
775
779
  constructor(apiKey, options) {
776
780
  this.apiKey = apiKey;
777
781
  this.eventQueue = [];
782
+ this.contextDependentEventQueue = [];
778
783
  this.useWebSocket = options?.useWebSocket ?? false;
779
784
  this.debugEnabled = options?.debug ?? false;
780
785
  this.offlineEnabled = options?.offline ?? false;
@@ -817,6 +822,7 @@ var Schematic = class {
817
822
  if (typeof window !== "undefined" && window?.addEventListener) {
818
823
  window.addEventListener("beforeunload", () => {
819
824
  this.flushEventQueue();
825
+ this.flushContextDependentEventQueue();
820
826
  });
821
827
  }
822
828
  if (this.offlineEnabled) {
@@ -1075,6 +1081,7 @@ var Schematic = class {
1075
1081
  setContext = async (context) => {
1076
1082
  if (this.isOffline() || !this.useWebSocket) {
1077
1083
  this.context = context;
1084
+ this.flushContextDependentEventQueue();
1078
1085
  this.setIsPending(false);
1079
1086
  return Promise.resolve();
1080
1087
  }
@@ -1097,6 +1104,25 @@ var Schematic = class {
1097
1104
  */
1098
1105
  track = (body) => {
1099
1106
  const { company, user, event, traits, quantity = 1 } = body;
1107
+ if (!this.hasContext(company, user)) {
1108
+ this.debug(`track: queuing event "${event}" until context is available`);
1109
+ const queuedEvent = {
1110
+ api_key: this.apiKey,
1111
+ body: {
1112
+ company,
1113
+ event,
1114
+ traits: traits ?? {},
1115
+ user,
1116
+ quantity
1117
+ },
1118
+ sent_at: (/* @__PURE__ */ new Date()).toISOString(),
1119
+ tracker_event_id: v4_default(),
1120
+ tracker_user_id: this.getAnonymousId(),
1121
+ type: "track"
1122
+ };
1123
+ this.contextDependentEventQueue.push(queuedEvent);
1124
+ return Promise.resolve();
1125
+ }
1100
1126
  const trackData = {
1101
1127
  company: company ?? this.context.company,
1102
1128
  event,
@@ -1159,6 +1185,38 @@ var Schematic = class {
1159
1185
  /**
1160
1186
  * Event processing
1161
1187
  */
1188
+ hasContext = (company, user) => {
1189
+ const hasProvidedContext = company !== void 0 && company !== null && Object.keys(company).length > 0 || user !== void 0 && user !== null && Object.keys(user).length > 0;
1190
+ const hasInstanceContext = this.context.company !== void 0 && this.context.company !== null && Object.keys(this.context.company).length > 0 || this.context.user !== void 0 && this.context.user !== null && Object.keys(this.context.user).length > 0;
1191
+ return hasProvidedContext || hasInstanceContext;
1192
+ };
1193
+ flushContextDependentEventQueue = () => {
1194
+ this.debug(
1195
+ `flushing ${this.contextDependentEventQueue.length} context-dependent events`
1196
+ );
1197
+ while (this.contextDependentEventQueue.length > 0) {
1198
+ const event = this.contextDependentEventQueue.shift();
1199
+ if (event) {
1200
+ if (event.type === "track" && typeof event.body === "object" && event.body !== null) {
1201
+ const trackBody = event.body;
1202
+ const updatedBody = {
1203
+ ...trackBody,
1204
+ company: trackBody.company ?? this.context.company,
1205
+ user: trackBody.user ?? this.context.user
1206
+ };
1207
+ const updatedEvent = {
1208
+ ...event,
1209
+ body: updatedBody,
1210
+ sent_at: (/* @__PURE__ */ new Date()).toISOString()
1211
+ // Update timestamp to actual send time
1212
+ };
1213
+ this.sendEvent(updatedEvent);
1214
+ } else {
1215
+ this.sendEvent(event);
1216
+ }
1217
+ }
1218
+ }
1219
+ };
1162
1220
  flushEventQueue = () => {
1163
1221
  while (this.eventQueue.length > 0) {
1164
1222
  const event = this.eventQueue.shift();
@@ -1188,7 +1246,7 @@ var Schematic = class {
1188
1246
  tracker_user_id: this.getAnonymousId(),
1189
1247
  type: eventType
1190
1248
  };
1191
- if (document?.hidden) {
1249
+ if (typeof document !== "undefined" && document?.hidden) {
1192
1250
  return this.storeEvent(event);
1193
1251
  } else {
1194
1252
  return this.sendEvent(event);
@@ -1317,6 +1375,7 @@ var Schematic = class {
1317
1375
  this.notifyFlagCheckListeners(flag.flag, flagCheck);
1318
1376
  this.notifyFlagValueListeners(flag.flag, flagCheck.value);
1319
1377
  });
1378
+ this.flushContextDependentEventQueue();
1320
1379
  this.setIsPending(false);
1321
1380
  if (!resolved) {
1322
1381
  resolved = true;
@@ -1460,7 +1519,7 @@ var notifyFlagValueListener = (listener, value) => {
1460
1519
  import React, { createContext, useEffect, useMemo, useRef } from "react";
1461
1520
 
1462
1521
  // src/version.ts
1463
- var version2 = "1.2.5";
1522
+ var version2 = "1.2.6";
1464
1523
 
1465
1524
  // src/context/schematic.tsx
1466
1525
  import { jsx } from "react/jsx-runtime";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@schematichq/schematic-react",
3
- "version": "1.2.5",
3
+ "version": "1.2.6",
4
4
  "main": "dist/schematic-react.cjs.js",
5
5
  "module": "dist/schematic-react.esm.js",
6
6
  "types": "dist/schematic-react.d.ts",
@@ -25,33 +25,38 @@
25
25
  "format": "prettier --write \"src/**/*.{ts,tsx}\"",
26
26
  "lint": "eslint src --ext ts,tsx --report-unused-disable-directives --fix",
27
27
  "test": "jest --config jest.config.js",
28
+ "test:reactnative": "jest --config jest.config.reactnative.js",
28
29
  "tsc": "npx tsc",
29
30
  "prepare": "husky"
30
31
  },
31
32
  "dependencies": {
32
- "@schematichq/schematic-js": "^1.2.3"
33
+ "@schematichq/schematic-js": "^1.2.6"
33
34
  },
34
35
  "devDependencies": {
35
36
  "@eslint/js": "^9.24.0",
36
37
  "@microsoft/api-extractor": "^7.52.2",
37
- "@types/jest": "^29.5.14",
38
+ "@testing-library/dom": "^10.4.1",
39
+ "@testing-library/jest-dom": "^6.8.0",
40
+ "@testing-library/react": "^16.3.0",
41
+ "@types/jest": "^30.0.0",
38
42
  "@types/react": "^19.1.1",
39
43
  "esbuild": "^0.25.2",
40
44
  "esbuild-jest": "^0.5.0",
41
45
  "eslint": "^9.24.0",
42
- "eslint-plugin-import": "^2.31.0",
46
+ "eslint-plugin-import": "^2.32.0",
43
47
  "eslint-plugin-react": "^7.37.5",
44
- "eslint-plugin-react-hooks": "^5.1.0",
48
+ "eslint-plugin-react-hooks": "^5.2.0",
45
49
  "globals": "^16.0.0",
46
50
  "husky": "^9.1.7",
47
- "jest": "^29.7.0",
48
- "jest-environment-jsdom": "^29.7.0",
49
- "jest-esbuild": "^0.3.0",
51
+ "jest": "^30.0.0",
52
+ "jest-environment-jsdom": "^30.0.0",
53
+ "jest-esbuild": "^0.4.0",
50
54
  "jest-fetch-mock": "^3.0.3",
51
- "prettier": "^3.4.2",
52
- "react": "^19.1.0",
55
+ "prettier": "^3.6.2",
56
+ "react": "^19.1.1",
57
+ "react-dom": "^19.1.1",
53
58
  "ts-jest": "^29.3.0",
54
- "typescript": "^5.7.3",
59
+ "typescript": "^5.9.2",
55
60
  "typescript-eslint": "^8.29.1"
56
61
  },
57
62
  "peerDependencies": {