@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
@@ -0,0 +1,98 @@
1
+ import {
2
+ GuideData,
3
+ GuideGroupData,
4
+ KnockGuide,
5
+ SelectFilterParams,
6
+ } from "./types";
7
+
8
+ // Extends the map class to allow having metadata on it, which is used to record
9
+ // the guide group context for the selection result (though currently only a
10
+ // default global group is supported).
11
+ export class SelectionResult<K = number, V = KnockGuide> extends Map<K, V> {
12
+ metadata: { guideGroup: GuideGroupData } | undefined;
13
+
14
+ constructor() {
15
+ super();
16
+ }
17
+ }
18
+
19
+ export const formatFilters = (filters: SelectFilterParams = {}) => {
20
+ return [
21
+ filters.key && `key=${filters.key}`,
22
+ filters.type && `type=${filters.type}`,
23
+ ]
24
+ .filter((x) => x)
25
+ .join(", ");
26
+ };
27
+
28
+ export const byKey = <T extends { key: string }>(items: T[]) => {
29
+ return items.reduce((acc, item) => ({ ...acc, [item.key]: item }), {});
30
+ };
31
+
32
+ const sortGuides = <T extends GuideData>(guides: T[]) => {
33
+ return [...guides].sort(
34
+ (a, b) =>
35
+ new Date(a.inserted_at).getTime() - new Date(b.inserted_at).getTime(),
36
+ );
37
+ };
38
+
39
+ // Default global guide group key.
40
+ export const DEFAULT_GROUP_KEY = "default";
41
+
42
+ // Prefixed with a special char $ to distinguish from an actual default group.
43
+ const MOCK_DEFAULT_GROUP_KEY = "$default";
44
+
45
+ // Build a notional default group to fall back on in case there is none, only
46
+ // for safety as there should always be a default guide group created.
47
+ export const mockDefaultGroup = (entries: GuideData[] = []) => {
48
+ const now = new Date();
49
+
50
+ return {
51
+ __typename: "GuideGroup",
52
+ key: MOCK_DEFAULT_GROUP_KEY,
53
+ display_sequence: sortGuides(entries).map((g) => g.key),
54
+ display_interval: null,
55
+ inserted_at: now.toISOString(),
56
+ updated_at: now.toISOString(),
57
+ } as GuideGroupData;
58
+ };
59
+
60
+ export const findDefaultGroup = (guideGroups: GuideGroupData[]) =>
61
+ guideGroups.find(
62
+ (group) =>
63
+ group.key === DEFAULT_GROUP_KEY || group.key === MOCK_DEFAULT_GROUP_KEY,
64
+ );
65
+
66
+ // Checks whether we are currently throttled (inside a "throttle window").
67
+ // A throttle window opens when a user dismisses (archives) a guide, and lasts
68
+ // for the configured display interval of the guide group used (currently only
69
+ // the default global group).
70
+ export const checkIfThrottled = (
71
+ throttleWindowStartedAtTs: string,
72
+ windowDurationInSeconds: number,
73
+ ) => {
74
+ // 1. Parse the given timestamp string into a Date object.
75
+ // Date.parse() handles ISO 8601 strings correctly and returns milliseconds
76
+ // since epoch. This inherently handles timezones by converting everything to
77
+ // a universal time representation (UTC).
78
+ const throttleWindowStartDate = new Date(throttleWindowStartedAtTs);
79
+
80
+ // Check if the given throttle window start timestamp string was valid, and
81
+ // if not disregard.
82
+ if (isNaN(throttleWindowStartDate.getTime())) {
83
+ return false;
84
+ }
85
+
86
+ // 2. Calculate the throttle window end time in milliseconds by adding the
87
+ // duration to the throttle window start time.
88
+ const throttleWindowEndInMilliseconds =
89
+ throttleWindowStartDate.getTime() + windowDurationInSeconds * 1000;
90
+
91
+ // 3. Get the current timestamp in milliseconds since epoch.
92
+ const currentTimeInMilliseconds = new Date().getTime();
93
+
94
+ // 4. Compare the current timestamp with the calculated future timestamp.
95
+ // Both are in milliseconds since epoch (UTC), so direct comparison is
96
+ // accurate regardless of local timezones.
97
+ return currentTimeInMilliseconds <= throttleWindowEndInMilliseconds;
98
+ };
@@ -4,4 +4,4 @@ export type {
4
4
  KnockGuideStep,
5
5
  TargetParams as KnockGuideTargetParams,
6
6
  SelectFilterParams as KnockGuideFilterParams,
7
- } from "./client";
7
+ } from "./types";
@@ -0,0 +1,206 @@
1
+ import { GenericData } from "@knocklabs/types";
2
+
3
+ //
4
+ // Fetch guides API
5
+ //
6
+
7
+ export interface StepMessageState {
8
+ id: string;
9
+ seen_at: string | null;
10
+ read_at: string | null;
11
+ interacted_at: string | null;
12
+ archived_at: string | null;
13
+ link_clicked_at: string | null;
14
+ }
15
+
16
+ export interface GuideStepData {
17
+ ref: string;
18
+ schema_key: string;
19
+ schema_semver: string;
20
+ schema_variant_key: string;
21
+ message: StepMessageState;
22
+ // eslint-disable-next-line
23
+ content: any;
24
+ }
25
+
26
+ interface GuideActivationLocationRuleData {
27
+ directive: "allow" | "block";
28
+ pathname: string;
29
+ }
30
+
31
+ export interface GuideData {
32
+ __typename: "Guide";
33
+ channel_id: string;
34
+ id: string;
35
+ key: string;
36
+ type: string;
37
+ semver: string;
38
+ steps: GuideStepData[];
39
+ activation_location_rules: GuideActivationLocationRuleData[];
40
+ bypass_global_group_limit: boolean;
41
+ inserted_at: string;
42
+ updated_at: string;
43
+ }
44
+
45
+ export interface GuideGroupData {
46
+ __typename: "GuideGroup";
47
+ key: string;
48
+ display_sequence: Array<GuideData["key"]>;
49
+ display_sequence_unthrottled: Array<GuideData["key"]> | null;
50
+ display_sequence_throttled: Array<GuideData["key"]> | null;
51
+ display_interval: number | null;
52
+ inserted_at: string;
53
+ updated_at: string;
54
+ }
55
+
56
+ export type GetGuidesQueryParams = {
57
+ data?: string;
58
+ tenant?: string;
59
+ type?: string;
60
+ };
61
+
62
+ export type GetGuidesResponse = {
63
+ entries: GuideData[];
64
+ guide_groups: GuideGroupData[];
65
+ guide_group_display_logs: Record<GuideGroupData["key"], string>;
66
+ };
67
+
68
+ //
69
+ // Engagement actions API
70
+ //
71
+
72
+ export type GuideEngagementEventBaseParams = {
73
+ // Base params required for all engagement update events
74
+ message_id: string;
75
+ channel_id: string;
76
+ guide_key: string;
77
+ guide_id: string;
78
+ guide_step_ref: string;
79
+ };
80
+
81
+ export type MarkAsSeenParams = GuideEngagementEventBaseParams & {
82
+ // Rendered step content seen by the recipient
83
+ content: GenericData;
84
+ // Target params
85
+ data?: GenericData;
86
+ tenant?: string;
87
+ };
88
+ export type MarkAsInteractedParams = GuideEngagementEventBaseParams;
89
+ export type MarkAsArchivedParams = GuideEngagementEventBaseParams & {
90
+ unthrottled?: boolean;
91
+ };
92
+
93
+ export type MarkGuideAsResponse = {
94
+ status: "ok";
95
+ };
96
+
97
+ //
98
+ // Socket events
99
+ //
100
+
101
+ type SocketEventType =
102
+ | "guide.added"
103
+ | "guide.updated"
104
+ | "guide.removed"
105
+ | "guide_group.added"
106
+ | "guide_group.updated";
107
+
108
+ type SocketEventPayload<E extends SocketEventType, D> = {
109
+ topic: string;
110
+ event: E;
111
+ data: D;
112
+ };
113
+
114
+ export type GuideAddedEvent = SocketEventPayload<
115
+ "guide.added",
116
+ { guide: GuideData; eligible: true }
117
+ >;
118
+
119
+ export type GuideUpdatedEvent = SocketEventPayload<
120
+ "guide.updated",
121
+ { guide: GuideData; eligible: boolean }
122
+ >;
123
+
124
+ export type GuideRemovedEvent = SocketEventPayload<
125
+ "guide.removed",
126
+ { guide: Pick<GuideData, "key"> }
127
+ >;
128
+
129
+ export type GuideGroupAddedEvent = SocketEventPayload<
130
+ "guide_group.added",
131
+ { guide_group: GuideGroupData }
132
+ >;
133
+
134
+ export type GuideGroupUpdatedEvent = SocketEventPayload<
135
+ "guide_group.updated",
136
+ { guide_group: GuideGroupData }
137
+ >;
138
+
139
+ export type GuideSocketEvent =
140
+ | GuideAddedEvent
141
+ | GuideUpdatedEvent
142
+ | GuideRemovedEvent
143
+ | GuideGroupAddedEvent
144
+ | GuideGroupUpdatedEvent;
145
+
146
+ //
147
+ // Guide client
148
+ //
149
+
150
+ export interface KnockGuideStep extends GuideStepData {
151
+ markAsSeen: () => void;
152
+ markAsInteracted: (params?: { metadata?: GenericData }) => void;
153
+ markAsArchived: () => void;
154
+ }
155
+
156
+ interface KnockGuideActivationLocationRule
157
+ extends GuideActivationLocationRuleData {
158
+ pattern: URLPattern;
159
+ }
160
+
161
+ export interface KnockGuide extends GuideData {
162
+ steps: KnockGuideStep[];
163
+ activation_location_rules: KnockGuideActivationLocationRule[];
164
+ getStep: () => KnockGuideStep | undefined;
165
+ }
166
+
167
+ type QueryKey = string;
168
+
169
+ export type QueryStatus = {
170
+ status: "loading" | "ok" | "error";
171
+ error?: Error;
172
+ };
173
+
174
+ export type StoreState = {
175
+ guideGroups: GuideGroupData[];
176
+ guideGroupDisplayLogs: Record<GuideGroupData["key"], string>;
177
+ guides: Record<KnockGuide["key"], KnockGuide>;
178
+ queries: Record<QueryKey, QueryStatus>;
179
+ location: string | undefined;
180
+ counter: number;
181
+ };
182
+
183
+ export type QueryFilterParams = Pick<GetGuidesQueryParams, "type">;
184
+
185
+ export type SelectFilterParams = {
186
+ key?: string;
187
+ type?: string;
188
+ };
189
+
190
+ export type TargetParams = {
191
+ data?: GenericData | undefined;
192
+ tenant?: string | undefined;
193
+ };
194
+
195
+ export type ConstructorOpts = {
196
+ trackLocationFromWindow?: boolean;
197
+ orderResolutionDuration?: number;
198
+ throttleCheckInterval?: number;
199
+ };
200
+
201
+ export type GroupStage = {
202
+ status: "open" | "closed" | "patch";
203
+ ordered: Array<KnockGuide["key"]>;
204
+ resolved?: KnockGuide["key"];
205
+ timeoutId: ReturnType<typeof setTimeout> | null;
206
+ };
@@ -3,10 +3,8 @@ import { GenericData } from "@knocklabs/types";
3
3
  import { ApiResponse } from "../../api";
4
4
  import { ChannelData, User } from "../../interfaces";
5
5
  import Knock from "../../knock";
6
- import {
7
- GuideEngagementEventBaseParams,
8
- guidesApiRootPath,
9
- } from "../guide/client";
6
+ import { guidesApiRootPath } from "../guide/client";
7
+ import { GuideEngagementEventBaseParams } from "../guide/types";
10
8
  import {
11
9
  GetPreferencesOptions,
12
10
  PreferenceOptions,