ropegeo-common 1.12.1 → 1.12.4

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/README.md CHANGED
@@ -11,7 +11,7 @@ npm install ropegeo-common
11
11
  ## Imports
12
12
 
13
13
  - **Models** — `import { … } from 'ropegeo-common/models'` (or `import type { … }` for symbols that are type-only in TypeScript). The package root `ropegeo-common` re-exports everything from `./models` for convenience. The subpath `ropegeo-common/classes` is kept as an alias of `./models` for older imports until dependents switch over.
14
- - **Helpers** — `import { … } from 'ropegeo-common/helpers'` (and `import type { … }` for helper types such as `GetS3ObjectResult`).
14
+ - **Helpers** — `import { … } from 'ropegeo-common/helpers'` (and `import type { … }` for helper types such as `GetS3ObjectResult`). The full barrel includes Node-only modules (S3 folder upload uses `fs`); **React Native / Metro** should use **`ropegeo-common/helpers/network`** (sources under `src/helpers/network/`, barrel `index.ts`) for the request-timeout / abort helpers only (`NETWORK_REQUEST_*`, `installNetworkRequestPolicyTimers`, `mergeParentSignalWithDeadline`, `resolveRequestTimeoutMs`, `isAbortError`, `isNetworkRequestTimeoutError`, and related types).
15
15
 
16
16
  Helper tables use columns **Name**, **Description**, **Import**. Model tables add **Base class** after **Name** (`N/A` for enums, TypeScript-only type aliases, constants, registration functions, and classes without an exported superclass; otherwise the direct superclass).
17
17
 
@@ -24,6 +24,17 @@ Helper tables use columns **Name**, **Description**, **Import**. Model tables ad
24
24
  | `httpRequest` | HTTP client wrapper: headers, throws on non-OK responses, optional Lambda proxy routing. | `import { httpRequest } from 'ropegeo-common/helpers'` |
25
25
  | `ProgressLogger` | Progress logging utility. | `import { ProgressLogger } from 'ropegeo-common/helpers'` |
26
26
  | `timeoutAfter` | Promise helper that rejects after a timeout. | `import { timeoutAfter } from 'ropegeo-common/helpers'` |
27
+ | `NETWORK_REQUEST_DEFAULT_TIMEOUT_SECONDS` | Default request deadline in seconds when `timeoutAfterSeconds` is omitted on components. | `import { NETWORK_REQUEST_DEFAULT_TIMEOUT_SECONDS } from 'ropegeo-common/helpers'` |
28
+ | `NETWORK_REQUEST_HARD_TIMEOUT_MS` | Default deadline in ms (`NETWORK_REQUEST_DEFAULT_TIMEOUT_SECONDS * 1000`); used by `resolveRequestTimeoutMs` and `mergeParentSignalWithDeadline` default. | `import { NETWORK_REQUEST_HARD_TIMEOUT_MS } from 'ropegeo-common/helpers'` |
29
+ | `NETWORK_REQUEST_SLOW_THRESHOLD_MS` | Suggested delay (15s) before *showing* slow-network UI; countdown ticks still emit from t=0 for consumers that compare elapsed time. | `import { NETWORK_REQUEST_SLOW_THRESHOLD_MS } from 'ropegeo-common/helpers'` |
30
+ | `NETWORK_REQUEST_TIMED_OUT_MESSAGE` | Exact timeout error string for classification. | `import { NETWORK_REQUEST_TIMED_OUT_MESSAGE } from 'ropegeo-common/helpers'` |
31
+ | `resolveRequestTimeoutMs` | Converts optional `timeoutAfterSeconds` to ms or returns the default deadline. | `import { resolveRequestTimeoutMs } from 'ropegeo-common/helpers'` |
32
+ | `installNetworkRequestPolicyTimers` | Emits whole-second countdown until `hardTimeoutMs`, then invokes hard-timeout callback (single in-flight request). | `import { installNetworkRequestPolicyTimers } from 'ropegeo-common/helpers'` |
33
+ | `mergeParentSignalWithDeadline` | Merges a parent `AbortSignal` with a per-request deadline (used for follow-up pagination fetches). | `import { mergeParentSignalWithDeadline } from 'ropegeo-common/helpers'` |
34
+ | `isNetworkRequestTimeoutError` | Returns true when an error is the standardized timeout message. | `import { isNetworkRequestTimeoutError } from 'ropegeo-common/helpers'` |
35
+ | `isAbortError` | Returns true for `AbortError` / DOMException abort failures. | `import { isAbortError } from 'ropegeo-common/helpers'` |
36
+ | `MergedDeadlineHandles` | Handles returned by `mergeParentSignalWithDeadline` (`signal`, `dispose`, `consumeDidTimeout`). | `import type { MergedDeadlineHandles } from 'ropegeo-common/helpers'` |
37
+ | `NetworkRequestPolicyTimerCallbacks` | Callback bundle for `installNetworkRequestPolicyTimers`. | `import type { NetworkRequestPolicyTimerCallbacks } from 'ropegeo-common/helpers'` |
27
38
 
28
39
  ### S3 helpers (`src/helpers/s3/`)
29
40
 
@@ -286,14 +297,18 @@ Helper tables use columns **Name**, **Description**, **Import**. Model tables ad
286
297
  | `VERSION_FORMAT` | N/A | Format constant for version strings. | `import { VERSION_FORMAT } from 'ropegeo-common/models'` |
287
298
  | `ImageVersions` | N/A | Map of image URLs by `ImageVersion`; `fromResult` for persisted JSON. | `import { ImageVersions } from 'ropegeo-common/models'` |
288
299
  | `SavedPage` | N/A | Saved page record (`OnlinePagePreview` or `OfflinePagePreview`) with optional `downloadedPageViewPath`. | `import { SavedPage } from 'ropegeo-common/models'` |
300
+ | `SAVED_PAGES_STORAGE_KEY` | N/A | AsyncStorage key for the saved-pages map (`ropegeo:savedPages`). | `import { SAVED_PAGES_STORAGE_KEY } from 'ropegeo-common/models'` |
301
+ | `DOWNLOADED_ROUTE_PREVIEWS_STORAGE_KEY` | N/A | AsyncStorage key for offline route preview rows (`ropegeo:downloadedRoutePreviews`). | `import { DOWNLOADED_ROUTE_PREVIEWS_STORAGE_KEY } from 'ropegeo-common/models'` |
302
+ | `SavedPagesStorageMap` | N/A | Type for the saved-pages record (`pageId` → `SavedPage` JSON string). | `import type { SavedPagesStorageMap } from 'ropegeo-common/models'` |
303
+ | `DownloadedRoutePreviewsStorageMap` | N/A | Type for the offline route-preview index (`routeId` → serialized preview rows). | `import type { DownloadedRoutePreviewsStorageMap } from 'ropegeo-common/models'` |
289
304
 
290
305
  ### React components (`src/components/`)
291
306
 
292
307
  | Name | Description | Import |
293
308
  | --- | --- | --- |
294
- | `RopeGeoHttpRequest` | Single GET/POST wrapper; parses `Result.fromResponseBody`. | `import { RopeGeoHttpRequest, Method, Service } from 'ropegeo-common/components'` |
295
- | `RopeGeoCursorPaginationHttpRequest` | Cursor-paginated fetch with `loadMore`. | `import { RopeGeoCursorPaginationHttpRequest } from 'ropegeo-common/components'` |
296
- | `RopeGeoPaginationHttpRequest<T>` | Page-based fetch; each page validated with `PaginationResults.fromResponseBody`; concatenates `results` into `data` (`T[]` when complete, otherwise `null` with `errors`). | `import { RopeGeoPaginationHttpRequest } from 'ropegeo-common/components'` |
309
+ | `RopeGeoHttpRequest` | Single GET/POST wrapper; parses `Result.fromResponseBody`; optional `timeoutAfterSeconds` (default from helpers) + `timeoutCountdown` children arg. | `import { RopeGeoHttpRequest, Method, Service } from 'ropegeo-common/components'` |
310
+ | `RopeGeoCursorPaginationHttpRequest` | Cursor-paginated fetch with `loadMore`; optional `timeoutAfterSeconds`; `timeoutCountdown` on initial and `loadMore` requests. | `import { RopeGeoCursorPaginationHttpRequest } from 'ropegeo-common/components'` |
311
+ | `RopeGeoPaginationHttpRequest<T>` | Page-based fetch; page 1 emits `timeoutCountdown`; later pages use the same per-fetch deadline; concatenates `results` into `data` (`T[]` when complete, otherwise `null` with `errors`). | `import { RopeGeoPaginationHttpRequest } from 'ropegeo-common/components'` |
297
312
 
298
313
  ---
299
314
 
@@ -7,6 +7,11 @@ export type RopeGeoCursorPaginationHttpRequestProps<T = unknown> = {
7
7
  path: string;
8
8
  pathParams?: Record<string, string>;
9
9
  queryParams: CursorPaginationParams;
10
+ /**
11
+ * Request deadline in seconds for each fetch (initial and `loadMore`). Defaults to the package
12
+ * default when omitted.
13
+ */
14
+ timeoutAfterSeconds?: number;
10
15
  /**
11
16
  * Response body is parsed via CursorPaginationResults.fromResponseBody (must include resultType).
12
17
  * Parsed shape is ValidatedCursorPaginationResponse; children receive result.results as data.
@@ -18,7 +23,8 @@ export type RopeGeoCursorPaginationHttpRequestProps<T = unknown> = {
18
23
  errors: Error | null;
19
24
  loadMore: () => void;
20
25
  hasMore: boolean;
26
+ timeoutCountdown: number | null;
21
27
  }) => ReactNode;
22
28
  };
23
- export declare function RopeGeoCursorPaginationHttpRequest<T = unknown>({ service, method, path, pathParams, queryParams, children, }: RopeGeoCursorPaginationHttpRequestProps<T>): import("react/jsx-runtime").JSX.Element;
29
+ export declare function RopeGeoCursorPaginationHttpRequest<T = unknown>({ service, method, path, pathParams, queryParams, timeoutAfterSeconds, children, }: RopeGeoCursorPaginationHttpRequestProps<T>): import("react/jsx-runtime").JSX.Element;
24
30
  //# sourceMappingURL=RopeGeoCursorPaginationHttpRequest.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"RopeGeoCursorPaginationHttpRequest.d.ts","sourceRoot":"","sources":["../../src/components/RopeGeoCursorPaginationHttpRequest.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,EACL,KAAK,sBAAsB,EAE5B,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,MAAM,EAAE,OAAO,EAAoB,MAAM,sBAAsB,CAAC;AAsCzE,MAAM,MAAM,uCAAuC,CAAC,CAAC,GAAG,OAAO,IAAI;IACjE,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,CAAC,OAAO,MAAM,CAAC,CAAC,MAAM,OAAO,MAAM,CAAC,CAAC;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,WAAW,EAAE,sBAAsB,CAAC;IACpC;;;OAGG;IACH,QAAQ,EAAE,CAAC,IAAI,EAAE;QACf,OAAO,EAAE,OAAO,CAAC;QACjB,WAAW,EAAE,OAAO,CAAC;QACrB,IAAI,EAAE,CAAC,EAAE,CAAC;QACV,MAAM,EAAE,KAAK,GAAG,IAAI,CAAC;QACrB,QAAQ,EAAE,MAAM,IAAI,CAAC;QACrB,OAAO,EAAE,OAAO,CAAC;KAClB,KAAK,SAAS,CAAC;CACjB,CAAC;AAEF,wBAAgB,kCAAkC,CAAC,CAAC,GAAG,OAAO,EAAE,EAC9D,OAAO,EACP,MAAmB,EACnB,IAAI,EACJ,UAAU,EACV,WAAW,EACX,QAAQ,GACT,EAAE,uCAAuC,CAAC,CAAC,CAAC,2CAyJ5C"}
1
+ {"version":3,"file":"RopeGeoCursorPaginationHttpRequest.d.ts","sourceRoot":"","sources":["../../src/components/RopeGeoCursorPaginationHttpRequest.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAQvC,OAAO,EACL,KAAK,sBAAsB,EAE5B,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,MAAM,EAAE,OAAO,EAAoB,MAAM,sBAAsB,CAAC;AAsCzE,MAAM,MAAM,uCAAuC,CAAC,CAAC,GAAG,OAAO,IAAI;IACjE,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,CAAC,OAAO,MAAM,CAAC,CAAC,MAAM,OAAO,MAAM,CAAC,CAAC;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,WAAW,EAAE,sBAAsB,CAAC;IACpC;;;OAGG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B;;;OAGG;IACH,QAAQ,EAAE,CAAC,IAAI,EAAE;QACf,OAAO,EAAE,OAAO,CAAC;QACjB,WAAW,EAAE,OAAO,CAAC;QACrB,IAAI,EAAE,CAAC,EAAE,CAAC;QACV,MAAM,EAAE,KAAK,GAAG,IAAI,CAAC;QACrB,QAAQ,EAAE,MAAM,IAAI,CAAC;QACrB,OAAO,EAAE,OAAO,CAAC;QACjB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;KACjC,KAAK,SAAS,CAAC;CACjB,CAAC;AAEF,wBAAgB,kCAAkC,CAAC,CAAC,GAAG,OAAO,EAAE,EAC9D,OAAO,EACP,MAAmB,EACnB,IAAI,EACJ,UAAU,EACV,WAAW,EACX,mBAAmB,EACnB,QAAQ,GACT,EAAE,uCAAuC,CAAC,CAAC,CAAC,2CA2O5C"}
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.RopeGeoCursorPaginationHttpRequest = RopeGeoCursorPaginationHttpRequest;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  const react_1 = require("react");
6
+ const network_1 = require("../helpers/network");
6
7
  const models_1 = require("../models");
7
8
  const RopeGeoHttpRequest_1 = require("./RopeGeoHttpRequest");
8
9
  const PATH_PARAM_PATTERN = /:([a-zA-Z0-9_]+)/g;
@@ -29,13 +30,15 @@ function getResponseBody(raw) {
29
30
  }
30
31
  return raw;
31
32
  }
32
- function RopeGeoCursorPaginationHttpRequest({ service, method = RopeGeoHttpRequest_1.Method.GET, path, pathParams, queryParams, children, }) {
33
+ function RopeGeoCursorPaginationHttpRequest({ service, method = RopeGeoHttpRequest_1.Method.GET, path, pathParams, queryParams, timeoutAfterSeconds, children, }) {
33
34
  const [loading, setLoading] = (0, react_1.useState)(true);
34
35
  const [loadingMore, setLoadingMore] = (0, react_1.useState)(false);
35
36
  const [data, setData] = (0, react_1.useState)([]);
36
37
  const [params, setParams] = (0, react_1.useState)(queryParams);
37
38
  const [errors, setErrors] = (0, react_1.useState)(null);
39
+ const [timeoutCountdown, setTimeoutCountdown] = (0, react_1.useState)(null);
38
40
  const loadingMoreRef = (0, react_1.useRef)(false);
41
+ const loadMoreAbortRef = (0, react_1.useRef)(null);
39
42
  const hasMore = params.cursor != null;
40
43
  const buildUrl = (0, react_1.useCallback)((p) => {
41
44
  const baseUrl = RopeGeoHttpRequest_1.SERVICE_BASE_URL[service];
@@ -46,14 +49,35 @@ function RopeGeoCursorPaginationHttpRequest({ service, method = RopeGeoHttpReque
46
49
  }, [service, path, pathParams]);
47
50
  (0, react_1.useEffect)(() => {
48
51
  let cancelled = false;
52
+ const abortController = new AbortController();
53
+ const timedOutRef = { current: false };
54
+ const requestStartedAt = Date.now();
55
+ const timeoutMs = (0, network_1.resolveRequestTimeoutMs)(timeoutAfterSeconds);
49
56
  setData([]);
50
57
  setParams(queryParams);
51
58
  setLoading(true);
52
59
  setErrors(null);
60
+ setTimeoutCountdown(null);
61
+ const policyDispose = (0, network_1.installNetworkRequestPolicyTimers)(requestStartedAt, timeoutMs, {
62
+ isActive: () => !cancelled,
63
+ onTimeoutCountdown: (seconds) => {
64
+ if (!cancelled)
65
+ setTimeoutCountdown(seconds);
66
+ },
67
+ onClearTimeoutCountdown: () => {
68
+ if (!cancelled)
69
+ setTimeoutCountdown(null);
70
+ },
71
+ onHardTimeout: () => {
72
+ timedOutRef.current = true;
73
+ abortController.abort();
74
+ },
75
+ });
53
76
  const url = buildUrl(queryParams);
54
77
  const init = {
55
78
  method,
56
79
  headers: { "Content-Type": "application/json" },
80
+ signal: abortController.signal,
57
81
  };
58
82
  fetch(url, init)
59
83
  .then(async (res) => {
@@ -94,23 +118,40 @@ function RopeGeoCursorPaginationHttpRequest({ service, method = RopeGeoHttpReque
94
118
  }
95
119
  })
96
120
  .catch((err) => {
97
- if (!cancelled) {
98
- console.error("[RopeGeoCursorPaginationHttpRequest] Request failed", {
99
- url,
100
- error: err instanceof Error ? err.message : String(err),
101
- });
102
- setErrors(err instanceof Error ? err : new Error(String(err)));
121
+ if (cancelled)
122
+ return;
123
+ if (timedOutRef.current) {
124
+ setErrors(new Error(network_1.NETWORK_REQUEST_TIMED_OUT_MESSAGE));
103
125
  setData([]);
126
+ return;
104
127
  }
128
+ if ((0, network_1.isAbortError)(err))
129
+ return;
130
+ console.error("[RopeGeoCursorPaginationHttpRequest] Request failed", {
131
+ url,
132
+ error: err instanceof Error ? err.message : String(err),
133
+ });
134
+ setErrors(err instanceof Error ? err : new Error(String(err)));
135
+ setData([]);
105
136
  })
106
137
  .finally(() => {
107
- if (!cancelled)
138
+ policyDispose();
139
+ if (!cancelled) {
140
+ setTimeoutCountdown(null);
108
141
  setLoading(false);
142
+ }
109
143
  });
110
144
  return () => {
111
145
  cancelled = true;
146
+ policyDispose();
147
+ abortController.abort();
148
+ };
149
+ }, [service, method, path, pathParams, queryParams, buildUrl, timeoutAfterSeconds]);
150
+ (0, react_1.useEffect)(() => {
151
+ return () => {
152
+ loadMoreAbortRef.current?.abort();
112
153
  };
113
- }, [service, method, path, pathParams, queryParams, buildUrl]);
154
+ }, []);
114
155
  const loadMore = (0, react_1.useCallback)(() => {
115
156
  if (params.cursor == null)
116
157
  return;
@@ -118,10 +159,33 @@ function RopeGeoCursorPaginationHttpRequest({ service, method = RopeGeoHttpReque
118
159
  return;
119
160
  loadingMoreRef.current = true;
120
161
  setLoadingMore(true);
162
+ const outer = new AbortController();
163
+ loadMoreAbortRef.current = outer;
164
+ const timedOutRef = { current: false };
165
+ const requestStartedAt = Date.now();
166
+ const timeoutMs = (0, network_1.resolveRequestTimeoutMs)(timeoutAfterSeconds);
167
+ const policyDispose = (0, network_1.installNetworkRequestPolicyTimers)(requestStartedAt, timeoutMs, {
168
+ isActive: () => loadMoreAbortRef.current === outer,
169
+ onTimeoutCountdown: (seconds) => {
170
+ if (loadMoreAbortRef.current === outer) {
171
+ setTimeoutCountdown(seconds);
172
+ }
173
+ },
174
+ onClearTimeoutCountdown: () => {
175
+ if (loadMoreAbortRef.current === outer) {
176
+ setTimeoutCountdown(null);
177
+ }
178
+ },
179
+ onHardTimeout: () => {
180
+ timedOutRef.current = true;
181
+ outer.abort();
182
+ },
183
+ });
121
184
  const url = buildUrl(params);
122
185
  const init = {
123
186
  method,
124
187
  headers: { "Content-Type": "application/json" },
188
+ signal: outer.signal,
125
189
  };
126
190
  fetch(url, init)
127
191
  .then(async (res) => {
@@ -145,21 +209,32 @@ function RopeGeoCursorPaginationHttpRequest({ service, method = RopeGeoHttpReque
145
209
  responseText: text.slice(0, 500),
146
210
  parseError: parseError instanceof Error ? parseError.message : String(parseError),
147
211
  });
148
- // keep existing data and params
149
212
  }
150
213
  })
151
214
  .catch((err) => {
215
+ if (loadMoreAbortRef.current !== outer)
216
+ return;
217
+ if (timedOutRef.current) {
218
+ console.error("[RopeGeoCursorPaginationHttpRequest] loadMore: timed out", { url });
219
+ return;
220
+ }
221
+ if ((0, network_1.isAbortError)(err))
222
+ return;
152
223
  console.error("[RopeGeoCursorPaginationHttpRequest] loadMore: Request failed", {
153
224
  url,
154
225
  error: err instanceof Error ? err.message : String(err),
155
226
  });
156
- // keep existing data and params
157
227
  })
158
228
  .finally(() => {
229
+ policyDispose();
230
+ if (loadMoreAbortRef.current === outer) {
231
+ setTimeoutCountdown(null);
232
+ loadMoreAbortRef.current = null;
233
+ }
159
234
  loadingMoreRef.current = false;
160
235
  setLoadingMore(false);
161
236
  });
162
- }, [params, method, buildUrl]);
237
+ }, [params, method, buildUrl, timeoutAfterSeconds]);
163
238
  return ((0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: children({
164
239
  loading,
165
240
  loadingMore,
@@ -167,5 +242,6 @@ function RopeGeoCursorPaginationHttpRequest({ service, method = RopeGeoHttpReque
167
242
  errors,
168
243
  loadMore,
169
244
  hasMore,
245
+ timeoutCountdown,
170
246
  }) }));
171
247
  }
@@ -18,6 +18,11 @@ export type RopeGeoHttpRequestProps<T = unknown> = {
18
18
  pathParams?: Record<string, string>;
19
19
  queryParams?: Record<string, string | number | boolean | undefined>;
20
20
  body?: object;
21
+ /**
22
+ * Request deadline in seconds (abort + timeout error). Defaults to the package default
23
+ * (`NETWORK_REQUEST_DEFAULT_TIMEOUT_SECONDS` from `ropegeo-common/helpers`) when omitted.
24
+ */
25
+ timeoutAfterSeconds?: number;
21
26
  /**
22
27
  * Response body is parsed via Result.fromResponseBody (must include resultType and result).
23
28
  * Children receive the validated result.result as data (typed by T).
@@ -26,7 +31,13 @@ export type RopeGeoHttpRequestProps<T = unknown> = {
26
31
  loading: boolean;
27
32
  data: T | null;
28
33
  errors: Error | null;
34
+ /**
35
+ * Whole seconds remaining until abort, emitted from request start through ~1s before timeout.
36
+ * `null` when idle or after completion/cleanup. UI may choose when to show a toast (e.g. only
37
+ * after `NETWORK_REQUEST_SLOW_THRESHOLD_MS` from `ropegeo-common/helpers`).
38
+ */
39
+ timeoutCountdown: number | null;
29
40
  }) => ReactNode;
30
41
  };
31
- export declare function RopeGeoHttpRequest<T = unknown>({ service, method, path, pathParams, queryParams, body, children, }: RopeGeoHttpRequestProps<T>): import("react/jsx-runtime").JSX.Element;
42
+ export declare function RopeGeoHttpRequest<T = unknown>({ service, method, path, pathParams, queryParams, body, timeoutAfterSeconds, children, }: RopeGeoHttpRequestProps<T>): import("react/jsx-runtime").JSX.Element;
32
43
  //# sourceMappingURL=RopeGeoHttpRequest.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"RopeGeoHttpRequest.d.ts","sourceRoot":"","sources":["../../src/components/RopeGeoHttpRequest.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAIvC,eAAO,MAAM,OAAO;;CAEV,CAAC;AACX,MAAM,MAAM,OAAO,GAAG,CAAC,OAAO,OAAO,CAAC,CAAC,MAAM,OAAO,OAAO,CAAC,CAAC;AAE7D,eAAO,MAAM,MAAM;;;;;CAKT,CAAC;AACX,MAAM,MAAM,MAAM,GAAG,CAAC,OAAO,MAAM,CAAC,CAAC,MAAM,OAAO,MAAM,CAAC,CAAC;AAE1D,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,CAEpD,CAAC;AAmCF,MAAM,MAAM,uBAAuB,CAAC,CAAC,GAAG,OAAO,IAAI;IACjD,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC,CAAC;IACpE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,QAAQ,EAAE,CAAC,IAAI,EAAE;QACf,OAAO,EAAE,OAAO,CAAC;QACjB,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC;QACf,MAAM,EAAE,KAAK,GAAG,IAAI,CAAC;KACtB,KAAK,SAAS,CAAC;CACjB,CAAC;AAEF,wBAAgB,kBAAkB,CAAC,CAAC,GAAG,OAAO,EAAE,EAC9C,OAAO,EACP,MAAM,EACN,IAAI,EACJ,UAAU,EACV,WAAW,EACX,IAAI,EACJ,QAAQ,GACT,EAAE,uBAAuB,CAAC,CAAC,CAAC,2CAuF5B"}
1
+ {"version":3,"file":"RopeGeoHttpRequest.d.ts","sourceRoot":"","sources":["../../src/components/RopeGeoHttpRequest.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAUvC,eAAO,MAAM,OAAO;;CAEV,CAAC;AACX,MAAM,MAAM,OAAO,GAAG,CAAC,OAAO,OAAO,CAAC,CAAC,MAAM,OAAO,OAAO,CAAC,CAAC;AAE7D,eAAO,MAAM,MAAM;;;;;CAKT,CAAC;AACX,MAAM,MAAM,MAAM,GAAG,CAAC,OAAO,MAAM,CAAC,CAAC,MAAM,OAAO,MAAM,CAAC,CAAC;AAE1D,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,CAEpD,CAAC;AAmCF,MAAM,MAAM,uBAAuB,CAAC,CAAC,GAAG,OAAO,IAAI;IACjD,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC,CAAC;IACpE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B;;;OAGG;IACH,QAAQ,EAAE,CAAC,IAAI,EAAE;QACf,OAAO,EAAE,OAAO,CAAC;QACjB,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC;QACf,MAAM,EAAE,KAAK,GAAG,IAAI,CAAC;QACrB;;;;WAIG;QACH,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;KACjC,KAAK,SAAS,CAAC;CACjB,CAAC;AAEF,wBAAgB,kBAAkB,CAAC,CAAC,GAAG,OAAO,EAAE,EAC9C,OAAO,EACP,MAAM,EACN,IAAI,EACJ,UAAU,EACV,WAAW,EACX,IAAI,EACJ,mBAAmB,EACnB,QAAQ,GACT,EAAE,uBAAuB,CAAC,CAAC,CAAC,2CAqI5B"}
@@ -4,6 +4,7 @@ exports.SERVICE_BASE_URL = exports.Method = exports.Service = void 0;
4
4
  exports.RopeGeoHttpRequest = RopeGeoHttpRequest;
5
5
  const jsx_runtime_1 = require("react/jsx-runtime");
6
6
  const react_1 = require("react");
7
+ const network_1 = require("../helpers/network");
7
8
  const models_1 = require("../models");
8
9
  exports.Service = {
9
10
  WEBSCRAPER: "WEBSCRAPER",
@@ -39,10 +40,11 @@ function buildUrl(baseUrl, path, pathParams, queryParams) {
39
40
  }
40
41
  return url.toString();
41
42
  }
42
- function RopeGeoHttpRequest({ service, method, path, pathParams, queryParams, body, children, }) {
43
+ function RopeGeoHttpRequest({ service, method, path, pathParams, queryParams, body, timeoutAfterSeconds, children, }) {
43
44
  const [loading, setLoading] = (0, react_1.useState)(true);
44
45
  const [data, setData] = (0, react_1.useState)(null);
45
46
  const [errors, setErrors] = (0, react_1.useState)(null);
47
+ const [timeoutCountdown, setTimeoutCountdown] = (0, react_1.useState)(null);
46
48
  const pathParamsKey = JSON.stringify(pathParams ?? null);
47
49
  const queryParamsKey = JSON.stringify(queryParams ?? null);
48
50
  const bodyKey = body === undefined || body === null
@@ -52,15 +54,36 @@ function RopeGeoHttpRequest({ service, method, path, pathParams, queryParams, bo
52
54
  : body;
53
55
  (0, react_1.useEffect)(() => {
54
56
  let cancelled = false;
55
- const baseUrl = exports.SERVICE_BASE_URL[service];
56
- const url = buildUrl(baseUrl, path, pathParams, queryParams);
57
+ const abortController = new AbortController();
58
+ const timedOutRef = { current: false };
59
+ const requestStartedAt = Date.now();
60
+ const timeoutMs = (0, network_1.resolveRequestTimeoutMs)(timeoutAfterSeconds);
57
61
  setLoading(true);
58
62
  setErrors(null);
63
+ setTimeoutCountdown(null);
64
+ const policyDispose = (0, network_1.installNetworkRequestPolicyTimers)(requestStartedAt, timeoutMs, {
65
+ isActive: () => !cancelled,
66
+ onTimeoutCountdown: (seconds) => {
67
+ if (!cancelled)
68
+ setTimeoutCountdown(seconds);
69
+ },
70
+ onClearTimeoutCountdown: () => {
71
+ if (!cancelled)
72
+ setTimeoutCountdown(null);
73
+ },
74
+ onHardTimeout: () => {
75
+ timedOutRef.current = true;
76
+ abortController.abort();
77
+ },
78
+ });
79
+ const baseUrl = exports.SERVICE_BASE_URL[service];
80
+ const url = buildUrl(baseUrl, path, pathParams, queryParams);
59
81
  const init = {
60
82
  method,
61
83
  headers: {
62
84
  "Content-Type": "application/json",
63
85
  },
86
+ signal: abortController.signal,
64
87
  };
65
88
  if (body != null && (method === exports.Method.POST || method === exports.Method.PUT)) {
66
89
  init.body = JSON.stringify(body);
@@ -101,22 +124,39 @@ function RopeGeoHttpRequest({ service, method, path, pathParams, queryParams, bo
101
124
  }
102
125
  })
103
126
  .catch((err) => {
104
- if (!cancelled) {
105
- console.error("[RopeGeoHttpRequest] Request failed", {
106
- url,
107
- error: err instanceof Error ? err.message : String(err),
108
- });
109
- setErrors(err instanceof Error ? err : new Error(String(err)));
127
+ if (cancelled)
128
+ return;
129
+ if (timedOutRef.current) {
130
+ setErrors(new Error(network_1.NETWORK_REQUEST_TIMED_OUT_MESSAGE));
110
131
  setData(null);
132
+ return;
111
133
  }
134
+ if ((0, network_1.isAbortError)(err))
135
+ return;
136
+ console.error("[RopeGeoHttpRequest] Request failed", {
137
+ url,
138
+ error: err instanceof Error ? err.message : String(err),
139
+ });
140
+ setErrors(err instanceof Error ? err : new Error(String(err)));
141
+ setData(null);
112
142
  })
113
143
  .finally(() => {
114
- if (!cancelled)
144
+ policyDispose();
145
+ if (!cancelled) {
146
+ setTimeoutCountdown(null);
115
147
  setLoading(false);
148
+ }
116
149
  });
117
150
  return () => {
118
151
  cancelled = true;
152
+ policyDispose();
153
+ abortController.abort();
119
154
  };
120
- }, [service, method, path, pathParamsKey, queryParamsKey, bodyKey]);
121
- return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: children({ loading, data, errors }) });
155
+ }, [service, method, path, pathParamsKey, queryParamsKey, bodyKey, timeoutAfterSeconds]);
156
+ return ((0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: children({
157
+ loading,
158
+ data,
159
+ errors,
160
+ timeoutCountdown,
161
+ }) }));
122
162
  }
@@ -13,6 +13,11 @@ export type RopeGeoPaginationHttpRequestProps<T = unknown> = {
13
13
  * @default 10
14
14
  */
15
15
  batchSize?: number;
16
+ /**
17
+ * Per-request deadline in seconds (page 1 countdown + each later page fetch). Defaults to the
18
+ * package default when omitted.
19
+ */
20
+ timeoutAfterSeconds?: number;
16
21
  children: (args: {
17
22
  loading: boolean;
18
23
  received: number;
@@ -24,6 +29,8 @@ export type RopeGeoPaginationHttpRequestProps<T = unknown> = {
24
29
  data: T[] | null;
25
30
  /** Set when `data` is `null` after a terminal failure; cleared only when all pages succeed. */
26
31
  errors: Error | null;
32
+ /** Timeout countdown for the first page request only; `null` when not applicable. */
33
+ timeoutCountdown: number | null;
27
34
  }) => ReactNode;
28
35
  };
29
36
  /**
@@ -32,5 +39,5 @@ export type RopeGeoPaginationHttpRequestProps<T = unknown> = {
32
39
  * {@link PaginationResults.fromResponseBody}. Final `data` is pages concatenated in page order.
33
40
  * In-flight requests use one {@link AbortController}: unmount or any failure aborts the rest.
34
41
  */
35
- export declare function RopeGeoPaginationHttpRequest<T = unknown>({ service, method, path, pathParams, queryParams, batchSize, children, }: RopeGeoPaginationHttpRequestProps<T>): import("react/jsx-runtime").JSX.Element;
42
+ export declare function RopeGeoPaginationHttpRequest<T = unknown>({ service, method, path, pathParams, queryParams, batchSize, timeoutAfterSeconds, children, }: RopeGeoPaginationHttpRequestProps<T>): import("react/jsx-runtime").JSX.Element;
36
43
  //# sourceMappingURL=RopeGeoPaginationHttpRequest.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"RopeGeoPaginationHttpRequest.d.ts","sourceRoot":"","sources":["../../src/components/RopeGeoPaginationHttpRequest.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,EACL,KAAK,gBAAgB,EAEtB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,MAAM,EAAE,OAAO,EAAoB,MAAM,sBAAsB,CAAC;AAoEzE,MAAM,MAAM,iCAAiC,CAAC,CAAC,GAAG,OAAO,IAAI;IAC3D,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,CAAC,OAAO,MAAM,CAAC,CAAC,MAAM,OAAO,MAAM,CAAC,CAAC;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,WAAW,EAAE,gBAAgB,CAAC;IAC9B;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,CAAC,IAAI,EAAE;QACf,OAAO,EAAE,OAAO,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;QACrB;;;WAGG;QACH,IAAI,EAAE,CAAC,EAAE,GAAG,IAAI,CAAC;QACjB,+FAA+F;QAC/F,MAAM,EAAE,KAAK,GAAG,IAAI,CAAC;KACtB,KAAK,SAAS,CAAC;CACjB,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,4BAA4B,CAAC,CAAC,GAAG,OAAO,EAAE,EACxD,OAAO,EACP,MAAmB,EACnB,IAAI,EACJ,UAAU,EACV,WAAW,EACX,SAAc,EACd,QAAQ,GACT,EAAE,iCAAiC,CAAC,CAAC,CAAC,2CAwKtC"}
1
+ {"version":3,"file":"RopeGeoPaginationHttpRequest.d.ts","sourceRoot":"","sources":["../../src/components/RopeGeoPaginationHttpRequest.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AASvC,OAAO,EACL,KAAK,gBAAgB,EAEtB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,MAAM,EAAE,OAAO,EAAoB,MAAM,sBAAsB,CAAC;AA8DzE,MAAM,MAAM,iCAAiC,CAAC,CAAC,GAAG,OAAO,IAAI;IAC3D,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,CAAC,OAAO,MAAM,CAAC,CAAC,MAAM,OAAO,MAAM,CAAC,CAAC;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,WAAW,EAAE,gBAAgB,CAAC;IAC9B;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,EAAE,CAAC,IAAI,EAAE;QACf,OAAO,EAAE,OAAO,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;QACrB;;;WAGG;QACH,IAAI,EAAE,CAAC,EAAE,GAAG,IAAI,CAAC;QACjB,+FAA+F;QAC/F,MAAM,EAAE,KAAK,GAAG,IAAI,CAAC;QACrB,qFAAqF;QACrF,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;KACjC,KAAK,SAAS,CAAC;CACjB,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,4BAA4B,CAAC,CAAC,GAAG,OAAO,EAAE,EACxD,OAAO,EACP,MAAmB,EACnB,IAAI,EACJ,UAAU,EACV,WAAW,EACX,SAAc,EACd,mBAAmB,EACnB,QAAQ,GACT,EAAE,iCAAiC,CAAC,CAAC,CAAC,2CA8NtC"}
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.RopeGeoPaginationHttpRequest = RopeGeoPaginationHttpRequest;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  const react_1 = require("react");
6
+ const network_1 = require("../helpers/network");
6
7
  const models_1 = require("../models");
7
8
  const RopeGeoHttpRequest_1 = require("./RopeGeoHttpRequest");
8
9
  const PATH_PARAM_PATTERN = /:([a-zA-Z0-9_]+)/g;
@@ -29,13 +30,6 @@ function getResponseBody(raw) {
29
30
  }
30
31
  return raw;
31
32
  }
32
- function isAbortError(e) {
33
- if (e instanceof DOMException && e.name === "AbortError")
34
- return true;
35
- if (e instanceof Error && e.name === "AbortError")
36
- return true;
37
- return false;
38
- }
39
33
  function sumReceived(pagesByNum) {
40
34
  let sum = 0;
41
35
  for (const p of pagesByNum.values()) {
@@ -63,12 +57,13 @@ function concatPaginationResultItemsSorted(pagesByNum) {
63
57
  * {@link PaginationResults.fromResponseBody}. Final `data` is pages concatenated in page order.
64
58
  * In-flight requests use one {@link AbortController}: unmount or any failure aborts the rest.
65
59
  */
66
- function RopeGeoPaginationHttpRequest({ service, method = RopeGeoHttpRequest_1.Method.GET, path, pathParams, queryParams, batchSize = 10, children, }) {
60
+ function RopeGeoPaginationHttpRequest({ service, method = RopeGeoHttpRequest_1.Method.GET, path, pathParams, queryParams, batchSize = 10, timeoutAfterSeconds, children, }) {
67
61
  const [loading, setLoading] = (0, react_1.useState)(true);
68
62
  const [received, setReceived] = (0, react_1.useState)(0);
69
63
  const [total, setTotal] = (0, react_1.useState)(null);
70
64
  const [data, setData] = (0, react_1.useState)(null);
71
65
  const [errors, setErrors] = (0, react_1.useState)(null);
66
+ const [timeoutCountdown, setTimeoutCountdown] = (0, react_1.useState)(null);
72
67
  const pathParamsKey = JSON.stringify(pathParams ?? null);
73
68
  const queryParamsKey = queryParams.toQueryString();
74
69
  const effectiveBatch = Math.max(1, Math.floor(batchSize));
@@ -76,17 +71,18 @@ function RopeGeoPaginationHttpRequest({ service, method = RopeGeoHttpRequest_1.M
76
71
  let cancelled = false;
77
72
  const abortController = new AbortController();
78
73
  const { signal } = abortController;
74
+ const timeoutMs = (0, network_1.resolveRequestTimeoutMs)(timeoutAfterSeconds);
79
75
  setLoading(true);
80
76
  setReceived(0);
81
77
  setTotal(null);
82
78
  setData(null);
83
79
  setErrors(null);
80
+ setTimeoutCountdown(null);
84
81
  const baseUrl = RopeGeoHttpRequest_1.SERVICE_BASE_URL[service];
85
82
  const resolvedPath = resolvePath(path, pathParams);
86
83
  const baseInit = {
87
84
  method,
88
85
  headers: { "Content-Type": "application/json" },
89
- signal,
90
86
  };
91
87
  (async () => {
92
88
  const pagesByNum = new Map();
@@ -98,39 +94,87 @@ function RopeGeoPaginationHttpRequest({ service, method = RopeGeoHttpRequest_1.M
98
94
  ? `${resolvedPath}?${queryString}`
99
95
  : resolvedPath;
100
96
  const url = new URL(fullPath, baseUrl).toString();
101
- const res = await fetch(url, baseInit);
102
- const text = await res.text();
103
- if (!res.ok) {
104
- abortController.abort();
105
- throw new Error(`HTTP ${res.status}: ${text || res.statusText}`);
106
- }
107
- if (text.length === 0) {
108
- abortController.abort();
109
- throw new Error("Empty response body");
97
+ let pageOnePolicyDispose = null;
98
+ const pageOneTimedOutRef = { current: false };
99
+ if (pageNum === 1) {
100
+ const startedAt = Date.now();
101
+ pageOnePolicyDispose = (0, network_1.installNetworkRequestPolicyTimers)(startedAt, timeoutMs, {
102
+ isActive: () => !cancelled,
103
+ onTimeoutCountdown: (seconds) => {
104
+ if (!cancelled)
105
+ setTimeoutCountdown(seconds);
106
+ },
107
+ onClearTimeoutCountdown: () => {
108
+ if (!cancelled)
109
+ setTimeoutCountdown(null);
110
+ },
111
+ onHardTimeout: () => {
112
+ pageOneTimedOutRef.current = true;
113
+ abortController.abort();
114
+ },
115
+ });
110
116
  }
111
- let raw;
112
- try {
113
- raw = JSON.parse(text);
117
+ let merged = null;
118
+ let signalForFetch;
119
+ if (pageNum === 1) {
120
+ signalForFetch = signal;
114
121
  }
115
- catch (parseError) {
116
- abortController.abort();
117
- console.error("[RopeGeoPaginationHttpRequest] Invalid JSON response", {
118
- url,
119
- status: res.status,
120
- responseText: text.slice(0, 500),
121
- parseError: parseError instanceof Error
122
- ? parseError.message
123
- : String(parseError),
124
- });
125
- throw new Error("Invalid JSON response");
122
+ else {
123
+ merged = (0, network_1.mergeParentSignalWithDeadline)(signal, timeoutMs);
124
+ signalForFetch = merged.signal;
126
125
  }
127
126
  try {
128
- return models_1.PaginationResults.fromResponseBody(getResponseBody(raw));
127
+ const res = await fetch(url, { ...baseInit, signal: signalForFetch });
128
+ const text = await res.text();
129
+ if (!res.ok) {
130
+ abortController.abort();
131
+ throw new Error(`HTTP ${res.status}: ${text || res.statusText}`);
132
+ }
133
+ if (text.length === 0) {
134
+ abortController.abort();
135
+ throw new Error("Empty response body");
136
+ }
137
+ let raw;
138
+ try {
139
+ raw = JSON.parse(text);
140
+ }
141
+ catch (parseError) {
142
+ abortController.abort();
143
+ console.error("[RopeGeoPaginationHttpRequest] Invalid JSON response", {
144
+ url,
145
+ status: res.status,
146
+ responseText: text.slice(0, 500),
147
+ parseError: parseError instanceof Error
148
+ ? parseError.message
149
+ : String(parseError),
150
+ });
151
+ throw new Error("Invalid JSON response");
152
+ }
153
+ try {
154
+ return models_1.PaginationResults.fromResponseBody(getResponseBody(raw));
155
+ }
156
+ catch (e) {
157
+ abortController.abort();
158
+ const msg = e instanceof Error ? e.message : String(e);
159
+ throw new Error(msg);
160
+ }
161
+ }
162
+ catch (err) {
163
+ if (pageNum !== 1 && merged != null && merged.consumeDidTimeout()) {
164
+ abortController.abort();
165
+ throw new Error(network_1.NETWORK_REQUEST_TIMED_OUT_MESSAGE);
166
+ }
167
+ if (pageNum === 1 && pageOneTimedOutRef.current) {
168
+ throw new Error(network_1.NETWORK_REQUEST_TIMED_OUT_MESSAGE);
169
+ }
170
+ throw err;
129
171
  }
130
- catch (e) {
131
- abortController.abort();
132
- const msg = e instanceof Error ? e.message : String(e);
133
- throw new Error(msg);
172
+ finally {
173
+ pageOnePolicyDispose?.();
174
+ merged?.dispose();
175
+ if (pageNum === 1 && !cancelled) {
176
+ setTimeoutCountdown(null);
177
+ }
134
178
  }
135
179
  };
136
180
  try {
@@ -181,7 +225,7 @@ function RopeGeoPaginationHttpRequest({ service, method = RopeGeoHttpRequest_1.M
181
225
  setErrors(null);
182
226
  }
183
227
  catch (err) {
184
- if (cancelled || isAbortError(err))
228
+ if (cancelled || (0, network_1.isAbortError)(err))
185
229
  return;
186
230
  console.error("[RopeGeoPaginationHttpRequest] Request failed", {
187
231
  error: err instanceof Error ? err.message : String(err),
@@ -206,6 +250,7 @@ function RopeGeoPaginationHttpRequest({ service, method = RopeGeoHttpRequest_1.M
206
250
  queryParamsKey,
207
251
  queryParams,
208
252
  effectiveBatch,
253
+ timeoutAfterSeconds,
209
254
  ]);
210
255
  return ((0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: children({
211
256
  loading,
@@ -213,5 +258,6 @@ function RopeGeoPaginationHttpRequest({ service, method = RopeGeoHttpRequest_1.M
213
258
  total,
214
259
  data,
215
260
  errors,
261
+ timeoutCountdown,
216
262
  }) }));
217
263
  }
@@ -1,6 +1,7 @@
1
1
  export { httpRequest } from './httpRequest';
2
2
  export { default as ProgressLogger } from './progressLogger';
3
3
  export { timeoutAfter } from './timeoutAfter';
4
+ export { NETWORK_REQUEST_DEFAULT_TIMEOUT_SECONDS, NETWORK_REQUEST_HARD_TIMEOUT_MS, NETWORK_REQUEST_SLOW_THRESHOLD_MS, NETWORK_REQUEST_TIMED_OUT_MESSAGE, installNetworkRequestPolicyTimers, isAbortError, isNetworkRequestTimeoutError, mergeParentSignalWithDeadline, resolveRequestTimeoutMs, type MergedDeadlineHandles, type NetworkRequestPolicyTimerCallbacks, } from './network';
4
5
  export { getS3Client, resetS3ClientForTests } from './s3/getS3Client';
5
6
  export { default as listS3Objects, type S3ObjectEntry } from './s3/listS3Objects';
6
7
  export { default as getS3Object, type GetS3ObjectResult } from './s3/getS3Object';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/helpers/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9C,OAAO,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AACtE,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,KAAK,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAClF,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,KAAK,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAClF,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAEvD,OAAO,EAAE,YAAY,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAC1E,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACjE,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AACrE,OAAO,EAAE,OAAO,IAAI,iCAAiC,EAAE,MAAM,yCAAyC,CAAC;AAEvG,OAAO,EAAE,4BAA4B,EAAE,MAAM,2CAA2C,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/helpers/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EACH,uCAAuC,EACvC,+BAA+B,EAC/B,iCAAiC,EACjC,iCAAiC,EACjC,iCAAiC,EACjC,YAAY,EACZ,4BAA4B,EAC5B,6BAA6B,EAC7B,uBAAuB,EACvB,KAAK,qBAAqB,EAC1B,KAAK,kCAAkC,GAC1C,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AACtE,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,KAAK,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAClF,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,KAAK,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAClF,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAEvD,OAAO,EAAE,YAAY,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAC1E,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACjE,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AACrE,OAAO,EAAE,OAAO,IAAI,iCAAiC,EAAE,MAAM,yCAAyC,CAAC;AAEvG,OAAO,EAAE,4BAA4B,EAAE,MAAM,2CAA2C,CAAC"}
@@ -3,13 +3,23 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.createCloudFrontInvalidation = exports.changeSQSMessageVisibilityTimeout = exports.deleteSQSMessage = exports.sendSQSMessage = exports.resetSQSClientForTests = exports.getSQSClient = exports.replaceS3Folder = exports.putS3Folder = exports.listS3Folder = exports.deleteS3Object = exports.putS3Object = exports.getS3Object = exports.listS3Objects = exports.resetS3ClientForTests = exports.getS3Client = exports.timeoutAfter = exports.ProgressLogger = exports.httpRequest = void 0;
6
+ exports.createCloudFrontInvalidation = exports.changeSQSMessageVisibilityTimeout = exports.deleteSQSMessage = exports.sendSQSMessage = exports.resetSQSClientForTests = exports.getSQSClient = exports.replaceS3Folder = exports.putS3Folder = exports.listS3Folder = exports.deleteS3Object = exports.putS3Object = exports.getS3Object = exports.listS3Objects = exports.resetS3ClientForTests = exports.getS3Client = exports.resolveRequestTimeoutMs = exports.mergeParentSignalWithDeadline = exports.isNetworkRequestTimeoutError = exports.isAbortError = exports.installNetworkRequestPolicyTimers = exports.NETWORK_REQUEST_TIMED_OUT_MESSAGE = exports.NETWORK_REQUEST_SLOW_THRESHOLD_MS = exports.NETWORK_REQUEST_HARD_TIMEOUT_MS = exports.NETWORK_REQUEST_DEFAULT_TIMEOUT_SECONDS = exports.timeoutAfter = exports.ProgressLogger = exports.httpRequest = void 0;
7
7
  var httpRequest_1 = require("./httpRequest");
8
8
  Object.defineProperty(exports, "httpRequest", { enumerable: true, get: function () { return httpRequest_1.httpRequest; } });
9
9
  var progressLogger_1 = require("./progressLogger");
10
10
  Object.defineProperty(exports, "ProgressLogger", { enumerable: true, get: function () { return __importDefault(progressLogger_1).default; } });
11
11
  var timeoutAfter_1 = require("./timeoutAfter");
12
12
  Object.defineProperty(exports, "timeoutAfter", { enumerable: true, get: function () { return timeoutAfter_1.timeoutAfter; } });
13
+ var network_1 = require("./network");
14
+ Object.defineProperty(exports, "NETWORK_REQUEST_DEFAULT_TIMEOUT_SECONDS", { enumerable: true, get: function () { return network_1.NETWORK_REQUEST_DEFAULT_TIMEOUT_SECONDS; } });
15
+ Object.defineProperty(exports, "NETWORK_REQUEST_HARD_TIMEOUT_MS", { enumerable: true, get: function () { return network_1.NETWORK_REQUEST_HARD_TIMEOUT_MS; } });
16
+ Object.defineProperty(exports, "NETWORK_REQUEST_SLOW_THRESHOLD_MS", { enumerable: true, get: function () { return network_1.NETWORK_REQUEST_SLOW_THRESHOLD_MS; } });
17
+ Object.defineProperty(exports, "NETWORK_REQUEST_TIMED_OUT_MESSAGE", { enumerable: true, get: function () { return network_1.NETWORK_REQUEST_TIMED_OUT_MESSAGE; } });
18
+ Object.defineProperty(exports, "installNetworkRequestPolicyTimers", { enumerable: true, get: function () { return network_1.installNetworkRequestPolicyTimers; } });
19
+ Object.defineProperty(exports, "isAbortError", { enumerable: true, get: function () { return network_1.isAbortError; } });
20
+ Object.defineProperty(exports, "isNetworkRequestTimeoutError", { enumerable: true, get: function () { return network_1.isNetworkRequestTimeoutError; } });
21
+ Object.defineProperty(exports, "mergeParentSignalWithDeadline", { enumerable: true, get: function () { return network_1.mergeParentSignalWithDeadline; } });
22
+ Object.defineProperty(exports, "resolveRequestTimeoutMs", { enumerable: true, get: function () { return network_1.resolveRequestTimeoutMs; } });
13
23
  var getS3Client_1 = require("./s3/getS3Client");
14
24
  Object.defineProperty(exports, "getS3Client", { enumerable: true, get: function () { return getS3Client_1.getS3Client; } });
15
25
  Object.defineProperty(exports, "resetS3ClientForTests", { enumerable: true, get: function () { return getS3Client_1.resetS3ClientForTests; } });
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Network-only helpers safe for browsers and React Native (no Node `fs`, no AWS S3/SQS in this graph).
3
+ * Published as `ropegeo-common/helpers/network`. Prefer this entry over `ropegeo-common/helpers` in
4
+ * Metro/RN bundles so the full helpers barrel (S3 folder upload, etc.) is not resolved.
5
+ */
6
+ export { NETWORK_REQUEST_DEFAULT_TIMEOUT_SECONDS, NETWORK_REQUEST_HARD_TIMEOUT_MS, NETWORK_REQUEST_SLOW_THRESHOLD_MS, NETWORK_REQUEST_TIMED_OUT_MESSAGE, installNetworkRequestPolicyTimers, isAbortError, isNetworkRequestTimeoutError, mergeParentSignalWithDeadline, resolveRequestTimeoutMs, type MergedDeadlineHandles, type NetworkRequestPolicyTimerCallbacks, } from "./networkRequestPolicy";
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/helpers/network/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACL,uCAAuC,EACvC,+BAA+B,EAC/B,iCAAiC,EACjC,iCAAiC,EACjC,iCAAiC,EACjC,YAAY,EACZ,4BAA4B,EAC5B,6BAA6B,EAC7B,uBAAuB,EACvB,KAAK,qBAAqB,EAC1B,KAAK,kCAAkC,GACxC,MAAM,wBAAwB,CAAC"}
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ /**
3
+ * Network-only helpers safe for browsers and React Native (no Node `fs`, no AWS S3/SQS in this graph).
4
+ * Published as `ropegeo-common/helpers/network`. Prefer this entry over `ropegeo-common/helpers` in
5
+ * Metro/RN bundles so the full helpers barrel (S3 folder upload, etc.) is not resolved.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.resolveRequestTimeoutMs = exports.mergeParentSignalWithDeadline = exports.isNetworkRequestTimeoutError = exports.isAbortError = exports.installNetworkRequestPolicyTimers = exports.NETWORK_REQUEST_TIMED_OUT_MESSAGE = exports.NETWORK_REQUEST_SLOW_THRESHOLD_MS = exports.NETWORK_REQUEST_HARD_TIMEOUT_MS = exports.NETWORK_REQUEST_DEFAULT_TIMEOUT_SECONDS = void 0;
9
+ var networkRequestPolicy_1 = require("./networkRequestPolicy");
10
+ Object.defineProperty(exports, "NETWORK_REQUEST_DEFAULT_TIMEOUT_SECONDS", { enumerable: true, get: function () { return networkRequestPolicy_1.NETWORK_REQUEST_DEFAULT_TIMEOUT_SECONDS; } });
11
+ Object.defineProperty(exports, "NETWORK_REQUEST_HARD_TIMEOUT_MS", { enumerable: true, get: function () { return networkRequestPolicy_1.NETWORK_REQUEST_HARD_TIMEOUT_MS; } });
12
+ Object.defineProperty(exports, "NETWORK_REQUEST_SLOW_THRESHOLD_MS", { enumerable: true, get: function () { return networkRequestPolicy_1.NETWORK_REQUEST_SLOW_THRESHOLD_MS; } });
13
+ Object.defineProperty(exports, "NETWORK_REQUEST_TIMED_OUT_MESSAGE", { enumerable: true, get: function () { return networkRequestPolicy_1.NETWORK_REQUEST_TIMED_OUT_MESSAGE; } });
14
+ Object.defineProperty(exports, "installNetworkRequestPolicyTimers", { enumerable: true, get: function () { return networkRequestPolicy_1.installNetworkRequestPolicyTimers; } });
15
+ Object.defineProperty(exports, "isAbortError", { enumerable: true, get: function () { return networkRequestPolicy_1.isAbortError; } });
16
+ Object.defineProperty(exports, "isNetworkRequestTimeoutError", { enumerable: true, get: function () { return networkRequestPolicy_1.isNetworkRequestTimeoutError; } });
17
+ Object.defineProperty(exports, "mergeParentSignalWithDeadline", { enumerable: true, get: function () { return networkRequestPolicy_1.mergeParentSignalWithDeadline; } });
18
+ Object.defineProperty(exports, "resolveRequestTimeoutMs", { enumerable: true, get: function () { return networkRequestPolicy_1.resolveRequestTimeoutMs; } });
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Defaults for HTTP wrappers and other network clients (see plan: mobile-network-resilience).
3
+ * Apps may pass their own deadline via `timeoutAfterSeconds` on components; when omitted,
4
+ * {@link NETWORK_REQUEST_DEFAULT_TIMEOUT_SECONDS} applies.
5
+ */
6
+ export declare const NETWORK_REQUEST_DEFAULT_TIMEOUT_SECONDS = 30;
7
+ export declare const NETWORK_REQUEST_HARD_TIMEOUT_MS: number;
8
+ /** Use when showing “slow network” UI before the full countdown is surfaced (e.g. toasts). */
9
+ export declare const NETWORK_REQUEST_SLOW_THRESHOLD_MS = 15000;
10
+ /** Use this exact message so callers can detect timeout vs other failures. */
11
+ export declare const NETWORK_REQUEST_TIMED_OUT_MESSAGE = "Network request timed out";
12
+ export declare function isNetworkRequestTimeoutError(e: unknown): boolean;
13
+ export declare function isAbortError(e: unknown): boolean;
14
+ /** Milliseconds for `timeoutAfterSeconds` on request components, or the package default. */
15
+ export declare function resolveRequestTimeoutMs(timeoutAfterSeconds?: number): number;
16
+ export type NetworkRequestPolicyTimerCallbacks = {
17
+ isActive: () => boolean;
18
+ onTimeoutCountdown: (secondsRemaining: number) => void;
19
+ onClearTimeoutCountdown: () => void;
20
+ onHardTimeout: () => void;
21
+ };
22
+ /**
23
+ * Emits whole seconds until {@link hardTimeoutMs} on install and roughly every second after
24
+ * (from ~full duration down to 1), then fires {@link NetworkRequestPolicyTimerCallbacks.onHardTimeout}
25
+ * at the deadline. Clears countdown on hard timeout or cleanup. Does not emit 0.
26
+ */
27
+ export declare function installNetworkRequestPolicyTimers(requestStartedAtMs: number, hardTimeoutMs: number, callbacks: NetworkRequestPolicyTimerCallbacks): () => void;
28
+ export type MergedDeadlineHandles = {
29
+ signal: AbortSignal;
30
+ dispose: () => void;
31
+ consumeDidTimeout: () => boolean;
32
+ };
33
+ /**
34
+ * Aborts `signal` when `parentSignal` aborts or when `deadlineMs` elapses (whichever comes first).
35
+ * Call `dispose()` after the request settles to clear the timer; then `consumeDidTimeout()` to see
36
+ * whether the deadline fired (vs parent abort only).
37
+ */
38
+ export declare function mergeParentSignalWithDeadline(parentSignal: AbortSignal, deadlineMs?: number): MergedDeadlineHandles;
39
+ //# sourceMappingURL=networkRequestPolicy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"networkRequestPolicy.d.ts","sourceRoot":"","sources":["../../../src/helpers/network/networkRequestPolicy.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,eAAO,MAAM,uCAAuC,KAAK,CAAC;AAE1D,eAAO,MAAM,+BAA+B,QACK,CAAC;AAElD,8FAA8F;AAC9F,eAAO,MAAM,iCAAiC,QAAS,CAAC;AAExD,8EAA8E;AAC9E,eAAO,MAAM,iCAAiC,8BAA8B,CAAC;AAE7E,wBAAgB,4BAA4B,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAEhE;AAED,wBAAgB,YAAY,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAIhD;AAED,4FAA4F;AAC5F,wBAAgB,uBAAuB,CAAC,mBAAmB,CAAC,EAAE,MAAM,GAAG,MAAM,CAS5E;AAED,MAAM,MAAM,kCAAkC,GAAG;IAC/C,QAAQ,EAAE,MAAM,OAAO,CAAC;IACxB,kBAAkB,EAAE,CAAC,gBAAgB,EAAE,MAAM,KAAK,IAAI,CAAC;IACvD,uBAAuB,EAAE,MAAM,IAAI,CAAC;IACpC,aAAa,EAAE,MAAM,IAAI,CAAC;CAC3B,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,iCAAiC,CAC/C,kBAAkB,EAAE,MAAM,EAC1B,aAAa,EAAE,MAAM,EACrB,SAAS,EAAE,kCAAkC,GAC5C,MAAM,IAAI,CAuCZ;AAED,MAAM,MAAM,qBAAqB,GAAG;IAClC,MAAM,EAAE,WAAW,CAAC;IACpB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,iBAAiB,EAAE,MAAM,OAAO,CAAC;CAClC,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,6BAA6B,CAC3C,YAAY,EAAE,WAAW,EACzB,UAAU,GAAE,MAAwC,GACnD,qBAAqB,CAqCvB"}
@@ -0,0 +1,126 @@
1
+ "use strict";
2
+ /**
3
+ * Defaults for HTTP wrappers and other network clients (see plan: mobile-network-resilience).
4
+ * Apps may pass their own deadline via `timeoutAfterSeconds` on components; when omitted,
5
+ * {@link NETWORK_REQUEST_DEFAULT_TIMEOUT_SECONDS} applies.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.NETWORK_REQUEST_TIMED_OUT_MESSAGE = exports.NETWORK_REQUEST_SLOW_THRESHOLD_MS = exports.NETWORK_REQUEST_HARD_TIMEOUT_MS = exports.NETWORK_REQUEST_DEFAULT_TIMEOUT_SECONDS = void 0;
9
+ exports.isNetworkRequestTimeoutError = isNetworkRequestTimeoutError;
10
+ exports.isAbortError = isAbortError;
11
+ exports.resolveRequestTimeoutMs = resolveRequestTimeoutMs;
12
+ exports.installNetworkRequestPolicyTimers = installNetworkRequestPolicyTimers;
13
+ exports.mergeParentSignalWithDeadline = mergeParentSignalWithDeadline;
14
+ exports.NETWORK_REQUEST_DEFAULT_TIMEOUT_SECONDS = 30;
15
+ exports.NETWORK_REQUEST_HARD_TIMEOUT_MS = exports.NETWORK_REQUEST_DEFAULT_TIMEOUT_SECONDS * 1000;
16
+ /** Use when showing “slow network” UI before the full countdown is surfaced (e.g. toasts). */
17
+ exports.NETWORK_REQUEST_SLOW_THRESHOLD_MS = 15000;
18
+ /** Use this exact message so callers can detect timeout vs other failures. */
19
+ exports.NETWORK_REQUEST_TIMED_OUT_MESSAGE = "Network request timed out";
20
+ function isNetworkRequestTimeoutError(e) {
21
+ return e instanceof Error && e.message === exports.NETWORK_REQUEST_TIMED_OUT_MESSAGE;
22
+ }
23
+ function isAbortError(e) {
24
+ if (e instanceof DOMException && e.name === "AbortError")
25
+ return true;
26
+ if (e instanceof Error && e.name === "AbortError")
27
+ return true;
28
+ return false;
29
+ }
30
+ /** Milliseconds for `timeoutAfterSeconds` on request components, or the package default. */
31
+ function resolveRequestTimeoutMs(timeoutAfterSeconds) {
32
+ if (timeoutAfterSeconds != null &&
33
+ Number.isFinite(timeoutAfterSeconds) &&
34
+ timeoutAfterSeconds > 0) {
35
+ return Math.floor(timeoutAfterSeconds) * 1000;
36
+ }
37
+ return exports.NETWORK_REQUEST_HARD_TIMEOUT_MS;
38
+ }
39
+ /**
40
+ * Emits whole seconds until {@link hardTimeoutMs} on install and roughly every second after
41
+ * (from ~full duration down to 1), then fires {@link NetworkRequestPolicyTimerCallbacks.onHardTimeout}
42
+ * at the deadline. Clears countdown on hard timeout or cleanup. Does not emit 0.
43
+ */
44
+ function installNetworkRequestPolicyTimers(requestStartedAtMs, hardTimeoutMs, callbacks) {
45
+ let intervalId = null;
46
+ const clearCountdown = () => {
47
+ if (intervalId != null) {
48
+ clearInterval(intervalId);
49
+ intervalId = null;
50
+ }
51
+ callbacks.onClearTimeoutCountdown();
52
+ };
53
+ const tick = () => {
54
+ if (!callbacks.isActive())
55
+ return;
56
+ const elapsed = Date.now() - requestStartedAtMs;
57
+ const remainingMs = hardTimeoutMs - elapsed;
58
+ if (remainingMs <= 500)
59
+ return;
60
+ const secs = Math.max(1, Math.ceil(remainingMs / 1000));
61
+ callbacks.onTimeoutCountdown(secs);
62
+ };
63
+ tick();
64
+ intervalId = setInterval(tick, 1000);
65
+ const hardId = setTimeout(() => {
66
+ clearCountdown();
67
+ if (callbacks.isActive())
68
+ callbacks.onHardTimeout();
69
+ }, hardTimeoutMs);
70
+ let disposed = false;
71
+ return () => {
72
+ if (disposed)
73
+ return;
74
+ disposed = true;
75
+ clearTimeout(hardId);
76
+ if (intervalId != null) {
77
+ clearInterval(intervalId);
78
+ intervalId = null;
79
+ }
80
+ callbacks.onClearTimeoutCountdown();
81
+ };
82
+ }
83
+ /**
84
+ * Aborts `signal` when `parentSignal` aborts or when `deadlineMs` elapses (whichever comes first).
85
+ * Call `dispose()` after the request settles to clear the timer; then `consumeDidTimeout()` to see
86
+ * whether the deadline fired (vs parent abort only).
87
+ */
88
+ function mergeParentSignalWithDeadline(parentSignal, deadlineMs = exports.NETWORK_REQUEST_HARD_TIMEOUT_MS) {
89
+ const controller = new AbortController();
90
+ let didTimeout = false;
91
+ const onParentAbort = () => {
92
+ clearTimeout(timeoutId);
93
+ if (!controller.signal.aborted)
94
+ controller.abort();
95
+ };
96
+ let timeoutId = setTimeout(() => {
97
+ didTimeout = true;
98
+ parentSignal.removeEventListener("abort", onParentAbort);
99
+ if (!controller.signal.aborted)
100
+ controller.abort();
101
+ }, deadlineMs);
102
+ if (parentSignal.aborted) {
103
+ clearTimeout(timeoutId);
104
+ if (!controller.signal.aborted)
105
+ controller.abort();
106
+ }
107
+ else {
108
+ parentSignal.addEventListener("abort", onParentAbort, { once: true });
109
+ }
110
+ let mergedDisposed = false;
111
+ return {
112
+ signal: controller.signal,
113
+ dispose: () => {
114
+ if (mergedDisposed)
115
+ return;
116
+ mergedDisposed = true;
117
+ clearTimeout(timeoutId);
118
+ parentSignal.removeEventListener("abort", onParentAbort);
119
+ },
120
+ consumeDidTimeout: () => {
121
+ const v = didTimeout;
122
+ didTimeout = false;
123
+ return v;
124
+ },
125
+ };
126
+ }
@@ -90,6 +90,6 @@ export { RopewikiRegionImagesParams } from './api/params/ropewikiRegionImagesPar
90
90
  export { RopewikiRegionImagesResult } from './api/results/ropewikiRegionImagesResult';
91
91
  export { RoutePreviewResult } from './api/results/routePreviewResult';
92
92
  export { ImageVersion, VERSION_FORMAT, ImageVersions } from './mobile/imageVersions';
93
- export { SavedPage } from './mobile/savedPage';
93
+ export { SavedPage, SAVED_PAGES_STORAGE_KEY, DOWNLOADED_ROUTE_PREVIEWS_STORAGE_KEY, type SavedPagesStorageMap, type DownloadedRoutePreviewsStorageMap, } from './mobile/savedPage';
94
94
  export type { FetchType } from './fetchType';
95
95
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/models/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/models/index.ts"],"names":[],"mappings":"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"}
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.RoutesGeojson = exports.RouteType = exports.RouteGeoJsonFeature = exports.Route = exports.RegionPreview = exports.OfflinePagePreview = exports.OnlinePagePreview = exports.PagePreview = exports.PreviewType = exports.Preview = exports.PermitStatus = exports.SavedFilters = exports.SavedPagesFilter = exports.SearchFilter = exports.RouteFilter = exports.RiskMinMax = exports.TimeMinMax = exports.WaterMinMax = exports.TechnicalMinMax = exports.AcaDifficultyFilterOptions = exports.registerDifficultyFilterOptionsParser = exports.DifficultyFilterOptions = exports.Q_ACA_RISK = exports.Q_ACA_TIME = exports.Q_ACA_WATER = exports.Q_ACA_TECHNICAL = exports.Q_DIFFICULTY_TYPE = exports.AcaDifficultyParams = exports.isDifficultyParamsActive = exports.registerDifficultyParamsQueryParser = exports.registerDifficultyParamsQueryInference = exports.DifficultyParams = exports.DifficultyRisk = exports.DifficultyTime = exports.DifficultyWater = exports.DifficultyTechnical = exports.AcaDifficulty = exports.RISK_ORDER = exports.AcaWaterRating = exports.AcaTimeRating = exports.AcaTechnicalRating = exports.AcaRiskRating = exports.ACA_WATER_ORDER = exports.ACA_TIME_ORDER = exports.ACA_TECHNICAL_ORDER = exports.ACA_RISK_ORDER = exports.registerDifficultyParser = exports.Difficulty = exports.DifficultyType = exports.PageDataSource = void 0;
4
4
  exports.RopewikiRegionPreviewsResult = exports.RopewikiRegionPreviewsParams = exports.RegionPreviewsCursor = exports.RopewikiRegionViewResult = exports.RopewikiRegionView = exports.SearchResults = exports.SearchParams = exports.SearchCursor = exports.MapDataTileKeysResults = exports.registerPaginationParser = exports.PaginationResultType = exports.PaginationResults = exports.registerCursorPaginationParser = exports.CursorPaginationResultType = exports.CursorPaginationResults = exports.registerResultParser = exports.ResultType = exports.Result = exports.PaginationParams = exports.CursorPaginationParams = exports.CursorType = exports.Cursor = exports.RopewikiPageLinkPreviewResult = exports.LinkPreviewImage = exports.LinkPreview = exports.OfflineRopewikiPageView = exports.OnlineRopewikiPageView = exports.BaseRopewikiPageView = exports.PageViewType = exports.RopewikiPageViewResult = exports.RopewikiPageView = exports.OfflineCenteredRegionMiniMap = exports.OnlineCenteredRegionMiniMap = exports.OfflinePageMiniMap = exports.OnlinePageMiniMap = exports.CenteredRegionMiniMap = exports.PageMiniMap = exports.RegionMiniMap = exports.MiniMap = exports.MiniMapType = exports.Bounds = exports.OfflineBetaSection = exports.OnlineBetaSection = exports.BetaSection = exports.DownloadBytes = exports.OfflineBetaSectionImage = exports.OnlineBetaSectionImage = exports.BetaSectionImage = exports.RoutesParams = exports.RouteResult = void 0;
5
- exports.SavedPage = exports.ImageVersions = exports.VERSION_FORMAT = exports.ImageVersion = exports.RoutePreviewResult = exports.RopewikiRegionImagesResult = exports.RopewikiRegionImagesParams = exports.RopewikiRegionImageView = exports.RegionImagesCursor = void 0;
5
+ exports.DOWNLOADED_ROUTE_PREVIEWS_STORAGE_KEY = exports.SAVED_PAGES_STORAGE_KEY = exports.SavedPage = exports.ImageVersions = exports.VERSION_FORMAT = exports.ImageVersion = exports.RoutePreviewResult = exports.RopewikiRegionImagesResult = exports.RopewikiRegionImagesParams = exports.RopewikiRegionImageView = exports.RegionImagesCursor = void 0;
6
6
  require("./api/results/registerAllResultParsers");
7
7
  require("./difficulty/registerDifficultyParsers");
8
8
  require("./filters/registerDifficultyFilterOptionsParsers");
@@ -198,3 +198,5 @@ Object.defineProperty(exports, "VERSION_FORMAT", { enumerable: true, get: functi
198
198
  Object.defineProperty(exports, "ImageVersions", { enumerable: true, get: function () { return imageVersions_1.ImageVersions; } });
199
199
  var savedPage_1 = require("./mobile/savedPage");
200
200
  Object.defineProperty(exports, "SavedPage", { enumerable: true, get: function () { return savedPage_1.SavedPage; } });
201
+ Object.defineProperty(exports, "SAVED_PAGES_STORAGE_KEY", { enumerable: true, get: function () { return savedPage_1.SAVED_PAGES_STORAGE_KEY; } });
202
+ Object.defineProperty(exports, "DOWNLOADED_ROUTE_PREVIEWS_STORAGE_KEY", { enumerable: true, get: function () { return savedPage_1.DOWNLOADED_ROUTE_PREVIEWS_STORAGE_KEY; } });
@@ -1,5 +1,21 @@
1
1
  import { OfflinePagePreview } from '../previews/offlinePagePreview';
2
2
  import { OnlinePagePreview } from '../previews/onlinePagePreview';
3
+ /** AsyncStorage key for the saved-pages map (see {@link SavedPagesStorageMap}). */
4
+ export declare const SAVED_PAGES_STORAGE_KEY = "ropegeo:savedPages";
5
+ /**
6
+ * AsyncStorage key for offline route preview rows keyed by route id
7
+ * (see {@link DownloadedRoutePreviewsStorageMap}).
8
+ */
9
+ export declare const DOWNLOADED_ROUTE_PREVIEWS_STORAGE_KEY = "ropegeo:downloadedRoutePreviews";
10
+ /**
11
+ * Persisted value for {@link SAVED_PAGES_STORAGE_KEY}: page id → JSON string from {@link SavedPage#toString}.
12
+ */
13
+ export type SavedPagesStorageMap = Record<string, string>;
14
+ /**
15
+ * Persisted value for {@link DOWNLOADED_ROUTE_PREVIEWS_STORAGE_KEY}. Entries are removed when a route
16
+ * has no previews; array elements are validated with {@link OfflinePagePreview.fromResult}.
17
+ */
18
+ export type DownloadedRoutePreviewsStorageMap = Record<string, unknown[]>;
3
19
  export declare class SavedPage {
4
20
  readonly preview: OnlinePagePreview | OfflinePagePreview;
5
21
  readonly savedAt: number;
@@ -1 +1 @@
1
- {"version":3,"file":"savedPage.d.ts","sourceRoot":"","sources":["../../../src/models/mobile/savedPage.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAiDlE,qBAAa,SAAS;IAClB,QAAQ,CAAC,OAAO,EAAE,iBAAiB,GAAG,kBAAkB,CAAC;IACzD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,sBAAsB,EAAE,MAAM,GAAG,IAAI,CAAC;gBAG3C,OAAO,EAAE,iBAAiB,GAAG,kBAAkB,EAC/C,OAAO,EAAE,MAAM,EACf,sBAAsB,GAAE,MAAM,GAAG,IAAW;IAOhD,MAAM,CAAC,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS;IA8BpD,QAAQ,IAAI,MAAM;CAOrB"}
1
+ {"version":3,"file":"savedPage.d.ts","sourceRoot":"","sources":["../../../src/models/mobile/savedPage.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAGlE,mFAAmF;AACnF,eAAO,MAAM,uBAAuB,uBAAuB,CAAC;AAE5D;;;GAGG;AACH,eAAO,MAAM,qCAAqC,oCAAoC,CAAC;AAEvF;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAE1D;;;GAGG;AACH,MAAM,MAAM,iCAAiC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;AAgD1E,qBAAa,SAAS;IAClB,QAAQ,CAAC,OAAO,EAAE,iBAAiB,GAAG,kBAAkB,CAAC;IACzD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,sBAAsB,EAAE,MAAM,GAAG,IAAI,CAAC;gBAG3C,OAAO,EAAE,iBAAiB,GAAG,kBAAkB,EAC/C,OAAO,EAAE,MAAM,EACf,sBAAsB,GAAE,MAAM,GAAG,IAAW;IAOhD,MAAM,CAAC,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS;IA8BpD,QAAQ,IAAI,MAAM;CAOrB"}
@@ -1,8 +1,15 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.SavedPage = void 0;
3
+ exports.SavedPage = exports.DOWNLOADED_ROUTE_PREVIEWS_STORAGE_KEY = exports.SAVED_PAGES_STORAGE_KEY = void 0;
4
4
  const acaDifficulty_1 = require("../difficulty/acaDifficulty");
5
5
  const pagePreview_1 = require("../previews/pagePreview");
6
+ /** AsyncStorage key for the saved-pages map (see {@link SavedPagesStorageMap}). */
7
+ exports.SAVED_PAGES_STORAGE_KEY = 'ropegeo:savedPages';
8
+ /**
9
+ * AsyncStorage key for offline route preview rows keyed by route id
10
+ * (see {@link DownloadedRoutePreviewsStorageMap}).
11
+ */
12
+ exports.DOWNLOADED_ROUTE_PREVIEWS_STORAGE_KEY = 'ropegeo:downloadedRoutePreviews';
6
13
  const STORAGE_KEYS = ['preview', 'savedAt'];
7
14
  function assertFiniteNumber(value, name) {
8
15
  if (typeof value !== 'number' || !Number.isFinite(value)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ropegeo-common",
3
- "version": "1.12.1",
3
+ "version": "1.12.4",
4
4
  "description": "Shared domain models and helpers for RopeGeo and WebScraper",
5
5
  "license": "ISC",
6
6
  "repository": {
@@ -23,6 +23,10 @@
23
23
  "types": "./dist/helpers/index.d.ts",
24
24
  "default": "./dist/helpers/index.js"
25
25
  },
26
+ "./helpers/network": {
27
+ "types": "./dist/helpers/network/index.d.ts",
28
+ "default": "./dist/helpers/network/index.js"
29
+ },
26
30
  "./components": {
27
31
  "types": "./dist/components/index.d.ts",
28
32
  "default": "./dist/components/index.js"