@knocklabs/client 0.15.2 → 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.
- package/CHANGELOG.md +7 -0
- package/dist/cjs/api.js +1 -1
- package/dist/cjs/clients/guide/client.js +1 -1
- package/dist/cjs/clients/guide/client.js.map +1 -1
- package/dist/cjs/clients/guide/helpers.js +2 -0
- package/dist/cjs/clients/guide/helpers.js.map +1 -0
- package/dist/cjs/clients/users/index.js.map +1 -1
- package/dist/esm/api.mjs +1 -1
- package/dist/esm/clients/guide/client.mjs +273 -117
- package/dist/esm/clients/guide/client.mjs.map +1 -1
- package/dist/esm/clients/guide/helpers.mjs +43 -0
- package/dist/esm/clients/guide/helpers.mjs.map +1 -0
- package/dist/esm/clients/users/index.mjs.map +1 -1
- package/dist/types/clients/guide/client.d.ts +17 -86
- package/dist/types/clients/guide/client.d.ts.map +1 -1
- package/dist/types/clients/guide/helpers.d.ts +16 -0
- package/dist/types/clients/guide/helpers.d.ts.map +1 -0
- package/dist/types/clients/guide/index.d.ts +1 -1
- package/dist/types/clients/guide/index.d.ts.map +1 -1
- package/dist/types/clients/guide/types.d.ts +147 -0
- package/dist/types/clients/guide/types.d.ts.map +1 -0
- package/dist/types/clients/users/index.d.ts +1 -1
- package/dist/types/clients/users/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/clients/guide/client.ts +466 -225
- package/src/clients/guide/helpers.ts +98 -0
- package/src/clients/guide/index.ts +1 -1
- package/src/clients/guide/types.ts +206 -0
- 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
|
+
};
|
|
@@ -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
|
-
|
|
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,
|