@schematichq/schematic-js 0.1.9 → 0.1.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,2 +1,2 @@
1
- "use strict";(()=>{var p,f=new Uint8Array(16);function u(){if(!p&&(p=typeof crypto<"u"&&crypto.getRandomValues&&crypto.getRandomValues.bind(crypto),!p))throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");return p(f)}var s=[];for(let n=0;n<256;++n)s.push((n+256).toString(16).slice(1));function v(n,e=0){return s[n[e+0]]+s[n[e+1]]+s[n[e+2]]+s[n[e+3]]+"-"+s[n[e+4]]+s[n[e+5]]+"-"+s[n[e+6]]+s[n[e+7]]+"-"+s[n[e+8]]+s[n[e+9]]+"-"+s[n[e+10]]+s[n[e+11]]+s[n[e+12]]+s[n[e+13]]+s[n[e+14]]+s[n[e+15]]}var m=typeof crypto<"u"&&crypto.randomUUID&&crypto.randomUUID.bind(crypto),y={randomUUID:m};function k(n,e,t){if(y.randomUUID&&!e&&!n)return y.randomUUID();n=n||{};let r=n.random||(n.rng||u)();if(r[6]=r[6]&15|64,r[8]=r[8]&63|128,e){t=t||0;for(let o=0;o<16;++o)e[t+o]=r[o];return e}return v(r)}var h=k;var g="schematicId";var l=class{apiKey;apiUrl="https://api.schematichq.com";webSocketUrl="wss://api.schematichq.com";eventUrl="https://c.schematichq.com";conn=null;context={};eventQueue;storage;useWebSocket=!1;values={};flagListener;constructor(e,t){this.apiKey=e,this.eventQueue=[],this.useWebSocket=t?.useWebSocket??!1,this.flagListener=t?.flagListener,t?.storage?this.storage=t.storage:typeof localStorage<"u"&&(this.storage=localStorage),t?.apiUrl!==void 0&&(this.apiUrl=t.apiUrl),t?.eventUrl!==void 0&&(this.eventUrl=t.eventUrl),t?.webSocketUrl!==void 0&&(this.webSocketUrl=t.webSocketUrl),window?.addEventListener&&window.addEventListener("beforeunload",()=>{this.flushEventQueue()})}async checkFlag(e){let{fallback:t=!1,key:r}=e,o=e.context||this.context;if(this.useWebSocket){let i=this.values[c(o)]??{};return typeof i[r]>"u"?t:i[r]}let a=`${this.apiUrl}/flags/${r}/check`;return fetch(a,{method:"POST",headers:{"X-Schematic-Api-Key":this.apiKey,"Content-Type":"application/json;charset=UTF-8"},body:JSON.stringify(o)}).then(i=>{if(!i.ok)throw new Error("Network response was not ok");return i.json()}).then(i=>i.data.value).catch(i=>(console.error("There was a problem with the fetch operation:",i),t))}checkFlags=async e=>{e=e||this.context;let t=`${this.apiUrl}/flags/check`,r=JSON.stringify(e);return fetch(t,{method:"POST",headers:{"Content-Type":"application/json;charset=UTF-8","X-Schematic-Api-Key":this.apiKey},body:r}).then(o=>{if(!o.ok)throw new Error("Network response was not ok");return o.json()}).then(o=>(o?.data?.flags??[]).reduce((a,i)=>(a[i.flag]=i.value,a),{})).catch(o=>(console.error("There was a problem with the fetch operation:",o),!1))};cleanup=()=>{this.conn&&this.conn.close()};identify=e=>{this.handleEvent("identify",e)};setContext=e=>this.useWebSocket?new Promise(t=>{this.wsConnect().then(()=>{this.wsSendMessage(e),t()})}):(this.context=e,Promise.resolve());track=e=>{this.handleEvent("track",e)};flushEventQueue=()=>{for(;this.eventQueue.length>0;){let e=this.eventQueue.shift();e&&this.sendEvent(e)}};getAnonymousId=()=>{if(!this.storage)return h();let e=this.storage.getItem(g);if(typeof e<"u")return e;let t=h();return this.storage.setItem(g,t),t};handleEvent=(e,t)=>{let r={api_key:this.apiKey,body:t,sent_at:new Date().toISOString(),tracker_event_id:h(),tracker_user_id:this.getAnonymousId(),type:e};document?.hidden?this.storeEvent(r):this.sendEvent(r)};sendEvent=e=>{let t=`${this.eventUrl}/e`,r=JSON.stringify(e);fetch(t,{method:"POST",headers:{"Content-Type":"application/json;charset=UTF-8"},body:r}).then(o=>{if(!o.ok)throw new Error(`Network response was not ok: ${o.statusText}`)}).catch(o=>{console.error("There was a problem with the fetch operation:",o)})};storeEvent=e=>{this.eventQueue.push(e)};wsConnect=()=>new Promise(e=>{this.conn&&e();let t=`${this.webSocketUrl}/flags/bootstrap`,r=new WebSocket(t);this.conn=r,r.onopen=()=>{e()},r.onclose=()=>{this.conn=null}});wsSendMessage=e=>new Promise((t,r)=>{if(c(e)==c(this.context)&&t(),this.context=e,!this.conn){r("Not connected");return}if(this.conn.readyState===WebSocket.OPEN){let o=!1;this.conn.onmessage=a=>{let i=JSON.parse(a.data);this.values[c(e)]||={},(i.flags??[]).forEach(d=>{this.values[c(e)][d.flag]=d.value}),this.flagListener&&this.flagListener(this.values[c(e)]),o||(o=!0,t())},this.conn.onerror=a=>{console.error("Schematic websocket error: ",a)},this.conn.send(JSON.stringify({apiKey:this.apiKey,data:e}))}else this.conn.readyState===WebSocket.CONNECTING?this.conn.onopen=()=>{this.wsSendMessage(e)}:r("Not connected")})};function c(n){let e=Object.keys(n).reduce((t,r)=>{let a=Object.keys(n[r]||{}).sort().reduce((i,d)=>(i[d]=n[r][d],i),{});return t[r]=a,t},{});return JSON.stringify(e)}window.Schematic=l;})();
1
+ "use strict";(()=>{var u,g=new Uint8Array(16);function p(){if(!u&&(u=typeof crypto<"u"&&crypto.getRandomValues&&crypto.getRandomValues.bind(crypto),!u))throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");return u(g)}var i=[];for(let n=0;n<256;++n)i.push((n+256).toString(16).slice(1));function v(n,e=0){return i[n[e+0]]+i[n[e+1]]+i[n[e+2]]+i[n[e+3]]+"-"+i[n[e+4]]+i[n[e+5]]+"-"+i[n[e+6]]+i[n[e+7]]+"-"+i[n[e+8]]+i[n[e+9]]+"-"+i[n[e+10]]+i[n[e+11]]+i[n[e+12]]+i[n[e+13]]+i[n[e+14]]+i[n[e+15]]}var f=typeof crypto<"u"&&crypto.randomUUID&&crypto.randomUUID.bind(crypto),y={randomUUID:f};function S(n,e,t){if(y.randomUUID&&!e&&!n)return y.randomUUID();n=n||{};let r=n.random||(n.rng||p)();if(r[6]=r[6]&15|64,r[8]=r[8]&63|128,e){t=t||0;for(let o=0;o<16;++o)e[t+o]=r[o];return e}return v(r)}var h=S;var m="schematicId";var l=class{apiKey;apiUrl="https://api.schematichq.com";webSocketUrl="wss://api.schematichq.com";eventUrl="https://c.schematichq.com";conn=null;context={};eventQueue;storage;useWebSocket=!1;values={};flagListener;constructor(e,t){this.apiKey=e,this.eventQueue=[],this.useWebSocket=t?.useWebSocket??!1,this.flagListener=t?.flagListener,t?.storage?this.storage=t.storage:typeof localStorage<"u"&&(this.storage=localStorage),t?.apiUrl!==void 0&&(this.apiUrl=t.apiUrl),t?.eventUrl!==void 0&&(this.eventUrl=t.eventUrl),t?.webSocketUrl!==void 0&&(this.webSocketUrl=t.webSocketUrl),window?.addEventListener&&window.addEventListener("beforeunload",()=>{this.flushEventQueue()})}async checkFlag(e){let{fallback:t=!1,key:r}=e,o=e.context||this.context;if(this.useWebSocket){let s=this.values[c(o)]??{};return typeof s[r]>"u"?t:s[r]}let a=`${this.apiUrl}/flags/${r}/check`;return fetch(a,{method:"POST",headers:{"X-Schematic-Api-Key":this.apiKey,"Content-Type":"application/json;charset=UTF-8"},body:JSON.stringify(o)}).then(s=>{if(!s.ok)throw new Error("Network response was not ok");return s.json()}).then(s=>s.data.value).catch(s=>(console.error("There was a problem with the fetch operation:",s),t))}checkFlags=async e=>{e=e||this.context;let t=`${this.apiUrl}/flags/check`,r=JSON.stringify(e);return fetch(t,{method:"POST",headers:{"Content-Type":"application/json;charset=UTF-8","X-Schematic-Api-Key":this.apiKey},body:r}).then(o=>{if(!o.ok)throw new Error("Network response was not ok");return o.json()}).then(o=>(o?.data?.flags??[]).reduce((a,s)=>(a[s.flag]=s.value,a),{})).catch(o=>(console.error("There was a problem with the fetch operation:",o),!1))};cleanup=()=>{this.conn&&this.conn.close()};identify=e=>(this.setContext({company:e.company?.keys,user:e.keys}),this.handleEvent("identify",e));setContext=e=>this.useWebSocket?new Promise(t=>{this.wsConnect().then(()=>{this.wsSendMessage(e),t()})}):(this.context=e,Promise.resolve());track=e=>{let{company:t,user:r,event:o,traits:a}=e;return this.handleEvent("track",{company:t??this.context.company,event:o,traits:a??{},user:r??this.context.user})};flushEventQueue=()=>{for(;this.eventQueue.length>0;){let e=this.eventQueue.shift();e&&this.sendEvent(e)}};getAnonymousId=()=>{if(!this.storage)return h();let e=this.storage.getItem(m);if(typeof e<"u")return e;let t=h();return this.storage.setItem(m,t),t};handleEvent=(e,t)=>{let r={api_key:this.apiKey,body:t,sent_at:new Date().toISOString(),tracker_event_id:h(),tracker_user_id:this.getAnonymousId(),type:e};return document?.hidden?this.storeEvent(r):this.sendEvent(r)};sendEvent=async e=>{let t=`${this.eventUrl}/e`,r=JSON.stringify(e);try{await fetch(t,{method:"POST",headers:{"Content-Type":"application/json;charset=UTF-8"},body:r})}catch(o){console.error("Error sending Schematic event: ",o)}return Promise.resolve()};storeEvent=e=>(this.eventQueue.push(e),Promise.resolve());wsConnect=()=>new Promise(e=>{this.conn&&e();let t=`${this.webSocketUrl}/flags/bootstrap`,r=new WebSocket(t);this.conn=r,r.onopen=()=>{e()},r.onclose=()=>{this.conn=null}});wsSendMessage=e=>new Promise((t,r)=>{if(c(e)==c(this.context)){t();return}if(this.context=e,!this.conn){r("Not connected");return}if(this.conn.readyState===WebSocket.OPEN){let o=!1;this.conn.onmessage=a=>{let s=JSON.parse(a.data);c(e)in this.values||(this.values[c(e)]={}),(s.flags??[]).forEach(d=>{this.values[c(e)][d.flag]=d.value}),this.flagListener&&this.flagListener(this.values[c(e)]),o||(o=!0,t())},this.conn.onerror=a=>{console.error("Schematic websocket error: ",a)},this.conn.send(JSON.stringify({apiKey:this.apiKey,data:e}))}else this.conn.readyState===WebSocket.CONNECTING?this.conn.onopen=()=>{this.wsSendMessage(e)}:r("Not connected")})};function c(n){let e=Object.keys(n).reduce((t,r)=>{let a=Object.keys(n[r]||{}).sort().reduce((s,d)=>(s[d]=n[r][d],s),{});return t[r]=a,t},{});return JSON.stringify(e)}window.Schematic=l;})();
2
2
  /* @preserve */
@@ -175,7 +175,11 @@ var Schematic = class {
175
175
  };
176
176
  // Send an identify event
177
177
  identify = (body) => {
178
- this.handleEvent("identify", body);
178
+ this.setContext({
179
+ company: body.company?.keys,
180
+ user: body.keys
181
+ });
182
+ return this.handleEvent("identify", body);
179
183
  };
180
184
  // Set the flag evaluation context; if the context has changed,
181
185
  // this will open a websocket connection (if not already open)
@@ -195,7 +199,13 @@ var Schematic = class {
195
199
  };
196
200
  // Send track event
197
201
  track = (body) => {
198
- this.handleEvent("track", body);
202
+ const { company, user, event, traits } = body;
203
+ return this.handleEvent("track", {
204
+ company: company ?? this.context.company,
205
+ event,
206
+ traits: traits ?? {},
207
+ user: user ?? this.context.user
208
+ });
199
209
  };
200
210
  flushEventQueue = () => {
201
211
  while (this.eventQueue.length > 0) {
@@ -227,32 +237,30 @@ var Schematic = class {
227
237
  type: eventType
228
238
  };
229
239
  if (document?.hidden) {
230
- this.storeEvent(event);
240
+ return this.storeEvent(event);
231
241
  } else {
232
- this.sendEvent(event);
242
+ return this.sendEvent(event);
233
243
  }
234
244
  };
235
- sendEvent = (event) => {
245
+ sendEvent = async (event) => {
236
246
  const captureUrl = `${this.eventUrl}/e`;
237
247
  const payload = JSON.stringify(event);
238
- fetch(captureUrl, {
239
- method: "POST",
240
- headers: {
241
- "Content-Type": "application/json;charset=UTF-8"
242
- },
243
- body: payload
244
- }).then((response) => {
245
- if (!response.ok) {
246
- throw new Error(
247
- `Network response was not ok: ${response.statusText}`
248
- );
249
- }
250
- }).catch((error) => {
251
- console.error("There was a problem with the fetch operation:", error);
252
- });
248
+ try {
249
+ await fetch(captureUrl, {
250
+ method: "POST",
251
+ headers: {
252
+ "Content-Type": "application/json;charset=UTF-8"
253
+ },
254
+ body: payload
255
+ });
256
+ } catch (error) {
257
+ console.error("Error sending Schematic event: ", error);
258
+ }
259
+ return Promise.resolve();
253
260
  };
254
261
  storeEvent = (event) => {
255
262
  this.eventQueue.push(event);
263
+ return Promise.resolve();
256
264
  };
257
265
  wsConnect = () => {
258
266
  return new Promise((resolve) => {
@@ -275,6 +283,7 @@ var Schematic = class {
275
283
  return new Promise((resolve, reject) => {
276
284
  if (contextString(context) == contextString(this.context)) {
277
285
  resolve();
286
+ return;
278
287
  }
279
288
  this.context = context;
280
289
  if (!this.conn) {
@@ -285,10 +294,14 @@ var Schematic = class {
285
294
  let resolved = false;
286
295
  this.conn.onmessage = (event) => {
287
296
  const message = JSON.parse(event.data);
288
- this.values[contextString(context)] ||= {};
289
- (message.flags ?? []).forEach((flag) => {
290
- this.values[contextString(context)][flag.flag] = flag.value;
291
- });
297
+ if (!(contextString(context) in this.values)) {
298
+ this.values[contextString(context)] = {};
299
+ }
300
+ (message.flags ?? []).forEach(
301
+ (flag) => {
302
+ this.values[contextString(context)][flag.flag] = flag.value;
303
+ }
304
+ );
292
305
  if (this.flagListener) {
293
306
  this.flagListener(this.values[contextString(context)]);
294
307
  }
@@ -16,22 +16,20 @@ export { Event_2 as Event }
16
16
 
17
17
  export declare type EventBody = EventBodyIdentify | EventBodyTrack;
18
18
 
19
- export declare type EventBodyCompany = {
20
- keys: Keys;
21
- name?: string;
22
- traits: Traits;
23
- };
24
-
25
19
  export declare type EventBodyIdentify = {
26
- company?: EventBodyCompany;
27
- keys: Keys;
20
+ company?: {
21
+ keys?: Keys;
22
+ name?: string;
23
+ traits?: Traits;
24
+ };
25
+ keys?: Keys;
28
26
  name?: string;
29
- traits: Traits;
27
+ traits?: Traits;
30
28
  };
31
29
 
32
30
  export declare type EventBodyTrack = SchematicContext & {
33
31
  event: string;
34
- traits: Traits;
32
+ traits?: Traits;
35
33
  };
36
34
 
37
35
  export declare type EventType = "identify" | "track";
@@ -67,9 +65,9 @@ export declare class Schematic {
67
65
  checkFlag(options: CheckOptions): Promise<boolean>;
68
66
  checkFlags: (context?: SchematicContext) => Promise<Record<string, boolean>>;
69
67
  cleanup: () => void;
70
- identify: (body: EventBodyIdentify) => void;
68
+ identify: (body: EventBodyIdentify) => Promise<void>;
71
69
  setContext: (context: SchematicContext) => Promise<void>;
72
- track: (body: EventBodyTrack) => void;
70
+ track: (body: EventBodyTrack) => Promise<void>;
73
71
  private flushEventQueue;
74
72
  private getAnonymousId;
75
73
  private handleEvent;
@@ -149,7 +149,11 @@ var Schematic = class {
149
149
  };
150
150
  // Send an identify event
151
151
  identify = (body) => {
152
- this.handleEvent("identify", body);
152
+ this.setContext({
153
+ company: body.company?.keys,
154
+ user: body.keys
155
+ });
156
+ return this.handleEvent("identify", body);
153
157
  };
154
158
  // Set the flag evaluation context; if the context has changed,
155
159
  // this will open a websocket connection (if not already open)
@@ -169,7 +173,13 @@ var Schematic = class {
169
173
  };
170
174
  // Send track event
171
175
  track = (body) => {
172
- this.handleEvent("track", body);
176
+ const { company, user, event, traits } = body;
177
+ return this.handleEvent("track", {
178
+ company: company ?? this.context.company,
179
+ event,
180
+ traits: traits ?? {},
181
+ user: user ?? this.context.user
182
+ });
173
183
  };
174
184
  flushEventQueue = () => {
175
185
  while (this.eventQueue.length > 0) {
@@ -201,32 +211,30 @@ var Schematic = class {
201
211
  type: eventType
202
212
  };
203
213
  if (document?.hidden) {
204
- this.storeEvent(event);
214
+ return this.storeEvent(event);
205
215
  } else {
206
- this.sendEvent(event);
216
+ return this.sendEvent(event);
207
217
  }
208
218
  };
209
- sendEvent = (event) => {
219
+ sendEvent = async (event) => {
210
220
  const captureUrl = `${this.eventUrl}/e`;
211
221
  const payload = JSON.stringify(event);
212
- fetch(captureUrl, {
213
- method: "POST",
214
- headers: {
215
- "Content-Type": "application/json;charset=UTF-8"
216
- },
217
- body: payload
218
- }).then((response) => {
219
- if (!response.ok) {
220
- throw new Error(
221
- `Network response was not ok: ${response.statusText}`
222
- );
223
- }
224
- }).catch((error) => {
225
- console.error("There was a problem with the fetch operation:", error);
226
- });
222
+ try {
223
+ await fetch(captureUrl, {
224
+ method: "POST",
225
+ headers: {
226
+ "Content-Type": "application/json;charset=UTF-8"
227
+ },
228
+ body: payload
229
+ });
230
+ } catch (error) {
231
+ console.error("Error sending Schematic event: ", error);
232
+ }
233
+ return Promise.resolve();
227
234
  };
228
235
  storeEvent = (event) => {
229
236
  this.eventQueue.push(event);
237
+ return Promise.resolve();
230
238
  };
231
239
  wsConnect = () => {
232
240
  return new Promise((resolve) => {
@@ -249,6 +257,7 @@ var Schematic = class {
249
257
  return new Promise((resolve, reject) => {
250
258
  if (contextString(context) == contextString(this.context)) {
251
259
  resolve();
260
+ return;
252
261
  }
253
262
  this.context = context;
254
263
  if (!this.conn) {
@@ -259,10 +268,14 @@ var Schematic = class {
259
268
  let resolved = false;
260
269
  this.conn.onmessage = (event) => {
261
270
  const message = JSON.parse(event.data);
262
- this.values[contextString(context)] ||= {};
263
- (message.flags ?? []).forEach((flag) => {
264
- this.values[contextString(context)][flag.flag] = flag.value;
265
- });
271
+ if (!(contextString(context) in this.values)) {
272
+ this.values[contextString(context)] = {};
273
+ }
274
+ (message.flags ?? []).forEach(
275
+ (flag) => {
276
+ this.values[contextString(context)][flag.flag] = flag.value;
277
+ }
278
+ );
266
279
  if (this.flagListener) {
267
280
  this.flagListener(this.values[contextString(context)]);
268
281
  }
package/package.json CHANGED
@@ -19,6 +19,7 @@
19
19
  "jest-environment-jsdom": "^29.7.0",
20
20
  "jest-esbuild": "^0.3.0",
21
21
  "jest-fetch-mock": "^3.0.3",
22
+ "prettier": "^3.3.1",
22
23
  "ts-jest": "^29.1.1",
23
24
  "typescript": "^5.0.2"
24
25
  },
@@ -40,9 +41,10 @@
40
41
  "build:esm": "npx esbuild src/index.ts --bundle --format=esm --outfile=dist/schematic.esm.js",
41
42
  "build:types": "npx tsc && npx api-extractor run",
42
43
  "clean": "rm -rf dist",
44
+ "format": "prettier --write src/*.ts",
43
45
  "lint": "eslint src --ext ts,tsx --report-unused-disable-directives --fix",
44
46
  "test": "jest --config jest.config.js"
45
47
  },
46
48
  "types": "dist/schematic.d.ts",
47
- "version": "0.1.9"
49
+ "version": "0.1.11"
48
50
  }