@schematichq/schematic-js 0.1.11 → 0.1.12

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 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;})();
1
+ "use strict";(()=>{var l,k=new Uint8Array(16);function p(){if(!l&&(l=typeof crypto<"u"&&crypto.getRandomValues&&crypto.getRandomValues.bind(crypto),!l))throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");return l(k)}var i=[];for(let r=0;r<256;++r)i.push((r+256).toString(16).slice(1));function m(r,e=0){return i[r[e+0]]+i[r[e+1]]+i[r[e+2]]+i[r[e+3]]+"-"+i[r[e+4]]+i[r[e+5]]+"-"+i[r[e+6]]+i[r[e+7]]+"-"+i[r[e+8]]+i[r[e+9]]+"-"+i[r[e+10]]+i[r[e+11]]+i[r[e+12]]+i[r[e+13]]+i[r[e+14]]+i[r[e+15]]}var x=typeof crypto<"u"&&crypto.randomUUID&&crypto.randomUUID.bind(crypto),y={randomUUID:x};function b(r,e,t){if(y.randomUUID&&!e&&!r)return y.randomUUID();r=r||{};let n=r.random||(r.rng||p)();if(n[6]=n[6]&15|64,n[8]=n[8]&63|128,e){t=t||0;for(let s=0;s<16;++s)e[t+s]=n[s];return e}return m(n)}var u=b;var g="schematicId";var h=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:n}=e,s=e.context||this.context;if(this.useWebSocket){let o=this.values[c(s)]??{};return typeof o[n]>"u"?t:o[n]}let a=`${this.apiUrl}/flags/${n}/check`;return fetch(a,{method:"POST",headers:{"X-Schematic-Api-Key":this.apiKey,"Content-Type":"application/json;charset=UTF-8"},body:JSON.stringify(s)}).then(o=>{if(!o.ok)throw new Error("Network response was not ok");return o.json()}).then(o=>o.data.value).catch(o=>(console.error("There was a problem with the fetch operation:",o),t))}checkFlags=async e=>{e=e||this.context;let t=`${this.apiUrl}/flags/check`,n=JSON.stringify(e);return fetch(t,{method:"POST",headers:{"Content-Type":"application/json;charset=UTF-8","X-Schematic-Api-Key":this.apiKey},body:n}).then(s=>{if(!s.ok)throw new Error("Network response was not ok");return s.json()}).then(s=>(s?.data?.flags??[]).reduce((a,o)=>(a[o.flag]=o.value,a),{})).catch(s=>(console.error("There was a problem with the fetch operation:",s),!1))};cleanup=async()=>{if(this.conn)try{(await this.conn).close()}catch(e){console.error("Error during cleanup:",e)}finally{this.conn=null}};identify=e=>(this.setContext({company:e.company?.keys,user:e.keys}),this.handleEvent("identify",e));setContext=async e=>this.useWebSocket?(this.conn||(this.conn=this.wsConnect()),this.conn.then(t=>this.wsSendMessage(t,e))):(this.context=e,Promise.resolve());track=e=>{let{company:t,user:n,event:s,traits:a}=e;return this.handleEvent("track",{company:t??this.context.company,event:s,traits:a??{},user:n??this.context.user})};flushEventQueue=()=>{for(;this.eventQueue.length>0;){let e=this.eventQueue.shift();e&&this.sendEvent(e)}};getAnonymousId=()=>{if(!this.storage)return u();let e=this.storage.getItem(g);if(typeof e<"u")return e;let t=u();return this.storage.setItem(g,t),t};handleEvent=(e,t)=>{let n={api_key:this.apiKey,body:t,sent_at:new Date().toISOString(),tracker_event_id:u(),tracker_user_id:this.getAnonymousId(),type:e};return document?.hidden?this.storeEvent(n):this.sendEvent(n)};sendEvent=async e=>{let t=`${this.eventUrl}/e`,n=JSON.stringify(e);try{await fetch(t,{method:"POST",headers:{"Content-Type":"application/json;charset=UTF-8"},body:n})}catch(s){console.error("Error sending Schematic event: ",s)}return Promise.resolve()};storeEvent=e=>(this.eventQueue.push(e),Promise.resolve());wsConnect=()=>new Promise((e,t)=>{let n=`${this.webSocketUrl}/flags/bootstrap`,s=new WebSocket(n);s.onopen=()=>{e(s)},s.onerror=a=>{t(a)},s.onclose=()=>{this.conn=null}});wsSendMessage=(e,t)=>new Promise((n,s)=>{if(c(t)==c(this.context)){n();return}this.context=t;let a=()=>{let o=!1,d=f=>{let S=JSON.parse(f.data);c(t)in this.values||(this.values[c(t)]={}),(S.flags??[]).forEach(v=>{this.values[c(t)][v.flag]=v.value}),this.flagListener&&this.flagListener(this.values[c(t)]),o||(o=!0,n()),e.removeEventListener("message",d)};e.addEventListener("message",d),e.send(JSON.stringify({apiKey:this.apiKey,data:t}))};e.readyState===WebSocket.OPEN?a():e.readyState===WebSocket.CONNECTING?e.addEventListener("open",a):s("WebSocket is not open or connecting")})};function c(r){let e=Object.keys(r).reduce((t,n)=>{let a=Object.keys(r[n]||{}).sort().reduce((o,d)=>(o[d]=r[n][d],o),{});return t[n]=a,t},{});return JSON.stringify(e)}window.Schematic=h;})();
2
2
  /* @preserve */
@@ -168,9 +168,16 @@ var Schematic = class {
168
168
  return false;
169
169
  });
170
170
  };
171
- cleanup = () => {
171
+ cleanup = async () => {
172
172
  if (this.conn) {
173
- this.conn.close();
173
+ try {
174
+ const socket = await this.conn;
175
+ socket.close();
176
+ } catch (error) {
177
+ console.error("Error during cleanup:", error);
178
+ } finally {
179
+ this.conn = null;
180
+ }
174
181
  }
175
182
  };
176
183
  // Send an identify event
@@ -185,16 +192,16 @@ var Schematic = class {
185
192
  // this will open a websocket connection (if not already open)
186
193
  // and submit this context. The promise will resolve when the
187
194
  // websocket sends back an initial set of flag values.
188
- setContext = (context) => {
195
+ setContext = async (context) => {
189
196
  if (!this.useWebSocket) {
190
197
  this.context = context;
191
198
  return Promise.resolve();
192
199
  }
193
- return new Promise((resolve) => {
194
- this.wsConnect().then(() => {
195
- this.wsSendMessage(context);
196
- resolve();
197
- });
200
+ if (!this.conn) {
201
+ this.conn = this.wsConnect();
202
+ }
203
+ return this.conn.then((socket) => {
204
+ return this.wsSendMessage(socket, context);
198
205
  });
199
206
  };
200
207
  // Send track event
@@ -263,36 +270,30 @@ var Schematic = class {
263
270
  return Promise.resolve();
264
271
  };
265
272
  wsConnect = () => {
266
- return new Promise((resolve) => {
267
- if (this.conn) {
268
- resolve();
269
- }
273
+ return new Promise((resolve, reject) => {
270
274
  const wsUrl = `${this.webSocketUrl}/flags/bootstrap`;
271
275
  const webSocket = new WebSocket(wsUrl);
272
- this.conn = webSocket;
273
276
  webSocket.onopen = () => {
274
- resolve();
277
+ resolve(webSocket);
278
+ };
279
+ webSocket.onerror = (error) => {
280
+ reject(error);
275
281
  };
276
282
  webSocket.onclose = () => {
277
283
  this.conn = null;
278
284
  };
279
285
  });
280
286
  };
281
- // Sends a message with a new context over the websocket connection
282
- wsSendMessage = (context) => {
287
+ wsSendMessage = (socket, context) => {
283
288
  return new Promise((resolve, reject) => {
284
289
  if (contextString(context) == contextString(this.context)) {
285
290
  resolve();
286
291
  return;
287
292
  }
288
293
  this.context = context;
289
- if (!this.conn) {
290
- reject("Not connected");
291
- return;
292
- }
293
- if (this.conn.readyState === WebSocket.OPEN) {
294
+ const sendMessage = () => {
294
295
  let resolved = false;
295
- this.conn.onmessage = (event) => {
296
+ const messageHandler = (event) => {
296
297
  const message = JSON.parse(event.data);
297
298
  if (!(contextString(context) in this.values)) {
298
299
  this.values[contextString(context)] = {};
@@ -309,22 +310,22 @@ var Schematic = class {
309
310
  resolved = true;
310
311
  resolve();
311
312
  }
313
+ socket.removeEventListener("message", messageHandler);
312
314
  };
313
- this.conn.onerror = (error) => {
314
- console.error("Schematic websocket error: ", error);
315
- };
316
- this.conn.send(
315
+ socket.addEventListener("message", messageHandler);
316
+ socket.send(
317
317
  JSON.stringify({
318
318
  apiKey: this.apiKey,
319
319
  data: context
320
320
  })
321
321
  );
322
- } else if (this.conn.readyState === WebSocket.CONNECTING) {
323
- this.conn.onopen = () => {
324
- this.wsSendMessage(context);
325
- };
322
+ };
323
+ if (socket.readyState === WebSocket.OPEN) {
324
+ sendMessage();
325
+ } else if (socket.readyState === WebSocket.CONNECTING) {
326
+ socket.addEventListener("open", sendMessage);
326
327
  } else {
327
- reject("Not connected");
328
+ reject("WebSocket is not open or connecting");
328
329
  }
329
330
  });
330
331
  };
@@ -64,7 +64,7 @@ export declare class Schematic {
64
64
  constructor(apiKey: string, options?: SchematicOptions);
65
65
  checkFlag(options: CheckOptions): Promise<boolean>;
66
66
  checkFlags: (context?: SchematicContext) => Promise<Record<string, boolean>>;
67
- cleanup: () => void;
67
+ cleanup: () => Promise<void>;
68
68
  identify: (body: EventBodyIdentify) => Promise<void>;
69
69
  setContext: (context: SchematicContext) => Promise<void>;
70
70
  track: (body: EventBodyTrack) => Promise<void>;
@@ -142,9 +142,16 @@ var Schematic = class {
142
142
  return false;
143
143
  });
144
144
  };
145
- cleanup = () => {
145
+ cleanup = async () => {
146
146
  if (this.conn) {
147
- this.conn.close();
147
+ try {
148
+ const socket = await this.conn;
149
+ socket.close();
150
+ } catch (error) {
151
+ console.error("Error during cleanup:", error);
152
+ } finally {
153
+ this.conn = null;
154
+ }
148
155
  }
149
156
  };
150
157
  // Send an identify event
@@ -159,16 +166,16 @@ var Schematic = class {
159
166
  // this will open a websocket connection (if not already open)
160
167
  // and submit this context. The promise will resolve when the
161
168
  // websocket sends back an initial set of flag values.
162
- setContext = (context) => {
169
+ setContext = async (context) => {
163
170
  if (!this.useWebSocket) {
164
171
  this.context = context;
165
172
  return Promise.resolve();
166
173
  }
167
- return new Promise((resolve) => {
168
- this.wsConnect().then(() => {
169
- this.wsSendMessage(context);
170
- resolve();
171
- });
174
+ if (!this.conn) {
175
+ this.conn = this.wsConnect();
176
+ }
177
+ return this.conn.then((socket) => {
178
+ return this.wsSendMessage(socket, context);
172
179
  });
173
180
  };
174
181
  // Send track event
@@ -237,36 +244,30 @@ var Schematic = class {
237
244
  return Promise.resolve();
238
245
  };
239
246
  wsConnect = () => {
240
- return new Promise((resolve) => {
241
- if (this.conn) {
242
- resolve();
243
- }
247
+ return new Promise((resolve, reject) => {
244
248
  const wsUrl = `${this.webSocketUrl}/flags/bootstrap`;
245
249
  const webSocket = new WebSocket(wsUrl);
246
- this.conn = webSocket;
247
250
  webSocket.onopen = () => {
248
- resolve();
251
+ resolve(webSocket);
252
+ };
253
+ webSocket.onerror = (error) => {
254
+ reject(error);
249
255
  };
250
256
  webSocket.onclose = () => {
251
257
  this.conn = null;
252
258
  };
253
259
  });
254
260
  };
255
- // Sends a message with a new context over the websocket connection
256
- wsSendMessage = (context) => {
261
+ wsSendMessage = (socket, context) => {
257
262
  return new Promise((resolve, reject) => {
258
263
  if (contextString(context) == contextString(this.context)) {
259
264
  resolve();
260
265
  return;
261
266
  }
262
267
  this.context = context;
263
- if (!this.conn) {
264
- reject("Not connected");
265
- return;
266
- }
267
- if (this.conn.readyState === WebSocket.OPEN) {
268
+ const sendMessage = () => {
268
269
  let resolved = false;
269
- this.conn.onmessage = (event) => {
270
+ const messageHandler = (event) => {
270
271
  const message = JSON.parse(event.data);
271
272
  if (!(contextString(context) in this.values)) {
272
273
  this.values[contextString(context)] = {};
@@ -283,22 +284,22 @@ var Schematic = class {
283
284
  resolved = true;
284
285
  resolve();
285
286
  }
287
+ socket.removeEventListener("message", messageHandler);
286
288
  };
287
- this.conn.onerror = (error) => {
288
- console.error("Schematic websocket error: ", error);
289
- };
290
- this.conn.send(
289
+ socket.addEventListener("message", messageHandler);
290
+ socket.send(
291
291
  JSON.stringify({
292
292
  apiKey: this.apiKey,
293
293
  data: context
294
294
  })
295
295
  );
296
- } else if (this.conn.readyState === WebSocket.CONNECTING) {
297
- this.conn.onopen = () => {
298
- this.wsSendMessage(context);
299
- };
296
+ };
297
+ if (socket.readyState === WebSocket.OPEN) {
298
+ sendMessage();
299
+ } else if (socket.readyState === WebSocket.CONNECTING) {
300
+ socket.addEventListener("open", sendMessage);
300
301
  } else {
301
- reject("Not connected");
302
+ reject("WebSocket is not open or connecting");
302
303
  }
303
304
  });
304
305
  };
package/package.json CHANGED
@@ -46,5 +46,5 @@
46
46
  "test": "jest --config jest.config.js"
47
47
  },
48
48
  "types": "dist/schematic.d.ts",
49
- "version": "0.1.11"
49
+ "version": "0.1.12"
50
50
  }