@d1g1tal/transportr 2.1.2 → 2.2.1

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/CHANGELOG.md ADDED
@@ -0,0 +1,231 @@
1
+ ## [2.2.1](https://github.com/D1g1talEntr0py/transportr/compare/v2.2.0...v2.2.1) (2026-04-04)
2
+
3
+ ### Bug Fixes
4
+
5
+ * **deps:** update subscribr dependency to address security issues via Dependabot (0a04a16e34daaae749927b4893f15a972d4eb5e0)
6
+
7
+ ### Miscellaneous Chores
8
+
9
+ * adjust typescript configuration and vs code settings (c1f28dff23025853f236fa380bc4f6e0ec26dd8c)
10
+ Updates `tsconfig.json` to use module preservation and stable type ordering, and refines cases for standard libraries. Updates `.vscode` configuration to point correctly to the local typescript SDK path.
11
+
12
+ * **deps-dev:** update essential dev dependencies (b94738075ce674d7e65952433cd553fa7af0bc35)
13
+ Bumps various development dependencies including TypeScript build tools, type definitions, and ESLint plugins. Updates the lockfile alongside package.json changes.
14
+
15
+
16
+ ### Tests
17
+
18
+ * remove vitest ui script (7d894526ffb17345986375d3700f53b42ba5a2bb)
19
+ Drops the `test:ui` script from package.json.
20
+
21
+
22
+ ### Build System
23
+
24
+ * auto-include changelog in releases (fd40332bd99c6b3a2ae5f260e9b7afcf0902665b)
25
+ Adds `CHANGELOG.md` to the package distribution files to ensure it gets packaged and published to npm.
26
+
27
+
28
+ ### Continuous Integration
29
+
30
+ * fix the versioning system that was broken by attempting to fix the fact that a refactor doesn't change the version (3b0642134714bd634ae11f8a3d4f4695ea25c953)
31
+
32
+ ## [2.2.0](https://github.com/D1g1talEntr0py/transportr/compare/v2.1.2...v2.2.0) (2026-03-31)
33
+
34
+ ### Features
35
+
36
+ * **docs:** expand package manager installation snippets (caea4a7d21067bd248f6cadc118010d91c28c59b)
37
+ Adds missing installation commands for native npm and yarn to the README explicitly, ensuring compatibility statements for multiple ecosystems.
38
+
39
+
40
+ ### Bug Fixes
41
+
42
+ * **deps:** update dependencies to address CVE-2026-33228 (67315abc69d4e9ca5d93ea7543d7149c42e616d8)
43
+ Upgrades the transitive dependency `flatted` to 3.4.2 to fix a prototype pollution vulnerability (CWE-1321). This update also includes a major version bump for TypeScript to v6.0.2, alongside several other development dependency bumps such as vitest, eslint plugins, and pnpm.
44
+
45
+ * **license:** change license from ISC to MIT (597c7c4414c8403ecdc4105c9250356b63faaf81)
46
+ Transitions the project license to MIT, updating the LICENSE file, the metadata field in `package.json`, and the visual status badge in the documentation.
47
+
48
+
49
+ ### Code Refactoring
50
+
51
+ * **config:** simplify compilation settings for TypeScript 6 (6d232397646a323241e094e3b723976e67be4786)
52
+ Removes historically explicit configuration options from `tsconfig.json` that are either default behavior or obsolete in TypeScript 6. Scope is securely narrowed to explicitly include and target the `./src` directory.
53
+
54
+
55
+ ### Documentation
56
+
57
+ * consolidate ai agent instructions (aaeccb9ea8be76a2524b86df51564cb3599de7d1)
58
+ Deletes the separate `AGENTS.md` file and rolls its relevant details and guidelines directly into `.github/copilot-instructions.md`. This centralizes architectural conventions, code formatting rules, testing guidelines, and new feature details (e.g., retries, lifecycle hooks, and XSRF protection) for AI coding assistants.
59
+
60
+ * update readme with cdn usage and submodule documentation (fa18f735cea5ce79763d127fff09332317f17f55)
61
+ Expands the project README to clarify native browser and CDN usage without bundlers through an import map. Adds new sections that detail submodules for HTTP constants (e.g., headers, methods, media-types) to make the code examples and documentation more comprehensive and usable.
62
+
63
+
64
+ ### Build System
65
+
66
+ * **deps:** update dependencies and package manager settings (1d2faaadb889fd71ff6c37f898de70a090df586b)
67
+ Removes the explicit `auto-install-peers=false` configuration from `.npmrc` and enables `autoInstallPeers: true` in the `pnpm-lock.yaml` settings. Updates the pnpm package manager version to 10.32.1 and bumps various dependencies, including `@d1g1tal/media-type`, `@d1g1tal/subscribr`, and development tools like `eslint`, `@typescript-eslint/eslint-plugin`, and `jsdom`.
68
+
69
+
70
+ ### Continuous Integration
71
+
72
+ * update github actions workflows (0f2108390b52fb66aa25a2f337616efb45bec339)
73
+ Updates GitHub Actions to use newer action versions, modernizes the `pnpm-setup` and `setup-node` tasks, and removes the environment variable `FORCE_JAVASCRIPT_ACTIONS_TO_NODE24`. Additionally, modifies the publishing workflow to properly install the latest `npm` version instead of clearing the auth token.
74
+
75
+ ## [2.1.2](https://github.com/D1g1talEntr0py/transportr/compare/v2.1.1...v2.1.2) (2026-03-14)
76
+
77
+ ### ⚠ BREAKING CHANGES
78
+
79
+ * Transportr.MediaType, Transportr.RequestMethod, Transportr.RequestHeader,
80
+ Transportr.ResponseHeader, and Transportr.CachingPolicy static properties have been removed.
81
+
82
+ Import constants directly from the package submodule paths instead:
83
+ - import { HttpMediaType } from '@d1g1tal/transportr/media-types'
84
+ - import { HttpRequestMethod } from '@d1g1tal/transportr/methods'
85
+ - import { HttpRequestHeader } from '@d1g1tal/transportr/headers'
86
+ - import { HttpResponseHeader } from '@d1g1tal/transportr/response-headers'
87
+
88
+ ### Code Refactoring
89
+
90
+ * remove static enum properties from Transportr class (8cde6bdddb5c00ed858517a938689099a02d1b7c)
91
+
92
+ ## [2.1.1](https://github.com/D1g1talEntr0py/transportr/compare/v2.1.0...v2.1.1) (2026-03-14)
93
+
94
+ ### Bug Fixes
95
+
96
+ * **ci:** fix the conflict for auto installing peer dependencies (23302387f160e55a60d775ffeb6f7ec4127721f6)
97
+
98
+ ## [2.1.0](https://github.com/D1g1talEntr0py/transportr/compare/v2.0.0...v2.1.0) (2026-03-14)
99
+
100
+ ### Features
101
+
102
+ * lower Node.js requirement to v20 and make jsdom optional (94dbcabeec9de2674f00b1c10cde6b48273d7a69)
103
+ Reduces the minimum required Node.js version from 22.0.0 to 20.0.0 to broaden compatibility. Makes `jsdom` an optional peer dependency, ensuring users are only required to install it if they utilize DOM-specific parsing features. Incorporates lazy-loading for DOMPurify and standardizes the JSDOM URL environment.
104
+
105
+
106
+ ### Code Refactoring
107
+
108
+ * replace custom enums with native string literals (c51a0dcdfeef4b5eda2aa1e1fe475e540b41a1e9)
109
+ Simplifies the codebase by stripping out internal `HttpMediaType`, `HttpRequestMethod`, `HttpRequestHeader`, and `HttpResponseHeader` abstractions. Instead, the implementation now leverages native string literals natively supported by JavaScript, reducing overhead and streamlining type checks.
110
+
111
+
112
+ ### Documentation
113
+
114
+ * correct TypeScript version in changelog (d196f4a315ec366397d7677fc5b8930f24930dc8)
115
+ Fixes typos in the CHANGELOG that referenced an incorrect 'TypeScript 6' version, ensuring accurate historical logs.
116
+
117
+
118
+ ### Miscellaneous Chores
119
+
120
+ * add .npmrc to set auto-install-peers=false (21393af89a95c63dcbc003bf37804977728537e0)
121
+
122
+ ### Tests
123
+
124
+ * modify tests to be compatible with Node 22 (71669f94854a1229bfe9c30d7f3dc0654b782fce)
125
+
126
+ ### Build System
127
+
128
+ * expose package submodules and introduce release script (b119ef8f2c3a8365a71119a95e4ce3be3574846b)
129
+ Expands the package's module definitions by exposing distinct exports for headers, methods, and media-types. Adds a dedicated minified release build command to optimize the final artifact.
130
+
131
+
132
+ ### Continuous Integration
133
+
134
+ * stop ignoring the config.ts for the test API key as it is just a placeholder now (a581ecc4ebbdf7661822412c8d26b9707b52d7ab)
135
+ * update the secret key (85673375cf8338f72ea0d9f4154bf61cb08179c7)
136
+
137
+ ## [2.0.0](https://github.com/D1g1talEntr0py/transportr/compare/v1.4.4...v2.0.0) (2026-03-13)
138
+
139
+ ### ⚠ BREAKING CHANGES
140
+
141
+ * Complete rewrite from JavaScript to TypeScript.
142
+
143
+ Source rewrite:
144
+ - AbortSignal → SignalController using native AbortController.any() and AbortSignal.timeout()
145
+ - ParameterMap class eliminated; options processing uses shallow merge with deep merge only for headers/searchParams
146
+ - Response handlers extracted to response-handlers.ts with DOMPurify sanitization
147
+ - Lazy JSDOM auto-import for Node.js DOM handler support
148
+ - contentTypeHandlers changed from handler-keyed Map to content-type-keyed array with MediaType.matches()
149
+ - MediaType caching with LRU eviction (100 entries)
150
+ - All modules use named exports instead of default exports
151
+
152
+ Type system:
153
+ - Branded types: JsonString<T>, JsonValue<T> for type-safe serialization
154
+ - TypedResponse<T> extending Response with typed json() method
155
+ - TypedHeaders with strongly-typed header names and AuthorizationScheme validation
156
+ - RequestOptions discriminated union based on method type (body allowed/disallowed)
157
+ - Recursive JSON types: Json, JsonPrimitive, JsonArray, JsonObject
158
+ - Utility types: Prettify<T>, LiteralUnion<T>
159
+
160
+ New utilities (utils.ts):
161
+ - serialize<T>() with branded JsonString<T> return type
162
+ - isRawBody() for FormData/Blob/ArrayBuffer/ReadableStream detection
163
+ - isObject(), isString() type predicates
164
+ - objectMerge(), getCookieValue()
165
+ - Removes @d1g1tal/chrysalis dependency
166
+
167
+ API changes:
168
+ - post(path, options) signature (body now in options)
169
+ - DELETE added to requestBodyMethods
170
+ - registerContentTypeHandler()/unregisterContentTypeHandler() public API
171
+ - Instance destroy() and static unregisterAll() teardown methods
172
+ - Enhanced HttpError with url, method, timing properties
173
+ - handleImage returns HTMLImageElement instead of blob URL string
174
+ - HTML selector support on getHtml() and getHtmlFragment()
175
+
176
+ ### Features
177
+
178
+ * rewrite source in TypeScript with full type system (b4f219ce9b91b814943664301af0699fe3f31061)
179
+
180
+ ### Documentation
181
+
182
+ * add release process, agent guidelines, and update README (584594ba5dd37d1a33e24547507d3e3ccf9da044)
183
+ - Add docs/release-process.md with semantic-release workflow documentation
184
+ - Add AGENTS.md with AI agent coding standards and protocols
185
+ - Update README.md for TypeScript API and new features
186
+ - Update LICENSE
187
+
188
+
189
+ ### Miscellaneous Chores
190
+
191
+ * migrate build tooling to TypeScript 5.9 and Vitest 4 (7cca59051a0106ab19975bd4496bf7f037a5b912)
192
+ - Replace .eslintrc.json with ESLint flat config (eslint.config.js)
193
+ - Remove esbuild.js and jsconfig.json in favor of tsconfig.json
194
+ - Add vitest.config.ts with dual-project setup (unit/integration)
195
+ - Add pnpm-workspace.yaml
196
+ - Remove @d1g1tal/chrysalis dependency
197
+ - Add dompurify, jsdom dependencies
198
+ - Update @d1g1tal/media-type to v6, @d1g1tal/subscribr to v4
199
+ - Update .gitignore and .vscode/settings.json
200
+
201
+
202
+ ### Tests
203
+
204
+ * add comprehensive TypeScript test suite (1934c956c43992497ba70360f02fc3da38fe87bc)
205
+ - Rewrite all existing JS tests in TypeScript with Vitest 4
206
+ - Add test setup (tests/scripts/setup.ts) with URL.createObjectURL/revokeObjectURL mocks
207
+ - Add test config (tests/scripts/config.ts) with mockapi.io integration key
208
+
209
+ New test files:
210
+ - retry.test.ts: exponential backoff, custom delay, status codes, network errors
211
+ - hooks.test.ts: beforeRequest, afterResponse, beforeError at global/instance/request scope
212
+ - browser-environment.test.ts: DOM-specific behavior validation
213
+ - environment-specific.test.ts: Node.js vs browser environment detection
214
+ - response-handlers.test.ts: JSON, HTML, XML, script, CSS, blob, image, stream handlers
215
+ - signal-controller.test.ts: SignalController lifecycle, timeout detection, destroy cleanup
216
+ - signal-controller-cleanup.test.ts: memory leak prevention, controller Set management
217
+ - request-options-optimization.test.ts: shallow vs deep merge, raw body detection
218
+ - mediatype-caching.test.ts: LRU eviction, cache hit/miss behavior
219
+ - utils.test.ts: serialize, isRawBody, isObject, objectMerge, getCookieValue
220
+ - network-integration.test.ts: real HTTP calls to mockapi.io
221
+
222
+
223
+ ### Continuous Integration
224
+
225
+ * add GitHub Actions workflows and semantic-release (2f2809b53b318af2dc41084e858547db022c1cee)
226
+ - ci.yml: lint, type-check, build, unit tests on Node.js 22, 24, 25 with Codecov upload
227
+ - integration.yml: integration tests against mockapi.io on main push
228
+ - publish.yml: automated semantic-release with npm provenance attestation
229
+ - .releaserc.json: conventional commits, changelog generation, npm publish, GitHub releases
230
+ - .githooks/commit-msg: git hook enforcing Conventional Commits format
231
+ - .github/copilot-instructions.md: AI assistant project context
package/LICENSE CHANGED
@@ -1,12 +1,21 @@
1
- Copyright (c) 2023, Jason DiMeo
1
+ MIT License
2
2
 
3
- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided
4
- that the above copyright notice and this permission notice appear in all copies.
3
+ Copyright (c) 2023 Jason DiMeo
5
4
 
6
- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
7
- INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE
8
- FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
9
- LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
10
- ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
11
 
12
- Source: http://opensource.org/licenses/ISC
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
  [![npm downloads](https://img.shields.io/npm/dm/@d1g1tal/transportr)](https://www.npmjs.com/package/@d1g1tal/transportr)
5
5
  [![CI](https://github.com/D1g1talEntr0py/transportr/actions/workflows/ci.yml/badge.svg)](https://github.com/D1g1talEntr0py/transportr/actions/workflows/ci.yml)
6
6
  [![codecov](https://codecov.io/gh/D1g1talEntr0py/transportr/graph/badge.svg)](https://codecov.io/gh/D1g1talEntr0py/transportr)
7
- [![License: ISC](https://img.shields.io/github/license/D1g1talEntr0py/transportr)](https://github.com/D1g1talEntr0py/transportr/blob/main/LICENSE)
7
+ [![License: MIT](https://img.shields.io/github/license/D1g1talEntr0py/transportr)](https://github.com/D1g1talEntr0py/transportr/blob/main/LICENSE)
8
8
  [![Node.js](https://img.shields.io/node/v/@d1g1tal/transportr)](https://nodejs.org)
9
9
  [![TypeScript](https://img.shields.io/badge/TypeScript-5.9-blue?logo=typescript&logoColor=white)](https://www.typescriptlang.org/)
10
10
 
@@ -23,12 +23,6 @@ A TypeScript Fetch API wrapper providing type-safe HTTP requests with advanced a
23
23
  - **HTML selectors** — Extract specific elements from HTML responses with CSS selectors
24
24
  - **FormData auto-detection** — Automatically handles FormData, Blob, ArrayBuffer, and stream bodies
25
25
 
26
- ## Installation
27
-
28
- ```bash
29
- pnpm add @d1g1tal/transportr
30
- ```
31
-
32
26
  ## Requirements
33
27
 
34
28
  - **Node.js** ≥ 20.0.0 or a modern browser with native [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) and `AbortController` support
@@ -38,17 +32,40 @@ pnpm add @d1g1tal/transportr
38
32
  pnpm add jsdom
39
33
  ```
40
34
 
35
+ ## Installation
36
+
37
+ ```bash
38
+ # With pnpm:
39
+ pnpm add @d1g1tal/transportr
40
+
41
+ # Or with npm:
42
+ npm install @d1g1tal/transportr
43
+
44
+ # Or with yarn
45
+ yarn add @d1g1tal/transportr
46
+ ```
47
+
41
48
  ## Quick Start
42
49
 
50
+ Only the main module is required — the submodule constants (`HttpRequestHeader`, `HttpMediaType`, etc.) are optional conveniences. Anywhere a constant is used, a plain string works just as well.
51
+
52
+ Out of the box, every instance defaults to:
53
+ - `Content-Type: application/json; charset=utf-8`
54
+ - `Accept: application/json; charset=utf-8`
55
+ - `timeout`: 30 000 ms
56
+ - `cache`: `no-store`
57
+ - `credentials`: `same-origin`
58
+ - `mode`: `cors`
59
+
43
60
  ```typescript
44
61
  import { Transportr } from '@d1g1tal/transportr';
45
62
 
46
63
  const api = new Transportr('https://api.example.com');
47
64
 
48
- // GET JSON
65
+ // GET JSON — default Accept header is already application/json
49
66
  const data = await api.getJson('/users/1');
50
67
 
51
- // POST with JSON body
68
+ // POST with JSON body — automatically serialized, no Content-Type needed
52
69
  const created = await api.post('/users', { body: { name: 'Alice' } });
53
70
 
54
71
  // GET with search params
@@ -57,8 +74,77 @@ const results = await api.getJson('/search', { searchParams: { q: 'term', page:
57
74
  // Typed response using generics
58
75
  interface User { id: number; name: string; }
59
76
  const user = await api.get<User>('/users/1');
77
+
78
+ // Plain strings work anywhere — constants are just for convenience
79
+ const api2 = new Transportr('https://api.example.com', {
80
+ headers: { 'authorization': 'Bearer token', 'accept-language': 'en-US' }
81
+ });
82
+ ```
83
+
84
+ ## Browser / CDN Usage
85
+
86
+ The package is published as pure ESM and works directly in modern browsers — no bundler required. All dependencies (`@d1g1tal/media-type`, `@d1g1tal/subscribr`, DOMPurify) are bundled into the output, so there are no external module URLs to manage. `jsdom` is not needed in a browser environment.
87
+
88
+ ### With an import map (recommended)
89
+
90
+ An [import map](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/script/type/importmap) mirrors the package's named submodule exports and keeps your code identical to the Node.js form — use bare specifiers exactly as you would in a bundled project:
91
+
92
+ ```html
93
+ <script type="importmap">
94
+ {
95
+ "imports": {
96
+ "@d1g1tal/transportr": "https://cdn.jsdelivr.net/npm/@d1g1tal/transportr/dist/transportr.js",
97
+ "@d1g1tal/transportr/headers": "https://cdn.jsdelivr.net/npm/@d1g1tal/transportr/dist/headers.js",
98
+ "@d1g1tal/transportr/methods": "https://cdn.jsdelivr.net/npm/@d1g1tal/transportr/dist/methods.js",
99
+ "@d1g1tal/transportr/media-types": "https://cdn.jsdelivr.net/npm/@d1g1tal/transportr/dist/media-types.js",
100
+ "@d1g1tal/transportr/response-headers": "https://cdn.jsdelivr.net/npm/@d1g1tal/transportr/dist/response-headers.js"
101
+ }
102
+ }
103
+ </script>
104
+
105
+ <script type="module">
106
+ import { Transportr } from '@d1g1tal/transportr';
107
+ import { HttpRequestHeader } from '@d1g1tal/transportr/headers';
108
+ import { HttpRequestMethod } from '@d1g1tal/transportr/methods';
109
+ import { HttpMediaType } from '@d1g1tal/transportr/media-types';
110
+ import { HttpResponseHeader } from '@d1g1tal/transportr/response-headers';
111
+
112
+ const api = new Transportr('https://api.example.com', {
113
+ headers: {
114
+ [HttpRequestHeader.AUTHORIZATION]: 'Bearer token',
115
+ [HttpRequestHeader.ACCEPT]: HttpMediaType.JSON
116
+ }
117
+ });
118
+
119
+ const data = await api.getJson('/users/1');
120
+ console.log(data);
121
+ </script>
60
122
  ```
61
123
 
124
+ ### Without an import map
125
+
126
+ The CDN resolves the `"."` entry in `exports` automatically, so no explicit file path is needed for the main module. Submodules use their CDN paths directly:
127
+
128
+ ```html
129
+ <script type="module">
130
+ import { Transportr } from 'https://cdn.jsdelivr.net/npm/@d1g1tal/transportr';
131
+ import { HttpRequestHeader } from 'https://cdn.jsdelivr.net/npm/@d1g1tal/transportr/dist/headers.js';
132
+ import { HttpMediaType } from 'https://cdn.jsdelivr.net/npm/@d1g1tal/transportr/dist/media-types.js';
133
+
134
+ const api = new Transportr('https://api.example.com', {
135
+ headers: {
136
+ [HttpRequestHeader.AUTHORIZATION]: 'Bearer token',
137
+ [HttpRequestHeader.ACCEPT]: HttpMediaType.JSON
138
+ }
139
+ });
140
+
141
+ const data = await api.getJson('/users/1');
142
+ console.log(data);
143
+ </script>
144
+ ```
145
+
146
+ Import map support is available in all browsers covered by this project's `browserslist` configuration (Chrome 89+, Firefox 108+, Safari 16.4+).
147
+
62
148
  ## API
63
149
 
64
150
  ### Constructor
@@ -339,21 +425,66 @@ api
339
425
 
340
426
  ### Submodule Imports
341
427
 
342
- HTTP constant objects are available as named submodule imports rather than static class properties:
428
+ HTTP constant objects are available as named submodule imports. Each is a tree-shakeable, side-effect-free object of string constants — useful for avoiding magic strings and getting autocomplete.
429
+
430
+ #### `@d1g1tal/transportr/headers`
431
+
432
+ Request header name constants.
343
433
 
344
434
  ```typescript
345
- import { HttpMediaType } from '@d1g1tal/transportr/media-types';
346
- import { HttpRequestMethod } from '@d1g1tal/transportr/methods';
347
435
  import { HttpRequestHeader } from '@d1g1tal/transportr/headers';
348
- import { HttpResponseHeader } from '@d1g1tal/transportr/response-headers';
436
+
437
+ const api = new Transportr('https://api.example.com', {
438
+ headers: {
439
+ [HttpRequestHeader.AUTHORIZATION]: 'Bearer token',
440
+ [HttpRequestHeader.CONTENT_TYPE]: 'application/json',
441
+ [HttpRequestHeader.ACCEPT_LANGUAGE]: 'en-US'
442
+ }
443
+ });
349
444
  ```
350
445
 
351
- | Submodule | Export | Description |
352
- |-----------|--------|-------------|
353
- | `@d1g1tal/transportr/media-types` | `HttpMediaType` | MIME type string constants |
354
- | `@d1g1tal/transportr/methods` | `HttpRequestMethod` | HTTP method string constants |
355
- | `@d1g1tal/transportr/headers` | `HttpRequestHeader` | Request header name constants |
356
- | `@d1g1tal/transportr/response-headers` | `HttpResponseHeader` | Response header name constants |
446
+ #### `@d1g1tal/transportr/methods`
447
+
448
+ HTTP method string constants.
449
+
450
+ ```typescript
451
+ import { HttpRequestMethod } from '@d1g1tal/transportr/methods';
452
+
453
+ const response = await api.request('/data', { method: HttpRequestMethod.PATCH });
454
+ ```
455
+
456
+ #### `@d1g1tal/transportr/media-types`
457
+
458
+ MIME type string constants covering common content types (JSON, HTML, XML, CSS, images, audio, video, and more).
459
+
460
+ ```typescript
461
+ import { HttpMediaType } from '@d1g1tal/transportr/media-types';
462
+
463
+ const api = new Transportr('https://api.example.com', {
464
+ headers: { [HttpRequestHeader.ACCEPT]: HttpMediaType.JSON }
465
+ });
466
+
467
+ // Use as a content-type value
468
+ await api.post('/upload', {
469
+ body: csvData,
470
+ headers: { [HttpRequestHeader.CONTENT_TYPE]: HttpMediaType.CSV }
471
+ });
472
+ ```
473
+
474
+ #### `@d1g1tal/transportr/response-headers`
475
+
476
+ Response header name constants — useful when reading headers from a response.
477
+
478
+ ```typescript
479
+ import { HttpResponseHeader } from '@d1g1tal/transportr/response-headers';
480
+
481
+ const reg = api.register(Transportr.RequestEvents.SUCCESS, (event, data) => {
482
+ const response = data as Response;
483
+ const etag = response.headers.get(HttpResponseHeader.ETAG);
484
+ const retryAfter = response.headers.get(HttpResponseHeader.RETRY_AFTER);
485
+ const location = response.headers.get(HttpResponseHeader.LOCATION);
486
+ });
487
+ ```
357
488
 
358
489
  ## License
359
490
 
@@ -1,4 +1,4 @@
1
- import { Subscription } from "@d1g1tal/subscribr";
1
+ import type { Subscription } from "@d1g1tal/subscribr";
2
2
  import { MediaType } from "@d1g1tal/media-type";
3
3
 
4
4
  /**
@@ -1,3 +1,3 @@
1
- var P=/^[-!#$%&'*+.^_`|~A-Za-z0-9]*$/u,me=/(["\\])/ug,ye=/^[\t\u0020-\u007E\u0080-\u00FF]*$/u,W=class K extends Map{constructor(e=[]){super(e)}static isValid(e,t){return P.test(e)&&ye.test(t)}get(e){return super.get(e.toLowerCase())}has(e){return super.has(e.toLowerCase())}set(e,t){if(!K.isValid(e,t))throw new Error(`Invalid media type parameter name/value: ${e}/${t}`);return super.set(e.toLowerCase(),t),this}delete(e){return super.delete(e.toLowerCase())}toString(){return Array.from(this).map(([e,t])=>`;${e}=${!t||!P.test(t)?`"${t.replace(me,"\\$1")}"`:t}`).join("")}get[Symbol.toStringTag](){return"MediaTypeParameters"}},be=new Set([" "," ",`
2
- `,"\r"]),Ee=/[ \t\n\r]+$/u,Te=/^[ \t\n\r]+|[ \t\n\r]+$/ug,Oe=class E{static parse(e){e=e.replace(Te,"");let t=0,[r,n]=E.collect(e,t,["/"]);if(t=n,!r.length||t>=e.length||!P.test(r))throw new TypeError(E.generateErrorMessage("type",r));++t;let[o,i]=E.collect(e,t,[";"],!0,!0);if(t=i,!o.length||!P.test(o))throw new TypeError(E.generateErrorMessage("subtype",o));let u=new W;for(;t<e.length;){for(++t;be.has(e[t]);)++t;let a;if([a,t]=E.collect(e,t,[";","="],!1),t>=e.length||e[t]===";")continue;++t;let l;if(e[t]==='"')for([l,t]=E.collectHttpQuotedString(e,t);t<e.length&&e[t]!==";";)++t;else if([l,t]=E.collect(e,t,[";"],!1,!0),!l)continue;a&&W.isValid(a,l)&&!u.has(a)&&u.set(a,l)}return{type:r,subtype:o,parameters:u}}get[Symbol.toStringTag](){return"MediaTypeParser"}static collect(e,t,r,n=!0,o=!1){let i="";for(let{length:u}=e;t<u&&!r.includes(e[t]);t++)i+=e[t];return n&&(i=i.toLowerCase()),o&&(i=i.replace(Ee,"")),[i,t]}static collectHttpQuotedString(e,t){let r="";for(let n=e.length,o;++t<n&&(o=e[t])!=='"';)r+=o=="\\"&&++t<n?e[t]:o;return[r,t]}static generateErrorMessage(e,t){return`Invalid ${e} "${t}": only HTTP token code points are valid.`}},y=class Y{_type;_subtype;_parameters;constructor(e,t={}){if(t===null||typeof t!="object"||Array.isArray(t))throw new TypeError("The parameters argument must be an object");({type:this._type,subtype:this._subtype,parameters:this._parameters}=Oe.parse(e));for(let[r,n]of Object.entries(t))this._parameters.set(r,n)}static parse(e){try{return new Y(e)}catch{}return null}get type(){return this._type}get subtype(){return this._subtype}get essence(){return`${this._type}/${this._subtype}`}get parameters(){return this._parameters}matches(e){return typeof e=="string"?this.essence.includes(e):this._type===e._type&&this._subtype===e._subtype}toString(){return`${this.essence}${this._parameters.toString()}`}get[Symbol.toStringTag](){return"MediaType"}};var Se=class extends Map{set(s,e){return super.set(s,e instanceof Set?e:(super.get(s)??new Set).add(e)),this}find(s,e){let t=this.get(s);if(t!==void 0)return Array.from(t).find(e)}hasValue(s,e){let t=super.get(s);return t?t.has(e):!1}deleteValue(s,e){if(e===void 0)return this.delete(s);let t=super.get(s);if(t){let r=t.delete(e);return t.size===0&&super.delete(s),r}return!1}get[Symbol.toStringTag](){return"SetMultiMap"}},ve=class{context;eventHandler;constructor(s,e){this.context=s,this.eventHandler=e}handle(s,e){this.eventHandler.call(this.context,s,e)}get[Symbol.toStringTag](){return"ContextEventHandler"}},we=class{_eventName;_contextEventHandler;constructor(s,e){this._eventName=s,this._contextEventHandler=e}get eventName(){return this._eventName}get contextEventHandler(){return this._contextEventHandler}get[Symbol.toStringTag](){return"Subscription"}},C=class{subscribers=new Se;errorHandler;setErrorHandler(s){this.errorHandler=s}subscribe(s,e,t=e,r){if(this.validateEventName(s),r?.once){let i=e;e=(u,a)=>{i.call(t,u,a),this.unsubscribe(o)}}let n=new ve(t,e);this.subscribers.set(s,n);let o=new we(s,n);return o}unsubscribe({eventName:s,contextEventHandler:e}){let t=this.subscribers.get(s)??new Set,r=t.delete(e);return r&&t.size===0&&this.subscribers.delete(s),r}publish(s,e=new CustomEvent(s),t){this.validateEventName(s),this.subscribers.get(s)?.forEach(r=>{try{r.handle(e,t)}catch(n){this.errorHandler?this.errorHandler(n,s,e,t):console.error(`Error in event handler for '${s}':`,n)}})}isSubscribed({eventName:s,contextEventHandler:e}){return this.subscribers.get(s)?.has(e)??!1}validateEventName(s){if(!s||typeof s!="string")throw new TypeError("Event name must be a non-empty string");if(s.trim()!==s)throw new Error("Event name cannot have leading or trailing whitespace")}destroy(){this.subscribers.clear()}get[Symbol.toStringTag](){return"Subscribr"}};var v=class extends Error{_entity;responseStatus;_url;_method;_timing;constructor(e,{message:t,cause:r,entity:n,url:o,method:i,timing:u}={}){super(t,{cause:r}),this._entity=n,this.responseStatus=e,this._url=o,this._method=i,this._timing=u}get entity(){return this._entity}get statusCode(){return this.responseStatus.code}get statusText(){return this.responseStatus?.text}get url(){return this._url}get method(){return this._method}get timing(){return this._timing}get name(){return"HttpError"}get[Symbol.toStringTag](){return this.name}};var T=class{_code;_text;constructor(e,t){this._code=e,this._text=t}get code(){return this._code}get text(){return this._text}get[Symbol.toStringTag](){return"ResponseStatus"}toString(){return`${this._code} ${this._text}`}};var w={charset:"utf-8"},Q=/\/$/,Z="XSRF-TOKEN",ee="X-XSRF-TOKEN",R={PNG:new y("image/png"),TEXT:new y("text/plain",w),JSON:new y("application/json",w),HTML:new y("text/html",w),JAVA_SCRIPT:new y("text/javascript",w),CSS:new y("text/css",w),XML:new y("application/xml",w),BIN:new y("application/octet-stream")},x=R.JSON.toString(),te={DEFAULT:"default",FORCE_CACHE:"force-cache",NO_CACHE:"no-cache",NO_STORE:"no-store",ONLY_IF_CACHED:"only-if-cached",RELOAD:"reload"},m={CONFIGURED:"configured",SUCCESS:"success",ERROR:"error",ABORTED:"aborted",TIMEOUT:"timeout",RETRY:"retry",COMPLETE:"complete",ALL_COMPLETE:"all-complete"},O={ABORT:"abort",TIMEOUT:"timeout"},S={ABORT:"AbortError",TIMEOUT:"TimeoutError"},H={once:!0,passive:!0},L=()=>new CustomEvent(O.ABORT,{detail:{cause:S.ABORT}}),se=()=>new CustomEvent(O.TIMEOUT,{detail:{cause:S.TIMEOUT}}),re=["POST","PUT","PATCH","DELETE"],ne=new T(500,"Internal Server Error"),oe=new T(499,"Aborted"),ie=new T(504,"Request Timeout"),I=[408,413,429,500,502,503,504],B=["GET","PUT","HEAD","DELETE","OPTIONS"],k=300,A=2;var M=class{abortSignal;abortController=new AbortController;events=new Map;constructor({signal:e,timeout:t=1/0}={}){if(t<0)throw new RangeError("The timeout cannot be negative");let r=[this.abortController.signal];e!=null&&r.push(e),t!==1/0&&r.push(AbortSignal.timeout(t)),(this.abortSignal=AbortSignal.any(r)).addEventListener(O.ABORT,this,H)}handleEvent({target:{reason:e}}){this.abortController.signal.aborted||e instanceof DOMException&&e.name===S.TIMEOUT&&this.abortSignal.dispatchEvent(se())}get signal(){return this.abortSignal}onAbort(e){return this.addEventListener(O.ABORT,e)}onTimeout(e){return this.addEventListener(O.TIMEOUT,e)}abort(e=L()){this.abortController.abort(e.detail?.cause)}destroy(){this.abortSignal.removeEventListener(O.ABORT,this,H);for(let[e,t]of this.events)this.abortSignal.removeEventListener(t,e,H);return this.events.clear(),this}addEventListener(e,t){return this.abortSignal.addEventListener(e,t,H),this.events.set(t,e),this}get[Symbol.toStringTag](){return"SignalController"}};var ae,qe,N=()=>qe??=import("./OP3JQ447.js").then(({default:s})=>e=>s.sanitize(e)),q=async()=>typeof document<"u"&&typeof DOMParser<"u"&&typeof DocumentFragment<"u"?Promise.resolve():ae??=import("jsdom").then(({JSDOM:s})=>{let{window:e}=new s("<!DOCTYPE html><html><head></head><body></body></html>",{url:"http://localhost"});globalThis.window=e,Object.assign(globalThis,{document:e.document,DOMParser:e.DOMParser,DocumentFragment:e.DocumentFragment})}).catch(()=>{throw ae=void 0,new Error("jsdom is required for HTML/XML/DOM features in Node.js environments. Install it with: npm install jsdom")}),ce=async s=>await s.text(),_=async s=>{await q();let e=URL.createObjectURL(await s.blob());return new Promise((t,r)=>{let n=document.createElement("script");Object.assign(n,{src:e,type:"text/javascript",async:!0}),n.onload=()=>{URL.revokeObjectURL(e),document.head.removeChild(n),t()},n.onerror=()=>{URL.revokeObjectURL(e),document.head.removeChild(n),r(new Error("Script failed to load"))},document.head.appendChild(n)})},D=async s=>{await q();let e=URL.createObjectURL(await s.blob());return new Promise((t,r)=>{let n=document.createElement("link");Object.assign(n,{href:e,type:"text/css",rel:"stylesheet"}),n.onload=()=>t(URL.revokeObjectURL(e)),n.onerror=()=>{URL.revokeObjectURL(e),document.head.removeChild(n),r(new Error("Stylesheet load failed"))},document.head.appendChild(n)})},j=async s=>await s.json(),ue=async s=>await s.blob(),F=async s=>{await q();let e=URL.createObjectURL(await s.blob());return new Promise((t,r)=>{let n=new Image;n.onload=()=>{URL.revokeObjectURL(e),t(n)},n.onerror=()=>{URL.revokeObjectURL(e),r(new Error("Image failed to load"))},n.src=e})},le=async s=>await s.arrayBuffer(),$=async s=>Promise.resolve(s.body),G=async s=>{await q();let e=await N();return new DOMParser().parseFromString(e(await s.text()),"application/xml")},J=async s=>{await q();let e=await N();return new DOMParser().parseFromString(e(await s.text()),"text/html")},de=async s=>{await q();let e=await N();return document.createRange().createContextualFragment(e(await s.text()))};var pe=s=>s!==void 0&&re.includes(s),fe=s=>s instanceof FormData||s instanceof Blob||s instanceof ArrayBuffer||s instanceof ReadableStream||s instanceof URLSearchParams||ArrayBuffer.isView(s),he=s=>{if(typeof document>"u"||!document.cookie)return;let e=`${s}=`,t=document.cookie.split(";");for(let r=0,n=t.length;r<n;r++){let o=t[r].trim();if(o.startsWith(e))return decodeURIComponent(o.slice(e.length))}},ge=s=>JSON.stringify(s),z=s=>s!==null&&typeof s=="string",b=s=>s!==null&&typeof s=="object"&&!Array.isArray(s)&&Object.getPrototypeOf(s)===Object.prototype,V=(...s)=>{let e=s.length;if(e===0)return;if(e===1){let[r]=s;return b(r)?X(r):r}let t={};for(let r of s){if(!b(r))return;for(let[n,o]of Object.entries(r)){let i=t[n];Array.isArray(o)?t[n]=[...o,...Array.isArray(i)?i.filter(u=>!o.includes(u)):[]]:b(o)?t[n]=b(i)?V(i,o):X(o):t[n]=o}}return t};function X(s){if(b(s)){let e={},t=Object.keys(s);for(let r=0,n=t.length,o;r<n;r++)o=t[r],e[o]=X(s[o]);return e}return s}var Re=class s{_baseUrl;_options;subscribr;hooks={beforeRequest:[],afterResponse:[],beforeError:[]};static globalSubscribr=new C;static globalHooks={beforeRequest:[],afterResponse:[],beforeError:[]};static signalControllers=new Set;static inflightRequests=new Map;static mediaTypeCache=new Map(Object.values(R).map(e=>[e.toString(),e]));static contentTypeHandlers=[[R.TEXT.type,ce],[R.JSON.subtype,j],[R.BIN.subtype,$],[R.HTML.subtype,J],[R.XML.subtype,G],[R.PNG.type,F],[R.JAVA_SCRIPT.subtype,_],[R.CSS.subtype,D]];constructor(e=globalThis.location?.origin??"http://localhost",t={}){b(e)&&([e,t]=[globalThis.location?.origin??"http://localhost",e]),this._baseUrl=s.getBaseUrl(e),this._options=s.createOptions(t,s.defaultRequestOptions),this.subscribr=new C}static CredentialsPolicy={INCLUDE:"include",OMIT:"omit",SAME_ORIGIN:"same-origin"};static RequestModes={CORS:"cors",NAVIGATE:"navigate",NO_CORS:"no-cors",SAME_ORIGIN:"same-origin"};static RequestPriorities={HIGH:"high",LOW:"low",AUTO:"auto"};static RedirectPolicies={ERROR:"error",FOLLOW:"follow",MANUAL:"manual"};static ReferrerPolicy={NO_REFERRER:"no-referrer",NO_REFERRER_WHEN_DOWNGRADE:"no-referrer-when-downgrade",ORIGIN:"origin",ORIGIN_WHEN_CROSS_ORIGIN:"origin-when-cross-origin",SAME_ORIGIN:"same-origin",STRICT_ORIGIN:"strict-origin",STRICT_ORIGIN_WHEN_CROSS_ORIGIN:"strict-origin-when-cross-origin",UNSAFE_URL:"unsafe-url"};static RequestEvents=m;static defaultRequestOptions={body:void 0,cache:te.NO_STORE,credentials:s.CredentialsPolicy.SAME_ORIGIN,headers:new Headers({"content-type":x,accept:x}),searchParams:void 0,integrity:void 0,keepalive:void 0,method:"GET",mode:s.RequestModes.CORS,priority:s.RequestPriorities.AUTO,redirect:s.RedirectPolicies.FOLLOW,referrer:"about:client",referrerPolicy:s.ReferrerPolicy.STRICT_ORIGIN_WHEN_CROSS_ORIGIN,signal:void 0,timeout:3e4,global:!0};static register(e,t,r){return s.globalSubscribr.subscribe(e,t,r)}static unregister(e){return s.globalSubscribr.unsubscribe(e)}static abortAll(){for(let e of this.signalControllers)e.abort(L());this.signalControllers.clear()}static registerContentTypeHandler(e,t){s.contentTypeHandlers.unshift([e,t])}static unregisterContentTypeHandler(e){let t=s.contentTypeHandlers.findIndex(([r])=>r===e);return t===-1?!1:(s.contentTypeHandlers.splice(t,1),!0)}static addHooks(e){e.beforeRequest&&s.globalHooks.beforeRequest.push(...e.beforeRequest),e.afterResponse&&s.globalHooks.afterResponse.push(...e.afterResponse),e.beforeError&&s.globalHooks.beforeError.push(...e.beforeError)}static clearHooks(){s.globalHooks={beforeRequest:[],afterResponse:[],beforeError:[]}}static unregisterAll(){s.abortAll(),s.globalSubscribr=new C,s.clearHooks(),s.inflightRequests.clear()}get baseUrl(){return this._baseUrl}register(e,t,r){return this.subscribr.subscribe(e,t,r)}unregister(e){return this.subscribr.unsubscribe(e)}addHooks(e){return e.beforeRequest&&this.hooks.beforeRequest.push(...e.beforeRequest),e.afterResponse&&this.hooks.afterResponse.push(...e.afterResponse),e.beforeError&&this.hooks.beforeError.push(...e.beforeError),this}clearHooks(){return this.hooks.beforeRequest.length=0,this.hooks.afterResponse.length=0,this.hooks.beforeError.length=0,this}destroy(){this.clearHooks(),this.subscribr.destroy()}async get(e,t){return this._get(e,t)}async post(e,t){return typeof e!="string"&&([e,t]=[void 0,e]),this.execute(e,t,{method:"POST"})}async put(e,t){return this.execute(e,t,{method:"PUT"})}async patch(e,t){return this.execute(e,t,{method:"PATCH"})}async delete(e,t){return this.execute(e,t,{method:"DELETE"})}async head(e,t){return this.execute(e,t,{method:"HEAD"})}async options(e,t={}){b(e)&&([e,t]=[void 0,e]);let r=this.processRequestOptions(t,{method:"OPTIONS"}),{requestOptions:n}=r,o=n.hooks,i=s.createUrl(this._baseUrl,e,n.searchParams),u=[s.globalHooks.beforeRequest,this.hooks.beforeRequest,o?.beforeRequest];for(let d of u)if(d)for(let c of d){let g=await c(n,i);g&&(Object.assign(n,g),g.searchParams!==void 0&&(i=s.createUrl(this._baseUrl,e,n.searchParams)))}let a=await this._request(e,r),l=[s.globalHooks.afterResponse,this.hooks.afterResponse,o?.afterResponse];for(let d of l)if(d)for(let c of d){let g=await c(a,n);g&&(a=g)}let h=a.headers.get("allow")?.split(",").map(d=>d.trim());return this.publish({name:m.SUCCESS,data:h,global:t.global}),h}async request(e,t={}){b(e)&&([e,t]=[void 0,e]);let r=await this._request(e,this.processRequestOptions(t,{}));return this.publish({name:m.SUCCESS,data:r,global:t.global}),r}async getJson(e,t){return this._get(e,t,{headers:{accept:`${R.JSON}`}},j)}async getXml(e,t){return this._get(e,t,{headers:{accept:`${R.XML}`}},G)}async getHtml(e,t,r){let n=await this._get(e,t,{headers:{accept:`${R.HTML}`}},J);return r&&n?n.querySelector(r):n}async getHtmlFragment(e,t,r){let n=await this._get(e,t,{headers:{accept:`${R.HTML}`}},de);return r&&n?n.querySelector(r):n}async getScript(e,t){return this._get(e,t,{headers:{accept:`${R.JAVA_SCRIPT}`}},_)}async getStylesheet(e,t){return this._get(e,t,{headers:{accept:`${R.CSS}`}},D)}async getBlob(e,t){return this._get(e,t,{headers:{accept:"application/octet-stream"}},ue)}async getImage(e,t){return this._get(e,t,{headers:{accept:"image/*"}},F)}async getBuffer(e,t){return this._get(e,t,{headers:{accept:"application/octet-stream"}},le)}async getStream(e,t){return this._get(e,t,{headers:{accept:"application/octet-stream"}},$)}async _get(e,t,r={},n){return this.execute(e,t,{...r,method:"GET",body:void 0},n)}async _request(e,{signalController:t,requestOptions:r,global:n}){s.signalControllers.add(t);let o=s.normalizeRetryOptions(r.retry),i=r.method??"GET",u=o.limit>0&&o.methods.includes(i),a=r.dedupe===!0&&(i==="GET"||i==="HEAD"),l=0,h=performance.now(),d=()=>{let c=performance.now();return{start:h,end:c,duration:c-h}};try{let c=s.createUrl(this._baseUrl,e,r.searchParams),g=a?`${i}:${c.href}`:"";if(a){let f=s.inflightRequests.get(g);if(f)return(await f).clone()}let p=async()=>{for(;;)try{let f=await fetch(c,r);if(!f.ok){if(u&&l<o.limit&&o.statusCodes.includes(f.status)){l++,this.publish({name:m.RETRY,data:{attempt:l,status:f.status,method:i,path:e,timing:d()},global:n}),await s.retryDelay(o,l);continue}let U;try{U=await f.text()}catch{}throw await this.handleError(e,f,{entity:U,url:c,method:i,timing:d()},r)}return f}catch(f){if(f instanceof v)throw f;if(u&&l<o.limit){l++,this.publish({name:m.RETRY,data:{attempt:l,error:f.message,method:i,path:e,timing:d()},global:n}),await s.retryDelay(o,l);continue}throw await this.handleError(e,void 0,{cause:f,url:c,method:i,timing:d()},r)}};if(a){let f=p();s.inflightRequests.set(g,f);try{return await f}finally{s.inflightRequests.delete(g)}}return await p()}finally{if(s.signalControllers.delete(t.destroy()),!r.signal?.aborted){let c=d();this.publish({name:m.COMPLETE,data:{timing:c},global:n}),s.signalControllers.size===0&&this.publish({name:m.ALL_COMPLETE,global:n})}}}static normalizeRetryOptions(e){return e===void 0?{limit:0,statusCodes:[],methods:[],delay:k,backoffFactor:A}:typeof e=="number"?{limit:e,statusCodes:[...I],methods:[...B],delay:k,backoffFactor:A}:{limit:e.limit??0,statusCodes:e.statusCodes??[...I],methods:e.methods??[...B],delay:e.delay??k,backoffFactor:e.backoffFactor??A}}static retryDelay(e,t){let r=typeof e.delay=="function"?e.delay(t):e.delay*e.backoffFactor**(t-1);return new Promise(n=>setTimeout(n,r))}async execute(e,t={},r={},n){b(e)&&([e,t]=[void 0,e]);let o=this.processRequestOptions(t,r),{requestOptions:i}=o,u=i.hooks,a=s.createUrl(this._baseUrl,e,i.searchParams),l=[s.globalHooks.beforeRequest,this.hooks.beforeRequest,u?.beforeRequest];for(let c of l)if(c)for(let g of c){let p=await g(i,a);p&&(Object.assign(i,p),p.searchParams!==void 0&&(a=s.createUrl(this._baseUrl,e,i.searchParams)))}let h=await this._request(e,o),d=[s.globalHooks.afterResponse,this.hooks.afterResponse,u?.afterResponse];for(let c of d)if(c)for(let g of c){let p=await g(h,i);p&&(h=p)}try{!n&&h.status!==204&&(n=this.getResponseHandler(h.headers.get("content-type")));let c=await n?.(h);return this.publish({name:m.SUCCESS,data:c,global:o.global}),c}catch(c){throw await this.handleError(e,h,{cause:c},i)}}static createOptions({headers:e,searchParams:t,...r},{headers:n,searchParams:o,...i}){return n=s.mergeHeaders(new Headers,e,n),o=s.mergeSearchParams(new URLSearchParams,t,o),{...V(i,r)??{},headers:n,searchParams:o}}static mergeHeaders(e,...t){for(let r of t)if(r!==void 0)if(r instanceof Headers)r.forEach((n,o)=>e.set(o,n));else if(Array.isArray(r))for(let[n,o]of r)e.set(n,o);else{let n=r,o=Object.keys(n);for(let i=0;i<o.length;i++){let u=o[i],a=n[u];a!==void 0&&e.set(u,String(a))}}return e}static mergeSearchParams(e,...t){for(let r of t)if(r!==void 0)if(r instanceof URLSearchParams)r.forEach((n,o)=>e.set(o,n));else if(z(r)||Array.isArray(r))for(let[n,o]of new URLSearchParams(r))e.set(n,o);else{let n=Object.keys(r);for(let o=0;o<n.length;o++){let i=n[o],u=r[i];u!==void 0&&e.set(i,String(u))}}return e}processRequestOptions({body:e,headers:t,searchParams:r,...n},{headers:o,searchParams:i,...u}){let a={...this._options,...n,...u,headers:s.mergeHeaders(new Headers,this._options.headers,t,o),searchParams:s.mergeSearchParams(new URLSearchParams,this._options.searchParams,r,i)};if(pe(a.method))if(fe(e))a.body=e,a.headers.delete("content-type");else{let p=a.headers.get("content-type")?.includes("json")??!1;a.body=p&&b(e)?ge(e):e}else a.headers.delete("content-type"),a.body instanceof URLSearchParams&&s.mergeSearchParams(a.searchParams,a.body),a.body=void 0;let{signal:l,timeout:h,global:d=!1,xsrf:c}=a;if(c){let p=typeof c=="object"?c:{},f=he(p.cookieName??Z);f&&a.headers.set(p.headerName??ee,f)}let g=new M({signal:l,timeout:h}).onAbort(p=>this.publish({name:m.ABORTED,event:p,global:d})).onTimeout(p=>this.publish({name:m.TIMEOUT,event:p,global:d}));return a.signal=g.signal,this.publish({name:m.CONFIGURED,data:a,global:d}),{signalController:g,requestOptions:a,global:d}}static getBaseUrl(e){if(e instanceof URL)return e;if(!z(e))throw new TypeError("Invalid URL");return new URL(e,e.startsWith("/")?globalThis.location.origin:void 0)}static getOrParseMediaType(e){if(e===null)return;let t=s.mediaTypeCache.get(e);if(t!==void 0)return t;if(t=y.parse(e)??void 0,t!==void 0){if(s.mediaTypeCache.size>=100){let r=s.mediaTypeCache.keys().next().value;r!==void 0&&s.mediaTypeCache.delete(r)}s.mediaTypeCache.set(e,t)}return t}static createUrl(e,t,r){let n=t?new URL(`${e.pathname.replace(Q,"")}${t}`,e.origin):new URL(e);return r&&s.mergeSearchParams(n.searchParams,r),n}static generateResponseStatusFromError(e,{status:t,statusText:r}=new Response){switch(e){case S.ABORT:return oe;case S.TIMEOUT:return ie;default:return t>=400?new T(t,r):ne}}async handleError(e,t,{cause:r,entity:n,url:o,method:i,timing:u}={},a){let l=i&&o?`${i} ${o.href} failed${t?` with status ${t.status}`:""}`:`An error has occurred with your request to: '${e}'`,h=new v(s.generateResponseStatusFromError(r?.name,t),{message:l,cause:r,entity:n,url:o,method:i,timing:u}),d=[s.globalHooks.beforeError,this.hooks.beforeError,a?.hooks?.beforeError];for(let c of d)if(c)for(let g of c){let p=await g(h);p instanceof v&&(h=p)}return this.publish({name:m.ERROR,data:h}),h}publish({name:e,event:t=new CustomEvent(e),data:r,global:n=!0}){n&&s.globalSubscribr.publish(e,t,r),this.subscribr.publish(e,t,r)}getResponseHandler(e){if(!e)return;let t=s.getOrParseMediaType(e);if(t){for(let[r,n]of s.contentTypeHandlers)if(t.matches(r))return n}}get[Symbol.toStringTag](){return"Transportr"}};export{Re as Transportr};
1
+ var C=/^[-!#$%&'*+.^_`|~A-Za-z0-9]*$/u,me=/(["\\])/ug,ye=/^[\t\u0020-\u007E\u0080-\u00FF]*$/u,K=class W extends Map{constructor(e=[]){super(e)}static isValid(e,t){return C.test(e)&&ye.test(t)}get(e){return super.get(e.toLowerCase())}has(e){return super.has(e.toLowerCase())}set(e,t){if(!W.isValid(e,t))throw new Error(`Invalid media type parameter name/value: ${e}/${t}`);return super.set(e.toLowerCase(),t),this}delete(e){return super.delete(e.toLowerCase())}toString(){return Array.from(this).map(([e,t])=>`;${e}=${!t||!C.test(t)?`"${t.replace(me,"\\$1")}"`:t}`).join("")}get[Symbol.toStringTag](){return"MediaTypeParameters"}},be=new Set([" "," ",`
2
+ `,"\r"]),Ee=/[ \t\n\r]+$/u,Te=/^[ \t\n\r]+|[ \t\n\r]+$/ug,Oe=class E{static parse(e){e=e.replace(Te,"");let t=0,[r,n]=E.collect(e,t,["/"]);if(t=n,!r.length||t>=e.length||!C.test(r))throw new TypeError(E.generateErrorMessage("type",r));++t;let[o,i]=E.collect(e,t,[";"],!0,!0);if(t=i,!o.length||!C.test(o))throw new TypeError(E.generateErrorMessage("subtype",o));let l=new K;for(;t<e.length;){for(++t;be.has(e[t]);)++t;let a;if([a,t]=E.collect(e,t,[";","="],!1),t>=e.length||e[t]===";")continue;++t;let c;if(e[t]==='"')for([c,t]=E.collectHttpQuotedString(e,t);t<e.length&&e[t]!==";";)++t;else if([c,t]=E.collect(e,t,[";"],!1,!0),!c)continue;a&&K.isValid(a,c)&&!l.has(a)&&l.set(a,c)}return{type:r,subtype:o,parameters:l}}get[Symbol.toStringTag](){return"MediaTypeParser"}static collect(e,t,r,n=!0,o=!1){let i="";for(let{length:l}=e;t<l&&!r.includes(e[t]);t++)i+=e[t];return n&&(i=i.toLowerCase()),o&&(i=i.replace(Ee,"")),[i,t]}static collectHttpQuotedString(e,t){let r="";for(let n=e.length,o;++t<n&&(o=e[t])!=='"';)r+=o=="\\"&&++t<n?e[t]:o;return[r,t]}static generateErrorMessage(e,t){return`Invalid ${e} "${t}": only HTTP token code points are valid.`}},y=class Y{_type;_subtype;_parameters;constructor(e,t={}){if(t===null||typeof t!="object"||Array.isArray(t))throw new TypeError("The parameters argument must be an object");({type:this._type,subtype:this._subtype,parameters:this._parameters}=Oe.parse(e));for(let[r,n]of Object.entries(t))this._parameters.set(r,n)}static parse(e){try{return new Y(e)}catch{}return null}get type(){return this._type}get subtype(){return this._subtype}get essence(){return`${this._type}/${this._subtype}`}get parameters(){return this._parameters}matches(e){return typeof e=="string"?this.essence.includes(e):this._type===e._type&&this._subtype===e._subtype}toString(){return`${this.essence}${this._parameters.toString()}`}get[Symbol.toStringTag](){return"MediaType"}};var Se=class extends Map{set(s,e){return super.set(s,e instanceof Set?e:(super.get(s)??new Set).add(e)),this}getOrInsert(s,e){return this.has(s)?super.get(s):(super.set(s,e instanceof Set?e:(super.get(s)??new Set).add(e)),e)}getOrInsertComputed(s,e){if(this.has(s))return super.get(s);let t=e(s);return super.set(s,t instanceof Set?t:(super.get(s)??new Set).add(t)),t}find(s,e){let t=this.get(s);if(t!==void 0)return Array.from(t).find(e)}hasValue(s,e){let t=super.get(s);return t?t.has(e):!1}deleteValue(s,e){if(e===void 0)return this.delete(s);let t=super.get(s);if(t){let r=t.delete(e);return t.size===0&&super.delete(s),r}return!1}get[Symbol.toStringTag](){return"SetMultiMap"}},ve=class{context;eventHandler;constructor(s,e){this.context=s,this.eventHandler=e}handle(s,e){this.eventHandler.call(this.context,s,e)}get[Symbol.toStringTag](){return"ContextEventHandler"}},we=class{_eventName;_contextEventHandler;constructor(s,e){this._eventName=s,this._contextEventHandler=e}get eventName(){return this._eventName}get contextEventHandler(){return this._contextEventHandler}get[Symbol.toStringTag](){return"Subscription"}},P=class{subscribers=new Se;errorHandler;setErrorHandler(s){this.errorHandler=s}subscribe(s,e,t=e,r){if(this.validateEventName(s),r?.once){let i=e;e=(l,a)=>{i.call(t,l,a),this.unsubscribe(o)}}let n=new ve(t,e);this.subscribers.set(s,n);let o=new we(s,n);return o}unsubscribe({eventName:s,contextEventHandler:e}){let t=this.subscribers.get(s)??new Set,r=t.delete(e);return r&&t.size===0&&this.subscribers.delete(s),r}publish(s,e=new CustomEvent(s),t){this.validateEventName(s),this.subscribers.get(s)?.forEach(r=>{try{r.handle(e,t)}catch(n){this.errorHandler?this.errorHandler(n,s,e,t):console.error(`Error in event handler for '${s}':`,n)}})}isSubscribed({eventName:s,contextEventHandler:e}){return this.subscribers.get(s)?.has(e)??!1}validateEventName(s){if(!s||typeof s!="string")throw new TypeError("Event name must be a non-empty string");if(s.trim()!==s)throw new Error("Event name cannot have leading or trailing whitespace")}destroy(){this.subscribers.clear()}get[Symbol.toStringTag](){return"Subscribr"}};var v=class extends Error{_entity;responseStatus;_url;_method;_timing;constructor(e,{message:t,cause:r,entity:n,url:o,method:i,timing:l}={}){super(t,{cause:r}),this._entity=n,this.responseStatus=e,this._url=o,this._method=i,this._timing=l}get entity(){return this._entity}get statusCode(){return this.responseStatus.code}get statusText(){return this.responseStatus?.text}get url(){return this._url}get method(){return this._method}get timing(){return this._timing}get name(){return"HttpError"}get[Symbol.toStringTag](){return this.name}};var T=class{_code;_text;constructor(e,t){this._code=e,this._text=t}get code(){return this._code}get text(){return this._text}get[Symbol.toStringTag](){return"ResponseStatus"}toString(){return`${this._code} ${this._text}`}};var w={charset:"utf-8"},Q=/\/$/,Z="XSRF-TOKEN",ee="X-XSRF-TOKEN",R={PNG:new y("image/png"),TEXT:new y("text/plain",w),JSON:new y("application/json",w),HTML:new y("text/html",w),JAVA_SCRIPT:new y("text/javascript",w),CSS:new y("text/css",w),XML:new y("application/xml",w),BIN:new y("application/octet-stream")},x=R.JSON.toString(),te={DEFAULT:"default",FORCE_CACHE:"force-cache",NO_CACHE:"no-cache",NO_STORE:"no-store",ONLY_IF_CACHED:"only-if-cached",RELOAD:"reload"},m={CONFIGURED:"configured",SUCCESS:"success",ERROR:"error",ABORTED:"aborted",TIMEOUT:"timeout",RETRY:"retry",COMPLETE:"complete",ALL_COMPLETE:"all-complete"},O={ABORT:"abort",TIMEOUT:"timeout"},S={ABORT:"AbortError",TIMEOUT:"TimeoutError"},H={once:!0,passive:!0},L=()=>new CustomEvent(O.ABORT,{detail:{cause:S.ABORT}}),se=()=>new CustomEvent(O.TIMEOUT,{detail:{cause:S.TIMEOUT}}),re=["POST","PUT","PATCH","DELETE"],ne=new T(500,"Internal Server Error"),oe=new T(499,"Aborted"),ie=new T(504,"Request Timeout"),I=[408,413,429,500,502,503,504],N=["GET","PUT","HEAD","DELETE","OPTIONS"],k=300,A=2;var U=class{abortSignal;abortController=new AbortController;events=new Map;constructor({signal:e,timeout:t=1/0}={}){if(t<0)throw new RangeError("The timeout cannot be negative");let r=[this.abortController.signal];e!=null&&r.push(e),t!==1/0&&r.push(AbortSignal.timeout(t)),(this.abortSignal=AbortSignal.any(r)).addEventListener(O.ABORT,this,H)}handleEvent({target:{reason:e}}){this.abortController.signal.aborted||e instanceof DOMException&&e.name===S.TIMEOUT&&this.abortSignal.dispatchEvent(se())}get signal(){return this.abortSignal}onAbort(e){return this.addEventListener(O.ABORT,e)}onTimeout(e){return this.addEventListener(O.TIMEOUT,e)}abort(e=L()){this.abortController.abort(e.detail?.cause)}destroy(){this.abortSignal.removeEventListener(O.ABORT,this,H);for(let[e,t]of this.events)this.abortSignal.removeEventListener(t,e,H);return this.events.clear(),this}addEventListener(e,t){return this.abortSignal.addEventListener(e,t,H),this.events.set(t,e),this}get[Symbol.toStringTag](){return"SignalController"}};var ae,qe,B=()=>qe??=import("./OP3JQ447.js").then(({default:s})=>e=>s.sanitize(e)),q=async()=>typeof document<"u"&&typeof DOMParser<"u"&&typeof DocumentFragment<"u"?Promise.resolve():ae??=import("jsdom").then(({JSDOM:s})=>{let{window:e}=new s("<!DOCTYPE html><html><head></head><body></body></html>",{url:"http://localhost"});globalThis.window=e,Object.assign(globalThis,{document:e.document,DOMParser:e.DOMParser,DocumentFragment:e.DocumentFragment})}).catch(()=>{throw ae=void 0,new Error("jsdom is required for HTML/XML/DOM features in Node.js environments. Install it with: npm install jsdom")}),ue=async s=>await s.text(),_=async s=>{await q();let e=URL.createObjectURL(await s.blob());return new Promise((t,r)=>{let n=document.createElement("script");Object.assign(n,{src:e,type:"text/javascript",async:!0}),n.onload=()=>{URL.revokeObjectURL(e),document.head.removeChild(n),t()},n.onerror=()=>{URL.revokeObjectURL(e),document.head.removeChild(n),r(new Error("Script failed to load"))},document.head.appendChild(n)})},D=async s=>{await q();let e=URL.createObjectURL(await s.blob());return new Promise((t,r)=>{let n=document.createElement("link");Object.assign(n,{href:e,type:"text/css",rel:"stylesheet"}),n.onload=()=>t(URL.revokeObjectURL(e)),n.onerror=()=>{URL.revokeObjectURL(e),document.head.removeChild(n),r(new Error("Stylesheet load failed"))},document.head.appendChild(n)})},j=async s=>await s.json(),le=async s=>await s.blob(),F=async s=>{await q();let e=URL.createObjectURL(await s.blob());return new Promise((t,r)=>{let n=new Image;n.onload=()=>{URL.revokeObjectURL(e),t(n)},n.onerror=()=>{URL.revokeObjectURL(e),r(new Error("Image failed to load"))},n.src=e})},ce=async s=>await s.arrayBuffer(),$=async s=>Promise.resolve(s.body),G=async s=>{await q();let e=await B();return new DOMParser().parseFromString(e(await s.text()),"application/xml")},J=async s=>{await q();let e=await B();return new DOMParser().parseFromString(e(await s.text()),"text/html")},de=async s=>{await q();let e=await B();return document.createRange().createContextualFragment(e(await s.text()))};var pe=s=>s!==void 0&&re.includes(s),fe=s=>s instanceof FormData||s instanceof Blob||s instanceof ArrayBuffer||s instanceof ReadableStream||s instanceof URLSearchParams||ArrayBuffer.isView(s),he=s=>{if(typeof document>"u"||!document.cookie)return;let e=`${s}=`,t=document.cookie.split(";");for(let r=0,n=t.length;r<n;r++){let o=t[r].trim();if(o.startsWith(e))return decodeURIComponent(o.slice(e.length))}},ge=s=>JSON.stringify(s),z=s=>s!==null&&typeof s=="string",b=s=>s!==null&&typeof s=="object"&&!Array.isArray(s)&&Object.getPrototypeOf(s)===Object.prototype,V=(...s)=>{let e=s.length;if(e===0)return;if(e===1){let[r]=s;return b(r)?X(r):r}let t={};for(let r of s){if(!b(r))return;for(let[n,o]of Object.entries(r)){let i=t[n];Array.isArray(o)?t[n]=[...o,...Array.isArray(i)?i.filter(l=>!o.includes(l)):[]]:b(o)?t[n]=b(i)?V(i,o):X(o):t[n]=o}}return t};function X(s){if(b(s)){let e={},t=Object.keys(s);for(let r=0,n=t.length,o;r<n;r++)o=t[r],e[o]=X(s[o]);return e}return s}var Re=class s{_baseUrl;_options;subscribr;hooks={beforeRequest:[],afterResponse:[],beforeError:[]};static globalSubscribr=new P;static globalHooks={beforeRequest:[],afterResponse:[],beforeError:[]};static signalControllers=new Set;static inflightRequests=new Map;static mediaTypeCache=new Map(Object.values(R).map(e=>[e.toString(),e]));static contentTypeHandlers=[[R.TEXT.type,ue],[R.JSON.subtype,j],[R.BIN.subtype,$],[R.HTML.subtype,J],[R.XML.subtype,G],[R.PNG.type,F],[R.JAVA_SCRIPT.subtype,_],[R.CSS.subtype,D]];constructor(e=globalThis.location?.origin??"http://localhost",t={}){b(e)&&([e,t]=[globalThis.location?.origin??"http://localhost",e]),this._baseUrl=s.getBaseUrl(e),this._options=s.createOptions(t,s.defaultRequestOptions),this.subscribr=new P}static CredentialsPolicy={INCLUDE:"include",OMIT:"omit",SAME_ORIGIN:"same-origin"};static RequestModes={CORS:"cors",NAVIGATE:"navigate",NO_CORS:"no-cors",SAME_ORIGIN:"same-origin"};static RequestPriorities={HIGH:"high",LOW:"low",AUTO:"auto"};static RedirectPolicies={ERROR:"error",FOLLOW:"follow",MANUAL:"manual"};static ReferrerPolicy={NO_REFERRER:"no-referrer",NO_REFERRER_WHEN_DOWNGRADE:"no-referrer-when-downgrade",ORIGIN:"origin",ORIGIN_WHEN_CROSS_ORIGIN:"origin-when-cross-origin",SAME_ORIGIN:"same-origin",STRICT_ORIGIN:"strict-origin",STRICT_ORIGIN_WHEN_CROSS_ORIGIN:"strict-origin-when-cross-origin",UNSAFE_URL:"unsafe-url"};static RequestEvents=m;static defaultRequestOptions={body:void 0,cache:te.NO_STORE,credentials:s.CredentialsPolicy.SAME_ORIGIN,headers:new Headers({"content-type":x,accept:x}),searchParams:void 0,integrity:void 0,keepalive:void 0,method:"GET",mode:s.RequestModes.CORS,priority:s.RequestPriorities.AUTO,redirect:s.RedirectPolicies.FOLLOW,referrer:"about:client",referrerPolicy:s.ReferrerPolicy.STRICT_ORIGIN_WHEN_CROSS_ORIGIN,signal:void 0,timeout:3e4,global:!0};static register(e,t,r){return s.globalSubscribr.subscribe(e,t,r)}static unregister(e){return s.globalSubscribr.unsubscribe(e)}static abortAll(){for(let e of this.signalControllers)e.abort(L());this.signalControllers.clear()}static registerContentTypeHandler(e,t){s.contentTypeHandlers.unshift([e,t])}static unregisterContentTypeHandler(e){let t=s.contentTypeHandlers.findIndex(([r])=>r===e);return t===-1?!1:(s.contentTypeHandlers.splice(t,1),!0)}static addHooks(e){e.beforeRequest&&s.globalHooks.beforeRequest.push(...e.beforeRequest),e.afterResponse&&s.globalHooks.afterResponse.push(...e.afterResponse),e.beforeError&&s.globalHooks.beforeError.push(...e.beforeError)}static clearHooks(){s.globalHooks={beforeRequest:[],afterResponse:[],beforeError:[]}}static unregisterAll(){s.abortAll(),s.globalSubscribr=new P,s.clearHooks(),s.inflightRequests.clear()}get baseUrl(){return this._baseUrl}register(e,t,r){return this.subscribr.subscribe(e,t,r)}unregister(e){return this.subscribr.unsubscribe(e)}addHooks(e){return e.beforeRequest&&this.hooks.beforeRequest.push(...e.beforeRequest),e.afterResponse&&this.hooks.afterResponse.push(...e.afterResponse),e.beforeError&&this.hooks.beforeError.push(...e.beforeError),this}clearHooks(){return this.hooks.beforeRequest.length=0,this.hooks.afterResponse.length=0,this.hooks.beforeError.length=0,this}destroy(){this.clearHooks(),this.subscribr.destroy()}async get(e,t){return this._get(e,t)}async post(e,t){return typeof e!="string"&&([e,t]=[void 0,e]),this.execute(e,t,{method:"POST"})}async put(e,t){return this.execute(e,t,{method:"PUT"})}async patch(e,t){return this.execute(e,t,{method:"PATCH"})}async delete(e,t){return this.execute(e,t,{method:"DELETE"})}async head(e,t){return this.execute(e,t,{method:"HEAD"})}async options(e,t={}){b(e)&&([e,t]=[void 0,e]);let r=this.processRequestOptions(t,{method:"OPTIONS"}),{requestOptions:n}=r,o=n.hooks,i=s.createUrl(this._baseUrl,e,n.searchParams),l=[s.globalHooks.beforeRequest,this.hooks.beforeRequest,o?.beforeRequest];for(let d of l)if(d)for(let u of d){let g=await u(n,i);g&&(Object.assign(n,g),g.searchParams!==void 0&&(i=s.createUrl(this._baseUrl,e,n.searchParams)))}let a=await this._request(e,r),c=[s.globalHooks.afterResponse,this.hooks.afterResponse,o?.afterResponse];for(let d of c)if(d)for(let u of d){let g=await u(a,n);g&&(a=g)}let h=a.headers.get("allow")?.split(",").map(d=>d.trim());return this.publish({name:m.SUCCESS,data:h,global:t.global}),h}async request(e,t={}){b(e)&&([e,t]=[void 0,e]);let r=await this._request(e,this.processRequestOptions(t,{}));return this.publish({name:m.SUCCESS,data:r,global:t.global}),r}async getJson(e,t){return this._get(e,t,{headers:{accept:`${R.JSON}`}},j)}async getXml(e,t){return this._get(e,t,{headers:{accept:`${R.XML}`}},G)}async getHtml(e,t,r){let n=await this._get(e,t,{headers:{accept:`${R.HTML}`}},J);return r&&n?n.querySelector(r):n}async getHtmlFragment(e,t,r){let n=await this._get(e,t,{headers:{accept:`${R.HTML}`}},de);return r&&n?n.querySelector(r):n}async getScript(e,t){return this._get(e,t,{headers:{accept:`${R.JAVA_SCRIPT}`}},_)}async getStylesheet(e,t){return this._get(e,t,{headers:{accept:`${R.CSS}`}},D)}async getBlob(e,t){return this._get(e,t,{headers:{accept:"application/octet-stream"}},le)}async getImage(e,t){return this._get(e,t,{headers:{accept:"image/*"}},F)}async getBuffer(e,t){return this._get(e,t,{headers:{accept:"application/octet-stream"}},ce)}async getStream(e,t){return this._get(e,t,{headers:{accept:"application/octet-stream"}},$)}async _get(e,t,r={},n){return this.execute(e,t,{...r,method:"GET",body:void 0},n)}async _request(e,{signalController:t,requestOptions:r,global:n}){s.signalControllers.add(t);let o=s.normalizeRetryOptions(r.retry),i=r.method??"GET",l=o.limit>0&&o.methods.includes(i),a=r.dedupe===!0&&(i==="GET"||i==="HEAD"),c=0,h=performance.now(),d=()=>{let u=performance.now();return{start:h,end:u,duration:u-h}};try{let u=s.createUrl(this._baseUrl,e,r.searchParams),g=a?`${i}:${u.href}`:"";if(a){let f=s.inflightRequests.get(g);if(f)return(await f).clone()}let p=async()=>{for(;;)try{let f=await fetch(u,r);if(!f.ok){if(l&&c<o.limit&&o.statusCodes.includes(f.status)){c++,this.publish({name:m.RETRY,data:{attempt:c,status:f.status,method:i,path:e,timing:d()},global:n}),await s.retryDelay(o,c);continue}let M;try{M=await f.text()}catch{}throw await this.handleError(e,f,{entity:M,url:u,method:i,timing:d()},r)}return f}catch(f){if(f instanceof v)throw f;if(l&&c<o.limit){c++,this.publish({name:m.RETRY,data:{attempt:c,error:f.message,method:i,path:e,timing:d()},global:n}),await s.retryDelay(o,c);continue}throw await this.handleError(e,void 0,{cause:f,url:u,method:i,timing:d()},r)}};if(a){let f=p();s.inflightRequests.set(g,f);try{return await f}finally{s.inflightRequests.delete(g)}}return await p()}finally{if(s.signalControllers.delete(t.destroy()),!r.signal?.aborted){let u=d();this.publish({name:m.COMPLETE,data:{timing:u},global:n}),s.signalControllers.size===0&&this.publish({name:m.ALL_COMPLETE,global:n})}}}static normalizeRetryOptions(e){return e===void 0?{limit:0,statusCodes:[],methods:[],delay:k,backoffFactor:A}:typeof e=="number"?{limit:e,statusCodes:[...I],methods:[...N],delay:k,backoffFactor:A}:{limit:e.limit??0,statusCodes:e.statusCodes??[...I],methods:e.methods??[...N],delay:e.delay??k,backoffFactor:e.backoffFactor??A}}static retryDelay(e,t){let r=typeof e.delay=="function"?e.delay(t):e.delay*e.backoffFactor**(t-1);return new Promise(n=>setTimeout(n,r))}async execute(e,t={},r={},n){b(e)&&([e,t]=[void 0,e]);let o=this.processRequestOptions(t,r),{requestOptions:i}=o,l=i.hooks,a=s.createUrl(this._baseUrl,e,i.searchParams),c=[s.globalHooks.beforeRequest,this.hooks.beforeRequest,l?.beforeRequest];for(let u of c)if(u)for(let g of u){let p=await g(i,a);p&&(Object.assign(i,p),p.searchParams!==void 0&&(a=s.createUrl(this._baseUrl,e,i.searchParams)))}let h=await this._request(e,o),d=[s.globalHooks.afterResponse,this.hooks.afterResponse,l?.afterResponse];for(let u of d)if(u)for(let g of u){let p=await g(h,i);p&&(h=p)}try{!n&&h.status!==204&&(n=this.getResponseHandler(h.headers.get("content-type")));let u=await n?.(h);return this.publish({name:m.SUCCESS,data:u,global:o.global}),u}catch(u){throw await this.handleError(e,h,{cause:u},i)}}static createOptions({headers:e,searchParams:t,...r},{headers:n,searchParams:o,...i}){return n=s.mergeHeaders(new Headers,e,n),o=s.mergeSearchParams(new URLSearchParams,t,o),{...V(i,r)??{},headers:n,searchParams:o}}static mergeHeaders(e,...t){for(let r of t)if(r!==void 0)if(r instanceof Headers)r.forEach((n,o)=>e.set(o,n));else if(Array.isArray(r))for(let[n,o]of r)e.set(n,o);else{let n=r,o=Object.keys(n);for(let i=0;i<o.length;i++){let l=o[i],a=n[l];a!==void 0&&e.set(l,String(a))}}return e}static mergeSearchParams(e,...t){for(let r of t)if(r!==void 0)if(r instanceof URLSearchParams)r.forEach((n,o)=>e.set(o,n));else if(z(r)||Array.isArray(r))for(let[n,o]of new URLSearchParams(r))e.set(n,o);else{let n=Object.keys(r);for(let o=0;o<n.length;o++){let i=n[o],l=r[i];l!==void 0&&e.set(i,String(l))}}return e}processRequestOptions({body:e,headers:t,searchParams:r,...n},{headers:o,searchParams:i,...l}){let a={...this._options,...n,...l,headers:s.mergeHeaders(new Headers,this._options.headers,t,o),searchParams:s.mergeSearchParams(new URLSearchParams,this._options.searchParams,r,i)};if(pe(a.method))if(fe(e))a.body=e,a.headers.delete("content-type");else{let p=a.headers.get("content-type")?.includes("json")??!1;a.body=p&&b(e)?ge(e):e}else a.headers.delete("content-type"),a.body instanceof URLSearchParams&&s.mergeSearchParams(a.searchParams,a.body),a.body=void 0;let{signal:c,timeout:h,global:d=!1,xsrf:u}=a;if(u){let p=typeof u=="object"?u:{},f=he(p.cookieName??Z);f&&a.headers.set(p.headerName??ee,f)}let g=new U({signal:c,timeout:h}).onAbort(p=>this.publish({name:m.ABORTED,event:p,global:d})).onTimeout(p=>this.publish({name:m.TIMEOUT,event:p,global:d}));return a.signal=g.signal,this.publish({name:m.CONFIGURED,data:a,global:d}),{signalController:g,requestOptions:a,global:d}}static getBaseUrl(e){if(e instanceof URL)return e;if(!z(e))throw new TypeError("Invalid URL");return new URL(e,e.startsWith("/")?globalThis.location.origin:void 0)}static getOrParseMediaType(e){if(e===null)return;let t=s.mediaTypeCache.get(e);if(t!==void 0)return t;if(t=y.parse(e)??void 0,t!==void 0){if(s.mediaTypeCache.size>=100){let r=s.mediaTypeCache.keys().next().value;r!==void 0&&s.mediaTypeCache.delete(r)}s.mediaTypeCache.set(e,t)}return t}static createUrl(e,t,r){let n=t?new URL(`${e.pathname.replace(Q,"")}${t}`,e.origin):new URL(e);return r&&s.mergeSearchParams(n.searchParams,r),n}static generateResponseStatusFromError(e,{status:t,statusText:r}=new Response){switch(e){case S.ABORT:return oe;case S.TIMEOUT:return ie;default:return t>=400?new T(t,r):ne}}async handleError(e,t,{cause:r,entity:n,url:o,method:i,timing:l}={},a){let c=i&&o?`${i} ${o.href} failed${t?` with status ${t.status}`:""}`:`An error has occurred with your request to: '${e}'`,h=new v(s.generateResponseStatusFromError(r?.name,t),{message:c,cause:r,entity:n,url:o,method:i,timing:l}),d=[s.globalHooks.beforeError,this.hooks.beforeError,a?.hooks?.beforeError];for(let u of d)if(u)for(let g of u){let p=await g(h);p instanceof v&&(h=p)}return this.publish({name:m.ERROR,data:h}),h}publish({name:e,event:t=new CustomEvent(e),data:r,global:n=!0}){n&&s.globalSubscribr.publish(e,t,r),this.subscribr.publish(e,t,r)}getResponseHandler(e){if(!e)return;let t=s.getOrParseMediaType(e);if(t){for(let[r,n]of s.contentTypeHandlers)if(t.matches(r))return n}}get[Symbol.toStringTag](){return"Transportr"}};export{Re as Transportr};
3
3
  //# sourceMappingURL=transportr.js.map