@wokuapp/react-native 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +139 -0
- package/dist/index.cjs +334 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +279 -0
- package/dist/index.d.ts +279 -0
- package/dist/index.js +322 -0
- package/dist/index.js.map +1 -0
- package/package.json +73 -0
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Injectable adapters. The SDK core never imports React Native or a
|
|
3
|
+
* concrete HTTP/storage library directly — host apps provide these so
|
|
4
|
+
* the core stays unit-testable and platform-agnostic.
|
|
5
|
+
*/
|
|
6
|
+
/** Minimal HTTP response the SDK needs. */
|
|
7
|
+
interface HttpResponse {
|
|
8
|
+
status: number;
|
|
9
|
+
ok: boolean;
|
|
10
|
+
json: () => Promise<unknown>;
|
|
11
|
+
headers: {
|
|
12
|
+
get: (name: string) => string | null;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
interface HttpRequest {
|
|
16
|
+
method: string;
|
|
17
|
+
url: string;
|
|
18
|
+
headers: Record<string, string>;
|
|
19
|
+
body?: string;
|
|
20
|
+
}
|
|
21
|
+
/** HTTP transport. Defaults to global `fetch`. */
|
|
22
|
+
interface HttpClient {
|
|
23
|
+
request: (req: HttpRequest) => Promise<HttpResponse>;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Key/value persistence for the offline queue. React Native apps pass an
|
|
27
|
+
* MMKV or AsyncStorage-backed adapter; tests use the in-memory one.
|
|
28
|
+
*/
|
|
29
|
+
interface Storage {
|
|
30
|
+
getItem: (key: string) => Promise<string | null> | string | null;
|
|
31
|
+
setItem: (key: string, value: string) => Promise<void> | void;
|
|
32
|
+
removeItem: (key: string) => Promise<void> | void;
|
|
33
|
+
}
|
|
34
|
+
interface Logger {
|
|
35
|
+
debug: (msg: string, meta?: unknown) => void;
|
|
36
|
+
warn: (msg: string, meta?: unknown) => void;
|
|
37
|
+
error: (msg: string, meta?: unknown) => void;
|
|
38
|
+
}
|
|
39
|
+
/** Default HTTP client backed by the global `fetch`. */
|
|
40
|
+
declare const fetchHttpClient: HttpClient;
|
|
41
|
+
/** In-memory storage. Default for tests and ephemeral usage. */
|
|
42
|
+
declare class InMemoryStorage implements Storage {
|
|
43
|
+
private readonly map;
|
|
44
|
+
getItem(key: string): string | null;
|
|
45
|
+
setItem(key: string, value: string): void;
|
|
46
|
+
removeItem(key: string): void;
|
|
47
|
+
}
|
|
48
|
+
/** No-op logger (default). */
|
|
49
|
+
declare const noopLogger: Logger;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Public types for the Woku React Native SDK.
|
|
53
|
+
*
|
|
54
|
+
* The SDK core is transport- and platform-agnostic: it builds typed
|
|
55
|
+
* capture payloads and hands them to injectable adapters (HTTP, storage,
|
|
56
|
+
* audio). React Native apps wire those adapters at init time.
|
|
57
|
+
*/
|
|
58
|
+
/** The two capture flows Woku supports. */
|
|
59
|
+
type CaptureKind = 'woku' | 'nps';
|
|
60
|
+
/** An audio attachment for a text/voice comment. */
|
|
61
|
+
interface AudioAttachment {
|
|
62
|
+
/** URI or local path to the recorded audio file. */
|
|
63
|
+
uri: string;
|
|
64
|
+
/** MIME type, e.g. `audio/m4a`. */
|
|
65
|
+
mimeType: string;
|
|
66
|
+
/** Duration in milliseconds, if known. */
|
|
67
|
+
durationMs?: number;
|
|
68
|
+
}
|
|
69
|
+
/** Who is giving the feedback. Anonymous unless an identifier is set. */
|
|
70
|
+
interface Respondent {
|
|
71
|
+
email?: string;
|
|
72
|
+
phone?: string;
|
|
73
|
+
/** Free-form external id the host app controls (e.g. a CRM id). */
|
|
74
|
+
externalId?: string;
|
|
75
|
+
}
|
|
76
|
+
/** Input for a Woku capture (1-tap rating 1..5 + optional comment/audio). */
|
|
77
|
+
interface WokuCaptureInput {
|
|
78
|
+
wokuId: string;
|
|
79
|
+
/** Integer 1..5. */
|
|
80
|
+
rating: number;
|
|
81
|
+
comment?: string;
|
|
82
|
+
audio?: AudioAttachment;
|
|
83
|
+
respondent?: Respondent;
|
|
84
|
+
/** Arbitrary context tags attached to the submission. */
|
|
85
|
+
metadata?: Record<string, unknown>;
|
|
86
|
+
}
|
|
87
|
+
/** Input for an NPS capture (score 0..10 + optional review). */
|
|
88
|
+
interface NpsCaptureInput {
|
|
89
|
+
npsId: string;
|
|
90
|
+
/** Integer 0..10. */
|
|
91
|
+
score: number;
|
|
92
|
+
comment?: string;
|
|
93
|
+
audio?: AudioAttachment;
|
|
94
|
+
respondent?: Respondent;
|
|
95
|
+
metadata?: Record<string, unknown>;
|
|
96
|
+
}
|
|
97
|
+
/** Normalized submission persisted in the offline queue and sent to Woku. */
|
|
98
|
+
interface CaptureSubmission {
|
|
99
|
+
/** Client-generated idempotency id. */
|
|
100
|
+
id: string;
|
|
101
|
+
kind: CaptureKind;
|
|
102
|
+
companyId: string;
|
|
103
|
+
targetId: string;
|
|
104
|
+
rating?: number;
|
|
105
|
+
score?: number;
|
|
106
|
+
comment?: string;
|
|
107
|
+
audio?: AudioAttachment;
|
|
108
|
+
respondent?: Respondent;
|
|
109
|
+
metadata?: Record<string, unknown>;
|
|
110
|
+
/** Unix epoch ms when the capture was created on the device. */
|
|
111
|
+
createdAt: number;
|
|
112
|
+
}
|
|
113
|
+
/** Result of attempting to deliver a submission. */
|
|
114
|
+
interface SubmissionResult {
|
|
115
|
+
id: string;
|
|
116
|
+
status: 'sent' | 'queued' | 'failed' | 'quarantined';
|
|
117
|
+
/** Server id once accepted. */
|
|
118
|
+
remoteId?: string;
|
|
119
|
+
error?: string;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
interface WokuClientConfig {
|
|
123
|
+
/** Base URL of the Woku API, e.g. `https://api.woku.app`. */
|
|
124
|
+
apiUrl: string;
|
|
125
|
+
/** Public SDK key issued per company for capture ingestion. */
|
|
126
|
+
publicKey: string;
|
|
127
|
+
/** Tenant the captures belong to. */
|
|
128
|
+
companyId: string;
|
|
129
|
+
/** HTTP transport. Defaults to the global `fetch`. */
|
|
130
|
+
http?: HttpClient;
|
|
131
|
+
logger?: Logger;
|
|
132
|
+
/** Ingest path appended to `apiUrl`. Defaults to `/sdk/v1/captures`. */
|
|
133
|
+
capturePath?: string;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Thin, typed transport to the Woku capture ingest endpoint. Stateless:
|
|
137
|
+
* it turns a normalized {@link CaptureSubmission} into an authenticated
|
|
138
|
+
* POST and maps the response (incl. quarantine 429) to a result/error.
|
|
139
|
+
*/
|
|
140
|
+
declare class WokuClient {
|
|
141
|
+
private readonly config;
|
|
142
|
+
private readonly http;
|
|
143
|
+
private readonly logger;
|
|
144
|
+
private readonly endpoint;
|
|
145
|
+
constructor(config: WokuClientConfig);
|
|
146
|
+
/**
|
|
147
|
+
* Sends one submission. Resolves with a `sent` result on 2xx, throws
|
|
148
|
+
* {@link WokuQuarantineError} on a quarantine 429, {@link WokuNetworkError}
|
|
149
|
+
* on transport failure, and resolves with a `failed` result on other
|
|
150
|
+
* non-2xx responses (so the caller can decide to re-queue).
|
|
151
|
+
*/
|
|
152
|
+
send(submission: CaptureSubmission): Promise<SubmissionResult>;
|
|
153
|
+
private safeJson;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
interface OfflineQueueOptions {
|
|
157
|
+
storage?: Storage;
|
|
158
|
+
/** Storage key for the persisted queue. */
|
|
159
|
+
storageKey?: string;
|
|
160
|
+
/** Drop a submission after this many failed delivery attempts. */
|
|
161
|
+
maxAttempts?: number;
|
|
162
|
+
}
|
|
163
|
+
interface FlushResult {
|
|
164
|
+
sent: number;
|
|
165
|
+
failed: number;
|
|
166
|
+
remaining: number;
|
|
167
|
+
/** Set when flushing stopped early due to quarantine. */
|
|
168
|
+
quarantined?: boolean;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Durable, FIFO offline buffer for captures. Persists through an
|
|
172
|
+
* injectable {@link Storage} so submissions survive app restarts, and
|
|
173
|
+
* flushes them through a caller-provided `send` function. Pure and
|
|
174
|
+
* deterministic: no timers, no platform APIs.
|
|
175
|
+
*
|
|
176
|
+
* Delivery semantics on flush:
|
|
177
|
+
* - `sent` -> removed from the queue.
|
|
178
|
+
* - quarantine -> flush stops (server is rate-limiting the
|
|
179
|
+
* respondent); items are kept for the next attempt.
|
|
180
|
+
* - network error -> attempt count bumped, item kept, flush continues.
|
|
181
|
+
* - `failed` -> attempt count bumped; dropped once maxAttempts is
|
|
182
|
+
* reached so a permanently-bad item can't wedge it.
|
|
183
|
+
*/
|
|
184
|
+
declare class OfflineQueue {
|
|
185
|
+
private readonly storage;
|
|
186
|
+
private readonly key;
|
|
187
|
+
private readonly maxAttempts;
|
|
188
|
+
private items;
|
|
189
|
+
constructor(opts?: OfflineQueueOptions);
|
|
190
|
+
private load;
|
|
191
|
+
private persist;
|
|
192
|
+
/** Adds a submission to the tail of the queue (dedup by id). */
|
|
193
|
+
enqueue(submission: CaptureSubmission): Promise<void>;
|
|
194
|
+
size(): Promise<number>;
|
|
195
|
+
/** Returns a copy of the pending submissions (oldest first). */
|
|
196
|
+
pending(): Promise<CaptureSubmission[]>;
|
|
197
|
+
/**
|
|
198
|
+
* Attempts to deliver every queued submission through `send`, applying
|
|
199
|
+
* the delivery semantics documented on the class. Persists once at the
|
|
200
|
+
* end and returns a summary.
|
|
201
|
+
*/
|
|
202
|
+
flush(send: (submission: CaptureSubmission) => Promise<SubmissionResult>): Promise<FlushResult>;
|
|
203
|
+
/** Clears the queue (e.g. on logout). */
|
|
204
|
+
clear(): Promise<void>;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
interface WokuSdkConfig extends WokuClientConfig {
|
|
208
|
+
/** Storage adapter for the offline queue (MMKV/AsyncStorage in RN). */
|
|
209
|
+
storage?: Storage;
|
|
210
|
+
/** Drop a queued submission after this many failed attempts. */
|
|
211
|
+
maxQueueAttempts?: number;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Top-level Woku SDK. Wires the typed {@link WokuClient} to a durable
|
|
215
|
+
* {@link OfflineQueue}: a capture is attempted immediately and, if the
|
|
216
|
+
* device is offline or the server is unreachable, transparently queued
|
|
217
|
+
* and retried on the next {@link WokuSdk.flush}.
|
|
218
|
+
*
|
|
219
|
+
* @example
|
|
220
|
+
* const woku = new WokuSdk({
|
|
221
|
+
* apiUrl: 'https://api.woku.app',
|
|
222
|
+
* publicKey: 'pk_live_...',
|
|
223
|
+
* companyId: 'company_123',
|
|
224
|
+
* storage: mmkvStorageAdapter,
|
|
225
|
+
* });
|
|
226
|
+
* await woku.captureNps({ npsId: 'nps_1', score: 9, comment: 'Great!' });
|
|
227
|
+
*/
|
|
228
|
+
declare class WokuSdk {
|
|
229
|
+
private readonly client;
|
|
230
|
+
private readonly queue;
|
|
231
|
+
private readonly logger;
|
|
232
|
+
private readonly companyId;
|
|
233
|
+
constructor(config: WokuSdkConfig);
|
|
234
|
+
/** Captures a Woku rating (1..5) with optional text/audio comment. */
|
|
235
|
+
captureWoku(input: WokuCaptureInput): Promise<SubmissionResult>;
|
|
236
|
+
/** Captures an NPS score (0..10) with optional text/audio review. */
|
|
237
|
+
captureNps(input: NpsCaptureInput): Promise<SubmissionResult>;
|
|
238
|
+
/** Retries every queued submission. Safe to call on app foreground or
|
|
239
|
+
* when connectivity is regained. */
|
|
240
|
+
flush(): Promise<FlushResult>;
|
|
241
|
+
/** Number of submissions waiting to be delivered. */
|
|
242
|
+
pendingCount(): Promise<number>;
|
|
243
|
+
/** Clears the offline queue (e.g. on user logout). */
|
|
244
|
+
clearQueue(): Promise<void>;
|
|
245
|
+
/**
|
|
246
|
+
* Tries to send immediately; on quarantine or network failure the
|
|
247
|
+
* submission is queued for a later flush rather than thrown away.
|
|
248
|
+
*/
|
|
249
|
+
private deliver;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/** Base error for all SDK failures. */
|
|
253
|
+
declare class WokuError extends Error {
|
|
254
|
+
readonly code: string;
|
|
255
|
+
constructor(message: string, code: string);
|
|
256
|
+
}
|
|
257
|
+
/** Thrown when the SDK is misconfigured (missing companyId, etc.). */
|
|
258
|
+
declare class WokuConfigError extends WokuError {
|
|
259
|
+
constructor(message: string);
|
|
260
|
+
}
|
|
261
|
+
/** Validation failure on a capture input (bad rating/score, etc.). */
|
|
262
|
+
declare class WokuValidationError extends WokuError {
|
|
263
|
+
constructor(message: string);
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* The backend rejected the submission because the respondent is in
|
|
267
|
+
* quarantine (HTTP 429 with a QUARANTINE_BLOCKED code). Carries the
|
|
268
|
+
* retry-after hint in seconds when the server provides one.
|
|
269
|
+
*/
|
|
270
|
+
declare class WokuQuarantineError extends WokuError {
|
|
271
|
+
readonly retryAfterSeconds?: number | undefined;
|
|
272
|
+
constructor(message: string, retryAfterSeconds?: number | undefined);
|
|
273
|
+
}
|
|
274
|
+
/** Network/transport failure; the submission should be retried later. */
|
|
275
|
+
declare class WokuNetworkError extends WokuError {
|
|
276
|
+
constructor(message: string);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
export { type AudioAttachment, type CaptureKind, type CaptureSubmission, type FlushResult, type HttpClient, type HttpRequest, type HttpResponse, InMemoryStorage, type Logger, type NpsCaptureInput, OfflineQueue, type OfflineQueueOptions, type Respondent, type Storage, type SubmissionResult, type WokuCaptureInput, WokuClient, type WokuClientConfig, WokuConfigError, WokuError, WokuNetworkError, WokuQuarantineError, WokuSdk, type WokuSdkConfig, WokuValidationError, fetchHttpClient, noopLogger };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Injectable adapters. The SDK core never imports React Native or a
|
|
3
|
+
* concrete HTTP/storage library directly — host apps provide these so
|
|
4
|
+
* the core stays unit-testable and platform-agnostic.
|
|
5
|
+
*/
|
|
6
|
+
/** Minimal HTTP response the SDK needs. */
|
|
7
|
+
interface HttpResponse {
|
|
8
|
+
status: number;
|
|
9
|
+
ok: boolean;
|
|
10
|
+
json: () => Promise<unknown>;
|
|
11
|
+
headers: {
|
|
12
|
+
get: (name: string) => string | null;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
interface HttpRequest {
|
|
16
|
+
method: string;
|
|
17
|
+
url: string;
|
|
18
|
+
headers: Record<string, string>;
|
|
19
|
+
body?: string;
|
|
20
|
+
}
|
|
21
|
+
/** HTTP transport. Defaults to global `fetch`. */
|
|
22
|
+
interface HttpClient {
|
|
23
|
+
request: (req: HttpRequest) => Promise<HttpResponse>;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Key/value persistence for the offline queue. React Native apps pass an
|
|
27
|
+
* MMKV or AsyncStorage-backed adapter; tests use the in-memory one.
|
|
28
|
+
*/
|
|
29
|
+
interface Storage {
|
|
30
|
+
getItem: (key: string) => Promise<string | null> | string | null;
|
|
31
|
+
setItem: (key: string, value: string) => Promise<void> | void;
|
|
32
|
+
removeItem: (key: string) => Promise<void> | void;
|
|
33
|
+
}
|
|
34
|
+
interface Logger {
|
|
35
|
+
debug: (msg: string, meta?: unknown) => void;
|
|
36
|
+
warn: (msg: string, meta?: unknown) => void;
|
|
37
|
+
error: (msg: string, meta?: unknown) => void;
|
|
38
|
+
}
|
|
39
|
+
/** Default HTTP client backed by the global `fetch`. */
|
|
40
|
+
declare const fetchHttpClient: HttpClient;
|
|
41
|
+
/** In-memory storage. Default for tests and ephemeral usage. */
|
|
42
|
+
declare class InMemoryStorage implements Storage {
|
|
43
|
+
private readonly map;
|
|
44
|
+
getItem(key: string): string | null;
|
|
45
|
+
setItem(key: string, value: string): void;
|
|
46
|
+
removeItem(key: string): void;
|
|
47
|
+
}
|
|
48
|
+
/** No-op logger (default). */
|
|
49
|
+
declare const noopLogger: Logger;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Public types for the Woku React Native SDK.
|
|
53
|
+
*
|
|
54
|
+
* The SDK core is transport- and platform-agnostic: it builds typed
|
|
55
|
+
* capture payloads and hands them to injectable adapters (HTTP, storage,
|
|
56
|
+
* audio). React Native apps wire those adapters at init time.
|
|
57
|
+
*/
|
|
58
|
+
/** The two capture flows Woku supports. */
|
|
59
|
+
type CaptureKind = 'woku' | 'nps';
|
|
60
|
+
/** An audio attachment for a text/voice comment. */
|
|
61
|
+
interface AudioAttachment {
|
|
62
|
+
/** URI or local path to the recorded audio file. */
|
|
63
|
+
uri: string;
|
|
64
|
+
/** MIME type, e.g. `audio/m4a`. */
|
|
65
|
+
mimeType: string;
|
|
66
|
+
/** Duration in milliseconds, if known. */
|
|
67
|
+
durationMs?: number;
|
|
68
|
+
}
|
|
69
|
+
/** Who is giving the feedback. Anonymous unless an identifier is set. */
|
|
70
|
+
interface Respondent {
|
|
71
|
+
email?: string;
|
|
72
|
+
phone?: string;
|
|
73
|
+
/** Free-form external id the host app controls (e.g. a CRM id). */
|
|
74
|
+
externalId?: string;
|
|
75
|
+
}
|
|
76
|
+
/** Input for a Woku capture (1-tap rating 1..5 + optional comment/audio). */
|
|
77
|
+
interface WokuCaptureInput {
|
|
78
|
+
wokuId: string;
|
|
79
|
+
/** Integer 1..5. */
|
|
80
|
+
rating: number;
|
|
81
|
+
comment?: string;
|
|
82
|
+
audio?: AudioAttachment;
|
|
83
|
+
respondent?: Respondent;
|
|
84
|
+
/** Arbitrary context tags attached to the submission. */
|
|
85
|
+
metadata?: Record<string, unknown>;
|
|
86
|
+
}
|
|
87
|
+
/** Input for an NPS capture (score 0..10 + optional review). */
|
|
88
|
+
interface NpsCaptureInput {
|
|
89
|
+
npsId: string;
|
|
90
|
+
/** Integer 0..10. */
|
|
91
|
+
score: number;
|
|
92
|
+
comment?: string;
|
|
93
|
+
audio?: AudioAttachment;
|
|
94
|
+
respondent?: Respondent;
|
|
95
|
+
metadata?: Record<string, unknown>;
|
|
96
|
+
}
|
|
97
|
+
/** Normalized submission persisted in the offline queue and sent to Woku. */
|
|
98
|
+
interface CaptureSubmission {
|
|
99
|
+
/** Client-generated idempotency id. */
|
|
100
|
+
id: string;
|
|
101
|
+
kind: CaptureKind;
|
|
102
|
+
companyId: string;
|
|
103
|
+
targetId: string;
|
|
104
|
+
rating?: number;
|
|
105
|
+
score?: number;
|
|
106
|
+
comment?: string;
|
|
107
|
+
audio?: AudioAttachment;
|
|
108
|
+
respondent?: Respondent;
|
|
109
|
+
metadata?: Record<string, unknown>;
|
|
110
|
+
/** Unix epoch ms when the capture was created on the device. */
|
|
111
|
+
createdAt: number;
|
|
112
|
+
}
|
|
113
|
+
/** Result of attempting to deliver a submission. */
|
|
114
|
+
interface SubmissionResult {
|
|
115
|
+
id: string;
|
|
116
|
+
status: 'sent' | 'queued' | 'failed' | 'quarantined';
|
|
117
|
+
/** Server id once accepted. */
|
|
118
|
+
remoteId?: string;
|
|
119
|
+
error?: string;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
interface WokuClientConfig {
|
|
123
|
+
/** Base URL of the Woku API, e.g. `https://api.woku.app`. */
|
|
124
|
+
apiUrl: string;
|
|
125
|
+
/** Public SDK key issued per company for capture ingestion. */
|
|
126
|
+
publicKey: string;
|
|
127
|
+
/** Tenant the captures belong to. */
|
|
128
|
+
companyId: string;
|
|
129
|
+
/** HTTP transport. Defaults to the global `fetch`. */
|
|
130
|
+
http?: HttpClient;
|
|
131
|
+
logger?: Logger;
|
|
132
|
+
/** Ingest path appended to `apiUrl`. Defaults to `/sdk/v1/captures`. */
|
|
133
|
+
capturePath?: string;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Thin, typed transport to the Woku capture ingest endpoint. Stateless:
|
|
137
|
+
* it turns a normalized {@link CaptureSubmission} into an authenticated
|
|
138
|
+
* POST and maps the response (incl. quarantine 429) to a result/error.
|
|
139
|
+
*/
|
|
140
|
+
declare class WokuClient {
|
|
141
|
+
private readonly config;
|
|
142
|
+
private readonly http;
|
|
143
|
+
private readonly logger;
|
|
144
|
+
private readonly endpoint;
|
|
145
|
+
constructor(config: WokuClientConfig);
|
|
146
|
+
/**
|
|
147
|
+
* Sends one submission. Resolves with a `sent` result on 2xx, throws
|
|
148
|
+
* {@link WokuQuarantineError} on a quarantine 429, {@link WokuNetworkError}
|
|
149
|
+
* on transport failure, and resolves with a `failed` result on other
|
|
150
|
+
* non-2xx responses (so the caller can decide to re-queue).
|
|
151
|
+
*/
|
|
152
|
+
send(submission: CaptureSubmission): Promise<SubmissionResult>;
|
|
153
|
+
private safeJson;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
interface OfflineQueueOptions {
|
|
157
|
+
storage?: Storage;
|
|
158
|
+
/** Storage key for the persisted queue. */
|
|
159
|
+
storageKey?: string;
|
|
160
|
+
/** Drop a submission after this many failed delivery attempts. */
|
|
161
|
+
maxAttempts?: number;
|
|
162
|
+
}
|
|
163
|
+
interface FlushResult {
|
|
164
|
+
sent: number;
|
|
165
|
+
failed: number;
|
|
166
|
+
remaining: number;
|
|
167
|
+
/** Set when flushing stopped early due to quarantine. */
|
|
168
|
+
quarantined?: boolean;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Durable, FIFO offline buffer for captures. Persists through an
|
|
172
|
+
* injectable {@link Storage} so submissions survive app restarts, and
|
|
173
|
+
* flushes them through a caller-provided `send` function. Pure and
|
|
174
|
+
* deterministic: no timers, no platform APIs.
|
|
175
|
+
*
|
|
176
|
+
* Delivery semantics on flush:
|
|
177
|
+
* - `sent` -> removed from the queue.
|
|
178
|
+
* - quarantine -> flush stops (server is rate-limiting the
|
|
179
|
+
* respondent); items are kept for the next attempt.
|
|
180
|
+
* - network error -> attempt count bumped, item kept, flush continues.
|
|
181
|
+
* - `failed` -> attempt count bumped; dropped once maxAttempts is
|
|
182
|
+
* reached so a permanently-bad item can't wedge it.
|
|
183
|
+
*/
|
|
184
|
+
declare class OfflineQueue {
|
|
185
|
+
private readonly storage;
|
|
186
|
+
private readonly key;
|
|
187
|
+
private readonly maxAttempts;
|
|
188
|
+
private items;
|
|
189
|
+
constructor(opts?: OfflineQueueOptions);
|
|
190
|
+
private load;
|
|
191
|
+
private persist;
|
|
192
|
+
/** Adds a submission to the tail of the queue (dedup by id). */
|
|
193
|
+
enqueue(submission: CaptureSubmission): Promise<void>;
|
|
194
|
+
size(): Promise<number>;
|
|
195
|
+
/** Returns a copy of the pending submissions (oldest first). */
|
|
196
|
+
pending(): Promise<CaptureSubmission[]>;
|
|
197
|
+
/**
|
|
198
|
+
* Attempts to deliver every queued submission through `send`, applying
|
|
199
|
+
* the delivery semantics documented on the class. Persists once at the
|
|
200
|
+
* end and returns a summary.
|
|
201
|
+
*/
|
|
202
|
+
flush(send: (submission: CaptureSubmission) => Promise<SubmissionResult>): Promise<FlushResult>;
|
|
203
|
+
/** Clears the queue (e.g. on logout). */
|
|
204
|
+
clear(): Promise<void>;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
interface WokuSdkConfig extends WokuClientConfig {
|
|
208
|
+
/** Storage adapter for the offline queue (MMKV/AsyncStorage in RN). */
|
|
209
|
+
storage?: Storage;
|
|
210
|
+
/** Drop a queued submission after this many failed attempts. */
|
|
211
|
+
maxQueueAttempts?: number;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Top-level Woku SDK. Wires the typed {@link WokuClient} to a durable
|
|
215
|
+
* {@link OfflineQueue}: a capture is attempted immediately and, if the
|
|
216
|
+
* device is offline or the server is unreachable, transparently queued
|
|
217
|
+
* and retried on the next {@link WokuSdk.flush}.
|
|
218
|
+
*
|
|
219
|
+
* @example
|
|
220
|
+
* const woku = new WokuSdk({
|
|
221
|
+
* apiUrl: 'https://api.woku.app',
|
|
222
|
+
* publicKey: 'pk_live_...',
|
|
223
|
+
* companyId: 'company_123',
|
|
224
|
+
* storage: mmkvStorageAdapter,
|
|
225
|
+
* });
|
|
226
|
+
* await woku.captureNps({ npsId: 'nps_1', score: 9, comment: 'Great!' });
|
|
227
|
+
*/
|
|
228
|
+
declare class WokuSdk {
|
|
229
|
+
private readonly client;
|
|
230
|
+
private readonly queue;
|
|
231
|
+
private readonly logger;
|
|
232
|
+
private readonly companyId;
|
|
233
|
+
constructor(config: WokuSdkConfig);
|
|
234
|
+
/** Captures a Woku rating (1..5) with optional text/audio comment. */
|
|
235
|
+
captureWoku(input: WokuCaptureInput): Promise<SubmissionResult>;
|
|
236
|
+
/** Captures an NPS score (0..10) with optional text/audio review. */
|
|
237
|
+
captureNps(input: NpsCaptureInput): Promise<SubmissionResult>;
|
|
238
|
+
/** Retries every queued submission. Safe to call on app foreground or
|
|
239
|
+
* when connectivity is regained. */
|
|
240
|
+
flush(): Promise<FlushResult>;
|
|
241
|
+
/** Number of submissions waiting to be delivered. */
|
|
242
|
+
pendingCount(): Promise<number>;
|
|
243
|
+
/** Clears the offline queue (e.g. on user logout). */
|
|
244
|
+
clearQueue(): Promise<void>;
|
|
245
|
+
/**
|
|
246
|
+
* Tries to send immediately; on quarantine or network failure the
|
|
247
|
+
* submission is queued for a later flush rather than thrown away.
|
|
248
|
+
*/
|
|
249
|
+
private deliver;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/** Base error for all SDK failures. */
|
|
253
|
+
declare class WokuError extends Error {
|
|
254
|
+
readonly code: string;
|
|
255
|
+
constructor(message: string, code: string);
|
|
256
|
+
}
|
|
257
|
+
/** Thrown when the SDK is misconfigured (missing companyId, etc.). */
|
|
258
|
+
declare class WokuConfigError extends WokuError {
|
|
259
|
+
constructor(message: string);
|
|
260
|
+
}
|
|
261
|
+
/** Validation failure on a capture input (bad rating/score, etc.). */
|
|
262
|
+
declare class WokuValidationError extends WokuError {
|
|
263
|
+
constructor(message: string);
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* The backend rejected the submission because the respondent is in
|
|
267
|
+
* quarantine (HTTP 429 with a QUARANTINE_BLOCKED code). Carries the
|
|
268
|
+
* retry-after hint in seconds when the server provides one.
|
|
269
|
+
*/
|
|
270
|
+
declare class WokuQuarantineError extends WokuError {
|
|
271
|
+
readonly retryAfterSeconds?: number | undefined;
|
|
272
|
+
constructor(message: string, retryAfterSeconds?: number | undefined);
|
|
273
|
+
}
|
|
274
|
+
/** Network/transport failure; the submission should be retried later. */
|
|
275
|
+
declare class WokuNetworkError extends WokuError {
|
|
276
|
+
constructor(message: string);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
export { type AudioAttachment, type CaptureKind, type CaptureSubmission, type FlushResult, type HttpClient, type HttpRequest, type HttpResponse, InMemoryStorage, type Logger, type NpsCaptureInput, OfflineQueue, type OfflineQueueOptions, type Respondent, type Storage, type SubmissionResult, type WokuCaptureInput, WokuClient, type WokuClientConfig, WokuConfigError, WokuError, WokuNetworkError, WokuQuarantineError, WokuSdk, type WokuSdkConfig, WokuValidationError, fetchHttpClient, noopLogger };
|