@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.
package/dist/index.cjs CHANGED
@@ -38,23 +38,42 @@ __export(index_exports, {
38
38
  module.exports = __toCommonJS(index_exports);
39
39
 
40
40
  // src/errors.ts
41
+ var API_CODE_SHAPE = /^[a-z][a-z0-9_]{2,63}$/;
42
+ var DAYKEEPER_GENERIC_API_CODE = "daykeeper_request_failed";
43
+ function isDaykeeperApiErrorCode(value) {
44
+ return typeof value === "string" && API_CODE_SHAPE.test(value);
45
+ }
46
+ var SAFE_NEXT_ACTIONS = /* @__PURE__ */ new Set([
47
+ "review_usage",
48
+ "review_setup",
49
+ "refresh_conversation"
50
+ ]);
41
51
  var DaykeeperReactNativeApiError = class extends Error {
42
52
  constructor(options) {
43
- super(options.code);
53
+ const code = isDaykeeperApiErrorCode(options.code) ? options.code : DAYKEEPER_GENERIC_API_CODE;
54
+ super(code);
44
55
  __publicField(this, "status");
45
56
  __publicField(this, "code");
46
57
  __publicField(this, "retryable");
58
+ __publicField(this, "outcomeUnknown");
59
+ __publicField(this, "nextAction");
47
60
  this.name = "DaykeeperReactNativeApiError";
48
61
  this.status = options.status;
49
- this.code = options.code;
50
- this.retryable = options.retryable;
62
+ this.code = code;
63
+ this.outcomeUnknown = options.outcomeUnknown ?? false;
64
+ this.retryable = !this.outcomeUnknown && (options.retryable ?? false);
65
+ if (typeof options.nextAction === "string" && SAFE_NEXT_ACTIONS.has(options.nextAction)) {
66
+ this.nextAction = options.nextAction;
67
+ }
51
68
  }
52
69
  toJSON() {
53
70
  return {
54
71
  name: this.name,
55
72
  status: this.status,
56
73
  code: this.code,
57
- retryable: this.retryable
74
+ retryable: this.retryable,
75
+ outcomeUnknown: this.outcomeUnknown,
76
+ ...this.nextAction === void 0 ? {} : { nextAction: this.nextAction }
58
77
  };
59
78
  }
60
79
  };
@@ -63,20 +82,109 @@ var DaykeeperReactNativeTransportError = class extends Error {
63
82
  super(options.message);
64
83
  __publicField(this, "code");
65
84
  __publicField(this, "retryable");
85
+ __publicField(this, "outcomeUnknown");
66
86
  this.name = "DaykeeperReactNativeTransportError";
67
87
  this.code = options.code;
68
- this.retryable = options.retryable ?? false;
88
+ this.outcomeUnknown = options.outcomeUnknown ?? false;
89
+ this.retryable = !this.outcomeUnknown && (options.retryable ?? false);
69
90
  }
70
91
  toJSON() {
71
92
  return {
72
93
  name: this.name,
73
94
  code: this.code,
74
95
  message: this.message,
75
- retryable: this.retryable
96
+ retryable: this.retryable,
97
+ outcomeUnknown: this.outcomeUnknown
76
98
  };
77
99
  }
78
100
  };
79
101
 
102
+ // src/requestLifetime.ts
103
+ function createRequestLifetime(timeoutMs, caller) {
104
+ const controller = new AbortController();
105
+ const now = () => typeof performance === "undefined" ? Date.now() : performance.now();
106
+ const deadline = now() + timeoutMs;
107
+ let failure;
108
+ const abort = (code) => {
109
+ if (failure) return;
110
+ failure = new DaykeeperReactNativeTransportError({
111
+ code,
112
+ message: code === "REQUEST_TIMEOUT" ? `The Daykeeper request exceeded ${timeoutMs}ms` : "The Daykeeper request was aborted",
113
+ retryable: code === "REQUEST_TIMEOUT"
114
+ });
115
+ controller.abort();
116
+ };
117
+ const onCallerAbort = () => abort("REQUEST_ABORTED");
118
+ if (caller?.aborted) onCallerAbort();
119
+ else caller?.addEventListener("abort", onCallerAbort, { once: true });
120
+ const timeout = failure ? void 0 : setTimeout(() => abort("REQUEST_TIMEOUT"), timeoutMs);
121
+ const checkDeadline = () => {
122
+ if (!failure && now() >= deadline) abort("REQUEST_TIMEOUT");
123
+ };
124
+ return {
125
+ signal: controller.signal,
126
+ run(work, onLateValue) {
127
+ checkDeadline();
128
+ if (failure) return Promise.reject(failure);
129
+ return new Promise((resolve, reject) => {
130
+ let settled = false;
131
+ const finish = () => {
132
+ settled = true;
133
+ controller.signal.removeEventListener("abort", onAbort);
134
+ };
135
+ const onAbort = () => {
136
+ finish();
137
+ reject(failure);
138
+ };
139
+ controller.signal.addEventListener("abort", onAbort, { once: true });
140
+ Promise.resolve().then(() => {
141
+ checkDeadline();
142
+ if (failure) throw failure;
143
+ return work();
144
+ }).then(
145
+ (value) => {
146
+ checkDeadline();
147
+ if (settled) {
148
+ try {
149
+ onLateValue?.(value);
150
+ } catch {
151
+ }
152
+ return;
153
+ }
154
+ finish();
155
+ resolve(value);
156
+ },
157
+ (error) => {
158
+ if (settled) return;
159
+ finish();
160
+ reject(error);
161
+ }
162
+ );
163
+ });
164
+ },
165
+ dispose() {
166
+ clearTimeout(timeout);
167
+ caller?.removeEventListener("abort", onCallerAbort);
168
+ }
169
+ };
170
+ }
171
+ function discardResponse(response) {
172
+ try {
173
+ void response.body?.cancel().catch(() => void 0);
174
+ } catch {
175
+ }
176
+ }
177
+ function discardReader(reader) {
178
+ try {
179
+ void reader.cancel().catch(() => void 0);
180
+ } catch {
181
+ }
182
+ try {
183
+ reader.releaseLock();
184
+ } catch {
185
+ }
186
+ }
187
+
80
188
  // src/client.ts
81
189
  var DEFAULT_TIMEOUT_MS = 3e4;
82
190
  var MAX_RESPONSE_BYTES = 1024 * 1024;
@@ -151,20 +259,21 @@ _getAccessToken = new WeakMap();
151
259
  _timeoutMs = new WeakMap();
152
260
  _DaykeeperReactNativeClient_instances = new WeakSet();
153
261
  request_fn = async function(path, options = {}) {
154
- const timeoutController = new AbortController();
155
- let timedOut = false;
156
- const timeout = setTimeout(() => {
157
- timedOut = true;
158
- timeoutController.abort();
159
- }, __privateGet(this, _timeoutMs));
160
- const onCallerAbort = () => timeoutController.abort();
161
- if (options.signal?.aborted) timeoutController.abort();
162
- else
163
- options.signal?.addEventListener("abort", onCallerAbort, { once: true });
262
+ const lifetime = createRequestLifetime(__privateGet(this, _timeoutMs), options.signal);
263
+ const method = options.method ?? "GET";
264
+ const mutating = method !== "GET";
265
+ const attempts = mutating ? 1 : 2;
266
+ let dispatched = false;
267
+ let response;
164
268
  try {
165
- for (let attempt = 0; attempt < 2; attempt += 1) {
269
+ for (let attempt = 0; attempt < attempts; attempt += 1) {
166
270
  const token = validateToken(
167
- await __privateGet(this, _getAccessToken).call(this, { forceRefresh: attempt === 1 })
271
+ await lifetime.run(
272
+ () => resolveToken(__privateGet(this, _getAccessToken), {
273
+ forceRefresh: attempt === 1,
274
+ signal: lifetime.signal
275
+ })
276
+ )
168
277
  );
169
278
  const headers = new Headers({
170
279
  accept: "application/json",
@@ -173,42 +282,48 @@ request_fn = async function(path, options = {}) {
173
282
  if (options.body !== void 0) {
174
283
  headers.set("content-type", "application/json");
175
284
  }
176
- let response;
177
285
  try {
178
- response = await __privateGet(this, _fetch).call(this, `${__privateGet(this, _baseUrl)}${path}`, {
179
- method: options.method ?? "GET",
180
- body: options.body === void 0 ? void 0 : JSON.stringify(options.body),
181
- headers,
182
- signal: timeoutController.signal
183
- });
184
- } catch {
185
- if (timedOut) {
186
- throw new DaykeeperReactNativeTransportError({
187
- code: "REQUEST_TIMEOUT",
188
- message: `The Daykeeper request exceeded ${__privateGet(this, _timeoutMs)}ms`,
189
- retryable: true
190
- });
191
- }
192
- if (options.signal?.aborted) {
193
- throw new DaykeeperReactNativeTransportError({
194
- code: "REQUEST_ABORTED",
195
- message: "The Daykeeper request was aborted"
286
+ response = await lifetime.run(() => {
287
+ dispatched = true;
288
+ return __privateGet(this, _fetch).call(this, `${__privateGet(this, _baseUrl)}${path}`, {
289
+ method,
290
+ body: options.body === void 0 ? void 0 : JSON.stringify(options.body),
291
+ headers,
292
+ signal: lifetime.signal,
293
+ redirect: "error",
294
+ credentials: "omit",
295
+ cache: "no-store"
196
296
  });
297
+ }, discardResponse);
298
+ } catch (error) {
299
+ if (error instanceof DaykeeperReactNativeTransportError) throw error;
300
+ throw networkError();
301
+ }
302
+ const refreshCandidate = !mutating && response.status === 401 && attempt === 0;
303
+ let payload;
304
+ if (refreshCandidate || !response.ok) {
305
+ try {
306
+ payload = await readJson(response, lifetime);
307
+ } catch (error) {
308
+ if (!(error instanceof DaykeeperReactNativeTransportError) || error.code !== "INVALID_RESPONSE")
309
+ throw error;
197
310
  }
198
- throw new DaykeeperReactNativeTransportError({
199
- code: "NETWORK_ERROR",
200
- message: "The Daykeeper customer API could not be reached",
201
- retryable: true
202
- });
311
+ } else {
312
+ payload = await readJson(response, lifetime);
313
+ }
314
+ if (refreshCandidate && !(isRecord(payload) && payload.retryable === false)) {
315
+ discardResponse(response);
316
+ continue;
203
317
  }
204
- const payload = await readJson(response);
205
- if (response.status === 401 && attempt === 0) continue;
206
318
  if (!response.ok) {
207
- const code = isRecord(payload) && typeof payload.error === "string" ? payload.error : "daykeeper_request_failed";
208
319
  throw new DaykeeperReactNativeApiError({
209
320
  status: response.status,
210
- code,
211
- retryable: response.status === 408 || response.status === 429 || response.status >= 500
321
+ code: isRecord(payload) ? payload.error : void 0,
322
+ // The contract's message stays unread; only the bounded
323
+ // nextAction vocabulary is projected.
324
+ nextAction: isRecord(payload) ? payload.nextAction : void 0,
325
+ retryable: !mutating && (isRecord(payload) && typeof payload.retryable === "boolean" ? payload.retryable : response.status === 408 || response.status === 429 || response.status >= 500),
326
+ outcomeUnknown: mutating && (response.status === 408 || response.status >= 500)
212
327
  });
213
328
  }
214
329
  if (!isRecord(payload)) {
@@ -221,14 +336,41 @@ request_fn = async function(path, options = {}) {
221
336
  return payload;
222
337
  }
223
338
  throw new Error("Unreachable Daykeeper request state");
339
+ } catch (error) {
340
+ if (error instanceof DaykeeperReactNativeApiError) throw error;
341
+ const safeError = error instanceof DaykeeperReactNativeTransportError ? error : networkError();
342
+ if (!mutating) throw safeError;
343
+ throw new DaykeeperReactNativeTransportError({
344
+ code: safeError.code,
345
+ message: safeError.message,
346
+ retryable: false,
347
+ outcomeUnknown: dispatched
348
+ });
224
349
  } finally {
225
- clearTimeout(timeout);
226
- options.signal?.removeEventListener("abort", onCallerAbort);
350
+ if (response) discardResponse(response);
351
+ lifetime.dispose();
227
352
  }
228
353
  };
229
354
  function createDaykeeperReactNativeClient(options) {
230
355
  return new DaykeeperReactNativeClient(options);
231
356
  }
357
+ async function resolveToken(provider, context) {
358
+ try {
359
+ return await provider(context);
360
+ } catch {
361
+ throw new DaykeeperReactNativeTransportError({
362
+ code: "TOKEN_PROVIDER_ERROR",
363
+ message: "The Daykeeper access token could not be obtained"
364
+ });
365
+ }
366
+ }
367
+ function networkError() {
368
+ return new DaykeeperReactNativeTransportError({
369
+ code: "NETWORK_ERROR",
370
+ message: "The Daykeeper customer API transport failed",
371
+ retryable: true
372
+ });
373
+ }
232
374
  function parseBaseUrl(value) {
233
375
  let url;
234
376
  try {
@@ -236,7 +378,8 @@ function parseBaseUrl(value) {
236
378
  } catch {
237
379
  throw configurationError("baseUrl must be a valid absolute URL");
238
380
  }
239
- const local = ["localhost", "127.0.0.1", "::1"].includes(url.hostname);
381
+ const hostname = url.hostname.replace(/^\[(.+)]$/, "$1").toLowerCase();
382
+ const local = ["localhost", "127.0.0.1", "::1"].includes(hostname);
240
383
  if (url.protocol !== "https:" && !(local && url.protocol === "http:")) {
241
384
  throw configurationError(
242
385
  "baseUrl must use HTTPS except for loopback development"
@@ -281,43 +424,46 @@ function validateMessage(value) {
281
424
  }
282
425
  return normalized;
283
426
  }
284
- async function readJson(response) {
427
+ async function readJson(response, lifetime) {
285
428
  const declaredLength = Number(response.headers.get("content-length"));
286
429
  if (Number.isFinite(declaredLength) && declaredLength > MAX_RESPONSE_BYTES) {
287
430
  throw responseTooLarge();
288
431
  }
289
432
  const body = response.body;
290
433
  if (body && typeof body.getReader === "function" && typeof TextDecoder === "function") {
291
- return parseJson(await readStream(body));
434
+ return parseJson(await readStream(body, lifetime));
292
435
  }
293
436
  let text;
294
437
  try {
295
- text = await response.text();
296
- } catch {
297
- throw invalidResponse();
438
+ text = await lifetime.run(() => response.text());
439
+ } catch (error) {
440
+ if (error instanceof DaykeeperReactNativeTransportError) throw error;
441
+ throw networkError();
298
442
  }
299
443
  if (utf8LengthExceeds(text, MAX_RESPONSE_BYTES)) {
300
444
  throw responseTooLarge();
301
445
  }
302
446
  return parseJson(text);
303
447
  }
304
- async function readStream(body) {
448
+ async function readStream(body, lifetime) {
305
449
  const reader = body.getReader();
306
450
  const chunks = [];
307
451
  let total = 0;
308
452
  try {
309
453
  for (; ; ) {
310
- const { done, value } = await reader.read();
454
+ const { done, value } = await lifetime.run(() => reader.read());
311
455
  if (done) break;
312
456
  total += value.byteLength;
313
457
  if (total > MAX_RESPONSE_BYTES) {
314
- await reader.cancel();
315
458
  throw responseTooLarge();
316
459
  }
317
460
  chunks.push(value);
318
461
  }
462
+ } catch (error) {
463
+ if (error instanceof DaykeeperReactNativeTransportError) throw error;
464
+ throw networkError();
319
465
  } finally {
320
- reader.releaseLock();
466
+ discardReader(reader);
321
467
  }
322
468
  const bytes = new Uint8Array(total);
323
469
  let offset = 0;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/client.ts"],"sourcesContent":["export {\n createDaykeeperReactNativeClient,\n DaykeeperReactNativeClient,\n} from \"./client.js\";\nexport type {\n DaykeeperReactNativeClientOptions,\n DaykeeperReactNativeRequestOptions,\n DaykeeperReactNativeTokenProvider,\n DaykeeperReactNativeTokenProviderContext,\n} from \"./client.js\";\nexport {\n DaykeeperReactNativeApiError,\n DaykeeperReactNativeTransportError,\n} from \"./errors.js\";\nexport type { DaykeeperReactNativeTransportErrorCode } from \"./errors.js\";\nexport type {\n components as DaykeeperCustomerOpenApiComponents,\n operations as DaykeeperCustomerOpenApiOperations,\n paths as DaykeeperCustomerOpenApiPaths,\n} from \"./generated/schema.js\";\nexport * from \"./types.js\";\n","export type DaykeeperReactNativeTransportErrorCode =\n | \"INVALID_CONFIGURATION\"\n | \"NETWORK_ERROR\"\n | \"REQUEST_ABORTED\"\n | \"REQUEST_TIMEOUT\"\n | \"INVALID_RESPONSE\"\n | \"RESPONSE_TOO_LARGE\";\n\nexport class DaykeeperReactNativeApiError extends Error {\n readonly status: number;\n readonly code: string;\n readonly retryable: boolean;\n\n constructor(options: { status: number; code: string; retryable: boolean }) {\n super(options.code);\n this.name = \"DaykeeperReactNativeApiError\";\n this.status = options.status;\n this.code = options.code;\n this.retryable = options.retryable;\n }\n\n toJSON(): {\n name: string;\n status: number;\n code: string;\n retryable: boolean;\n } {\n return {\n name: this.name,\n status: this.status,\n code: this.code,\n retryable: this.retryable,\n };\n }\n}\n\nexport class DaykeeperReactNativeTransportError extends Error {\n readonly code: DaykeeperReactNativeTransportErrorCode;\n readonly retryable: boolean;\n\n constructor(options: {\n code: DaykeeperReactNativeTransportErrorCode;\n message: string;\n retryable?: boolean;\n }) {\n super(options.message);\n this.name = \"DaykeeperReactNativeTransportError\";\n this.code = options.code;\n this.retryable = options.retryable ?? false;\n }\n\n toJSON(): {\n name: string;\n code: DaykeeperReactNativeTransportErrorCode;\n message: string;\n retryable: boolean;\n } {\n return {\n name: this.name,\n code: this.code,\n message: this.message,\n retryable: this.retryable,\n };\n }\n}\n","import {\n DaykeeperReactNativeApiError,\n DaykeeperReactNativeTransportError,\n} from \"./errors.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 Daykeeper rejected the first token with HTTP 401. The\n * provider should bypass any token cache and exchange the app session again.\n */\n forceRefresh: boolean;\n}\n\nexport type DaykeeperReactNativeTokenProvider = (\n context: DaykeeperReactNativeTokenProviderContext,\n) => string | Promise<string>;\n\nexport interface DaykeeperReactNativeClientOptions {\n baseUrl: string;\n getAccessToken: DaykeeperReactNativeTokenProvider;\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 timeoutController = new AbortController();\n let timedOut = false;\n const timeout = setTimeout(() => {\n timedOut = true;\n timeoutController.abort();\n }, this.#timeoutMs);\n const onCallerAbort = () => timeoutController.abort();\n if (options.signal?.aborted) timeoutController.abort();\n else\n options.signal?.addEventListener(\"abort\", onCallerAbort, { once: true });\n\n try {\n for (let attempt = 0; attempt < 2; attempt += 1) {\n const token = validateToken(\n await this.#getAccessToken({ forceRefresh: attempt === 1 }),\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 let response: Response;\n try {\n response = await this.#fetch(`${this.#baseUrl}${path}`, {\n method: options.method ?? \"GET\",\n body:\n options.body === undefined\n ? undefined\n : JSON.stringify(options.body),\n headers,\n signal: timeoutController.signal,\n });\n } catch {\n if (timedOut) {\n throw new DaykeeperReactNativeTransportError({\n code: \"REQUEST_TIMEOUT\",\n message: `The Daykeeper request exceeded ${this.#timeoutMs}ms`,\n retryable: true,\n });\n }\n if (options.signal?.aborted) {\n throw new DaykeeperReactNativeTransportError({\n code: \"REQUEST_ABORTED\",\n message: \"The Daykeeper request was aborted\",\n });\n }\n throw new DaykeeperReactNativeTransportError({\n code: \"NETWORK_ERROR\",\n message: \"The Daykeeper customer API could not be reached\",\n retryable: true,\n });\n }\n\n const payload = await readJson(response);\n if (response.status === 401 && attempt === 0) continue;\n if (!response.ok) {\n const code =\n isRecord(payload) && typeof payload.error === \"string\"\n ? payload.error\n : \"daykeeper_request_failed\";\n throw new DaykeeperReactNativeApiError({\n status: response.status,\n code,\n retryable:\n response.status === 408 ||\n response.status === 429 ||\n 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 } finally {\n clearTimeout(timeout);\n options.signal?.removeEventListener(\"abort\", onCallerAbort);\n }\n }\n}\n\nexport function createDaykeeperReactNativeClient(\n options: DaykeeperReactNativeClientOptions,\n): DaykeeperReactNativeClient {\n return new DaykeeperReactNativeClient(options);\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 const local = [\"localhost\", \"127.0.0.1\", \"::1\"].includes(url.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(response: Response): 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));\n }\n\n let text: string;\n try {\n text = await response.text();\n } catch {\n throw invalidResponse();\n }\n if (utf8LengthExceeds(text, MAX_RESPONSE_BYTES)) {\n throw responseTooLarge();\n }\n return parseJson(text);\n}\n\nasync function readStream(body: ReadableStream<Uint8Array>): Promise<string> {\n const reader = body.getReader();\n const chunks: Uint8Array[] = [];\n let total = 0;\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n total += value.byteLength;\n if (total > MAX_RESPONSE_BYTES) {\n await reader.cancel();\n throw responseTooLarge();\n }\n chunks.push(value);\n }\n } finally {\n reader.releaseLock();\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":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/requestLifetime.ts","../src/client.ts"],"sourcesContent":["export {\n createDaykeeperReactNativeClient,\n DaykeeperReactNativeClient,\n} from \"./client.js\";\nexport type {\n DaykeeperReactNativeClientOptions,\n DaykeeperReactNativeRequestOptions,\n DaykeeperReactNativeTokenProvider,\n DaykeeperReactNativeTokenProviderContext,\n} from \"./client.js\";\nexport {\n DaykeeperReactNativeApiError,\n DaykeeperReactNativeTransportError,\n} from \"./errors.js\";\nexport type {\n DaykeeperReactNativeNextAction,\n DaykeeperReactNativeTransportErrorCode,\n} from \"./errors.js\";\nexport type {\n components as DaykeeperCustomerOpenApiComponents,\n operations as DaykeeperCustomerOpenApiOperations,\n paths as DaykeeperCustomerOpenApiPaths,\n} from \"./generated/schema.js\";\nexport * from \"./types.js\";\n","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":[]}