@knocklabs/client 0.15.1 → 0.16.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.
Files changed (34) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/cjs/api.js +1 -1
  3. package/dist/cjs/api.js.map +1 -1
  4. package/dist/cjs/clients/guide/client.js +1 -1
  5. package/dist/cjs/clients/guide/client.js.map +1 -1
  6. package/dist/cjs/clients/guide/helpers.js +2 -0
  7. package/dist/cjs/clients/guide/helpers.js.map +1 -0
  8. package/dist/cjs/clients/users/index.js.map +1 -1
  9. package/dist/esm/api.mjs +5 -1
  10. package/dist/esm/api.mjs.map +1 -1
  11. package/dist/esm/clients/guide/client.mjs +273 -117
  12. package/dist/esm/clients/guide/client.mjs.map +1 -1
  13. package/dist/esm/clients/guide/helpers.mjs +43 -0
  14. package/dist/esm/clients/guide/helpers.mjs.map +1 -0
  15. package/dist/esm/clients/users/index.mjs.map +1 -1
  16. package/dist/types/api.d.ts +1 -0
  17. package/dist/types/api.d.ts.map +1 -1
  18. package/dist/types/clients/guide/client.d.ts +17 -86
  19. package/dist/types/clients/guide/client.d.ts.map +1 -1
  20. package/dist/types/clients/guide/helpers.d.ts +16 -0
  21. package/dist/types/clients/guide/helpers.d.ts.map +1 -0
  22. package/dist/types/clients/guide/index.d.ts +1 -1
  23. package/dist/types/clients/guide/index.d.ts.map +1 -1
  24. package/dist/types/clients/guide/types.d.ts +147 -0
  25. package/dist/types/clients/guide/types.d.ts.map +1 -0
  26. package/dist/types/clients/users/index.d.ts +1 -1
  27. package/dist/types/clients/users/index.d.ts.map +1 -1
  28. package/package.json +2 -2
  29. package/src/api.ts +10 -0
  30. package/src/clients/guide/client.ts +466 -225
  31. package/src/clients/guide/helpers.ts +98 -0
  32. package/src/clients/guide/index.ts +1 -1
  33. package/src/clients/guide/types.ts +206 -0
  34. package/src/clients/users/index.ts +2 -4
@@ -1,64 +1,119 @@
1
- var l = Object.defineProperty;
2
- var k = (o, e, t) => e in o ? l(o, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : o[e] = t;
3
- var c = (o, e, t) => k(o, typeof e != "symbol" ? e + "" : e, t);
4
- import { Store as m } from "@tanstack/store";
5
- import { URLPattern as p } from "urlpattern-polyfill";
6
- const u = (o) => [...o].sort(
7
- (e, t) => t.priority - e.priority || new Date(t.inserted_at).getTime() - new Date(e.inserted_at).getTime()
8
- ), g = (o) => `/v1/users/${o}/guides`;
9
- class v {
10
- constructor(e, t, s = {}, n = {}) {
11
- c(this, "store");
1
+ var f = Object.defineProperty;
2
+ var S = (u, e, t) => e in u ? f(u, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : u[e] = t;
3
+ var d = (u, e, t) => S(u, typeof e != "symbol" ? e + "" : e, t);
4
+ import { Store as y } from "@tanstack/store";
5
+ import { URLPattern as v } from "urlpattern-polyfill";
6
+ import { byKey as G, mockDefaultGroup as _, formatFilters as l, findDefaultGroup as k, DEFAULT_GROUP_KEY as g, checkIfThrottled as w, SelectionResult as C } from "./helpers.mjs";
7
+ const b = 50, I = 30 * 1e3, A = (u) => `/v1/users/${u}/guides`, p = (u, e = {}) => {
8
+ const t = new C(), s = k(u.guideGroups);
9
+ if (!s) return t;
10
+ const r = s.display_sequence, i = u.location;
11
+ for (const [n, o] of r.entries()) {
12
+ const a = u.guides[o];
13
+ !a || !L(a, { location: i, filters: e }) || t.set(n, a);
14
+ }
15
+ return t.metadata = { guideGroup: s }, t;
16
+ }, L = (u, { location: e, filters: t = {} }) => {
17
+ if (t.type && t.type !== u.type || t.key && t.key !== u.key || u.steps.every((r) => !!r.message.archived_at))
18
+ return !1;
19
+ const s = u.activation_location_rules || [];
20
+ return !(s.length > 0 && e && !s.reduce((i, n) => {
21
+ if (i === !1) return !1;
22
+ switch (n.directive) {
23
+ case "allow":
24
+ return i === !0 || n.pattern.test(e) ? !0 : void 0;
25
+ case "block":
26
+ return n.pattern.test(e) ? !1 : i;
27
+ }
28
+ }, void 0));
29
+ };
30
+ class F {
31
+ constructor(e, t, s = {}, r = {}) {
32
+ d(this, "store");
12
33
  // Phoenix channels for real time guide updates over websocket
13
- c(this, "socket");
14
- c(this, "socketChannel");
15
- c(this, "socketChannelTopic");
16
- c(this, "socketEventTypes", ["guide.added", "guide.updated", "guide.removed"]);
34
+ d(this, "socket");
35
+ d(this, "socketChannel");
36
+ d(this, "socketChannelTopic");
37
+ d(this, "socketEventTypes", [
38
+ "guide.added",
39
+ "guide.updated",
40
+ "guide.removed",
41
+ "guide_group.added",
42
+ "guide_group.updated"
43
+ ]);
17
44
  // Original history methods to monkey patch, or restore in cleanups.
18
- c(this, "pushStateFn");
19
- c(this, "replaceStateFn");
45
+ d(this, "pushStateFn");
46
+ d(this, "replaceStateFn");
47
+ // Guides that are competing to render are "staged" first without rendering
48
+ // and ranked based on its relative order in the group over a duration of time
49
+ // to resolve and render the prevailing one.
50
+ d(this, "stage");
51
+ d(this, "counterIntervalId");
20
52
  // Define as an arrow func property to always bind this to the class instance.
21
- c(this, "handleLocationChange", () => {
53
+ d(this, "handleLocationChange", () => {
22
54
  const e = window.location.href;
23
- this.store.state.location !== e && (this.knock.log(`[Guide] Handle Location change: ${e}`), this.store.setState((t) => ({ ...t, location: e })));
55
+ this.store.state.location !== e && (this.knock.log(`[Guide] Handle Location change: ${e}`), this.setLocation(e));
24
56
  });
25
- this.knock = e, this.channelId = t, this.targetParams = s, this.options = n;
26
- const { trackLocationFromWindow: i = !0 } = n, r = i ? window == null ? void 0 : window.location.href : void 0;
27
- this.store = new m({
28
- guides: [],
57
+ this.knock = e, this.channelId = t, this.targetParams = s, this.options = r;
58
+ const { trackLocationFromWindow: i = !0 } = r, n = i ? window == null ? void 0 : window.location.href : void 0;
59
+ this.store = new y({
60
+ guideGroups: [],
61
+ guideGroupDisplayLogs: {},
62
+ guides: {},
29
63
  queries: {},
30
- location: r
64
+ location: n,
65
+ // Increment to update the state store and trigger re-selection.
66
+ counter: 0
31
67
  });
32
- const { socket: a } = this.knock.client();
33
- this.socket = a, this.socketChannelTopic = `guides:${t}`, i && this.listenForLocationChangesFromWindow(), this.knock.log("[Guide] Initialized a guide client");
68
+ const { socket: o } = this.knock.client();
69
+ this.socket = o, this.socketChannelTopic = `guides:${t}`, i && this.listenForLocationChangesFromWindow(), this.startCounterInterval(), this.knock.log("[Guide] Initialized a guide client");
70
+ }
71
+ incrementCounter() {
72
+ this.knock.log("[Guide] Incrementing the counter"), this.store.setState((e) => ({ ...e, counter: e.counter + 1 }));
73
+ }
74
+ startCounterInterval() {
75
+ const {
76
+ throttleCheckInterval: e = I
77
+ } = this.options;
78
+ this.counterIntervalId = setInterval(() => {
79
+ this.knock.log("[Guide] Counter interval tick"), !(this.stage && this.stage.status !== "closed") && this.incrementCounter();
80
+ }, e);
81
+ }
82
+ clearCounterInterval() {
83
+ this.counterIntervalId && (clearInterval(this.counterIntervalId), this.counterIntervalId = void 0);
34
84
  }
35
85
  cleanup() {
36
- this.unsubscribe(), this.removeEventListeners();
86
+ this.unsubscribe(), this.removeEventListeners(), this.clearGroupStage(), this.clearCounterInterval();
37
87
  }
38
88
  async fetch(e) {
39
89
  this.knock.failIfNotAuthenticated(), this.knock.log("[Guide] Loading all eligible guides");
40
- const t = this.buildQueryParams(e == null ? void 0 : e.filters), s = this.formatQueryKey(t), n = this.store.state.queries[s];
41
- if (n)
42
- return n;
43
- this.store.setState((r) => ({
44
- ...r,
45
- queries: { ...r.queries, [s]: { status: "loading" } }
90
+ const t = this.buildQueryParams(e == null ? void 0 : e.filters), s = this.formatQueryKey(t), r = this.store.state.queries[s];
91
+ if (r)
92
+ return r;
93
+ this.store.setState((n) => ({
94
+ ...n,
95
+ queries: { ...n.queries, [s]: { status: "loading" } }
46
96
  }));
47
97
  let i;
48
98
  try {
49
- const r = await this.knock.user.getGuides(this.channelId, t);
50
- i = { status: "ok" }, this.store.setState((a) => ({
51
- ...a,
52
- // For now assume a single fetch to get all eligible guides. When/if
53
- // we implement incremental loads, then this will need to be a merge
54
- // and sort operation.
55
- guides: r.entries.map((d) => this.localCopy(d)),
56
- queries: { ...a.queries, [s]: i }
99
+ const n = await this.knock.user.getGuides(this.channelId, t);
100
+ i = { status: "ok" };
101
+ const {
102
+ entries: o,
103
+ guide_groups: a,
104
+ guide_group_display_logs: h
105
+ } = n;
106
+ this.store.setState((c) => ({
107
+ ...c,
108
+ guideGroups: (a == null ? void 0 : a.length) > 0 ? a : [_(o)],
109
+ guideGroupDisplayLogs: h,
110
+ guides: G(o.map((m) => this.localCopy(m))),
111
+ queries: { ...c.queries, [s]: i }
57
112
  }));
58
- } catch (r) {
59
- i = { status: "error", error: r }, this.store.setState((a) => ({
60
- ...a,
61
- queries: { ...a.queries, [s]: i }
113
+ } catch (n) {
114
+ i = { status: "error", error: n }, this.store.setState((o) => ({
115
+ ...o,
116
+ queries: { ...o.queries, [s]: i }
62
117
  }));
63
118
  }
64
119
  return i;
@@ -68,7 +123,7 @@ class v {
68
123
  this.knock.failIfNotAuthenticated(), this.knock.log("[Guide] Subscribing to real time updates"), this.socket.isConnected() || this.socket.connect(), this.socketChannel && this.unsubscribe();
69
124
  const e = { ...this.targetParams, user_id: this.knock.userId }, t = this.socket.channel(this.socketChannelTopic, e);
70
125
  for (const s of this.socketEventTypes)
71
- t.on(s, (n) => this.handleSocketEvent(n));
126
+ t.on(s, (r) => this.handleSocketEvent(r));
72
127
  ["closed", "errored"].includes(t.state) && t.join(), this.socketChannel = t;
73
128
  }
74
129
  unsubscribe() {
@@ -83,36 +138,113 @@ class v {
83
138
  const { event: t, data: s } = e;
84
139
  switch (t) {
85
140
  case "guide.added":
86
- return this.addGuide(e);
141
+ return this.addOrReplaceGuide(e);
87
142
  case "guide.updated":
88
- return s.eligible ? this.replaceOrAddGuide(e) : this.removeGuide(e);
143
+ return s.eligible ? this.addOrReplaceGuide(e) : this.removeGuide(e);
89
144
  case "guide.removed":
90
145
  return this.removeGuide(e);
146
+ case "guide_group.added":
147
+ case "guide_group.updated":
148
+ return this.addOrReplaceGuideGroup(e);
91
149
  default:
92
150
  return;
93
151
  }
94
152
  }
153
+ setLocation(e) {
154
+ this.clearGroupStage(), this.store.setState((t) => ({ ...t, location: e }));
155
+ }
95
156
  //
96
157
  // Store selector
97
158
  //
98
- select(e, t = {}) {
99
- return e.guides.filter((s) => {
100
- if (t.type && t.type !== s.type || t.key && t.key !== s.key)
101
- return !1;
102
- const n = s.activation_location_rules || [];
103
- return !(n.length > 0 && e.location && !n.reduce(
104
- (r, a) => {
105
- if (r === !1) return !1;
106
- switch (a.directive) {
107
- case "allow":
108
- return r === !0 || a.pattern.test(e.location) ? !0 : void 0;
109
- case "block":
110
- return a.pattern.test(e.location) ? !1 : r;
111
- }
112
- },
113
- void 0
114
- ));
115
- });
159
+ selectGuides(e, t = {}) {
160
+ if (Object.keys(e.guides).length === 0)
161
+ return [];
162
+ this.knock.log(`[Guide] Selecting guides for: ${l(t)}`);
163
+ const s = p(e, t);
164
+ return s.size === 0 ? (this.knock.log("[Guide] Selection returned zero result"), []) : [...s.values()];
165
+ }
166
+ selectGuide(e, t = {}) {
167
+ if (Object.keys(e.guides).length === 0)
168
+ return;
169
+ this.knock.log(`[Guide] Selecting a guide for: ${l(t)}`);
170
+ const s = p(e, t);
171
+ if (s.size === 0) {
172
+ this.knock.log("[Guide] Selection returned zero result");
173
+ return;
174
+ }
175
+ const [r, i] = [...s][0];
176
+ if (i.bypass_global_group_limit)
177
+ return i;
178
+ const n = k(e.guideGroups), o = e.guideGroupDisplayLogs[g];
179
+ if (!(n && n.display_interval && o && w(
180
+ o,
181
+ n.display_interval
182
+ )))
183
+ switch (this.stage || (this.stage = this.openGroupStage()), this.stage.status) {
184
+ case "open": {
185
+ this.knock.log(`[Guide] Addng to the group stage: ${i.key}`), this.stage.ordered[r] = i.key;
186
+ return;
187
+ }
188
+ case "patch":
189
+ return this.knock.log(`[Guide] Patching the group stage: ${i.key}`), this.stage.ordered[r] = i.key, this.stage.resolved === i.key ? i : void 0;
190
+ case "closed":
191
+ return this.stage.resolved === i.key ? i : void 0;
192
+ }
193
+ }
194
+ openGroupStage() {
195
+ this.knock.log("[Guide] Opening a new group stage");
196
+ const {
197
+ orderResolutionDuration: e = b
198
+ } = this.options, t = setTimeout(() => {
199
+ this.closePendingGroupStage(), this.incrementCounter();
200
+ }, e);
201
+ return this.stage = {
202
+ status: "open",
203
+ ordered: [],
204
+ timeoutId: t
205
+ }, this.stage;
206
+ }
207
+ // Close the current non-closed stage to resolve the prevailing guide up next
208
+ // for display amongst the ones that have been staged.
209
+ closePendingGroupStage() {
210
+ if (!(!this.stage || this.stage.status === "closed"))
211
+ return this.knock.log("[Guide] Closing the current group stage"), this.ensureClearTimeout(), this.stage = {
212
+ ...this.stage,
213
+ status: "closed",
214
+ resolved: this.stage.ordered.find((e) => e !== void 0),
215
+ timeoutId: null
216
+ }, this.stage;
217
+ }
218
+ // Set the current closed stage status to "patch" to allow re-running
219
+ // selections and re-building a group stage with the latest/updated state,
220
+ // while keeping the currently resolved guide in place so that it stays
221
+ // rendered until we are ready to resolve the updated stage and re-render.
222
+ // Note, must be called ahead of updating the state store.
223
+ patchClosedGroupStage() {
224
+ var s;
225
+ if (((s = this.stage) == null ? void 0 : s.status) !== "closed") return;
226
+ this.knock.log("[Guide] Patching the current group stage");
227
+ const { orderResolutionDuration: e = 0 } = this.options, t = setTimeout(() => {
228
+ this.closePendingGroupStage(), this.incrementCounter();
229
+ }, e);
230
+ return this.ensureClearTimeout(), this.stage = {
231
+ ...this.stage,
232
+ status: "patch",
233
+ ordered: [],
234
+ timeoutId: t
235
+ }, this.stage;
236
+ }
237
+ clearGroupStage() {
238
+ this.stage && (this.knock.log("[Guide] Clearing the current group stage"), this.ensureClearTimeout(), this.stage = void 0);
239
+ }
240
+ ensureClearTimeout() {
241
+ var e;
242
+ (e = this.stage) != null && e.timeoutId && clearTimeout(this.stage.timeoutId);
243
+ }
244
+ // Test helper that opens and closes the group stage to return the select
245
+ // result immediately.
246
+ _selectGuide(e, t = {}) {
247
+ return this.openGroupStage(), this.selectGuide(e, t), this.closePendingGroupStage(), this.selectGuide(e, t);
116
248
  }
117
249
  //
118
250
  // Engagement event handlers
@@ -121,6 +253,7 @@ class v {
121
253
  // event to the backend.
122
254
  //
123
255
  async markAsSeen(e, t) {
256
+ if (t.message.seen_at) return;
124
257
  this.knock.log(
125
258
  `[Guide] Marking as seen (Guide key: ${e.key}, Step ref:${t.ref})`
126
259
  );
@@ -128,7 +261,7 @@ class v {
128
261
  seen_at: (/* @__PURE__ */ new Date()).toISOString()
129
262
  });
130
263
  if (!s) return;
131
- const n = {
264
+ const r = {
132
265
  ...this.buildEngagementEventBaseParams(e, s),
133
266
  content: s.content,
134
267
  data: this.targetParams.data,
@@ -136,25 +269,26 @@ class v {
136
269
  };
137
270
  return this.knock.user.markGuideStepAs(
138
271
  "seen",
139
- n
272
+ r
140
273
  ), s;
141
274
  }
142
275
  async markAsInteracted(e, t, s) {
143
276
  this.knock.log(
144
277
  `[Guide] Marking as interacted (Guide key: ${e.key}, Step ref:${t.ref})`
145
278
  );
146
- const n = (/* @__PURE__ */ new Date()).toISOString(), i = this.setStepMessageAttrs(e.key, t.ref, {
147
- read_at: n,
148
- interacted_at: n
279
+ const r = (/* @__PURE__ */ new Date()).toISOString(), i = this.setStepMessageAttrs(e.key, t.ref, {
280
+ read_at: r,
281
+ interacted_at: r
149
282
  });
150
283
  if (!i) return;
151
- const r = {
284
+ const n = {
152
285
  ...this.buildEngagementEventBaseParams(e, i),
153
286
  metadata: s
154
287
  };
155
- return this.knock.user.markGuideStepAs("interacted", r), i;
288
+ return this.knock.user.markGuideStepAs("interacted", n), i;
156
289
  }
157
290
  async markAsArchived(e, t) {
291
+ if (t.message.archived_at) return;
158
292
  this.knock.log(
159
293
  `[Guide] Marking as archived (Guide key: ${e.key}, Step ref:${t.ref})`
160
294
  );
@@ -162,44 +296,53 @@ class v {
162
296
  archived_at: (/* @__PURE__ */ new Date()).toISOString()
163
297
  });
164
298
  if (!s) return;
165
- const n = this.buildEngagementEventBaseParams(e, s);
299
+ const r = this.buildEngagementEventBaseParams(e, s);
166
300
  return this.knock.user.markGuideStepAs(
167
301
  "archived",
168
- n
302
+ {
303
+ ...r,
304
+ unthrottled: e.bypass_global_group_limit
305
+ }
169
306
  ), s;
170
307
  }
171
308
  //
172
309
  // Helpers
173
310
  //
174
311
  localCopy(e) {
175
- const t = this, s = { ...e };
176
- return s.steps = e.steps.map(({ message: n, ...i }) => {
177
- const r = {
312
+ const t = this, s = {
313
+ ...e,
314
+ // Get the next unarchived step.
315
+ getStep() {
316
+ return this.steps.find((r) => !r.message.archived_at);
317
+ }
318
+ };
319
+ return s.getStep = s.getStep.bind(s), s.steps = e.steps.map(({ message: r, ...i }) => {
320
+ const n = {
178
321
  ...i,
179
- message: { ...n },
322
+ message: { ...r },
180
323
  markAsSeen() {
181
324
  if (!this.message.seen_at)
182
325
  return t.markAsSeen(s, this);
183
326
  },
184
- markAsInteracted({ metadata: a } = {}) {
185
- return t.markAsInteracted(s, this, a);
327
+ markAsInteracted({ metadata: o } = {}) {
328
+ return t.markAsInteracted(s, this, o);
186
329
  },
187
330
  markAsArchived() {
188
331
  if (!this.message.archived_at)
189
332
  return t.markAsArchived(s, this);
190
333
  }
191
334
  };
192
- return r.markAsSeen = r.markAsSeen.bind(r), r.markAsInteracted = r.markAsInteracted.bind(r), r.markAsArchived = r.markAsArchived.bind(r), r;
193
- }), s.activation_location_rules = e.activation_location_rules.map((n) => ({
194
- ...n,
195
- pattern: new p({ pathname: n.pathname })
335
+ return n.markAsSeen = n.markAsSeen.bind(n), n.markAsInteracted = n.markAsInteracted.bind(n), n.markAsArchived = n.markAsArchived.bind(n), n;
336
+ }), s.activation_location_rules = e.activation_location_rules.map((r) => ({
337
+ ...r,
338
+ pattern: new v({ pathname: r.pathname })
196
339
  })), s;
197
340
  }
198
341
  buildQueryParams(e = {}) {
199
342
  const t = { ...this.targetParams, ...e };
200
343
  let s = Object.fromEntries(
201
344
  Object.entries(t).filter(
202
- ([n, i]) => i != null
345
+ ([r, i]) => i != null
203
346
  )
204
347
  );
205
348
  return s = s.data ? { ...s, data: JSON.stringify(s.data) } : s, s;
@@ -207,19 +350,22 @@ class v {
207
350
  formatQueryKey(e) {
208
351
  const s = Object.keys(e).sort().map(
209
352
  (i) => `${encodeURIComponent(i)}=${encodeURIComponent(e[i])}`
210
- ).join("&"), n = g(this.knock.userId);
211
- return s ? `${n}?${s}` : n;
353
+ ).join("&"), r = A(this.knock.userId);
354
+ return s ? `${r}?${s}` : r;
212
355
  }
213
356
  setStepMessageAttrs(e, t, s) {
214
- let n;
215
- return this.store.setState((i) => {
216
- const r = i.guides.map((a) => {
217
- if (a.key !== e) return a;
218
- const d = a.steps.map((h) => (h.ref !== t || (h.message = { ...h.message, ...s }, n = h), h));
219
- return { ...a, steps: d };
220
- });
221
- return { ...i, guides: r };
222
- }), n;
357
+ let r;
358
+ return s.archived_at && this.clearGroupStage(), this.store.setState((i) => {
359
+ const n = i.guides[e];
360
+ if (!n) return i;
361
+ const o = n.steps.map((c) => (c.ref !== t || (c.message = { ...c.message, ...s }, r = c), c));
362
+ n.steps = o;
363
+ const a = { ...i.guides, [n.key]: n }, h = s.archived_at && !n.bypass_global_group_limit ? {
364
+ ...i.guideGroupDisplayLogs,
365
+ [g]: s.archived_at
366
+ } : i.guideGroupDisplayLogs;
367
+ return { ...i, guides: a, guideGroupDisplayLogs: h };
368
+ }), r;
223
369
  }
224
370
  buildEngagementEventBaseParams(e, t) {
225
371
  return {
@@ -230,25 +376,35 @@ class v {
230
376
  guide_step_ref: t.ref
231
377
  };
232
378
  }
233
- addGuide({ data: e }) {
234
- const t = this.localCopy(e.guide);
235
- this.store.setState((s) => ({ ...s, guides: u([...s.guides, t]) }));
236
- }
237
- replaceOrAddGuide({ data: e }) {
379
+ addOrReplaceGuide({ data: e }) {
380
+ this.patchClosedGroupStage();
238
381
  const t = this.localCopy(e.guide);
239
382
  this.store.setState((s) => {
240
- let n = !1;
241
- const i = s.guides.map((r) => r.key !== t.key ? r : (n = !0, t));
242
- return {
243
- ...s,
244
- guides: u(n ? i : [...i, t])
245
- };
383
+ const r = { ...s.guides, [t.key]: t };
384
+ return { ...s, guides: r };
246
385
  });
247
386
  }
248
387
  removeGuide({ data: e }) {
249
- this.store.setState((t) => {
250
- const s = t.guides.filter((n) => n.key !== e.guide.key);
251
- return { ...t, guides: s };
388
+ this.patchClosedGroupStage(), this.store.setState((t) => {
389
+ const { [e.guide.key]: s, ...r } = t.guides;
390
+ return { ...t, guides: r };
391
+ });
392
+ }
393
+ addOrReplaceGuideGroup({
394
+ data: e
395
+ }) {
396
+ this.patchClosedGroupStage(), this.store.setState((t) => {
397
+ const s = [e.guide_group], r = e.guide_group.display_sequence_unthrottled || [], i = e.guide_group.display_sequence_throttled || [];
398
+ let n = t.guides;
399
+ return n = r.reduce((o, a) => {
400
+ if (!o[a]) return o;
401
+ const h = { ...o[a], bypass_global_group_limit: !0 };
402
+ return { ...o, [a]: h };
403
+ }, n), n = i.reduce((o, a) => {
404
+ if (!o[a]) return o;
405
+ const h = { ...o[a], bypass_global_group_limit: !1 };
406
+ return { ...o, [a]: h };
407
+ }, n), { ...t, guides: n, guideGroups: s };
252
408
  });
253
409
  }
254
410
  listenForLocationChangesFromWindow() {
@@ -256,14 +412,14 @@ class v {
256
412
  window.addEventListener("popstate", this.handleLocationChange), window.addEventListener("hashchange", this.handleLocationChange);
257
413
  const e = window.history.pushState, t = window.history.replaceState;
258
414
  window.history.pushState = new Proxy(e, {
259
- apply: (s, n, i) => {
260
- Reflect.apply(s, n, i), setTimeout(() => {
415
+ apply: (s, r, i) => {
416
+ Reflect.apply(s, r, i), setTimeout(() => {
261
417
  this.handleLocationChange();
262
418
  }, 0);
263
419
  }
264
420
  }), window.history.replaceState = new Proxy(t, {
265
- apply: (s, n, i) => {
266
- Reflect.apply(s, n, i), setTimeout(() => {
421
+ apply: (s, r, i) => {
422
+ Reflect.apply(s, r, i), setTimeout(() => {
267
423
  this.handleLocationChange();
268
424
  }, 0);
269
425
  }
@@ -278,7 +434,7 @@ class v {
278
434
  }
279
435
  }
280
436
  export {
281
- v as KnockGuideClient,
282
- g as guidesApiRootPath
437
+ F as KnockGuideClient,
438
+ A as guidesApiRootPath
283
439
  };
284
440
  //# sourceMappingURL=client.mjs.map