@journeeze/sdk 0.0.0-stage → 0.1.0-rc.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.
Files changed (60) hide show
  1. package/AGENTS.md +67 -0
  2. package/CHANGELOG.md +30 -0
  3. package/LICENSE.md +27 -0
  4. package/README.md +164 -2
  5. package/dist/THIRD_PARTY_LICENSES.txt +246 -0
  6. package/dist/consent-CoKD33Rm.js +170 -0
  7. package/dist/context-UnG6_X61.js +17 -0
  8. package/dist/contract.js +10 -0
  9. package/dist/feedback-C4CYtS2A.js +196 -0
  10. package/dist/help-C1x-y2f9.js +878 -0
  11. package/dist/hook-BdG4UL4k.js +91 -0
  12. package/dist/index-B_nob8S0.js +2091 -0
  13. package/dist/index-CfCbJJXa.js +20 -0
  14. package/dist/index.js +10 -0
  15. package/dist/loader-Ba4X-WNl.js +95 -0
  16. package/dist/react.js +70 -0
  17. package/dist/resolve-BfWByuTp.js +97 -0
  18. package/dist/script.js +13 -0
  19. package/dist/signals-E3VEdmWH.js +477 -0
  20. package/dist/telemetry-Cik-cYzw.js +140 -0
  21. package/dist/transport-B5YJFQEF.js +60 -0
  22. package/dist/types/config.d.ts +17 -0
  23. package/dist/types/context.d.ts +6 -0
  24. package/dist/types/contract.d.ts +13 -0
  25. package/dist/types/help/core/client.d.ts +26 -0
  26. package/dist/types/help/core/controller.d.ts +175 -0
  27. package/dist/types/help/core/events.d.ts +8 -0
  28. package/dist/types/help/core/help.d.ts +24 -0
  29. package/dist/types/help/core/highlight.d.ts +38 -0
  30. package/dist/types/help/core/panel.d.ts +11 -0
  31. package/dist/types/help/core/search.d.ts +7 -0
  32. package/dist/types/help/ui/callout.d.ts +2 -0
  33. package/dist/types/help/ui/panel.d.ts +3 -0
  34. package/dist/types/help/ui/request.d.ts +2 -0
  35. package/dist/types/help/ui/styles.d.ts +1 -0
  36. package/dist/types/index.d.ts +7 -0
  37. package/dist/types/invite.d.ts +4 -0
  38. package/dist/types/loader.d.ts +57 -0
  39. package/dist/types/mask.d.ts +10 -0
  40. package/dist/types/react.d.ts +9 -0
  41. package/dist/types/resolve/resolve.d.ts +12 -0
  42. package/dist/types/script.d.ts +6 -0
  43. package/dist/types/signals/cel.d.ts +8 -0
  44. package/dist/types/signals/hook.d.ts +10 -0
  45. package/dist/types/signals/signals.conformance.d.ts +2 -0
  46. package/dist/types/signals/signals.d.ts +145 -0
  47. package/dist/types/telemetry/telemetry.d.ts +63 -0
  48. package/dist/types/transport.d.ts +30 -0
  49. package/dist/types/types.d.ts +391 -0
  50. package/dist/types/widget/anchor.d.ts +11 -0
  51. package/dist/types/widget/capture.d.ts +28 -0
  52. package/dist/types/widget/consent.d.ts +25 -0
  53. package/dist/types/widget/feedback.d.ts +36 -0
  54. package/dist/types/widget/icons.d.ts +14 -0
  55. package/dist/types/widget/outbox.d.ts +69 -0
  56. package/dist/types/widget/styles.d.ts +1 -0
  57. package/dist/types/widget.d.ts +27 -0
  58. package/dist/types/wire.d.ts +66 -0
  59. package/dist/widget-DQZm8sAC.js +1599 -0
  60. package/package.json +68 -4
@@ -0,0 +1,6 @@
1
+ /** The tflo id grammar (ADR 0009): an analytics contract name, never user content. */
2
+ export declare const TFLOW_ID: RegExp;
3
+ /** The nearest `data-tflow-id` at or above `el`, if it is a valid contract name. */
4
+ export declare function tflowIdAt(el: Element | null): string | undefined;
5
+ /** Path only: query strings and fragments can carry tokens and PII, so they never leave the page. */
6
+ export declare function pagePath(href: string): string;
@@ -0,0 +1,13 @@
1
+ export { SITE_KEY_PATTERN, USER_ID_PATTERN } from "./loader";
2
+ /** `apiPath`: one or more `/segment`s of URL-safe characters; no trailing `/`, query or fragment. */
3
+ export declare const API_PATH_PATTERN: RegExp;
4
+ /** A context value the collector accepts: 1..=128 printable characters (no control characters). */
5
+ export declare const CONTEXT_VALUE_MAX_LEN = 128;
6
+ /**
7
+ * The recommended context-id grammar (the tflo id grammar, ADR 0009): an id, never user content.
8
+ * The SDK warns once per field when a value does not match it, and still sends it if it is valid
9
+ * for the collector ({@link CONTEXT_VALUE_MAX_LEN}).
10
+ */
11
+ export declare const CONTEXT_ID_PATTERN: RegExp;
12
+ /** The context fields, in wire order. `setContext` merges; `undefined` clears one field. */
13
+ export declare const CONTEXT_FIELDS: readonly ["persona", "journeyId", "journeyStep", "device", "businessType", "state"];
@@ -0,0 +1,26 @@
1
+ import type { GetGuideResponse, GetHelpIndexResponse } from "../../types";
2
+ export interface HelpClientOptions {
3
+ endpoint: string;
4
+ siteKey: string;
5
+ /** Sent on help reads when set: reviewers sites require it, public sites validate it (ADR 0013 §1). */
6
+ reviewerToken?: string | undefined;
7
+ fetchImpl?: typeof fetch;
8
+ now?: () => number;
9
+ /** How long a value is used without asking the collector again. */
10
+ freshMs?: number;
11
+ }
12
+ /** Refetch a guide this long before its asset links stop working. */
13
+ export declare const LINK_MARGIN_MS = 60000;
14
+ export declare class HelpClient {
15
+ private readonly opts;
16
+ private indexEntry;
17
+ private readonly guides;
18
+ private readonly inflight;
19
+ private readonly now;
20
+ private readonly freshMs;
21
+ constructor(opts: HelpClientOptions);
22
+ index(): Promise<GetHelpIndexResponse>;
23
+ guide(journeyId: string): Promise<GetGuideResponse>;
24
+ private body;
25
+ private read;
26
+ }
@@ -0,0 +1,175 @@
1
+ import type { GetGuideResponse, HelpIndexEntry } from "../../types";
2
+ import type { AnchorResult, StepTarget } from "./highlight";
3
+ export type CheckinAnswer = "understood" | "confused";
4
+ export type CheckinPhase = "idle" | "asking" | "needs-consent" | "sending";
5
+ export type SendOutcome = "sent" | "queued" | "dropped";
6
+ export interface GuideStepView {
7
+ index: number;
8
+ title: string;
9
+ instruction: string;
10
+ tflowId?: string | undefined;
11
+ /** Ways to find the step's element, most stable first (a lone tflo anchor when none were stored). */
12
+ targets?: StepTarget[] | undefined;
13
+ /** The host's `journeyStep` id for this step, when known (contract C0c v1.1-r2). */
14
+ stepRef?: string | undefined;
15
+ /** Absolute, short-lived URL (same origin as the collector). */
16
+ screenshotUrl?: string | undefined;
17
+ videoStartMs?: number | undefined;
18
+ videoEndMs?: number | undefined;
19
+ }
20
+ export interface GuideView {
21
+ journeyId: string;
22
+ /** The journey's id within its project (`r1-j09`); check-ins are recorded against it. */
23
+ slug: string;
24
+ title: string;
25
+ goal: string;
26
+ steps: GuideStepView[];
27
+ videoUrl?: string | undefined;
28
+ captionsUrl?: string | undefined;
29
+ /** The guide content shown (`GetGuide.contentVersion`); help events quote it. */
30
+ version?: string | undefined;
31
+ }
32
+ export type HelpState = {
33
+ view: "closed";
34
+ }
35
+ /** The site has help switched off (collector `failed_precondition`). Render nothing; final. */
36
+ | {
37
+ view: "disabled";
38
+ } | {
39
+ view: "index";
40
+ status: "loading" | "ready" | "error";
41
+ query: string;
42
+ results: HelpIndexEntry[];
43
+ total: number;
44
+ /** A request in progress (absent: idle). */
45
+ signal?: SignalPhase | undefined;
46
+ /** Result of the last callout/request send. */
47
+ signalNotice?: SendOutcome | undefined;
48
+ } | {
49
+ view: "guide";
50
+ status: "loading" | "ready" | "error";
51
+ journeyId: string;
52
+ guide?: GuideView | undefined;
53
+ /** Position in `guide.steps`. */
54
+ step: number;
55
+ /** Whether the current step's element was found and highlighted. */
56
+ anchor: AnchorResult;
57
+ checkin: CheckinPhase;
58
+ /** Result of the last check-in send, for a short confirmation. */
59
+ notice?: SendOutcome | undefined;
60
+ /** A callout or request in progress (absent: idle). */
61
+ signal?: SignalPhase | undefined;
62
+ /** Result of the last callout/request send. */
63
+ signalNotice?: SendOutcome | undefined;
64
+ } | {
65
+ view: "finished";
66
+ guide: GuideView;
67
+ notice?: SendOutcome | undefined;
68
+ };
69
+ export interface CheckinRequest {
70
+ guide: GuideView;
71
+ step: GuideStepView;
72
+ answer: CheckinAnswer;
73
+ }
74
+ /**
75
+ * A help signal the reader chose to send (north-star N4): a one-tap "This is confusing" on a
76
+ * guide step, or an "I couldn't find how to …" request in their own words. Never automatic:
77
+ * only an explicit command creates one, and nothing is sent before consent.
78
+ */
79
+ export type HelpSignal = {
80
+ kind: "callout";
81
+ guide: GuideView;
82
+ step: GuideStepView;
83
+ } | {
84
+ kind: "request";
85
+ text: string;
86
+ guide?: GuideView | undefined;
87
+ step?: GuideStepView | undefined;
88
+ };
89
+ /** A signal's progress: consent first (nothing leaves the page before), then sending. */
90
+ export type SignalPhase = "idle" | "needs-consent" | "sending";
91
+ /** Longest request text, in characters (the collector's comment limit). */
92
+ export declare const MAX_REQUEST_CHARS = 4000;
93
+ export interface HelpControllerDeps {
94
+ /** Collector endpoint, to make asset paths absolute. */
95
+ endpoint: string;
96
+ loadIndex(): Promise<{
97
+ guides?: HelpIndexEntry[];
98
+ }>;
99
+ loadGuide(journeyId: string): Promise<GetGuideResponse>;
100
+ /** Highlights the step's element by its targets (`undefined` or empty: the step names none). */
101
+ highlight(targets: readonly StepTarget[] | undefined): AnchorResult | Promise<AnchorResult>;
102
+ clearHighlight(): void;
103
+ hasConsent(): boolean;
104
+ giveConsent(): void;
105
+ /** Queues the check-in through the feedback outbox. Called only after consent. */
106
+ sendCheckin(request: CheckinRequest): Promise<SendOutcome>;
107
+ /** Queues a callout or request through the feedback outbox. Called only after consent. */
108
+ sendSignal(signal: HelpSignal): Promise<SendOutcome>;
109
+ /**
110
+ * Whether this reader may send callouts and requests (default: yes). An anonymous reader on a
111
+ * site that refuses anonymous feedback may not (ADR 0013 §4): the view hides those entry
112
+ * points (`HelpController.canSignal`) and the controller drops the commands.
113
+ */
114
+ signalsAllowed?(): boolean;
115
+ }
116
+ export type HelpListener = (state: HelpState) => void;
117
+ /** The collector's answer when the site's help is switched off (board settings, lane N6a). */
118
+ export declare function isHelpDisabled(err: unknown): boolean;
119
+ export declare class HelpController {
120
+ private readonly deps;
121
+ private current;
122
+ private readonly listeners;
123
+ private entries;
124
+ /** Invalidates async results after a later command (open another guide, close). */
125
+ private generation;
126
+ private readonly asked;
127
+ private pending;
128
+ private pendingSignal;
129
+ constructor(deps: HelpControllerDeps);
130
+ get state(): HelpState;
131
+ subscribe(listener: HelpListener): () => void;
132
+ /** Whether the site serves help (the cached index read). A disabled site goes `disabled`. */
133
+ probe(): Promise<void>;
134
+ /** Opens the panel on the guide list (loads the index). */
135
+ open(): void;
136
+ close(): void;
137
+ /** Filters the loaded index locally; the query is never sent. */
138
+ search(query: string): void;
139
+ openGuide(journeyId: string): void;
140
+ /** Help is off for this site: no panel, no launcher, no retries. */
141
+ private disable;
142
+ /** Back one step; from the first step, back to the list. */
143
+ back(): void;
144
+ /** Ends the current step: asks "Did this step make sense?" once per step, then moves on. */
145
+ next(): void;
146
+ /** Answers the check-in. Without consent, asks for it first (nothing is sent before). */
147
+ answer(answer: CheckinAnswer): void;
148
+ /** Skips the check-in for this step. */
149
+ skipCheckin(): void;
150
+ giveConsent(): void;
151
+ /** "Not now": nothing is sent (a pending callout/request is dropped; the guide continues). */
152
+ declineConsent(): void;
153
+ canSignal(): boolean;
154
+ /** "This is confusing" on step `index`: guide, step and element only. After consent. */
155
+ sendCallout(stepIndex: number): void;
156
+ /** "I couldn't find how to …" in the reader's words, after consent; never the raw query. */
157
+ sendRequest(text: string): void;
158
+ private signal;
159
+ private patchSignal;
160
+ private sendSignalNow;
161
+ private send;
162
+ /** Re-finds the current step's element (the host's route or layout changed). */
163
+ refreshHighlight(): void;
164
+ private markAsked;
165
+ private advance;
166
+ private showStep;
167
+ /**
168
+ * Highlights the step's element. A result that arrives later (the fallback resolver loads
169
+ * lazily) is applied only while the same guide is still on the same step.
170
+ */
171
+ private anchorFor;
172
+ private set;
173
+ }
174
+ /** Wire guide → view model: defaults filled, steps ordered, asset paths made absolute. */
175
+ export declare function toView(g: GetGuideResponse, journeyId: string, endpoint: string): GuideView;
@@ -0,0 +1,8 @@
1
+ import type { CheckinRequest, HelpController } from "./controller";
2
+ export interface TelemetryHook {
3
+ start(): void;
4
+ checkin(req: CheckinRequest): void;
5
+ /** `discard` (a logout): drop queued events instead of sending them. */
6
+ stop(discard?: boolean): void;
7
+ }
8
+ export declare function telemetryHook(endpoint: string, siteKey: string, controller: HelpController): TelemetryHook;
@@ -0,0 +1,24 @@
1
+ import type { MountedWidget, MountOptions, WidgetModule } from "../../loader";
2
+ import { type JourneezeContext, type SubmitFeedbackRequest, type WireFeedbackContext } from "../../types";
3
+ import { type CheckinRequest, type GuideStepView, type GuideView, type HelpSignal } from "./controller";
4
+ import { type HelpView } from "./panel";
5
+ export { HelpController, isHelpDisabled, toView } from "./controller";
6
+ export type { CheckinAnswer, CheckinPhase, CheckinRequest, GuideStepView, GuideView, HelpControllerDeps, HelpListener, HelpSignal, HelpState, SendOutcome, SignalPhase } from "./controller";
7
+ export { MAX_REQUEST_CHARS } from "./controller";
8
+ export { HelpClient } from "./client";
9
+ export { searchGuides } from "./search";
10
+ export { findAnchor, Highlighter, type AnchorResult, type StepTarget } from "./highlight";
11
+ export { PANEL_CSS, placeholderView, type HelpView, type HelpViewOptions } from "./panel";
12
+ /** Feedback context of a guide step: the host's ids, with the guide's journey and step. */
13
+ export declare function guideStepContext(host: Readonly<JourneezeContext>, guide: GuideView, step: GuideStepView, href: string, width: number): WireFeedbackContext;
14
+ /** Feedback context of a check-in: the host's ids, with the guide's journey and step. */
15
+ export declare function checkinContext(host: Readonly<JourneezeContext>, req: CheckinRequest, href: string, width: number): WireFeedbackContext;
16
+ /** Feedback context outside a guide: the host's ids and the page path only. */
17
+ export declare function pageContext(host: Readonly<JourneezeContext>, href: string, width: number): WireFeedbackContext;
18
+ /**
19
+ * The collector request for a help signal. A callout carries the guide step (and its element id)
20
+ * with no comment and no reaction; a request carries the reader's own text as its comment.
21
+ */
22
+ export declare function signalRequest(siteKey: string, host: Readonly<JourneezeContext>, sig: HelpSignal, href: string, width: number): SubmitFeedbackRequest;
23
+ export declare function mountHelp(options: MountOptions, getContext: () => Readonly<JourneezeContext>, view?: HelpView): MountedWidget;
24
+ export declare const mount: WidgetModule["mount"];
@@ -0,0 +1,38 @@
1
+ export type AnchorResult = "found" | "missing" | "none";
2
+ /** One way to find a step's element (contract C0c v1.1 § Targets), most stable first. */
3
+ export interface StepTarget {
4
+ by: "tflow" | "testId" | "role" | "css";
5
+ value: string;
6
+ /** For `role`: the accessible name. */
7
+ name?: string | undefined;
8
+ }
9
+ /** The lazily loaded fallback resolver (its own chunk: `src/resolve/resolve.ts`). */
10
+ export type ResolverLoader = () => Promise<{
11
+ resolveTarget(targets: readonly StepTarget[], doc: Document): Element | undefined;
12
+ }>;
13
+ /**
14
+ * The visible element named `tflowId`, or `undefined`. The id is checked against the tflo
15
+ * grammar first, so it can never inject into the selector.
16
+ */
17
+ export declare function findAnchor(tflowId: string, doc?: Document): Element | undefined;
18
+ export declare function prefersReducedMotion(win?: Window): boolean;
19
+ export declare class Highlighter {
20
+ private readonly win;
21
+ private readonly load;
22
+ private target;
23
+ private readonly box;
24
+ private readonly onMove;
25
+ /** Bumped on every show/hide, so a late resolver result never outlines a step left behind. */
26
+ private seq;
27
+ constructor(layer: HTMLElement | ShadowRoot, win?: Window, load?: ResolverLoader);
28
+ /**
29
+ * Outlines the step's element: `"none"` when the step names none, `"missing"` when no target
30
+ * resolves. A lone tflo anchor resolves at once; other targets load the fallback resolver
31
+ * first (a failed load is `"missing"`: the step's text still guides).
32
+ */
33
+ show(targets: readonly StepTarget[] | undefined): AnchorResult | Promise<AnchorResult>;
34
+ private outline;
35
+ hide(): void;
36
+ dispose(): void;
37
+ private place;
38
+ }
@@ -0,0 +1,11 @@
1
+ import type { HelpController } from "./controller";
2
+ export interface HelpViewOptions {
3
+ /** The consent copy to show before the first check-in is sent (the site's or the default). */
4
+ consentText(): Promise<string>;
5
+ }
6
+ /** A view renders the controller's state into the help's closed shadow root. */
7
+ export type HelpView = (root: ShadowRoot, controller: HelpController, options: HelpViewOptions) => {
8
+ destroy(): void;
9
+ };
10
+ export declare const PANEL_CSS = "\n:host { all: initial; }\n.launcher { position:fixed; left:24px; bottom:24px; z-index:2147483646; width:44px; height:44px; border-radius:22px;\n border:1px solid #1D2024; background:#1D2024; color:#FFFFFF; font:600 18px/1 system-ui,sans-serif; cursor:pointer; }\n.panel { position:fixed; left:24px; bottom:80px; z-index:2147483646; width:min(340px, calc(100vw - 48px));\n max-height:min(70vh, 560px); overflow:auto; box-sizing:border-box; padding:16px; border:1px solid rgb(29 32 36/12%);\n border-radius:10px; background:#FFFFFF; color:#1D2024; font:14px/1.45 system-ui,sans-serif; box-shadow:0 8px 24px rgb(0 0 0/12%); }\n.panel h2 { font-size:15px; margin:0 0 8px; }\n.panel p { margin:0 0 8px; }\n.panel ul { list-style:none; padding:0; margin:8px 0 0; }\n.panel li button { display:block; width:100%; text-align:left; padding:8px; margin:0 0 4px; border:1px solid rgb(29 32 36/12%);\n border-radius:6px; background:#FFFFFF; color:inherit; font:inherit; cursor:pointer; }\n.panel input { width:100%; box-sizing:border-box; padding:8px; border:1px solid rgb(29 32 36/24%); border-radius:6px; font:inherit; }\n.panel .row { display:flex; gap:8px; flex-wrap:wrap; margin-top:12px; }\n.panel .row button { padding:6px 12px; border:1px solid #1D2024; border-radius:6px; background:#FFFFFF; color:#1D2024; font:inherit; cursor:pointer; }\n.panel .muted { color:#5D636B; font-size:13px; }\n.panel img { max-width:100%; border:1px solid rgb(29 32 36/12%); border-radius:6px; }\n:focus-visible { outline:2px solid #3D63C9; outline-offset:2px; }\n.jz-help-highlight { position:fixed; z-index:2147483645; pointer-events:none; box-sizing:border-box;\n border:2px solid #3D63C9; border-radius:6px; box-shadow:0 0 0 4px rgb(61 99 201/20%); }\n";
11
+ export declare const placeholderView: HelpView;
@@ -0,0 +1,7 @@
1
+ import type { HelpIndexEntry } from "../../types";
2
+ /**
3
+ * Guides matching every word of `query`, best first: a word in the title counts most, then the
4
+ * goal, then a step title. A blank query returns the index unchanged. No match returns `[]` (the
5
+ * UI offers "I couldn't find how to …" there).
6
+ */
7
+ export declare function searchGuides(entries: readonly HelpIndexEntry[], query: string): HelpIndexEntry[];
@@ -0,0 +1,2 @@
1
+ import type { HelpController } from "../core/controller";
2
+ export declare function calloutButton(controller: HelpController, stepIndex: number): HTMLButtonElement;
@@ -0,0 +1,3 @@
1
+ import type { HelpView } from "../core/panel";
2
+ export type { HelpView, HelpViewOptions } from "../core/panel";
3
+ export declare const helpPanelView: HelpView;
@@ -0,0 +1,2 @@
1
+ import type { HelpController } from "../core/controller";
2
+ export declare function requestForm(controller: HelpController, query: string): HTMLFormElement;
@@ -0,0 +1 @@
1
+ export declare const HELP_UI_CSS = "\n:host {\n all: initial;\n --jz-canvas:#F7F7F5; --jz-surface:#FFFFFF; --jz-raised:#F0F0EC; --jz-ink:#1D2024; --jz-muted:#5D636B;\n --jz-faint:#6C727B; --jz-rule:rgb(29 32 36 / 12%); --jz-accent:#3D63C9; --jz-accent-ink:#FFFFFF;\n --jz-accent-soft:rgb(61 99 201 / 10%); --jz-polished:#2F7D4F; --jz-confused:#6E4FC2;\n --jz-shadow:0 1px 3px rgb(0 0 0 / 12%);\n font: 400 13px/1.45 system-ui, -apple-system, \"Segoe UI\", sans-serif;\n color: var(--jz-ink); -webkit-font-smoothing: antialiased;\n}\n@media (prefers-color-scheme: dark) {\n :host {\n --jz-canvas:#111316; --jz-surface:#181B1F; --jz-raised:#20242A; --jz-ink:#E8EAED; --jz-muted:#A3A9B2;\n --jz-faint:#8A919B; --jz-rule:rgb(232 234 237 / 12%); --jz-accent:#7F9CF0; --jz-accent-ink:#111316;\n --jz-accent-soft:rgb(127 156 240 / 14%); --jz-polished:#5DBB84; --jz-confused:#A48BEA;\n }\n}\n*, *::before, *::after { box-sizing: border-box; }\nbutton { font: inherit; color: inherit; cursor: pointer; }\n:focus-visible { outline: 2px solid var(--jz-accent); outline-offset: 2px; }\nbutton:disabled { opacity: .45; cursor: not-allowed; }\n[hidden] { display: none !important; }\n\n.launcher { position:fixed; left:24px; bottom:24px; z-index:2147483646; width:44px; height:44px; min-width:44px; min-height:44px;\n display:flex; align-items:center; justify-content:center; padding:0; border:1px solid var(--jz-accent); border-radius:999px;\n background:var(--jz-accent); color:var(--jz-accent-ink); font-size:18px; font-weight:600; box-shadow:var(--jz-shadow);\n transition: background 180ms ease-out, transform 180ms ease-out; }\n.launcher:hover, .launcher[aria-expanded=\"true\"] { background:var(--jz-ink); border-color:var(--jz-ink); }\n\n.panel { position:fixed; left:24px; bottom:80px; z-index:2147483646; width:min(360px, calc(100vw - 48px));\n max-height:min(75vh, 580px); overflow:auto; padding:16px; border:1px solid var(--jz-rule); border-radius:10px;\n background:var(--jz-surface); color:var(--jz-ink); box-shadow:var(--jz-shadow);\n transition: opacity 180ms ease-out, transform 180ms ease-out; }\n.panel-head { display:flex; align-items:center; justify-content:space-between; gap:8px; margin-bottom:12px; }\n.panel-head h2 { margin:0; font-size:14px; font-weight:600; }\n.icon-btn { width:44px; height:44px; min-width:44px; display:flex; align-items:center; justify-content:center; padding:0;\n border:0; border-radius:6px; background:transparent; color:var(--jz-faint); transition: color 180ms ease-out, background 180ms ease-out; }\n.icon-btn:hover { color:var(--jz-ink); background:var(--jz-raised); }\n\n.panel input { width:100%; min-height:44px; padding:8px 12px; border:1px solid var(--jz-rule); border-radius:6px;\n background:var(--jz-canvas); color:var(--jz-ink); font:inherit; }\n.panel input::placeholder { color:var(--jz-faint); }\n.panel ul { list-style:none; padding:0; margin:12px 0 0; }\n.panel li { margin:0 0 8px; }\n.panel li button { display:block; width:100%; min-height:44px; text-align:left; padding:8px 12px; border:1px solid var(--jz-rule);\n border-radius:6px; background:var(--jz-surface); color:inherit; font:inherit; transition: background 180ms ease-out, border-color 180ms ease-out; }\n.panel li button:hover { background:var(--jz-raised); border-color:var(--jz-accent); }\n.muted { color:var(--jz-muted); font-size:12px; margin:4px 0 0; }\n.row { display:flex; gap:8px; flex-wrap:wrap; margin-top:12px; }\n.row button { min-height:44px; padding:0 16px; border:1px solid var(--jz-rule); border-radius:6px; background:var(--jz-surface);\n color:var(--jz-muted); transition: background 180ms ease-out, color 180ms ease-out; }\n.row button:hover { color:var(--jz-ink); background:var(--jz-raised); }\n.row .primary { border-color:var(--jz-accent); background:var(--jz-accent); color:var(--jz-accent-ink); }\n\n.card { border:1px solid var(--jz-rule); border-radius:10px; padding:16px; }\n.card h2 { margin:0 0 8px; font-size:14px; font-weight:600; }\n.progress { height:4px; margin:8px 0 12px; border-radius:999px; background:var(--jz-raised); overflow:hidden; }\n.progress-fill { height:100%; border-radius:999px; background:var(--jz-accent); transition: width 180ms ease-out; }\n.step-title { margin:0 0 8px; font-weight:600; }\n.panel img, .panel video { max-width:100%; margin-top:12px; border:1px solid var(--jz-rule); border-radius:6px; }\n\n.checkin { margin-top:12px; padding-top:12px; border-top:1px solid var(--jz-rule); }\n.checkin p { margin:0 0 8px; }\n.callout-btn { display:inline-flex; align-items:center; gap:6px; margin-top:12px; min-height:44px; padding:0 16px;\n border:1px solid var(--jz-confused); border-radius:6px; background:var(--jz-surface); color:var(--jz-confused);\n transition: background 180ms ease-out, color 180ms ease-out; }\n.callout-btn:hover { background:var(--jz-raised); }\n.callout-btn svg { flex:none; }\n.request { margin-top:12px; }\n.request p { margin:0 0 8px; color:var(--jz-muted); font-size:12px; }\n.request textarea { display:block; width:100%; padding:8px 12px; border:1px solid var(--jz-rule); border-radius:6px;\n background:var(--jz-canvas); color:var(--jz-ink); font:inherit; resize:vertical; }\n.request button { margin-top:8px; min-height:44px; padding:0 16px; border:1px solid var(--jz-accent);\n border-radius:6px; background:var(--jz-accent); color:var(--jz-accent-ink); }\n.checkin-btn { display:inline-flex; align-items:center; gap:6px; }\n.checkin-btn.understood { color:var(--jz-polished); border-color:var(--jz-polished); }\n.checkin-btn.confused { color:var(--jz-confused); border-color:var(--jz-confused); }\n.checkin-btn svg { flex:none; }\n\n@media (prefers-reduced-motion: reduce) {\n :host *, :host *::before, :host *::after { animation-duration:1ms !important; transition-duration:1ms !important; }\n}\n.jz-help-highlight { position:fixed; z-index:2147483645; pointer-events:none; box-sizing:border-box;\n border:2px solid var(--jz-accent); border-radius:6px; box-shadow:0 0 0 4px var(--jz-accent-soft); }\n";
@@ -0,0 +1,7 @@
1
+ export { createJourneeze } from "./loader";
2
+ export { tflowIdAt, pagePath } from "./context";
3
+ export { isMasked } from "./mask";
4
+ export { takeReviewerToken } from "./invite";
5
+ export type { HelpModule, JourneezeHelpHandle, MountedWidget, WidgetModule } from "./loader";
6
+ export type { MountOptions } from "./loader";
7
+ export type { JourneezeConfigV1, JourneezeContext, JourneezeHandle, JourneezeHandleV1, JourneezeMaskingConfig, JourneezeOptions, JourneezeTransportConfig, HelpStatus, Reaction } from "./types";
@@ -0,0 +1,4 @@
1
+ /** Takes `jz_invite` from the URL (removing it) or from this tab's session. */
2
+ export declare function takeReviewerToken(siteKey: string): string | undefined;
3
+ /** Forgets this tab's reviewer token (`reset()`, a logout). */
4
+ export declare function clearReviewerToken(siteKey: string): void;
@@ -0,0 +1,57 @@
1
+ import type { JourneezeConfigV1, JourneezeContext, JourneezeHandle, JourneezeHandleV1, JourneezeOptions, JourneezeSignalsConfigV1, JourneezeUserIdSignature } from "./types";
2
+ /**
3
+ * An opaque, host-computed user id (§4 of the contract): the host's server computes
4
+ * `HMAC-SHA256(server_secret, immutable_internal_user_id)` and passes the digest as 64 lowercase
5
+ * hex characters or 43 unpadded base64url characters. Nothing else passes: an email, a name, a raw
6
+ * UUID, a ULID or a numeric id is refused. The SDK never hashes. Deliver it only in an
7
+ * authenticated, per-user, `Cache-Control: no-store` response, never in public config.
8
+ */
9
+ export declare const USER_ID_PATTERN: RegExp;
10
+ /** A site's public key: `pk_` + 32 lowercase hex (the collector's `SiteKey`). Not a secret. */
11
+ export declare const SITE_KEY_PATTERN: RegExp;
12
+ export interface MountedWidget {
13
+ /** `discard` (on `reset()`, a logout): drop queued telemetry instead of sending it. */
14
+ unmount(discard?: boolean): void;
15
+ /** Called after the host changed the context (route or journey step changes). */
16
+ contextChanged?(): void;
17
+ }
18
+ /**
19
+ * What a lazy chunk is mounted with: the host's options. The chunk resolves the collector base
20
+ * (`collectorBase`) and validates context (`checkContext`). `userId` is read live (it can change
21
+ * while a chunk is mounted; consent uses the current one) and is already validated.
22
+ */
23
+ export interface MountOptions extends JourneezeOptions {
24
+ apiHost?: string;
25
+ apiPath?: string;
26
+ readonly userId?: string | undefined;
27
+ /** Signed host identity (contract §4, v1.3) from the config; checked and sent by the widget. */
28
+ userIdSig?: string | undefined;
29
+ userIdExp?: number | undefined;
30
+ /** The signature last given to `setUser` (it wins over the config's). */
31
+ signature?: JourneezeUserIdSignature | "" | undefined;
32
+ maskSelectors?: readonly string[];
33
+ maskAllText?: boolean;
34
+ nonce?: string;
35
+ /** Global Privacy Control is on and honoured: no help telemetry and no signals (§7). */
36
+ noTelemetry?: boolean;
37
+ pagePath?: "redacted" | "none";
38
+ /** Signals (signals-v1.md §3.2); run only by the lazy chunks, after consent. */
39
+ signals?: JourneezeSignalsConfigV1;
40
+ }
41
+ export interface WidgetModule {
42
+ /** `exit` switches the mode off from inside the widget (Esc, the toggle's Exit, "Not now"). */
43
+ mount(options: MountOptions, getContext: () => Readonly<JourneezeContext>, exit: () => void): MountedWidget;
44
+ }
45
+ /** The lazy `help` chunk (north-star N3): a launcher and guides built from the site's journeys. */
46
+ export type HelpModule = WidgetModule;
47
+ export type WidgetImporter = () => Promise<WidgetModule>;
48
+ export type HelpImporter = () => Promise<HelpModule>;
49
+ /** Kept for compatibility: `JourneezeHandle` itself now declares the help methods (#68). */
50
+ export type JourneezeHelpHandle = JourneezeHandle;
51
+ /** One `[journeeze]` warning per message per page. */
52
+ export declare function warnOnce(message: string): void;
53
+ /** Test helper: forget which warnings were shown. */
54
+ export declare function resetWarnings(): void;
55
+ /** A user id the contract accepts (§4), else `undefined` with a warning. Never hashed here. */
56
+ export declare function checkUserId(id: string | undefined): string | undefined;
57
+ export declare function createJourneeze(config: JourneezeConfigV1, importWidget?: WidgetImporter, importHelp?: HelpImporter): JourneezeHandleV1;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Editable by state, not by attribute spelling: `contenteditable` is an enumerated attribute with
3
+ * case-insensitive keywords (`TRUE`, `Plaintext-Only` are editable), so any value other than a
4
+ * case-insensitive "false" counts, as does `isContentEditable` (inherited editability).
5
+ */
6
+ export declare function isEditable(el: Element): boolean;
7
+ /** This element itself must be masked (used by the screenshot clone filter). */
8
+ export declare function isMaskedElement(el: Element): boolean;
9
+ /** This element or an ancestor is masked. */
10
+ export declare function isMasked(el: Element): boolean;
@@ -0,0 +1,9 @@
1
+ import { type ReactNode } from "react";
2
+ import { type JourneezeHelpHandle } from "./loader";
3
+ import { type JourneezeConfigV1, type JourneezeContext, type JourneezeHandle, type JourneezeHandleV1, type JourneezeOptions, type JourneezeProviderPropsV1 } from "./types";
4
+ export type JourneezeProviderProps = JourneezeProviderPropsV1 & {
5
+ children: ReactNode;
6
+ };
7
+ export declare function JourneezeProvider(props: JourneezeProviderProps): import("react").JSX.Element;
8
+ export declare function useJourneeze(): JourneezeHandleV1;
9
+ export type { JourneezeConfigV1, JourneezeContext, JourneezeHandle, JourneezeHandleV1, JourneezeHelpHandle, JourneezeOptions };
@@ -0,0 +1,12 @@
1
+ import type { StepTarget } from "../help/core/highlight";
2
+ /**
3
+ * The element's accessible name, following the accname order closely enough for guides:
4
+ * aria-labelledby, aria-label, native labels (`<label>`, `alt`), content for roles named from
5
+ * content, then `title` / `placeholder`.
6
+ */
7
+ export declare function accessibleName(el: Element, role: string): string;
8
+ /**
9
+ * The step's element: the first target, in the stored order, that resolves to exactly one
10
+ * visible element; `undefined` when none does.
11
+ */
12
+ export declare function resolveTarget(targets: readonly StepTarget[], doc?: Document): Element | undefined;
@@ -0,0 +1,6 @@
1
+ import { type JourneezeHelpHandle } from "./loader";
2
+ declare global {
3
+ interface Window {
4
+ Journeeze?: JourneezeHelpHandle;
5
+ }
6
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Compile a site-rule string in the SDK's CEL subset into a single-event predicate, or return
3
+ * `undefined` for anything outside the subset. Never throws and never uses `eval`/`Function`.
4
+ */
5
+ export declare function compileRule(src: string): ((event: {
6
+ kind: string;
7
+ fields: Readonly<Record<string, unknown>>;
8
+ }) => boolean) | undefined;
@@ -0,0 +1,10 @@
1
+ import type { MountOptions } from "../loader";
2
+ import type { GetWidgetConfigResponse, JourneezeContext } from "../types";
3
+ export interface SignalsHook {
4
+ /** `site`: the widget config the chunk read (or is reading); its `signalsEnabled` gates loading. */
5
+ start(site: Promise<GetWidgetConfigResponse | undefined> | GetWidgetConfigResponse | undefined): void;
6
+ /** `discard` (a logout, `reset()`): drop queued signals instead of sending them. */
7
+ stop(discard?: boolean): void;
8
+ contextChanged(): void;
9
+ }
10
+ export declare function signalsHook(options: MountOptions, endpoint: string, getContext: () => Readonly<JourneezeContext>): SignalsHook;
@@ -0,0 +1,2 @@
1
+ /** Runs the whole conformance suite against one installed tflo package (`pkg` is its node_modules name). */
2
+ export declare function signalsConformance(pkg: string, tflo: typeof import("tflo-browser-events")): void;
@@ -0,0 +1,145 @@
1
+ import { CONTEXT_FIELDS } from "../contract";
2
+ import type { MountOptions } from "../loader";
3
+ import type { GetWidgetConfigResponse, JourneezeContext, JourneezeTfloModule } from "../types";
4
+ export type ParamValue = {
5
+ intValue: string;
6
+ } | {
7
+ doubleValue: number;
8
+ } | {
9
+ boolValue: boolean;
10
+ } | {
11
+ stringValue: string;
12
+ };
13
+ export interface SignalEnvelope {
14
+ eventId: string;
15
+ seq: number;
16
+ name: string;
17
+ source: "SIGNAL_SOURCE_PLATFORM" | "SIGNAL_SOURCE_SITE";
18
+ context: Partial<Record<(typeof CONTEXT_FIELDS)[number], string>>;
19
+ target?: string;
20
+ params?: Record<string, ParamValue>;
21
+ }
22
+ export interface RecordSignalsRequest {
23
+ siteKey: string;
24
+ consentGiven: true;
25
+ ruleSet: string;
26
+ signals: SignalEnvelope[];
27
+ /** Signals that didn't fit the page-end send and were dropped (C0d §6, field 5). */
28
+ droppedAtUnload?: number;
29
+ }
30
+ /** The platform rule set this chunk runs (C0d §4.1). Thresholds are part of the version. */
31
+ export declare const RULE_SET = "jz-platform@1";
32
+ export declare const RAGE_WINDOW_MS = 700;
33
+ export declare const RAGE_CLICKS = 3;
34
+ export declare const HESITATION_MS = 3000;
35
+ export declare const DEAD_CLICK_MS = 1000;
36
+ export declare const ABANDON_MS = 120000;
37
+ export declare const CLOCK_MS = 1000;
38
+ export declare const THROTTLE_MS = 10000;
39
+ export declare const PAGE_CAP = 300;
40
+ export declare const MAX_TARGETS = 64;
41
+ export declare const MAX_BATCH = 50;
42
+ export declare const MAX_BATCH_BYTES: number;
43
+ export declare const FLUSH_MS = 5000;
44
+ export declare const MAX_QUEUED = 200;
45
+ export declare const MAX_QUEUED_BYTES: number;
46
+ interface RawEvent {
47
+ kind: string;
48
+ ts: number;
49
+ fields: Record<string, unknown>;
50
+ }
51
+ interface DerivedSignal {
52
+ name: string;
53
+ ts: number;
54
+ payload: Record<string, unknown>;
55
+ }
56
+ interface Match {
57
+ first(): RawEvent;
58
+ last(): RawEvent;
59
+ readonly length: number;
60
+ }
61
+ interface PatternBuilder {
62
+ when(p: (e: RawEvent) => boolean): PatternBuilder;
63
+ then(p: (e: RawEvent) => boolean): PatternBuilder;
64
+ notThen(p: (e: RawEvent) => boolean): PatternBuilder;
65
+ within(ms: number): PatternBuilder;
66
+ emit(fn: (m: Match) => {
67
+ name?: string;
68
+ payload?: Record<string, unknown>;
69
+ }): unknown;
70
+ }
71
+ interface TfloInstance {
72
+ init(): Promise<void>;
73
+ addPattern(compiled: unknown): unknown;
74
+ removePattern(name: string): void;
75
+ ingest(e: RawEvent): unknown;
76
+ flush(): Promise<void>;
77
+ destroy(): void;
78
+ }
79
+ interface TfloCtors {
80
+ TFloBrowser: new (opts: {
81
+ sinks: {
82
+ name: string;
83
+ send(s: DerivedSignal): void;
84
+ }[];
85
+ onSinkError?: (sink: string, err: unknown) => void;
86
+ }) => TfloInstance;
87
+ Pattern: new (name: string) => PatternBuilder;
88
+ }
89
+ /** Whether `m` offers the capability floor the SDK relies on (C0d §3.3). */
90
+ export declare function hasFloor(m: unknown): m is TfloCtors;
91
+ /**
92
+ * The tflo module to attach to: the host's (injected, or its lazy loader, called only now, after
93
+ * consent), else the SDK's own. An injected module below the floor is ignored with one warning.
94
+ */
95
+ export declare function resolveTflo(injected: JourneezeSignalsTflo | undefined, loadOwn?: () => Promise<unknown>): Promise<TfloCtors | undefined>;
96
+ type JourneezeSignalsTflo = JourneezeTfloModule | (() => Promise<JourneezeTfloModule>);
97
+ /** The tracked element's `data-tflow-id` for an event target, or undefined (masked or untracked). */
98
+ export declare function trackedId(node: EventTarget | null | undefined, extraMask?: readonly string[]): string | undefined;
99
+ export interface SignalsOptions {
100
+ options: MountOptions;
101
+ endpoint: string;
102
+ getContext: () => Readonly<JourneezeContext>;
103
+ /** The site's widget config (the chunk that started signals already read it, §4.4). */
104
+ site: GetWidgetConfigResponse;
105
+ /** Tests: time, timers, transport and the own-copy loader. */
106
+ now?: () => number;
107
+ fetchImpl?: typeof fetch;
108
+ loadOwn?: () => Promise<unknown>;
109
+ newId?: () => string;
110
+ }
111
+ export interface Signals {
112
+ contextChanged(): void;
113
+ /** Detaches every capture; `discard` (consent withdrawn, logout) drops queued signals unsent. */
114
+ stop(discard?: boolean): void;
115
+ /** Tests: settles pending sends. */
116
+ idle(): Promise<void>;
117
+ }
118
+ /** At most this many windows open at once; the oldest is dropped without firing. */
119
+ export declare const MAX_WINDOWS = 128;
120
+ /**
121
+ * Starts signals for one handle, or resolves undefined (the site's switch is off, or tflo is
122
+ * unavailable). Never throws into the host.
123
+ *
124
+ * Windows (`notThen(...).within(ms)`) on tflo 0.1.0: a negative step completes only when its
125
+ * runtime is flushed, and `TFloBrowser.flush()` drains every runtime at once (so it is never
126
+ * called, C0d §6 N1). Each window is therefore its own pattern, keyed to its own start event, and
127
+ * at its deadline the SDK flushes that one runtime (`addPattern`'s return value): a window that a
128
+ * settling event negated flushes empty. At page end windows are removed unflushed.
129
+ */
130
+ export declare function startSignals(o: SignalsOptions): Promise<Signals | undefined>;
131
+ /** Where the host is in a published journey's checkpoints (C0d §4.1, C0c § Step ids). */
132
+ export interface Checkpoint {
133
+ journey: string;
134
+ /** 0-based position in the journey's checkpoints. */
135
+ index: number;
136
+ last: boolean;
137
+ }
138
+ /**
139
+ * The one place that knows how `journeyId`/`journeyStep` match `GetWidgetConfig.publishedJourneys`
140
+ * (C0c § Step ids): the journey by its slug; the step by exact string equality against the
141
+ * checkpoints; a repeated checkpoint matches its first occurrence after the current one, else
142
+ * its first occurrence.
143
+ */
144
+ export declare function matchCheckpoint(site: GetWidgetConfigResponse, c: Readonly<JourneezeContext>, current?: Checkpoint): Checkpoint | undefined;
145
+ export {};