@richardmcquiston01/chat-and-react-core 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -201
- package/README.md +1 -0
- package/dist/index.cjs +441 -77
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +209 -41
- package/dist/index.d.ts +209 -41
- package/dist/index.mjs +443 -76
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
import { StorageAdapter } from '@richardmcquiston01/storage-adapters';
|
|
2
|
+
export { StorageAdapter, StoreErrorCode, StoreResult, StoredValue } from '@richardmcquiston01/storage-adapters';
|
|
3
|
+
export { LocalStorageAdapterOptions, SessionStorageAdapterOptions, StorageChangeEvent, StorageEventTarget, WebStorageAdapterOptions, WebStorageLike, WebStorageName, localStorageAdapter, sessionStorageAdapter } from '@richardmcquiston01/storage-adapters/browser';
|
|
4
|
+
export { IndexedDbAdapterOptions, indexedDbAdapter } from '@richardmcquiston01/storage-adapters/indexeddb';
|
|
5
|
+
import { ErrorLogEntry, ErrorLogAdapter as ErrorLogAdapter$1, ConsoleErrorAdapter as ConsoleErrorAdapter$1, ConsoleErrorOptions } from '@richardmcquiston01/error-log-adapter';
|
|
6
|
+
export { ErrorLogEntry, ErrorLogResult } from '@richardmcquiston01/error-log-adapter';
|
|
7
|
+
|
|
1
8
|
type AnswerMap = Record<string, string | string[]>;
|
|
2
9
|
type ChatInputType = 'text' | 'textarea' | 'dropdown' | 'radio' | 'checkbox';
|
|
3
10
|
type JsonLogicRule = Record<string, unknown>;
|
|
@@ -11,6 +18,16 @@ interface ChatQuestion {
|
|
|
11
18
|
title: string;
|
|
12
19
|
'x-chat-placeholder'?: string;
|
|
13
20
|
'x-chat-condition'?: JsonLogicRule | ChatConditionRef;
|
|
21
|
+
/**
|
|
22
|
+
* Shows a "thinking" bubble before this question appears, so the conversation
|
|
23
|
+
* feels less abrupt. A number is the delay in milliseconds (0 turns it off for
|
|
24
|
+
* this question). An object can also set the text shown instead of the dots.
|
|
25
|
+
* Overrides the `thinkingDelay` and `thinkingText` options.
|
|
26
|
+
*/
|
|
27
|
+
'x-chat-thinking'?: number | {
|
|
28
|
+
delay?: number;
|
|
29
|
+
text?: string;
|
|
30
|
+
};
|
|
14
31
|
enum?: string[];
|
|
15
32
|
items?: {
|
|
16
33
|
enum: string[];
|
|
@@ -33,13 +50,31 @@ interface ChatPage {
|
|
|
33
50
|
questions: ChatQuestion[];
|
|
34
51
|
branching: BranchingRule[];
|
|
35
52
|
}
|
|
53
|
+
/** How a bubble leaves the screen when it is hidden: fade out, shrink away ("pop"), or vanish at once. */
|
|
54
|
+
type HideAnimation = 'fade' | 'pop' | 'none';
|
|
36
55
|
interface ChatOptions {
|
|
37
56
|
userResumable: boolean;
|
|
38
57
|
localStorage: boolean;
|
|
39
58
|
autoComplete: boolean;
|
|
59
|
+
/** Show an Edit icon on answered questions so users can change their answers. */
|
|
60
|
+
allowEdit: boolean;
|
|
61
|
+
/** Milliseconds to wait before hiding an answered question bubble. 0 keeps them visible. */
|
|
62
|
+
autoHideQuestions: number;
|
|
63
|
+
/** Milliseconds to wait before hiding an answer bubble once its question is answered. 0 keeps them visible. */
|
|
64
|
+
autoHideAnswers: number;
|
|
65
|
+
/** How hidden bubbles leave the screen. */
|
|
66
|
+
hideAnimation: HideAnimation;
|
|
67
|
+
/** Milliseconds per character when question text is "typed" onto the screen. 0 shows it at once. */
|
|
68
|
+
typingSpeed: number;
|
|
69
|
+
/** Milliseconds to show a "thinking" bubble before each question. 0 turns it off. */
|
|
70
|
+
thinkingDelay: number;
|
|
71
|
+
/** Text for the thinking bubble. Empty shows animated dots. */
|
|
72
|
+
thinkingText: string;
|
|
73
|
+
/** Show an icon on the Continue button. */
|
|
74
|
+
showContinueIcon: boolean;
|
|
40
75
|
}
|
|
41
76
|
interface ChatSchema {
|
|
42
|
-
|
|
77
|
+
$schema?: string;
|
|
43
78
|
'x-chat-version': '1';
|
|
44
79
|
'x-chat-options'?: Partial<ChatOptions>;
|
|
45
80
|
'x-chat-pages': ChatPage[];
|
|
@@ -51,7 +86,23 @@ interface PageSubmitEvent {
|
|
|
51
86
|
pageId: string;
|
|
52
87
|
visitIndex: number;
|
|
53
88
|
answers: AnswerMap;
|
|
89
|
+
/** Present and true when the user re-submitted a page they had already answered. */
|
|
90
|
+
edited?: boolean;
|
|
91
|
+
}
|
|
92
|
+
/** One page the user has submitted, in the order they visited it. */
|
|
93
|
+
interface RouteEntry {
|
|
94
|
+
pageId: string;
|
|
95
|
+
answers: AnswerMap;
|
|
54
96
|
}
|
|
97
|
+
/**
|
|
98
|
+
* What happened to the form after `submitPage`:
|
|
99
|
+
* - `advanced`: a normal submit; the form moved to the next page or completed.
|
|
100
|
+
* - `edit-resumed`: an earlier page was edited and the route is unchanged, so the
|
|
101
|
+
* form is back on the page the user was on.
|
|
102
|
+
* - `edit-rerouted`: an earlier page was edited and its branching now leads
|
|
103
|
+
* somewhere else. Later pages were dropped and the form moved to the new page.
|
|
104
|
+
*/
|
|
105
|
+
type SubmitOutcome = 'advanced' | 'edit-resumed' | 'edit-rerouted';
|
|
55
106
|
interface FormCompleteEvent {
|
|
56
107
|
type: 'form:complete';
|
|
57
108
|
sessionId: string;
|
|
@@ -59,49 +110,42 @@ interface FormCompleteEvent {
|
|
|
59
110
|
}
|
|
60
111
|
type ChatEvent = PageSubmitEvent | FormCompleteEvent;
|
|
61
112
|
type ChatErrorCode = 'SCHEMA_INVALID' | 'RULE_EVAL_FAILED' | 'ADAPTER_ERROR' | 'STORAGE_UNAVAILABLE';
|
|
62
|
-
|
|
63
|
-
code: ChatErrorCode;
|
|
64
|
-
message: string;
|
|
65
|
-
cause?: unknown;
|
|
66
|
-
}
|
|
113
|
+
type ChatError = ErrorLogEntry<ChatErrorCode>;
|
|
67
114
|
interface InputAdapter {
|
|
68
115
|
transform(raw: unknown): unknown;
|
|
69
116
|
}
|
|
70
117
|
interface OutputAdapter {
|
|
71
118
|
emit(event: ChatEvent): void;
|
|
72
119
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
120
|
+
/**
|
|
121
|
+
* Routes `ChatError`s to a sink. `log` returns a result instead of throwing, so the
|
|
122
|
+
* caller can check `ok`. See `@richardmcquiston01/error-log-adapter`.
|
|
123
|
+
*/
|
|
124
|
+
type ErrorLogAdapter = ErrorLogAdapter$1<ChatErrorCode>;
|
|
125
|
+
|
|
76
126
|
interface ChatControllerConfig {
|
|
77
127
|
schema: unknown;
|
|
78
128
|
inputAdapter?: InputAdapter;
|
|
79
129
|
outputAdapter?: OutputAdapter;
|
|
80
130
|
errorAdapter?: ErrorLogAdapter;
|
|
131
|
+
/**
|
|
132
|
+
* Option values that win over the schema's `x-chat-options`, for example ones
|
|
133
|
+
* read from element attributes.
|
|
134
|
+
*/
|
|
135
|
+
options?: Partial<ChatOptions>;
|
|
81
136
|
}
|
|
82
137
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
* @param raw - The raw input to validate.
|
|
87
|
-
* @returns The typed {@link ChatSchema} if valid.
|
|
88
|
-
* @throws `{ code: 'SCHEMA_INVALID', message: string }` on any validation failure.
|
|
89
|
-
*/
|
|
90
|
-
declare function parseSchema(raw: unknown): ChatSchema;
|
|
138
|
+
declare class BrowserEventAdapter implements OutputAdapter {
|
|
139
|
+
emit(event: ChatEvent): void;
|
|
140
|
+
}
|
|
91
141
|
|
|
92
|
-
/**
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
* @param rule The JSON Logic rule to evaluate
|
|
100
|
-
* @param answers The answer map to evaluate the rule against
|
|
101
|
-
* @returns The boolean result of the rule evaluation
|
|
102
|
-
* @throws {{ code: 'RULE_EVAL_FAILED', message: string, cause: unknown }} if rule evaluation fails
|
|
103
|
-
*/
|
|
104
|
-
evaluate(rule: JsonLogicRule, answers: AnswerMap): boolean;
|
|
142
|
+
/** Writes errors to `console.error`, prefixed with `[chat-and-react]` unless overridden. */
|
|
143
|
+
declare class ConsoleErrorAdapter extends ConsoleErrorAdapter$1<ChatErrorCode> {
|
|
144
|
+
constructor(options?: ConsoleErrorOptions);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
declare class IdentityInputAdapter implements InputAdapter {
|
|
148
|
+
transform(raw: unknown): unknown;
|
|
105
149
|
}
|
|
106
150
|
|
|
107
151
|
interface StorageBackend {
|
|
@@ -115,7 +159,22 @@ interface SessionProgress {
|
|
|
115
159
|
currentPageId: string;
|
|
116
160
|
answers: AnswerMap;
|
|
117
161
|
}
|
|
118
|
-
|
|
162
|
+
/**
|
|
163
|
+
* What `ChatController` needs from session storage. `SessionManager` keeps it in a
|
|
164
|
+
* synchronous Web Storage object; `AdapterSessionManager` keeps it in a `StorageAdapter`.
|
|
165
|
+
*/
|
|
166
|
+
interface SessionStore {
|
|
167
|
+
readonly isStorageAvailable: boolean;
|
|
168
|
+
getSessionId(): string;
|
|
169
|
+
saveProgress(currentPageId: string, answers: AnswerMap): void;
|
|
170
|
+
loadProgress(): SessionProgress | null;
|
|
171
|
+
saveInput(questionId: string, value: string | string[]): void;
|
|
172
|
+
loadInput(questionId: string): string | string[] | null;
|
|
173
|
+
clearAll(): void;
|
|
174
|
+
/** Stops listening for changes made elsewhere. Called when the controller is destroyed. */
|
|
175
|
+
dispose?(): void;
|
|
176
|
+
}
|
|
177
|
+
declare class SessionManager implements SessionStore {
|
|
119
178
|
private readonly storage;
|
|
120
179
|
private readonly sessionId;
|
|
121
180
|
private readonly available;
|
|
@@ -146,12 +205,18 @@ declare class ChatController {
|
|
|
146
205
|
private currentPage;
|
|
147
206
|
private allAnswers;
|
|
148
207
|
private visitIndex;
|
|
208
|
+
/** Pages submitted in this session, in visit order. Used for editing earlier answers. */
|
|
209
|
+
private route;
|
|
210
|
+
/** Index in `route` of the page being edited, or null when not editing. */
|
|
211
|
+
private editIndex;
|
|
212
|
+
/** The page the user was on before they started editing an earlier one. */
|
|
213
|
+
private resumePageId;
|
|
149
214
|
private readonly listeners;
|
|
150
215
|
/**
|
|
151
216
|
* @param config - Controller configuration including schema and optional adapters.
|
|
152
217
|
* @param sessionManager - Optional session manager for test injection.
|
|
153
218
|
*/
|
|
154
|
-
constructor(config: ChatControllerConfig, sessionManager?:
|
|
219
|
+
constructor(config: ChatControllerConfig, sessionManager?: SessionStore);
|
|
155
220
|
/**
|
|
156
221
|
* Initialises the controller: parses the schema, resolves options,
|
|
157
222
|
* restores any saved session, and positions to the first page.
|
|
@@ -170,6 +235,24 @@ declare class ChatController {
|
|
|
170
235
|
* Returns the resolved options for the current session (schema options merged with defaults).
|
|
171
236
|
*/
|
|
172
237
|
getOptions(): ChatOptions;
|
|
238
|
+
/**
|
|
239
|
+
* Returns the pages the user has submitted in this session, in visit order.
|
|
240
|
+
* A session restored from storage starts with an empty route.
|
|
241
|
+
*/
|
|
242
|
+
getRoute(): RouteEntry[];
|
|
243
|
+
/**
|
|
244
|
+
* Returns every answer given so far, for pre-filling inputs.
|
|
245
|
+
*/
|
|
246
|
+
getAnswers(): AnswerMap;
|
|
247
|
+
/**
|
|
248
|
+
* Re-opens a page the user already submitted so its answers can be changed.
|
|
249
|
+
* Call `submitPage` afterwards to save the new answers. Not available once the
|
|
250
|
+
* form is complete.
|
|
251
|
+
*
|
|
252
|
+
* @param routeIndex - Position of the page in `getRoute()`.
|
|
253
|
+
* @returns true when the page was re-opened.
|
|
254
|
+
*/
|
|
255
|
+
reopenPage(routeIndex: number): boolean;
|
|
173
256
|
/**
|
|
174
257
|
* Submits the answers for the current page and advances the session.
|
|
175
258
|
* - Accumulates answers into `allAnswers`.
|
|
@@ -177,9 +260,15 @@ declare class ChatController {
|
|
|
177
260
|
* - Resolves branching to find the next page.
|
|
178
261
|
* - Emits `form:complete` and clears session when `**FORM_COMPLETED**` is reached.
|
|
179
262
|
*
|
|
263
|
+
* When the page was re-opened with `reopenPage`, the new answers replace the
|
|
264
|
+
* old ones and `page:submit` is emitted with `edited: true`. If the page's
|
|
265
|
+
* branching still leads to the same next page, the form returns to the page the
|
|
266
|
+
* user was on. If it leads somewhere else, the pages that followed it are
|
|
267
|
+
* dropped, along with their answers, and the form continues from the new page.
|
|
268
|
+
*
|
|
180
269
|
* @param pageAnswers - Answers collected for the current page.
|
|
181
270
|
*/
|
|
182
|
-
submitPage(pageAnswers: AnswerMap):
|
|
271
|
+
submitPage(pageAnswers: AnswerMap): SubmitOutcome;
|
|
183
272
|
/**
|
|
184
273
|
* Registers an event listener for the given event type.
|
|
185
274
|
*
|
|
@@ -203,6 +292,12 @@ declare class ChatController {
|
|
|
203
292
|
* Returns true when no condition is present. On evaluation failure, logs and returns false.
|
|
204
293
|
*/
|
|
205
294
|
private isQuestionVisible;
|
|
295
|
+
/**
|
|
296
|
+
* The answers question conditions are checked against. While an earlier page is
|
|
297
|
+
* being edited, that page's own answers are left out, so its questions are
|
|
298
|
+
* shown exactly as they were the first time the page was asked.
|
|
299
|
+
*/
|
|
300
|
+
private answersForRules;
|
|
206
301
|
/**
|
|
207
302
|
* Walks the branching rules top-to-bottom. Returns the first match's destination_id.
|
|
208
303
|
* Falls back to `**FORM_COMPLETED**` if nothing matches (should not happen with valid schemas).
|
|
@@ -221,18 +316,91 @@ declare class ChatController {
|
|
|
221
316
|
* Used for unrecoverable errors (schema invalid, adapter failure on start).
|
|
222
317
|
*/
|
|
223
318
|
private handleFatal;
|
|
319
|
+
/**
|
|
320
|
+
* Sends the error to the error adapter without waiting for it. If the adapter
|
|
321
|
+
* reports a failure, rejects, or throws, that is written to `console.error` so
|
|
322
|
+
* the original error is never lost and the form keeps running.
|
|
323
|
+
*/
|
|
324
|
+
private logError;
|
|
224
325
|
}
|
|
225
326
|
|
|
226
|
-
|
|
227
|
-
|
|
327
|
+
/**
|
|
328
|
+
* Evaluates JSON Logic rules against provided answers.
|
|
329
|
+
* Wraps json-logic-js to provide type-safe rule evaluation.
|
|
330
|
+
*/
|
|
331
|
+
declare class RuleEngine {
|
|
332
|
+
/**
|
|
333
|
+
* Evaluates a JSON Logic rule against an answer map.
|
|
334
|
+
* @param rule The JSON Logic rule to evaluate
|
|
335
|
+
* @param answers The answer map to evaluate the rule against
|
|
336
|
+
* @returns The boolean result of the rule evaluation
|
|
337
|
+
* @throws {{ code: 'RULE_EVAL_FAILED', message: string, cause: unknown }} if rule evaluation fails
|
|
338
|
+
*/
|
|
339
|
+
evaluate(rule: JsonLogicRule, answers: AnswerMap): boolean;
|
|
228
340
|
}
|
|
229
341
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
342
|
+
/**
|
|
343
|
+
* Parses and validates an unknown input as a {@link ChatSchema}.
|
|
344
|
+
*
|
|
345
|
+
* @param raw - The raw input to validate.
|
|
346
|
+
* @returns The typed {@link ChatSchema} if valid.
|
|
347
|
+
* @throws `{ code: 'SCHEMA_INVALID', message: string }` on any validation failure.
|
|
348
|
+
*/
|
|
349
|
+
declare function parseSchema(raw: unknown): ChatSchema;
|
|
233
350
|
|
|
234
|
-
|
|
235
|
-
|
|
351
|
+
type InputValue = string | string[];
|
|
352
|
+
interface AdapterSessionManagerOptions {
|
|
353
|
+
/** Called when the adapter fails. The session keeps working from memory. */
|
|
354
|
+
onError?: (error: ChatError) => void;
|
|
355
|
+
/**
|
|
356
|
+
* Called when saved progress changes because of a write made elsewhere, such as
|
|
357
|
+
* another tab. `null` means the session was cleared there.
|
|
358
|
+
*/
|
|
359
|
+
onExternalChange?: (progress: SessionProgress | null) => void;
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* A session store backed by a `StorageAdapter`. `ChatController` is synchronous, so
|
|
363
|
+
* the saved session is loaded into memory by `create()`, reads come from memory, and
|
|
364
|
+
* writes are saved in the background. Failures are reported through `onError`.
|
|
365
|
+
*
|
|
366
|
+
* Saves carry the revision they were based on. If another tab wrote first, the two
|
|
367
|
+
* are merged: inputs per question, and progress only when this tab saved progress
|
|
368
|
+
* itself, so a tab that only typed cannot overwrite another tab's progress.
|
|
369
|
+
*/
|
|
370
|
+
declare class AdapterSessionManager implements SessionStore {
|
|
371
|
+
private readonly adapter;
|
|
372
|
+
private readonly sessionId;
|
|
373
|
+
private readonly available;
|
|
374
|
+
private readonly options;
|
|
375
|
+
private record;
|
|
376
|
+
private revision;
|
|
377
|
+
private readonly dirtyInputs;
|
|
378
|
+
private dirtyProgress;
|
|
379
|
+
private pending;
|
|
380
|
+
private persisting;
|
|
381
|
+
private readonly signal;
|
|
382
|
+
private unsubscribe;
|
|
383
|
+
private constructor();
|
|
384
|
+
/** Loads (or creates) the session id and its saved record, then returns the manager. */
|
|
385
|
+
static create(adapter: StorageAdapter, options?: AdapterSessionManagerOptions): Promise<AdapterSessionManager>;
|
|
386
|
+
getSessionId(): string;
|
|
387
|
+
get isStorageAvailable(): boolean;
|
|
388
|
+
saveProgress(currentPageId: string, answers: AnswerMap): void;
|
|
389
|
+
loadProgress(): SessionProgress | null;
|
|
390
|
+
saveInput(questionId: string, value: InputValue): void;
|
|
391
|
+
loadInput(questionId: string): InputValue | null;
|
|
392
|
+
clearAll(): void;
|
|
393
|
+
dispose(): void;
|
|
394
|
+
/** Resolves when every save made so far has finished. Mostly for tests. */
|
|
395
|
+
flush(): Promise<void>;
|
|
396
|
+
private report;
|
|
397
|
+
private schedulePersist;
|
|
398
|
+
/** Saves until nothing is left to save. Never rejects. */
|
|
399
|
+
private drain;
|
|
400
|
+
private persistOnce;
|
|
401
|
+
/** Merges a version written elsewhere with this tab's unsaved changes. */
|
|
402
|
+
private applyRemote;
|
|
403
|
+
private removeRecord;
|
|
236
404
|
}
|
|
237
405
|
|
|
238
|
-
export { type AnswerMap, type BranchingAlways, type BranchingIf, type BranchingRule, BrowserEventAdapter, type ChatConditionRef, ChatController, type ChatControllerConfig, type ChatError, type ChatErrorCode, type ChatEvent, type ChatInputType, type ChatOptions, type ChatPage, type ChatQuestion, type ChatSchema, ConsoleErrorAdapter, type ErrorLogAdapter, type FormCompleteEvent, IdentityInputAdapter, type InputAdapter, type JsonLogicRule, type OutputAdapter, type PageSubmitEvent, RuleEngine, SessionManager, type SessionProgress, type StorageBackend, parseSchema };
|
|
406
|
+
export { AdapterSessionManager, type AdapterSessionManagerOptions, type AnswerMap, type BranchingAlways, type BranchingIf, type BranchingRule, BrowserEventAdapter, type ChatConditionRef, ChatController, type ChatControllerConfig, type ChatError, type ChatErrorCode, type ChatEvent, type ChatInputType, type ChatOptions, type ChatPage, type ChatQuestion, type ChatSchema, ConsoleErrorAdapter, type ErrorLogAdapter, type FormCompleteEvent, type HideAnimation, IdentityInputAdapter, type InputAdapter, type JsonLogicRule, type OutputAdapter, type PageSubmitEvent, type RouteEntry, RuleEngine, SessionManager, type SessionProgress, type SessionStore, type StorageBackend, type SubmitOutcome, parseSchema };
|