katanakit-js 6.2.0 → 6.6.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.
Files changed (116) hide show
  1. package/README.md +11 -3
  2. package/dist/adapters/better-auth/better-auth.service.d.ts +83 -0
  3. package/dist/adapters/better-auth/better-auth.service.d.ts.map +1 -0
  4. package/dist/adapters/better-auth/better-auth.service.js +110 -0
  5. package/dist/adapters/better-auth/better-auth.service.js.map +1 -0
  6. package/dist/adapters/better-auth/client.d.ts +51 -0
  7. package/dist/adapters/better-auth/client.d.ts.map +1 -0
  8. package/dist/adapters/better-auth/client.js +67 -0
  9. package/dist/adapters/better-auth/client.js.map +1 -0
  10. package/dist/adapters/better-auth/index.d.ts +3 -0
  11. package/dist/adapters/better-auth/index.d.ts.map +1 -0
  12. package/dist/adapters/better-auth/index.js +3 -0
  13. package/dist/adapters/better-auth/index.js.map +1 -0
  14. package/dist/adapters/express/server.d.ts +133 -1
  15. package/dist/adapters/express/server.d.ts.map +1 -1
  16. package/dist/adapters/express/server.js +199 -97
  17. package/dist/adapters/express/server.js.map +1 -1
  18. package/dist/adapters/index.d.ts +2 -0
  19. package/dist/adapters/index.d.ts.map +1 -1
  20. package/dist/adapters/index.js +2 -0
  21. package/dist/adapters/index.js.map +1 -1
  22. package/dist/adapters/insforge/insforge.service.d.ts +139 -4
  23. package/dist/adapters/insforge/insforge.service.d.ts.map +1 -1
  24. package/dist/adapters/insforge/insforge.service.js +383 -213
  25. package/dist/adapters/insforge/insforge.service.js.map +1 -1
  26. package/dist/adapters/notion/notion.service.d.ts +130 -144
  27. package/dist/adapters/notion/notion.service.d.ts.map +1 -1
  28. package/dist/adapters/notion/notion.service.js +330 -341
  29. package/dist/adapters/notion/notion.service.js.map +1 -1
  30. package/dist/adapters/photoswipe/index.d.ts +2 -0
  31. package/dist/adapters/photoswipe/index.d.ts.map +1 -0
  32. package/dist/adapters/photoswipe/index.js +2 -0
  33. package/dist/adapters/photoswipe/index.js.map +1 -0
  34. package/dist/adapters/photoswipe/photoswipe.service.d.ts +99 -0
  35. package/dist/adapters/photoswipe/photoswipe.service.d.ts.map +1 -0
  36. package/dist/adapters/photoswipe/photoswipe.service.js +150 -0
  37. package/dist/adapters/photoswipe/photoswipe.service.js.map +1 -0
  38. package/dist/adapters/telegram/telegram.service.d.ts +82 -13
  39. package/dist/adapters/telegram/telegram.service.d.ts.map +1 -1
  40. package/dist/adapters/telegram/telegram.service.js +133 -55
  41. package/dist/adapters/telegram/telegram.service.js.map +1 -1
  42. package/dist/adapters/wordpress/wordpress.service.d.ts +181 -110
  43. package/dist/adapters/wordpress/wordpress.service.d.ts.map +1 -1
  44. package/dist/adapters/wordpress/wordpress.service.js +556 -465
  45. package/dist/adapters/wordpress/wordpress.service.js.map +1 -1
  46. package/dist/config/seo.service.d.ts +1 -1
  47. package/dist/config/seo.service.d.ts.map +1 -1
  48. package/dist/config/seo.service.js +1 -1
  49. package/dist/config/seo.service.js.map +1 -1
  50. package/dist/core/services/access.service.d.ts +167 -4
  51. package/dist/core/services/access.service.d.ts.map +1 -1
  52. package/dist/core/services/access.service.js +241 -31
  53. package/dist/core/services/access.service.js.map +1 -1
  54. package/dist/core/services/agent.service.d.ts +182 -27
  55. package/dist/core/services/agent.service.d.ts.map +1 -1
  56. package/dist/core/services/agent.service.js +296 -164
  57. package/dist/core/services/agent.service.js.map +1 -1
  58. package/dist/core/services/assistant.service.d.ts +96 -5
  59. package/dist/core/services/assistant.service.d.ts.map +1 -1
  60. package/dist/core/services/assistant.service.js +171 -66
  61. package/dist/core/services/assistant.service.js.map +1 -1
  62. package/dist/core/services/dates.service.d.ts +183 -11
  63. package/dist/core/services/dates.service.d.ts.map +1 -1
  64. package/dist/core/services/dates.service.js +256 -57
  65. package/dist/core/services/dates.service.js.map +1 -1
  66. package/dist/core/services/http.service.d.ts +172 -133
  67. package/dist/core/services/http.service.d.ts.map +1 -1
  68. package/dist/core/services/http.service.js +367 -310
  69. package/dist/core/services/http.service.js.map +1 -1
  70. package/dist/core/services/logger.service.d.ts +96 -34
  71. package/dist/core/services/logger.service.d.ts.map +1 -1
  72. package/dist/core/services/logger.service.js +128 -41
  73. package/dist/core/services/logger.service.js.map +1 -1
  74. package/dist/core/services/query.service.d.ts +101 -0
  75. package/dist/core/services/query.service.d.ts.map +1 -1
  76. package/dist/core/services/query.service.js +131 -14
  77. package/dist/core/services/query.service.js.map +1 -1
  78. package/dist/core/services/utils.service.d.ts +286 -131
  79. package/dist/core/services/utils.service.d.ts.map +1 -1
  80. package/dist/core/services/utils.service.js +417 -227
  81. package/dist/core/services/utils.service.js.map +1 -1
  82. package/dist/core/services/validation.service.d.ts +48 -3
  83. package/dist/core/services/validation.service.d.ts.map +1 -1
  84. package/dist/core/services/validation.service.js +75 -23
  85. package/dist/core/services/validation.service.js.map +1 -1
  86. package/dist/infrastructure/decorators/cache.decorator.d.ts +53 -0
  87. package/dist/infrastructure/decorators/cache.decorator.d.ts.map +1 -0
  88. package/dist/infrastructure/decorators/cache.decorator.js +47 -0
  89. package/dist/infrastructure/decorators/cache.decorator.js.map +1 -0
  90. package/dist/infrastructure/decorators/index.d.ts +4 -0
  91. package/dist/infrastructure/decorators/index.d.ts.map +1 -0
  92. package/dist/infrastructure/decorators/index.js +4 -0
  93. package/dist/infrastructure/decorators/index.js.map +1 -0
  94. package/dist/infrastructure/decorators/logger.decorator.d.ts +38 -0
  95. package/dist/infrastructure/decorators/logger.decorator.d.ts.map +1 -0
  96. package/dist/infrastructure/decorators/logger.decorator.js +66 -0
  97. package/dist/infrastructure/decorators/logger.decorator.js.map +1 -0
  98. package/dist/infrastructure/decorators/retry.decorator.d.ts +37 -0
  99. package/dist/infrastructure/decorators/retry.decorator.d.ts.map +1 -0
  100. package/dist/infrastructure/decorators/retry.decorator.js +54 -0
  101. package/dist/infrastructure/decorators/retry.decorator.js.map +1 -0
  102. package/dist/infrastructure/index.d.ts +1 -0
  103. package/dist/infrastructure/index.d.ts.map +1 -1
  104. package/dist/infrastructure/index.js +1 -0
  105. package/dist/infrastructure/index.js.map +1 -1
  106. package/dist/styles.css +2 -0
  107. package/package.json +49 -14
  108. package/scss/_flags.scss +179 -0
  109. package/scss/_functions.scss +168 -0
  110. package/scss/_mixins.scss +658 -0
  111. package/scss/_reset.scss +275 -0
  112. package/scss/_utilities.scss +449 -0
  113. package/scss/_variables.scss +539 -0
  114. package/scss/components/_index.scss +50 -0
  115. package/scss/demo.scss +12 -0
  116. package/scss/main.scss +56 -0
@@ -1,7 +1,7 @@
1
1
  import { useTryJsonParse } from "./result.service.js";
2
2
  import { useSplitConfig } from "./utils.service.js";
3
- /** Module-level API registry. */
4
- let apis = {};
3
+ /** Error bodies are truncated so a hostile response cannot exhaust memory. */
4
+ const MAX_ERROR_BODY_CHARS = 32 * 1024;
5
5
  /**
6
6
  * Internal: narrows an unknown literal entry to an {@link ApiEntry}.
7
7
  *
@@ -11,28 +11,12 @@ let apis = {};
11
11
  function isApiEntry(value) {
12
12
  return typeof value === "object" && value !== null && "baseUri" in value && "endpoints" in value;
13
13
  }
14
- /**
15
- * Internal: retrieves a registered API entry or throws.
16
- *
17
- * @param apiName - The key of the API in the registry.
18
- * @returns The {@link ApiEntry} for the given name.
19
- * @throws {Error} If the API is not registered.
20
- */
21
- function getApiEntry(apiName) {
22
- const api = apis[apiName];
23
- if (!api) {
24
- throw new Error(`[FetchApiManager] API "${apiName}" is not registered.`);
25
- }
26
- return api;
27
- }
28
14
  /**
29
15
  * Internal: reads a Response body, attempting JSON parse for JSON content types.
30
16
  *
31
17
  * @param response - The fetch Response to read.
32
18
  * @returns The parsed body or `null` for empty responses.
33
19
  */
34
- /** Error bodies are truncated so a hostile response cannot exhaust memory. */
35
- const MAX_ERROR_BODY_CHARS = 32 * 1024;
36
20
  async function readBody(response, maxChars) {
37
21
  const text = await response.text();
38
22
  if (!text)
@@ -72,7 +56,338 @@ function serializeBody(body) {
72
56
  };
73
57
  }
74
58
  /**
75
- * Declares the API registry in a single call — the config-file entry point.
59
+ * Picks the URL-building keys out of an options bag so nothing else leaks into
60
+ * the `RequestInit` handed to `fetch`.
61
+ *
62
+ * @param urlOptions - Legacy URL options, if any.
63
+ * @returns Only `params`, `query` and `ignoreDefaultQuery`, when defined.
64
+ */
65
+ function pickUrl(urlOptions) {
66
+ if (!urlOptions)
67
+ return {};
68
+ const { params, query, ignoreDefaultQuery } = urlOptions;
69
+ return {
70
+ ...(params !== undefined && { params }),
71
+ ...(query !== undefined && { query }),
72
+ ...(ignoreDefaultQuery !== undefined && { ignoreDefaultQuery }),
73
+ };
74
+ }
75
+ /**
76
+ * FetchApiManager - Singleton para el registro de APIs y peticiones HTTP.
77
+ *
78
+ * Aplica el patrón Singleton: una única instancia mantiene el registro de APIs
79
+ * (`ApisConfig`) y centraliza la construcción de URLs y las peticiones fetch,
80
+ * devolviendo siempre Safe Results (nunca lanza en rutas async).
81
+ *
82
+ * @example
83
+ * ```ts
84
+ * const fetchApi = FetchApiManager.getInstance();
85
+ * fetchApi.useInit({ pokeapi: { baseUri: "https://pokeapi.co/api/v2", endpoints: { byId: "/pokemon/:id/" } } });
86
+ * const result = await fetchApi.useFetch("pokeapi", "byId", { params: { id: 25 } });
87
+ * ```
88
+ */
89
+ export class FetchApiManager {
90
+ static instance;
91
+ /** Module-level API registry. */
92
+ apis = {};
93
+ /**
94
+ * Constructor privado - enforce singleton.
95
+ */
96
+ constructor() { }
97
+ /**
98
+ * Obtiene la instancia única del FetchApiManager (Singleton).
99
+ *
100
+ * @returns La instancia única de {@link FetchApiManager}.
101
+ */
102
+ static getInstance() {
103
+ if (!FetchApiManager.instance) {
104
+ FetchApiManager.instance = new FetchApiManager();
105
+ }
106
+ return FetchApiManager.instance;
107
+ }
108
+ /**
109
+ * Internal: retrieves a registered API entry or throws.
110
+ *
111
+ * @param apiName - The key of the API in the registry.
112
+ * @returns The {@link ApiEntry} for the given name.
113
+ * @throws {Error} If the API is not registered.
114
+ */
115
+ useGetApiEntry(apiName) {
116
+ const api = this.apis[apiName];
117
+ if (!api) {
118
+ throw new Error(`[FetchApiManager] API "${apiName}" is not registered.`);
119
+ }
120
+ return api;
121
+ }
122
+ /**
123
+ * Registers (merges) API definitions into the client registry.
124
+ *
125
+ * @param apisConfig - The API definitions to merge into the registry.
126
+ *
127
+ * @example
128
+ * ```ts
129
+ * FetchApiManager.getInstance().useInit({
130
+ * pokeapi: { baseUri: "https://pokeapi.co/api/v2", endpoints: { pokemonById: "/pokemon/:id/" } },
131
+ * });
132
+ * ```
133
+ */
134
+ useInit(apisConfig) {
135
+ this.apis = { ...this.apis, ...apisConfig };
136
+ }
137
+ /**
138
+ * Returns a shallow copy of the registered APIs (safe to mutate locally).
139
+ *
140
+ * @returns A shallow copy of the current API registry.
141
+ */
142
+ useGetApis() {
143
+ return { ...this.apis };
144
+ }
145
+ /**
146
+ * Returns the URL of a registered endpoint **without calling `fetch`**.
147
+ *
148
+ * It builds the same URL {@link useFetch} would request - same registry,
149
+ * same `:param` substitution, same `query` merge with `defaultQueryParams`,
150
+ * same http(s) guard - then stops and hands you the string.
151
+ *
152
+ * @param apiName - Registered API key (from `defineApiConfig`).
153
+ * @param endpointName - Endpoint key inside that API.
154
+ * @param options - `params` fills `:placeholders`, `query` becomes the search string.
155
+ * @returns The fully built URL.
156
+ * @throws {Error} Unknown API, unknown endpoint, or a scheme that is not http(s).
157
+ *
158
+ * @example
159
+ * ```ts
160
+ * FetchApiManager.getInstance().useBuildUrl("pokeapi", "pokemonById", { params: { id: 25 } });
161
+ * // → "https://pokeapi.co/api/v2/pokemon/25"
162
+ * ```
163
+ *
164
+ * @remarks Synchronous, so it **throws** instead of returning a Safe Result -
165
+ * Safe Result covers fallible async work, and this never leaves the process.
166
+ */
167
+ useBuildUrl(apiName, endpointName, options = {}) {
168
+ const { params, query, ignoreDefaultQuery = false } = options;
169
+ const api = this.useGetApiEntry(apiName);
170
+ let path = api.endpoints?.[endpointName] ?? "";
171
+ if (!path) {
172
+ throw new Error(`[FetchApiManager] Endpoint "${endpointName}" not found in API "${apiName}".`);
173
+ }
174
+ if (params) {
175
+ const escapeRegex = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
176
+ path = Object.entries(params).reduce((acc, [key, value]) => acc.replace(new RegExp(`:${escapeRegex(key)}\\b`, "g"), encodeURIComponent(String(value))), path);
177
+ }
178
+ const baseStr = api.baseUri instanceof URL ? api.baseUri.toString() : api.baseUri;
179
+ const baseClean = baseStr.endsWith("/") ? baseStr.slice(0, -1) : baseStr;
180
+ const pathClean = path.startsWith("/") ? path : `/${path}`;
181
+ const url = new URL(`${baseClean}${pathClean}`);
182
+ // Only allow http(s) to prevent SSRF and `javascript:` URLs.
183
+ if (url.protocol !== "http:" && url.protocol !== "https:") {
184
+ throw new Error(`[FetchApiManager] Scheme "${url.protocol}" is not allowed.`);
185
+ }
186
+ const defaultParams = ignoreDefaultQuery ? {} : (api.defaultQueryParams?.[endpointName] ?? {});
187
+ const mergedQuery = { ...defaultParams, ...query };
188
+ for (const [key, value] of Object.entries(mergedQuery)) {
189
+ if (value !== undefined && value !== null) {
190
+ url.searchParams.set(key, String(value));
191
+ }
192
+ }
193
+ return url.toString();
194
+ }
195
+ /**
196
+ * Fetches a registered endpoint and returns a Safe Result.
197
+ *
198
+ * URL options sit at the top level, next to `method`/`headers`/`body` - no
199
+ * nesting, same shape as `useGet` and Nuxt's `useFetch`.
200
+ *
201
+ * @param apiName - The registered API key.
202
+ * @param endpointName - The endpoint key within the API.
203
+ * @param options - Fetch options: `params`, `query`, `transform` and any `RequestInit` field.
204
+ * @returns A `FetchResult` with data or error. Never throws - a failing
205
+ * `transform` comes back as an error result too.
206
+ *
207
+ * @example
208
+ * ```ts
209
+ * const result = await FetchApiManager.getInstance().useFetch<{ results: unknown[] }>(
210
+ * "pokeapi",
211
+ * "pokemons",
212
+ * { method: "GET", query: { limit: 2 } },
213
+ * );
214
+ * if (result.ok) console.log(result.data.results);
215
+ * ```
216
+ */
217
+ async useFetch(apiName, endpointName, { params, query, ignoreDefaultQuery, urlOptions, transform, ...init } = {}) {
218
+ let url = "";
219
+ // The flat keys and the deprecated `urlOptions` nest describe the same
220
+ // thing; the flat form wins. `undefined` means "not given", so it must not
221
+ // overwrite a value coming from the legacy nest.
222
+ const buildOptions = { ...urlOptions };
223
+ if (params !== undefined)
224
+ buildOptions.params = params;
225
+ if (query !== undefined)
226
+ buildOptions.query = query;
227
+ if (ignoreDefaultQuery !== undefined)
228
+ buildOptions.ignoreDefaultQuery = ignoreDefaultQuery;
229
+ try {
230
+ url = this.useBuildUrl(apiName, endpointName, buildOptions);
231
+ }
232
+ catch (err) {
233
+ const message = err instanceof Error ? err.message : String(err);
234
+ return {
235
+ data: null,
236
+ error: {
237
+ message: `Config Error: ${message}`,
238
+ status: 0,
239
+ },
240
+ url,
241
+ status: 0,
242
+ ok: false,
243
+ };
244
+ }
245
+ try {
246
+ // Fail closed on redirects: the scheme allow-list only validates the
247
+ // initial URL, so following a 3xx could steer the request to a private
248
+ // host (SSRF). Callers that need redirects can pass `redirect: "follow"`.
249
+ const response = await fetch(url, { redirect: "error", ...init });
250
+ if (!response.ok) {
251
+ const errorDetails = await readBody(response, MAX_ERROR_BODY_CHARS);
252
+ return {
253
+ data: null,
254
+ error: {
255
+ message: `HTTP Error: ${response.statusText || "Unsuccessful response"}`,
256
+ status: response.status,
257
+ details: errorDetails,
258
+ },
259
+ url: response.url || url,
260
+ status: response.status,
261
+ ok: false,
262
+ };
263
+ }
264
+ // 204 No Content and empty bodies are valid success responses.
265
+ const body = response.status === 204 ? null : await readBody(response);
266
+ let data = body;
267
+ if (transform) {
268
+ try {
269
+ data = await transform(body);
270
+ }
271
+ catch (err) {
272
+ // A failing transform is still a failed request - return it as a
273
+ // Safe Result instead of letting it escape `useFetch`.
274
+ const message = err instanceof Error ? err.message : String(err);
275
+ return {
276
+ data: null,
277
+ error: {
278
+ message: `Transform Error: ${message}`,
279
+ status: response.status,
280
+ },
281
+ url: response.url || url,
282
+ status: response.status,
283
+ ok: false,
284
+ };
285
+ }
286
+ }
287
+ return {
288
+ data: data,
289
+ error: null,
290
+ url: response.url || url,
291
+ status: response.status,
292
+ ok: true,
293
+ };
294
+ }
295
+ catch (err) {
296
+ const message = err instanceof Error ? err.message : String(err);
297
+ return {
298
+ data: null,
299
+ error: {
300
+ message: `Network/Client Error: ${message}`,
301
+ status: 0,
302
+ },
303
+ url,
304
+ status: 0,
305
+ ok: false,
306
+ };
307
+ }
308
+ }
309
+ /**
310
+ * GET helper over a registered API endpoint.
311
+ *
312
+ * @param apiName - The registered API key.
313
+ * @param endpointName - The endpoint key within the API.
314
+ * @param urlOptions - Optional URL building options.
315
+ * @returns A `FetchResult` with data or error.
316
+ */
317
+ async useGet(apiName, endpointName, urlOptions) {
318
+ return this.useFetch(apiName, endpointName, {
319
+ method: "GET",
320
+ ...pickUrl(urlOptions),
321
+ });
322
+ }
323
+ /**
324
+ * POST helper over a registered API endpoint.
325
+ *
326
+ * @param apiName - The registered API key.
327
+ * @param endpointName - The endpoint key within the API.
328
+ * @param body - Optional request body (auto-serialized to JSON unless raw).
329
+ * @param urlOptions - Optional URL building options.
330
+ * @returns A `FetchResult` with data or error.
331
+ */
332
+ async usePost(apiName, endpointName, body, urlOptions) {
333
+ return this.useFetch(apiName, endpointName, {
334
+ method: "POST",
335
+ ...serializeBody(body),
336
+ ...pickUrl(urlOptions),
337
+ });
338
+ }
339
+ /**
340
+ * PUT helper over a registered API endpoint.
341
+ *
342
+ * @param apiName - The registered API key.
343
+ * @param endpointName - The endpoint key within the API.
344
+ * @param body - Optional request body (auto-serialized to JSON unless raw).
345
+ * @param urlOptions - Optional URL building options.
346
+ * @returns A `FetchResult` with data or error.
347
+ */
348
+ async usePut(apiName, endpointName, body, urlOptions) {
349
+ return this.useFetch(apiName, endpointName, {
350
+ method: "PUT",
351
+ ...serializeBody(body),
352
+ ...pickUrl(urlOptions),
353
+ });
354
+ }
355
+ /**
356
+ * PATCH helper over a registered API endpoint.
357
+ *
358
+ * @param apiName - The registered API key.
359
+ * @param endpointName - The endpoint key within the API.
360
+ * @param body - Optional request body (auto-serialized to JSON unless raw).
361
+ * @param urlOptions - Optional URL building options.
362
+ * @returns A `FetchResult` with data or error.
363
+ */
364
+ async usePatch(apiName, endpointName, body, urlOptions) {
365
+ return this.useFetch(apiName, endpointName, {
366
+ method: "PATCH",
367
+ ...serializeBody(body),
368
+ ...pickUrl(urlOptions),
369
+ });
370
+ }
371
+ /**
372
+ * DELETE helper over a registered API endpoint.
373
+ *
374
+ * @param apiName - The registered API key.
375
+ * @param endpointName - The endpoint key within the API.
376
+ * @param urlOptions - Optional URL building options.
377
+ * @returns A `FetchResult` with data or error.
378
+ */
379
+ async useDelete(apiName, endpointName, urlOptions) {
380
+ return this.useFetch(apiName, endpointName, {
381
+ method: "DELETE",
382
+ ...pickUrl(urlOptions),
383
+ });
384
+ }
385
+ }
386
+ /* -------------------------------------------------------------------------- */
387
+ /* define*Config */
388
+ /* -------------------------------------------------------------------------- */
389
+ /**
390
+ * Declares the API registry in a single call - the config-file entry point.
76
391
  *
77
392
  * Registers every {@link ApiEntry} you list and returns them alongside the
78
393
  * props, flags and methods declared on the same literal, so one `export default`
@@ -83,7 +398,7 @@ function serializeBody(body) {
83
398
  *
84
399
  * @typeParam T - The literal passed in.
85
400
  * @param input - API entries, flags and methods in one object literal.
86
- * @returns The registered config plus the declared methods (see {@link ConfigFacade}).
401
+ * @returns The registered config plus the declared methods (see `ConfigFacade`).
87
402
  * @throws {Error} If a non-method value is an object that is not an API entry.
88
403
  *
89
404
  * @example
@@ -99,7 +414,7 @@ function serializeBody(body) {
99
414
  * });
100
415
  * ```
101
416
  *
102
- * @remarks Methods must be written with `function`, not as arrows — `this.config`
417
+ * @remarks Methods must be written with `function`, not as arrows - `this.config`
103
418
  * is undefined inside an arrow function.
104
419
  */
105
420
  export function defineApiConfig(input) {
@@ -119,6 +434,9 @@ export function defineApiConfig(input) {
119
434
  // `methods` is a generic spread, so TS cannot prove the exact facade shape.
120
435
  return { config, ...methods };
121
436
  }
437
+ /* -------------------------------------------------------------------------- */
438
+ /* Wrappers (backward compatibility) */
439
+ /* -------------------------------------------------------------------------- */
122
440
  /**
123
441
  * Registers (merges) API definitions into the client registry.
124
442
  *
@@ -131,36 +449,18 @@ export function defineApiConfig(input) {
131
449
  * ```ts
132
450
  * import { useInit } from "katanakit-js";
133
451
  *
134
- * useInit({
135
- * pokeapi: {
136
- * baseUri: "https://pokeapi.co/api/v2",
137
- * endpoints: { pokemonById: "/pokemon/:id/" },
138
- * },
139
- * });
452
+ * useInit({ pokeapi: { baseUri: "https://pokeapi.co/api/v2", endpoints: { pokemonById: "/pokemon/:id/" } } });
140
453
  * ```
141
454
  */
142
455
  export function useInit(apisConfig) {
143
- apis = { ...apis, ...apisConfig };
456
+ FetchApiManager.getInstance().useInit(apisConfig);
144
457
  }
145
458
  /**
146
459
  * Registers (merges) API definitions into the client registry.
147
460
  *
148
- * @deprecated Use {@link defineApiConfig}, which registers the config and
149
- * returns it with any methods declared on the same literal.
461
+ * @deprecated Use {@link defineApiConfig}.
150
462
  *
151
463
  * @param apisConfig - The API definitions to merge into the registry.
152
- *
153
- * @example
154
- * ```ts
155
- * import { useInitApis } from "katanakit-js";
156
- *
157
- * useInitApis({
158
- * pokeapi: {
159
- * baseUri: "https://pokeapi.co/api/v2",
160
- * endpoints: { pokemonById: "/pokemon/:id/" },
161
- * },
162
- * });
163
- * ```
164
464
  */
165
465
  export function useInitApis(apisConfig) {
166
466
  useInit(apisConfig);
@@ -178,20 +478,12 @@ export function useInitApis(apisConfig) {
178
478
  * ```
179
479
  */
180
480
  export function useGetApis() {
181
- return { ...apis };
481
+ return FetchApiManager.getInstance().useGetApis();
182
482
  }
183
483
  /**
184
- * Returns a shallow copy of the registered APIs.
185
- * Alias for {@link useGetApis}.
484
+ * Returns a shallow copy of the registered APIs. Alias for {@link useGetApis}.
186
485
  *
187
486
  * @returns A shallow copy of the current API registry.
188
- *
189
- * @example
190
- * ```ts
191
- * import { useGetApisConfig } from "katanakit-js";
192
- *
193
- * const apis = useGetApisConfig();
194
- * ```
195
487
  */
196
488
  export function useGetApisConfig() {
197
489
  return useGetApis();
@@ -199,18 +491,7 @@ export function useGetApisConfig() {
199
491
  /**
200
492
  * Returns the URL of a registered endpoint **without calling `fetch`**.
201
493
  *
202
- * It builds the same URL {@link useFetch} would request — same registry, same
203
- * `:param` substitution, same `query` merge with `defaultQueryParams`, same
204
- * http(s) guard — then stops and hands you the string.
205
- *
206
- * Use it when you need the **address**, not the response:
207
- *
208
- * - `<a href>` / `<img src>` — links and media built from an endpoint name
209
- * - libraries that fetch on their own (charts, maps, analytics)
210
- * - logging the exact URL you are about to request
211
- * - SSR / prerender, where nothing should be requested
212
- *
213
- * @param apiName - Registered API key (from {@link defineApiConfig}).
494
+ * @param apiName - Registered API key (from `defineApiConfig`).
214
495
  * @param endpointName - Endpoint key inside that API.
215
496
  * @param options - `params` fills `:placeholders`, `query` becomes the search string.
216
497
  * @returns The fully built URL.
@@ -222,208 +503,43 @@ export function useGetApisConfig() {
222
503
  *
223
504
  * useBuildUrl("pokeapi", "pokemonById", { params: { id: 25 } });
224
505
  * // → "https://pokeapi.co/api/v2/pokemon/25"
225
- *
226
- * useBuildUrl("myApi", "export", { query: { format: "pdf" } });
227
- * // → "https://api.myapp.com/v1/export?format=pdf"
228
- *
229
- * <a href={useBuildUrl("myApi", "download", { params: { id: 42 } })}>Download</a>
230
506
  * ```
231
- *
232
- * @remarks Synchronous, so it **throws** instead of returning a Safe Result —
233
- * Safe Result covers fallible async work, and this never leaves the process.
234
- * To see the URL a request actually used, read `result.url` off a `useFetch`
235
- * result instead.
236
507
  */
237
508
  export function useBuildUrl(apiName, endpointName, options = {}) {
238
- const { params, query, ignoreDefaultQuery = false } = options;
239
- const api = getApiEntry(apiName);
240
- let path = api.endpoints?.[endpointName] ?? "";
241
- if (!path) {
242
- throw new Error(`[FetchApiManager] Endpoint "${endpointName}" not found in API "${apiName}".`);
243
- }
244
- if (params) {
245
- const escapeRegex = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
246
- path = Object.entries(params).reduce((acc, [key, value]) => acc.replace(new RegExp(`:${escapeRegex(key)}\\b`, "g"), encodeURIComponent(String(value))), path);
247
- }
248
- const baseStr = api.baseUri instanceof URL ? api.baseUri.toString() : api.baseUri;
249
- const baseClean = baseStr.endsWith("/") ? baseStr.slice(0, -1) : baseStr;
250
- const pathClean = path.startsWith("/") ? path : `/${path}`;
251
- const url = new URL(`${baseClean}${pathClean}`);
252
- // Only allow http(s) to prevent SSRF and `javascript:` URLs.
253
- if (url.protocol !== "http:" && url.protocol !== "https:") {
254
- throw new Error(`[FetchApiManager] Scheme "${url.protocol}" is not allowed.`);
255
- }
256
- const defaultParams = ignoreDefaultQuery ? {} : (api.defaultQueryParams?.[endpointName] ?? {});
257
- const mergedQuery = { ...defaultParams, ...query };
258
- for (const [key, value] of Object.entries(mergedQuery)) {
259
- if (value !== undefined && value !== null) {
260
- url.searchParams.set(key, String(value));
261
- }
262
- }
263
- return url.toString();
509
+ return FetchApiManager.getInstance().useBuildUrl(apiName, endpointName, options);
264
510
  }
265
511
  /**
266
512
  * Builds a safe http(s) URL from a registered API + endpoint.
267
513
  *
268
- * @deprecated Use {@link useBuildUrl}. This is a behaviourally identical wrapper around it and
269
- * exists only for backwards compatibility — scheduled for removal in the next major.
514
+ * @deprecated Use {@link useBuildUrl}. Behaviourally identical wrapper kept
515
+ * for backwards compatibility - scheduled for removal in the next major.
270
516
  *
271
517
  * @param apiName - The registered API key.
272
518
  * @param endpointName - The endpoint key within the API.
273
519
  * @param options - Optional URL building options.
274
520
  * @returns The fully constructed URL string.
275
- *
276
- * @example
277
- * ```ts
278
- * import { useBuildApiUrl } from "katanakit-js";
279
- *
280
- * const url = useBuildApiUrl("pokeapi", "pokemonById", { params: { id: 25 } });
281
- * ```
282
521
  */
283
522
  export function useBuildApiUrl(apiName, endpointName, options = {}) {
284
523
  return useBuildUrl(apiName, endpointName, options);
285
524
  }
286
- /**
287
- * Picks the URL-building keys out of an options bag so nothing else leaks into
288
- * the `RequestInit` handed to `fetch`.
289
- *
290
- * `useGet`/`usePost`/… accept `UrlOptions`, but JS callers can stuff arbitrary
291
- * keys in there; spreading the whole bag would let a stray `method`, `headers`
292
- * or `body` silently override what the wrapper already set.
293
- *
294
- * @param urlOptions - Legacy URL options, if any.
295
- * @returns Only `params`, `query` and `ignoreDefaultQuery`, when defined.
296
- */
297
- function pickUrl(urlOptions) {
298
- if (!urlOptions)
299
- return {};
300
- const { params, query, ignoreDefaultQuery } = urlOptions;
301
- return {
302
- ...(params !== undefined && { params }),
303
- ...(query !== undefined && { query }),
304
- ...(ignoreDefaultQuery !== undefined && { ignoreDefaultQuery }),
305
- };
306
- }
307
525
  /**
308
526
  * Fetches a registered endpoint and returns a Safe Result.
309
527
  *
310
- * URL options sit at the top level, next to `method`/`headers`/`body` — no
311
- * nesting, same shape as `useGet` and Nuxt's `useFetch`.
312
- *
313
528
  * @param apiName - The registered API key.
314
529
  * @param endpointName - The endpoint key within the API.
315
530
  * @param options - Fetch options: `params`, `query`, `transform` and any `RequestInit` field.
316
- * @returns A {@link FetchResult} with data or error. Never throws — a failing
317
- * `transform` comes back as an error result too.
531
+ * @returns A `FetchResult` with data or error. Never throws.
318
532
  *
319
533
  * @example
320
534
  * ```ts
321
535
  * import { useFetch } from "katanakit-js";
322
536
  *
323
- * const result = await useFetch<{ results: unknown[] }>("pokeapi", "pokemons", {
324
- * method: "GET",
325
- * query: { limit: 2 },
326
- * transform: (body) => ({ results: (body as { results: unknown[] }).results }),
327
- * });
328
- * if (result.ok) console.log(result.data.results);
329
- * ```
330
- *
331
- * @example Legacy nested form (deprecated)
332
- * ```ts
333
- * useFetch("pokeapi", "pokemonById", { urlOptions: { params: { id: 25 } } });
537
+ * const result = await useFetch("pokeapi", "pokemonById", { params: { id: 25 } });
538
+ * if (result.ok) console.log(result.data);
334
539
  * ```
335
540
  */
336
- export async function useFetch(apiName, endpointName, { params, query, ignoreDefaultQuery, urlOptions, transform, ...init } = {}) {
337
- let url = "";
338
- // The flat keys and the deprecated `urlOptions` nest describe the same
339
- // thing; the flat form wins. `undefined` means "not given", so it must not
340
- // overwrite a value coming from the legacy nest.
341
- const buildOptions = { ...urlOptions };
342
- if (params !== undefined)
343
- buildOptions.params = params;
344
- if (query !== undefined)
345
- buildOptions.query = query;
346
- if (ignoreDefaultQuery !== undefined)
347
- buildOptions.ignoreDefaultQuery = ignoreDefaultQuery;
348
- try {
349
- url = useBuildUrl(apiName, endpointName, buildOptions);
350
- }
351
- catch (err) {
352
- const message = err instanceof Error ? err.message : String(err);
353
- return {
354
- data: null,
355
- error: {
356
- message: `Config Error: ${message}`,
357
- status: 0,
358
- },
359
- url,
360
- status: 0,
361
- ok: false,
362
- };
363
- }
364
- try {
365
- // Fail closed on redirects: the scheme allow-list only validates the
366
- // initial URL, so following a 3xx could steer the request to a private
367
- // host (SSRF). Callers that need redirects can pass `redirect: "follow"`.
368
- const response = await fetch(url, { redirect: "error", ...init });
369
- if (!response.ok) {
370
- const errorDetails = await readBody(response, MAX_ERROR_BODY_CHARS);
371
- return {
372
- data: null,
373
- error: {
374
- message: `HTTP Error: ${response.statusText || "Unsuccessful response"}`,
375
- status: response.status,
376
- details: errorDetails,
377
- },
378
- url: response.url || url,
379
- status: response.status,
380
- ok: false,
381
- };
382
- }
383
- // 204 No Content and empty bodies are valid success responses.
384
- const body = response.status === 204 ? null : await readBody(response);
385
- let data = body;
386
- if (transform) {
387
- try {
388
- data = await transform(body);
389
- }
390
- catch (err) {
391
- // A failing transform is still a failed request — return it as a
392
- // Safe Result instead of letting it escape `useFetch`.
393
- const message = err instanceof Error ? err.message : String(err);
394
- return {
395
- data: null,
396
- error: {
397
- message: `Transform Error: ${message}`,
398
- status: response.status,
399
- },
400
- url: response.url || url,
401
- status: response.status,
402
- ok: false,
403
- };
404
- }
405
- }
406
- return {
407
- data: data,
408
- error: null,
409
- url: response.url || url,
410
- status: response.status,
411
- ok: true,
412
- };
413
- }
414
- catch (err) {
415
- const message = err instanceof Error ? err.message : String(err);
416
- return {
417
- data: null,
418
- error: {
419
- message: `Network/Client Error: ${message}`,
420
- status: 0,
421
- },
422
- url,
423
- status: 0,
424
- ok: false,
425
- };
426
- }
541
+ export async function useFetch(apiName, endpointName, options = {}) {
542
+ return FetchApiManager.getInstance().useFetch(apiName, endpointName, options);
427
543
  }
428
544
  /**
429
545
  * Fetches a registered endpoint and returns a Safe Result.
@@ -432,18 +548,7 @@ export async function useFetch(apiName, endpointName, { params, query, ignoreDef
432
548
  * @param apiName - The registered API key.
433
549
  * @param endpointName - The endpoint key within the API.
434
550
  * @param options - Optional fetch options.
435
- * @returns A {@link FetchResult} with data or error.
436
- *
437
- * @example
438
- * ```ts
439
- * import { useFetchApi } from "katanakit-js";
440
- *
441
- * const result = await useFetchApi("pokeapi", "pokemonById", {
442
- * method: "GET",
443
- * params: { id: 25 },
444
- * });
445
- * if (result.ok) console.log(result.data);
446
- * ```
551
+ * @returns A `FetchResult` with data or error.
447
552
  */
448
553
  export async function useFetchApi(apiName, endpointName, options) {
449
554
  return useFetch(apiName, endpointName, options);
@@ -454,37 +559,25 @@ export async function useFetchApi(apiName, endpointName, options) {
454
559
  * @param apiName - The registered API key.
455
560
  * @param endpointName - The endpoint key within the API.
456
561
  * @param urlOptions - Optional URL building options.
457
- * @returns A {@link FetchResult} with data or error.
562
+ * @returns A `FetchResult` with data or error.
458
563
  *
459
564
  * @example
460
565
  * ```ts
461
566
  * import { useGet } from "katanakit-js";
462
567
  *
463
- * const result = await useGet<{ name: string }>("pokeapi", "pokemonById", {
464
- * params: { id: 25 },
465
- * });
568
+ * const result = await useGet("pokeapi", "pokemonById", { params: { id: 25 } });
466
569
  * ```
467
570
  */
468
571
  export async function useGet(apiName, endpointName, urlOptions) {
469
- return useFetch(apiName, endpointName, { method: "GET", ...pickUrl(urlOptions) });
572
+ return FetchApiManager.getInstance().useGet(apiName, endpointName, urlOptions);
470
573
  }
471
574
  /**
472
- * GET helper over a registered API endpoint.
473
- * Alias for {@link useGet}.
575
+ * GET helper over a registered API endpoint. Alias for {@link useGet}.
474
576
  *
475
577
  * @param apiName - The registered API key.
476
578
  * @param endpointName - The endpoint key within the API.
477
579
  * @param urlOptions - Optional URL building options.
478
- * @returns A {@link FetchResult} with data or error.
479
- *
480
- * @example
481
- * ```ts
482
- * import { useGetApi } from "katanakit-js";
483
- *
484
- * const result = await useGetApi<{ name: string }>("pokeapi", "pokemonById", {
485
- * params: { id: 25 },
486
- * });
487
- * ```
580
+ * @returns A `FetchResult` with data or error.
488
581
  */
489
582
  export async function useGetApi(apiName, endpointName, urlOptions) {
490
583
  return useGet(apiName, endpointName, urlOptions);
@@ -496,7 +589,7 @@ export async function useGetApi(apiName, endpointName, urlOptions) {
496
589
  * @param endpointName - The endpoint key within the API.
497
590
  * @param body - Optional request body (auto-serialized to JSON unless raw).
498
591
  * @param urlOptions - Optional URL building options.
499
- * @returns A {@link FetchResult} with data or error.
592
+ * @returns A `FetchResult` with data or error.
500
593
  *
501
594
  * @example
502
595
  * ```ts
@@ -506,11 +599,7 @@ export async function useGetApi(apiName, endpointName, urlOptions) {
506
599
  * ```
507
600
  */
508
601
  export async function usePost(apiName, endpointName, body, urlOptions) {
509
- return useFetch(apiName, endpointName, {
510
- method: "POST",
511
- ...serializeBody(body),
512
- ...pickUrl(urlOptions),
513
- });
602
+ return FetchApiManager.getInstance().usePost(apiName, endpointName, body, urlOptions);
514
603
  }
515
604
  /**
516
605
  * PUT helper over a registered API endpoint.
@@ -519,21 +608,10 @@ export async function usePost(apiName, endpointName, body, urlOptions) {
519
608
  * @param endpointName - The endpoint key within the API.
520
609
  * @param body - Optional request body (auto-serialized to JSON unless raw).
521
610
  * @param urlOptions - Optional URL building options.
522
- * @returns A {@link FetchResult} with data or error.
523
- *
524
- * @example
525
- * ```ts
526
- * import { usePut } from "katanakit-js";
527
- *
528
- * const result = await usePut("pokeapi", "updatePokemon", { name: "Raichu" }, { params: { id: 25 } });
529
- * ```
611
+ * @returns A `FetchResult` with data or error.
530
612
  */
531
613
  export async function usePut(apiName, endpointName, body, urlOptions) {
532
- return useFetch(apiName, endpointName, {
533
- method: "PUT",
534
- ...serializeBody(body),
535
- ...pickUrl(urlOptions),
536
- });
614
+ return FetchApiManager.getInstance().usePut(apiName, endpointName, body, urlOptions);
537
615
  }
538
616
  /**
539
617
  * PATCH helper over a registered API endpoint.
@@ -542,21 +620,10 @@ export async function usePut(apiName, endpointName, body, urlOptions) {
542
620
  * @param endpointName - The endpoint key within the API.
543
621
  * @param body - Optional request body (auto-serialized to JSON unless raw).
544
622
  * @param urlOptions - Optional URL building options.
545
- * @returns A {@link FetchResult} with data or error.
546
- *
547
- * @example
548
- * ```ts
549
- * import { usePatch } from "katanakit-js";
550
- *
551
- * const result = await usePatch("pokeapi", "updatePokemon", { name: "Raichu" }, { params: { id: 25 } });
552
- * ```
623
+ * @returns A `FetchResult` with data or error.
553
624
  */
554
625
  export async function usePatch(apiName, endpointName, body, urlOptions) {
555
- return useFetch(apiName, endpointName, {
556
- method: "PATCH",
557
- ...serializeBody(body),
558
- ...pickUrl(urlOptions),
559
- });
626
+ return FetchApiManager.getInstance().usePatch(apiName, endpointName, body, urlOptions);
560
627
  }
561
628
  /**
562
629
  * DELETE helper over a registered API endpoint.
@@ -564,19 +631,9 @@ export async function usePatch(apiName, endpointName, body, urlOptions) {
564
631
  * @param apiName - The registered API key.
565
632
  * @param endpointName - The endpoint key within the API.
566
633
  * @param urlOptions - Optional URL building options.
567
- * @returns A {@link FetchResult} with data or error.
568
- *
569
- * @example
570
- * ```ts
571
- * import { useDelete } from "katanakit-js";
572
- *
573
- * const result = await useDelete("pokeapi", "deletePokemon", { params: { id: 25 } });
574
- * ```
634
+ * @returns A `FetchResult` with data or error.
575
635
  */
576
636
  export async function useDelete(apiName, endpointName, urlOptions) {
577
- return useFetch(apiName, endpointName, {
578
- method: "DELETE",
579
- ...pickUrl(urlOptions),
580
- });
637
+ return FetchApiManager.getInstance().useDelete(apiName, endpointName, urlOptions);
581
638
  }
582
639
  //# sourceMappingURL=http.service.js.map