@knocklabs/client 0.14.10 → 0.15.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 (130) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/cjs/api.js +2 -0
  3. package/dist/cjs/api.js.map +1 -0
  4. package/dist/cjs/clients/feed/feed.js +2 -0
  5. package/dist/cjs/clients/feed/feed.js.map +1 -0
  6. package/dist/cjs/clients/feed/index.js +2 -0
  7. package/dist/cjs/clients/feed/index.js.map +1 -0
  8. package/dist/cjs/clients/feed/socket-manager.js +2 -0
  9. package/dist/cjs/clients/feed/socket-manager.js.map +1 -0
  10. package/dist/cjs/clients/feed/store.js +2 -0
  11. package/dist/cjs/clients/feed/store.js.map +1 -0
  12. package/dist/cjs/clients/feed/utils.js +2 -0
  13. package/dist/cjs/clients/feed/utils.js.map +1 -0
  14. package/dist/cjs/clients/guide/client.js +2 -0
  15. package/dist/cjs/clients/guide/client.js.map +1 -0
  16. package/dist/cjs/clients/messages/index.js +2 -0
  17. package/dist/cjs/clients/messages/index.js.map +1 -0
  18. package/dist/cjs/clients/ms-teams/index.js +2 -0
  19. package/dist/cjs/clients/ms-teams/index.js.map +1 -0
  20. package/dist/cjs/clients/objects/constants.js +2 -0
  21. package/dist/cjs/clients/objects/constants.js.map +1 -0
  22. package/dist/cjs/clients/objects/index.js +2 -0
  23. package/dist/cjs/clients/objects/index.js.map +1 -0
  24. package/dist/cjs/clients/preferences/index.js +2 -0
  25. package/dist/cjs/clients/preferences/index.js.map +1 -0
  26. package/dist/cjs/clients/slack/index.js +2 -0
  27. package/dist/cjs/clients/slack/index.js.map +1 -0
  28. package/dist/cjs/clients/users/index.js +2 -0
  29. package/dist/cjs/clients/users/index.js.map +1 -0
  30. package/dist/cjs/helpers.js +2 -0
  31. package/dist/cjs/helpers.js.map +1 -0
  32. package/dist/cjs/index.js +2 -0
  33. package/dist/cjs/index.js.map +1 -0
  34. package/dist/cjs/knock.js +2 -0
  35. package/dist/cjs/knock.js.map +1 -0
  36. package/dist/cjs/networkStatus.js +2 -0
  37. package/dist/cjs/networkStatus.js.map +1 -0
  38. package/dist/esm/api.mjs +58 -0
  39. package/dist/esm/api.mjs.map +1 -0
  40. package/dist/esm/clients/feed/feed.mjs +422 -0
  41. package/dist/esm/clients/feed/feed.mjs.map +1 -0
  42. package/dist/esm/clients/feed/index.mjs +47 -0
  43. package/dist/esm/clients/feed/index.mjs.map +1 -0
  44. package/dist/esm/clients/feed/socket-manager.mjs +81 -0
  45. package/dist/esm/clients/feed/socket-manager.mjs.map +1 -0
  46. package/dist/esm/clients/feed/store.mjs +104 -0
  47. package/dist/esm/clients/feed/store.mjs.map +1 -0
  48. package/dist/esm/clients/feed/utils.mjs +35 -0
  49. package/dist/esm/clients/feed/utils.mjs.map +1 -0
  50. package/dist/esm/clients/guide/client.mjs +284 -0
  51. package/dist/esm/clients/guide/client.mjs.map +1 -0
  52. package/dist/esm/clients/messages/index.mjs +64 -0
  53. package/dist/esm/clients/messages/index.mjs.map +1 -0
  54. package/dist/esm/clients/ms-teams/index.mjs +91 -0
  55. package/dist/esm/clients/ms-teams/index.mjs.map +1 -0
  56. package/dist/esm/clients/objects/constants.mjs +5 -0
  57. package/dist/esm/clients/objects/constants.mjs.map +1 -0
  58. package/dist/esm/clients/objects/index.mjs +42 -0
  59. package/dist/esm/clients/objects/index.mjs.map +1 -0
  60. package/dist/esm/clients/preferences/index.mjs +128 -0
  61. package/dist/esm/clients/preferences/index.mjs.map +1 -0
  62. package/dist/esm/clients/slack/index.mjs +72 -0
  63. package/dist/esm/clients/slack/index.mjs.map +1 -0
  64. package/dist/esm/clients/users/index.mjs +99 -0
  65. package/dist/esm/clients/users/index.mjs.map +1 -0
  66. package/dist/esm/helpers.mjs +8 -0
  67. package/dist/esm/helpers.mjs.map +1 -0
  68. package/dist/esm/index.mjs +16 -0
  69. package/dist/esm/index.mjs.map +1 -0
  70. package/dist/esm/knock.mjs +108 -0
  71. package/dist/esm/knock.mjs.map +1 -0
  72. package/dist/esm/networkStatus.mjs +15 -0
  73. package/dist/esm/networkStatus.mjs.map +1 -0
  74. package/dist/types/api.d.ts +25 -0
  75. package/dist/types/api.d.ts.map +1 -0
  76. package/dist/types/clients/feed/feed.d.ts +75 -0
  77. package/dist/types/clients/feed/feed.d.ts.map +1 -0
  78. package/dist/types/clients/feed/index.d.ts +17 -0
  79. package/dist/types/clients/feed/index.d.ts.map +1 -0
  80. package/dist/types/clients/feed/interfaces.d.ts +99 -0
  81. package/dist/types/clients/feed/interfaces.d.ts.map +1 -0
  82. package/dist/types/clients/feed/socket-manager.d.ts +31 -0
  83. package/dist/types/clients/feed/socket-manager.d.ts.map +1 -0
  84. package/dist/types/clients/feed/store.d.ts +20 -0
  85. package/dist/types/clients/feed/store.d.ts.map +1 -0
  86. package/dist/types/clients/feed/types.d.ts +35 -0
  87. package/dist/types/clients/feed/types.d.ts.map +1 -0
  88. package/dist/types/clients/feed/utils.d.ts +20 -0
  89. package/dist/types/clients/feed/utils.d.ts.map +1 -0
  90. package/dist/types/clients/guide/client.d.ts +124 -0
  91. package/dist/types/clients/guide/client.d.ts.map +1 -0
  92. package/dist/types/clients/guide/index.d.ts +3 -0
  93. package/dist/types/clients/guide/index.d.ts.map +1 -0
  94. package/dist/types/clients/messages/index.d.ts +15 -0
  95. package/dist/types/clients/messages/index.d.ts.map +1 -0
  96. package/dist/types/clients/messages/interfaces.d.ts +46 -0
  97. package/dist/types/clients/messages/interfaces.d.ts.map +1 -0
  98. package/dist/types/clients/ms-teams/index.d.ts +14 -0
  99. package/dist/types/clients/ms-teams/index.d.ts.map +1 -0
  100. package/dist/types/clients/ms-teams/interfaces.d.ts +49 -0
  101. package/dist/types/clients/ms-teams/interfaces.d.ts.map +1 -0
  102. package/dist/types/clients/objects/constants.d.ts +2 -0
  103. package/dist/types/clients/objects/constants.d.ts.map +1 -0
  104. package/dist/types/clients/objects/index.d.ts +23 -0
  105. package/dist/types/clients/objects/index.d.ts.map +1 -0
  106. package/dist/types/clients/preferences/index.d.ts +46 -0
  107. package/dist/types/clients/preferences/index.d.ts.map +1 -0
  108. package/dist/types/clients/preferences/interfaces.d.ts +29 -0
  109. package/dist/types/clients/preferences/interfaces.d.ts.map +1 -0
  110. package/dist/types/clients/slack/index.d.ts +13 -0
  111. package/dist/types/clients/slack/index.d.ts.map +1 -0
  112. package/dist/types/clients/slack/interfaces.d.ts +29 -0
  113. package/dist/types/clients/slack/interfaces.d.ts.map +1 -0
  114. package/dist/types/clients/users/index.d.ts +22 -0
  115. package/dist/types/clients/users/index.d.ts.map +1 -0
  116. package/dist/types/clients/users/interfaces.d.ts +9 -0
  117. package/dist/types/clients/users/interfaces.d.ts.map +1 -0
  118. package/dist/types/helpers.d.ts +2 -0
  119. package/dist/types/helpers.d.ts.map +1 -0
  120. package/dist/types/index.d.ts +21 -0
  121. package/dist/types/index.d.ts.map +1 -0
  122. package/dist/types/interfaces.d.ts +72 -0
  123. package/dist/types/interfaces.d.ts.map +1 -0
  124. package/dist/types/knock.d.ts +55 -0
  125. package/dist/types/knock.d.ts.map +1 -0
  126. package/dist/types/networkStatus.d.ts +8 -0
  127. package/dist/types/networkStatus.d.ts.map +1 -0
  128. package/package.json +2 -1
  129. package/src/interfaces.ts +6 -0
  130. package/src/knock.ts +52 -5
@@ -0,0 +1,81 @@
1
+ var f = Object.defineProperty;
2
+ var u = (c, s, e) => s in c ? f(c, s, { enumerable: !0, configurable: !0, writable: !0, value: e }) : c[s] = e;
3
+ var h = (c, s, e) => u(c, typeof s != "symbol" ? s + "" : s, e);
4
+ import { Store as k } from "@tanstack/store";
5
+ const S = {
6
+ NewMessage: "new-message"
7
+ }, m = [S.NewMessage];
8
+ class x {
9
+ constructor(s) {
10
+ // Mapping of live channels by topic. Note, there can be one or more feed
11
+ // client(s) that can subscribe.
12
+ h(this, "channels");
13
+ // Mapping of query params for each feeds client, partitioned by reference id,
14
+ // and grouped by channel topic. It's a double nested object that looks like:
15
+ // {
16
+ // "feeds:<channel_1>:<user_1>": {
17
+ // "ref-1": {
18
+ // "tenant": "foo",
19
+ // },
20
+ // "ref-2": {
21
+ // "tenant": "bar",
22
+ // },
23
+ // },
24
+ // "feeds:<channel_2>:<user_1>": {
25
+ // "ref-3": {
26
+ // "tenant": "baz",
27
+ // },
28
+ // }
29
+ // }
30
+ //
31
+ // Each time a new feed client joins a channel, we send all cumulated
32
+ // params such that the socket API can apply filtering rules and figure out
33
+ // which feed clients should be notified based on reference ids in
34
+ // "attn" field of the event payload when sending out an event.
35
+ h(this, "params");
36
+ // A reactive store that captures a new socket event, that notifies any feed
37
+ // clients that have subscribed.
38
+ h(this, "inbox");
39
+ this.socket = s, this.channels = {}, this.params = {}, this.inbox = new k({});
40
+ }
41
+ join(s) {
42
+ const e = s.socketChannelTopic, t = s.referenceId, a = s.defaultOptions;
43
+ this.socket.isConnected() || this.socket.connect(), this.params[e] || (this.params[e] = {});
44
+ const n = this.params[e][t], p = !n || JSON.stringify(n) !== JSON.stringify(a);
45
+ if (p && (this.params[e] = { ...this.params[e], [t]: a }), !this.channels[e] || p) {
46
+ const o = this.socket.channel(e, this.params[e]);
47
+ for (const l of m)
48
+ o.on(l, (b) => this.setInbox(b));
49
+ this.channels[e] = o;
50
+ }
51
+ const i = this.channels[e];
52
+ return ["closed", "errored"].includes(i.state) && i.join(), this.inbox.subscribe(() => {
53
+ const o = this.inbox.state[t];
54
+ o && s.handleSocketEvent(o);
55
+ });
56
+ }
57
+ leave(s) {
58
+ var o;
59
+ (o = s.unsubscribeFromSocketEvents) == null || o.call(s);
60
+ const e = s.socketChannelTopic, t = s.referenceId, a = { ...this.params }, n = a[e] || {};
61
+ n[t] && delete n[t];
62
+ const i = { ...this.channels }, r = i[e];
63
+ if (r && Object.keys(n).length === 0) {
64
+ for (const l of m)
65
+ r.off(l);
66
+ r.leave(), delete i[e];
67
+ }
68
+ this.params = a, this.channels = i;
69
+ }
70
+ setInbox(s) {
71
+ const { attn: e, ...t } = s;
72
+ this.inbox.setState(
73
+ () => e.reduce((a, n) => ({ ...a, [n]: t }), {})
74
+ );
75
+ }
76
+ }
77
+ export {
78
+ x as FeedSocketManager,
79
+ S as SocketEventType
80
+ };
81
+ //# sourceMappingURL=socket-manager.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"socket-manager.mjs","sources":["../../../../src/clients/feed/socket-manager.ts"],"sourcesContent":["import { Store } from \"@tanstack/store\";\nimport { Channel, Socket } from \"phoenix\";\n\nimport Feed from \"./feed\";\nimport type { FeedClientOptions, FeedMetadata } from \"./interfaces\";\n\nexport const SocketEventType = {\n NewMessage: \"new-message\",\n} as const;\n\nconst SOCKET_EVENT_TYPES = [SocketEventType.NewMessage];\n\ntype ClientQueryParams = FeedClientOptions;\n\n// e.g. feeds:<channel_id>:<user_id>\ntype ChannelTopic = string;\n\n// Unique reference id of a feed client\ntype ClientReferenceId = string;\n\ntype NewMessageEventPayload = {\n event: typeof SocketEventType.NewMessage;\n /**\n * @deprecated Top-level feed metadata. Exists for legacy reasons.\n */\n metadata: FeedMetadata;\n /** Feed metadata, keyed by client reference id. */\n data: Record<ClientReferenceId, { metadata: FeedMetadata }>;\n};\n\nexport type SocketEventPayload = NewMessageEventPayload;\n\n// \"attn\" field contains a list of client reference ids that should be notified\n// of a socket event.\ntype WithAttn<P> = P & { attn: ClientReferenceId[] };\n\ntype FeedSocketInbox = Record<ClientReferenceId, SocketEventPayload>;\n\n/*\n * Manages socket subscriptions for feeds, allowing multiple feed clients\n * to listen for real time updates from the socket API via a single socket\n * connection.\n */\nexport class FeedSocketManager {\n // Mapping of live channels by topic. Note, there can be one or more feed\n // client(s) that can subscribe.\n private channels: Record<ChannelTopic, Channel>;\n\n // Mapping of query params for each feeds client, partitioned by reference id,\n // and grouped by channel topic. It's a double nested object that looks like:\n // {\n // \"feeds:<channel_1>:<user_1>\": {\n // \"ref-1\": {\n // \"tenant\": \"foo\",\n // },\n // \"ref-2\": {\n // \"tenant\": \"bar\",\n // },\n // },\n // \"feeds:<channel_2>:<user_1>\": {\n // \"ref-3\": {\n // \"tenant\": \"baz\",\n // },\n // }\n // }\n //\n // Each time a new feed client joins a channel, we send all cumulated\n // params such that the socket API can apply filtering rules and figure out\n // which feed clients should be notified based on reference ids in\n // \"attn\" field of the event payload when sending out an event.\n private params: Record<\n ChannelTopic,\n Record<ClientReferenceId, ClientQueryParams>\n >;\n\n // A reactive store that captures a new socket event, that notifies any feed\n // clients that have subscribed.\n private inbox: Store<\n FeedSocketInbox,\n (cb: FeedSocketInbox) => FeedSocketInbox\n >;\n\n constructor(readonly socket: Socket) {\n this.channels = {};\n this.params = {};\n this.inbox = new Store<FeedSocketInbox>({});\n }\n\n join(feed: Feed) {\n const topic = feed.socketChannelTopic;\n const referenceId = feed.referenceId;\n const params = feed.defaultOptions;\n\n // Ensure a live socket connection if not yet connected.\n if (!this.socket.isConnected()) {\n this.socket.connect();\n }\n\n // If a new feed client joins, or has updated query params, then\n // track the updated params and (re)join with the latest query params.\n // Note, each time we send combined params of all feed clients that\n // have subscribed for a given feed channel and user, grouped by\n // client's reference id.\n if (!this.params[topic]) {\n this.params[topic] = {};\n }\n\n const maybeParams = this.params[topic][referenceId];\n const hasNewOrUpdatedParams =\n !maybeParams || JSON.stringify(maybeParams) !== JSON.stringify(params);\n\n if (hasNewOrUpdatedParams) {\n // Tracks all subscribed clients' params by reference id and by topic.\n this.params[topic] = { ...this.params[topic], [referenceId]: params };\n }\n\n if (!this.channels[topic] || hasNewOrUpdatedParams) {\n const newChannel = this.socket.channel(topic, this.params[topic]);\n for (const eventType of SOCKET_EVENT_TYPES) {\n newChannel.on(eventType, (payload) => this.setInbox(payload));\n }\n // Tracks live channels by channel topic.\n this.channels[topic] = newChannel;\n }\n\n const channel = this.channels[topic];\n\n // Join the channel if not already joined or joining or leaving.\n if ([\"closed\", \"errored\"].includes(channel.state)) {\n channel.join();\n }\n\n // Let the feed client subscribe to the \"inbox\", so it can be notified\n // when there's a new socket event that is relevant to it\n const unsub = this.inbox.subscribe(() => {\n const payload = this.inbox.state[referenceId];\n if (!payload) return;\n\n feed.handleSocketEvent(payload);\n });\n\n return unsub;\n }\n\n leave(feed: Feed) {\n feed.unsubscribeFromSocketEvents?.();\n\n const topic = feed.socketChannelTopic;\n const referenceId = feed.referenceId;\n\n const partitionedParams = { ...this.params };\n const paramsForTopic = partitionedParams[topic] || {};\n const paramsForReferenceClient = paramsForTopic[referenceId];\n\n if (paramsForReferenceClient) {\n delete paramsForTopic[referenceId];\n }\n\n const channels = { ...this.channels };\n const channelForTopic = channels[topic];\n if (channelForTopic && Object.keys(paramsForTopic).length === 0) {\n for (const eventType of SOCKET_EVENT_TYPES) {\n channelForTopic.off(eventType);\n }\n channelForTopic.leave();\n delete channels[topic];\n }\n\n this.params = partitionedParams;\n this.channels = channels;\n }\n\n private setInbox(payload: WithAttn<SocketEventPayload>) {\n const { attn, ...rest } = payload;\n\n // Set the incoming socket event into the inbox, keyed by relevant client\n // reference ids provided by the server (via attn field), so we can notify\n // only the clients that need to be notified.\n this.inbox.setState(() =>\n attn.reduce((acc, referenceId) => {\n return { ...acc, [referenceId]: rest };\n }, {}),\n );\n }\n}\n"],"names":["SocketEventType","SOCKET_EVENT_TYPES","FeedSocketManager","socket","__publicField","Store","feed","topic","referenceId","params","maybeParams","hasNewOrUpdatedParams","newChannel","eventType","payload","channel","_a","partitionedParams","paramsForTopic","channels","channelForTopic","attn","rest","acc"],"mappings":";;;;AAMO,MAAMA,IAAkB;AAAA,EAC7B,YAAY;AACd,GAEMC,IAAqB,CAACD,EAAgB,UAAU;AAiC/C,MAAME,EAAkB;AAAA,EAuC7B,YAAqBC,GAAgB;AApC7B;AAAA;AAAA,IAAAC,EAAA;AAwBA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAAAA,EAAA;AAOA;AAAA;AAAA,IAAAA,EAAA;AAKa,SAAA,SAAAD,GACnB,KAAK,WAAW,CAAC,GACjB,KAAK,SAAS,CAAC,GACf,KAAK,QAAQ,IAAIE,EAAuB,EAAE;AAAA,EAAA;AAAA,EAG5C,KAAKC,GAAY;AACf,UAAMC,IAAQD,EAAK,oBACbE,IAAcF,EAAK,aACnBG,IAASH,EAAK;AAGpB,IAAK,KAAK,OAAO,iBACf,KAAK,OAAO,QAAQ,GAQjB,KAAK,OAAOC,CAAK,MACf,KAAA,OAAOA,CAAK,IAAI,CAAC;AAGxB,UAAMG,IAAc,KAAK,OAAOH,CAAK,EAAEC,CAAW,GAC5CG,IACJ,CAACD,KAAe,KAAK,UAAUA,CAAW,MAAM,KAAK,UAAUD,CAAM;AAOvE,QALIE,MAEF,KAAK,OAAOJ,CAAK,IAAI,EAAE,GAAG,KAAK,OAAOA,CAAK,GAAG,CAACC,CAAW,GAAGC,EAAO,IAGlE,CAAC,KAAK,SAASF,CAAK,KAAKI,GAAuB;AAC5C,YAAAC,IAAa,KAAK,OAAO,QAAQL,GAAO,KAAK,OAAOA,CAAK,CAAC;AAChE,iBAAWM,KAAaZ;AACtB,QAAAW,EAAW,GAAGC,GAAW,CAACC,MAAY,KAAK,SAASA,CAAO,CAAC;AAGzD,WAAA,SAASP,CAAK,IAAIK;AAAA,IAAA;AAGnB,UAAAG,IAAU,KAAK,SAASR,CAAK;AAGnC,WAAI,CAAC,UAAU,SAAS,EAAE,SAASQ,EAAQ,KAAK,KAC9CA,EAAQ,KAAK,GAKD,KAAK,MAAM,UAAU,MAAM;AACvC,YAAMD,IAAU,KAAK,MAAM,MAAMN,CAAW;AAC5C,MAAKM,KAELR,EAAK,kBAAkBQ,CAAO;AAAA,IAAA,CAC/B;AAAA,EAEM;AAAA,EAGT,MAAMR,GAAY;;AAChB,KAAAU,IAAAV,EAAK,gCAAL,QAAAU,EAAA,KAAAV;AAEA,UAAMC,IAAQD,EAAK,oBACbE,IAAcF,EAAK,aAEnBW,IAAoB,EAAE,GAAG,KAAK,OAAO,GACrCC,IAAiBD,EAAkBV,CAAK,KAAK,CAAC;AAGpD,IAFiCW,EAAeV,CAAW,KAGzD,OAAOU,EAAeV,CAAW;AAGnC,UAAMW,IAAW,EAAE,GAAG,KAAK,SAAS,GAC9BC,IAAkBD,EAASZ,CAAK;AACtC,QAAIa,KAAmB,OAAO,KAAKF,CAAc,EAAE,WAAW,GAAG;AAC/D,iBAAWL,KAAaZ;AACtB,QAAAmB,EAAgB,IAAIP,CAAS;AAE/B,MAAAO,EAAgB,MAAM,GACtB,OAAOD,EAASZ,CAAK;AAAA,IAAA;AAGvB,SAAK,SAASU,GACd,KAAK,WAAWE;AAAA,EAAA;AAAA,EAGV,SAASL,GAAuC;AACtD,UAAM,EAAE,MAAAO,GAAM,GAAGC,EAAA,IAASR;AAK1B,SAAK,MAAM;AAAA,MAAS,MAClBO,EAAK,OAAO,CAACE,GAAKf,OACT,EAAE,GAAGe,GAAK,CAACf,CAAW,GAAGc,EAAK,IACpC,CAAE,CAAA;AAAA,IACP;AAAA,EAAA;AAEJ;"}
@@ -0,0 +1,104 @@
1
+ var c = Object.defineProperty;
2
+ var S = (s, t, e) => t in s ? c(s, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : s[t] = e;
3
+ var l = (s, t, e) => S(s, typeof t != "symbol" ? t + "" : t, e);
4
+ import { Store as f } from "@tanstack/store";
5
+ import { NetworkStatus as d } from "../../networkStatus.mjs";
6
+ import { deduplicateItems as m, sortItems as p } from "./utils.mjs";
7
+ function g(s) {
8
+ const t = m(s);
9
+ return p(t);
10
+ }
11
+ const I = {
12
+ shouldSetPage: !0,
13
+ shouldAppend: !1
14
+ }, i = {
15
+ items: [],
16
+ metadata: {
17
+ total_count: 0,
18
+ unread_count: 0,
19
+ unseen_count: 0
20
+ },
21
+ pageInfo: {
22
+ before: null,
23
+ after: null,
24
+ page_size: 50
25
+ },
26
+ loading: !1,
27
+ networkStatus: d.ready,
28
+ setResult: () => {
29
+ },
30
+ setMetadata: () => {
31
+ },
32
+ setNetworkStatus: () => {
33
+ },
34
+ resetStore: () => {
35
+ },
36
+ setItemAttrs: () => {
37
+ }
38
+ }, h = () => {
39
+ const s = new f(i);
40
+ return s.setState((t) => ({
41
+ ...t,
42
+ // The network status indicates what's happening with the request
43
+ networkStatus: d.ready,
44
+ loading: !1,
45
+ setNetworkStatus: (e) => s.setState((r) => ({
46
+ ...r,
47
+ networkStatus: e,
48
+ loading: e === d.loading
49
+ })),
50
+ setResult: ({ entries: e, meta: r, page_info: u }, o = I) => s.setState((a) => {
51
+ const n = o.shouldAppend ? g(a.items.concat(e)) : e;
52
+ return {
53
+ ...a,
54
+ items: n,
55
+ metadata: r,
56
+ pageInfo: o.shouldSetPage ? u : a.pageInfo,
57
+ loading: !1,
58
+ networkStatus: d.ready
59
+ };
60
+ }),
61
+ setMetadata: (e) => s.setState((r) => ({ ...r, metadata: e })),
62
+ resetStore: (e = i.metadata) => s.setState(() => ({ ...i, metadata: e })),
63
+ setItemAttrs: (e, r) => {
64
+ const u = e.reduce(
65
+ (o, a) => ({ ...o, [a]: r }),
66
+ {}
67
+ );
68
+ return s.setState((o) => {
69
+ const a = o.items.map((n) => u[n.id] ? { ...n, ...u[n.id] } : n);
70
+ return { ...o, items: a };
71
+ });
72
+ }
73
+ })), s;
74
+ };
75
+ class w {
76
+ constructor(t) {
77
+ l(this, "store");
78
+ this.store = t;
79
+ }
80
+ getState() {
81
+ return this.store.state;
82
+ }
83
+ setState(t) {
84
+ this.store.setState(
85
+ typeof t == "function" ? t : () => t
86
+ );
87
+ }
88
+ getInitialState() {
89
+ return i;
90
+ }
91
+ subscribe(t) {
92
+ return this.store.subscribe((e) => t(e.currentVal));
93
+ }
94
+ }
95
+ function _() {
96
+ const s = h();
97
+ return new w(s);
98
+ }
99
+ export {
100
+ w as FeedStore,
101
+ _ as default,
102
+ i as initialStoreState
103
+ };
104
+ //# sourceMappingURL=store.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.mjs","sources":["../../../../src/clients/feed/store.ts"],"sourcesContent":["import { GenericData } from \"@knocklabs/types\";\nimport { Store } from \"@tanstack/store\";\n\nimport { NetworkStatus } from \"../../networkStatus\";\n\nimport { FeedItem, FeedMetadata, FeedResponse } from \"./interfaces\";\nimport { FeedStoreState, StoreFeedResultOptions } from \"./types\";\nimport { deduplicateItems, sortItems } from \"./utils\";\n\nfunction processItems(items: FeedItem[]) {\n const deduped = deduplicateItems(items);\n const sorted = sortItems(deduped);\n\n return sorted;\n}\n\nconst defaultSetResultOptions = {\n shouldSetPage: true,\n shouldAppend: false,\n};\n\nexport const initialStoreState: FeedStoreState = {\n items: [],\n metadata: {\n total_count: 0,\n unread_count: 0,\n unseen_count: 0,\n },\n pageInfo: {\n before: null,\n after: null,\n page_size: 50,\n },\n loading: false,\n networkStatus: NetworkStatus.ready,\n setResult: () => {},\n setMetadata: () => {},\n setNetworkStatus: () => {},\n resetStore: () => {},\n setItemAttrs: () => {},\n};\n\n/**\n * Initalize the store with tanstack store so it can be used within our\n * FeedStore class. We do this seperately so that we have the ability to\n * change which store library we use in the future if need be.\n */\nconst initalizeStore = () => {\n const store = new Store(initialStoreState);\n\n store.setState((state) => ({\n ...state,\n // The network status indicates what's happening with the request\n networkStatus: NetworkStatus.ready,\n loading: false,\n setNetworkStatus: (networkStatus: NetworkStatus) =>\n store.setState((state) => ({\n ...state,\n networkStatus,\n loading: networkStatus === NetworkStatus.loading,\n })),\n\n setResult: (\n { entries, meta, page_info }: FeedResponse,\n options: StoreFeedResultOptions = defaultSetResultOptions,\n ) =>\n store.setState((state) => {\n // We resort the list on set, so concating everything is fine (if a bit suboptimal)\n const items = options.shouldAppend\n ? processItems(state.items.concat(entries as FeedItem<GenericData>[]))\n : entries;\n\n return {\n ...state,\n items,\n metadata: meta,\n pageInfo: options.shouldSetPage ? page_info : state.pageInfo,\n loading: false,\n networkStatus: NetworkStatus.ready,\n };\n }),\n\n setMetadata: (metadata: FeedMetadata) =>\n store.setState((state) => ({ ...state, metadata })),\n\n resetStore: (metadata = initialStoreState.metadata) =>\n store.setState(() => ({ ...initialStoreState, metadata })),\n\n setItemAttrs: (itemIds: Array<string>, attrs: object) => {\n // Create a map for the items to the updates to be made\n const itemUpdatesMap: { [id: string]: object } = itemIds.reduce(\n (acc, itemId) => ({ ...acc, [itemId]: attrs }),\n {},\n );\n\n return store.setState((state) => {\n const items = state.items.map((item) => {\n if (itemUpdatesMap[item.id]) {\n return { ...item, ...itemUpdatesMap[item.id] };\n }\n\n return item;\n });\n\n return { ...state, items };\n });\n },\n }));\n\n return store;\n};\n\n/**\n * The FeedStore class is a wrapper for our store solution that's\n * based on the same shape as zustand. This wrapping class allows\n * us to maintain backwards compatibility with the zustand model\n * while still allowing us to utilize tanstack store for the\n * underlying store solution.\n */\nexport class FeedStore {\n store: Store<FeedStoreState>;\n\n constructor(store: Store<FeedStoreState>) {\n this.store = store;\n }\n\n getState() {\n return this.store.state;\n }\n\n setState(\n updater: ((state: FeedStoreState) => FeedStoreState) | FeedStoreState,\n ) {\n this.store.setState(\n typeof updater === \"function\" ? updater : () => updater,\n );\n }\n\n getInitialState() {\n return initialStoreState;\n }\n\n subscribe(listener: (state: FeedStoreState) => void) {\n return this.store.subscribe((state) => listener(state.currentVal));\n }\n}\n\nexport default function createStore() {\n const store = initalizeStore();\n return new FeedStore(store);\n}\n"],"names":["processItems","items","deduped","deduplicateItems","sortItems","defaultSetResultOptions","initialStoreState","NetworkStatus","initalizeStore","store","Store","state","networkStatus","entries","meta","page_info","options","metadata","itemIds","attrs","itemUpdatesMap","acc","itemId","item","FeedStore","__publicField","updater","listener","createStore"],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ function i(e) {
2
+ const t = {}, r = [];
3
+ return e.reduce((a, n) => t[n.id] ? a : (t[n.id] = !0, [...a, n]), r);
4
+ }
5
+ function g(e) {
6
+ return e.sort((t, r) => new Date(r.inserted_at).getTime() - new Date(t.inserted_at).getTime());
7
+ }
8
+ function s(e) {
9
+ const { inserted_at_date_range: t, ...r } = e;
10
+ if (!t)
11
+ return r;
12
+ const a = {}, n = t.inclusive ?? !1;
13
+ if (t.start) {
14
+ const d = n ? "inserted_at.gte" : "inserted_at.gt";
15
+ a[d] = t.start;
16
+ }
17
+ if (t.end) {
18
+ const d = n ? "inserted_at.lte" : "inserted_at.lt";
19
+ a[d] = t.end;
20
+ }
21
+ return { ...r, ...a };
22
+ }
23
+ function u(e) {
24
+ if (typeof (e == null ? void 0 : e.trigger_data) == "object")
25
+ return JSON.stringify(e.trigger_data);
26
+ if (typeof (e == null ? void 0 : e.trigger_data) == "string")
27
+ return e.trigger_data;
28
+ }
29
+ export {
30
+ i as deduplicateItems,
31
+ u as getFormattedTriggerData,
32
+ s as mergeDateRangeParams,
33
+ g as sortItems
34
+ };
35
+ //# sourceMappingURL=utils.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.mjs","sources":["../../../../src/clients/feed/utils.ts"],"sourcesContent":["import type { FeedClientOptions, FeedItem } from \"./interfaces\";\n\nexport function deduplicateItems(items: FeedItem[]): FeedItem[] {\n const seen: Record<string, boolean> = {};\n const values: FeedItem[] = [];\n\n return items.reduce((acc, item) => {\n if (seen[item.id]) {\n return acc;\n }\n\n seen[item.id] = true;\n return [...acc, item];\n }, values);\n}\n\nexport function sortItems(items: FeedItem[]) {\n return items.sort((a, b) => {\n return (\n new Date(b.inserted_at).getTime() - new Date(a.inserted_at).getTime()\n );\n });\n}\n\nexport function mergeDateRangeParams(options: FeedClientOptions) {\n const { inserted_at_date_range, ...rest } = options;\n\n if (!inserted_at_date_range) {\n return rest;\n }\n\n const dateRangeParams: Record<string, string> = {};\n\n // Determine which operators to use based on the inclusive flag\n const isInclusive = inserted_at_date_range.inclusive ?? false;\n\n // For start date: use gte if inclusive, gt if not\n if (inserted_at_date_range.start) {\n const startOperator = isInclusive ? \"inserted_at.gte\" : \"inserted_at.gt\";\n dateRangeParams[startOperator] = inserted_at_date_range.start;\n }\n\n // For end date: use lte if inclusive, lt if not\n if (inserted_at_date_range.end) {\n const endOperator = isInclusive ? \"inserted_at.lte\" : \"inserted_at.lt\";\n dateRangeParams[endOperator] = inserted_at_date_range.end;\n }\n\n return { ...rest, ...dateRangeParams };\n}\n\n// If the trigger data is an object, stringify it to conform to API expectations\n// https://docs.knock.app/reference#get-feed\n// We also want to be careful to check for string values already,\n// because this was a bug (KNO-7843) and customers had to manually stringify their trigger data\nexport function getFormattedTriggerData(options: FeedClientOptions) {\n // If the trigger data is an object, stringify it to conform to API expectations\n if (typeof options?.trigger_data === \"object\") {\n return JSON.stringify(options.trigger_data);\n }\n\n // For when the trigger data is already formatted as a string by the user\n if (typeof options?.trigger_data === \"string\") {\n return options.trigger_data;\n }\n\n return undefined;\n}\n"],"names":["deduplicateItems","items","seen","values","acc","item","sortItems","a","b","mergeDateRangeParams","options","inserted_at_date_range","rest","dateRangeParams","isInclusive","startOperator","endOperator","getFormattedTriggerData"],"mappings":"AAEO,SAASA,EAAiBC,GAA+B;AAC9D,QAAMC,IAAgC,CAAC,GACjCC,IAAqB,CAAC;AAE5B,SAAOF,EAAM,OAAO,CAACG,GAAKC,MACpBH,EAAKG,EAAK,EAAE,IACPD,KAGJF,EAAAG,EAAK,EAAE,IAAI,IACT,CAAC,GAAGD,GAAKC,CAAI,IACnBF,CAAM;AACX;AAEO,SAASG,EAAUL,GAAmB;AAC3C,SAAOA,EAAM,KAAK,CAACM,GAAGC,MAElB,IAAI,KAAKA,EAAE,WAAW,EAAE,YAAY,IAAI,KAAKD,EAAE,WAAW,EAAE,QAAQ,CAEvE;AACH;AAEO,SAASE,EAAqBC,GAA4B;AAC/D,QAAM,EAAE,wBAAAC,GAAwB,GAAGC,EAAA,IAASF;AAE5C,MAAI,CAACC;AACI,WAAAC;AAGT,QAAMC,IAA0C,CAAC,GAG3CC,IAAcH,EAAuB,aAAa;AAGxD,MAAIA,EAAuB,OAAO;AAC1B,UAAAI,IAAgBD,IAAc,oBAAoB;AACxC,IAAAD,EAAAE,CAAa,IAAIJ,EAAuB;AAAA,EAAA;AAI1D,MAAIA,EAAuB,KAAK;AACxB,UAAAK,IAAcF,IAAc,oBAAoB;AACtC,IAAAD,EAAAG,CAAW,IAAIL,EAAuB;AAAA,EAAA;AAGxD,SAAO,EAAE,GAAGC,GAAM,GAAGC,EAAgB;AACvC;AAMO,SAASI,EAAwBP,GAA4B;AAE9D,MAAA,QAAOA,KAAA,gBAAAA,EAAS,iBAAiB;AAC5B,WAAA,KAAK,UAAUA,EAAQ,YAAY;AAIxC,MAAA,QAAOA,KAAA,gBAAAA,EAAS,iBAAiB;AACnC,WAAOA,EAAQ;AAInB;"}
@@ -0,0 +1,284 @@
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");
12
+ // 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"]);
17
+ // Original history methods to monkey patch, or restore in cleanups.
18
+ c(this, "pushStateFn");
19
+ c(this, "replaceStateFn");
20
+ // Define as an arrow func property to always bind this to the class instance.
21
+ c(this, "handleLocationChange", () => {
22
+ 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 })));
24
+ });
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: [],
29
+ queries: {},
30
+ location: r
31
+ });
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");
34
+ }
35
+ cleanup() {
36
+ this.unsubscribe(), this.removeEventListeners();
37
+ }
38
+ async fetch(e) {
39
+ 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" } }
46
+ }));
47
+ let i;
48
+ 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 }
57
+ }));
58
+ } catch (r) {
59
+ i = { status: "error", error: r }, this.store.setState((a) => ({
60
+ ...a,
61
+ queries: { ...a.queries, [s]: i }
62
+ }));
63
+ }
64
+ return i;
65
+ }
66
+ subscribe() {
67
+ if (!this.socket) return;
68
+ this.knock.failIfNotAuthenticated(), this.knock.log("[Guide] Subscribing to real time updates"), this.socket.isConnected() || this.socket.connect(), this.socketChannel && this.unsubscribe();
69
+ const e = { ...this.targetParams, user_id: this.knock.userId }, t = this.socket.channel(this.socketChannelTopic, e);
70
+ for (const s of this.socketEventTypes)
71
+ t.on(s, (n) => this.handleSocketEvent(n));
72
+ ["closed", "errored"].includes(t.state) && t.join(), this.socketChannel = t;
73
+ }
74
+ unsubscribe() {
75
+ if (this.socketChannel) {
76
+ this.knock.log("[Guide] Unsubscribing from real time updates");
77
+ for (const e of this.socketEventTypes)
78
+ this.socketChannel.off(e);
79
+ this.socketChannel.leave(), this.socketChannel = void 0;
80
+ }
81
+ }
82
+ handleSocketEvent(e) {
83
+ const { event: t, data: s } = e;
84
+ switch (t) {
85
+ case "guide.added":
86
+ return this.addGuide(e);
87
+ case "guide.updated":
88
+ return s.eligible ? this.replaceOrAddGuide(e) : this.removeGuide(e);
89
+ case "guide.removed":
90
+ return this.removeGuide(e);
91
+ default:
92
+ return;
93
+ }
94
+ }
95
+ //
96
+ // Store selector
97
+ //
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
+ });
116
+ }
117
+ //
118
+ // Engagement event handlers
119
+ //
120
+ // Make an optimistic update on the client side first, then send an engagement
121
+ // event to the backend.
122
+ //
123
+ async markAsSeen(e, t) {
124
+ this.knock.log(
125
+ `[Guide] Marking as seen (Guide key: ${e.key}, Step ref:${t.ref})`
126
+ );
127
+ const s = this.setStepMessageAttrs(e.key, t.ref, {
128
+ seen_at: (/* @__PURE__ */ new Date()).toISOString()
129
+ });
130
+ if (!s) return;
131
+ const n = {
132
+ ...this.buildEngagementEventBaseParams(e, s),
133
+ content: s.content,
134
+ data: this.targetParams.data,
135
+ tenant: this.targetParams.tenant
136
+ };
137
+ return this.knock.user.markGuideStepAs(
138
+ "seen",
139
+ n
140
+ ), s;
141
+ }
142
+ async markAsInteracted(e, t, s) {
143
+ this.knock.log(
144
+ `[Guide] Marking as interacted (Guide key: ${e.key}, Step ref:${t.ref})`
145
+ );
146
+ const n = (/* @__PURE__ */ new Date()).toISOString(), i = this.setStepMessageAttrs(e.key, t.ref, {
147
+ read_at: n,
148
+ interacted_at: n
149
+ });
150
+ if (!i) return;
151
+ const r = {
152
+ ...this.buildEngagementEventBaseParams(e, i),
153
+ metadata: s
154
+ };
155
+ return this.knock.user.markGuideStepAs("interacted", r), i;
156
+ }
157
+ async markAsArchived(e, t) {
158
+ this.knock.log(
159
+ `[Guide] Marking as archived (Guide key: ${e.key}, Step ref:${t.ref})`
160
+ );
161
+ const s = this.setStepMessageAttrs(e.key, t.ref, {
162
+ archived_at: (/* @__PURE__ */ new Date()).toISOString()
163
+ });
164
+ if (!s) return;
165
+ const n = this.buildEngagementEventBaseParams(e, s);
166
+ return this.knock.user.markGuideStepAs(
167
+ "archived",
168
+ n
169
+ ), s;
170
+ }
171
+ //
172
+ // Helpers
173
+ //
174
+ localCopy(e) {
175
+ const t = this, s = { ...e };
176
+ return s.steps = e.steps.map(({ message: n, ...i }) => {
177
+ const r = {
178
+ ...i,
179
+ message: { ...n },
180
+ markAsSeen() {
181
+ if (!this.message.seen_at)
182
+ return t.markAsSeen(s, this);
183
+ },
184
+ markAsInteracted({ metadata: a } = {}) {
185
+ return t.markAsInteracted(s, this, a);
186
+ },
187
+ markAsArchived() {
188
+ if (!this.message.archived_at)
189
+ return t.markAsArchived(s, this);
190
+ }
191
+ };
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 })
196
+ })), s;
197
+ }
198
+ buildQueryParams(e = {}) {
199
+ const t = { ...this.targetParams, ...e };
200
+ let s = Object.fromEntries(
201
+ Object.entries(t).filter(
202
+ ([n, i]) => i != null
203
+ )
204
+ );
205
+ return s = s.data ? { ...s, data: JSON.stringify(s.data) } : s, s;
206
+ }
207
+ formatQueryKey(e) {
208
+ const s = Object.keys(e).sort().map(
209
+ (i) => `${encodeURIComponent(i)}=${encodeURIComponent(e[i])}`
210
+ ).join("&"), n = g(this.knock.userId);
211
+ return s ? `${n}?${s}` : n;
212
+ }
213
+ 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;
223
+ }
224
+ buildEngagementEventBaseParams(e, t) {
225
+ return {
226
+ message_id: t.message.id,
227
+ channel_id: e.channel_id,
228
+ guide_key: e.key,
229
+ guide_id: e.id,
230
+ guide_step_ref: t.ref
231
+ };
232
+ }
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 }) {
238
+ const t = this.localCopy(e.guide);
239
+ 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
+ };
246
+ });
247
+ }
248
+ 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 };
252
+ });
253
+ }
254
+ listenForLocationChangesFromWindow() {
255
+ if (window != null && window.history) {
256
+ window.addEventListener("popstate", this.handleLocationChange), window.addEventListener("hashchange", this.handleLocationChange);
257
+ const e = window.history.pushState, t = window.history.replaceState;
258
+ window.history.pushState = new Proxy(e, {
259
+ apply: (s, n, i) => {
260
+ Reflect.apply(s, n, i), setTimeout(() => {
261
+ this.handleLocationChange();
262
+ }, 0);
263
+ }
264
+ }), window.history.replaceState = new Proxy(t, {
265
+ apply: (s, n, i) => {
266
+ Reflect.apply(s, n, i), setTimeout(() => {
267
+ this.handleLocationChange();
268
+ }, 0);
269
+ }
270
+ }), this.pushStateFn = e, this.replaceStateFn = t;
271
+ } else
272
+ this.knock.log(
273
+ "[Guide] Unable to access the `window.history` object to detect location changes"
274
+ );
275
+ }
276
+ removeEventListeners() {
277
+ window.removeEventListener("popstate", this.handleLocationChange), window.removeEventListener("hashchange", this.handleLocationChange), this.pushStateFn && (window.history.pushState = this.pushStateFn, this.pushStateFn = void 0), this.replaceStateFn && (window.history.replaceState = this.replaceStateFn, this.replaceStateFn = void 0);
278
+ }
279
+ }
280
+ export {
281
+ v as KnockGuideClient,
282
+ g as guidesApiRootPath
283
+ };
284
+ //# sourceMappingURL=client.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.mjs","sources":["../../../../src/clients/guide/client.ts"],"sourcesContent":["import { GenericData } from \"@knocklabs/types\";\nimport { Store } from \"@tanstack/store\";\nimport { Channel, Socket } from \"phoenix\";\nimport { URLPattern } from \"urlpattern-polyfill\";\n\nimport Knock from \"../../knock\";\n\nconst sortGuides = (guides: KnockGuide[]) => {\n return [...guides].sort(\n (a, b) =>\n b.priority - a.priority ||\n new Date(b.inserted_at).getTime() - new Date(a.inserted_at).getTime(),\n );\n};\n\n//\n// Guides API (via User client)\n//\n\nexport const guidesApiRootPath = (userId: string | undefined | null) =>\n `/v1/users/${userId}/guides`;\n\ninterface StepMessageState {\n id: string;\n seen_at: string | null;\n read_at: string | null;\n interacted_at: string | null;\n archived_at: string | null;\n link_clicked_at: string | null;\n}\n\ninterface GuideStepData {\n ref: string;\n schema_key: string;\n schema_semver: string;\n schema_variant_key: string;\n message: StepMessageState;\n // eslint-disable-next-line\n content: any;\n}\n\ninterface GuideActivationLocationRuleData {\n directive: \"allow\" | \"block\";\n pathname: string;\n}\n\ninterface GuideData {\n __typename: \"Guide\";\n channel_id: string;\n id: string;\n key: string;\n priority: number;\n type: string;\n semver: string;\n steps: GuideStepData[];\n activation_location_rules: GuideActivationLocationRuleData[];\n inserted_at: string;\n updated_at: string;\n}\n\nexport interface KnockGuideStep extends GuideStepData {\n markAsSeen: () => void;\n markAsInteracted: (params?: { metadata?: GenericData }) => void;\n markAsArchived: () => void;\n}\n\ninterface KnockGuideActivationLocationRule\n extends GuideActivationLocationRuleData {\n pattern: URLPattern;\n}\n\nexport interface KnockGuide extends GuideData {\n steps: KnockGuideStep[];\n activation_location_rules: KnockGuideActivationLocationRule[];\n}\n\ntype GetGuidesQueryParams = {\n data?: string;\n tenant?: string;\n type?: string;\n};\n\ntype GetGuidesResponse = {\n entries: GuideData[];\n};\n\nexport type GuideEngagementEventBaseParams = {\n // Base params required for all engagement update events\n message_id: string;\n channel_id: string;\n guide_key: string;\n guide_id: string;\n guide_step_ref: string;\n};\n\ntype MarkAsSeenParams = GuideEngagementEventBaseParams & {\n // Rendered step content seen by the recipient\n content: GenericData;\n // Target params\n data?: GenericData;\n tenant?: string;\n};\ntype MarkAsInteractedParams = GuideEngagementEventBaseParams;\ntype MarkAsArchivedParams = GuideEngagementEventBaseParams;\n\ntype MarkGuideAsResponse = {\n status: \"ok\";\n};\n\ntype SocketEventType = \"guide.added\" | \"guide.updated\" | \"guide.removed\";\n\ntype SocketEventPayload<E extends SocketEventType, D> = {\n topic: string;\n event: E;\n data: D;\n};\n\ntype GuideAddedEvent = SocketEventPayload<\n \"guide.added\",\n { guide: GuideData; eligible: true }\n>;\n\ntype GuideUpdatedEvent = SocketEventPayload<\n \"guide.updated\",\n { guide: GuideData; eligible: boolean }\n>;\n\ntype GuideRemovedEvent = SocketEventPayload<\n \"guide.removed\",\n { guide: Pick<GuideData, \"key\"> }\n>;\n\ntype GuideSocketEvent = GuideAddedEvent | GuideUpdatedEvent | GuideRemovedEvent;\n\n//\n// Guides client\n//\n\ntype QueryKey = string;\n\ntype QueryStatus = {\n status: \"loading\" | \"ok\" | \"error\";\n error?: Error;\n};\n\ntype StoreState = {\n guides: KnockGuide[];\n queries: Record<QueryKey, QueryStatus>;\n location: string | undefined;\n};\n\ntype QueryFilterParams = Pick<GetGuidesQueryParams, \"type\">;\n\nexport type SelectFilterParams = {\n key?: string;\n type?: string;\n};\n\nexport type TargetParams = {\n data?: GenericData | undefined;\n tenant?: string | undefined;\n};\n\ntype ConstructorOpts = {\n trackLocationFromWindow?: boolean;\n};\n\nexport class KnockGuideClient {\n public store: Store<StoreState, (state: StoreState) => StoreState>;\n\n // Phoenix channels for real time guide updates over websocket\n private socket: Socket | undefined;\n private socketChannel: Channel | undefined;\n private socketChannelTopic: string;\n private socketEventTypes = [\"guide.added\", \"guide.updated\", \"guide.removed\"];\n\n // Original history methods to monkey patch, or restore in cleanups.\n private pushStateFn: History[\"pushState\"] | undefined;\n private replaceStateFn: History[\"replaceState\"] | undefined;\n\n constructor(\n readonly knock: Knock,\n readonly channelId: string,\n readonly targetParams: TargetParams = {},\n readonly options: ConstructorOpts = {},\n ) {\n const { trackLocationFromWindow = true } = options;\n\n const location = trackLocationFromWindow\n ? window?.location.href\n : undefined;\n\n this.store = new Store<StoreState>({\n guides: [],\n queries: {},\n location,\n });\n\n // In server environments we might not have a socket connection.\n const { socket: maybeSocket } = this.knock.client();\n this.socket = maybeSocket;\n this.socketChannelTopic = `guides:${channelId}`;\n\n if (trackLocationFromWindow) {\n this.listenForLocationChangesFromWindow();\n }\n\n this.knock.log(\"[Guide] Initialized a guide client\");\n }\n\n cleanup() {\n this.unsubscribe();\n this.removeEventListeners();\n }\n\n async fetch(opts?: { filters?: QueryFilterParams }) {\n this.knock.failIfNotAuthenticated();\n this.knock.log(\"[Guide] Loading all eligible guides\");\n\n const queryParams = this.buildQueryParams(opts?.filters);\n const queryKey = this.formatQueryKey(queryParams);\n\n // If already fetched before, then noop.\n const maybeQueryStatus = this.store.state.queries[queryKey];\n if (maybeQueryStatus) {\n return maybeQueryStatus;\n }\n\n // Mark this query status as loading.\n this.store.setState((state) => ({\n ...state,\n queries: { ...state.queries, [queryKey]: { status: \"loading\" } },\n }));\n\n let queryStatus: QueryStatus;\n try {\n const data = await this.knock.user.getGuides<\n GetGuidesQueryParams,\n GetGuidesResponse\n >(this.channelId, queryParams);\n queryStatus = { status: \"ok\" };\n\n this.store.setState((state) => ({\n ...state,\n // For now assume a single fetch to get all eligible guides. When/if\n // we implement incremental loads, then this will need to be a merge\n // and sort operation.\n guides: data.entries.map((g) => this.localCopy(g)),\n queries: { ...state.queries, [queryKey]: queryStatus },\n }));\n } catch (e) {\n queryStatus = { status: \"error\", error: e as Error };\n\n this.store.setState((state) => ({\n ...state,\n queries: { ...state.queries, [queryKey]: queryStatus },\n }));\n }\n\n return queryStatus;\n }\n\n subscribe() {\n if (!this.socket) return;\n this.knock.failIfNotAuthenticated();\n this.knock.log(\"[Guide] Subscribing to real time updates\");\n\n // Ensure a live socket connection if not yet connected.\n if (!this.socket.isConnected()) {\n this.socket.connect();\n }\n\n // If there's an existing connected channel, then disconnect.\n if (this.socketChannel) {\n this.unsubscribe();\n }\n\n // Join the channel topic and subscribe to supported events.\n const params = { ...this.targetParams, user_id: this.knock.userId };\n const newChannel = this.socket.channel(this.socketChannelTopic, params);\n\n for (const eventType of this.socketEventTypes) {\n newChannel.on(eventType, (payload) => this.handleSocketEvent(payload));\n }\n\n if ([\"closed\", \"errored\"].includes(newChannel.state)) {\n newChannel.join();\n }\n\n // Track the joined channel.\n this.socketChannel = newChannel;\n }\n\n unsubscribe() {\n if (!this.socketChannel) return;\n this.knock.log(\"[Guide] Unsubscribing from real time updates\");\n\n // Unsubscribe from the socket events and leave the channel.\n for (const eventType of this.socketEventTypes) {\n this.socketChannel.off(eventType);\n }\n this.socketChannel.leave();\n\n // Unset the channel.\n this.socketChannel = undefined;\n }\n\n private handleSocketEvent(payload: GuideSocketEvent) {\n const { event, data } = payload;\n\n switch (event) {\n case \"guide.added\":\n return this.addGuide(payload);\n\n case \"guide.updated\":\n return data.eligible\n ? this.replaceOrAddGuide(payload)\n : this.removeGuide(payload);\n\n case \"guide.removed\":\n return this.removeGuide(payload);\n\n default:\n return;\n }\n }\n\n //\n // Store selector\n //\n\n select(state: StoreState, filters: SelectFilterParams = {}) {\n return state.guides.filter((guide) => {\n if (filters.type && filters.type !== guide.type) {\n return false;\n }\n\n if (filters.key && filters.key !== guide.key) {\n return false;\n }\n\n const locationRules = guide.activation_location_rules || [];\n\n if (locationRules.length > 0 && state.location) {\n const allowed = locationRules.reduce<boolean | undefined>(\n (acc, rule) => {\n // Any matched block rule prevails so no need to evaluate further\n // as soon as there is one.\n if (acc === false) return false;\n\n // At this point we either have a matched allow rule (acc is true),\n // or no matched rule found yet (acc is undefined).\n\n switch (rule.directive) {\n case \"allow\": {\n // No need to evaluate more allow rules once we matched one\n // since any matched allowed rule means allow.\n if (acc === true) return true;\n\n const matched = rule.pattern.test(state.location);\n return matched ? true : undefined;\n }\n\n case \"block\": {\n // Always test block rules (unless already matched to block)\n // because they'd prevail over matched allow rules.\n const matched = rule.pattern.test(state.location);\n return matched ? false : acc;\n }\n }\n },\n undefined,\n );\n\n if (!allowed) return false;\n }\n\n return true;\n });\n }\n\n //\n // Engagement event handlers\n //\n // Make an optimistic update on the client side first, then send an engagement\n // event to the backend.\n //\n\n async markAsSeen(guide: GuideData, step: GuideStepData) {\n this.knock.log(\n `[Guide] Marking as seen (Guide key: ${guide.key}, Step ref:${step.ref})`,\n );\n\n const updatedStep = this.setStepMessageAttrs(guide.key, step.ref, {\n seen_at: new Date().toISOString(),\n });\n if (!updatedStep) return;\n\n const params = {\n ...this.buildEngagementEventBaseParams(guide, updatedStep),\n content: updatedStep.content,\n data: this.targetParams.data,\n tenant: this.targetParams.tenant,\n };\n\n this.knock.user.markGuideStepAs<MarkAsSeenParams, MarkGuideAsResponse>(\n \"seen\",\n params,\n );\n\n return updatedStep;\n }\n\n async markAsInteracted(\n guide: GuideData,\n step: GuideStepData,\n metadata?: GenericData,\n ) {\n this.knock.log(\n `[Guide] Marking as interacted (Guide key: ${guide.key}, Step ref:${step.ref})`,\n );\n\n const ts = new Date().toISOString();\n const updatedStep = this.setStepMessageAttrs(guide.key, step.ref, {\n read_at: ts,\n interacted_at: ts,\n });\n if (!updatedStep) return;\n\n const params = {\n ...this.buildEngagementEventBaseParams(guide, updatedStep),\n metadata,\n };\n\n this.knock.user.markGuideStepAs<\n MarkAsInteractedParams,\n MarkGuideAsResponse\n >(\"interacted\", params);\n\n return updatedStep;\n }\n\n async markAsArchived(guide: GuideData, step: GuideStepData) {\n this.knock.log(\n `[Guide] Marking as archived (Guide key: ${guide.key}, Step ref:${step.ref})`,\n );\n\n const updatedStep = this.setStepMessageAttrs(guide.key, step.ref, {\n archived_at: new Date().toISOString(),\n });\n if (!updatedStep) return;\n\n const params = this.buildEngagementEventBaseParams(guide, updatedStep);\n\n this.knock.user.markGuideStepAs<MarkAsArchivedParams, MarkGuideAsResponse>(\n \"archived\",\n params,\n );\n\n return updatedStep;\n }\n\n //\n // Helpers\n //\n\n private localCopy(remoteGuide: GuideData) {\n // eslint-disable-next-line @typescript-eslint/no-this-alias\n const self = this;\n\n // Build a local copy with helper methods added.\n const localGuide = { ...remoteGuide };\n\n localGuide.steps = remoteGuide.steps.map(({ message, ...rest }) => {\n const localStep = {\n ...rest,\n message: { ...message },\n markAsSeen() {\n // Send a seen event if it has not been previously seen.\n if (this.message.seen_at) return;\n return self.markAsSeen(localGuide, this);\n },\n markAsInteracted({ metadata }: { metadata?: GenericData } = {}) {\n // Always send an interaction event through.\n return self.markAsInteracted(localGuide, this, metadata);\n },\n markAsArchived() {\n // Send an archived event if it has not been previously archived.\n if (this.message.archived_at) return;\n return self.markAsArchived(localGuide, this);\n },\n };\n\n // Bind all engagement action handler methods to the local step object so\n // they can operate on itself.\n localStep.markAsSeen = localStep.markAsSeen.bind(localStep);\n localStep.markAsInteracted = localStep.markAsInteracted.bind(localStep);\n localStep.markAsArchived = localStep.markAsArchived.bind(localStep);\n\n return localStep;\n });\n\n localGuide.activation_location_rules =\n remoteGuide.activation_location_rules.map((rule) => {\n return {\n ...rule,\n pattern: new URLPattern({ pathname: rule.pathname }),\n };\n });\n\n return localGuide as KnockGuide;\n }\n\n private buildQueryParams(filterParams: QueryFilterParams = {}) {\n // Combine the target params with the given filter params.\n const combinedParams = { ...this.targetParams, ...filterParams };\n\n // Prune out any keys that have an undefined or null value.\n let params = Object.fromEntries(\n Object.entries(combinedParams).filter(\n ([_k, v]) => v !== undefined && v !== null,\n ),\n );\n\n // Encode target data as a JSON string, if provided.\n params = params.data\n ? { ...params, data: JSON.stringify(params.data) }\n : params;\n\n return params as GetGuidesQueryParams;\n }\n\n private formatQueryKey(queryParams: GenericData) {\n const sortedKeys = Object.keys(queryParams).sort();\n\n const queryStr = sortedKeys\n .map(\n (key) =>\n `${encodeURIComponent(key)}=${encodeURIComponent(queryParams[key])}`,\n )\n .join(\"&\");\n\n const basePath = guidesApiRootPath(this.knock.userId);\n return queryStr ? `${basePath}?${queryStr}` : basePath;\n }\n\n private setStepMessageAttrs(\n guideKey: string,\n stepRef: string,\n attrs: Partial<StepMessageState>,\n ) {\n let updatedStep: KnockGuideStep | undefined;\n\n this.store.setState((state) => {\n const guides = state.guides.map((guide) => {\n if (guide.key !== guideKey) return guide;\n\n const steps = guide.steps.map((step) => {\n if (step.ref !== stepRef) return step;\n\n // Mutate in place and maintain the same obj ref so to make it easier\n // to use in hook deps.\n step.message = { ...step.message, ...attrs };\n updatedStep = step;\n\n return step;\n });\n return { ...guide, steps };\n });\n return { ...state, guides };\n });\n\n return updatedStep;\n }\n\n private buildEngagementEventBaseParams(\n guide: GuideData,\n step: GuideStepData,\n ) {\n return {\n message_id: step.message.id,\n channel_id: guide.channel_id,\n guide_key: guide.key,\n guide_id: guide.id,\n guide_step_ref: step.ref,\n };\n }\n\n private addGuide({ data }: GuideAddedEvent) {\n const guide = this.localCopy(data.guide);\n\n this.store.setState((state) => {\n return { ...state, guides: sortGuides([...state.guides, guide]) };\n });\n }\n\n private replaceOrAddGuide({ data }: GuideUpdatedEvent) {\n const guide = this.localCopy(data.guide);\n\n this.store.setState((state) => {\n let replaced = false;\n\n const guides = state.guides.map((g) => {\n if (g.key !== guide.key) return g;\n replaced = true;\n return guide;\n });\n\n return {\n ...state,\n guides: replaced ? sortGuides(guides) : sortGuides([...guides, guide]),\n };\n });\n }\n\n private removeGuide({ data }: GuideUpdatedEvent | GuideRemovedEvent) {\n this.store.setState((state) => {\n const guides = state.guides.filter((g) => g.key !== data.guide.key);\n return { ...state, guides };\n });\n }\n\n // Define as an arrow func property to always bind this to the class instance.\n private handleLocationChange = () => {\n const href = window.location.href;\n if (this.store.state.location === href) return;\n\n this.knock.log(`[Guide] Handle Location change: ${href}`);\n\n this.store.setState((state) => ({ ...state, location: href }));\n };\n\n private listenForLocationChangesFromWindow() {\n if (window?.history) {\n // 1. Listen for browser back/forward button clicks.\n window.addEventListener(\"popstate\", this.handleLocationChange);\n\n // 2. Listen for hash changes in case it's used for routing.\n window.addEventListener(\"hashchange\", this.handleLocationChange);\n\n // 3. Monkey-patch history methods to catch programmatic navigation.\n const pushStateFn = window.history.pushState;\n const replaceStateFn = window.history.replaceState;\n\n // Use setTimeout to allow the browser state to potentially settle.\n window.history.pushState = new Proxy(pushStateFn, {\n apply: (target, history, args) => {\n Reflect.apply(target, history, args);\n setTimeout(() => {\n this.handleLocationChange();\n }, 0);\n },\n });\n window.history.replaceState = new Proxy(replaceStateFn, {\n apply: (target, history, args) => {\n Reflect.apply(target, history, args);\n setTimeout(() => {\n this.handleLocationChange();\n }, 0);\n },\n });\n\n // 4. Keep refs to the original handlers so we can restore during cleanup.\n this.pushStateFn = pushStateFn;\n this.replaceStateFn = replaceStateFn;\n } else {\n this.knock.log(\n \"[Guide] Unable to access the `window.history` object to detect location changes\",\n );\n }\n }\n\n private removeEventListeners() {\n window.removeEventListener(\"popstate\", this.handleLocationChange);\n window.removeEventListener(\"hashchange\", this.handleLocationChange);\n\n if (this.pushStateFn) {\n window.history.pushState = this.pushStateFn;\n this.pushStateFn = undefined;\n }\n if (this.replaceStateFn) {\n window.history.replaceState = this.replaceStateFn;\n this.replaceStateFn = undefined;\n }\n }\n}\n"],"names":["sortGuides","guides","a","b","guidesApiRootPath","userId","KnockGuideClient","knock","channelId","targetParams","options","__publicField","href","state","trackLocationFromWindow","location","Store","maybeSocket","opts","queryParams","queryKey","maybeQueryStatus","queryStatus","data","g","e","params","newChannel","eventType","payload","event","filters","guide","locationRules","acc","rule","step","updatedStep","metadata","ts","remoteGuide","self","localGuide","message","rest","localStep","URLPattern","filterParams","combinedParams","_k","v","queryStr","key","basePath","guideKey","stepRef","attrs","steps","replaced","pushStateFn","replaceStateFn","target","history","args"],"mappings":"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"}