@skyporch/daykeeper-react-native 0.1.0 → 0.2.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.
@@ -0,0 +1,500 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __typeError = (msg) => {
3
+ throw TypeError(msg);
4
+ };
5
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
6
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
7
+ var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
8
+ var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
9
+ var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
10
+ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
11
+ var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
12
+
13
+ // src/errors.ts
14
+ var API_CODE_SHAPE = /^[a-z][a-z0-9_]{2,63}$/;
15
+ var DAYKEEPER_GENERIC_API_CODE = "daykeeper_request_failed";
16
+ function isDaykeeperApiErrorCode(value) {
17
+ return typeof value === "string" && API_CODE_SHAPE.test(value);
18
+ }
19
+ var SAFE_NEXT_ACTIONS = /* @__PURE__ */ new Set([
20
+ "review_usage",
21
+ "review_setup",
22
+ "refresh_conversation"
23
+ ]);
24
+ var DaykeeperReactNativeApiError = class extends Error {
25
+ constructor(options) {
26
+ const code = isDaykeeperApiErrorCode(options.code) ? options.code : DAYKEEPER_GENERIC_API_CODE;
27
+ super(code);
28
+ __publicField(this, "status");
29
+ __publicField(this, "code");
30
+ __publicField(this, "retryable");
31
+ __publicField(this, "outcomeUnknown");
32
+ __publicField(this, "nextAction");
33
+ this.name = "DaykeeperReactNativeApiError";
34
+ this.status = options.status;
35
+ this.code = code;
36
+ this.outcomeUnknown = options.outcomeUnknown ?? false;
37
+ this.retryable = !this.outcomeUnknown && (options.retryable ?? false);
38
+ if (typeof options.nextAction === "string" && SAFE_NEXT_ACTIONS.has(options.nextAction)) {
39
+ this.nextAction = options.nextAction;
40
+ }
41
+ }
42
+ toJSON() {
43
+ return {
44
+ name: this.name,
45
+ status: this.status,
46
+ code: this.code,
47
+ retryable: this.retryable,
48
+ outcomeUnknown: this.outcomeUnknown,
49
+ ...this.nextAction === void 0 ? {} : { nextAction: this.nextAction }
50
+ };
51
+ }
52
+ };
53
+ var DaykeeperReactNativeTransportError = class extends Error {
54
+ constructor(options) {
55
+ super(options.message);
56
+ __publicField(this, "code");
57
+ __publicField(this, "retryable");
58
+ __publicField(this, "outcomeUnknown");
59
+ this.name = "DaykeeperReactNativeTransportError";
60
+ this.code = options.code;
61
+ this.outcomeUnknown = options.outcomeUnknown ?? false;
62
+ this.retryable = !this.outcomeUnknown && (options.retryable ?? false);
63
+ }
64
+ toJSON() {
65
+ return {
66
+ name: this.name,
67
+ code: this.code,
68
+ message: this.message,
69
+ retryable: this.retryable,
70
+ outcomeUnknown: this.outcomeUnknown
71
+ };
72
+ }
73
+ };
74
+
75
+ // src/requestLifetime.ts
76
+ function createRequestLifetime(timeoutMs, caller) {
77
+ const controller = new AbortController();
78
+ const now = () => typeof performance === "undefined" ? Date.now() : performance.now();
79
+ const deadline = now() + timeoutMs;
80
+ let failure;
81
+ const abort = (code) => {
82
+ if (failure) return;
83
+ failure = new DaykeeperReactNativeTransportError({
84
+ code,
85
+ message: code === "REQUEST_TIMEOUT" ? `The Daykeeper request exceeded ${timeoutMs}ms` : "The Daykeeper request was aborted",
86
+ retryable: code === "REQUEST_TIMEOUT"
87
+ });
88
+ controller.abort();
89
+ };
90
+ const onCallerAbort = () => abort("REQUEST_ABORTED");
91
+ if (caller?.aborted) onCallerAbort();
92
+ else caller?.addEventListener("abort", onCallerAbort, { once: true });
93
+ const timeout = failure ? void 0 : setTimeout(() => abort("REQUEST_TIMEOUT"), timeoutMs);
94
+ const checkDeadline = () => {
95
+ if (!failure && now() >= deadline) abort("REQUEST_TIMEOUT");
96
+ };
97
+ return {
98
+ signal: controller.signal,
99
+ run(work, onLateValue) {
100
+ checkDeadline();
101
+ if (failure) return Promise.reject(failure);
102
+ return new Promise((resolve, reject) => {
103
+ let settled = false;
104
+ const finish = () => {
105
+ settled = true;
106
+ controller.signal.removeEventListener("abort", onAbort);
107
+ };
108
+ const onAbort = () => {
109
+ finish();
110
+ reject(failure);
111
+ };
112
+ controller.signal.addEventListener("abort", onAbort, { once: true });
113
+ Promise.resolve().then(() => {
114
+ checkDeadline();
115
+ if (failure) throw failure;
116
+ return work();
117
+ }).then(
118
+ (value) => {
119
+ checkDeadline();
120
+ if (settled) {
121
+ try {
122
+ onLateValue?.(value);
123
+ } catch {
124
+ }
125
+ return;
126
+ }
127
+ finish();
128
+ resolve(value);
129
+ },
130
+ (error) => {
131
+ if (settled) return;
132
+ finish();
133
+ reject(error);
134
+ }
135
+ );
136
+ });
137
+ },
138
+ dispose() {
139
+ clearTimeout(timeout);
140
+ caller?.removeEventListener("abort", onCallerAbort);
141
+ }
142
+ };
143
+ }
144
+ function discardResponse(response) {
145
+ try {
146
+ void response.body?.cancel().catch(() => void 0);
147
+ } catch {
148
+ }
149
+ }
150
+ function discardReader(reader) {
151
+ try {
152
+ void reader.cancel().catch(() => void 0);
153
+ } catch {
154
+ }
155
+ try {
156
+ reader.releaseLock();
157
+ } catch {
158
+ }
159
+ }
160
+
161
+ // src/client.ts
162
+ var DEFAULT_TIMEOUT_MS = 3e4;
163
+ var MAX_RESPONSE_BYTES = 1024 * 1024;
164
+ var MAX_TOKEN_LENGTH = 16384;
165
+ var MAX_MESSAGE_LENGTH = 16e3;
166
+ var _baseUrl, _fetch, _getAccessToken, _timeoutMs, _DaykeeperReactNativeClient_instances, request_fn;
167
+ var DaykeeperReactNativeClient = class {
168
+ constructor(options) {
169
+ __privateAdd(this, _DaykeeperReactNativeClient_instances);
170
+ __privateAdd(this, _baseUrl);
171
+ __privateAdd(this, _fetch);
172
+ __privateAdd(this, _getAccessToken);
173
+ __privateAdd(this, _timeoutMs);
174
+ __privateSet(this, _baseUrl, parseBaseUrl(options.baseUrl));
175
+ __privateSet(this, _fetch, options.fetch ?? globalThis.fetch);
176
+ if (typeof __privateGet(this, _fetch) !== "function") {
177
+ throw configurationError("A Fetch API implementation is required");
178
+ }
179
+ if (typeof options.getAccessToken !== "function") {
180
+ throw configurationError("getAccessToken must be a function");
181
+ }
182
+ __privateSet(this, _getAccessToken, options.getAccessToken);
183
+ __privateSet(this, _timeoutMs, validateTimeout(options.timeoutMs ?? DEFAULT_TIMEOUT_MS));
184
+ }
185
+ getIdentity(options) {
186
+ return __privateMethod(this, _DaykeeperReactNativeClient_instances, request_fn).call(this, "/v1/identity", { signal: options?.signal });
187
+ }
188
+ listConversations(options) {
189
+ return __privateMethod(this, _DaykeeperReactNativeClient_instances, request_fn).call(this, "/v1/conversations", { signal: options?.signal });
190
+ }
191
+ createConversation(options) {
192
+ return __privateMethod(this, _DaykeeperReactNativeClient_instances, request_fn).call(this, "/v1/conversations", {
193
+ method: "POST",
194
+ signal: options?.signal
195
+ });
196
+ }
197
+ getUnread(options) {
198
+ return __privateMethod(this, _DaykeeperReactNativeClient_instances, request_fn).call(this, "/v1/unread", { signal: options?.signal });
199
+ }
200
+ markConversationSeen(conversationId, options) {
201
+ return __privateMethod(this, _DaykeeperReactNativeClient_instances, request_fn).call(this, `/v1/conversations/${positiveInteger(conversationId, "conversationId")}/seen`, { method: "POST", signal: options?.signal });
202
+ }
203
+ listMessages(conversationId, options = {}) {
204
+ const id = positiveInteger(conversationId, "conversationId");
205
+ const after = options.after === void 0 ? "" : `?after=${positiveInteger(options.after, "after")}`;
206
+ return __privateMethod(this, _DaykeeperReactNativeClient_instances, request_fn).call(this, `/v1/conversations/${id}/messages${after}`, {
207
+ signal: options.signal
208
+ });
209
+ }
210
+ sendMessage(conversationId, content, options) {
211
+ const normalizedContent = validateMessage(content);
212
+ return __privateMethod(this, _DaykeeperReactNativeClient_instances, request_fn).call(this, `/v1/conversations/${positiveInteger(conversationId, "conversationId")}/messages`, {
213
+ method: "POST",
214
+ body: { content: normalizedContent },
215
+ signal: options?.signal
216
+ });
217
+ }
218
+ claimAnonymousConversation(widgetToken, options) {
219
+ if (typeof widgetToken !== "string" || !widgetToken.trim() || widgetToken.length > MAX_TOKEN_LENGTH) {
220
+ throw configurationError("widgetToken is invalid");
221
+ }
222
+ return __privateMethod(this, _DaykeeperReactNativeClient_instances, request_fn).call(this, "/v1/anonymous-conversations/claim", {
223
+ method: "POST",
224
+ body: { widgetToken: widgetToken.trim() },
225
+ signal: options?.signal
226
+ });
227
+ }
228
+ };
229
+ _baseUrl = new WeakMap();
230
+ _fetch = new WeakMap();
231
+ _getAccessToken = new WeakMap();
232
+ _timeoutMs = new WeakMap();
233
+ _DaykeeperReactNativeClient_instances = new WeakSet();
234
+ request_fn = async function(path, options = {}) {
235
+ const lifetime = createRequestLifetime(__privateGet(this, _timeoutMs), options.signal);
236
+ const method = options.method ?? "GET";
237
+ const mutating = method !== "GET";
238
+ const attempts = mutating ? 1 : 2;
239
+ let dispatched = false;
240
+ let response;
241
+ try {
242
+ for (let attempt = 0; attempt < attempts; attempt += 1) {
243
+ const token = validateToken(
244
+ await lifetime.run(
245
+ () => resolveToken(__privateGet(this, _getAccessToken), {
246
+ forceRefresh: attempt === 1,
247
+ signal: lifetime.signal
248
+ })
249
+ )
250
+ );
251
+ const headers = new Headers({
252
+ accept: "application/json",
253
+ authorization: `Bearer ${token}`
254
+ });
255
+ if (options.body !== void 0) {
256
+ headers.set("content-type", "application/json");
257
+ }
258
+ try {
259
+ response = await lifetime.run(() => {
260
+ dispatched = true;
261
+ return __privateGet(this, _fetch).call(this, `${__privateGet(this, _baseUrl)}${path}`, {
262
+ method,
263
+ body: options.body === void 0 ? void 0 : JSON.stringify(options.body),
264
+ headers,
265
+ signal: lifetime.signal,
266
+ redirect: "error",
267
+ credentials: "omit",
268
+ cache: "no-store"
269
+ });
270
+ }, discardResponse);
271
+ } catch (error) {
272
+ if (error instanceof DaykeeperReactNativeTransportError) throw error;
273
+ throw networkError();
274
+ }
275
+ const refreshCandidate = !mutating && response.status === 401 && attempt === 0;
276
+ let payload;
277
+ if (refreshCandidate || !response.ok) {
278
+ try {
279
+ payload = await readJson(response, lifetime);
280
+ } catch (error) {
281
+ if (!(error instanceof DaykeeperReactNativeTransportError) || error.code !== "INVALID_RESPONSE")
282
+ throw error;
283
+ }
284
+ } else {
285
+ payload = await readJson(response, lifetime);
286
+ }
287
+ if (refreshCandidate && !(isRecord(payload) && payload.retryable === false)) {
288
+ discardResponse(response);
289
+ continue;
290
+ }
291
+ if (!response.ok) {
292
+ throw new DaykeeperReactNativeApiError({
293
+ status: response.status,
294
+ code: isRecord(payload) ? payload.error : void 0,
295
+ // The contract's message stays unread; only the bounded
296
+ // nextAction vocabulary is projected.
297
+ nextAction: isRecord(payload) ? payload.nextAction : void 0,
298
+ retryable: !mutating && (isRecord(payload) && typeof payload.retryable === "boolean" ? payload.retryable : response.status === 408 || response.status === 429 || response.status >= 500),
299
+ outcomeUnknown: mutating && (response.status === 408 || response.status >= 500)
300
+ });
301
+ }
302
+ if (!isRecord(payload)) {
303
+ throw new DaykeeperReactNativeTransportError({
304
+ code: "INVALID_RESPONSE",
305
+ message: "The Daykeeper customer API returned an invalid response",
306
+ retryable: true
307
+ });
308
+ }
309
+ return payload;
310
+ }
311
+ throw new Error("Unreachable Daykeeper request state");
312
+ } catch (error) {
313
+ if (error instanceof DaykeeperReactNativeApiError) throw error;
314
+ const safeError = error instanceof DaykeeperReactNativeTransportError ? error : networkError();
315
+ if (!mutating) throw safeError;
316
+ throw new DaykeeperReactNativeTransportError({
317
+ code: safeError.code,
318
+ message: safeError.message,
319
+ retryable: false,
320
+ outcomeUnknown: dispatched
321
+ });
322
+ } finally {
323
+ if (response) discardResponse(response);
324
+ lifetime.dispose();
325
+ }
326
+ };
327
+ function createDaykeeperReactNativeClient(options) {
328
+ return new DaykeeperReactNativeClient(options);
329
+ }
330
+ async function resolveToken(provider, context) {
331
+ try {
332
+ return await provider(context);
333
+ } catch {
334
+ throw new DaykeeperReactNativeTransportError({
335
+ code: "TOKEN_PROVIDER_ERROR",
336
+ message: "The Daykeeper access token could not be obtained"
337
+ });
338
+ }
339
+ }
340
+ function networkError() {
341
+ return new DaykeeperReactNativeTransportError({
342
+ code: "NETWORK_ERROR",
343
+ message: "The Daykeeper customer API transport failed",
344
+ retryable: true
345
+ });
346
+ }
347
+ function parseBaseUrl(value) {
348
+ let url;
349
+ try {
350
+ url = new URL(value);
351
+ } catch {
352
+ throw configurationError("baseUrl must be a valid absolute URL");
353
+ }
354
+ const hostname = url.hostname.replace(/^\[(.+)]$/, "$1").toLowerCase();
355
+ const local = ["localhost", "127.0.0.1", "::1"].includes(hostname);
356
+ if (url.protocol !== "https:" && !(local && url.protocol === "http:")) {
357
+ throw configurationError(
358
+ "baseUrl must use HTTPS except for loopback development"
359
+ );
360
+ }
361
+ if (url.username || url.password || url.search || url.hash) {
362
+ throw configurationError(
363
+ "baseUrl cannot include credentials, a query, or a fragment"
364
+ );
365
+ }
366
+ return url.toString().replace(/\/$/, "");
367
+ }
368
+ function validateTimeout(value) {
369
+ if (!Number.isInteger(value) || value < 1e3 || value > 6e4) {
370
+ throw configurationError(
371
+ "timeoutMs must be an integer from 1000 through 60000"
372
+ );
373
+ }
374
+ return value;
375
+ }
376
+ function validateToken(value) {
377
+ if (typeof value !== "string" || !value || value.length > MAX_TOKEN_LENGTH || /[\r\n]/.test(value)) {
378
+ throw configurationError("The customer access token is invalid");
379
+ }
380
+ return value;
381
+ }
382
+ function positiveInteger(value, name) {
383
+ if (!Number.isInteger(value) || value < 1) {
384
+ throw configurationError(`${name} must be a positive integer`);
385
+ }
386
+ return value;
387
+ }
388
+ function validateMessage(value) {
389
+ if (typeof value !== "string") {
390
+ throw configurationError("Message content must be a string");
391
+ }
392
+ const normalized = value.trim();
393
+ if (!normalized || normalized.length > MAX_MESSAGE_LENGTH) {
394
+ throw configurationError(
395
+ `Message content must be 1 to ${MAX_MESSAGE_LENGTH} characters`
396
+ );
397
+ }
398
+ return normalized;
399
+ }
400
+ async function readJson(response, lifetime) {
401
+ const declaredLength = Number(response.headers.get("content-length"));
402
+ if (Number.isFinite(declaredLength) && declaredLength > MAX_RESPONSE_BYTES) {
403
+ throw responseTooLarge();
404
+ }
405
+ const body = response.body;
406
+ if (body && typeof body.getReader === "function" && typeof TextDecoder === "function") {
407
+ return parseJson(await readStream(body, lifetime));
408
+ }
409
+ let text;
410
+ try {
411
+ text = await lifetime.run(() => response.text());
412
+ } catch (error) {
413
+ if (error instanceof DaykeeperReactNativeTransportError) throw error;
414
+ throw networkError();
415
+ }
416
+ if (utf8LengthExceeds(text, MAX_RESPONSE_BYTES)) {
417
+ throw responseTooLarge();
418
+ }
419
+ return parseJson(text);
420
+ }
421
+ async function readStream(body, lifetime) {
422
+ const reader = body.getReader();
423
+ const chunks = [];
424
+ let total = 0;
425
+ try {
426
+ for (; ; ) {
427
+ const { done, value } = await lifetime.run(() => reader.read());
428
+ if (done) break;
429
+ total += value.byteLength;
430
+ if (total > MAX_RESPONSE_BYTES) {
431
+ throw responseTooLarge();
432
+ }
433
+ chunks.push(value);
434
+ }
435
+ } catch (error) {
436
+ if (error instanceof DaykeeperReactNativeTransportError) throw error;
437
+ throw networkError();
438
+ } finally {
439
+ discardReader(reader);
440
+ }
441
+ const bytes = new Uint8Array(total);
442
+ let offset = 0;
443
+ for (const chunk of chunks) {
444
+ bytes.set(chunk, offset);
445
+ offset += chunk.byteLength;
446
+ }
447
+ return new TextDecoder().decode(bytes);
448
+ }
449
+ function parseJson(text) {
450
+ if (!text) return void 0;
451
+ try {
452
+ return JSON.parse(text);
453
+ } catch {
454
+ throw invalidResponse();
455
+ }
456
+ }
457
+ function utf8LengthExceeds(value, maximum) {
458
+ let bytes = 0;
459
+ for (let index = 0; index < value.length; index += 1) {
460
+ const codeUnit = value.charCodeAt(index);
461
+ if (codeUnit < 128) bytes += 1;
462
+ else if (codeUnit < 2048) bytes += 2;
463
+ else if (codeUnit >= 55296 && codeUnit <= 56319 && index + 1 < value.length && value.charCodeAt(index + 1) >= 56320 && value.charCodeAt(index + 1) <= 57343) {
464
+ bytes += 4;
465
+ index += 1;
466
+ } else bytes += 3;
467
+ if (bytes > maximum) return true;
468
+ }
469
+ return false;
470
+ }
471
+ function isRecord(value) {
472
+ return typeof value === "object" && value !== null && !Array.isArray(value);
473
+ }
474
+ function responseTooLarge() {
475
+ return new DaykeeperReactNativeTransportError({
476
+ code: "RESPONSE_TOO_LARGE",
477
+ message: "The Daykeeper response exceeded 1 MiB"
478
+ });
479
+ }
480
+ function invalidResponse() {
481
+ return new DaykeeperReactNativeTransportError({
482
+ code: "INVALID_RESPONSE",
483
+ message: "The Daykeeper customer API returned invalid JSON",
484
+ retryable: true
485
+ });
486
+ }
487
+ function configurationError(message) {
488
+ return new DaykeeperReactNativeTransportError({
489
+ code: "INVALID_CONFIGURATION",
490
+ message
491
+ });
492
+ }
493
+
494
+ export {
495
+ DaykeeperReactNativeApiError,
496
+ DaykeeperReactNativeTransportError,
497
+ DaykeeperReactNativeClient,
498
+ createDaykeeperReactNativeClient
499
+ };
500
+ //# sourceMappingURL=chunk-BAQHY3R2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/errors.ts","../src/requestLifetime.ts","../src/client.ts"],"sourcesContent":["export type DaykeeperReactNativeTransportErrorCode =\n | \"INVALID_CONFIGURATION\"\n | \"TOKEN_PROVIDER_ERROR\"\n | \"NETWORK_ERROR\"\n | \"REQUEST_ABORTED\"\n | \"REQUEST_TIMEOUT\"\n | \"INVALID_RESPONSE\"\n | \"RESPONSE_TOO_LARGE\";\n\n// Error bodies are untrusted, so a code is only allowed through when its\n// *shape* proves it is a code and not prose, a token, or an identifier: ASCII\n// lowercase snake_case, 3 to 64 characters. The gateway's error vocabulary is\n// open and grows without an SDK release, so an allowlist would silently\n// collapse codes that consuming apps switch on. Anything failing the shape --\n// free-form English messages, mixed case, punctuation, whitespace, non-string\n// values -- is replaced with the generic code below. Message projection is\n// unchanged: the error message is the code itself, never raw body text.\nconst API_CODE_SHAPE = /^[a-z][a-z0-9_]{2,63}$/;\n\nexport const DAYKEEPER_GENERIC_API_CODE = \"daykeeper_request_failed\";\n\n/** True when `value` has the documented customer API error-code shape. */\nexport function isDaykeeperApiErrorCode(value: unknown): value is string {\n return typeof value === \"string\" && API_CODE_SHAPE.test(value);\n}\n\n/**\n * Contract-documented next steps. The envelope is open and its values are\n * extensible, so an unrecognized hint is dropped rather than surfaced: a\n * gateway string is untrusted and none of these grant account authority.\n *\n * This is deliberately narrower than the error-code rule above. A code is only\n * ever compared by the app, so an unknown one is inert; a next action is a UI\n * instruction the app acts on, so only the three the contract defines are\n * allowed through.\n */\nexport type DaykeeperReactNativeNextAction =\n \"review_usage\" | \"review_setup\" | \"refresh_conversation\";\n\nconst SAFE_NEXT_ACTIONS = new Set<string>([\n \"review_usage\",\n \"review_setup\",\n \"refresh_conversation\",\n]);\n\nexport class DaykeeperReactNativeApiError extends Error {\n readonly status: number;\n readonly code: string;\n readonly retryable: boolean;\n readonly outcomeUnknown: boolean;\n readonly nextAction?: DaykeeperReactNativeNextAction;\n\n constructor(options: {\n status: number;\n code?: unknown;\n nextAction?: unknown;\n retryable?: boolean;\n outcomeUnknown?: boolean;\n }) {\n const code = isDaykeeperApiErrorCode(options.code)\n ? options.code\n : DAYKEEPER_GENERIC_API_CODE;\n super(code);\n this.name = \"DaykeeperReactNativeApiError\";\n this.status = options.status;\n this.code = code;\n this.outcomeUnknown = options.outcomeUnknown ?? false;\n this.retryable = !this.outcomeUnknown && (options.retryable ?? false);\n if (\n typeof options.nextAction === \"string\" &&\n SAFE_NEXT_ACTIONS.has(options.nextAction)\n ) {\n this.nextAction = options.nextAction as DaykeeperReactNativeNextAction;\n }\n }\n\n toJSON(): {\n name: string;\n status: number;\n code: string;\n retryable: boolean;\n outcomeUnknown: boolean;\n nextAction?: DaykeeperReactNativeNextAction;\n } {\n return {\n name: this.name,\n status: this.status,\n code: this.code,\n retryable: this.retryable,\n outcomeUnknown: this.outcomeUnknown,\n ...(this.nextAction === undefined ? {} : { nextAction: this.nextAction }),\n };\n }\n}\n\nexport class DaykeeperReactNativeTransportError extends Error {\n readonly code: DaykeeperReactNativeTransportErrorCode;\n readonly retryable: boolean;\n readonly outcomeUnknown: boolean;\n\n constructor(options: {\n code: DaykeeperReactNativeTransportErrorCode;\n message: string;\n retryable?: boolean;\n outcomeUnknown?: boolean;\n }) {\n super(options.message);\n this.name = \"DaykeeperReactNativeTransportError\";\n this.code = options.code;\n this.outcomeUnknown = options.outcomeUnknown ?? false;\n this.retryable = !this.outcomeUnknown && (options.retryable ?? false);\n }\n\n toJSON(): {\n name: string;\n code: DaykeeperReactNativeTransportErrorCode;\n message: string;\n retryable: boolean;\n outcomeUnknown: boolean;\n } {\n return {\n name: this.name,\n code: this.code,\n message: this.message,\n retryable: this.retryable,\n outcomeUnknown: this.outcomeUnknown,\n };\n }\n}\n","import { DaykeeperReactNativeTransportError } from \"./errors.js\";\n\n// One budget covers credentials, both authentication attempts, and the body.\n// Aborting fetch alone cannot bound a provider or custom transport that stalls.\nexport function createRequestLifetime(timeoutMs: number, caller?: AbortSignal) {\n const controller = new AbortController();\n const now = () =>\n typeof performance === \"undefined\" ? Date.now() : performance.now();\n const deadline = now() + timeoutMs;\n let failure: DaykeeperReactNativeTransportError | undefined;\n const abort = (code: \"REQUEST_ABORTED\" | \"REQUEST_TIMEOUT\") => {\n if (failure) return;\n failure = new DaykeeperReactNativeTransportError({\n code,\n message:\n code === \"REQUEST_TIMEOUT\"\n ? `The Daykeeper request exceeded ${timeoutMs}ms`\n : \"The Daykeeper request was aborted\",\n retryable: code === \"REQUEST_TIMEOUT\",\n });\n controller.abort();\n };\n const onCallerAbort = () => abort(\"REQUEST_ABORTED\");\n if (caller?.aborted) onCallerAbort();\n else caller?.addEventListener(\"abort\", onCallerAbort, { once: true });\n const timeout = failure\n ? undefined\n : setTimeout(() => abort(\"REQUEST_TIMEOUT\"), timeoutMs);\n const checkDeadline = () => {\n if (!failure && now() >= deadline) abort(\"REQUEST_TIMEOUT\");\n };\n\n return {\n signal: controller.signal,\n run<T>(\n work: () => T | PromiseLike<T>,\n onLateValue?: (value: T) => void,\n ): Promise<T> {\n checkDeadline();\n if (failure) return Promise.reject(failure);\n return new Promise<T>((resolve, reject) => {\n let settled = false;\n const finish = () => {\n settled = true;\n controller.signal.removeEventListener(\"abort\", onAbort);\n };\n const onAbort = () => {\n finish();\n reject(failure);\n };\n controller.signal.addEventListener(\"abort\", onAbort, { once: true });\n Promise.resolve()\n .then(() => {\n // Do not start work queued before an abort, or send with a late token.\n checkDeadline();\n if (failure) throw failure;\n return work();\n })\n .then(\n (value) => {\n checkDeadline();\n if (settled) {\n try {\n onLateValue?.(value);\n } catch {\n // Late cleanup must not cause an unhandled rejection.\n }\n return;\n }\n finish();\n resolve(value);\n },\n (error: unknown) => {\n if (settled) return;\n finish();\n reject(error);\n },\n );\n });\n },\n dispose() {\n clearTimeout(timeout);\n caller?.removeEventListener(\"abort\", onCallerAbort);\n },\n };\n}\n\n// Cleanup must not delay timeout/error delivery, including with custom streams.\nexport function discardResponse(response: Response): void {\n try {\n void response.body?.cancel().catch(() => undefined);\n } catch {\n // A consumed/locked or non-streaming body may not support cancellation.\n }\n}\n\nexport function discardReader(\n reader: ReadableStreamDefaultReader<Uint8Array>,\n): void {\n try {\n void reader.cancel().catch(() => undefined);\n } catch {\n // A failed transport may already have released the reader.\n }\n try {\n reader.releaseLock();\n } catch {\n // Some stream polyfills cannot release a pending read. Preserve the\n // original timeout/abort/error even when their cleanup is unsupported.\n }\n}\n","import {\n DaykeeperReactNativeApiError,\n DaykeeperReactNativeTransportError,\n} from \"./errors.js\";\nimport {\n createRequestLifetime,\n discardResponse,\n discardReader,\n} from \"./requestLifetime.js\";\nimport type {\n DaykeeperClaimConversationResult,\n DaykeeperConversationList,\n DaykeeperConversationResult,\n DaykeeperCustomerIdentity,\n DaykeeperMessageList,\n DaykeeperMessageResult,\n DaykeeperSeenResult,\n DaykeeperUnreadSummary,\n} from \"./types.js\";\n\nconst DEFAULT_TIMEOUT_MS = 30_000;\nconst MAX_RESPONSE_BYTES = 1024 * 1024;\nconst MAX_TOKEN_LENGTH = 16_384;\nconst MAX_MESSAGE_LENGTH = 16_000;\n\nexport interface DaykeeperReactNativeTokenProviderContext {\n /**\n * True only after a GET rejected the first token with HTTP 401 and did not\n * explicitly forbid replay with retryable: false. The\n * provider should bypass any token cache and exchange the app session again.\n */\n forceRefresh: boolean;\n /** Cancel credential exchange when the request is aborted or expires. */\n signal?: AbortSignal;\n}\n\nexport type DaykeeperReactNativeTokenProvider = (\n context: DaykeeperReactNativeTokenProviderContext,\n) => string | Promise<string>;\n\nexport interface DaykeeperReactNativeClientOptions {\n baseUrl: string;\n getAccessToken: DaykeeperReactNativeTokenProvider;\n /** Must reject redirects, omit ambient cookies, and honor cache policy. */\n fetch?: typeof globalThis.fetch;\n timeoutMs?: number;\n}\n\nexport interface DaykeeperReactNativeRequestOptions {\n signal?: AbortSignal;\n}\n\nexport class DaykeeperReactNativeClient {\n readonly #baseUrl: string;\n readonly #fetch: typeof globalThis.fetch;\n readonly #getAccessToken: DaykeeperReactNativeTokenProvider;\n readonly #timeoutMs: number;\n\n constructor(options: DaykeeperReactNativeClientOptions) {\n this.#baseUrl = parseBaseUrl(options.baseUrl);\n this.#fetch = options.fetch ?? globalThis.fetch;\n if (typeof this.#fetch !== \"function\") {\n throw configurationError(\"A Fetch API implementation is required\");\n }\n if (typeof options.getAccessToken !== \"function\") {\n throw configurationError(\"getAccessToken must be a function\");\n }\n this.#getAccessToken = options.getAccessToken;\n this.#timeoutMs = validateTimeout(options.timeoutMs ?? DEFAULT_TIMEOUT_MS);\n }\n\n getIdentity(\n options?: DaykeeperReactNativeRequestOptions,\n ): Promise<DaykeeperCustomerIdentity> {\n return this.#request(\"/v1/identity\", { signal: options?.signal });\n }\n\n listConversations(\n options?: DaykeeperReactNativeRequestOptions,\n ): Promise<DaykeeperConversationList> {\n return this.#request(\"/v1/conversations\", { signal: options?.signal });\n }\n\n createConversation(\n options?: DaykeeperReactNativeRequestOptions,\n ): Promise<DaykeeperConversationResult> {\n return this.#request(\"/v1/conversations\", {\n method: \"POST\",\n signal: options?.signal,\n });\n }\n\n getUnread(\n options?: DaykeeperReactNativeRequestOptions,\n ): Promise<DaykeeperUnreadSummary> {\n return this.#request(\"/v1/unread\", { signal: options?.signal });\n }\n\n markConversationSeen(\n conversationId: number,\n options?: DaykeeperReactNativeRequestOptions,\n ): Promise<DaykeeperSeenResult> {\n return this.#request(\n `/v1/conversations/${positiveInteger(conversationId, \"conversationId\")}/seen`,\n { method: \"POST\", signal: options?.signal },\n );\n }\n\n listMessages(\n conversationId: number,\n options: DaykeeperReactNativeRequestOptions & { after?: number } = {},\n ): Promise<DaykeeperMessageList> {\n const id = positiveInteger(conversationId, \"conversationId\");\n const after =\n options.after === undefined\n ? \"\"\n : `?after=${positiveInteger(options.after, \"after\")}`;\n return this.#request(`/v1/conversations/${id}/messages${after}`, {\n signal: options.signal,\n });\n }\n\n sendMessage(\n conversationId: number,\n content: string,\n options?: DaykeeperReactNativeRequestOptions,\n ): Promise<DaykeeperMessageResult> {\n const normalizedContent = validateMessage(content);\n return this.#request(\n `/v1/conversations/${positiveInteger(conversationId, \"conversationId\")}/messages`,\n {\n method: \"POST\",\n body: { content: normalizedContent },\n signal: options?.signal,\n },\n );\n }\n\n claimAnonymousConversation(\n widgetToken: string,\n options?: DaykeeperReactNativeRequestOptions,\n ): Promise<DaykeeperClaimConversationResult> {\n if (\n typeof widgetToken !== \"string\" ||\n !widgetToken.trim() ||\n widgetToken.length > MAX_TOKEN_LENGTH\n ) {\n throw configurationError(\"widgetToken is invalid\");\n }\n return this.#request(\"/v1/anonymous-conversations/claim\", {\n method: \"POST\",\n body: { widgetToken: widgetToken.trim() },\n signal: options?.signal,\n });\n }\n\n async #request<ResponseBody>(\n path: string,\n options: {\n method?: \"GET\" | \"POST\";\n body?: unknown;\n signal?: AbortSignal;\n } = {},\n ): Promise<ResponseBody> {\n const lifetime = createRequestLifetime(this.#timeoutMs, options.signal);\n const method = options.method ?? \"GET\";\n const mutating = method !== \"GET\";\n const attempts = mutating ? 1 : 2;\n let dispatched = false;\n let response: Response | undefined;\n\n try {\n for (let attempt = 0; attempt < attempts; attempt += 1) {\n const token = validateToken(\n await lifetime.run(() =>\n resolveToken(this.#getAccessToken, {\n forceRefresh: attempt === 1,\n signal: lifetime.signal,\n }),\n ),\n );\n const headers = new Headers({\n accept: \"application/json\",\n authorization: `Bearer ${token}`,\n });\n if (options.body !== undefined) {\n headers.set(\"content-type\", \"application/json\");\n }\n\n try {\n response = await lifetime.run(() => {\n dispatched = true;\n return this.#fetch(`${this.#baseUrl}${path}`, {\n method,\n body:\n options.body === undefined\n ? undefined\n : JSON.stringify(options.body),\n headers,\n signal: lifetime.signal,\n redirect: \"error\",\n credentials: \"omit\",\n cache: \"no-store\",\n });\n }, discardResponse);\n } catch (error) {\n if (error instanceof DaykeeperReactNativeTransportError) throw error;\n throw networkError();\n }\n\n // A read that may still refresh its credentials once, on the first\n // 401 only, unless the server explicitly forbids the replay.\n const refreshCandidate =\n !mutating && response.status === 401 && attempt === 0;\n\n let payload: unknown;\n if (refreshCandidate || !response.ok) {\n // The HTTP status decides the outcome, so the body is read for\n // advice only. A gateway, proxy or CDN can answer any error status\n // with an HTML page instead of the contract envelope; parsing that\n // first used to throw INVALID_RESPONSE and lose both the status and,\n // on a 401, the one permitted credential refresh.\n try {\n payload = await readJson(response, lifetime);\n } catch (error) {\n // Only a completed, unparseable body is tolerated. Failed,\n // stalled, oversized, or cancelled reads never authorize a replay.\n if (\n !(error instanceof DaykeeperReactNativeTransportError) ||\n error.code !== \"INVALID_RESPONSE\"\n )\n throw error;\n }\n } else {\n payload = await readJson(response, lifetime);\n }\n if (\n refreshCandidate &&\n !(isRecord(payload) && payload.retryable === false)\n ) {\n discardResponse(response);\n continue;\n }\n if (!response.ok) {\n throw new DaykeeperReactNativeApiError({\n status: response.status,\n code: isRecord(payload) ? payload.error : undefined,\n // The contract's message stays unread; only the bounded\n // nextAction vocabulary is projected.\n nextAction: isRecord(payload) ? payload.nextAction : undefined,\n retryable:\n !mutating &&\n (isRecord(payload) && typeof payload.retryable === \"boolean\"\n ? payload.retryable\n : response.status === 408 ||\n response.status === 429 ||\n response.status >= 500),\n outcomeUnknown:\n mutating && (response.status === 408 || response.status >= 500),\n });\n }\n if (!isRecord(payload)) {\n throw new DaykeeperReactNativeTransportError({\n code: \"INVALID_RESPONSE\",\n message: \"The Daykeeper customer API returned an invalid response\",\n retryable: true,\n });\n }\n return payload as ResponseBody;\n }\n throw new Error(\"Unreachable Daykeeper request state\");\n } catch (error) {\n if (error instanceof DaykeeperReactNativeApiError) throw error;\n const safeError =\n error instanceof DaykeeperReactNativeTransportError\n ? error\n : networkError();\n if (!mutating) throw safeError;\n throw new DaykeeperReactNativeTransportError({\n code: safeError.code,\n message: safeError.message,\n retryable: false,\n outcomeUnknown: dispatched,\n });\n } finally {\n if (response) discardResponse(response);\n lifetime.dispose();\n }\n }\n}\n\nexport function createDaykeeperReactNativeClient(\n options: DaykeeperReactNativeClientOptions,\n): DaykeeperReactNativeClient {\n return new DaykeeperReactNativeClient(options);\n}\n\nasync function resolveToken(\n provider: DaykeeperReactNativeTokenProvider,\n context: DaykeeperReactNativeTokenProviderContext,\n): Promise<string> {\n try {\n return await provider(context);\n } catch {\n throw new DaykeeperReactNativeTransportError({\n code: \"TOKEN_PROVIDER_ERROR\",\n message: \"The Daykeeper access token could not be obtained\",\n });\n }\n}\n\nfunction networkError(): DaykeeperReactNativeTransportError {\n return new DaykeeperReactNativeTransportError({\n code: \"NETWORK_ERROR\",\n message: \"The Daykeeper customer API transport failed\",\n retryable: true,\n });\n}\n\nfunction parseBaseUrl(value: string): string {\n let url: URL;\n try {\n url = new URL(value);\n } catch {\n throw configurationError(\"baseUrl must be a valid absolute URL\");\n }\n // URL.hostname keeps the brackets around an IPv6 literal, so a bare \"::1\"\n // comparison never matched http://[::1]:3002 and rejected local development.\n const hostname = url.hostname.replace(/^\\[(.+)]$/, \"$1\").toLowerCase();\n const local = [\"localhost\", \"127.0.0.1\", \"::1\"].includes(hostname);\n if (url.protocol !== \"https:\" && !(local && url.protocol === \"http:\")) {\n throw configurationError(\n \"baseUrl must use HTTPS except for loopback development\",\n );\n }\n if (url.username || url.password || url.search || url.hash) {\n throw configurationError(\n \"baseUrl cannot include credentials, a query, or a fragment\",\n );\n }\n return url.toString().replace(/\\/$/, \"\");\n}\n\nfunction validateTimeout(value: number): number {\n if (!Number.isInteger(value) || value < 1_000 || value > 60_000) {\n throw configurationError(\n \"timeoutMs must be an integer from 1000 through 60000\",\n );\n }\n return value;\n}\n\nfunction validateToken(value: string): string {\n if (\n typeof value !== \"string\" ||\n !value ||\n value.length > MAX_TOKEN_LENGTH ||\n /[\\r\\n]/.test(value)\n ) {\n throw configurationError(\"The customer access token is invalid\");\n }\n return value;\n}\n\nfunction positiveInteger(value: number, name: string): number {\n if (!Number.isInteger(value) || value < 1) {\n throw configurationError(`${name} must be a positive integer`);\n }\n return value;\n}\n\nfunction validateMessage(value: string): string {\n if (typeof value !== \"string\") {\n throw configurationError(\"Message content must be a string\");\n }\n const normalized = value.trim();\n if (!normalized || normalized.length > MAX_MESSAGE_LENGTH) {\n throw configurationError(\n `Message content must be 1 to ${MAX_MESSAGE_LENGTH} characters`,\n );\n }\n return normalized;\n}\n\nasync function readJson(\n response: Response,\n lifetime: ReturnType<typeof createRequestLifetime>,\n): Promise<unknown> {\n const declaredLength = Number(response.headers.get(\"content-length\"));\n if (Number.isFinite(declaredLength) && declaredLength > MAX_RESPONSE_BYTES) {\n throw responseTooLarge();\n }\n\n const body = response.body;\n if (\n body &&\n typeof body.getReader === \"function\" &&\n typeof TextDecoder === \"function\"\n ) {\n return parseJson(await readStream(body, lifetime));\n }\n\n let text: string;\n try {\n text = await lifetime.run(() => response.text());\n } catch (error) {\n if (error instanceof DaykeeperReactNativeTransportError) throw error;\n throw networkError();\n }\n if (utf8LengthExceeds(text, MAX_RESPONSE_BYTES)) {\n throw responseTooLarge();\n }\n return parseJson(text);\n}\n\nasync function readStream(\n body: ReadableStream<Uint8Array>,\n lifetime: ReturnType<typeof createRequestLifetime>,\n): Promise<string> {\n const reader = body.getReader();\n const chunks: Uint8Array[] = [];\n let total = 0;\n try {\n for (;;) {\n const { done, value } = await lifetime.run(() => reader.read());\n if (done) break;\n total += value.byteLength;\n if (total > MAX_RESPONSE_BYTES) {\n throw responseTooLarge();\n }\n chunks.push(value);\n }\n } catch (error) {\n if (error instanceof DaykeeperReactNativeTransportError) throw error;\n throw networkError();\n } finally {\n discardReader(reader);\n }\n\n const bytes = new Uint8Array(total);\n let offset = 0;\n for (const chunk of chunks) {\n bytes.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return new TextDecoder().decode(bytes);\n}\n\nfunction parseJson(text: string): unknown {\n if (!text) return undefined;\n try {\n return JSON.parse(text) as unknown;\n } catch {\n throw invalidResponse();\n }\n}\n\nfunction utf8LengthExceeds(value: string, maximum: number): boolean {\n let bytes = 0;\n for (let index = 0; index < value.length; index += 1) {\n const codeUnit = value.charCodeAt(index);\n if (codeUnit < 0x80) bytes += 1;\n else if (codeUnit < 0x800) bytes += 2;\n else if (\n codeUnit >= 0xd800 &&\n codeUnit <= 0xdbff &&\n index + 1 < value.length &&\n value.charCodeAt(index + 1) >= 0xdc00 &&\n value.charCodeAt(index + 1) <= 0xdfff\n ) {\n bytes += 4;\n index += 1;\n } else bytes += 3;\n if (bytes > maximum) return true;\n }\n return false;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction responseTooLarge(): DaykeeperReactNativeTransportError {\n return new DaykeeperReactNativeTransportError({\n code: \"RESPONSE_TOO_LARGE\",\n message: \"The Daykeeper response exceeded 1 MiB\",\n });\n}\n\nfunction invalidResponse(): DaykeeperReactNativeTransportError {\n return new DaykeeperReactNativeTransportError({\n code: \"INVALID_RESPONSE\",\n message: \"The Daykeeper customer API returned invalid JSON\",\n retryable: true,\n });\n}\n\nfunction configurationError(\n message: string,\n): DaykeeperReactNativeTransportError {\n return new DaykeeperReactNativeTransportError({\n code: \"INVALID_CONFIGURATION\",\n message,\n });\n}\n"],"mappings":"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","names":[]}