@burdenoff/website-sdk 2026.922.5 → 2026.923.1

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.
@@ -1,4 +1,4 @@
1
- import { b as ExploreLimits, c as ExploreCatalog, d as ExploreMessage, e as ExploreFeedbackRating, f as ExploreFeedbackReason } from './explore-types-DvQPiL1t.mjs';
1
+ import { b as ExploreLimits, c as ExploreCatalog, d as ExploreMessage, e as ExploreFeedbackRating, f as ExploreFeedbackReason } from './explore-types-B5sdOC5M.mjs';
2
2
 
3
3
  /** What travels on the wire — mirrors `ExploreAttachmentInput`. */
4
4
  interface ExploreAttachmentPayload {
@@ -34,6 +34,34 @@ interface ExploreChatError {
34
34
  /** Whether re-sending the same message could plausibly succeed. */
35
35
  retryable: boolean;
36
36
  }
37
+ /**
38
+ * What came of handing the conversation to a person.
39
+ *
40
+ * `sent` and `already-handed-over` are BOTH successes. The second is the double-clicked
41
+ * button, the retried request and the reloaded page: the server keeps one handover per
42
+ * conversation, so a repeat submit returns the first one rather than filing a second ticket.
43
+ * A visitor who is worried enough to ask for a human must never be told "you already did
44
+ * that" — the conversation is with a person either way, and the UI says so either way.
45
+ */
46
+ type ExploreEscalateOutcome = "sent" | "already-handed-over"
47
+ /** The SERVER rejected the address (`INVALID_EMAIL`), after the client let it through. */
48
+ | "invalid-email" | "rate-limited" | "captcha"
49
+ /** No conversation token — nothing to hand over. The form should not have been offered. */
50
+ | "no-conversation" | "failed";
51
+ interface ExploreEscalateResult {
52
+ outcome: ExploreEscalateOutcome;
53
+ /**
54
+ * The address the SERVER says the reply is going to, on `sent` and `already-handed-over`;
55
+ * null otherwise.
56
+ *
57
+ * On `already-handed-over` this is the address the FIRST handover used, which may not be
58
+ * the one just typed — and it is the one the reply actually reaches, so it is the one to
59
+ * show back.
60
+ */
61
+ email: string | null;
62
+ /** When the handover was filed, as the server holds it. Null unless it exists. */
63
+ createdAt: string | null;
64
+ }
37
65
  interface UseExploreChatOptions {
38
66
  /** Slug of the product site this page is on — a soft hint to the harness. */
39
67
  productSlug?: string;
@@ -116,6 +144,30 @@ interface UseExploreChatResult {
116
144
  reason?: ExploreFeedbackReason | null;
117
145
  comment?: string | null;
118
146
  }) => Promise<boolean>;
147
+ /**
148
+ * Hand this conversation to a person, so they pick it up with the transcript attached.
149
+ *
150
+ * Awaited, like {@link submitFeedback} and for the same reason — the visitor is watching
151
+ * the control they pressed — but this one has more to say than true/false: "we could not
152
+ * deliver to that address", "you are sending these too fast" and "it is already with a
153
+ * human" are three different things to a worried visitor, and one boolean flattens them
154
+ * into a shrug. It never throws; every outcome comes back in
155
+ * {@link ExploreEscalateResult}.
156
+ *
157
+ * `email` is personal data. It goes into the mutation variables and nowhere else — not
158
+ * into `messages` (which is what this hook persists), not into `onSend`, not into any
159
+ * analytics call.
160
+ */
161
+ escalate: (email: string, note?: string) => Promise<ExploreEscalateResult>;
162
+ /**
163
+ * Whether there is a conversation a person could actually be handed.
164
+ *
165
+ * False on a fresh page and false in a read-only shared view (a `?c=` secret is not a
166
+ * conversation token), which are exactly the cases where {@link escalate} could only
167
+ * answer `no-conversation`. Offer the form on this, rather than offering one that cannot
168
+ * work.
169
+ */
170
+ canEscalate: boolean;
119
171
  /**
120
172
  * Mint a read-only link to this conversation, or null when there is nothing to share yet.
121
173
  * The secret returned is not the conversation token and cannot continue the thread.
@@ -141,11 +193,6 @@ declare const FALLBACK_EXPLORE_LIMITS: ExploreLimits;
141
193
  * brochure without a single copy change being reviewed.
142
194
  */
143
195
  declare const DEFAULT_EXPLORE_EXAMPLE_PROMPTS: string[];
144
- /**
145
- * Map a `extensions.code` (or a client-side transport code) onto the bucket the
146
- * UI renders. Unknown codes are treated as retryable — a transient backend
147
- * condition we have not enumerated is far more likely than a permanent one.
148
- */
149
196
  declare function classifyExploreError(code: string | null | undefined, serverMessage?: string | null): ExploreChatError;
150
197
  /** One past conversation, as shown in the history list and restored on click. */
151
198
  interface ArchivedExploreConversation {
@@ -194,4 +241,4 @@ declare function useSpeechOutput({ lang, }?: UseSpeechOutputOptions): UseSpeechO
194
241
  */
195
242
  declare function useProgressiveReveal(text: string, settled: boolean): string;
196
243
 
197
- export { DEFAULT_EXPLORE_EXAMPLE_PROMPTS as D, type ExploreChatError as E, FALLBACK_EXPLORE_LIMITS as F, type UseExploreChatOptions as U, type ExploreChatErrorKind as a, type ExploreChatPhase as b, type UseExploreChatResult as c, type UseSpeechOutputOptions as d, type UseSpeechOutputResult as e, classifyExploreError as f, useProgressiveReveal as g, useSpeechOutput as h, stopSpeaking as s, useExploreChat as u };
244
+ export { DEFAULT_EXPLORE_EXAMPLE_PROMPTS as D, type ExploreChatError as E, FALLBACK_EXPLORE_LIMITS as F, type UseExploreChatOptions as U, type ExploreChatErrorKind as a, type ExploreChatPhase as b, type ExploreEscalateOutcome as c, type ExploreEscalateResult as d, type UseExploreChatResult as e, type UseSpeechOutputOptions as f, type UseSpeechOutputResult as g, classifyExploreError as h, useProgressiveReveal as i, useSpeechOutput as j, stopSpeaking as s, useExploreChat as u };
@@ -1,4 +1,4 @@
1
- import { b as ExploreLimits, c as ExploreCatalog, d as ExploreMessage, e as ExploreFeedbackRating, f as ExploreFeedbackReason } from './explore-types-DvQPiL1t.js';
1
+ import { b as ExploreLimits, c as ExploreCatalog, d as ExploreMessage, e as ExploreFeedbackRating, f as ExploreFeedbackReason } from './explore-types-B5sdOC5M.js';
2
2
 
3
3
  /** What travels on the wire — mirrors `ExploreAttachmentInput`. */
4
4
  interface ExploreAttachmentPayload {
@@ -34,6 +34,34 @@ interface ExploreChatError {
34
34
  /** Whether re-sending the same message could plausibly succeed. */
35
35
  retryable: boolean;
36
36
  }
37
+ /**
38
+ * What came of handing the conversation to a person.
39
+ *
40
+ * `sent` and `already-handed-over` are BOTH successes. The second is the double-clicked
41
+ * button, the retried request and the reloaded page: the server keeps one handover per
42
+ * conversation, so a repeat submit returns the first one rather than filing a second ticket.
43
+ * A visitor who is worried enough to ask for a human must never be told "you already did
44
+ * that" — the conversation is with a person either way, and the UI says so either way.
45
+ */
46
+ type ExploreEscalateOutcome = "sent" | "already-handed-over"
47
+ /** The SERVER rejected the address (`INVALID_EMAIL`), after the client let it through. */
48
+ | "invalid-email" | "rate-limited" | "captcha"
49
+ /** No conversation token — nothing to hand over. The form should not have been offered. */
50
+ | "no-conversation" | "failed";
51
+ interface ExploreEscalateResult {
52
+ outcome: ExploreEscalateOutcome;
53
+ /**
54
+ * The address the SERVER says the reply is going to, on `sent` and `already-handed-over`;
55
+ * null otherwise.
56
+ *
57
+ * On `already-handed-over` this is the address the FIRST handover used, which may not be
58
+ * the one just typed — and it is the one the reply actually reaches, so it is the one to
59
+ * show back.
60
+ */
61
+ email: string | null;
62
+ /** When the handover was filed, as the server holds it. Null unless it exists. */
63
+ createdAt: string | null;
64
+ }
37
65
  interface UseExploreChatOptions {
38
66
  /** Slug of the product site this page is on — a soft hint to the harness. */
39
67
  productSlug?: string;
@@ -116,6 +144,30 @@ interface UseExploreChatResult {
116
144
  reason?: ExploreFeedbackReason | null;
117
145
  comment?: string | null;
118
146
  }) => Promise<boolean>;
147
+ /**
148
+ * Hand this conversation to a person, so they pick it up with the transcript attached.
149
+ *
150
+ * Awaited, like {@link submitFeedback} and for the same reason — the visitor is watching
151
+ * the control they pressed — but this one has more to say than true/false: "we could not
152
+ * deliver to that address", "you are sending these too fast" and "it is already with a
153
+ * human" are three different things to a worried visitor, and one boolean flattens them
154
+ * into a shrug. It never throws; every outcome comes back in
155
+ * {@link ExploreEscalateResult}.
156
+ *
157
+ * `email` is personal data. It goes into the mutation variables and nowhere else — not
158
+ * into `messages` (which is what this hook persists), not into `onSend`, not into any
159
+ * analytics call.
160
+ */
161
+ escalate: (email: string, note?: string) => Promise<ExploreEscalateResult>;
162
+ /**
163
+ * Whether there is a conversation a person could actually be handed.
164
+ *
165
+ * False on a fresh page and false in a read-only shared view (a `?c=` secret is not a
166
+ * conversation token), which are exactly the cases where {@link escalate} could only
167
+ * answer `no-conversation`. Offer the form on this, rather than offering one that cannot
168
+ * work.
169
+ */
170
+ canEscalate: boolean;
119
171
  /**
120
172
  * Mint a read-only link to this conversation, or null when there is nothing to share yet.
121
173
  * The secret returned is not the conversation token and cannot continue the thread.
@@ -141,11 +193,6 @@ declare const FALLBACK_EXPLORE_LIMITS: ExploreLimits;
141
193
  * brochure without a single copy change being reviewed.
142
194
  */
143
195
  declare const DEFAULT_EXPLORE_EXAMPLE_PROMPTS: string[];
144
- /**
145
- * Map a `extensions.code` (or a client-side transport code) onto the bucket the
146
- * UI renders. Unknown codes are treated as retryable — a transient backend
147
- * condition we have not enumerated is far more likely than a permanent one.
148
- */
149
196
  declare function classifyExploreError(code: string | null | undefined, serverMessage?: string | null): ExploreChatError;
150
197
  /** One past conversation, as shown in the history list and restored on click. */
151
198
  interface ArchivedExploreConversation {
@@ -194,4 +241,4 @@ declare function useSpeechOutput({ lang, }?: UseSpeechOutputOptions): UseSpeechO
194
241
  */
195
242
  declare function useProgressiveReveal(text: string, settled: boolean): string;
196
243
 
197
- export { DEFAULT_EXPLORE_EXAMPLE_PROMPTS as D, type ExploreChatError as E, FALLBACK_EXPLORE_LIMITS as F, type UseExploreChatOptions as U, type ExploreChatErrorKind as a, type ExploreChatPhase as b, type UseExploreChatResult as c, type UseSpeechOutputOptions as d, type UseSpeechOutputResult as e, classifyExploreError as f, useProgressiveReveal as g, useSpeechOutput as h, stopSpeaking as s, useExploreChat as u };
244
+ export { DEFAULT_EXPLORE_EXAMPLE_PROMPTS as D, type ExploreChatError as E, FALLBACK_EXPLORE_LIMITS as F, type UseExploreChatOptions as U, type ExploreChatErrorKind as a, type ExploreChatPhase as b, type ExploreEscalateOutcome as c, type ExploreEscalateResult as d, type UseExploreChatResult as e, type UseSpeechOutputOptions as f, type UseSpeechOutputResult as g, classifyExploreError as h, useProgressiveReveal as i, useSpeechOutput as j, stopSpeaking as s, useExploreChat as u };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/website-sdk",
3
- "version": "2026.922.5",
3
+ "version": "2026.923.1",
4
4
  "description": "Shared SDK for Burdenoff product websites - reusable React components, utilities, and configurations",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -74,8 +74,9 @@
74
74
  "type-check": "bash scripts/build-limits.sh tsc --noEmit",
75
75
  "prepublishOnly": "bun run build",
76
76
  "test": "vitest",
77
+ "test:run": "vitest run --maxWorkers=4",
77
78
  "test:coverage": "vitest --coverage",
78
- "sanity": "bash scripts/build-limits.sh --gate bash -c 'bun run format:check && bun run lint:sanity && bun run type-check && bun run build'"
79
+ "sanity": "bash scripts/build-limits.sh --gate bash -c 'bun run format:check && bun run lint:sanity && bun run type-check && bun run test:run && bun run build'"
79
80
  },
80
81
  "keywords": [
81
82
  "burdenoff",