@getuserfeedback/protocol 0.6.1 → 0.7.2
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/dist/app-event.d.ts +659 -17
- package/dist/app-event.d.ts.map +1 -1
- package/dist/app-event.js +71 -8
- package/dist/flow-assignments.d.ts +4 -4
- package/dist/host/constants.d.ts +2 -2
- package/dist/host/constants.d.ts.map +1 -1
- package/dist/host/constants.js +3 -1
- package/dist/host/sdk-types.d.ts +73 -28
- package/dist/host/sdk-types.d.ts.map +1 -1
- package/dist/host/sdk.d.ts.map +1 -1
- package/dist/identity-type.d.ts +4 -2
- package/dist/identity-type.d.ts.map +1 -1
- package/dist/identity-type.js +4 -2
- package/dist/index.d.ts +2 -86
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -6
- package/dist/widget-commands.d.ts +102 -3
- package/dist/widget-commands.d.ts.map +1 -1
- package/dist/widget-commands.js +5 -0
- package/dist/widget-config.d.ts +5 -5
- package/package.json +22 -2
- package/src/app-event.ts +211 -0
- package/src/client-meta.ts +25 -0
- package/src/defaults.ts +4 -0
- package/src/errors.ts +58 -0
- package/src/flow-assignments.test.ts +63 -0
- package/src/flow-assignments.ts +125 -0
- package/src/host/command-dispatch.ts +59 -0
- package/src/host/command-envelope.ts +41 -0
- package/src/host/command-settlement.ts +121 -0
- package/src/host/constants.ts +103 -0
- package/src/host/host-event-contract.ts +277 -0
- package/src/host/index.ts +13 -0
- package/src/host/lazy-handle-client.ts +207 -0
- package/src/host/request-id.ts +3 -0
- package/src/host/sdk-types.ts +506 -0
- package/src/host/sdk.ts +43 -0
- package/src/host/unique-id.ts +17 -0
- package/src/identity-type.ts +102 -0
- package/src/index.ts +136 -0
- package/src/protocol-root.test.ts +476 -0
- package/src/public-grant-scope.ts +25 -0
- package/src/runtime-endpoints.ts +69 -0
- package/src/scopes.ts +79 -0
- package/src/trpc-envelope.ts +9 -0
- package/src/version-resolution.ts +18 -0
- package/src/widget-commands.ts +152 -0
- package/src/widget-config.ts +157 -0
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
type Subscriber<THandleId extends string> = (handleId: THandleId) => void;
|
|
2
|
+
type StageSubscriber<TStage extends string> = (stage: TStage) => void;
|
|
3
|
+
|
|
4
|
+
type LifecycleStage = "uninitialized" | "ready" | "invalidated";
|
|
5
|
+
type StageMap<TStage extends string> = {
|
|
6
|
+
uninitialized: TStage;
|
|
7
|
+
ready: TStage;
|
|
8
|
+
invalidated: TStage;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const DEFAULT_LIFECYCLE_STAGES: StageMap<LifecycleStage> = {
|
|
12
|
+
uninitialized: "uninitialized",
|
|
13
|
+
ready: "ready",
|
|
14
|
+
invalidated: "invalidated",
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export type LazyHandleClient<
|
|
18
|
+
THandleId extends string,
|
|
19
|
+
TSettlement,
|
|
20
|
+
TStage extends string = LifecycleStage,
|
|
21
|
+
> = {
|
|
22
|
+
runCommand<TPayload>(
|
|
23
|
+
buildPayload: (handleId: THandleId | undefined) => TPayload,
|
|
24
|
+
dispatchAndWait: (payload: TPayload) => Promise<TSettlement>,
|
|
25
|
+
): Promise<TSettlement>;
|
|
26
|
+
getHandleId(): THandleId | undefined;
|
|
27
|
+
subscribeHandle(subscriber: Subscriber<THandleId>): () => void;
|
|
28
|
+
getStage(): TStage;
|
|
29
|
+
setStage(stage: TStage): void;
|
|
30
|
+
subscribeStage(subscriber: StageSubscriber<TStage>): () => void;
|
|
31
|
+
reset(): void;
|
|
32
|
+
invalidate(): void;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export type CreateLazyHandleClientOptions<
|
|
36
|
+
THandleId extends string,
|
|
37
|
+
TSettlement,
|
|
38
|
+
TStage extends string = LifecycleStage,
|
|
39
|
+
> = {
|
|
40
|
+
getHandleIdFromSettlement: (settlement: TSettlement) => THandleId | undefined;
|
|
41
|
+
stages?: StageMap<TStage>;
|
|
42
|
+
initialStage?: TStage;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
type CreateLazyHandleClientDefaultOptions<
|
|
46
|
+
THandleId extends string,
|
|
47
|
+
TSettlement,
|
|
48
|
+
> = {
|
|
49
|
+
getHandleIdFromSettlement: (settlement: TSettlement) => THandleId | undefined;
|
|
50
|
+
initialStage?: LifecycleStage;
|
|
51
|
+
stages?: undefined;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
type CreateLazyHandleClientCustomOptions<
|
|
55
|
+
THandleId extends string,
|
|
56
|
+
TSettlement,
|
|
57
|
+
TStage extends string,
|
|
58
|
+
> = {
|
|
59
|
+
getHandleIdFromSettlement: (settlement: TSettlement) => THandleId | undefined;
|
|
60
|
+
stages: StageMap<TStage>;
|
|
61
|
+
initialStage?: TStage;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Serializes commands for a lazily initialized handle.
|
|
66
|
+
* Each command payload is built only when its turn runs, so it always sees
|
|
67
|
+
* the latest settled handle id from prior commands.
|
|
68
|
+
*/
|
|
69
|
+
const createLazyHandleClientWithStages = <
|
|
70
|
+
THandleId extends string,
|
|
71
|
+
TSettlement,
|
|
72
|
+
TStage extends string,
|
|
73
|
+
>(input: {
|
|
74
|
+
getHandleIdFromSettlement: (settlement: TSettlement) => THandleId | undefined;
|
|
75
|
+
stages: StageMap<TStage>;
|
|
76
|
+
initialStage?: TStage;
|
|
77
|
+
}): LazyHandleClient<THandleId, TSettlement, TStage> => {
|
|
78
|
+
let handleId: THandleId | undefined;
|
|
79
|
+
let tail = Promise.resolve();
|
|
80
|
+
let isIdle = true;
|
|
81
|
+
let epoch = 0;
|
|
82
|
+
const { stages } = input;
|
|
83
|
+
let stage = input.initialStage ?? stages.uninitialized;
|
|
84
|
+
const subscribers = new Set<Subscriber<THandleId>>();
|
|
85
|
+
const stageSubscribers = new Set<StageSubscriber<TStage>>();
|
|
86
|
+
|
|
87
|
+
const setStage = (nextStage: TStage): void => {
|
|
88
|
+
if (stage === nextStage) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (stage === stages.invalidated && nextStage !== stages.invalidated) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
stage = nextStage;
|
|
95
|
+
stageSubscribers.forEach((subscriber) => {
|
|
96
|
+
subscriber(nextStage);
|
|
97
|
+
});
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const publishHandle = (nextHandleId: THandleId): void => {
|
|
101
|
+
if (stage === stages.invalidated) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
handleId = nextHandleId;
|
|
105
|
+
setStage(stages.ready);
|
|
106
|
+
subscribers.forEach((subscriber) => {
|
|
107
|
+
subscriber(nextHandleId);
|
|
108
|
+
});
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const runCommand: LazyHandleClient<
|
|
112
|
+
THandleId,
|
|
113
|
+
TSettlement,
|
|
114
|
+
TStage
|
|
115
|
+
>["runCommand"] = (buildPayload, dispatchAndWait) => {
|
|
116
|
+
const execute = async (): Promise<TSettlement> => {
|
|
117
|
+
if (stage === stages.invalidated) {
|
|
118
|
+
throw new Error("Handle client is invalidated");
|
|
119
|
+
}
|
|
120
|
+
const commandEpoch = epoch;
|
|
121
|
+
const payload = buildPayload(handleId);
|
|
122
|
+
const settlement = await dispatchAndWait(payload);
|
|
123
|
+
const settledHandleId = input.getHandleIdFromSettlement(settlement);
|
|
124
|
+
if (settledHandleId && commandEpoch === epoch) {
|
|
125
|
+
publishHandle(settledHandleId);
|
|
126
|
+
}
|
|
127
|
+
return settlement;
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
const run = isIdle ? execute() : tail.then(execute, execute);
|
|
131
|
+
isIdle = false;
|
|
132
|
+
tail = run.then(
|
|
133
|
+
() => {
|
|
134
|
+
isIdle = true;
|
|
135
|
+
},
|
|
136
|
+
() => {
|
|
137
|
+
isIdle = true;
|
|
138
|
+
},
|
|
139
|
+
);
|
|
140
|
+
return run;
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
return {
|
|
144
|
+
runCommand,
|
|
145
|
+
getHandleId: () => handleId,
|
|
146
|
+
subscribeHandle: (subscriber) => {
|
|
147
|
+
subscribers.add(subscriber);
|
|
148
|
+
if (handleId) {
|
|
149
|
+
subscriber(handleId);
|
|
150
|
+
}
|
|
151
|
+
return () => {
|
|
152
|
+
subscribers.delete(subscriber);
|
|
153
|
+
};
|
|
154
|
+
},
|
|
155
|
+
getStage: () => stage,
|
|
156
|
+
setStage,
|
|
157
|
+
subscribeStage: (subscriber) => {
|
|
158
|
+
stageSubscribers.add(subscriber);
|
|
159
|
+
subscriber(stage);
|
|
160
|
+
return () => {
|
|
161
|
+
stageSubscribers.delete(subscriber);
|
|
162
|
+
};
|
|
163
|
+
},
|
|
164
|
+
reset: () => {
|
|
165
|
+
epoch += 1;
|
|
166
|
+
handleId = undefined;
|
|
167
|
+
stage = stages.uninitialized;
|
|
168
|
+
stageSubscribers.forEach((subscriber) => {
|
|
169
|
+
subscriber(stage);
|
|
170
|
+
});
|
|
171
|
+
},
|
|
172
|
+
invalidate: () => {
|
|
173
|
+
epoch += 1;
|
|
174
|
+
handleId = undefined;
|
|
175
|
+
setStage(stages.invalidated);
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
export function createLazyHandleClient<THandleId extends string, TSettlement>(
|
|
181
|
+
options: CreateLazyHandleClientDefaultOptions<THandleId, TSettlement>,
|
|
182
|
+
): LazyHandleClient<THandleId, TSettlement, LifecycleStage>;
|
|
183
|
+
export function createLazyHandleClient<
|
|
184
|
+
THandleId extends string,
|
|
185
|
+
TSettlement,
|
|
186
|
+
TStage extends string,
|
|
187
|
+
>(
|
|
188
|
+
options: CreateLazyHandleClientCustomOptions<THandleId, TSettlement, TStage>,
|
|
189
|
+
): LazyHandleClient<THandleId, TSettlement, TStage>;
|
|
190
|
+
export function createLazyHandleClient<THandleId extends string, TSettlement>(
|
|
191
|
+
options:
|
|
192
|
+
| CreateLazyHandleClientDefaultOptions<THandleId, TSettlement>
|
|
193
|
+
| CreateLazyHandleClientCustomOptions<THandleId, TSettlement, string>,
|
|
194
|
+
): LazyHandleClient<THandleId, TSettlement, string> {
|
|
195
|
+
if ("stages" in options && options.stages) {
|
|
196
|
+
return createLazyHandleClientWithStages({
|
|
197
|
+
getHandleIdFromSettlement: options.getHandleIdFromSettlement,
|
|
198
|
+
stages: options.stages,
|
|
199
|
+
initialStage: options.initialStage,
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
return createLazyHandleClientWithStages({
|
|
203
|
+
getHandleIdFromSettlement: options.getHandleIdFromSettlement,
|
|
204
|
+
stages: DEFAULT_LIFECYCLE_STAGES,
|
|
205
|
+
initialStage: options.initialStage,
|
|
206
|
+
});
|
|
207
|
+
}
|
|
@@ -0,0 +1,506 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* getuserfeedback widget SDK public API.
|
|
3
|
+
*
|
|
4
|
+
* {@link FlowState} lives in `./host-event-contract` (re-exported from the `@getuserfeedback/protocol/host` barrel).
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { Scope } from "../scopes.js";
|
|
8
|
+
|
|
9
|
+
// ── Events and identity ─────────────────────────────────────────────
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Identifier types supported by event tracking and identity resolution.
|
|
13
|
+
*
|
|
14
|
+
* Use `custom.<name>` for business-specific identifiers such as
|
|
15
|
+
* `custom.shopifyCustomerId` or `custom.salesforceContactId`.
|
|
16
|
+
*
|
|
17
|
+
* @see https://getuserfeedback.com/docs/guides/identity-resolution
|
|
18
|
+
*/
|
|
19
|
+
export type AppEventIdentityType =
|
|
20
|
+
| "anonymousId"
|
|
21
|
+
| "userId"
|
|
22
|
+
| "traits.email"
|
|
23
|
+
| "traits.phone"
|
|
24
|
+
| "context.device.id"
|
|
25
|
+
| "context.device.advertisingId"
|
|
26
|
+
| "context.device.token"
|
|
27
|
+
| `custom.${string}`;
|
|
28
|
+
|
|
29
|
+
/** A single identifier, such as a user ID, anonymous ID, email, or custom external ID. */
|
|
30
|
+
export type AppEventIdentity = {
|
|
31
|
+
type: AppEventIdentityType;
|
|
32
|
+
value: string;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/** Page, feature flag, and capability context attached to an event. */
|
|
36
|
+
export type AppEventContext = {
|
|
37
|
+
page?: {
|
|
38
|
+
path?: string;
|
|
39
|
+
referrer?: string;
|
|
40
|
+
search?: string;
|
|
41
|
+
};
|
|
42
|
+
locale?: string;
|
|
43
|
+
userAgent?: string;
|
|
44
|
+
flags?: AppEventFlag[] | undefined;
|
|
45
|
+
capabilities?: AppEventCapability[] | undefined;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/** Supported feature flag value shape. */
|
|
49
|
+
export type AppEventFlagValue =
|
|
50
|
+
| string
|
|
51
|
+
| number
|
|
52
|
+
| boolean
|
|
53
|
+
| null
|
|
54
|
+
| AppEventFlagValue[]
|
|
55
|
+
| { [key: string]: AppEventFlagValue };
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Supported event property value shape.
|
|
59
|
+
*
|
|
60
|
+
* @see https://getuserfeedback.com/docs/reference/events
|
|
61
|
+
*/
|
|
62
|
+
export type AppEventJsonValue = AppEventFlagValue;
|
|
63
|
+
|
|
64
|
+
/** Feature flag provider metadata. */
|
|
65
|
+
export type AppEventFlagProvider = {
|
|
66
|
+
name: string;
|
|
67
|
+
project?: string | undefined;
|
|
68
|
+
environment?: string | undefined;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/** Feature flag evaluation context attached to widget initialization or events. */
|
|
72
|
+
export type AppEventFlag = {
|
|
73
|
+
key: string;
|
|
74
|
+
target?: string | undefined;
|
|
75
|
+
value: AppEventFlagValue;
|
|
76
|
+
variant?: string | undefined;
|
|
77
|
+
origin?: "runtime_config" | "trigger_context" | "provider_sync" | "api";
|
|
78
|
+
provider?: AppEventFlagProvider | undefined;
|
|
79
|
+
status?: string | undefined;
|
|
80
|
+
reason?: string | undefined;
|
|
81
|
+
evaluatedAt?: string | undefined;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
/** Host capability context attached to widget initialization or events. */
|
|
85
|
+
export type AppEventCapability = {
|
|
86
|
+
key: string;
|
|
87
|
+
source?: string | undefined;
|
|
88
|
+
provider?: AppEventFlagProvider | undefined;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
/** Feature flag evaluations accepted by SDK initialization. */
|
|
92
|
+
export type FlagsInput = AppEventFlag[] | Record<string, AppEventFlagValue>;
|
|
93
|
+
|
|
94
|
+
/** Host capabilities accepted by SDK initialization. */
|
|
95
|
+
export type CapabilitiesInput = Array<string | AppEventCapability>;
|
|
96
|
+
|
|
97
|
+
/** Optional fields shared by event payloads. */
|
|
98
|
+
export type AppEventBase = {
|
|
99
|
+
traits?: Record<string, unknown> | undefined;
|
|
100
|
+
identities?: AppEventIdentity[] | undefined;
|
|
101
|
+
context?: AppEventContext | undefined;
|
|
102
|
+
messageId?: string | undefined;
|
|
103
|
+
timestamp?: number | undefined;
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
/** Reference to a specific survey or flow. */
|
|
107
|
+
export type AppEventSurveyReference = {
|
|
108
|
+
surveyId: string;
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
/** Event payload emitted when a flow is shown. */
|
|
112
|
+
export type FlowViewedPayload = AppEventBase & {
|
|
113
|
+
origin?: "system" | undefined;
|
|
114
|
+
event: "Flow Viewed";
|
|
115
|
+
references: AppEventSurveyReference;
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
/** Event payload emitted when a user dismisses a flow. */
|
|
119
|
+
export type FlowDismissedPayload = AppEventBase & {
|
|
120
|
+
origin?: "system" | undefined;
|
|
121
|
+
event: "Flow Dismissed";
|
|
122
|
+
references: AppEventSurveyReference;
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Event payload sent with `client.track()`.
|
|
127
|
+
*
|
|
128
|
+
* @see https://getuserfeedback.com/docs/reference/events
|
|
129
|
+
*/
|
|
130
|
+
export type CustomerTrackPayload = AppEventBase & {
|
|
131
|
+
origin: "customer";
|
|
132
|
+
event: string;
|
|
133
|
+
properties?: Record<string, AppEventJsonValue> | undefined;
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Event payload union. Use `origin` as the discriminator when handling the
|
|
138
|
+
* union directly.
|
|
139
|
+
*/
|
|
140
|
+
export type AppEventPayload =
|
|
141
|
+
| FlowViewedPayload
|
|
142
|
+
| FlowDismissedPayload
|
|
143
|
+
| CustomerTrackPayload;
|
|
144
|
+
|
|
145
|
+
// ── Client metadata ─────────────────────────────────────────────────
|
|
146
|
+
|
|
147
|
+
/** How the widget was loaded, such as SDK, GTM, Segment, or a custom loader. */
|
|
148
|
+
export type ClientMetaLoader =
|
|
149
|
+
| "sdk"
|
|
150
|
+
| "gtm"
|
|
151
|
+
| "segment"
|
|
152
|
+
| "rudderstack"
|
|
153
|
+
| "tealium"
|
|
154
|
+
| "custom";
|
|
155
|
+
|
|
156
|
+
/** Delivery method for the widget script. */
|
|
157
|
+
export type ClientMetaTransport =
|
|
158
|
+
| "script-tag"
|
|
159
|
+
| "esm"
|
|
160
|
+
| "loader"
|
|
161
|
+
| "tag-manager"
|
|
162
|
+
| "snippet";
|
|
163
|
+
|
|
164
|
+
/** Detected or configured front-end framework. */
|
|
165
|
+
export type ClientMetaRuntimeFramework = "next" | "vite" | "webpack" | "plain";
|
|
166
|
+
|
|
167
|
+
/** Runtime environment (browser or edge). */
|
|
168
|
+
export type ClientMetaRuntimeEnv = "browser" | "edge";
|
|
169
|
+
|
|
170
|
+
/** Metadata about the client integration, used for diagnostics and support. */
|
|
171
|
+
export type ClientMeta = {
|
|
172
|
+
loader: ClientMetaLoader;
|
|
173
|
+
clientName?: string | undefined;
|
|
174
|
+
clientVersion?: string | undefined;
|
|
175
|
+
integrator?:
|
|
176
|
+
| {
|
|
177
|
+
name?: string | undefined;
|
|
178
|
+
version?: string | undefined;
|
|
179
|
+
}
|
|
180
|
+
| undefined;
|
|
181
|
+
transport?: ClientMetaTransport | undefined;
|
|
182
|
+
runtime?:
|
|
183
|
+
| {
|
|
184
|
+
framework?: ClientMetaRuntimeFramework | undefined;
|
|
185
|
+
runtime?: ClientMetaRuntimeEnv | undefined;
|
|
186
|
+
}
|
|
187
|
+
| undefined;
|
|
188
|
+
notes?: string | undefined;
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
export type RuntimeEndpoints = {
|
|
192
|
+
apiUrl?: string | undefined;
|
|
193
|
+
coreUrl?: string | undefined;
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
/** A single response metadata tag value preserved with a submitted response. */
|
|
197
|
+
export type ResponseMetadataTagValue = string | string[];
|
|
198
|
+
|
|
199
|
+
/** Optional metadata tags the host can attach to a widget flow run. */
|
|
200
|
+
export type ResponseMetadataInput = {
|
|
201
|
+
tags?: Record<string, ResponseMetadataTagValue> | undefined;
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
// ── Configuration (color scheme, consent, auth) ─────────────────────────────
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Use host attributes to auto-detect the color scheme ("light" by default). The widget observes these attributes on body and html elements,
|
|
208
|
+
*
|
|
209
|
+
* class="dark" or data-theme="dark" on html or body elements will set the widget to dark mode.
|
|
210
|
+
*
|
|
211
|
+
* class="light" or data-theme="light" will set the widget to light mode.
|
|
212
|
+
*
|
|
213
|
+
* class="system" or data-theme="system" will set the widget to follow the user's OS/browser preference (prefers-color-scheme).
|
|
214
|
+
*/
|
|
215
|
+
export type ColorSchemeAutoDetect = {
|
|
216
|
+
/** List of host attribute names (html or body) to observe for color scheme.
|
|
217
|
+
*
|
|
218
|
+
* Default: ["class", "data-theme"]
|
|
219
|
+
*/
|
|
220
|
+
autoDetectColorScheme: string[];
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Color scheme: either auto-detect from host html and body attributes (default) or provide an explicit value.
|
|
225
|
+
* - { autoDetectColorScheme: ["class", "data-theme"] }
|
|
226
|
+
* - "light" | "dark" | "system"
|
|
227
|
+
*/
|
|
228
|
+
export type ColorSchemeConfig =
|
|
229
|
+
| ColorSchemeAutoDetect
|
|
230
|
+
| "light"
|
|
231
|
+
| "dark"
|
|
232
|
+
| "system";
|
|
233
|
+
|
|
234
|
+
/** Whether telemetry is enabled. */
|
|
235
|
+
export type TelemetryConfig = {
|
|
236
|
+
enabled?: boolean | undefined;
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
/** When true, prints debug logs to console. Narrow down with a namespace string/array eg. `["loader", "handshake"]`. Default `false`. */
|
|
240
|
+
export type DebugConfig = boolean | string | string[];
|
|
241
|
+
|
|
242
|
+
/** Status of a consent decision (pending, granted, denied, revoked). */
|
|
243
|
+
export type ConsentStatus = "pending" | "granted" | "denied" | "revoked";
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Supported consent scope identifiers for explicit allow-list grants (full CMP-aligned set).
|
|
247
|
+
* Alias for {@link Scope}; kept for backward-compatible imports.
|
|
248
|
+
*/
|
|
249
|
+
export type GrantScope = Scope;
|
|
250
|
+
|
|
251
|
+
/** Consent configuration. Use a single decision for all scopes, or provide an explicit allow-list of granted scopes. */
|
|
252
|
+
export type ConsentConfig = ConsentStatus | Scope[];
|
|
253
|
+
|
|
254
|
+
/** Auth configuration, such as a JWT for authenticated flows. */
|
|
255
|
+
export type AuthConfig = {
|
|
256
|
+
jwt?: { token: string } | null | undefined;
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Runtime configuration updates accepted by `client.configure()`.
|
|
261
|
+
*
|
|
262
|
+
* @see https://getuserfeedback.com/docs/security-and-privacy/compliance-and-consent
|
|
263
|
+
*/
|
|
264
|
+
export type ConfigureOptions = {
|
|
265
|
+
colorScheme?: ColorSchemeConfig | undefined;
|
|
266
|
+
consent?: ConsentConfig | undefined;
|
|
267
|
+
auth?: AuthConfig | undefined;
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
/** @internal Options when initializing the widget: API key, color scheme, disableTelemetry, enableDebug, defaultConsent, capabilities, and optional host targeting context. */
|
|
271
|
+
export type InitOptions = {
|
|
272
|
+
apiKey: string;
|
|
273
|
+
colorScheme?: ColorSchemeConfig | undefined;
|
|
274
|
+
disableTelemetry?: boolean | undefined;
|
|
275
|
+
enableDebug?: DebugConfig | undefined;
|
|
276
|
+
defaultConsent?: ConsentConfig | undefined;
|
|
277
|
+
clientMeta?: ClientMeta | undefined;
|
|
278
|
+
capabilities?: string[] | undefined;
|
|
279
|
+
flags?: AppEventFlag[] | undefined;
|
|
280
|
+
hostCapabilities?: AppEventCapability[] | undefined;
|
|
281
|
+
runtimeEndpoints?: RuntimeEndpoints | undefined;
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
export type ClientOptions = {
|
|
285
|
+
/** Your project API key. */
|
|
286
|
+
apiKey: string;
|
|
287
|
+
/** Color scheme config: host-driven (loader observes attributes) or explicit. */
|
|
288
|
+
colorScheme?: ColorSchemeConfig | undefined;
|
|
289
|
+
/**
|
|
290
|
+
* Initial consent state.
|
|
291
|
+
*
|
|
292
|
+
* Default: `granted` (all scopes).
|
|
293
|
+
*
|
|
294
|
+
* Accepts either a single decision (`granted` | `denied` | `pending` | `revoked`)
|
|
295
|
+
* or an explicit allow-list of additional granted scopes (`Scope[]`).
|
|
296
|
+
*
|
|
297
|
+
* For strict privacy guarantees, use `pending` at init time and later call `client.configure({ consent })` after
|
|
298
|
+
* the user makes a consent choice via your CMP or UI.
|
|
299
|
+
*
|
|
300
|
+
* In array form, listed scopes are granted and all other non-essential scopes are denied.
|
|
301
|
+
*
|
|
302
|
+
* Essential baseline scopes are always granted for core widget operation and cannot be denied:
|
|
303
|
+
* - `functionality.storage`
|
|
304
|
+
* - `security.storage`
|
|
305
|
+
*
|
|
306
|
+
* @see https://getuserfeedback.com/docs/security-and-privacy/compliance-and-consent
|
|
307
|
+
*/
|
|
308
|
+
defaultConsent?: ConsentConfig | undefined;
|
|
309
|
+
/** When true, prints debug logs to console. Narrow down with a namespace string/array eg. ```["loader", "handshake"]```. Default `false`.
|
|
310
|
+
*/
|
|
311
|
+
enableDebug?: DebugConfig | undefined;
|
|
312
|
+
/** When true, the widget does not load automatically; call `client.load()` when ready. Default `false` */
|
|
313
|
+
disableAutoLoad?: boolean;
|
|
314
|
+
/** Disable anonymous widget telemetry.
|
|
315
|
+
*
|
|
316
|
+
* When `true`, telemetry is not sent.
|
|
317
|
+
*
|
|
318
|
+
* This flag does not disable user targeting and reporting analytics. It's used (by us) for performance monitoring and error tracking.
|
|
319
|
+
*
|
|
320
|
+
* Included telemetry data when enabled: apiKey, ephemeral one-time session id,
|
|
321
|
+
* execution flow, event timestamps, and page origin (domain name).
|
|
322
|
+
*
|
|
323
|
+
* Excluded by default: user identity and detailed page fields (path/referrer/search).
|
|
324
|
+
*/
|
|
325
|
+
disableTelemetry?: boolean | undefined;
|
|
326
|
+
/** Feature flag evaluations collected with widget initialization. */
|
|
327
|
+
flags?: FlagsInput | undefined;
|
|
328
|
+
/** Host capabilities collected with widget initialization. */
|
|
329
|
+
capabilities?: CapabilitiesInput | undefined;
|
|
330
|
+
_loaderUrl?: string | undefined;
|
|
331
|
+
_coreUrl?: string | undefined;
|
|
332
|
+
_apiUrl?: string | undefined;
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
// ── Flow and widget actions (used by Client methods) ───────────────────
|
|
336
|
+
|
|
337
|
+
/** Open a flow with a given instance. Omit flowHandleId for first open; core returns it in settlement. */
|
|
338
|
+
export type OpenCommandPayload = {
|
|
339
|
+
kind: "open";
|
|
340
|
+
flowId: string;
|
|
341
|
+
flowHandleId?: string | undefined;
|
|
342
|
+
container?: HTMLElement | null | undefined;
|
|
343
|
+
/** Optional metadata tags to persist with the eventual response submission. */
|
|
344
|
+
metadata?: ResponseMetadataInput | undefined;
|
|
345
|
+
/**
|
|
346
|
+
* When `true`, the flow view does not show a close button.
|
|
347
|
+
* Useful for mobile or embedded contexts where the host handles dismissal (e.g. via a drawer or back gesture).
|
|
348
|
+
*/
|
|
349
|
+
hideCloseButton?: boolean | undefined;
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
/** Prefetch a flow. Omit flowHandleId for first prefetch; core returns it in settlement. */
|
|
353
|
+
export type PrefetchCommandPayload = {
|
|
354
|
+
kind: "prefetch";
|
|
355
|
+
flowId: string;
|
|
356
|
+
flowHandleId?: string | undefined;
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
/** Prerender a flow. Omit flowHandleId for first prerender; core returns it in settlement. */
|
|
360
|
+
export type PrerenderCommandPayload = {
|
|
361
|
+
kind: "prerender";
|
|
362
|
+
flowId: string;
|
|
363
|
+
flowHandleId?: string | undefined;
|
|
364
|
+
/**
|
|
365
|
+
* When `true`, the prerendered view does not show a close button.
|
|
366
|
+
*/
|
|
367
|
+
hideCloseButton?: boolean | undefined;
|
|
368
|
+
};
|
|
369
|
+
|
|
370
|
+
/** Identify with traits only (`client.identify(traits)`). */
|
|
371
|
+
export type IdentifyTraitsOnlyCommandPayload = {
|
|
372
|
+
kind: "identify";
|
|
373
|
+
traits: Record<string, unknown>;
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
/** Identify with userId and optional traits (`client.identify(userId, traits?)`). */
|
|
377
|
+
export type IdentifyUserCommandPayload = {
|
|
378
|
+
kind: "identify";
|
|
379
|
+
userId: string;
|
|
380
|
+
traits?: Record<string, unknown> | undefined;
|
|
381
|
+
};
|
|
382
|
+
|
|
383
|
+
/** Identify command payload: either traits-only or userId+traits shape. */
|
|
384
|
+
export type IdentifyCommandPayload =
|
|
385
|
+
| IdentifyTraitsOnlyCommandPayload
|
|
386
|
+
| IdentifyUserCommandPayload;
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Command payload for `client.track()`.
|
|
390
|
+
*
|
|
391
|
+
* @see https://getuserfeedback.com/docs/reference/events
|
|
392
|
+
*/
|
|
393
|
+
export type TrackCommandPayload = AppEventBase & {
|
|
394
|
+
kind: "track";
|
|
395
|
+
origin: "customer";
|
|
396
|
+
event: string;
|
|
397
|
+
properties?: Record<string, AppEventJsonValue> | undefined;
|
|
398
|
+
};
|
|
399
|
+
|
|
400
|
+
/** Attach or detach a resolved flow handle from a host container. */
|
|
401
|
+
export type SetContainerCommandPayload = {
|
|
402
|
+
kind: "setContainer";
|
|
403
|
+
flowHandleId: string;
|
|
404
|
+
container: HTMLElement | null;
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
/** Default container behavior for flows in an instance. */
|
|
408
|
+
export type ContainerPolicy =
|
|
409
|
+
| {
|
|
410
|
+
kind: "floating";
|
|
411
|
+
}
|
|
412
|
+
| {
|
|
413
|
+
kind: "hostContainer";
|
|
414
|
+
host: HTMLElement | null;
|
|
415
|
+
sharing: "shared" | "perFlowRun";
|
|
416
|
+
};
|
|
417
|
+
|
|
418
|
+
/** Set the instance-level default container policy. */
|
|
419
|
+
export type SetDefaultContainerPolicyCommandPayload = {
|
|
420
|
+
kind: "setDefaultContainerPolicy";
|
|
421
|
+
policy: ContainerPolicy;
|
|
422
|
+
};
|
|
423
|
+
|
|
424
|
+
/** Update color scheme, consent, and auth. */
|
|
425
|
+
export type ConfigureCommandPayload = {
|
|
426
|
+
kind: "configure";
|
|
427
|
+
opts: ConfigureOptions;
|
|
428
|
+
};
|
|
429
|
+
|
|
430
|
+
/** Initialize the widget with options. */
|
|
431
|
+
export type InitCommandPayload = {
|
|
432
|
+
kind: "init";
|
|
433
|
+
opts: InitOptions;
|
|
434
|
+
};
|
|
435
|
+
|
|
436
|
+
/** Close a flow. */
|
|
437
|
+
export type CloseCommandPayload = {
|
|
438
|
+
kind: "close";
|
|
439
|
+
flowHandleId?: string | undefined;
|
|
440
|
+
};
|
|
441
|
+
|
|
442
|
+
/** Reset widget state. */
|
|
443
|
+
export type ResetCommandPayload = {
|
|
444
|
+
kind: "reset";
|
|
445
|
+
};
|
|
446
|
+
|
|
447
|
+
/** Union of widget action payloads such as open, prefetch, identify, configure, and track. */
|
|
448
|
+
export type PublicCommandPayload =
|
|
449
|
+
| OpenCommandPayload
|
|
450
|
+
| PrefetchCommandPayload
|
|
451
|
+
| PrerenderCommandPayload
|
|
452
|
+
| IdentifyCommandPayload
|
|
453
|
+
| SetContainerCommandPayload
|
|
454
|
+
| SetDefaultContainerPolicyCommandPayload
|
|
455
|
+
| ConfigureCommandPayload
|
|
456
|
+
| InitCommandPayload
|
|
457
|
+
| CloseCommandPayload
|
|
458
|
+
| ResetCommandPayload
|
|
459
|
+
| TrackCommandPayload;
|
|
460
|
+
|
|
461
|
+
/** Input for a single widget action; same shape as {@link PublicCommandPayload}. */
|
|
462
|
+
export type Command = PublicCommandPayload;
|
|
463
|
+
|
|
464
|
+
/** Wrapper for a widget action (version, request id, idempotency key, optional client metadata, and the action payload). */
|
|
465
|
+
export type CommandEnvelope = {
|
|
466
|
+
version: "1";
|
|
467
|
+
instanceId?: string | undefined;
|
|
468
|
+
requestId: string;
|
|
469
|
+
idempotencyKey: string;
|
|
470
|
+
clientMeta?: ClientMeta | undefined;
|
|
471
|
+
command: PublicCommandPayload;
|
|
472
|
+
};
|
|
473
|
+
|
|
474
|
+
// ── Telemetry (use("beforeSend")) ─────────────────────────────────────
|
|
475
|
+
|
|
476
|
+
/** Identity kind for telemetry events; same as {@link AppEventIdentityType}. */
|
|
477
|
+
export type TelemetryIdentityType = AppEventIdentityType;
|
|
478
|
+
|
|
479
|
+
/**
|
|
480
|
+
* Event passed to the `beforeSend` hook in {@link Client.use}.
|
|
481
|
+
* When `debug` is true, identities and full page context are included; when false, no identities and page is origin-only.
|
|
482
|
+
*
|
|
483
|
+
* TODO(migration): Rename this SDK-facing type to WidgetTelemetryEvent in the
|
|
484
|
+
* same change that updates SDK imports, so the generic TelemetryEvent name can
|
|
485
|
+
* retire here without colliding with shared telemetry.
|
|
486
|
+
* [from=TelemetryEvent-widget-surface] [to=WidgetTelemetryEvent] [scope=slice] [priority=low]
|
|
487
|
+
*/
|
|
488
|
+
export type TelemetryEvent = {
|
|
489
|
+
type: string;
|
|
490
|
+
timestamp: number;
|
|
491
|
+
apiKey: string;
|
|
492
|
+
/** When true, identities and full page context are present; when false, no identities and page.origin only. */
|
|
493
|
+
debug: boolean;
|
|
494
|
+
identities: Array<{ type: TelemetryIdentityType; value: string }>;
|
|
495
|
+
context: {
|
|
496
|
+
page: {
|
|
497
|
+
path?: string | null | undefined;
|
|
498
|
+
referrer?: string | null | undefined;
|
|
499
|
+
search?: string | null | undefined;
|
|
500
|
+
origin?: string | null | undefined;
|
|
501
|
+
};
|
|
502
|
+
widgetVersion: string;
|
|
503
|
+
clientMeta?: ClientMeta | undefined;
|
|
504
|
+
};
|
|
505
|
+
properties?: Record<string, unknown> | undefined;
|
|
506
|
+
};
|