@journeeze/sdk 0.0.0-stage → 0.1.0-rc.3

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 (90) hide show
  1. package/AGENTS.md +80 -0
  2. package/CHANGELOG.md +41 -0
  3. package/LICENSE.md +27 -0
  4. package/README.md +343 -2
  5. package/dist/THIRD_PARTY_LICENSES.txt +246 -0
  6. package/dist/anchor-cPvFFTOS.js +1 -0
  7. package/dist/attach-DpioALxW.js +21 -0
  8. package/dist/chat-BgmB0rnW.js +64 -0
  9. package/dist/consent-DckCUvYR.js +1 -0
  10. package/dist/context-UnG6_X61.js +17 -0
  11. package/dist/contract.js +10 -0
  12. package/dist/feedback-CtH1a_kQ.js +1 -0
  13. package/dist/help-w0O5Qu-4.js +105 -0
  14. package/dist/hook-DGWypoEO.js +1 -0
  15. package/dist/index-B_nob8S0.js +1 -0
  16. package/dist/index-CwG2LrhO.js +20 -0
  17. package/dist/index.js +10 -0
  18. package/dist/jobs-CAXbM5-g.js +45 -0
  19. package/dist/loader-BZiIDD1D.js +96 -0
  20. package/dist/mask-audit-Du3NPoG_.js +5 -0
  21. package/dist/react.js +71 -0
  22. package/dist/resolve-BfWByuTp.js +1 -0
  23. package/dist/script.js +14 -0
  24. package/dist/signals-amjMQyOy.js +1 -0
  25. package/dist/telemetry-CyRJDaVs.js +1 -0
  26. package/dist/transport-B64CfIcF.js +1 -0
  27. package/dist/types/chat/capture/attach.d.ts +41 -0
  28. package/dist/types/chat/capture/draw.d.ts +10 -0
  29. package/dist/types/chat/capture/geometry.d.ts +30 -0
  30. package/dist/types/chat/capture/styles.d.ts +1 -0
  31. package/dist/types/chat/shell/chat.d.ts +27 -0
  32. package/dist/types/chat/shell/converse.d.ts +95 -0
  33. package/dist/types/chat/shell/copy.d.ts +8 -0
  34. package/dist/types/chat/shell/model.d.ts +36 -0
  35. package/dist/types/chat/shell/session.d.ts +148 -0
  36. package/dist/types/chat/shell/styles.d.ts +1 -0
  37. package/dist/types/chat/shell/view.d.ts +18 -0
  38. package/dist/types/chat/transport.d.ts +5 -0
  39. package/dist/types/config.d.ts +34 -0
  40. package/dist/types/context.d.ts +6 -0
  41. package/dist/types/contract.d.ts +13 -0
  42. package/dist/types/help/core/client.d.ts +26 -0
  43. package/dist/types/help/core/controller.d.ts +184 -0
  44. package/dist/types/help/core/events.d.ts +32 -0
  45. package/dist/types/help/core/help.d.ts +25 -0
  46. package/dist/types/help/core/highlight.d.ts +38 -0
  47. package/dist/types/help/core/panel.d.ts +27 -0
  48. package/dist/types/help/core/routing.d.ts +21 -0
  49. package/dist/types/help/core/search.d.ts +7 -0
  50. package/dist/types/help/ui/callout.d.ts +2 -0
  51. package/dist/types/help/ui/jobs/jobs.d.ts +24 -0
  52. package/dist/types/help/ui/jobs/model.d.ts +33 -0
  53. package/dist/types/help/ui/jobs/styles.d.ts +1 -0
  54. package/dist/types/help/ui/panel.d.ts +3 -0
  55. package/dist/types/help/ui/request.d.ts +2 -0
  56. package/dist/types/help/ui/styles.d.ts +1 -0
  57. package/dist/types/index.d.ts +7 -0
  58. package/dist/types/invite.d.ts +4 -0
  59. package/dist/types/loader.d.ts +59 -0
  60. package/dist/types/mask-detector.d.ts +22 -0
  61. package/dist/types/mask.d.ts +10 -0
  62. package/dist/types/preview/flag.d.ts +2 -0
  63. package/dist/types/preview/mask-audit.d.ts +29 -0
  64. package/dist/types/react.d.ts +9 -0
  65. package/dist/types/resolve/resolve.d.ts +12 -0
  66. package/dist/types/schemas/feedback.d.ts +76 -0
  67. package/dist/types/schemas/finding.d.ts +22 -0
  68. package/dist/types/schemas/index.d.ts +6 -0
  69. package/dist/types/schemas/turn.d.ts +26 -0
  70. package/dist/types/script.d.ts +6 -0
  71. package/dist/types/signals/cel.d.ts +8 -0
  72. package/dist/types/signals/hook.d.ts +10 -0
  73. package/dist/types/signals/signals.d.ts +149 -0
  74. package/dist/types/signals/steps.d.ts +33 -0
  75. package/dist/types/telemetry/telemetry.d.ts +73 -0
  76. package/dist/types/transport.d.ts +30 -0
  77. package/dist/types/types.d.ts +462 -0
  78. package/dist/types/widget/anchor.d.ts +11 -0
  79. package/dist/types/widget/capture.d.ts +39 -0
  80. package/dist/types/widget/consent.d.ts +25 -0
  81. package/dist/types/widget/feedback.d.ts +36 -0
  82. package/dist/types/widget/icons.d.ts +14 -0
  83. package/dist/types/widget/outbox.d.ts +69 -0
  84. package/dist/types/widget/styles.d.ts +1 -0
  85. package/dist/types/widget/upload.d.ts +14 -0
  86. package/dist/types/widget.d.ts +7 -0
  87. package/dist/types/wire.d.ts +66 -0
  88. package/dist/upload-Fu2qvSJV.js +14 -0
  89. package/dist/widget-B6mR1J3P.js +118 -0
  90. package/package.json +70 -4
@@ -0,0 +1,33 @@
1
+ import type { HelpJobEntry } from "../../../types";
2
+ import type { JobEvent } from "../../core/events";
3
+ /** A job with an id and a title (the controller drops entries without either). */
4
+ export type Job = HelpJobEntry & {
5
+ jobId: string;
6
+ title: string;
7
+ };
8
+ /**
9
+ * `HelpJobEntry.common_for`: the jobs common for `persona` first ("Common for you"), the rest
10
+ * after ("More"), each in index order. Without a persona every job is under "More".
11
+ */
12
+ export declare function jobsForPersona(jobs: readonly Job[], persona: string | undefined): {
13
+ common: Job[];
14
+ more: Job[];
15
+ };
16
+ /** The guide that answers `job` for `persona`: its persona guide, else the job's own; none: no guide yet. */
17
+ export declare function guideFor(job: Job, persona: string | undefined): {
18
+ journeyId: string;
19
+ stepCount: number;
20
+ } | undefined;
21
+ /** Local filter on the job titles (every word must appear); the query is never sent. */
22
+ export declare function matchJobs(jobs: readonly Job[], query: string): Job[];
23
+ /** A host `persona` context id the collector accepts on job events (`^[a-z0-9._:-]{1,96}$`). */
24
+ export declare const JOB_PERSONA: RegExp;
25
+ /**
26
+ * The job event with the host's persona (engagement.proto `persona`, follow-up 21), so answers
27
+ * can be read per job × persona. Only a context id is added: anything else is left off, so an
28
+ * out-of-shape persona never refuses the whole batch. The collector keeps it only when it names
29
+ * one of the project's catalog personas; it is never a user, session or device id.
30
+ */
31
+ export declare function withPersona(e: JobEvent, persona: string | undefined): JobEvent;
32
+ /** "1 step", "3 steps"; nothing without steps. */
33
+ export declare function stepsLabel(n: number): string;
@@ -0,0 +1 @@
1
+ export declare const JOBS_CSS = "\n:host { --jz-polish:#9A6412; --jz-broken:#B42318; }\n@media (prefers-color-scheme: dark) { :host { --jz-polish:#E0A54A; --jz-broken:#F07B70; } }\n.head-title { flex:1; display:flex; align-items:baseline; gap:8px; min-width:0; }\n.mono { font:11px/1.4 ui-monospace, \"JetBrains Mono\", monospace; color:var(--jz-faint); white-space:nowrap; }\n.panel h3 { margin:12px 0 4px; padding:0 12px; font-size:11px; font-weight:600; letter-spacing:.06em; text-transform:uppercase; color:var(--jz-faint); }\n.panel .jobs ul { margin:0; }\n.panel .jobs li { margin:0 0 2px; }\n.panel button.job { display:flex; align-items:center; justify-content:space-between; gap:8px; width:100%; min-height:48px;\n padding:10px 12px; border:1px solid transparent; border-radius:6px; background:transparent; text-align:left; font-weight:500;\n transition: background 180ms ease-out, border-color 180ms ease-out; }\n.panel button.job:hover { background:var(--jz-raised); border-color:var(--jz-rule); }\n.job .dim { color:var(--jz-faint); }\n.chip { flex:none; padding:2px 6px; border-radius:4px; background:var(--jz-accent-soft); color:var(--jz-accent); font-size:10px; font-weight:600; }\n.chip.none { background:var(--jz-raised); color:var(--jz-muted); }\n.jobs-foot { margin-top:8px; padding-top:8px; border-top:1px solid var(--jz-rule); }\n.row .not-possible { border-color:var(--jz-confused); color:var(--jz-confused); }\n.else label { display:block; margin:12px 0 6px; font-size:11px; font-weight:500; color:var(--jz-faint); }\n.else textarea { display:block; width:100%; min-height:88px; 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.note { margin:12px 0; padding:8px 12px; border-radius:6px; background:var(--jz-raised); color:var(--jz-muted); font-size:11px; }\n.panel .wide { width:100%; min-height:44px; padding:0 16px; border:1px solid var(--jz-accent); border-radius:6px;\n background:var(--jz-accent); color:var(--jz-accent-ink); font-weight:500; }\n.route { margin:12px 0; padding:12px; border:1px solid var(--jz-accent); border-radius:6px; background:var(--jz-accent-soft); }\n.route p { margin:0 0 4px; }\n.done .progress-fill { width:100%; background:var(--jz-polished); }\n.answers { display:grid; grid-template-columns:repeat(3, 1fr); gap:8px; margin:12px 0 16px; }\n.answers button { display:flex; flex-direction:column; align-items:center; justify-content:center; gap:4px; min-height:64px;\n border:1px solid var(--jz-rule); border-radius:6px; background:var(--jz-surface); font-size:12px; font-weight:500;\n transition: background 180ms ease-out, border-color 180ms ease-out; }\n.answers button:hover { background:var(--jz-raised); }\n.answers button[aria-pressed=\"true\"] { border-color:var(--jz-accent); background:var(--jz-accent-soft); box-shadow:inset 0 0 0 1px var(--jz-accent); }\n.answers svg { width:18px; height:18px; }\n.a-YES svg { color:var(--jz-polished); }\n.a-PARTLY svg { color:var(--jz-polish); }\n.a-NO svg { color:var(--jz-broken); }\n.harder { padding-top:12px; border-top:1px solid var(--jz-rule); }\n.harder label { display:flex; align-items:center; gap:8px; min-height:44px; color:var(--jz-muted); font-weight:500; cursor:pointer; }\n.panel .harder input { width:16px; height:16px; min-height:0; margin:0; padding:0; accent-color:var(--jz-accent); }\n.harder p { margin:0; }\n@media (max-width: 480px) {\n .panel { left:0; right:0; bottom:0; width:100%; max-height:85vh; border-radius:10px 10px 0 0;\n padding-bottom:calc(16px + env(safe-area-inset-bottom, 0px)); }\n}\n";
@@ -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,59 @@
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
+ /** The chat (SDK 2.x): open at "Report a problem" (`feedback()`). */
18
+ report?(): void;
19
+ }
20
+ /**
21
+ * What a lazy chunk is mounted with: the host's options. The chunk resolves the collector base
22
+ * (`collectorBase`) and validates context (`checkContext`). `userId` is read live (it can change
23
+ * while a chunk is mounted; consent uses the current one) and is already validated.
24
+ */
25
+ export interface MountOptions extends JourneezeOptions {
26
+ apiHost?: string;
27
+ apiPath?: string;
28
+ readonly userId?: string | undefined;
29
+ /** Signed host identity (contract §4, v1.3) from the config; checked and sent by the widget. */
30
+ userIdSig?: string | undefined;
31
+ userIdExp?: number | undefined;
32
+ /** The signature last given to `setUser` (it wins over the config's). */
33
+ signature?: JourneezeUserIdSignature | "" | undefined;
34
+ maskSelectors?: readonly string[];
35
+ maskAllText?: boolean;
36
+ nonce?: string;
37
+ /** Global Privacy Control is on and honoured: no help telemetry and no signals (§7). */
38
+ noTelemetry?: boolean;
39
+ pagePath?: "redacted" | "none";
40
+ /** Signals (signals-v1.md §3.2); run only by the lazy chunks, after consent. */
41
+ signals?: JourneezeSignalsConfigV1;
42
+ }
43
+ export interface WidgetModule {
44
+ /** `exit` switches the mode off from inside the widget (Esc, the toggle's Exit, "Not now"). */
45
+ mount(options: MountOptions, getContext: () => Readonly<JourneezeContext>, exit: () => void): MountedWidget;
46
+ }
47
+ /** The lazy `help` chunk (north-star N3): a launcher and guides built from the site's journeys. */
48
+ export type HelpModule = WidgetModule;
49
+ export type WidgetImporter = () => Promise<WidgetModule>;
50
+ export type HelpImporter = () => Promise<HelpModule>;
51
+ /** Kept for compatibility: `JourneezeHandle` itself now declares the help methods (#68). */
52
+ export type JourneezeHelpHandle = JourneezeHandle;
53
+ /** One `[journeeze]` warning per message per page. */
54
+ export declare function warnOnce(message: string): void;
55
+ /** Test helper: forget which warnings were shown. */
56
+ export declare function resetWarnings(): void;
57
+ /** A user id the contract accepts (§4), else `undefined` with a warning. Never hashed here. */
58
+ export declare function checkUserId(id: string | undefined): string | undefined;
59
+ export declare function createJourneeze(config: JourneezeConfigV1, importWidget?: WidgetImporter, importHelp?: HelpImporter): JourneezeHandleV1;
@@ -0,0 +1,22 @@
1
+ /** The detector's rule ids (a closed set: later lanes count and report these, nothing else). */
2
+ export declare const DETECTOR_RULES: readonly ["autocomplete_cc", "autocomplete_otp", "autocomplete_password", "input_password", "label_sensitive", "name_sensitive", "value_key_shaped", "value_card_shaped", "value_email_shaped"];
3
+ export type DetectorRule = (typeof DETECTOR_RULES)[number];
4
+ /** One flagged element: the rule that matched and where it is. Never the value. */
5
+ export interface DetectorHit {
6
+ rule: DetectorRule;
7
+ selector: string;
8
+ }
9
+ /** Hits per rule, for one capture or one scan. */
10
+ export type DetectorCounts = Partial<Record<DetectorRule, number>>;
11
+ /**
12
+ * The first rule this element matches, or `undefined`. Pure and value-free in its result: it
13
+ * reads attributes, labels and the element's own text only to decide, and returns only the rule.
14
+ */
15
+ export declare function detectSensitive(el: Element): DetectorRule | undefined;
16
+ /** A value-free anchor for a hit: the nearest stable hook, else a short structural path. */
17
+ export declare function anchorSelector(el: Element): string;
18
+ /**
19
+ * Every flagged element under `root` that `isAlreadyMasked` does not already cover (subtrees of
20
+ * masked elements are skipped). For integration aids; captures use {@link detectSensitive}.
21
+ */
22
+ export declare function scanSensitive(root: Element, isAlreadyMasked?: (el: Element) => boolean): DetectorHit[];
@@ -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,2 @@
1
+ /** Whether the mask audit was asked for in this tab. */
2
+ export declare function takePreviewFlag(): boolean;
@@ -0,0 +1,29 @@
1
+ import type { DetectorHit } from "../mask-detector";
2
+ /** One `journeeze.finding/v1` payload (docs/contract/schemas/finding-v1.schema.json). */
3
+ export interface MaskFindingWire {
4
+ anchor?: string;
5
+ selector?: string;
6
+ pagePath: string;
7
+ rule: string;
8
+ detectedAt: string;
9
+ }
10
+ /** The detector's scan (`scanSensitive` from `../mask-detector`), passed in by the caller: the
11
+ * widget already holds it, so this chunk never imports the widget chunk. */
12
+ export type Scan = (root: Element, isAlreadyMasked: (el: Element) => boolean) => DetectorHit[];
13
+ export interface MaskAuditOptions {
14
+ scan: Scan;
15
+ endpoint: string;
16
+ siteKey: string;
17
+ /** The implementer's widget token (an owner's or triager's). */
18
+ reviewerToken: string;
19
+ /** Host and journey-configuration selectors: elements they cover are already masked. */
20
+ maskSelectors?: readonly string[];
21
+ fetchImpl?: typeof fetch;
22
+ now?: () => Date;
23
+ }
24
+ /** The page's findings, value-free. Pure apart from reading the DOM. */
25
+ export declare function auditPage(scan: Scan, root: Element, href: string, selectors: readonly string[], now: Date): MaskFindingWire[];
26
+ /** Audits the current page and submits what is new for this tab. Never throws into the host. */
27
+ export declare function runMaskAudit(opts: MaskAuditOptions): Promise<number>;
28
+ /** Test helper: forget what this tab sent. */
29
+ export declare function resetMaskAudit(): void;
@@ -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,76 @@
1
+ /** Wire id of the `journeeze.feedback/v2` schema. */
2
+ export declare const FEEDBACK_SCHEMA_ID = "journeeze.feedback/v2";
3
+ /** `kind` — why the feedback was filed. */
4
+ export type FeedbackKind = "problem" | "unmet_need" | "suggestion" | "praise";
5
+ /** `reaction` — a one-tap mood on the reply that prompted the feedback. */
6
+ export type FeedbackReaction = "like" | "dislike" | "understood" | "confused";
7
+ /** `context.device`. */
8
+ export type FeedbackDevice = "desktop" | "tablet" | "mobile";
9
+ /** `context.removed[]` — context chips the user removed before sending. */
10
+ export type FeedbackRemovedChip = "page" | "persona" | "browser" | "device" | "viewport" | "build";
11
+ /** `team.verdict` — reviewer-only. */
12
+ export type FeedbackVerdict = "unreviewed" | "polished" | "needs_polish" | "broken";
13
+ /** `team.severity` — reviewer-only. */
14
+ export type FeedbackSeverity = "low" | "medium" | "high" | "critical";
15
+ /** `attachments[].shapes[].type`. */
16
+ export type FeedbackShapeType = "box" | "circle" | "arrow";
17
+ /** `job` — the journey job the feedback is about. */
18
+ export interface FeedbackJob {
19
+ jobId: string;
20
+ stepReached?: number;
21
+ }
22
+ /** `unmetNeed` — what the user could not do with the product. */
23
+ export interface FeedbackUnmetNeed {
24
+ need: string;
25
+ soYouCan?: string;
26
+ typedQuery?: string;
27
+ }
28
+ /** `context.viewport` — both sides optional. */
29
+ export interface FeedbackViewport {
30
+ w?: number;
31
+ h?: number;
32
+ }
33
+ /** `attachments[].shapes[]` — a normalised annotation over the screenshot. */
34
+ export interface FeedbackShape {
35
+ type: FeedbackShapeType;
36
+ x: number;
37
+ y: number;
38
+ w: number;
39
+ h: number;
40
+ }
41
+ /** `attachments[]` — one masked screenshot upload. */
42
+ export interface FeedbackAttachment {
43
+ screenshotId: string;
44
+ shapes?: FeedbackShape[];
45
+ }
46
+ /** One message in a feedback payload (also `$defs.message` reused by `journeeze.turn/v1`). */
47
+ export interface FeedbackMessage {
48
+ text?: string;
49
+ anchor?: string;
50
+ attachments?: FeedbackAttachment[];
51
+ }
52
+ /** `context` — the closed context allowlist. */
53
+ export interface FeedbackContext {
54
+ pagePath: string;
55
+ persona?: string;
56
+ browser?: string;
57
+ device?: FeedbackDevice;
58
+ viewport?: FeedbackViewport;
59
+ build?: string;
60
+ removed?: FeedbackRemovedChip[];
61
+ }
62
+ /** `team` — reviewer-only fields. */
63
+ export interface FeedbackTeam {
64
+ verdict?: FeedbackVerdict;
65
+ severity?: FeedbackSeverity;
66
+ }
67
+ /** A `journeeze.feedback/v2` payload. */
68
+ export interface Feedback {
69
+ kind: FeedbackKind;
70
+ job?: FeedbackJob;
71
+ unmetNeed?: FeedbackUnmetNeed;
72
+ reaction?: FeedbackReaction;
73
+ messages: FeedbackMessage[];
74
+ context: FeedbackContext;
75
+ team?: FeedbackTeam;
76
+ }
@@ -0,0 +1,22 @@
1
+ /** Wire id of the `journeeze.finding/v1` schema. */
2
+ export declare const FINDING_SCHEMA_ID = "journeeze.finding/v1";
3
+ /** `rule` — which secret-detector rule matched. */
4
+ export type FindingRule = "autocomplete_cc" | "autocomplete_otp" | "autocomplete_password" | "input_password" | "label_sensitive" | "name_sensitive" | "value_key_shaped" | "value_card_shaped" | "value_email_shaped";
5
+ /** Fields shared by both branches of the `oneOf`. */
6
+ export interface FindingBase {
7
+ pagePath: string;
8
+ rule: FindingRule;
9
+ detectedAt: string;
10
+ }
11
+ /** Branch for a flagged element that has a tflo id. */
12
+ export interface FindingWithAnchor extends FindingBase {
13
+ anchor: string;
14
+ selector?: never;
15
+ }
16
+ /** Branch for a flagged element addressed by CSS selector. */
17
+ export interface FindingWithSelector extends FindingBase {
18
+ selector: string;
19
+ anchor?: never;
20
+ }
21
+ /** A `journeeze.finding/v1` payload: exactly one of `anchor` or `selector`. */
22
+ export type Finding = FindingWithAnchor | FindingWithSelector;
@@ -0,0 +1,6 @@
1
+ export { FEEDBACK_SCHEMA_ID } from "./feedback";
2
+ export type { Feedback, FeedbackAttachment, FeedbackContext, FeedbackDevice, FeedbackJob, FeedbackKind, FeedbackMessage, FeedbackReaction, FeedbackRemovedChip, FeedbackSeverity, FeedbackShape, FeedbackShapeType, FeedbackTeam, FeedbackUnmetNeed, FeedbackViewport, FeedbackVerdict, } from "./feedback";
3
+ export { TURN_SCHEMA_ID } from "./turn";
4
+ export type { Turn, TurnAction, TurnActionInput, TurnInput, TurnTextInput } from "./turn";
5
+ export { FINDING_SCHEMA_ID } from "./finding";
6
+ export type { Finding, FindingBase, FindingRule, FindingWithAnchor, FindingWithSelector } from "./finding";
@@ -0,0 +1,26 @@
1
+ import type { FeedbackMessage, FeedbackTeam } from "./feedback";
2
+ /** Wire id of the `journeeze.turn/v1` schema. */
3
+ export declare const TURN_SCHEMA_ID = "journeeze.turn/v1";
4
+ /** `input.action` — the stable chat intents (ADR 0018). */
5
+ export type TurnAction = "pick_job" | "other_need" | "guide_next" | "guide_back" | "didnt_work" | "report_problem" | "send" | "cancel" | "affirm" | "deny" | "talk_to_assistant";
6
+ /** `input` branch where the user typed text. */
7
+ export interface TurnTextInput {
8
+ kind: "text";
9
+ text: string;
10
+ }
11
+ /** `input` branch where the user chose an action. */
12
+ export interface TurnActionInput {
13
+ kind: "action";
14
+ action: TurnAction;
15
+ jobId?: string;
16
+ step?: number;
17
+ }
18
+ /** `input` — the `oneOf` text|action as a discriminated union on `kind`. */
19
+ export type TurnInput = TurnTextInput | TurnActionInput;
20
+ /** A `journeeze.turn/v1` payload. */
21
+ export interface Turn {
22
+ input: TurnInput;
23
+ draft?: FeedbackMessage;
24
+ /** Reviewer-only (verdict, severity); the collector rejects it from non-reviewer sessions. */
25
+ team?: FeedbackTeam;
26
+ }
@@ -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,149 @@
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
+ /** The host's context ids, plus its release id (`appVersion`, ADR 0014 addendum) when set. */
19
+ context: Partial<Record<(typeof CONTEXT_FIELDS)[number] | "appVersion", string>>;
20
+ target?: string;
21
+ params?: Record<string, ParamValue>;
22
+ }
23
+ export interface RecordSignalsRequest {
24
+ siteKey: string;
25
+ consentGiven: true;
26
+ ruleSet: string;
27
+ signals: SignalEnvelope[];
28
+ /** Signals that didn't fit the page-end send and were dropped (C0d §6, field 5). */
29
+ droppedAtUnload?: number;
30
+ }
31
+ /**
32
+ * The platform rule set this chunk runs (C0d §4.1). Thresholds are part of the version.
33
+ * `jz-platform@2` adds `step_completed` (ADR 0014 addendum); the collector still accepts `@1`.
34
+ */
35
+ export declare const RULE_SET = "jz-platform@2";
36
+ export declare const RAGE_WINDOW_MS = 700;
37
+ export declare const RAGE_CLICKS = 3;
38
+ export declare const HESITATION_MS = 3000;
39
+ export declare const DEAD_CLICK_MS = 1000;
40
+ export declare const ABANDON_MS = 120000;
41
+ export declare const CLOCK_MS = 1000;
42
+ export declare const THROTTLE_MS = 10000;
43
+ export declare const PAGE_CAP = 300;
44
+ export declare const MAX_TARGETS = 64;
45
+ export declare const MAX_BATCH = 50;
46
+ export declare const MAX_BATCH_BYTES: number;
47
+ export declare const FLUSH_MS = 5000;
48
+ export declare const MAX_QUEUED = 200;
49
+ export declare const MAX_QUEUED_BYTES: number;
50
+ interface RawEvent {
51
+ kind: string;
52
+ ts: number;
53
+ fields: Record<string, unknown>;
54
+ }
55
+ interface DerivedSignal {
56
+ name: string;
57
+ ts: number;
58
+ payload: Record<string, unknown>;
59
+ }
60
+ interface Match {
61
+ first(): RawEvent;
62
+ last(): RawEvent;
63
+ readonly length: number;
64
+ }
65
+ interface PatternBuilder {
66
+ when(p: (e: RawEvent) => boolean): PatternBuilder;
67
+ then(p: (e: RawEvent) => boolean): PatternBuilder;
68
+ notThen(p: (e: RawEvent) => boolean): PatternBuilder;
69
+ within(ms: number): PatternBuilder;
70
+ emit(fn: (m: Match) => {
71
+ name?: string;
72
+ payload?: Record<string, unknown>;
73
+ }): unknown;
74
+ }
75
+ interface TfloInstance {
76
+ init(): Promise<void>;
77
+ addPattern(compiled: unknown): unknown;
78
+ removePattern(name: string): void;
79
+ ingest(e: RawEvent): unknown;
80
+ flush(): Promise<void>;
81
+ destroy(): void;
82
+ }
83
+ interface TfloCtors {
84
+ TFloBrowser: new (opts: {
85
+ sinks: {
86
+ name: string;
87
+ send(s: DerivedSignal): void;
88
+ }[];
89
+ onSinkError?: (sink: string, err: unknown) => void;
90
+ }) => TfloInstance;
91
+ Pattern: new (name: string) => PatternBuilder;
92
+ }
93
+ /** Whether `m` offers the capability floor the SDK relies on (C0d §3.3). */
94
+ export declare function hasFloor(m: unknown): m is TfloCtors;
95
+ /**
96
+ * The tflo module to attach to: the host's (injected, or its lazy loader, called only now, after
97
+ * consent), else the SDK's own. An injected module below the floor is ignored with one warning.
98
+ */
99
+ export declare function resolveTflo(injected: JourneezeSignalsTflo | undefined, loadOwn?: () => Promise<unknown>): Promise<TfloCtors | undefined>;
100
+ type JourneezeSignalsTflo = JourneezeTfloModule | (() => Promise<JourneezeTfloModule>);
101
+ /** The tracked element's `data-tflow-id` for an event target, or undefined (masked or untracked). */
102
+ export declare function trackedId(node: EventTarget | null | undefined, extraMask?: readonly string[]): string | undefined;
103
+ export interface SignalsOptions {
104
+ options: MountOptions;
105
+ endpoint: string;
106
+ getContext: () => Readonly<JourneezeContext>;
107
+ /** The site's widget config (the chunk that started signals already read it, §4.4). */
108
+ site: GetWidgetConfigResponse;
109
+ /** Tests: time, timers, transport and the own-copy loader. */
110
+ now?: () => number;
111
+ fetchImpl?: typeof fetch;
112
+ loadOwn?: () => Promise<unknown>;
113
+ newId?: () => string;
114
+ }
115
+ export interface Signals {
116
+ contextChanged(): void;
117
+ /** Detaches every capture; `discard` (consent withdrawn, logout) drops queued signals unsent. */
118
+ stop(discard?: boolean): void;
119
+ /** Tests: settles pending sends. */
120
+ idle(): Promise<void>;
121
+ }
122
+ /** At most this many windows open at once; the oldest is dropped without firing. */
123
+ export declare const MAX_WINDOWS = 128;
124
+ /**
125
+ * Starts signals for one handle, or resolves undefined (the site's switch is off, or tflo is
126
+ * unavailable). Never throws into the host.
127
+ *
128
+ * Windows (`notThen(...).within(ms)`) on tflo 0.1.0: a negative step completes only when its
129
+ * runtime is flushed, and `TFloBrowser.flush()` drains every runtime at once (so it is never
130
+ * called, C0d §6 N1). Each window is therefore its own pattern, keyed to its own start event, and
131
+ * at its deadline the SDK flushes that one runtime (`addPattern`'s return value): a window that a
132
+ * settling event negated flushes empty. At page end windows are removed unflushed.
133
+ */
134
+ export declare function startSignals(o: SignalsOptions): Promise<Signals | undefined>;
135
+ /** Where the host is in a published journey's checkpoints (C0d §4.1, C0c § Step ids). */
136
+ export interface Checkpoint {
137
+ journey: string;
138
+ /** 0-based position in the journey's checkpoints. */
139
+ index: number;
140
+ last: boolean;
141
+ }
142
+ /**
143
+ * The one place that knows how `journeyId`/`journeyStep` match `GetWidgetConfig.publishedJourneys`
144
+ * (C0c § Step ids): the journey by its slug; the step by exact string equality against the
145
+ * checkpoints; a repeated checkpoint matches its first occurrence after the current one, else
146
+ * its first occurrence.
147
+ */
148
+ export declare function matchCheckpoint(site: GetWidgetConfigResponse, c: Readonly<JourneezeContext>, current?: Checkpoint): Checkpoint | undefined;
149
+ export {};
@@ -0,0 +1,33 @@
1
+ /** The platform rule's name. */
2
+ export declare const STEP_COMPLETED = "step_completed";
3
+ /** The closed duration buckets, fastest first (proto `StepDurationBucket`). */
4
+ export declare const DURATION_BUCKETS: readonly ["lt5s", "lt15s", "lt60s", "lt5m", "ge5m"];
5
+ export type DurationBucket = (typeof DURATION_BUCKETS)[number];
6
+ /** The bucket a measured duration falls in. */
7
+ export declare function durationBucket(ms: number): DurationBucket;
8
+ /** `step_completed`'s params: ids and a bucket, nothing else. */
9
+ export interface StepCompleted {
10
+ journey: string;
11
+ step: string;
12
+ duration_bucket: DurationBucket;
13
+ }
14
+ /** Where the page is in a published journey's checkpoints (as matched by `matchCheckpoint`). */
15
+ export interface ReachedAnchor {
16
+ journey: string;
17
+ /** 0-based checkpoint position. */
18
+ index: number;
19
+ /** The anchor's name: the checkpoint's step id. */
20
+ step: string;
21
+ }
22
+ export interface StepTimer {
23
+ /**
24
+ * Called on every context sync with the journey the host is in and the anchor matched (if any).
25
+ * Returns the params of the completion to send, once per anchor going forward.
26
+ */
27
+ sync(journeyId: string | undefined, at: ReachedAnchor | undefined): StepCompleted | undefined;
28
+ }
29
+ /**
30
+ * One handle's step clock. It outlives tflo instances (a back/forward-cache restore re-arms the
31
+ * checkpoints but does not complete the current anchor twice); a journey change restarts it.
32
+ */
33
+ export declare function stepTimer(now: () => number): StepTimer;