@getuserfeedback/protocol 0.1.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.
@@ -0,0 +1,26 @@
1
+ import type { CommandEnvelope, PublicCommandPayload } from "./sdk-types.js";
2
+ /**
3
+ * A command input: the pure payload with `kind` and command-specific fields.
4
+ * Distributes over the PublicCommandPayload union so discriminant
5
+ * narrowing is preserved (e.g. `{ kind: "open", flowId }` type-checks).
6
+ */
7
+ export type CommandInput = PublicCommandPayload extends infer C ? C extends PublicCommandPayload ? C : never : never;
8
+ type DispatchOptions = {
9
+ idempotencyKey?: string;
10
+ };
11
+ /**
12
+ * Creates a dispatch function that wraps a command payload in an envelope,
13
+ * pushes it into the queue, and returns a promise that settles when the
14
+ * runtime acknowledges it.
15
+ *
16
+ * Request IDs are generated automatically.
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * const dispatch = createCommandDispatch((env) => queue.push(env));
21
+ * await dispatch({ kind: "open", flowId: "f1", flowHandleId: "h1" });
22
+ * ```
23
+ */
24
+ export declare const createCommandDispatch: (push: (envelope: CommandEnvelope) => void) => ((input: CommandInput, options?: DispatchOptions) => Promise<void>);
25
+ export {};
26
+ //# sourceMappingURL=command-dispatch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command-dispatch.d.ts","sourceRoot":"","sources":["../../src/host/command-dispatch.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAE5E;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG,oBAAoB,SAAS,MAAM,CAAC,GAC5D,CAAC,SAAS,oBAAoB,GAC7B,CAAC,GACD,KAAK,GACN,KAAK,CAAC;AAET,KAAK,eAAe,GAAG;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,qBAAqB,GACjC,MAAM,CAAC,QAAQ,EAAE,eAAe,KAAK,IAAI,KACvC,CAAC,CAAC,KAAK,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE,eAAe,KAAK,OAAO,CAAC,IAAI,CAAC,CAgBpE,CAAC"}
@@ -0,0 +1,39 @@
1
+ import { waitForCommandSettlement } from "./command-settlement.js";
2
+ import { createCommandRequestId } from "./request-id.js";
3
+ /**
4
+ * Creates a dispatch function that wraps a command payload in an envelope,
5
+ * pushes it into the queue, and returns a promise that settles when the
6
+ * runtime acknowledges it.
7
+ *
8
+ * Request IDs are generated automatically.
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * const dispatch = createCommandDispatch((env) => queue.push(env));
13
+ * await dispatch({ kind: "open", flowId: "f1", flowHandleId: "h1" });
14
+ * ```
15
+ */
16
+ export const createCommandDispatch = (push) => {
17
+ return async (input, options) => {
18
+ const requestId = createCommandRequestId();
19
+ const idempotencyKey = resolveIdempotencyKey(options?.idempotencyKey);
20
+ const envelope = {
21
+ version: "1",
22
+ requestId,
23
+ idempotencyKey: idempotencyKey ?? requestId,
24
+ command: input,
25
+ };
26
+ push(envelope);
27
+ await waitForCommandSettlement({ requestId });
28
+ };
29
+ };
30
+ const resolveIdempotencyKey = (value) => {
31
+ if (value === undefined) {
32
+ return null;
33
+ }
34
+ const idempotencyKey = value.trim();
35
+ if (!idempotencyKey) {
36
+ throw new Error("idempotencyKey must be a non-empty string");
37
+ }
38
+ return idempotencyKey;
39
+ };
@@ -0,0 +1,32 @@
1
+ import { type CommandSettledDetail } from "./host-event-contract.js";
2
+ type WaitForCommandSettlementInput = {
3
+ requestId: string;
4
+ windowRef?: Window;
5
+ timeoutMs?: number;
6
+ };
7
+ export declare const COMMAND_SETTLEMENT_TIMEOUT_CODE: "COMMAND_SETTLEMENT_TIMEOUT";
8
+ export declare class CommandSettlementError extends Error {
9
+ readonly code?: string;
10
+ readonly requestId: string;
11
+ readonly kind: string;
12
+ readonly instanceId: string | null;
13
+ constructor(input: {
14
+ requestId: string;
15
+ kind: string;
16
+ instanceId: string | null;
17
+ message: string;
18
+ code?: string;
19
+ });
20
+ }
21
+ type CommandSettledFailureDetail = Extract<CommandSettledDetail, {
22
+ ok: false;
23
+ }>;
24
+ export declare const toCommandSettlementError: (detail: CommandSettledFailureDetail) => CommandSettlementError;
25
+ export declare class CommandSettlementTimeoutError extends Error {
26
+ readonly code: "COMMAND_SETTLEMENT_TIMEOUT";
27
+ readonly requestId: string;
28
+ constructor(requestId: string);
29
+ }
30
+ export declare const waitForCommandSettlement: (input: WaitForCommandSettlementInput) => Promise<CommandSettledDetail>;
31
+ export {};
32
+ //# sourceMappingURL=command-settlement.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command-settlement.d.ts","sourceRoot":"","sources":["../../src/host/command-settlement.ts"],"names":[],"mappings":"AACA,OAAO,EACN,KAAK,oBAAoB,EAEzB,MAAM,0BAA0B,CAAC;AAElC,KAAK,6BAA6B,GAAG;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAGF,eAAO,MAAM,+BAA+B,EAC3C,4BAAqC,CAAC;AAEvC,qBAAa,sBAAuB,SAAQ,KAAK;IAChD,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;gBAEvB,KAAK,EAAE;QAClB,SAAS,EAAE,MAAM,CAAC;QAClB,IAAI,EAAE,MAAM,CAAC;QACb,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;QAC1B,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,CAAC,EAAE,MAAM,CAAC;KACd;CAUD;AAED,KAAK,2BAA2B,GAAG,OAAO,CAAC,oBAAoB,EAAE;IAAE,EAAE,EAAE,KAAK,CAAA;CAAE,CAAC,CAAC;AAEhF,eAAO,MAAM,wBAAwB,GACpC,QAAQ,2BAA2B,KACjC,sBAUF,CAAC;AAEF,qBAAa,6BAA8B,SAAQ,KAAK;IACvD,QAAQ,CAAC,IAAI,+BAAmC;IAChD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;gBAEf,SAAS,EAAE,MAAM;CAK7B;AASD,eAAO,MAAM,wBAAwB,GACpC,OAAO,6BAA6B,KAClC,OAAO,CAAC,oBAAoB,CAgD9B,CAAC"}
@@ -0,0 +1,86 @@
1
+ import { HOST_EVENT_KEYS, toHostEventName } from "./constants.js";
2
+ import { isCommandSettledDetail, } from "./host-event-contract.js";
3
+ const DEFAULT_SETTLEMENT_TIMEOUT_MS = 30_000;
4
+ export const COMMAND_SETTLEMENT_TIMEOUT_CODE = "COMMAND_SETTLEMENT_TIMEOUT";
5
+ export class CommandSettlementError extends Error {
6
+ code;
7
+ requestId;
8
+ kind;
9
+ instanceId;
10
+ constructor(input) {
11
+ super(input.message);
12
+ this.name = "CommandSettlementError";
13
+ this.requestId = input.requestId;
14
+ this.kind = input.kind;
15
+ this.instanceId = input.instanceId;
16
+ if (input.code) {
17
+ this.code = input.code;
18
+ }
19
+ }
20
+ }
21
+ export const toCommandSettlementError = (detail) => {
22
+ return new CommandSettlementError({
23
+ requestId: detail.requestId,
24
+ kind: detail.kind,
25
+ instanceId: detail.instanceId,
26
+ message: detail.error.message ??
27
+ `Command ${detail.kind} failed without an error payload`,
28
+ code: detail.error.code,
29
+ });
30
+ };
31
+ export class CommandSettlementTimeoutError extends Error {
32
+ code = COMMAND_SETTLEMENT_TIMEOUT_CODE;
33
+ requestId;
34
+ constructor(requestId) {
35
+ super(`Timed out waiting for command settlement: ${requestId}`);
36
+ this.name = "CommandSettlementTimeoutError";
37
+ this.requestId = requestId;
38
+ }
39
+ }
40
+ const eventHasDetail = (event) => {
41
+ if (typeof CustomEvent !== "undefined") {
42
+ return event instanceof CustomEvent;
43
+ }
44
+ return typeof event === "object" && event !== null && "detail" in event;
45
+ };
46
+ export const waitForCommandSettlement = (input) => {
47
+ const requestId = input.requestId;
48
+ if (!requestId) {
49
+ return Promise.reject(new Error("requestId must be a non-empty string"));
50
+ }
51
+ const targetWindow = input.windowRef ?? (typeof window === "undefined" ? undefined : window);
52
+ if (!targetWindow) {
53
+ return Promise.reject(new Error("Cannot await command settlement without window"));
54
+ }
55
+ const eventName = toHostEventName(HOST_EVENT_KEYS.INSTANCE_COMMAND_SETTLED);
56
+ const timeoutMs = Math.max(0, input.timeoutMs ?? DEFAULT_SETTLEMENT_TIMEOUT_MS);
57
+ return new Promise((resolve, reject) => {
58
+ const cleanup = () => {
59
+ if (typeof targetWindow.removeEventListener === "function") {
60
+ targetWindow.removeEventListener(eventName, handleEvent);
61
+ }
62
+ };
63
+ const timeoutId = setTimeout(() => {
64
+ cleanup();
65
+ reject(new CommandSettlementTimeoutError(requestId));
66
+ }, timeoutMs);
67
+ const handleEvent = (event) => {
68
+ if (!eventHasDetail(event)) {
69
+ return;
70
+ }
71
+ const raw = event.detail;
72
+ if (!isCommandSettledDetail(raw) || raw.requestId !== requestId) {
73
+ return;
74
+ }
75
+ const detail = raw;
76
+ cleanup();
77
+ clearTimeout(timeoutId);
78
+ if (detail.ok) {
79
+ resolve(detail);
80
+ return;
81
+ }
82
+ reject(toCommandSettlementError(detail));
83
+ };
84
+ targetWindow.addEventListener(eventName, handleEvent);
85
+ });
86
+ };
@@ -0,0 +1,35 @@
1
+ export declare const TELEMETRY_EVENTS: {
2
+ readonly INSTANCE_READY: "instance_ready";
3
+ readonly VIEW_DISPLAYED: "view_displayed";
4
+ readonly VIEW_CLOSED: "view_closed";
5
+ readonly FLOW_DISPLAYED: "flow_displayed";
6
+ readonly FLOW_CLOSED: "flow_closed";
7
+ };
8
+ export type TelemetryEventName = (typeof TELEMETRY_EVENTS)[keyof typeof TELEMETRY_EVENTS];
9
+ export declare const WIDGET_SELECTOR: "[data-gx-widget]";
10
+ export declare const PARENT_ORIGIN_PARAM: "gx_parent_origin";
11
+ export declare const HOST_EVENT_PREFIX: "getuserfeedback:";
12
+ export declare const HOST_EVENT_KEYS: {
13
+ readonly INSTANCE_FLOW_STATE_CHANGED: "instance:flow:state-changed";
14
+ readonly INSTANCE_FLOW_STATE_CHANGED_INSTANCE: "instance:flow:state-changed:instance";
15
+ readonly INSTANCE_COMMAND_SETTLED: "instance:command:settled";
16
+ readonly INSTANCE_HANDLE_INVALIDATED: "instance:handle:invalidated";
17
+ readonly INSTANCE_OPEN_REQUESTED: "instance:open:requested";
18
+ readonly INSTANCE_COMMAND_UNSUPPORTED: "instance:command:unsupported";
19
+ readonly INSTANCE_READY: "instance:ready";
20
+ readonly INSTANCE_BRIDGE_MOUNTED: "instance:bridge:mounted";
21
+ readonly INSTANCE_BRIDGE_UNMOUNTED: "instance:bridge:unmounted";
22
+ readonly INSTANCE_IFRAME_READY: "instance:iframe:ready";
23
+ readonly INSTANCE_ERROR: "instance:error";
24
+ readonly INSTANCE_APP_EVENT: "instance:app-event";
25
+ };
26
+ /**
27
+ * Stable capability tokens announced by the browser SDK during init.
28
+ * These are versioned and namespaced to support forward-compatible negotiation.
29
+ */
30
+ export declare const SDK_PROTOCOL_CAPABILITIES: readonly ["gx.protocol.public.v1", "gx.command.configure.v1", "gx.command.open.v1", "gx.command.prefetch.v1", "gx.command.prerender.v1", "gx.command.identify.v1", "gx.command.setContainer.v1", "gx.command.close.v1", "gx.command.reset.v1", "gx.command.setDefaultContainerPolicy.v1"];
31
+ export type SdkProtocolCapability = (typeof SDK_PROTOCOL_CAPABILITIES)[number];
32
+ export declare const LOADER_RUNTIME_GLOBAL_KEY: "__getuserfeedback_runtime";
33
+ export declare const LOADER_RUNTIME_PROTOCOL_VERSION: "v1";
34
+ export declare const toHostEventName: (key: string) => string;
35
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/host/constants.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,gBAAgB;;;;;;CAMnB,CAAC;AAEX,MAAM,MAAM,kBAAkB,GAC7B,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,OAAO,gBAAgB,CAAC,CAAC;AAI1D,eAAO,MAAM,eAAe,EAAG,kBAA2B,CAAC;AAE3D,eAAO,MAAM,mBAAmB,EAAG,kBAA2B,CAAC;AAI/D,eAAO,MAAM,iBAAiB,EAAG,kBAA2B,CAAC;AAE7D,eAAO,MAAM,eAAe;;;;;;;;;;;;;CAalB,CAAC;AAgDX;;;GAGG;AACH,eAAO,MAAM,yBAAyB,2RAG5B,CAAC;AAEX,MAAM,MAAM,qBAAqB,GAAG,CAAC,OAAO,yBAAyB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE/E,eAAO,MAAM,yBAAyB,EAAG,2BAAoC,CAAC;AAC9E,eAAO,MAAM,+BAA+B,EAAG,IAAa,CAAC;AAG7D,eAAO,MAAM,eAAe,GAAI,KAAK,MAAM,KAAG,MACjB,CAAC"}
@@ -0,0 +1,62 @@
1
+ export const TELEMETRY_EVENTS = {
2
+ INSTANCE_READY: "instance_ready",
3
+ VIEW_DISPLAYED: "view_displayed",
4
+ VIEW_CLOSED: "view_closed",
5
+ FLOW_DISPLAYED: "flow_displayed",
6
+ FLOW_CLOSED: "flow_closed",
7
+ };
8
+ // Data attribute used to anchor widget styles & root element. Shared so build
9
+ // scripts, runtime code and tests stay in sync.
10
+ export const WIDGET_SELECTOR = "[data-gx-widget]";
11
+ export const PARENT_ORIGIN_PARAM = "gx_parent_origin";
12
+ // Host event names (DOM events emitted by widget for external consumption)
13
+ // Uses prefix "getuserfeedback:" for namespacing
14
+ export const HOST_EVENT_PREFIX = "getuserfeedback:";
15
+ export const HOST_EVENT_KEYS = {
16
+ INSTANCE_FLOW_STATE_CHANGED: "instance:flow:state-changed",
17
+ INSTANCE_FLOW_STATE_CHANGED_INSTANCE: "instance:flow:state-changed:instance",
18
+ INSTANCE_COMMAND_SETTLED: "instance:command:settled",
19
+ INSTANCE_HANDLE_INVALIDATED: "instance:handle:invalidated",
20
+ INSTANCE_OPEN_REQUESTED: "instance:open:requested",
21
+ INSTANCE_COMMAND_UNSUPPORTED: "instance:command:unsupported",
22
+ INSTANCE_READY: "instance:ready",
23
+ INSTANCE_BRIDGE_MOUNTED: "instance:bridge:mounted",
24
+ INSTANCE_BRIDGE_UNMOUNTED: "instance:bridge:unmounted",
25
+ INSTANCE_IFRAME_READY: "instance:iframe:ready",
26
+ INSTANCE_ERROR: "instance:error",
27
+ INSTANCE_APP_EVENT: "instance:app-event",
28
+ };
29
+ const SDK_COMMAND_CAPABILITY_BY_KIND = {
30
+ configure: "gx.command.configure.v1",
31
+ open: "gx.command.open.v1",
32
+ prefetch: "gx.command.prefetch.v1",
33
+ prerender: "gx.command.prerender.v1",
34
+ identify: "gx.command.identify.v1",
35
+ setContainer: "gx.command.setContainer.v1",
36
+ close: "gx.command.close.v1",
37
+ reset: "gx.command.reset.v1",
38
+ setDefaultContainerPolicy: "gx.command.setDefaultContainerPolicy.v1",
39
+ };
40
+ const SDK_COMMAND_CAPABILITIES = [
41
+ SDK_COMMAND_CAPABILITY_BY_KIND.configure,
42
+ SDK_COMMAND_CAPABILITY_BY_KIND.open,
43
+ SDK_COMMAND_CAPABILITY_BY_KIND.prefetch,
44
+ SDK_COMMAND_CAPABILITY_BY_KIND.prerender,
45
+ SDK_COMMAND_CAPABILITY_BY_KIND.identify,
46
+ SDK_COMMAND_CAPABILITY_BY_KIND.setContainer,
47
+ SDK_COMMAND_CAPABILITY_BY_KIND.close,
48
+ SDK_COMMAND_CAPABILITY_BY_KIND.reset,
49
+ SDK_COMMAND_CAPABILITY_BY_KIND.setDefaultContainerPolicy,
50
+ ];
51
+ /**
52
+ * Stable capability tokens announced by the browser SDK during init.
53
+ * These are versioned and namespaced to support forward-compatible negotiation.
54
+ */
55
+ export const SDK_PROTOCOL_CAPABILITIES = [
56
+ "gx.protocol.public.v1",
57
+ ...SDK_COMMAND_CAPABILITIES,
58
+ ];
59
+ export const LOADER_RUNTIME_GLOBAL_KEY = "__getuserfeedback_runtime";
60
+ export const LOADER_RUNTIME_PROTOCOL_VERSION = "v1";
61
+ // Helper to construct full event name with prefix
62
+ export const toHostEventName = (key) => `${HOST_EVENT_PREFIX}${key}`;
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Types and type guards for flow state and lifecycle events.
3
+ */
4
+ /** Current state of the flow: whether it is open, loading, and its dimensions (if known). */
5
+ export interface FlowState {
6
+ /** True when the flow is visible. */
7
+ isOpen: boolean;
8
+ /** True when the flow is loading (e.g. fetching or rendering). */
9
+ isLoading: boolean;
10
+ /** Flow width in pixels when known. */
11
+ width?: number;
12
+ /** Flow height in pixels when known. */
13
+ height?: number;
14
+ }
15
+ export interface FlowStateChangedDetail extends FlowState {
16
+ instanceId: string;
17
+ flowHandleId: string;
18
+ }
19
+ export interface InstanceFlowStateChangedDetail extends FlowState {
20
+ instanceId: string;
21
+ }
22
+ export interface OpenRequestedDetail {
23
+ instanceId: string;
24
+ source: "command" | "targeting";
25
+ flowId: string;
26
+ flowHandleId?: string;
27
+ hideCloseButton?: boolean;
28
+ }
29
+ export declare const HANDLE_INVALIDATED_REASON_CODES: readonly [
30
+ "RESET",
31
+ "CLOSED",
32
+ "STALE_HANDLE",
33
+ "OWNERSHIP_CONFLICT",
34
+ "OWNER_DISPOSED",
35
+ "UPSTREAM_INVALIDATED",
36
+ "INTERNAL"
37
+ ];
38
+ export type HandleInvalidatedReasonCode = (typeof HANDLE_INVALIDATED_REASON_CODES)[number];
39
+ export declare const HANDLE_INVALIDATED_SOURCES: readonly ["loader", "core"];
40
+ export type HandleInvalidatedSource = (typeof HANDLE_INVALIDATED_SOURCES)[number];
41
+ /** Generic handle lifecycle event emitted when a previously issued handle is invalidated upstream. */
42
+ export interface HandleInvalidatedDetail {
43
+ instanceId: string;
44
+ handleKind: string;
45
+ handleId: string;
46
+ reasonCode: HandleInvalidatedReasonCode;
47
+ reasonMessage?: string;
48
+ relatedRequestId?: string;
49
+ source: HandleInvalidatedSource;
50
+ at: number;
51
+ }
52
+ export declare function isFlowStateChangedDetail(detail: unknown): detail is FlowStateChangedDetail;
53
+ export declare function isInstanceFlowStateChangedDetail(detail: unknown): detail is InstanceFlowStateChangedDetail;
54
+ export declare function isOpenRequestedDetail(detail: unknown): detail is OpenRequestedDetail;
55
+ export declare function isHandleInvalidatedDetail(detail: unknown): detail is HandleInvalidatedDetail;
56
+ export interface CommandSettledSuccessDetail {
57
+ requestId: string;
58
+ instanceId: string | null;
59
+ kind: string;
60
+ ok: true;
61
+ result?: unknown;
62
+ }
63
+ export interface CommandSettledFailureDetail {
64
+ requestId: string;
65
+ instanceId: string | null;
66
+ kind: string;
67
+ ok: false;
68
+ error: {
69
+ message: string;
70
+ code?: string;
71
+ };
72
+ }
73
+ export type CommandSettledDetail = CommandSettledSuccessDetail | CommandSettledFailureDetail;
74
+ export declare function isCommandSettledDetail(detail: unknown): detail is CommandSettledDetail;
75
+ /** Result of extracting a flow handle from a successful open/prerender/prefetch settlement. */
76
+ export type FlowHandleFromSettlement = {
77
+ flowHandleId: string;
78
+ flowRunId?: string;
79
+ };
80
+ /**
81
+ * Host-facing handle result: same shape as FlowHandleFromSettlement.
82
+ * Used when projecting instance:command:settled so the SDK receives flowHandleId (and flowRunId when present).
83
+ */
84
+ export type HostHandleSettlementResult = FlowHandleFromSettlement;
85
+ /**
86
+ * Return the host-facing handle result for a command settlement, or null.
87
+ * Use in the loader when projecting instance:command:settled; keeps the host contract in one place (no zod).
88
+ */
89
+ export declare function getHostHandleResultFromSettlement(detail: {
90
+ ok: boolean;
91
+ kind: string;
92
+ result?: unknown;
93
+ }): HostHandleSettlementResult | null;
94
+ /**
95
+ * Extract flow handle from a successful command settlement when the command
96
+ * is one that can return a handle (open, prerender, prefetch). Uses plain
97
+ * checks only (no Zod). Returns null if detail is not ok, kind is not one of
98
+ * those, or result does not contain a non-empty flowHandleId.
99
+ */
100
+ export declare function getFlowHandleFromSettlementDetail(detail: {
101
+ ok: boolean;
102
+ kind: string;
103
+ result?: unknown;
104
+ }): FlowHandleFromSettlement | null;
105
+ //# sourceMappingURL=host-event-contract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host-event-contract.d.ts","sourceRoot":"","sources":["../../src/host/host-event-contract.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Types and type guards for flow state and lifecycle events.
3
+ */
4
+ export const HANDLE_INVALIDATED_REASON_CODES = [
5
+ "RESET",
6
+ "CLOSED",
7
+ "STALE_HANDLE",
8
+ "OWNERSHIP_CONFLICT",
9
+ "OWNER_DISPOSED",
10
+ "UPSTREAM_INVALIDATED",
11
+ "INTERNAL",
12
+ ];
13
+ export const HANDLE_INVALIDATED_SOURCES = [
14
+ "loader",
15
+ "core",
16
+ ];
17
+ const isNonEmptyString = (v) => typeof v === "string" && v.trim().length > 0;
18
+ const isRecord = (v) => typeof v === "object" && v !== null;
19
+ const HANDLE_INVALIDATED_REASON_CODE_SET = new Set(HANDLE_INVALIDATED_REASON_CODES);
20
+ const isHandleInvalidatedReasonCode = (v) => typeof v === "string" && HANDLE_INVALIDATED_REASON_CODE_SET.has(v);
21
+ const HANDLE_INVALIDATED_SOURCE_SET = new Set(HANDLE_INVALIDATED_SOURCES);
22
+ const isHandleInvalidatedSource = (v) => typeof v === "string" && HANDLE_INVALIDATED_SOURCE_SET.has(v);
23
+ export function isFlowStateChangedDetail(detail) {
24
+ if (!isRecord(detail))
25
+ return false;
26
+ const { instanceId, isOpen, isLoading, width, height, flowHandleId } = detail;
27
+ return (isNonEmptyString(instanceId) &&
28
+ typeof isOpen === "boolean" &&
29
+ typeof isLoading === "boolean" &&
30
+ (typeof width === "undefined" || typeof width === "number") &&
31
+ (typeof height === "undefined" || typeof height === "number") &&
32
+ isNonEmptyString(flowHandleId));
33
+ }
34
+ export function isInstanceFlowStateChangedDetail(detail) {
35
+ if (!isRecord(detail))
36
+ return false;
37
+ const { instanceId, isOpen, isLoading, width, height } = detail;
38
+ return (isNonEmptyString(instanceId) &&
39
+ typeof isOpen === "boolean" &&
40
+ typeof isLoading === "boolean" &&
41
+ (typeof width === "undefined" || typeof width === "number") &&
42
+ (typeof height === "undefined" || typeof height === "number"));
43
+ }
44
+ export function isOpenRequestedDetail(detail) {
45
+ if (!isRecord(detail))
46
+ return false;
47
+ const { instanceId, source, flowId, flowHandleId, hideCloseButton } = detail;
48
+ return (isNonEmptyString(instanceId) &&
49
+ (source === "command" || source === "targeting") &&
50
+ isNonEmptyString(flowId) &&
51
+ (typeof flowHandleId === "undefined" || isNonEmptyString(flowHandleId)) &&
52
+ (typeof hideCloseButton === "undefined" ||
53
+ typeof hideCloseButton === "boolean"));
54
+ }
55
+ export function isHandleInvalidatedDetail(detail) {
56
+ if (!isRecord(detail))
57
+ return false;
58
+ const { instanceId, handleKind, handleId, reasonCode, reasonMessage, relatedRequestId, source, at, } = detail;
59
+ return (isNonEmptyString(instanceId) &&
60
+ isNonEmptyString(handleKind) &&
61
+ isNonEmptyString(handleId) &&
62
+ isHandleInvalidatedReasonCode(reasonCode) &&
63
+ (typeof reasonMessage === "undefined" ||
64
+ typeof reasonMessage === "string") &&
65
+ (typeof relatedRequestId === "undefined" ||
66
+ isNonEmptyString(relatedRequestId)) &&
67
+ isHandleInvalidatedSource(source) &&
68
+ typeof at === "number" &&
69
+ Number.isFinite(at));
70
+ }
71
+ export function isCommandSettledDetail(detail) {
72
+ if (!isRecord(detail))
73
+ return false;
74
+ const { requestId, instanceId, kind, ok, error } = detail;
75
+ if (!isNonEmptyString(requestId) ||
76
+ (typeof instanceId !== "string" && instanceId !== null) ||
77
+ typeof kind !== "string" ||
78
+ typeof ok !== "boolean") {
79
+ return false;
80
+ }
81
+ if (ok === true) {
82
+ return true;
83
+ }
84
+ if (ok === false && isRecord(error)) {
85
+ return typeof error.message === "string";
86
+ }
87
+ return false;
88
+ }
89
+ /**
90
+ * Return the host-facing handle result for a command settlement, or null.
91
+ * Use in the loader when projecting instance:command:settled; keeps the host contract in one place (no zod).
92
+ */
93
+ export function getHostHandleResultFromSettlement(detail) {
94
+ return getFlowHandleFromSettlementDetail(detail);
95
+ }
96
+ const HANDLE_RETURNING_KINDS = [
97
+ "open",
98
+ "prerender",
99
+ "prefetch",
100
+ ];
101
+ const HANDLE_RETURNING_KIND_SET = new Set(HANDLE_RETURNING_KINDS);
102
+ const isHandleReturningKind = (kind) => HANDLE_RETURNING_KIND_SET.has(kind);
103
+ /**
104
+ * Extract flow handle from a successful command settlement when the command
105
+ * is one that can return a handle (open, prerender, prefetch). Uses plain
106
+ * checks only (no Zod). Returns null if detail is not ok, kind is not one of
107
+ * those, or result does not contain a non-empty flowHandleId.
108
+ */
109
+ export function getFlowHandleFromSettlementDetail(detail) {
110
+ if (!detail.ok || !isHandleReturningKind(detail.kind)) {
111
+ return null;
112
+ }
113
+ const result = detail.result;
114
+ if (!isRecord(result)) {
115
+ return null;
116
+ }
117
+ const { flowHandleId, flowRunId } = result;
118
+ if (!isNonEmptyString(flowHandleId)) {
119
+ return null;
120
+ }
121
+ return {
122
+ flowHandleId,
123
+ ...(isNonEmptyString(flowRunId) && { flowRunId }),
124
+ };
125
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Host-page / npm SDK protocol surface (Zod-free).
3
+ */
4
+ export * from "./command-dispatch.js";
5
+ export * from "./command-settlement.js";
6
+ export * from "./constants.js";
7
+ export * from "./host-event-contract.js";
8
+ export * from "./lazy-handle-client.js";
9
+ export { createCommandRequestId } from "./request-id.js";
10
+ export * from "./sdk-types.js";
11
+ export { createUniqueId } from "./unique-id.js";
12
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/host/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,0BAA0B,CAAC;AACzC,cAAc,yBAAyB,CAAC;AACxC,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AACzD,cAAc,gBAAgB,CAAC;AAC/B,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Host-page / npm SDK protocol surface (Zod-free).
3
+ */
4
+ export * from "./command-dispatch.js";
5
+ export * from "./command-settlement.js";
6
+ export * from "./constants.js";
7
+ export * from "./host-event-contract.js";
8
+ export * from "./lazy-handle-client.js";
9
+ export { createCommandRequestId } from "./request-id.js";
10
+ export * from "./sdk-types.js";
11
+ export { createUniqueId } from "./unique-id.js";
@@ -0,0 +1,37 @@
1
+ type Subscriber<THandleId extends string> = (handleId: THandleId) => void;
2
+ type StageSubscriber<TStage extends string> = (stage: TStage) => void;
3
+ type LifecycleStage = "uninitialized" | "ready" | "invalidated";
4
+ type StageMap<TStage extends string> = {
5
+ uninitialized: TStage;
6
+ ready: TStage;
7
+ invalidated: TStage;
8
+ };
9
+ export type LazyHandleClient<THandleId extends string, TSettlement, TStage extends string = LifecycleStage> = {
10
+ runCommand<TPayload>(buildPayload: (handleId: THandleId | undefined) => TPayload, dispatchAndWait: (payload: TPayload) => Promise<TSettlement>): Promise<TSettlement>;
11
+ getHandleId(): THandleId | undefined;
12
+ subscribeHandle(subscriber: Subscriber<THandleId>): () => void;
13
+ getStage(): TStage;
14
+ setStage(stage: TStage): void;
15
+ subscribeStage(subscriber: StageSubscriber<TStage>): () => void;
16
+ reset(): void;
17
+ invalidate(): void;
18
+ };
19
+ export type CreateLazyHandleClientOptions<THandleId extends string, TSettlement, TStage extends string = LifecycleStage> = {
20
+ getHandleIdFromSettlement: (settlement: TSettlement) => THandleId | undefined;
21
+ stages?: StageMap<TStage>;
22
+ initialStage?: TStage;
23
+ };
24
+ type CreateLazyHandleClientDefaultOptions<THandleId extends string, TSettlement> = {
25
+ getHandleIdFromSettlement: (settlement: TSettlement) => THandleId | undefined;
26
+ initialStage?: LifecycleStage;
27
+ stages?: undefined;
28
+ };
29
+ type CreateLazyHandleClientCustomOptions<THandleId extends string, TSettlement, TStage extends string> = {
30
+ getHandleIdFromSettlement: (settlement: TSettlement) => THandleId | undefined;
31
+ stages: StageMap<TStage>;
32
+ initialStage?: TStage;
33
+ };
34
+ export declare function createLazyHandleClient<THandleId extends string, TSettlement>(options: CreateLazyHandleClientDefaultOptions<THandleId, TSettlement>): LazyHandleClient<THandleId, TSettlement, LifecycleStage>;
35
+ export declare function createLazyHandleClient<THandleId extends string, TSettlement, TStage extends string>(options: CreateLazyHandleClientCustomOptions<THandleId, TSettlement, TStage>): LazyHandleClient<THandleId, TSettlement, TStage>;
36
+ export {};
37
+ //# sourceMappingURL=lazy-handle-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lazy-handle-client.d.ts","sourceRoot":"","sources":["../../src/host/lazy-handle-client.ts"],"names":[],"mappings":"AAAA,KAAK,UAAU,CAAC,SAAS,SAAS,MAAM,IAAI,CAAC,QAAQ,EAAE,SAAS,KAAK,IAAI,CAAC;AAC1E,KAAK,eAAe,CAAC,MAAM,SAAS,MAAM,IAAI,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;AAEtE,KAAK,cAAc,GAAG,eAAe,GAAG,OAAO,GAAG,aAAa,CAAC;AAChE,KAAK,QAAQ,CAAC,MAAM,SAAS,MAAM,IAAI;IACtC,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;CACpB,CAAC;AAQF,MAAM,MAAM,gBAAgB,CAC3B,SAAS,SAAS,MAAM,EACxB,WAAW,EACX,MAAM,SAAS,MAAM,GAAG,cAAc,IACnC;IACH,UAAU,CAAC,QAAQ,EAClB,YAAY,EAAE,CAAC,QAAQ,EAAE,SAAS,GAAG,SAAS,KAAK,QAAQ,EAC3D,eAAe,EAAE,CAAC,OAAO,EAAE,QAAQ,KAAK,OAAO,CAAC,WAAW,CAAC,GAC1D,OAAO,CAAC,WAAW,CAAC,CAAC;IACxB,WAAW,IAAI,SAAS,GAAG,SAAS,CAAC;IACrC,eAAe,CAAC,UAAU,EAAE,UAAU,CAAC,SAAS,CAAC,GAAG,MAAM,IAAI,CAAC;IAC/D,QAAQ,IAAI,MAAM,CAAC;IACnB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,cAAc,CAAC,UAAU,EAAE,eAAe,CAAC,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC;IAChE,KAAK,IAAI,IAAI,CAAC;IACd,UAAU,IAAI,IAAI,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,6BAA6B,CACxC,SAAS,SAAS,MAAM,EACxB,WAAW,EACX,MAAM,SAAS,MAAM,GAAG,cAAc,IACnC;IACH,yBAAyB,EAAE,CAAC,UAAU,EAAE,WAAW,KAAK,SAAS,GAAG,SAAS,CAAC;IAC9E,MAAM,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,KAAK,oCAAoC,CACxC,SAAS,SAAS,MAAM,EACxB,WAAW,IACR;IACH,yBAAyB,EAAE,CAAC,UAAU,EAAE,WAAW,KAAK,SAAS,GAAG,SAAS,CAAC;IAC9E,YAAY,CAAC,EAAE,cAAc,CAAC;IAC9B,MAAM,CAAC,EAAE,SAAS,CAAC;CACnB,CAAC;AAEF,KAAK,mCAAmC,CACvC,SAAS,SAAS,MAAM,EACxB,WAAW,EACX,MAAM,SAAS,MAAM,IAClB;IACH,yBAAyB,EAAE,CAAC,UAAU,EAAE,WAAW,KAAK,SAAS,GAAG,SAAS,CAAC;IAC9E,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAsHF,wBAAgB,sBAAsB,CAAC,SAAS,SAAS,MAAM,EAAE,WAAW,EAC3E,OAAO,EAAE,oCAAoC,CAAC,SAAS,EAAE,WAAW,CAAC,GACnE,gBAAgB,CAAC,SAAS,EAAE,WAAW,EAAE,cAAc,CAAC,CAAC;AAC5D,wBAAgB,sBAAsB,CACrC,SAAS,SAAS,MAAM,EACxB,WAAW,EACX,MAAM,SAAS,MAAM,EAErB,OAAO,EAAE,mCAAmC,CAAC,SAAS,EAAE,WAAW,EAAE,MAAM,CAAC,GAC1E,gBAAgB,CAAC,SAAS,EAAE,WAAW,EAAE,MAAM,CAAC,CAAC"}
@@ -0,0 +1,114 @@
1
+ const DEFAULT_LIFECYCLE_STAGES = {
2
+ uninitialized: "uninitialized",
3
+ ready: "ready",
4
+ invalidated: "invalidated",
5
+ };
6
+ /**
7
+ * Serializes commands for a lazily initialized handle.
8
+ * Each command payload is built only when its turn runs, so it always sees
9
+ * the latest settled handle id from prior commands.
10
+ */
11
+ const createLazyHandleClientWithStages = (input) => {
12
+ let handleId;
13
+ let tail = Promise.resolve();
14
+ let isIdle = true;
15
+ let epoch = 0;
16
+ const { stages } = input;
17
+ let stage = input.initialStage ?? stages.uninitialized;
18
+ const subscribers = new Set();
19
+ const stageSubscribers = new Set();
20
+ const setStage = (nextStage) => {
21
+ if (stage === nextStage) {
22
+ return;
23
+ }
24
+ if (stage === stages.invalidated && nextStage !== stages.invalidated) {
25
+ return;
26
+ }
27
+ stage = nextStage;
28
+ stageSubscribers.forEach((subscriber) => {
29
+ subscriber(nextStage);
30
+ });
31
+ };
32
+ const publishHandle = (nextHandleId) => {
33
+ if (stage === stages.invalidated) {
34
+ return;
35
+ }
36
+ handleId = nextHandleId;
37
+ setStage(stages.ready);
38
+ subscribers.forEach((subscriber) => {
39
+ subscriber(nextHandleId);
40
+ });
41
+ };
42
+ const runCommand = (buildPayload, dispatchAndWait) => {
43
+ const execute = async () => {
44
+ if (stage === stages.invalidated) {
45
+ throw new Error("Handle client is invalidated");
46
+ }
47
+ const commandEpoch = epoch;
48
+ const payload = buildPayload(handleId);
49
+ const settlement = await dispatchAndWait(payload);
50
+ const settledHandleId = input.getHandleIdFromSettlement(settlement);
51
+ if (settledHandleId && commandEpoch === epoch) {
52
+ publishHandle(settledHandleId);
53
+ }
54
+ return settlement;
55
+ };
56
+ const run = isIdle ? execute() : tail.then(execute, execute);
57
+ isIdle = false;
58
+ tail = run.then(() => {
59
+ isIdle = true;
60
+ }, () => {
61
+ isIdle = true;
62
+ });
63
+ return run;
64
+ };
65
+ return {
66
+ runCommand,
67
+ getHandleId: () => handleId,
68
+ subscribeHandle: (subscriber) => {
69
+ subscribers.add(subscriber);
70
+ if (handleId) {
71
+ subscriber(handleId);
72
+ }
73
+ return () => {
74
+ subscribers.delete(subscriber);
75
+ };
76
+ },
77
+ getStage: () => stage,
78
+ setStage,
79
+ subscribeStage: (subscriber) => {
80
+ stageSubscribers.add(subscriber);
81
+ subscriber(stage);
82
+ return () => {
83
+ stageSubscribers.delete(subscriber);
84
+ };
85
+ },
86
+ reset: () => {
87
+ epoch += 1;
88
+ handleId = undefined;
89
+ stage = stages.uninitialized;
90
+ stageSubscribers.forEach((subscriber) => {
91
+ subscriber(stage);
92
+ });
93
+ },
94
+ invalidate: () => {
95
+ epoch += 1;
96
+ handleId = undefined;
97
+ setStage(stages.invalidated);
98
+ },
99
+ };
100
+ };
101
+ export function createLazyHandleClient(options) {
102
+ if ("stages" in options && options.stages) {
103
+ return createLazyHandleClientWithStages({
104
+ getHandleIdFromSettlement: options.getHandleIdFromSettlement,
105
+ stages: options.stages,
106
+ initialStage: options.initialStage,
107
+ });
108
+ }
109
+ return createLazyHandleClientWithStages({
110
+ getHandleIdFromSettlement: options.getHandleIdFromSettlement,
111
+ stages: DEFAULT_LIFECYCLE_STAGES,
112
+ initialStage: options.initialStage,
113
+ });
114
+ }
@@ -0,0 +1,2 @@
1
+ export declare const createCommandRequestId: () => string;
2
+ //# sourceMappingURL=request-id.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-id.d.ts","sourceRoot":"","sources":["../../src/host/request-id.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,sBAAsB,QAAO,MAA+B,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { createUniqueId } from "./unique-id.js";
2
+ export const createCommandRequestId = () => createUniqueId("req");
@@ -0,0 +1,311 @@
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
+ /** Supported identity kinds for analytics and tracking. */
7
+ export type AppEventIdentityType = "anonymousId" | "userId" | "traits.email" | "traits.phone" | "context.device.id" | "context.device.advertisingId" | "context.device.token";
8
+ /** A single identity (e.g. userId or anonymousId) and its value. */
9
+ export type AppEventIdentity = {
10
+ type: AppEventIdentityType;
11
+ value: string;
12
+ };
13
+ /** Page and device context you can attach to tracked events. */
14
+ export type AppEventContext = {
15
+ page?: {
16
+ path?: string;
17
+ referrer?: string;
18
+ search?: string;
19
+ };
20
+ locale?: string;
21
+ userAgent?: string;
22
+ };
23
+ /** Optional fields shared by all flow lifecycle events (traits, identities, context, timing). */
24
+ export type AppEventBase = {
25
+ traits?: Record<string, unknown> | undefined;
26
+ identities?: AppEventIdentity[] | undefined;
27
+ context?: AppEventContext | undefined;
28
+ messageId?: string | undefined;
29
+ timestamp?: number | undefined;
30
+ };
31
+ /** Reference to a specific survey/flow. */
32
+ export type AppEventSurveyReference = {
33
+ surveyId: string;
34
+ };
35
+ /** Event payload when a flow is shown (Flow Viewed event). */
36
+ export type FlowViewedPayload = AppEventBase & {
37
+ event: "Flow Viewed";
38
+ references: AppEventSurveyReference;
39
+ };
40
+ /** Event payload when a user dismisses a flow. */
41
+ export type FlowDismissedPayload = AppEventBase & {
42
+ event: "Flow Dismissed";
43
+ references: AppEventSurveyReference;
44
+ };
45
+ /**
46
+ * Payload for {@link Client.track}. Use this to record when a flow is shown or dismissed.
47
+ * Discriminate on the `event` field (`"Flow Viewed"` or `"Flow Dismissed"`).
48
+ */
49
+ export type AppEventPayload = FlowViewedPayload | FlowDismissedPayload;
50
+ /** How the widget was loaded (e.g. via SDK, GTM, or a tag manager). */
51
+ export type ClientMetaLoader = "sdk" | "gtm" | "segment" | "rudderstack" | "tealium" | "custom";
52
+ /** Delivery method for the widget script (e.g. ESM, script tag). */
53
+ export type ClientMetaTransport = "script-tag" | "esm" | "loader" | "tag-manager" | "snippet";
54
+ /** Detected or configured front-end framework. */
55
+ export type ClientMetaRuntimeFramework = "next" | "vite" | "webpack" | "plain";
56
+ /** Runtime environment (browser or edge). */
57
+ export type ClientMetaRuntimeEnv = "browser" | "edge";
58
+ /** Metadata about the client and integration (e.g. loader, framework). Used for analytics and support. */
59
+ export type ClientMeta = {
60
+ loader: ClientMetaLoader;
61
+ clientName?: string | undefined;
62
+ clientVersion?: string | undefined;
63
+ integrator?: {
64
+ name?: string | undefined;
65
+ version?: string | undefined;
66
+ } | undefined;
67
+ transport?: ClientMetaTransport | undefined;
68
+ runtime?: {
69
+ framework?: ClientMetaRuntimeFramework | undefined;
70
+ runtime?: ClientMetaRuntimeEnv | undefined;
71
+ } | undefined;
72
+ notes?: string | undefined;
73
+ };
74
+ export type RuntimeEndpoints = {
75
+ apiUrl?: string | undefined;
76
+ coreUrl?: string | undefined;
77
+ };
78
+ /**
79
+ * Use host attributes to auto-detect the color scheme ("light" by default). The widget observes these attributes on body and html elements,
80
+ *
81
+ * class="dark" or data-theme="dark" on html or body elements will set the widget to dark mode.
82
+ *
83
+ * class="light" or data-theme="light" will set the widget to light mode.
84
+ *
85
+ * class="system" or data-theme="system" will set the widget to follow the user's OS/browser preference (prefers-color-scheme).
86
+ */
87
+ export type ColorSchemeAutoDetect = {
88
+ /** List of host attribute names (html or body) to observe for color scheme.
89
+ *
90
+ * Default: ["class", "data-theme"]
91
+ */
92
+ autoDetectColorScheme: string[];
93
+ };
94
+ /**
95
+ * Color scheme: either auto-detect from host html and body attributes (default) or provide an explicit value.
96
+ * - { autoDetectColorScheme: ["class", "data-theme"] }
97
+ * - "light" | "dark" | "system"
98
+ */
99
+ export type ColorSchemeConfig = ColorSchemeAutoDetect | "light" | "dark" | "system";
100
+ /** Whether telemetry is enabled. */
101
+ export type TelemetryConfig = {
102
+ enabled?: boolean | undefined;
103
+ };
104
+ /** When true, prints debug logs to console. Narrow down with a namespace string/array eg. `["loader", "handshake"]`. Default `false`. */
105
+ export type DebugConfig = boolean | string | string[];
106
+ /** Status of a consent decision (pending, granted, denied, revoked). */
107
+ export type ConsentStatus = "pending" | "granted" | "denied" | "revoked";
108
+ /** Supported consent scope identifiers for explicit allow-list grants. */
109
+ export type GrantScope = "analytics.storage" | "analytics.measurement" | "personalization.storage" | "ads.storage" | "ads.user_data" | "ads.personalization";
110
+ /** Consent configuration. Use a single decision for all scopes, or provide an explicit allow-list of granted scopes. */
111
+ export type ConsentConfig = ConsentStatus | GrantScope[];
112
+ /** Auth configuration (e.g. JWT for authenticated flows). */
113
+ export type AuthConfig = {
114
+ jwt?: {
115
+ token: string;
116
+ } | null | undefined;
117
+ };
118
+ /** Options for {@link Client.configure}: color scheme, consent, and auth. */
119
+ export type ConfigureOptions = {
120
+ colorScheme?: ColorSchemeConfig | undefined;
121
+ consent?: ConsentConfig | undefined;
122
+ auth?: AuthConfig | undefined;
123
+ };
124
+ /** @internal Options when initializing the widget: API key, color scheme, disableTelemetry, enableDebug, defaultConsent, and optional capabilities. */
125
+ export type InitOptions = {
126
+ apiKey: string;
127
+ colorScheme?: ColorSchemeConfig | undefined;
128
+ disableTelemetry?: boolean | undefined;
129
+ enableDebug?: DebugConfig | undefined;
130
+ defaultConsent?: ConsentConfig | undefined;
131
+ clientMeta?: ClientMeta | undefined;
132
+ capabilities?: string[] | undefined;
133
+ runtimeEndpoints?: RuntimeEndpoints | undefined;
134
+ };
135
+ export type ClientOptions = {
136
+ /** Your project API key. */
137
+ apiKey: string;
138
+ /** Color scheme config: host-driven (loader observes attributes) or explicit. */
139
+ colorScheme?: ColorSchemeConfig | undefined;
140
+ /** Consent settings compatible with common privacy frameworks (GDPR, CCPA, LGPD) and CMP flows.
141
+ *
142
+ * Default: `granted` (all scopes).
143
+ *
144
+ * Accepts either a single decision (`granted` | `denied` | `pending` | `revoked`)
145
+ * or an explicit allow-list of additional granted scopes (`GrantScope[]`).
146
+ *
147
+ * For strict privacy guarantees, use `pending` at init time and later call `client.configure({ consent })` after
148
+ * the user makes a consent choice via your CMP or UI.
149
+ *
150
+ * In array form, listed scopes are granted and all other non-essential scopes are denied.
151
+ *
152
+ * Essential baseline scopes are always granted for core widget operation and cannot be denied:
153
+ * - `functionality.storage`
154
+ * - `security.storage`
155
+ */
156
+ defaultConsent?: ConsentConfig | undefined;
157
+ /** When true, prints debug logs to console. Narrow down with a namespace string/array eg. ```["loader", "handshake"]```. Default `false`.
158
+ */
159
+ enableDebug?: DebugConfig | undefined;
160
+ /** When true, the widget does not load automatically; call `client.load()` when ready. Default `false` */
161
+ disableAutoLoad?: boolean;
162
+ /** Disable anonymous widget telemetry.
163
+ *
164
+ * When `true`, telemetry is not sent.
165
+ *
166
+ * This flag does not disable user targeting and reporting analytics. It's used (by us) for performance monitoring and error tracking.
167
+ *
168
+ * Included telemetry data when enabled: apiKey, ephemeral one-time session id,
169
+ * execution flow, event timestamps, and page origin (domain name).
170
+ *
171
+ * Excluded by default: user identity and detailed page fields (path/referrer/search).
172
+ */
173
+ disableTelemetry?: boolean | undefined;
174
+ _loaderUrl?: string | undefined;
175
+ _coreUrl?: string | undefined;
176
+ _apiUrl?: string | undefined;
177
+ };
178
+ /** Open a flow with a given instance. Omit flowHandleId for first open; core returns it in settlement. */
179
+ export type OpenCommandPayload = {
180
+ kind: "open";
181
+ flowId: string;
182
+ flowHandleId?: string | undefined;
183
+ container?: HTMLElement | undefined;
184
+ /**
185
+ * @deprecated Simply use `container` directly instead.
186
+ */
187
+ containerRequirement?: "any" | "hostOnly" | undefined;
188
+ /**
189
+ * When `true`, the flow view does not show a close button.
190
+ * Useful for mobile or embedded contexts where the host handles dismissal (e.g. via a drawer or back gesture).
191
+ */
192
+ hideCloseButton?: boolean | undefined;
193
+ };
194
+ /** Prefetch a flow. Omit flowHandleId for first prefetch; core returns it in settlement. */
195
+ export type PrefetchCommandPayload = {
196
+ kind: "prefetch";
197
+ flowId: string;
198
+ flowHandleId?: string | undefined;
199
+ };
200
+ /** Prerender a flow. Omit flowHandleId for first prerender; core returns it in settlement. */
201
+ export type PrerenderCommandPayload = {
202
+ kind: "prerender";
203
+ flowId: string;
204
+ flowHandleId?: string | undefined;
205
+ /**
206
+ * When `true`, the prerendered view does not show a close button.
207
+ */
208
+ hideCloseButton?: boolean | undefined;
209
+ };
210
+ /** Identify with traits only (`client.identify(traits)`). */
211
+ export type IdentifyTraitsOnlyCommandPayload = {
212
+ kind: "identify";
213
+ traits: Record<string, unknown>;
214
+ };
215
+ /** Identify with userId and optional traits (`client.identify(userId, traits?)`). */
216
+ export type IdentifyUserCommandPayload = {
217
+ kind: "identify";
218
+ userId: string;
219
+ traits?: Record<string, unknown> | undefined;
220
+ };
221
+ /** Identify command payload: either traits-only or userId+traits shape. */
222
+ export type IdentifyCommandPayload = IdentifyTraitsOnlyCommandPayload | IdentifyUserCommandPayload;
223
+ /** Track a flow shown or dismissed event. */
224
+ export type TrackCommandPayload = AppEventBase & {
225
+ kind: "track";
226
+ event: "Flow Viewed" | "Flow Dismissed";
227
+ references: AppEventSurveyReference;
228
+ };
229
+ /** Attach or detach a resolved flow handle from a host container. */
230
+ export type SetContainerCommandPayload = {
231
+ kind: "setContainer";
232
+ flowHandleId: string;
233
+ container: HTMLElement | null;
234
+ };
235
+ /** Default container behavior for flows in an instance. */
236
+ export type ContainerPolicy = {
237
+ kind: "floating";
238
+ } | {
239
+ kind: "hostContainer";
240
+ host: HTMLElement | null;
241
+ sharing: "shared" | "perFlowRun";
242
+ };
243
+ /** Set the instance-level default container policy. */
244
+ export type SetDefaultContainerPolicyCommandPayload = {
245
+ kind: "setDefaultContainerPolicy";
246
+ policy: ContainerPolicy;
247
+ };
248
+ /** Update color scheme, consent, and auth. */
249
+ export type ConfigureCommandPayload = {
250
+ kind: "configure";
251
+ opts: ConfigureOptions;
252
+ };
253
+ /** Initialize the widget with options. */
254
+ export type InitCommandPayload = {
255
+ kind: "init";
256
+ opts: InitOptions;
257
+ };
258
+ /** Close a flow. */
259
+ export type CloseCommandPayload = {
260
+ kind: "close";
261
+ flowHandleId?: string | undefined;
262
+ };
263
+ /** Reset widget state. */
264
+ export type ResetCommandPayload = {
265
+ kind: "reset";
266
+ };
267
+ /** Union of all widget action payloads (open, prefetch, identify, configure, etc.). */
268
+ export type PublicCommandPayload = OpenCommandPayload | PrefetchCommandPayload | PrerenderCommandPayload | IdentifyCommandPayload | SetContainerCommandPayload | SetDefaultContainerPolicyCommandPayload | ConfigureCommandPayload | InitCommandPayload | CloseCommandPayload | ResetCommandPayload;
269
+ /** Input for a single widget action; same shape as {@link PublicCommandPayload}. */
270
+ export type Command = PublicCommandPayload;
271
+ /** Wrapper for a widget action (version, request id, idempotency key, optional client metadata, and the action payload). */
272
+ export type CommandEnvelope = {
273
+ version: "1";
274
+ instanceId?: string | undefined;
275
+ requestId: string;
276
+ idempotencyKey: string;
277
+ clientMeta?: ClientMeta | undefined;
278
+ command: PublicCommandPayload;
279
+ };
280
+ /** Identity kind for telemetry events; same as {@link AppEventIdentityType}. */
281
+ export type TelemetryIdentityType = AppEventIdentityType;
282
+ /**
283
+ * Event passed to the `beforeSend` hook in {@link Client.use}.
284
+ * When `debug` is true, identities and full page context are included; when false, no identities and page is origin-only.
285
+ * TODO(telemetry-contract-cleanup): Rename this SDK-facing type to
286
+ * WidgetTelemetryEvent in a coordinated SDK-facing change so we can retire the
287
+ * generic TelemetryEvent alias from shared telemetry.
288
+ */
289
+ export type TelemetryEvent = {
290
+ type: string;
291
+ timestamp: number;
292
+ apiKey: string;
293
+ /** When true, identities and full page context are present; when false, no identities and page.origin only. */
294
+ debug: boolean;
295
+ identities: Array<{
296
+ type: TelemetryIdentityType;
297
+ value: string;
298
+ }>;
299
+ context: {
300
+ page: {
301
+ path?: string | null | undefined;
302
+ referrer?: string | null | undefined;
303
+ search?: string | null | undefined;
304
+ origin?: string | null | undefined;
305
+ };
306
+ widgetVersion: string;
307
+ clientMeta?: ClientMeta | undefined;
308
+ };
309
+ properties?: Record<string, unknown> | undefined;
310
+ };
311
+ //# sourceMappingURL=sdk-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sdk-types.d.ts","sourceRoot":"","sources":["../../src/host/sdk-types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
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
+ export {};
@@ -0,0 +1,10 @@
1
+ /**
2
+ * SDK-facing protocol surface: types (from sdk-types) and values only.
3
+ * Does not re-export schema modules so SDK dist stays zod-free.
4
+ */
5
+ export { createCommandDispatch } from "./command-dispatch.js";
6
+ export { COMMAND_SETTLEMENT_TIMEOUT_CODE, CommandSettlementError, CommandSettlementTimeoutError, toCommandSettlementError, waitForCommandSettlement, } from "./command-settlement.js";
7
+ export type { FlowHandleFromSettlement, FlowState, } from "./host-event-contract.js";
8
+ export { getFlowHandleFromSettlementDetail, isFlowStateChangedDetail, isHandleInvalidatedDetail, isInstanceFlowStateChangedDetail, } from "./host-event-contract.js";
9
+ export type { AppEventPayload, ClientMeta, ClientOptions, Command, CommandEnvelope, ConfigureOptions, ContainerPolicy, InitOptions, PublicCommandPayload, TelemetryEvent, } from "./sdk-types.js";
10
+ //# sourceMappingURL=sdk.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sdk.d.ts","sourceRoot":"","sources":["../../src/host/sdk.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EACN,+BAA+B,EAC/B,sBAAsB,EACtB,6BAA6B,EAC7B,wBAAwB,EACxB,wBAAwB,GACxB,MAAM,yBAAyB,CAAC;AACjC,YAAY,EACX,wBAAwB,EACxB,SAAS,GACT,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACN,iCAAiC,EACjC,wBAAwB,EACxB,yBAAyB,EACzB,gCAAgC,GAChC,MAAM,0BAA0B,CAAC;AAClC,YAAY,EACX,eAAe,EACf,UAAU,EACV,aAAa,EACb,OAAO,EACP,eAAe,EACf,gBAAgB,EAChB,eAAe,EACf,WAAW,EACX,oBAAoB,EAGpB,cAAc,GACd,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * SDK-facing protocol surface: types (from sdk-types) and values only.
3
+ * Does not re-export schema modules so SDK dist stays zod-free.
4
+ */
5
+ export { createCommandDispatch } from "./command-dispatch.js";
6
+ export { COMMAND_SETTLEMENT_TIMEOUT_CODE, CommandSettlementError, CommandSettlementTimeoutError, toCommandSettlementError, waitForCommandSettlement, } from "./command-settlement.js";
7
+ export { getFlowHandleFromSettlementDetail, isFlowStateChangedDetail, isHandleInvalidatedDetail, isInstanceFlowStateChangedDetail, } from "./host-event-contract.js";
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Generates a unique string ID. Uses crypto.randomUUID when available,
3
+ * otherwise falls back to a timestamp + random string.
4
+ *
5
+ * @param prefix - Optional prefix for the fallback format (e.g. "req", "err").
6
+ * Only affects fallback; UUIDs have no prefix.
7
+ */
8
+ export declare function createUniqueId(prefix?: string): string;
9
+ //# sourceMappingURL=unique-id.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unique-id.d.ts","sourceRoot":"","sources":["../../src/host/unique-id.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAStD"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Generates a unique string ID. Uses crypto.randomUUID when available,
3
+ * otherwise falls back to a timestamp + random string.
4
+ *
5
+ * @param prefix - Optional prefix for the fallback format (e.g. "req", "err").
6
+ * Only affects fallback; UUIDs have no prefix.
7
+ */
8
+ export function createUniqueId(prefix) {
9
+ if (typeof globalThis.crypto !== "undefined" &&
10
+ typeof globalThis.crypto.randomUUID === "function") {
11
+ return globalThis.crypto.randomUUID();
12
+ }
13
+ const fallback = `${Date.now()}_${Math.random().toString(36).slice(2)}`;
14
+ return prefix ? `${prefix}_${fallback}` : fallback;
15
+ }
package/package.json ADDED
@@ -0,0 +1,73 @@
1
+ {
2
+ "name": "@getuserfeedback/protocol",
3
+ "version": "0.1.0",
4
+ "description": "getuserfeedback widget protocol — host surface and (later) wire protocol",
5
+ "keywords": [
6
+ "getuserfeedback",
7
+ "widget",
8
+ "protocol"
9
+ ],
10
+ "license": "MIT",
11
+ "publishConfig": {
12
+ "access": "public"
13
+ },
14
+ "type": "module",
15
+ "sideEffects": false,
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "exports": {
20
+ "./host": {
21
+ "types": "./dist/host/index.d.ts",
22
+ "import": "./dist/host/index.js"
23
+ },
24
+ "./host/sdk-types": {
25
+ "types": "./dist/host/sdk-types.d.ts",
26
+ "import": "./dist/host/sdk-types.js"
27
+ },
28
+ "./host/host-event-contract": {
29
+ "types": "./dist/host/host-event-contract.d.ts",
30
+ "import": "./dist/host/host-event-contract.js"
31
+ },
32
+ "./host/constants": {
33
+ "types": "./dist/host/constants.d.ts",
34
+ "import": "./dist/host/constants.js"
35
+ },
36
+ "./host/command-dispatch": {
37
+ "types": "./dist/host/command-dispatch.d.ts",
38
+ "import": "./dist/host/command-dispatch.js"
39
+ },
40
+ "./host/command-settlement": {
41
+ "types": "./dist/host/command-settlement.d.ts",
42
+ "import": "./dist/host/command-settlement.js"
43
+ },
44
+ "./host/sdk": {
45
+ "types": "./dist/host/sdk.d.ts",
46
+ "import": "./dist/host/sdk.js"
47
+ },
48
+ "./host/request-id": {
49
+ "types": "./dist/host/request-id.d.ts",
50
+ "import": "./dist/host/request-id.js"
51
+ },
52
+ "./host/unique-id": {
53
+ "types": "./dist/host/unique-id.d.ts",
54
+ "import": "./dist/host/unique-id.js"
55
+ },
56
+ "./host/lazy-handle-client": {
57
+ "types": "./dist/host/lazy-handle-client.d.ts",
58
+ "import": "./dist/host/lazy-handle-client.js"
59
+ }
60
+ },
61
+ "scripts": {
62
+ "lint": "ultracite check .",
63
+ "build": "bun x rimraf dist tsconfig.tsbuildinfo && tsc -b tsconfig.json",
64
+ "typecheck": "tsc -b tsconfig.json",
65
+ "pack:verify": "node ../../scripts/pack-and-verify.cjs",
66
+ "publish:dry-run": "node ../../scripts/publish-package.cjs . -- --dry-run",
67
+ "publish:npm": "node ../../scripts/publish-package.cjs ."
68
+ },
69
+ "devDependencies": {
70
+ "rimraf": "^6.0.0",
71
+ "typescript": "^5.8.3"
72
+ }
73
+ }