katanakit-js 6.4.3 → 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 (88) hide show
  1. package/README.md +4 -3
  2. package/dist/adapters/express/server.d.ts +133 -1
  3. package/dist/adapters/express/server.d.ts.map +1 -1
  4. package/dist/adapters/express/server.js +199 -97
  5. package/dist/adapters/express/server.js.map +1 -1
  6. package/dist/adapters/insforge/insforge.service.d.ts +139 -4
  7. package/dist/adapters/insforge/insforge.service.d.ts.map +1 -1
  8. package/dist/adapters/insforge/insforge.service.js +383 -213
  9. package/dist/adapters/insforge/insforge.service.js.map +1 -1
  10. package/dist/adapters/notion/notion.service.d.ts +130 -144
  11. package/dist/adapters/notion/notion.service.d.ts.map +1 -1
  12. package/dist/adapters/notion/notion.service.js +330 -341
  13. package/dist/adapters/notion/notion.service.js.map +1 -1
  14. package/dist/adapters/telegram/telegram.service.d.ts +82 -13
  15. package/dist/adapters/telegram/telegram.service.d.ts.map +1 -1
  16. package/dist/adapters/telegram/telegram.service.js +133 -55
  17. package/dist/adapters/telegram/telegram.service.js.map +1 -1
  18. package/dist/adapters/wordpress/wordpress.service.d.ts +181 -110
  19. package/dist/adapters/wordpress/wordpress.service.d.ts.map +1 -1
  20. package/dist/adapters/wordpress/wordpress.service.js +556 -465
  21. package/dist/adapters/wordpress/wordpress.service.js.map +1 -1
  22. package/dist/core/services/access.service.d.ts +167 -4
  23. package/dist/core/services/access.service.d.ts.map +1 -1
  24. package/dist/core/services/access.service.js +241 -31
  25. package/dist/core/services/access.service.js.map +1 -1
  26. package/dist/core/services/agent.service.d.ts +182 -27
  27. package/dist/core/services/agent.service.d.ts.map +1 -1
  28. package/dist/core/services/agent.service.js +296 -164
  29. package/dist/core/services/agent.service.js.map +1 -1
  30. package/dist/core/services/assistant.service.d.ts +96 -5
  31. package/dist/core/services/assistant.service.d.ts.map +1 -1
  32. package/dist/core/services/assistant.service.js +171 -66
  33. package/dist/core/services/assistant.service.js.map +1 -1
  34. package/dist/core/services/dates.service.d.ts +183 -11
  35. package/dist/core/services/dates.service.d.ts.map +1 -1
  36. package/dist/core/services/dates.service.js +256 -57
  37. package/dist/core/services/dates.service.js.map +1 -1
  38. package/dist/core/services/http.service.d.ts +172 -133
  39. package/dist/core/services/http.service.d.ts.map +1 -1
  40. package/dist/core/services/http.service.js +367 -310
  41. package/dist/core/services/http.service.js.map +1 -1
  42. package/dist/core/services/logger.service.d.ts +96 -34
  43. package/dist/core/services/logger.service.d.ts.map +1 -1
  44. package/dist/core/services/logger.service.js +128 -41
  45. package/dist/core/services/logger.service.js.map +1 -1
  46. package/dist/core/services/query.service.d.ts +101 -0
  47. package/dist/core/services/query.service.d.ts.map +1 -1
  48. package/dist/core/services/query.service.js +131 -14
  49. package/dist/core/services/query.service.js.map +1 -1
  50. package/dist/core/services/utils.service.d.ts +286 -131
  51. package/dist/core/services/utils.service.d.ts.map +1 -1
  52. package/dist/core/services/utils.service.js +417 -227
  53. package/dist/core/services/utils.service.js.map +1 -1
  54. package/dist/core/services/validation.service.d.ts +48 -3
  55. package/dist/core/services/validation.service.d.ts.map +1 -1
  56. package/dist/core/services/validation.service.js +75 -23
  57. package/dist/core/services/validation.service.js.map +1 -1
  58. package/dist/infrastructure/decorators/cache.decorator.d.ts +53 -0
  59. package/dist/infrastructure/decorators/cache.decorator.d.ts.map +1 -0
  60. package/dist/infrastructure/decorators/cache.decorator.js +47 -0
  61. package/dist/infrastructure/decorators/cache.decorator.js.map +1 -0
  62. package/dist/infrastructure/decorators/index.d.ts +4 -0
  63. package/dist/infrastructure/decorators/index.d.ts.map +1 -0
  64. package/dist/infrastructure/decorators/index.js +4 -0
  65. package/dist/infrastructure/decorators/index.js.map +1 -0
  66. package/dist/infrastructure/decorators/logger.decorator.d.ts +38 -0
  67. package/dist/infrastructure/decorators/logger.decorator.d.ts.map +1 -0
  68. package/dist/infrastructure/decorators/logger.decorator.js +66 -0
  69. package/dist/infrastructure/decorators/logger.decorator.js.map +1 -0
  70. package/dist/infrastructure/decorators/retry.decorator.d.ts +37 -0
  71. package/dist/infrastructure/decorators/retry.decorator.d.ts.map +1 -0
  72. package/dist/infrastructure/decorators/retry.decorator.js +54 -0
  73. package/dist/infrastructure/decorators/retry.decorator.js.map +1 -0
  74. package/dist/infrastructure/index.d.ts +1 -0
  75. package/dist/infrastructure/index.d.ts.map +1 -1
  76. package/dist/infrastructure/index.js +1 -0
  77. package/dist/infrastructure/index.js.map +1 -1
  78. package/dist/styles.css +2 -0
  79. package/package.json +14 -16
  80. package/scss/_flags.scss +179 -0
  81. package/scss/_functions.scss +168 -0
  82. package/scss/_mixins.scss +658 -0
  83. package/scss/_reset.scss +275 -0
  84. package/scss/_utilities.scss +449 -0
  85. package/scss/_variables.scss +539 -0
  86. package/scss/components/_index.scss +50 -0
  87. package/scss/demo.scss +12 -0
  88. package/scss/main.scss +56 -0
@@ -4,50 +4,6 @@ import { useValidate } from "../../core/services/validation.service.js";
4
4
  import { WordPressConfigSchema, WpBatchOperationSchema, WpBatchResultSchema, WpCategoryCreateSchema, WpCategoryListSchema, WpCategoryResponseSchema, WpCategoryUpdateSchema, WpCommentCreateSchema, WpCommentListSchema, WpCommentResponseSchema, WpCommentUpdateSchema, WpMediaListSchema, WpMediaMetaSchema, WpMediaResponseSchema, WpMediaUpdateSchema, WpPageCreateSchema, WpPageListSchema, WpPageResponseSchema, WpPageUpdateSchema, WpPostCreateSchema, WpPostListSchema, WpPostResponseSchema, WpPostUpdateSchema, WpTagCreateSchema, WpTagListSchema, WpTagResponseSchema, WpTagUpdateSchema, WpUserCreateSchema, WpUserListSchema, WpUserResponseSchema, WpUserUpdateSchema, } from "../../schemas/wordpress.schema.js";
5
5
  /** Default WordPress REST API namespace. */
6
6
  const WP_API_NAMESPACE = "wp/v2";
7
- /** Module-level WordPress configuration. */
8
- let config = null;
9
- /**
10
- * Retrieves the registered WordPress config or throws.
11
- * @internal
12
- */
13
- function getConfig() {
14
- if (!config) {
15
- throw new Error("[WordPress] Not configured. Call defineWordPressConfig() first.");
16
- }
17
- return config;
18
- }
19
- /**
20
- * Builds the base API URL from config.
21
- * @internal
22
- */
23
- function getApiBase() {
24
- const { baseUrl, apiNamespace } = getConfig();
25
- const ns = apiNamespace ?? WP_API_NAMESPACE;
26
- const cleanBase = baseUrl.replace(/\/+$/, "");
27
- return `${cleanBase}/wp-json/${ns}`;
28
- }
29
- /**
30
- * Builds authentication headers from config.
31
- * @internal
32
- */
33
- function getAuthHeaders() {
34
- const { auth } = getConfig();
35
- if (!auth)
36
- return {};
37
- switch (auth.type) {
38
- case "application-passwords":
39
- case "basic": {
40
- const credentials = btoa(`${auth.username}:${auth.password}`);
41
- return { Authorization: `Basic ${credentials}` };
42
- }
43
- case "jwt":
44
- return { Authorization: `Bearer ${auth.token}` };
45
- case "nonce":
46
- return { "X-WP-Nonce": auth.nonce, Cookie: auth.cookie };
47
- default:
48
- return {};
49
- }
50
- }
51
7
  /**
52
8
  * Converts query params to URL search params.
53
9
  * Handles _fields (string), _embed (boolean or string), and arrays.
@@ -116,115 +72,519 @@ function invalidInput(parsed) {
116
72
  ok: false,
117
73
  };
118
74
  }
119
- /**
120
- * Internal helper to make requests to the WordPress REST API.
121
- * @internal
122
- */
123
- async function wpFetch(endpoint, options = {}, schema) {
124
- let url = "";
125
- const attempt = await useAttempt(async () => {
126
- url = `${getApiBase()}${endpoint}`;
127
- const response = await fetch(url, {
128
- ...options,
129
- headers: {
130
- "Content-Type": "application/json",
131
- ...getAuthHeaders(),
132
- ...options.headers,
133
- },
134
- });
135
- if (!response.ok) {
136
- const errorBody = await response.text().catch(() => null);
137
- throw apiError(`WordPress API Error: ${response.statusText}`, response.status, errorBody);
138
- }
139
- if (response.status === 204) {
140
- return { data: null, status: 204 };
141
- }
142
- const raw = (await response.json());
143
- if (schema) {
144
- const parsed = useValidate(schema, raw);
145
- if (!parsed.ok) {
146
- throw apiError("WordPress Validation Error: " + parsed.error.message, 502, parsed.error.details);
147
- }
148
- // WP may return partials via `_fields`, so the schema guarantees present fields are well-typed.
149
- return { data: parsed.data, status: response.status };
150
- }
151
- return { data: raw, status: response.status };
152
- });
153
- if (!attempt.ok) {
154
- const { message, status, details } = attempt.error;
155
- return {
156
- data: null,
157
- error: { message: status === 0 ? `Network Error: ${message}` : message, status, details },
158
- url,
159
- status,
160
- ok: false,
161
- };
162
- }
163
- return { data: attempt.data.data, error: null, url, status: attempt.data.status, ok: true };
164
- }
165
75
  /** Builds a throwable error carrying `status`/`details` for `useAttempt`. */
166
76
  function apiError(message, status, details) {
167
77
  return Object.assign(new Error(message), { status, details });
168
78
  }
169
79
  /**
170
- * Internal helper for multipart file uploads.
171
- * @internal
80
+ * WordPressService - Singleton para el cliente del WordPress REST API.
81
+ *
82
+ * Aplica el patrón Singleton: una única instancia mantiene la configuración
83
+ * (URL base, auth, namespace) y centraliza todas las operaciones CRUD contra
84
+ * la REST API de WordPress, devolviendo siempre Safe Results.
85
+ *
86
+ * @example
87
+ * ```ts
88
+ * const wp = WordPressService.getInstance();
89
+ * wp.useInit({ baseUrl: "https://mysite.com", auth: { type: "jwt", token: "..." } });
90
+ * const result = await wp.useGetPosts({ per_page: 5 });
91
+ * ```
172
92
  */
173
- async function wpUpload(endpoint, file, meta, schema) {
174
- let url = "";
175
- const attempt = await useAttempt(async () => {
176
- url = `${getApiBase()}${endpoint}`;
177
- const formData = new FormData();
178
- // Add the file
179
- if (typeof Buffer !== "undefined" && file instanceof Buffer) {
180
- const blob = new Blob([new Uint8Array(file)]);
181
- formData.append("file", blob, "upload");
93
+ export class WordPressService {
94
+ static instance;
95
+ /** Registered WordPress configuration. */
96
+ config = null;
97
+ /**
98
+ * Constructor privado - enforce singleton.
99
+ */
100
+ constructor() { }
101
+ /**
102
+ * Obtiene la instancia única del WordPressService (Singleton).
103
+ *
104
+ * @returns La instancia única de {@link WordPressService}.
105
+ */
106
+ static getInstance() {
107
+ if (!WordPressService.instance) {
108
+ WordPressService.instance = new WordPressService();
182
109
  }
183
- else {
184
- formData.append("file", file, file.name ?? "upload");
110
+ return WordPressService.instance;
111
+ }
112
+ /**
113
+ * Retrieves the registered WordPress config or throws.
114
+ * @internal
115
+ */
116
+ useGetConfig() {
117
+ if (!this.config) {
118
+ throw new Error("[WordPress] Not configured. Call defineWordPressConfig() first.");
119
+ }
120
+ return this.config;
121
+ }
122
+ /**
123
+ * Builds the base API URL from config.
124
+ * @internal
125
+ */
126
+ useGetApiBase() {
127
+ const { baseUrl, apiNamespace } = this.useGetConfig();
128
+ const ns = apiNamespace ?? WP_API_NAMESPACE;
129
+ const cleanBase = baseUrl.replace(/\/+$/, "");
130
+ return `${cleanBase}/wp-json/${ns}`;
131
+ }
132
+ /**
133
+ * Builds authentication headers from config.
134
+ * @internal
135
+ */
136
+ useGetAuthHeaders() {
137
+ const { auth } = this.useGetConfig();
138
+ if (!auth)
139
+ return {};
140
+ switch (auth.type) {
141
+ case "application-passwords":
142
+ case "basic": {
143
+ const credentials = btoa(`${auth.username}:${auth.password}`);
144
+ return { Authorization: `Basic ${credentials}` };
145
+ }
146
+ case "jwt":
147
+ return { Authorization: `Bearer ${auth.token}` };
148
+ case "nonce":
149
+ return { "X-WP-Nonce": auth.nonce, Cookie: auth.cookie };
150
+ default:
151
+ return {};
152
+ }
153
+ }
154
+ /**
155
+ * Internal helper to make requests to the WordPress REST API.
156
+ * @internal
157
+ */
158
+ async useRequest(endpoint, options = {}, schema) {
159
+ let url = "";
160
+ const attempt = await useAttempt(async () => {
161
+ url = `${this.useGetApiBase()}${endpoint}`;
162
+ const response = await fetch(url, {
163
+ ...options,
164
+ headers: {
165
+ "Content-Type": "application/json",
166
+ ...this.useGetAuthHeaders(),
167
+ ...options.headers,
168
+ },
169
+ });
170
+ if (!response.ok) {
171
+ const errorBody = await response.text().catch(() => null);
172
+ throw apiError(`WordPress API Error: ${response.statusText}`, response.status, errorBody);
173
+ }
174
+ if (response.status === 204) {
175
+ return { data: null, status: 204 };
176
+ }
177
+ const raw = (await response.json());
178
+ if (schema) {
179
+ const parsed = useValidate(schema, raw);
180
+ if (!parsed.ok) {
181
+ throw apiError("WordPress Validation Error: " + parsed.error.message, 502, parsed.error.details);
182
+ }
183
+ // WP may return partials via `_fields`, so the schema guarantees present fields are well-typed.
184
+ return { data: parsed.data, status: response.status };
185
+ }
186
+ return { data: raw, status: response.status };
187
+ });
188
+ if (!attempt.ok) {
189
+ const { message, status, details } = attempt.error;
190
+ return {
191
+ data: null,
192
+ error: {
193
+ message: status === 0 ? `Network Error: ${message}` : message,
194
+ status,
195
+ details,
196
+ },
197
+ url,
198
+ status,
199
+ ok: false,
200
+ };
185
201
  }
186
- // Add metadata
187
- if (meta) {
188
- for (const [key, value] of Object.entries(meta)) {
189
- if (value !== undefined && value !== null) {
190
- formData.append(key, String(value));
202
+ return { data: attempt.data.data, error: null, url, status: attempt.data.status, ok: true };
203
+ }
204
+ /**
205
+ * Internal helper for multipart file uploads.
206
+ * @internal
207
+ */
208
+ async useUpload(endpoint, file, meta, schema) {
209
+ let url = "";
210
+ const attempt = await useAttempt(async () => {
211
+ url = `${this.useGetApiBase()}${endpoint}`;
212
+ const formData = new FormData();
213
+ // Add the file
214
+ if (typeof Buffer !== "undefined" && file instanceof Buffer) {
215
+ const blob = new Blob([new Uint8Array(file)]);
216
+ formData.append("file", blob, "upload");
217
+ }
218
+ else {
219
+ formData.append("file", file, file.name ?? "upload");
220
+ }
221
+ // Add metadata
222
+ if (meta) {
223
+ for (const [key, value] of Object.entries(meta)) {
224
+ if (value !== undefined && value !== null) {
225
+ formData.append(key, String(value));
226
+ }
227
+ }
228
+ }
229
+ const response = await fetch(url, {
230
+ method: "POST",
231
+ headers: {
232
+ ...this.useGetAuthHeaders(),
233
+ // Don't set Content-Type — browser sets multipart boundary
234
+ },
235
+ body: formData,
236
+ });
237
+ if (!response.ok) {
238
+ const errorBody = await response.text().catch(() => null);
239
+ throw apiError(`WordPress Upload Error: ${response.statusText}`, response.status, errorBody);
240
+ }
241
+ const raw = (await response.json());
242
+ if (schema) {
243
+ const parsed = useValidate(schema, raw);
244
+ if (!parsed.ok) {
245
+ throw apiError("WordPress Validation Error: " + parsed.error.message, 502, parsed.error.details);
191
246
  }
247
+ return { data: parsed.data, status: response.status };
192
248
  }
249
+ return { data: raw, status: response.status };
250
+ });
251
+ if (!attempt.ok) {
252
+ const { message, status, details } = attempt.error;
253
+ return {
254
+ data: null,
255
+ error: { message: status === 0 ? `Upload Error: ${message}` : message, status, details },
256
+ url,
257
+ status,
258
+ ok: false,
259
+ };
260
+ }
261
+ return { data: attempt.data.data, error: null, url, status: attempt.data.status, ok: true };
262
+ }
263
+ /**
264
+ * Registers the WordPress REST API configuration.
265
+ *
266
+ * Supports multiple auth methods: Application Passwords, JWT tokens,
267
+ * Basic Auth, and nonce-based auth for themes.
268
+ *
269
+ * @param cfg - Site URL, authentication method, and optional API namespace.
270
+ * @throws {Error} If the config fails Zod validation.
271
+ *
272
+ * @example
273
+ * ```ts
274
+ * WordPressService.getInstance().useInit({
275
+ * baseUrl: "https://mysite.com",
276
+ * auth: { type: "application-passwords", username: "admin", password: "xxxx xxxx xxxx" },
277
+ * });
278
+ * ```
279
+ */
280
+ useInit(cfg) {
281
+ const parsed = useValidate(WordPressConfigSchema, cfg);
282
+ if (!parsed.ok) {
283
+ throw new Error("[WordPress] Invalid config: " + parsed.error.message);
193
284
  }
194
- const response = await fetch(url, {
285
+ this.config = { ...parsed.data };
286
+ }
287
+ /* ---------------------------------------------------------------------- */
288
+ /* Posts */
289
+ /* ---------------------------------------------------------------------- */
290
+ /** Retrieves a list of WordPress posts with optional filtering and pagination. */
291
+ async useGetPosts(options) {
292
+ return this.useRequest(`/posts${buildQueryParams(options)}`, {}, WpPostListSchema);
293
+ }
294
+ /** Retrieves a single WordPress post by ID. */
295
+ async useGetPost(id, options) {
296
+ return this.useRequest(`/posts/${id}${buildQueryParams(options)}`, {}, WpPostResponseSchema);
297
+ }
298
+ /** Creates a new WordPress post. */
299
+ async useCreatePost(data) {
300
+ const parsed = useValidate(WpPostCreateSchema, data);
301
+ if (!parsed.ok)
302
+ return invalidInput(parsed);
303
+ return this.useRequest("/posts", { method: "POST", body: JSON.stringify(data) }, WpPostResponseSchema);
304
+ }
305
+ /** Updates an existing WordPress post. */
306
+ async useUpdatePost(id, data) {
307
+ const parsed = useValidate(WpPostUpdateSchema, data);
308
+ if (!parsed.ok)
309
+ return invalidInput(parsed);
310
+ return this.useRequest(`/posts/${id}`, { method: "POST", body: JSON.stringify(data) }, WpPostResponseSchema);
311
+ }
312
+ /** Deletes a WordPress post (trash by default, permanent with `force`). */
313
+ async useDeletePost(id, force = false) {
314
+ return this.useRequest(`/posts/${id}?force=${force}`, { method: "DELETE" }, WpPostResponseSchema);
315
+ }
316
+ /* ---------------------------------------------------------------------- */
317
+ /* Pages */
318
+ /* ---------------------------------------------------------------------- */
319
+ /** Retrieves a list of WordPress pages. */
320
+ async useGetPages(options) {
321
+ return this.useRequest(`/pages${buildQueryParams(options)}`, {}, WpPageListSchema);
322
+ }
323
+ /** Retrieves a single WordPress page by ID. */
324
+ async useGetPage(id, options) {
325
+ return this.useRequest(`/pages/${id}${buildQueryParams(options)}`, {}, WpPageResponseSchema);
326
+ }
327
+ /** Creates a new WordPress page. */
328
+ async useCreatePage(data) {
329
+ const parsed = useValidate(WpPageCreateSchema, data);
330
+ if (!parsed.ok)
331
+ return invalidInput(parsed);
332
+ return this.useRequest("/pages", { method: "POST", body: JSON.stringify(data) }, WpPageResponseSchema);
333
+ }
334
+ /** Updates an existing WordPress page. */
335
+ async useUpdatePage(id, data) {
336
+ const parsed = useValidate(WpPageUpdateSchema, data);
337
+ if (!parsed.ok)
338
+ return invalidInput(parsed);
339
+ return this.useRequest(`/pages/${id}`, { method: "POST", body: JSON.stringify(data) }, WpPageResponseSchema);
340
+ }
341
+ /** Deletes a WordPress page (trash by default, permanent with `force`). */
342
+ async useDeletePage(id, force = false) {
343
+ return this.useRequest(`/pages/${id}?force=${force}`, { method: "DELETE" }, WpPageResponseSchema);
344
+ }
345
+ /* ---------------------------------------------------------------------- */
346
+ /* Media */
347
+ /* ---------------------------------------------------------------------- */
348
+ /** Retrieves a list of WordPress media items. */
349
+ async useGetMedia(options) {
350
+ return this.useRequest(`/media${buildQueryParams(options)}`, {}, WpMediaListSchema);
351
+ }
352
+ /** Retrieves a single WordPress media item by ID. */
353
+ async useGetMediaItem(id) {
354
+ return this.useRequest(`/media/${id}`, {}, WpMediaResponseSchema);
355
+ }
356
+ /** Uploads a file to the WordPress media library. */
357
+ async useUploadMedia(file, meta) {
358
+ if (meta !== undefined) {
359
+ const parsed = useValidate(WpMediaMetaSchema, meta);
360
+ if (!parsed.ok)
361
+ return invalidInput(parsed);
362
+ }
363
+ return this.useUpload("/media", file, meta, WpMediaResponseSchema);
364
+ }
365
+ /** Updates metadata of a WordPress media item. */
366
+ async useUpdateMedia(id, data) {
367
+ const parsed = useValidate(WpMediaUpdateSchema, data);
368
+ if (!parsed.ok)
369
+ return invalidInput(parsed);
370
+ return this.useRequest(`/media/${id}`, { method: "POST", body: JSON.stringify(data) }, WpMediaResponseSchema);
371
+ }
372
+ /** Deletes a WordPress media item. */
373
+ async useDeleteMedia(id, force = false) {
374
+ return this.useRequest(`/media/${id}?force=${force}`, { method: "DELETE" }, WpMediaResponseSchema);
375
+ }
376
+ /* ---------------------------------------------------------------------- */
377
+ /* Categories */
378
+ /* ---------------------------------------------------------------------- */
379
+ /** Retrieves a list of WordPress categories. */
380
+ async useGetCategories(options) {
381
+ return this.useRequest(`/categories${buildQueryParams(options)}`, {}, WpCategoryListSchema);
382
+ }
383
+ /** Retrieves a single WordPress category by ID. */
384
+ async useGetCategory(id) {
385
+ return this.useRequest(`/categories/${id}`, {}, WpCategoryResponseSchema);
386
+ }
387
+ /** Creates a new WordPress category. */
388
+ async useCreateCategory(data) {
389
+ const parsed = useValidate(WpCategoryCreateSchema, data);
390
+ if (!parsed.ok)
391
+ return invalidInput(parsed);
392
+ return this.useRequest("/categories", { method: "POST", body: JSON.stringify(data) }, WpCategoryResponseSchema);
393
+ }
394
+ /** Updates an existing WordPress category. */
395
+ async useUpdateCategory(id, data) {
396
+ const parsed = useValidate(WpCategoryUpdateSchema, data);
397
+ if (!parsed.ok)
398
+ return invalidInput(parsed);
399
+ return this.useRequest(`/categories/${id}`, { method: "POST", body: JSON.stringify(data) }, WpCategoryResponseSchema);
400
+ }
401
+ /** Deletes a WordPress category. */
402
+ async useDeleteCategory(id, force = false) {
403
+ return this.useRequest(`/categories/${id}?force=${force}`, { method: "DELETE" }, WpCategoryResponseSchema);
404
+ }
405
+ /* ---------------------------------------------------------------------- */
406
+ /* Tags */
407
+ /* ---------------------------------------------------------------------- */
408
+ /** Retrieves a list of WordPress tags. */
409
+ async useGetTags(options) {
410
+ return this.useRequest(`/tags${buildQueryParams(options)}`, {}, WpTagListSchema);
411
+ }
412
+ /** Retrieves a single WordPress tag by ID. */
413
+ async useGetTag(id) {
414
+ return this.useRequest(`/tags/${id}`, {}, WpTagResponseSchema);
415
+ }
416
+ /** Creates a new WordPress tag. */
417
+ async useCreateTag(data) {
418
+ const parsed = useValidate(WpTagCreateSchema, data);
419
+ if (!parsed.ok)
420
+ return invalidInput(parsed);
421
+ return this.useRequest("/tags", { method: "POST", body: JSON.stringify(data) }, WpTagResponseSchema);
422
+ }
423
+ /** Updates an existing WordPress tag. */
424
+ async useUpdateTag(id, data) {
425
+ const parsed = useValidate(WpTagUpdateSchema, data);
426
+ if (!parsed.ok)
427
+ return invalidInput(parsed);
428
+ return this.useRequest(`/tags/${id}`, { method: "POST", body: JSON.stringify(data) }, WpTagResponseSchema);
429
+ }
430
+ /** Deletes a WordPress tag. */
431
+ async useDeleteTag(id, force = false) {
432
+ return this.useRequest(`/tags/${id}?force=${force}`, { method: "DELETE" }, WpTagResponseSchema);
433
+ }
434
+ /* ---------------------------------------------------------------------- */
435
+ /* Comments */
436
+ /* ---------------------------------------------------------------------- */
437
+ /** Retrieves a list of WordPress comments. */
438
+ async useGetComments(options) {
439
+ return this.useRequest(`/comments${buildQueryParams(options)}`, {}, WpCommentListSchema);
440
+ }
441
+ /** Retrieves a single WordPress comment by ID. */
442
+ async useGetComment(id) {
443
+ return this.useRequest(`/comments/${id}`, {}, WpCommentResponseSchema);
444
+ }
445
+ /** Creates a new WordPress comment. */
446
+ async useCreateComment(data) {
447
+ const parsed = useValidate(WpCommentCreateSchema, data);
448
+ if (!parsed.ok)
449
+ return invalidInput(parsed);
450
+ return this.useRequest("/comments", { method: "POST", body: JSON.stringify(data) }, WpCommentResponseSchema);
451
+ }
452
+ /** Updates an existing WordPress comment. */
453
+ async useUpdateComment(id, data) {
454
+ const parsed = useValidate(WpCommentUpdateSchema, data);
455
+ if (!parsed.ok)
456
+ return invalidInput(parsed);
457
+ return this.useRequest(`/comments/${id}`, { method: "POST", body: JSON.stringify(data) }, WpCommentResponseSchema);
458
+ }
459
+ /** Deletes a WordPress comment. */
460
+ async useDeleteComment(id, force = false) {
461
+ return this.useRequest(`/comments/${id}?force=${force}`, { method: "DELETE" }, WpCommentResponseSchema);
462
+ }
463
+ /* ---------------------------------------------------------------------- */
464
+ /* Users */
465
+ /* ---------------------------------------------------------------------- */
466
+ /** Retrieves a list of WordPress users. */
467
+ async useGetUsers(options) {
468
+ return this.useRequest(`/users${buildQueryParams(options)}`, {}, WpUserListSchema);
469
+ }
470
+ /** Retrieves a single WordPress user by ID. */
471
+ async useGetUser(id) {
472
+ return this.useRequest(`/users/${id}`, {}, WpUserResponseSchema);
473
+ }
474
+ /** Retrieves the currently authenticated WordPress user. */
475
+ async useGetCurrentUser() {
476
+ return this.useRequest("/users/me", {}, WpUserResponseSchema);
477
+ }
478
+ /** Creates a new WordPress user. */
479
+ async useCreateUser(data) {
480
+ const parsed = useValidate(WpUserCreateSchema, data);
481
+ if (!parsed.ok)
482
+ return invalidInput(parsed);
483
+ return this.useRequest("/users", { method: "POST", body: JSON.stringify(data) }, WpUserResponseSchema);
484
+ }
485
+ /** Updates an existing WordPress user. */
486
+ async useUpdateUser(id, data) {
487
+ const parsed = useValidate(WpUserUpdateSchema, data);
488
+ if (!parsed.ok)
489
+ return invalidInput(parsed);
490
+ return this.useRequest(`/users/${id}`, { method: "POST", body: JSON.stringify(data) }, WpUserResponseSchema);
491
+ }
492
+ /** Deletes a WordPress user, optionally reassigning their content. */
493
+ async useDeleteUser(id, reassign) {
494
+ const qs = reassign ? `?reassign=${reassign}` : "";
495
+ return this.useRequest(`/users/${id}${qs}`, { method: "DELETE" }, WpUserResponseSchema);
496
+ }
497
+ /* ---------------------------------------------------------------------- */
498
+ /* Custom Post Types */
499
+ /* ---------------------------------------------------------------------- */
500
+ /** Retrieves a list of custom post type entries. */
501
+ async useGetCustomPosts(postType, options) {
502
+ return this.useRequest(`/${postType}${buildQueryParams(options)}`);
503
+ }
504
+ /** Retrieves a single custom post type entry by ID. */
505
+ async useGetCustomPost(postType, id, options) {
506
+ return this.useRequest(`/${postType}/${id}${buildQueryParams(options)}`);
507
+ }
508
+ /** Creates a new custom post type entry. */
509
+ async useCreateCustomPost(postType, data) {
510
+ return this.useRequest(`/${postType}`, {
511
+ method: "POST",
512
+ body: JSON.stringify(data),
513
+ });
514
+ }
515
+ /** Updates a custom post type entry. */
516
+ async useUpdateCustomPost(postType, id, data) {
517
+ return this.useRequest(`/${postType}/${id}`, {
195
518
  method: "POST",
196
- headers: {
197
- ...getAuthHeaders(),
198
- // Don't set Content-Type — browser sets multipart boundary
199
- },
200
- body: formData,
519
+ body: JSON.stringify(data),
520
+ });
521
+ }
522
+ /** Deletes a custom post type entry. */
523
+ async useDeleteCustomPost(postType, id, force = false) {
524
+ return this.useRequest(`/${postType}/${id}?force=${force}`, {
525
+ method: "DELETE",
201
526
  });
202
- if (!response.ok) {
203
- const errorBody = await response.text().catch(() => null);
204
- throw apiError(`WordPress Upload Error: ${response.statusText}`, response.status, errorBody);
527
+ }
528
+ /* ---------------------------------------------------------------------- */
529
+ /* Batch Operations */
530
+ /* ---------------------------------------------------------------------- */
531
+ /** Executes multiple WordPress REST API requests in a single batch. */
532
+ async useBatch(operations) {
533
+ for (const operation of operations) {
534
+ const parsed = useValidate(WpBatchOperationSchema, operation);
535
+ if (!parsed.ok)
536
+ return invalidInput(parsed);
205
537
  }
206
- const raw = (await response.json());
207
- if (schema) {
208
- const parsed = useValidate(schema, raw);
209
- if (!parsed.ok) {
210
- throw apiError("WordPress Validation Error: " + parsed.error.message, 502, parsed.error.details);
211
- }
212
- // WP may return partials via `_fields`, so the schema guarantees present fields are well-typed.
213
- return { data: parsed.data, status: response.status };
538
+ return this.useRequest("/batch/v1", { method: "POST", body: JSON.stringify({ requests: operations }) }, WpBatchResultSchema);
539
+ }
540
+ /* ---------------------------------------------------------------------- */
541
+ /* Pagination Helpers */
542
+ /* ---------------------------------------------------------------------- */
543
+ /**
544
+ * Fetches ALL posts matching the criteria, automatically handling pagination.
545
+ *
546
+ * @param options - Query params for filtering. Do NOT pass `page` or `per_page`.
547
+ * @param maxItems - Cap on the total number of posts to fetch (default `1000`).
548
+ */
549
+ async useListAllPosts(options, maxItems = 1000) {
550
+ const allPosts = [];
551
+ let page = 1;
552
+ while (true) {
553
+ const result = await this.useGetPosts({ ...options, page, per_page: 100 });
554
+ if (!result.ok)
555
+ return result;
556
+ allPosts.push(...result.data);
557
+ if (maxItems && allPosts.length >= maxItems)
558
+ break;
559
+ if (result.data.length < 100)
560
+ break;
561
+ page++;
214
562
  }
215
- return { data: raw, status: response.status };
216
- });
217
- if (!attempt.ok) {
218
- const { message, status, details } = attempt.error;
219
563
  return {
220
- data: null,
221
- error: { message: status === 0 ? `Upload Error: ${message}` : message, status, details },
222
- url,
223
- status,
224
- ok: false,
564
+ data: allPosts.slice(0, maxItems),
565
+ error: null,
566
+ url: "",
567
+ status: 200,
568
+ ok: true,
569
+ };
570
+ }
571
+ /** Searches ALL posts by keyword, automatically handling pagination. */
572
+ async useSearchAllPosts(query, options) {
573
+ return this.useListAllPosts({ ...options, search: query });
574
+ }
575
+ /** Finds a single post by its URL slug. Returns `null` if not found. */
576
+ async useFindPostBySlug(slug) {
577
+ const result = await this.useGetPosts({ slug, per_page: 1 });
578
+ if (!result.ok)
579
+ return result;
580
+ return {
581
+ data: result.data[0] ?? null,
582
+ error: null,
583
+ url: result.url,
584
+ status: result.status,
585
+ ok: true,
225
586
  };
226
587
  }
227
- return { data: attempt.data.data, error: null, url, status: attempt.data.status, ok: true };
228
588
  }
229
589
  /* -------------------------------------------------------------------------- */
230
590
  /* Initialization */
@@ -280,53 +640,31 @@ export function defineWordPressConfig(input) {
280
640
  * ```ts
281
641
  * import { useInitWordPress } from "katanakit-js/adapters/wordpress";
282
642
  *
283
- * // With Application Passwords (recommended)
284
643
  * useInitWordPress({
285
644
  * baseUrl: "https://mysite.com",
286
645
  * auth: { type: "application-passwords", username: "admin", password: "xxxx xxxx xxxx" },
287
646
  * });
288
- *
289
- * // With JWT
290
- * useInitWordPress({
291
- * baseUrl: "https://mysite.com",
292
- * auth: { type: "jwt", token: "eyJhbGci..." },
293
- * });
294
647
  * ```
295
648
  */
296
649
  export function useInitWordPress(cfg) {
297
- const parsed = useValidate(WordPressConfigSchema, cfg);
298
- if (!parsed.ok) {
299
- throw new Error("[WordPress] Invalid config: " + parsed.error.message);
300
- }
301
- config = { ...parsed.data };
650
+ WordPressService.getInstance().useInit(cfg);
302
651
  }
303
652
  /* -------------------------------------------------------------------------- */
304
- /* Posts */
653
+ /* Wrappers (backward compatibility) */
305
654
  /* -------------------------------------------------------------------------- */
306
655
  /**
307
656
  * Retrieves a list of WordPress posts with optional filtering and pagination.
308
657
  *
309
- * Use to display blog listings, search posts, or filter by category/tag/status.
310
- * Returns a single page of results (default 10 per page).
311
- *
312
- * @param options - Query params: `per_page`, `page`, `search`, `categories`,
313
- * `tags`, `status`, `orderby`, `order`, `_embed`, etc.
658
+ * @param options - Query params: `per_page`, `page`, `search`, `categories`, etc.
314
659
  * @returns Array of posts.
315
660
  *
316
661
  * @example
317
662
  * ```ts
318
- * // Get latest 5 published posts
319
663
  * const result = await useWpGetPosts({ per_page: 5, status: "publish" });
320
- *
321
- * // Search posts by keyword
322
- * const result = await useWpGetPosts({ search: "tutorial" });
323
- *
324
- * // Get posts from category 3, sorted by title
325
- * const result = await useWpGetPosts({ categories: 3, orderby: "title", order: "asc" });
326
664
  * ```
327
665
  */
328
666
  export async function useWpGetPosts(options) {
329
- return wpFetch(`/posts${buildQueryParams(options)}`, {}, WpPostListSchema);
667
+ return WordPressService.getInstance().useGetPosts(options);
330
668
  }
331
669
  /**
332
670
  * Retrieves a single WordPress post by ID.
@@ -338,44 +676,24 @@ export async function useWpGetPosts(options) {
338
676
  * @example
339
677
  * ```ts
340
678
  * const result = await useWpGetPost(42, { _embed: true });
341
- * if (result.ok) console.log(result.data.title.rendered);
342
679
  * ```
343
680
  */
344
681
  export async function useWpGetPost(id, options) {
345
- return wpFetch(`/posts/${id}${buildQueryParams(options)}`, {}, WpPostResponseSchema);
682
+ return WordPressService.getInstance().useGetPost(id, options);
346
683
  }
347
684
  /**
348
685
  * Creates a new WordPress post.
349
686
  *
350
- * Use to publish blog entries, create drafts, or schedule future posts.
351
- * The post will be assigned to the authenticated user by default.
352
- *
353
- * @param data - Post data: `title`, `content` (HTML), `status` (publish/draft/pending),
354
- * `categories`, `tags`, `featured_media`, `excerpt`, `slug`, `date`, etc.
687
+ * @param data - Post data: `title`, `content`, `status`, `categories`, etc.
355
688
  * @returns The created post.
356
689
  *
357
690
  * @example
358
691
  * ```ts
359
- * // Publish immediately
360
- * const result = await useWpCreatePost({
361
- * title: "My New Post",
362
- * content: "<p>Hello World!</p>",
363
- * status: "publish",
364
- * categories: [1, 3],
365
- * });
366
- *
367
- * // Create as draft
368
- * await useWpCreatePost({ title: "Draft Post", content: "...", status: "draft" });
692
+ * const result = await useWpCreatePost({ title: "My New Post", content: "<p>Hello</p>", status: "publish" });
369
693
  * ```
370
694
  */
371
695
  export async function useWpCreatePost(data) {
372
- const parsed = useValidate(WpPostCreateSchema, data);
373
- if (!parsed.ok)
374
- return invalidInput(parsed);
375
- return wpFetch("/posts", {
376
- method: "POST",
377
- body: JSON.stringify(data),
378
- }, WpPostResponseSchema);
696
+ return WordPressService.getInstance().useCreatePost(data);
379
697
  }
380
698
  /**
381
699
  * Updates an existing WordPress post.
@@ -390,13 +708,7 @@ export async function useWpCreatePost(data) {
390
708
  * ```
391
709
  */
392
710
  export async function useWpUpdatePost(id, data) {
393
- const parsed = useValidate(WpPostUpdateSchema, data);
394
- if (!parsed.ok)
395
- return invalidInput(parsed);
396
- return wpFetch(`/posts/${id}`, {
397
- method: "POST",
398
- body: JSON.stringify(data),
399
- }, WpPostResponseSchema);
711
+ return WordPressService.getInstance().useUpdatePost(id, data);
400
712
  }
401
713
  /**
402
714
  * Deletes a WordPress post.
@@ -407,18 +719,12 @@ export async function useWpUpdatePost(id, data) {
407
719
  *
408
720
  * @example
409
721
  * ```ts
410
- * await useWpDeletePost(42); // Move to trash
411
- * await useWpDeletePost(42, true); // Permanently delete
722
+ * await useWpDeletePost(42, true);
412
723
  * ```
413
724
  */
414
725
  export async function useWpDeletePost(id, force = false) {
415
- return wpFetch(`/posts/${id}?force=${force}`, {
416
- method: "DELETE",
417
- }, WpPostResponseSchema);
726
+ return WordPressService.getInstance().useDeletePost(id, force);
418
727
  }
419
- /* -------------------------------------------------------------------------- */
420
- /* Pages */
421
- /* -------------------------------------------------------------------------- */
422
728
  /**
423
729
  * Retrieves a list of WordPress pages.
424
730
  *
@@ -431,7 +737,7 @@ export async function useWpDeletePost(id, force = false) {
431
737
  * ```
432
738
  */
433
739
  export async function useWpGetPages(options) {
434
- return wpFetch(`/pages${buildQueryParams(options)}`, {}, WpPageListSchema);
740
+ return WordPressService.getInstance().useGetPages(options);
435
741
  }
436
742
  /**
437
743
  * Retrieves a single WordPress page by ID.
@@ -443,11 +749,10 @@ export async function useWpGetPages(options) {
443
749
  * @example
444
750
  * ```ts
445
751
  * const result = await useWpGetPage(10);
446
- * if (result.ok) console.log(result.data.title.rendered);
447
752
  * ```
448
753
  */
449
754
  export async function useWpGetPage(id, options) {
450
- return wpFetch(`/pages/${id}${buildQueryParams(options)}`, {}, WpPageResponseSchema);
755
+ return WordPressService.getInstance().useGetPage(id, options);
451
756
  }
452
757
  /**
453
758
  * Creates a new WordPress page.
@@ -457,21 +762,11 @@ export async function useWpGetPage(id, options) {
457
762
  *
458
763
  * @example
459
764
  * ```ts
460
- * await useWpCreatePage({
461
- * title: "About Us",
462
- * content: "<p>Welcome to our site!</p>",
463
- * status: "publish",
464
- * });
765
+ * await useWpCreatePage({ title: "About Us", content: "<p>Welcome</p>", status: "publish" });
465
766
  * ```
466
767
  */
467
768
  export async function useWpCreatePage(data) {
468
- const parsed = useValidate(WpPageCreateSchema, data);
469
- if (!parsed.ok)
470
- return invalidInput(parsed);
471
- return wpFetch("/pages", {
472
- method: "POST",
473
- body: JSON.stringify(data),
474
- }, WpPageResponseSchema);
769
+ return WordPressService.getInstance().useCreatePage(data);
475
770
  }
476
771
  /**
477
772
  * Updates an existing WordPress page.
@@ -486,13 +781,7 @@ export async function useWpCreatePage(data) {
486
781
  * ```
487
782
  */
488
783
  export async function useWpUpdatePage(id, data) {
489
- const parsed = useValidate(WpPageUpdateSchema, data);
490
- if (!parsed.ok)
491
- return invalidInput(parsed);
492
- return wpFetch(`/pages/${id}`, {
493
- method: "POST",
494
- body: JSON.stringify(data),
495
- }, WpPageResponseSchema);
784
+ return WordPressService.getInstance().useUpdatePage(id, data);
496
785
  }
497
786
  /**
498
787
  * Deletes a WordPress page.
@@ -503,17 +792,12 @@ export async function useWpUpdatePage(id, data) {
503
792
  *
504
793
  * @example
505
794
  * ```ts
506
- * await useWpDeletePage(10); // Move to trash
795
+ * await useWpDeletePage(10);
507
796
  * ```
508
797
  */
509
798
  export async function useWpDeletePage(id, force = false) {
510
- return wpFetch(`/pages/${id}?force=${force}`, {
511
- method: "DELETE",
512
- }, WpPageResponseSchema);
799
+ return WordPressService.getInstance().useDeletePage(id, force);
513
800
  }
514
- /* -------------------------------------------------------------------------- */
515
- /* Media */
516
- /* -------------------------------------------------------------------------- */
517
801
  /**
518
802
  * Retrieves a list of WordPress media items.
519
803
  *
@@ -526,7 +810,7 @@ export async function useWpDeletePage(id, force = false) {
526
810
  * ```
527
811
  */
528
812
  export async function useWpGetMedia(options) {
529
- return wpFetch(`/media${buildQueryParams(options)}`, {}, WpMediaListSchema);
813
+ return WordPressService.getInstance().useGetMedia(options);
530
814
  }
531
815
  /**
532
816
  * Retrieves a single WordPress media item by ID.
@@ -537,45 +821,25 @@ export async function useWpGetMedia(options) {
537
821
  * @example
538
822
  * ```ts
539
823
  * const result = await useWpGetMediaItem(42);
540
- * if (result.ok) console.log(result.data.source_url);
541
824
  * ```
542
825
  */
543
826
  export async function useWpGetMediaItem(id) {
544
- return wpFetch(`/media/${id}`, {}, WpMediaResponseSchema);
827
+ return WordPressService.getInstance().useGetMediaItem(id);
545
828
  }
546
829
  /**
547
830
  * Uploads a file to the WordPress media library.
548
831
  *
549
- * Use to upload images, documents, or any file type supported by WordPress.
550
- * Returns the media item with its URL for use as featured media or in content.
551
- *
552
832
  * @param file - File (browser), Blob, or Buffer (Node.js) to upload.
553
- * @param meta - Optional metadata: `title`, `alt_text`, `caption`, `description`,
554
- * `post` (attach to existing post), `slug`.
833
+ * @param meta - Optional metadata: `title`, `alt_text`, `caption`, etc.
555
834
  * @returns The created media item with `source_url`.
556
835
  *
557
836
  * @example
558
837
  * ```ts
559
- * // From a file input (browser)
560
- * const file = document.querySelector("input[type=file]").files[0];
561
- * const result = await useWpUploadMedia(file, {
562
- * title: "My Image",
563
- * alt_text: "Description for accessibility",
564
- * });
565
- * if (result.ok) console.log(result.data.source_url); // URL to use in content
566
- *
567
- * // From a Buffer (Node.js)
568
- * const buffer = fs.readFileSync("photo.jpg");
569
- * await useWpUploadMedia(buffer, { title: "Photo" });
838
+ * const result = await useWpUploadMedia(file, { title: "My Image" });
570
839
  * ```
571
840
  */
572
841
  export async function useWpUploadMedia(file, meta) {
573
- if (meta !== undefined) {
574
- const parsed = useValidate(WpMediaMetaSchema, meta);
575
- if (!parsed.ok)
576
- return invalidInput(parsed);
577
- }
578
- return wpUpload("/media", file, meta, WpMediaResponseSchema);
842
+ return WordPressService.getInstance().useUploadMedia(file, meta);
579
843
  }
580
844
  /**
581
845
  * Updates metadata of a WordPress media item.
@@ -590,13 +854,7 @@ export async function useWpUploadMedia(file, meta) {
590
854
  * ```
591
855
  */
592
856
  export async function useWpUpdateMedia(id, data) {
593
- const parsed = useValidate(WpMediaUpdateSchema, data);
594
- if (!parsed.ok)
595
- return invalidInput(parsed);
596
- return wpFetch(`/media/${id}`, {
597
- method: "POST",
598
- body: JSON.stringify(data),
599
- }, WpMediaResponseSchema);
857
+ return WordPressService.getInstance().useUpdateMedia(id, data);
600
858
  }
601
859
  /**
602
860
  * Deletes a WordPress media item.
@@ -611,13 +869,8 @@ export async function useWpUpdateMedia(id, data) {
611
869
  * ```
612
870
  */
613
871
  export async function useWpDeleteMedia(id, force = false) {
614
- return wpFetch(`/media/${id}?force=${force}`, {
615
- method: "DELETE",
616
- }, WpMediaResponseSchema);
872
+ return WordPressService.getInstance().useDeleteMedia(id, force);
617
873
  }
618
- /* -------------------------------------------------------------------------- */
619
- /* Categories */
620
- /* -------------------------------------------------------------------------- */
621
874
  /**
622
875
  * Retrieves a list of WordPress categories.
623
876
  *
@@ -627,11 +880,10 @@ export async function useWpDeleteMedia(id, force = false) {
627
880
  * @example
628
881
  * ```ts
629
882
  * const result = await useWpGetCategories({ per_page: 50 });
630
- * if (result.ok) result.data.forEach(c => console.log(c.name));
631
883
  * ```
632
884
  */
633
885
  export async function useWpGetCategories(options) {
634
- return wpFetch(`/categories${buildQueryParams(options)}`, {}, WpCategoryListSchema);
886
+ return WordPressService.getInstance().useGetCategories(options);
635
887
  }
636
888
  /**
637
889
  * Retrieves a single WordPress category by ID.
@@ -642,11 +894,10 @@ export async function useWpGetCategories(options) {
642
894
  * @example
643
895
  * ```ts
644
896
  * const result = await useWpGetCategory(5);
645
- * if (result.ok) console.log(result.data.name);
646
897
  * ```
647
898
  */
648
899
  export async function useWpGetCategory(id) {
649
- return wpFetch(`/categories/${id}`, {}, WpCategoryResponseSchema);
900
+ return WordPressService.getInstance().useGetCategory(id);
650
901
  }
651
902
  /**
652
903
  * Creates a new WordPress category.
@@ -660,13 +911,7 @@ export async function useWpGetCategory(id) {
660
911
  * ```
661
912
  */
662
913
  export async function useWpCreateCategory(data) {
663
- const parsed = useValidate(WpCategoryCreateSchema, data);
664
- if (!parsed.ok)
665
- return invalidInput(parsed);
666
- return wpFetch("/categories", {
667
- method: "POST",
668
- body: JSON.stringify(data),
669
- }, WpCategoryResponseSchema);
914
+ return WordPressService.getInstance().useCreateCategory(data);
670
915
  }
671
916
  /**
672
917
  * Updates an existing WordPress category.
@@ -681,13 +926,7 @@ export async function useWpCreateCategory(data) {
681
926
  * ```
682
927
  */
683
928
  export async function useWpUpdateCategory(id, data) {
684
- const parsed = useValidate(WpCategoryUpdateSchema, data);
685
- if (!parsed.ok)
686
- return invalidInput(parsed);
687
- return wpFetch(`/categories/${id}`, {
688
- method: "POST",
689
- body: JSON.stringify(data),
690
- }, WpCategoryResponseSchema);
929
+ return WordPressService.getInstance().useUpdateCategory(id, data);
691
930
  }
692
931
  /**
693
932
  * Deletes a WordPress category.
@@ -702,13 +941,8 @@ export async function useWpUpdateCategory(id, data) {
702
941
  * ```
703
942
  */
704
943
  export async function useWpDeleteCategory(id, force = false) {
705
- return wpFetch(`/categories/${id}?force=${force}`, {
706
- method: "DELETE",
707
- }, WpCategoryResponseSchema);
944
+ return WordPressService.getInstance().useDeleteCategory(id, force);
708
945
  }
709
- /* -------------------------------------------------------------------------- */
710
- /* Tags */
711
- /* -------------------------------------------------------------------------- */
712
946
  /**
713
947
  * Retrieves a list of WordPress tags.
714
948
  *
@@ -721,7 +955,7 @@ export async function useWpDeleteCategory(id, force = false) {
721
955
  * ```
722
956
  */
723
957
  export async function useWpGetTags(options) {
724
- return wpFetch(`/tags${buildQueryParams(options)}`, {}, WpTagListSchema);
958
+ return WordPressService.getInstance().useGetTags(options);
725
959
  }
726
960
  /**
727
961
  * Retrieves a single WordPress tag by ID.
@@ -735,7 +969,7 @@ export async function useWpGetTags(options) {
735
969
  * ```
736
970
  */
737
971
  export async function useWpGetTag(id) {
738
- return wpFetch(`/tags/${id}`, {}, WpTagResponseSchema);
972
+ return WordPressService.getInstance().useGetTag(id);
739
973
  }
740
974
  /**
741
975
  * Creates a new WordPress tag.
@@ -749,13 +983,7 @@ export async function useWpGetTag(id) {
749
983
  * ```
750
984
  */
751
985
  export async function useWpCreateTag(data) {
752
- const parsed = useValidate(WpTagCreateSchema, data);
753
- if (!parsed.ok)
754
- return invalidInput(parsed);
755
- return wpFetch("/tags", {
756
- method: "POST",
757
- body: JSON.stringify(data),
758
- }, WpTagResponseSchema);
986
+ return WordPressService.getInstance().useCreateTag(data);
759
987
  }
760
988
  /**
761
989
  * Updates an existing WordPress tag.
@@ -770,13 +998,7 @@ export async function useWpCreateTag(data) {
770
998
  * ```
771
999
  */
772
1000
  export async function useWpUpdateTag(id, data) {
773
- const parsed = useValidate(WpTagUpdateSchema, data);
774
- if (!parsed.ok)
775
- return invalidInput(parsed);
776
- return wpFetch(`/tags/${id}`, {
777
- method: "POST",
778
- body: JSON.stringify(data),
779
- }, WpTagResponseSchema);
1001
+ return WordPressService.getInstance().useUpdateTag(id, data);
780
1002
  }
781
1003
  /**
782
1004
  * Deletes a WordPress tag.
@@ -791,13 +1013,8 @@ export async function useWpUpdateTag(id, data) {
791
1013
  * ```
792
1014
  */
793
1015
  export async function useWpDeleteTag(id, force = false) {
794
- return wpFetch(`/tags/${id}?force=${force}`, {
795
- method: "DELETE",
796
- }, WpTagResponseSchema);
1016
+ return WordPressService.getInstance().useDeleteTag(id, force);
797
1017
  }
798
- /* -------------------------------------------------------------------------- */
799
- /* Comments */
800
- /* -------------------------------------------------------------------------- */
801
1018
  /**
802
1019
  * Retrieves a list of WordPress comments.
803
1020
  *
@@ -806,12 +1023,11 @@ export async function useWpDeleteTag(id, force = false) {
806
1023
  *
807
1024
  * @example
808
1025
  * ```ts
809
- * // Get comments for a specific post
810
1026
  * const result = await useWpGetComments({ post: 42 });
811
1027
  * ```
812
1028
  */
813
1029
  export async function useWpGetComments(options) {
814
- return wpFetch(`/comments${buildQueryParams(options)}`, {}, WpCommentListSchema);
1030
+ return WordPressService.getInstance().useGetComments(options);
815
1031
  }
816
1032
  /**
817
1033
  * Retrieves a single WordPress comment by ID.
@@ -825,7 +1041,7 @@ export async function useWpGetComments(options) {
825
1041
  * ```
826
1042
  */
827
1043
  export async function useWpGetComment(id) {
828
- return wpFetch(`/comments/${id}`, {}, WpCommentResponseSchema);
1044
+ return WordPressService.getInstance().useGetComment(id);
829
1045
  }
830
1046
  /**
831
1047
  * Creates a new WordPress comment.
@@ -835,22 +1051,11 @@ export async function useWpGetComment(id) {
835
1051
  *
836
1052
  * @example
837
1053
  * ```ts
838
- * await useWpCreateComment({
839
- * post: 42,
840
- * content: "Great article!",
841
- * author_name: "John",
842
- * author_email: "john@example.com",
843
- * });
1054
+ * await useWpCreateComment({ post: 42, content: "Great article!" });
844
1055
  * ```
845
1056
  */
846
1057
  export async function useWpCreateComment(data) {
847
- const parsed = useValidate(WpCommentCreateSchema, data);
848
- if (!parsed.ok)
849
- return invalidInput(parsed);
850
- return wpFetch("/comments", {
851
- method: "POST",
852
- body: JSON.stringify(data),
853
- }, WpCommentResponseSchema);
1058
+ return WordPressService.getInstance().useCreateComment(data);
854
1059
  }
855
1060
  /**
856
1061
  * Updates an existing WordPress comment.
@@ -865,13 +1070,7 @@ export async function useWpCreateComment(data) {
865
1070
  * ```
866
1071
  */
867
1072
  export async function useWpUpdateComment(id, data) {
868
- const parsed = useValidate(WpCommentUpdateSchema, data);
869
- if (!parsed.ok)
870
- return invalidInput(parsed);
871
- return wpFetch(`/comments/${id}`, {
872
- method: "POST",
873
- body: JSON.stringify(data),
874
- }, WpCommentResponseSchema);
1073
+ return WordPressService.getInstance().useUpdateComment(id, data);
875
1074
  }
876
1075
  /**
877
1076
  * Deletes a WordPress comment.
@@ -886,13 +1085,8 @@ export async function useWpUpdateComment(id, data) {
886
1085
  * ```
887
1086
  */
888
1087
  export async function useWpDeleteComment(id, force = false) {
889
- return wpFetch(`/comments/${id}?force=${force}`, {
890
- method: "DELETE",
891
- }, WpCommentResponseSchema);
1088
+ return WordPressService.getInstance().useDeleteComment(id, force);
892
1089
  }
893
- /* -------------------------------------------------------------------------- */
894
- /* Users */
895
- /* -------------------------------------------------------------------------- */
896
1090
  /**
897
1091
  * Retrieves a list of WordPress users.
898
1092
  *
@@ -905,7 +1099,7 @@ export async function useWpDeleteComment(id, force = false) {
905
1099
  * ```
906
1100
  */
907
1101
  export async function useWpGetUsers(options) {
908
- return wpFetch(`/users${buildQueryParams(options)}`, {}, WpUserListSchema);
1102
+ return WordPressService.getInstance().useGetUsers(options);
909
1103
  }
910
1104
  /**
911
1105
  * Retrieves a single WordPress user by ID.
@@ -916,11 +1110,10 @@ export async function useWpGetUsers(options) {
916
1110
  * @example
917
1111
  * ```ts
918
1112
  * const result = await useWpGetUser(1);
919
- * if (result.ok) console.log(result.data.name);
920
1113
  * ```
921
1114
  */
922
1115
  export async function useWpGetUser(id) {
923
- return wpFetch(`/users/${id}`, {}, WpUserResponseSchema);
1116
+ return WordPressService.getInstance().useGetUser(id);
924
1117
  }
925
1118
  /**
926
1119
  * Retrieves the currently authenticated WordPress user.
@@ -930,11 +1123,10 @@ export async function useWpGetUser(id) {
930
1123
  * @example
931
1124
  * ```ts
932
1125
  * const result = await useWpGetCurrentUser();
933
- * if (result.ok) console.log(result.data.email);
934
1126
  * ```
935
1127
  */
936
1128
  export async function useWpGetCurrentUser() {
937
- return wpFetch("/users/me", {}, WpUserResponseSchema);
1129
+ return WordPressService.getInstance().useGetCurrentUser();
938
1130
  }
939
1131
  /**
940
1132
  * Creates a new WordPress user.
@@ -944,22 +1136,11 @@ export async function useWpGetCurrentUser() {
944
1136
  *
945
1137
  * @example
946
1138
  * ```ts
947
- * await useWpCreateUser({
948
- * username: "johndoe",
949
- * email: "john@example.com",
950
- * password: "secure-password",
951
- * roles: ["editor"],
952
- * });
1139
+ * await useWpCreateUser({ username: "johndoe", email: "john@example.com", password: "..." });
953
1140
  * ```
954
1141
  */
955
1142
  export async function useWpCreateUser(data) {
956
- const parsed = useValidate(WpUserCreateSchema, data);
957
- if (!parsed.ok)
958
- return invalidInput(parsed);
959
- return wpFetch("/users", {
960
- method: "POST",
961
- body: JSON.stringify(data),
962
- }, WpUserResponseSchema);
1143
+ return WordPressService.getInstance().useCreateUser(data);
963
1144
  }
964
1145
  /**
965
1146
  * Updates an existing WordPress user.
@@ -974,13 +1155,7 @@ export async function useWpCreateUser(data) {
974
1155
  * ```
975
1156
  */
976
1157
  export async function useWpUpdateUser(id, data) {
977
- const parsed = useValidate(WpUserUpdateSchema, data);
978
- if (!parsed.ok)
979
- return invalidInput(parsed);
980
- return wpFetch(`/users/${id}`, {
981
- method: "POST",
982
- body: JSON.stringify(data),
983
- }, WpUserResponseSchema);
1158
+ return WordPressService.getInstance().useUpdateUser(id, data);
984
1159
  }
985
1160
  /**
986
1161
  * Deletes a WordPress user.
@@ -991,18 +1166,12 @@ export async function useWpUpdateUser(id, data) {
991
1166
  *
992
1167
  * @example
993
1168
  * ```ts
994
- * await useWpDeleteUser(2, 1); // Reassign content to user 1
1169
+ * await useWpDeleteUser(2, 1);
995
1170
  * ```
996
1171
  */
997
1172
  export async function useWpDeleteUser(id, reassign) {
998
- const qs = reassign ? `?reassign=${reassign}` : "";
999
- return wpFetch(`/users/${id}${qs}`, {
1000
- method: "DELETE",
1001
- }, WpUserResponseSchema);
1173
+ return WordPressService.getInstance().useDeleteUser(id, reassign);
1002
1174
  }
1003
- /* -------------------------------------------------------------------------- */
1004
- /* Custom Post Types */
1005
- /* -------------------------------------------------------------------------- */
1006
1175
  /**
1007
1176
  * Retrieves a list of custom post type entries.
1008
1177
  *
@@ -1016,7 +1185,7 @@ export async function useWpDeleteUser(id, reassign) {
1016
1185
  * ```
1017
1186
  */
1018
1187
  export async function useWpGetCustomPosts(postType, options) {
1019
- return wpFetch(`/${postType}${buildQueryParams(options)}`);
1188
+ return WordPressService.getInstance().useGetCustomPosts(postType, options);
1020
1189
  }
1021
1190
  /**
1022
1191
  * Retrieves a single custom post type entry by ID.
@@ -1032,7 +1201,7 @@ export async function useWpGetCustomPosts(postType, options) {
1032
1201
  * ```
1033
1202
  */
1034
1203
  export async function useWpGetCustomPost(postType, id, options) {
1035
- return wpFetch(`/${postType}/${id}${buildQueryParams(options)}`);
1204
+ return WordPressService.getInstance().useGetCustomPost(postType, id, options);
1036
1205
  }
1037
1206
  /**
1038
1207
  * Creates a new custom post type entry.
@@ -1043,18 +1212,11 @@ export async function useWpGetCustomPost(postType, id, options) {
1043
1212
  *
1044
1213
  * @example
1045
1214
  * ```ts
1046
- * await useWpCreateCustomPost("product", {
1047
- * title: "Widget",
1048
- * content: "<p>A great widget</p>",
1049
- * status: "publish",
1050
- * });
1215
+ * await useWpCreateCustomPost("product", { title: "Widget", status: "publish" });
1051
1216
  * ```
1052
1217
  */
1053
1218
  export async function useWpCreateCustomPost(postType, data) {
1054
- return wpFetch(`/${postType}`, {
1055
- method: "POST",
1056
- body: JSON.stringify(data),
1057
- });
1219
+ return WordPressService.getInstance().useCreateCustomPost(postType, data);
1058
1220
  }
1059
1221
  /**
1060
1222
  * Updates a custom post type entry.
@@ -1070,10 +1232,7 @@ export async function useWpCreateCustomPost(postType, data) {
1070
1232
  * ```
1071
1233
  */
1072
1234
  export async function useWpUpdateCustomPost(postType, id, data) {
1073
- return wpFetch(`/${postType}/${id}`, {
1074
- method: "POST",
1075
- body: JSON.stringify(data),
1076
- });
1235
+ return WordPressService.getInstance().useUpdateCustomPost(postType, id, data);
1077
1236
  }
1078
1237
  /**
1079
1238
  * Deletes a custom post type entry.
@@ -1089,13 +1248,8 @@ export async function useWpUpdateCustomPost(postType, id, data) {
1089
1248
  * ```
1090
1249
  */
1091
1250
  export async function useWpDeleteCustomPost(postType, id, force = false) {
1092
- return wpFetch(`/${postType}/${id}?force=${force}`, {
1093
- method: "DELETE",
1094
- });
1251
+ return WordPressService.getInstance().useDeleteCustomPost(postType, id, force);
1095
1252
  }
1096
- /* -------------------------------------------------------------------------- */
1097
- /* Batch Operations */
1098
- /* -------------------------------------------------------------------------- */
1099
1253
  /**
1100
1254
  * Executes multiple WordPress REST API requests in a single batch.
1101
1255
  *
@@ -1106,75 +1260,30 @@ export async function useWpDeleteCustomPost(postType, id, force = false) {
1106
1260
  * ```ts
1107
1261
  * const result = await useWpBatch([
1108
1262
  * { method: "GET", path: "/wp/v2/posts/1" },
1109
- * { method: "POST", path: "/wp/v2/posts", body: { title: "New Post" } },
1110
1263
  * ]);
1111
- * if (result.ok) console.log(result.data.responses);
1112
1264
  * ```
1113
1265
  */
1114
1266
  export async function useWpBatch(operations) {
1115
- for (const operation of operations) {
1116
- const parsed = useValidate(WpBatchOperationSchema, operation);
1117
- if (!parsed.ok)
1118
- return invalidInput(parsed);
1119
- }
1120
- return wpFetch("/batch/v1", {
1121
- method: "POST",
1122
- body: JSON.stringify({ requests: operations }),
1123
- }, WpBatchResultSchema);
1267
+ return WordPressService.getInstance().useBatch(operations);
1124
1268
  }
1125
- /* -------------------------------------------------------------------------- */
1126
- /* Pagination Helpers */
1127
- /* -------------------------------------------------------------------------- */
1128
1269
  /**
1129
1270
  * Fetches ALL posts matching the criteria, automatically handling pagination.
1130
1271
  *
1131
- * Use when you need every post (e.g. for sitemap generation, RSS feeds,
1132
- * or data exports) without managing page numbers. Loops until all pages
1133
- * are fetched.
1134
- *
1135
- * @param options - Query params for filtering (`status`, `categories`, `tags`,
1136
- * `author`, `search`, `orderby`, `order`). Do NOT pass `page` or `per_page`.
1272
+ * @param options - Query params for filtering. Do NOT pass `page` or `per_page`.
1137
1273
  * @param maxItems - Cap on the total number of posts to fetch (default `1000`).
1138
1274
  * @returns All posts as a flat array.
1139
1275
  *
1140
1276
  * @example
1141
1277
  * ```ts
1142
- * // Get ALL published posts
1143
1278
  * const result = await useWpListAllPosts({ status: "publish" });
1144
- * if (result.ok) console.log(`Total: ${result.data.length}`);
1145
- *
1146
- * // Get ALL posts from category 5
1147
- * const cat5 = await useWpListAllPosts({ categories: 5 });
1148
1279
  * ```
1149
1280
  */
1150
1281
  export async function useWpListAllPosts(options, maxItems = 1000) {
1151
- const allPosts = [];
1152
- let page = 1;
1153
- while (true) {
1154
- const result = await useWpGetPosts({ ...options, page, per_page: 100 });
1155
- if (!result.ok)
1156
- return result;
1157
- allPosts.push(...result.data);
1158
- if (maxItems && allPosts.length >= maxItems)
1159
- break;
1160
- if (result.data.length < 100)
1161
- break;
1162
- page++;
1163
- }
1164
- return {
1165
- data: allPosts.slice(0, maxItems),
1166
- error: null,
1167
- url: "",
1168
- status: 200,
1169
- ok: true,
1170
- };
1282
+ return WordPressService.getInstance().useListAllPosts(options, maxItems);
1171
1283
  }
1172
1284
  /**
1173
1285
  * Searches ALL posts by keyword, automatically handling pagination.
1174
1286
  *
1175
- * Use to find posts by title or content when you need every match,
1176
- * not just the first page. Combines `search` param with auto-pagination.
1177
- *
1178
1287
  * @param query - Search term (searches title and content).
1179
1288
  * @param options - Additional filters (`status`, `categories`, etc.).
1180
1289
  * @returns All posts matching the search.
@@ -1182,41 +1291,23 @@ export async function useWpListAllPosts(options, maxItems = 1000) {
1182
1291
  * @example
1183
1292
  * ```ts
1184
1293
  * const result = await useWpSearchAllPosts("tutorial");
1185
- * if (result.ok) result.data.forEach(p => console.log(p.title.rendered));
1186
1294
  * ```
1187
1295
  */
1188
1296
  export async function useWpSearchAllPosts(query, options) {
1189
- return useWpListAllPosts({ ...options, search: query });
1297
+ return WordPressService.getInstance().useSearchAllPosts(query, options);
1190
1298
  }
1191
1299
  /**
1192
- * Finds a single post by its URL slug.
1193
- *
1194
- * Use for slug-based routing (e.g. `/blog/:slug` pages) where you need
1195
- * to fetch a post by its human-readable URL identifier. Returns `null`
1196
- * if no post matches the slug.
1300
+ * Finds a single post by its URL slug. Returns `null` if not found.
1197
1301
  *
1198
1302
  * @param slug - The post slug (e.g. "hello-world").
1199
1303
  * @returns The post or `null` if not found.
1200
1304
  *
1201
1305
  * @example
1202
1306
  * ```ts
1203
- * // In an Astro/[slug].astro or similar dynamic route
1204
1307
  * const result = await useWpFindPostBySlug("hello-world");
1205
- * if (result.ok && result.data) {
1206
- * console.log(result.data.title.rendered);
1207
- * }
1208
1308
  * ```
1209
1309
  */
1210
1310
  export async function useWpFindPostBySlug(slug) {
1211
- const result = await useWpGetPosts({ slug, per_page: 1 });
1212
- if (!result.ok)
1213
- return result;
1214
- return {
1215
- data: result.data[0] ?? null,
1216
- error: null,
1217
- url: result.url,
1218
- status: result.status,
1219
- ok: true,
1220
- };
1311
+ return WordPressService.getInstance().useFindPostBySlug(slug);
1221
1312
  }
1222
1313
  //# sourceMappingURL=wordpress.service.js.map