@atomic-solutions/wordpress-api-client 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +295 -0
  3. package/dist/calculatePagination-WUlN3OdM.d.cts +19 -0
  4. package/dist/calculatePagination-WUlN3OdM.d.ts +19 -0
  5. package/dist/client/index.cjs +1143 -0
  6. package/dist/client/index.cjs.map +1 -0
  7. package/dist/client/index.d.cts +10 -0
  8. package/dist/client/index.d.ts +10 -0
  9. package/dist/client/index.js +1137 -0
  10. package/dist/client/index.js.map +1 -0
  11. package/dist/http/index.cjs +674 -0
  12. package/dist/http/index.cjs.map +1 -0
  13. package/dist/http/index.d.cts +12 -0
  14. package/dist/http/index.d.ts +12 -0
  15. package/dist/http/index.js +667 -0
  16. package/dist/http/index.js.map +1 -0
  17. package/dist/index.cjs +1251 -0
  18. package/dist/index.cjs.map +1 -0
  19. package/dist/index.d.cts +51 -0
  20. package/dist/index.d.ts +51 -0
  21. package/dist/index.js +1205 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/schemas/index.cjs +414 -0
  24. package/dist/schemas/index.cjs.map +1 -0
  25. package/dist/schemas/index.d.cts +1339 -0
  26. package/dist/schemas/index.d.ts +1339 -0
  27. package/dist/schemas/index.js +376 -0
  28. package/dist/schemas/index.js.map +1 -0
  29. package/dist/testing/index.cjs +1435 -0
  30. package/dist/testing/index.cjs.map +1 -0
  31. package/dist/testing/index.d.cts +50 -0
  32. package/dist/testing/index.d.ts +50 -0
  33. package/dist/testing/index.js +1426 -0
  34. package/dist/testing/index.js.map +1 -0
  35. package/dist/types-8pbwmNdu.d.ts +154 -0
  36. package/dist/types-BTo088EY.d.cts +154 -0
  37. package/dist/user.schema-eeUHQ4sI.d.cts +10614 -0
  38. package/dist/user.schema-eeUHQ4sI.d.ts +10614 -0
  39. package/dist/utils/index.cjs +47 -0
  40. package/dist/utils/index.cjs.map +1 -0
  41. package/dist/utils/index.d.cts +10 -0
  42. package/dist/utils/index.d.ts +10 -0
  43. package/dist/utils/index.js +44 -0
  44. package/dist/utils/index.js.map +1 -0
  45. package/package.json +103 -0
@@ -0,0 +1,1137 @@
1
+ import axios, { isAxiosError } from 'axios';
2
+ import { z } from 'zod';
3
+
4
+ // src/client/createClient.ts
5
+
6
+ // src/constants/endpoints.ts
7
+ var ENDPOINTS = {
8
+ // Posts
9
+ POSTS: "/wp/v2/posts",
10
+ POST: (id) => `/wp/v2/posts/${id}`,
11
+ // Categories
12
+ CATEGORIES: "/wp/v2/categories",
13
+ CATEGORY: (id) => `/wp/v2/categories/${id}`,
14
+ // Tags
15
+ TAGS: "/wp/v2/tags",
16
+ TAG: (id) => `/wp/v2/tags/${id}`,
17
+ // Media
18
+ MEDIA: "/wp/v2/media",
19
+ MEDIA_ITEM: (id) => `/wp/v2/media/${id}`,
20
+ // Users
21
+ USERS: "/wp/v2/users",
22
+ USER: (id) => `/wp/v2/users/${id}`,
23
+ USERS_ME: "/wp/v2/users/me",
24
+ // JWT Auth (requires JWT Authentication plugin)
25
+ JWT_TOKEN: "/jwt-auth/v1/token",
26
+ JWT_VALIDATE: "/jwt-auth/v1/token/validate",
27
+ // Pages
28
+ PAGES: "/wp/v2/pages",
29
+ PAGE: (id) => `/wp/v2/pages/${id}`,
30
+ // Comments
31
+ COMMENTS: "/wp/v2/comments",
32
+ COMMENT: (id) => `/wp/v2/comments/${id}`,
33
+ // Search
34
+ SEARCH: "/wp/v2/search"
35
+ };
36
+
37
+ // src/errors/classes/BaseError.ts
38
+ var BaseError = class extends Error {
39
+ constructor(options) {
40
+ super(options.message);
41
+ this.options = options;
42
+ Object.setPrototypeOf(this, new.target.prototype);
43
+ this.name = this.constructor.name;
44
+ if (Error.captureStackTrace) {
45
+ Error.captureStackTrace(this, this.constructor);
46
+ }
47
+ if (this.options.cause instanceof Error && this.options.cause.stack) {
48
+ this.stack = `${this.stack}
49
+ Caused by: ${this.options.cause.stack}`;
50
+ }
51
+ }
52
+ /**
53
+ * Get reportable data for error tracking/logging
54
+ * Subclasses should override this to add their own reportable fields
55
+ */
56
+ getReportableData() {
57
+ const { code, operation, userMessage, retryable } = this.options;
58
+ return {
59
+ code,
60
+ operation,
61
+ userMessage,
62
+ retryable
63
+ };
64
+ }
65
+ /**
66
+ * Custom JSON serialization
67
+ * Makes the error properly serializable for logging/reporting
68
+ */
69
+ toJSON() {
70
+ return {
71
+ ...this.getReportableData(),
72
+ name: this.name,
73
+ message: this.message,
74
+ stack: this.stack
75
+ };
76
+ }
77
+ };
78
+
79
+ // src/errors/mapping/auth.ts
80
+ var AUTH_CODE_TRANSFORMATIONS = {
81
+ // Authentication
82
+ jwt_auth_failed: "auth_failed",
83
+ jwt_auth_invalid_token: "token_expired",
84
+ jwt_auth_bad_request: "invalid_auth_request",
85
+ // Authorization
86
+ rest_forbidden: "forbidden",
87
+ rest_cannot_read: "cannot_read",
88
+ rest_cannot_edit: "cannot_edit",
89
+ rest_cannot_create: "cannot_create",
90
+ rest_cannot_delete: "cannot_delete"
91
+ };
92
+ var AUTH_ERROR_METADATA = {
93
+ // Authentication
94
+ auth_failed: {
95
+ operation: "authenticate",
96
+ retryable: false,
97
+ userMessage: "Invalid username or password"
98
+ },
99
+ token_expired: {
100
+ operation: "validate_token",
101
+ retryable: false,
102
+ userMessage: "Your session has expired. Please sign in again."
103
+ },
104
+ invalid_auth_request: {
105
+ operation: "authenticate",
106
+ retryable: false,
107
+ userMessage: "Invalid login request"
108
+ },
109
+ // Authorization
110
+ forbidden: {
111
+ operation: "authorize",
112
+ retryable: false,
113
+ userMessage: "You do not have permission to access this resource"
114
+ },
115
+ cannot_read: {
116
+ operation: "read_resource",
117
+ retryable: false,
118
+ userMessage: "You do not have permission to view this content"
119
+ },
120
+ cannot_edit: {
121
+ operation: "edit_resource",
122
+ retryable: false,
123
+ userMessage: "You do not have permission to edit this content"
124
+ },
125
+ cannot_create: {
126
+ operation: "create_resource",
127
+ retryable: false,
128
+ userMessage: "You do not have permission to create content"
129
+ },
130
+ cannot_delete: {
131
+ operation: "delete_resource",
132
+ retryable: false,
133
+ userMessage: "You do not have permission to delete this content"
134
+ }
135
+ };
136
+
137
+ // src/errors/mapping/resources.ts
138
+ var RESOURCE_CODE_TRANSFORMATIONS = {
139
+ rest_post_invalid_id: "post_not_found",
140
+ rest_term_invalid: "term_not_found",
141
+ rest_user_invalid_id: "user_not_found",
142
+ rest_no_route: "route_not_found"
143
+ };
144
+ var RESOURCE_ERROR_METADATA = {
145
+ post_not_found: {
146
+ operation: "get_post",
147
+ retryable: false,
148
+ userMessage: "Post not found"
149
+ },
150
+ term_not_found: {
151
+ operation: "get_term",
152
+ retryable: false,
153
+ userMessage: "Category or tag not found"
154
+ },
155
+ user_not_found: {
156
+ operation: "get_user",
157
+ retryable: false,
158
+ userMessage: "User not found"
159
+ },
160
+ route_not_found: {
161
+ operation: "api_request",
162
+ retryable: false,
163
+ userMessage: "The requested resource was not found"
164
+ }
165
+ };
166
+
167
+ // src/errors/mapping/server.ts
168
+ var SERVER_CODE_TRANSFORMATIONS = {
169
+ rest_cookie_invalid_nonce: "invalid_nonce",
170
+ internal_error: "server_error"
171
+ };
172
+ var SERVER_ERROR_METADATA = {
173
+ invalid_nonce: {
174
+ operation: "api_request",
175
+ retryable: true,
176
+ userMessage: "Session expired. Please try again."
177
+ },
178
+ server_error: {
179
+ operation: "api_request",
180
+ retryable: true,
181
+ userMessage: "An unexpected error occurred. Please try again."
182
+ },
183
+ unknown_error: {
184
+ operation: "api_request",
185
+ retryable: false,
186
+ userMessage: "An error occurred"
187
+ }
188
+ };
189
+
190
+ // src/errors/mapping/constants.ts
191
+ var VALIDATION_CODE_TRANSFORMATIONS = {
192
+ rest_invalid_param: "invalid_params",
193
+ rest_missing_callback_param: "missing_params"
194
+ };
195
+ var VALIDATION_ERROR_METADATA = {
196
+ invalid_params: {
197
+ operation: "api_request",
198
+ retryable: false,
199
+ userMessage: "Invalid request parameters"
200
+ },
201
+ missing_params: {
202
+ operation: "api_request",
203
+ retryable: false,
204
+ userMessage: "Missing required parameters"
205
+ }
206
+ };
207
+ var WORDPRESS_ERROR_METADATA = {
208
+ ...AUTH_ERROR_METADATA,
209
+ ...RESOURCE_ERROR_METADATA,
210
+ ...VALIDATION_ERROR_METADATA,
211
+ ...SERVER_ERROR_METADATA
212
+ };
213
+
214
+ // src/errors/mapping/index.ts
215
+ var CODE_TRANSFORMATIONS = {
216
+ ...AUTH_CODE_TRANSFORMATIONS,
217
+ ...RESOURCE_CODE_TRANSFORMATIONS,
218
+ ...VALIDATION_CODE_TRANSFORMATIONS,
219
+ ...SERVER_CODE_TRANSFORMATIONS
220
+ };
221
+ var ERROR_METADATA = {
222
+ ...AUTH_ERROR_METADATA,
223
+ ...RESOURCE_ERROR_METADATA,
224
+ ...VALIDATION_ERROR_METADATA,
225
+ ...SERVER_ERROR_METADATA
226
+ };
227
+ var mapWordPressCode = (apiCode, status) => {
228
+ const appCode = CODE_TRANSFORMATIONS[apiCode];
229
+ if (appCode) {
230
+ const metadata = ERROR_METADATA[appCode];
231
+ return {
232
+ code: appCode,
233
+ ...metadata
234
+ };
235
+ }
236
+ if (apiCode.includes("invalid_id") || apiCode.includes("not_found")) {
237
+ return {
238
+ code: "route_not_found",
239
+ ...ERROR_METADATA.route_not_found
240
+ };
241
+ }
242
+ if (apiCode.includes("cannot_") || apiCode.includes("forbidden")) {
243
+ return {
244
+ code: "forbidden",
245
+ ...ERROR_METADATA.forbidden
246
+ };
247
+ }
248
+ if (apiCode.includes("invalid_param") || apiCode.includes("missing_")) {
249
+ return {
250
+ code: "invalid_params",
251
+ ...ERROR_METADATA.invalid_params
252
+ };
253
+ }
254
+ if (apiCode.includes("nonce")) {
255
+ return {
256
+ code: "invalid_nonce",
257
+ ...ERROR_METADATA.invalid_nonce
258
+ };
259
+ }
260
+ if (status === 401) {
261
+ return {
262
+ code: "auth_failed",
263
+ retryable: false,
264
+ userMessage: "Authentication required"
265
+ };
266
+ }
267
+ if (status === 403) {
268
+ return {
269
+ code: "forbidden",
270
+ ...ERROR_METADATA.forbidden
271
+ };
272
+ }
273
+ if (status === 404) {
274
+ return {
275
+ code: "route_not_found",
276
+ ...ERROR_METADATA.route_not_found
277
+ };
278
+ }
279
+ if (status >= 500) {
280
+ return {
281
+ code: "server_error",
282
+ ...ERROR_METADATA.server_error
283
+ };
284
+ }
285
+ return {
286
+ code: "unknown_error",
287
+ ...ERROR_METADATA.unknown_error
288
+ };
289
+ };
290
+
291
+ // src/errors/classes/WordPressApiError.ts
292
+ var WordPressApiError = class extends BaseError {
293
+ constructor(options) {
294
+ const metadata = WORDPRESS_ERROR_METADATA[options.code] ?? {
295
+ operation: "wordpress_api_request",
296
+ retryable: false
297
+ };
298
+ const enrichedOptions = {
299
+ ...options,
300
+ operation: options.operation || metadata.operation,
301
+ userMessage: options.userMessage || metadata.userMessage,
302
+ retryable: options.retryable ?? metadata.retryable
303
+ };
304
+ super(enrichedOptions);
305
+ }
306
+ // Convenient getters for frequently accessed fields (no duplication)
307
+ get wpCode() {
308
+ return this.options.code;
309
+ }
310
+ get originalCode() {
311
+ return this.options.originalCode;
312
+ }
313
+ get statusCode() {
314
+ return this.options.statusCode;
315
+ }
316
+ get url() {
317
+ return this.options.url;
318
+ }
319
+ get method() {
320
+ return this.options.method;
321
+ }
322
+ get requestBody() {
323
+ return this.options.requestBody;
324
+ }
325
+ get responseBody() {
326
+ return this.options.responseBody;
327
+ }
328
+ get data() {
329
+ return this.options.data;
330
+ }
331
+ /**
332
+ * Check if error is an authentication error
333
+ */
334
+ get isAuthError() {
335
+ return this.statusCode === 401 || this.wpCode === "auth_failed" || this.wpCode === "token_expired" || this.wpCode === "invalid_auth_request";
336
+ }
337
+ /**
338
+ * Check if error is a not found error
339
+ */
340
+ get isNotFoundError() {
341
+ return this.statusCode === 404 || this.wpCode === "route_not_found" || this.wpCode === "post_not_found" || this.wpCode === "term_not_found" || this.wpCode === "user_not_found";
342
+ }
343
+ /**
344
+ * Get reportable data for error tracking/logging
345
+ * Includes HTTP context and WordPress-specific fields
346
+ */
347
+ getReportableData() {
348
+ return {
349
+ code: this.options.code,
350
+ operation: this.options.operation,
351
+ userMessage: this.options.userMessage,
352
+ retryable: this.options.retryable,
353
+ statusCode: this.options.statusCode,
354
+ url: this.options.url,
355
+ method: this.options.method,
356
+ wpCode: this.options.code,
357
+ originalCode: this.options.originalCode
358
+ };
359
+ }
360
+ };
361
+
362
+ // src/errors/classes/WordPressDataValidationError.ts
363
+ var WordPressDataValidationError = class _WordPressDataValidationError extends BaseError {
364
+ constructor(options) {
365
+ const fieldErrors = options.zodError ? _WordPressDataValidationError.extractFieldErrorsFromZod(options.zodError) : void 0;
366
+ super({
367
+ code: "validation_error",
368
+ message: options.message,
369
+ operation: options.operation,
370
+ userMessage: options.userMessage || "Received unexpected data from WordPress",
371
+ retryable: false,
372
+ cause: options.cause,
373
+ url: options.url,
374
+ zodError: options.zodError,
375
+ value: options.value,
376
+ fieldErrors
377
+ });
378
+ }
379
+ // Convenient getters for validation-specific fields (no duplication)
380
+ get url() {
381
+ return this.options.url;
382
+ }
383
+ get zodError() {
384
+ return this.options.zodError;
385
+ }
386
+ get invalidValue() {
387
+ return this.options.value;
388
+ }
389
+ get fieldErrors() {
390
+ return this.options.fieldErrors;
391
+ }
392
+ /**
393
+ * Get reportable data for error tracking/logging
394
+ * Includes validation-specific fields
395
+ */
396
+ getReportableData() {
397
+ return {
398
+ code: this.options.code,
399
+ operation: this.options.operation,
400
+ userMessage: this.options.userMessage,
401
+ retryable: false,
402
+ fieldErrors: this.options.fieldErrors,
403
+ url: this.options.url
404
+ };
405
+ }
406
+ /**
407
+ * Extract field errors from Zod error
408
+ */
409
+ static extractFieldErrorsFromZod(zodError) {
410
+ if (!zodError?.errors) {
411
+ return void 0;
412
+ }
413
+ const fieldErrors = {};
414
+ for (const issue of zodError.errors) {
415
+ const path = issue.path.join(".");
416
+ if (!fieldErrors[path]) {
417
+ fieldErrors[path] = [];
418
+ }
419
+ fieldErrors[path].push(issue.message);
420
+ }
421
+ return fieldErrors;
422
+ }
423
+ /**
424
+ * Get all error messages as a flat array
425
+ */
426
+ getMessages() {
427
+ if (!this.fieldErrors) {
428
+ return [this.message];
429
+ }
430
+ const messages = [];
431
+ for (const fieldMessages of Object.values(this.fieldErrors)) {
432
+ messages.push(...fieldMessages);
433
+ }
434
+ return messages;
435
+ }
436
+ /**
437
+ * Check if a specific field has errors
438
+ */
439
+ hasFieldError(field) {
440
+ return this.fieldErrors ? field in this.fieldErrors : false;
441
+ }
442
+ /**
443
+ * Get error messages for a specific field
444
+ */
445
+ getFieldError(field) {
446
+ return this.fieldErrors?.[field];
447
+ }
448
+ };
449
+
450
+ // src/http/handleApiResponse.ts
451
+ var handleApiResponse = (response, schema, options) => {
452
+ const validationMode = options?.validationMode ?? "strict";
453
+ if (validationMode === "warn") {
454
+ const result = schema.safeParse(response.data);
455
+ if (!result.success) {
456
+ const validationError = new WordPressDataValidationError({
457
+ message: `WordPress API response validation failed for ${response.config.url || "unknown endpoint"}`,
458
+ url: response.config.url,
459
+ zodError: result.error,
460
+ value: response.data,
461
+ userMessage: "Received unexpected data from WordPress"
462
+ });
463
+ options?.onValidationError?.(validationError);
464
+ options?.errorReporter?.(validationError);
465
+ console.warn("[wordpress-utils] Validation warning:", validationError.message);
466
+ return response.data;
467
+ }
468
+ return result.data;
469
+ }
470
+ try {
471
+ return schema.parse(response.data);
472
+ } catch (error2) {
473
+ if (error2 && typeof error2 === "object" && "issues" in error2) {
474
+ const validationError = new WordPressDataValidationError({
475
+ message: `WordPress API response validation failed for ${response.config.url || "unknown endpoint"}`,
476
+ url: response.config.url,
477
+ zodError: error2,
478
+ value: response.data,
479
+ userMessage: "Received unexpected data from WordPress"
480
+ });
481
+ options?.onValidationError?.(validationError);
482
+ options?.errorReporter?.(validationError);
483
+ throw validationError;
484
+ }
485
+ throw error2;
486
+ }
487
+ };
488
+
489
+ // src/utils/calculatePagination.ts
490
+ var calculatePagination = (input) => {
491
+ const { page, perPage, total, totalPages } = input;
492
+ const hasNextPage = page < totalPages;
493
+ const hasPrevPage = page > 1;
494
+ return {
495
+ page,
496
+ perPage,
497
+ total,
498
+ totalPages,
499
+ hasNextPage,
500
+ hasPrevPage,
501
+ nextPage: hasNextPage ? page + 1 : null,
502
+ prevPage: hasPrevPage ? page - 1 : null
503
+ };
504
+ };
505
+
506
+ // src/utils/getPaginationMeta.ts
507
+ var getPaginationMeta = (headers) => {
508
+ if (!headers) {
509
+ return {
510
+ total: 0,
511
+ totalPages: 0
512
+ };
513
+ }
514
+ const wpTotal = headers["x-wp-total"];
515
+ const wpTotalPages = headers["x-wp-totalpages"];
516
+ if (!wpTotal || !wpTotalPages) {
517
+ return {
518
+ total: 0,
519
+ totalPages: 0
520
+ };
521
+ }
522
+ const total = parseInt(wpTotal, 10);
523
+ const totalPages = parseInt(wpTotalPages, 10);
524
+ return {
525
+ total,
526
+ totalPages
527
+ };
528
+ };
529
+
530
+ // src/http/handlePaginatedApiResponse.ts
531
+ var handlePaginatedApiResponse = (response, schema, params = {}, options) => {
532
+ const validationMode = options?.validationMode ?? "strict";
533
+ let data;
534
+ if (validationMode === "warn") {
535
+ const result = schema.safeParse(response.data);
536
+ if (!result.success) {
537
+ const validationError = new WordPressDataValidationError({
538
+ message: `WordPress API response validation failed for ${response.config.url || "unknown endpoint"}`,
539
+ url: response.config.url,
540
+ zodError: result.error,
541
+ value: response.data,
542
+ userMessage: "Received unexpected data from WordPress"
543
+ });
544
+ options?.onValidationError?.(validationError);
545
+ options?.errorReporter?.(validationError);
546
+ console.warn("[wordpress-utils] Validation warning:", validationError.message);
547
+ data = Array.isArray(response.data) ? response.data : [];
548
+ } else {
549
+ data = result.data;
550
+ }
551
+ } else {
552
+ try {
553
+ data = schema.parse(response.data);
554
+ } catch (error2) {
555
+ if (error2 && typeof error2 === "object" && "issues" in error2) {
556
+ const validationError = new WordPressDataValidationError({
557
+ message: `WordPress API response validation failed for ${response.config.url || "unknown endpoint"}`,
558
+ url: response.config.url,
559
+ zodError: error2,
560
+ value: response.data,
561
+ userMessage: "Received unexpected data from WordPress"
562
+ });
563
+ options?.onValidationError?.(validationError);
564
+ options?.errorReporter?.(validationError);
565
+ throw validationError;
566
+ }
567
+ throw error2;
568
+ }
569
+ }
570
+ const { total, totalPages } = getPaginationMeta(response.headers);
571
+ const page = params.page || 1;
572
+ const perPage = params.per_page || 10;
573
+ const pagination = calculatePagination({
574
+ page,
575
+ perPage,
576
+ total,
577
+ totalPages
578
+ });
579
+ return {
580
+ data,
581
+ pagination
582
+ };
583
+ };
584
+
585
+ // src/logging/logger.ts
586
+ var DEBUG_PREFIX = "[wordpress-utils]";
587
+ var isDebugEnabled = () => {
588
+ if (typeof process !== "undefined" && process.env) {
589
+ return process.env.DEBUG === "true" || process.env.DEBUG === "1";
590
+ }
591
+ return false;
592
+ };
593
+ var debug = (...args) => {
594
+ if (isDebugEnabled()) {
595
+ console.log(DEBUG_PREFIX, ...args);
596
+ }
597
+ };
598
+ var error = (...args) => {
599
+ console.error(DEBUG_PREFIX, ...args);
600
+ };
601
+
602
+ // src/http/interceptors/request.ts
603
+ var setupRequestInterceptor = (axiosInstance, config, client) => {
604
+ axiosInstance.interceptors.request.use(
605
+ async (requestConfig) => {
606
+ if (config.jwtToken) {
607
+ const token = await config.jwtToken.get();
608
+ if (token) {
609
+ requestConfig.headers.Authorization = `Bearer ${token}`;
610
+ debug("JWT token injected");
611
+ }
612
+ }
613
+ if (config.onRequest) {
614
+ return config.onRequest(requestConfig, client);
615
+ }
616
+ return requestConfig;
617
+ },
618
+ (error2) => {
619
+ return Promise.reject(error2);
620
+ }
621
+ );
622
+ };
623
+
624
+ // src/http/interceptors/response.ts
625
+ var setupResponseInterceptor = (axiosInstance, config, client) => {
626
+ axiosInstance.interceptors.response.use(
627
+ async (response) => {
628
+ debug("Response:", response.config.url, response.status);
629
+ if (config.onResponse) {
630
+ return config.onResponse(response, client);
631
+ }
632
+ return response;
633
+ },
634
+ (error2) => {
635
+ return Promise.reject(error2);
636
+ }
637
+ );
638
+ };
639
+ var setupErrorInterceptor = (axiosInstance, config, client) => {
640
+ axiosInstance.interceptors.response.use(
641
+ (response) => response,
642
+ async (axiosError) => {
643
+ const status = axiosError.response?.status || 500;
644
+ const errorData = axiosError.response?.data;
645
+ const apiCode = errorData?.code || "unknown_error";
646
+ const wpMessage = errorData?.message || axiosError.message || "Unknown error";
647
+ const mappedError = mapWordPressCode(apiCode, status);
648
+ const wpError = new WordPressApiError({
649
+ message: wpMessage,
650
+ statusCode: status,
651
+ code: mappedError.code,
652
+ // Transformed app-level code
653
+ originalCode: apiCode,
654
+ // Original WordPress API code
655
+ retryable: mappedError.retryable,
656
+ userMessage: mappedError.userMessage,
657
+ operation: axiosError.config?.url || "wordpress_api_request",
658
+ url: axiosError.config?.url || "",
659
+ method: axiosError.config?.method?.toUpperCase() || "GET",
660
+ requestBody: axiosError.config?.data,
661
+ responseBody: errorData,
662
+ cause: axiosError
663
+ });
664
+ if (config.debug) {
665
+ debug("WordPress error:", apiCode, status, wpMessage);
666
+ error(wpError);
667
+ }
668
+ if ((status === 401 || status === 403) && config.onAuthError && isAxiosError(axiosError) && !axiosError.config?._retry) {
669
+ if (config.debug) {
670
+ debug("Auth error detected, calling onAuthError handler");
671
+ }
672
+ try {
673
+ const shouldRetry = await config.onAuthError(wpError, client);
674
+ if (shouldRetry) {
675
+ if (config.debug) {
676
+ debug("Retrying request after auth error");
677
+ }
678
+ axiosError.config._retry = true;
679
+ return axiosInstance.request(axiosError.config);
680
+ }
681
+ } catch (authErrorHandlerError) {
682
+ if (config.debug) {
683
+ debug("onAuthError handler threw error", authErrorHandlerError);
684
+ }
685
+ }
686
+ }
687
+ if (config.onError) {
688
+ config.onError(wpError, client);
689
+ }
690
+ config.errorReporter?.report(wpError);
691
+ return Promise.reject(wpError);
692
+ }
693
+ );
694
+ };
695
+ var paginationParamsSchema = z.object({
696
+ /** Current page number */
697
+ page: z.number().int().positive().optional(),
698
+ /** Items per page */
699
+ per_page: z.number().int().positive().max(100).optional(),
700
+ /** Offset for pagination */
701
+ offset: z.number().int().nonnegative().optional(),
702
+ /** Sort order */
703
+ order: z.enum(["asc", "desc"]).optional(),
704
+ /** Field to sort by */
705
+ orderby: z.string().optional()
706
+ });
707
+ z.object({
708
+ /** Total number of items */
709
+ total: z.number(),
710
+ /** Total number of pages */
711
+ totalPages: z.number(),
712
+ /** Current page */
713
+ currentPage: z.number(),
714
+ /** Items per page */
715
+ perPage: z.number()
716
+ });
717
+ z.object({
718
+ username: z.string(),
719
+ password: z.string()
720
+ });
721
+ var JwtTokenResponseSchema = z.object({
722
+ token: z.string(),
723
+ user_email: z.string(),
724
+ user_nicename: z.string(),
725
+ user_display_name: z.string()
726
+ });
727
+ z.object({
728
+ code: z.string(),
729
+ data: z.object({
730
+ status: z.number()
731
+ })
732
+ });
733
+ z.object({
734
+ code: z.string(),
735
+ message: z.string(),
736
+ data: z.object({
737
+ status: z.number()
738
+ }).optional()
739
+ });
740
+ var RenderedContentSchema = z.object({
741
+ rendered: z.string(),
742
+ protected: z.boolean().optional()
743
+ });
744
+ var GuidSchema = z.object({
745
+ rendered: z.string()
746
+ });
747
+ z.enum([
748
+ "publish",
749
+ "future",
750
+ "draft",
751
+ "pending",
752
+ "private",
753
+ "trash",
754
+ "auto-draft",
755
+ "inherit"
756
+ ]);
757
+ var HalLinkItemSchema = z.object({
758
+ href: z.string(),
759
+ embeddable: z.boolean().optional(),
760
+ templated: z.boolean().optional()
761
+ });
762
+ var HalLinksSchema = z.object({
763
+ self: z.array(HalLinkItemSchema).optional(),
764
+ collection: z.array(HalLinkItemSchema).optional(),
765
+ about: z.array(HalLinkItemSchema).optional(),
766
+ author: z.array(HalLinkItemSchema).optional(),
767
+ replies: z.array(HalLinkItemSchema).optional(),
768
+ "wp:featuredmedia": z.array(HalLinkItemSchema).optional(),
769
+ "wp:attachment": z.array(HalLinkItemSchema).optional(),
770
+ "wp:term": z.array(HalLinkItemSchema).optional(),
771
+ "wp:post_type": z.array(HalLinkItemSchema).optional(),
772
+ curies: z.array(
773
+ z.object({
774
+ name: z.string(),
775
+ href: z.string(),
776
+ templated: z.boolean()
777
+ })
778
+ ).optional()
779
+ }).passthrough();
780
+ z.object({
781
+ id: z.number(),
782
+ date: z.string(),
783
+ date_gmt: z.string(),
784
+ modified: z.string(),
785
+ modified_gmt: z.string(),
786
+ slug: z.string(),
787
+ status: z.string(),
788
+ type: z.string(),
789
+ link: z.string(),
790
+ _links: HalLinksSchema.optional()
791
+ });
792
+ var CategorySchema = z.object({
793
+ id: z.number(),
794
+ count: z.number(),
795
+ description: z.string().optional(),
796
+ link: z.string(),
797
+ name: z.string(),
798
+ slug: z.string(),
799
+ taxonomy: z.string(),
800
+ parent: z.number(),
801
+ meta: z.any().optional(),
802
+ _links: HalLinksSchema.optional()
803
+ });
804
+ var CategoriesListSchema = z.array(CategorySchema);
805
+ paginationParamsSchema.extend({
806
+ context: z.enum(["view", "embed", "edit"]).optional(),
807
+ search: z.string().optional(),
808
+ exclude: z.array(z.number()).optional(),
809
+ include: z.array(z.number()).optional(),
810
+ hide_empty: z.boolean().optional(),
811
+ parent: z.number().optional(),
812
+ post: z.number().optional(),
813
+ slug: z.string().optional()
814
+ });
815
+ var TagSchema = z.object({
816
+ id: z.number(),
817
+ count: z.number(),
818
+ description: z.string().optional(),
819
+ link: z.string(),
820
+ name: z.string(),
821
+ slug: z.string(),
822
+ taxonomy: z.string(),
823
+ meta: z.any().optional(),
824
+ _links: HalLinksSchema.optional()
825
+ });
826
+ z.array(TagSchema);
827
+ var MediaSizeSchema = z.object({
828
+ file: z.string(),
829
+ width: z.number(),
830
+ height: z.number(),
831
+ filesize: z.number().optional(),
832
+ mime_type: z.string(),
833
+ source_url: z.string()
834
+ });
835
+ var MediaSizesSchema = z.object({
836
+ thumbnail: MediaSizeSchema.optional(),
837
+ medium: MediaSizeSchema.optional(),
838
+ medium_large: MediaSizeSchema.optional(),
839
+ large: MediaSizeSchema.optional(),
840
+ full: MediaSizeSchema.optional()
841
+ }).passthrough();
842
+ var ImageMetaSchema = z.object({
843
+ aperture: z.string().optional(),
844
+ credit: z.string().optional(),
845
+ camera: z.string().optional(),
846
+ caption: z.string().optional(),
847
+ created_timestamp: z.string().optional(),
848
+ copyright: z.string().optional(),
849
+ focal_length: z.string().optional(),
850
+ iso: z.string().optional(),
851
+ shutter_speed: z.string().optional(),
852
+ title: z.string().optional(),
853
+ orientation: z.string().optional(),
854
+ keywords: z.array(z.string()).optional()
855
+ });
856
+ var MediaDetailsSchema = z.object({
857
+ width: z.number(),
858
+ height: z.number(),
859
+ file: z.string(),
860
+ filesize: z.number().optional(),
861
+ sizes: MediaSizesSchema,
862
+ image_meta: ImageMetaSchema.optional()
863
+ });
864
+ var MediaSchema = z.object({
865
+ id: z.number(),
866
+ date: z.string(),
867
+ date_gmt: z.string(),
868
+ guid: GuidSchema,
869
+ modified: z.string(),
870
+ modified_gmt: z.string(),
871
+ slug: z.string(),
872
+ status: z.string(),
873
+ type: z.string(),
874
+ link: z.string(),
875
+ title: RenderedContentSchema,
876
+ author: z.number(),
877
+ comment_status: z.string(),
878
+ ping_status: z.string(),
879
+ template: z.string().optional(),
880
+ meta: z.any().optional(),
881
+ description: RenderedContentSchema,
882
+ caption: RenderedContentSchema,
883
+ alt_text: z.string(),
884
+ media_type: z.string(),
885
+ mime_type: z.string(),
886
+ media_details: MediaDetailsSchema,
887
+ post: z.number().nullable(),
888
+ source_url: z.string(),
889
+ _links: HalLinksSchema.optional()
890
+ });
891
+ z.array(MediaSchema);
892
+ var PostSchema = z.object({
893
+ id: z.number(),
894
+ date: z.string(),
895
+ date_gmt: z.string(),
896
+ guid: GuidSchema,
897
+ modified: z.string(),
898
+ modified_gmt: z.string(),
899
+ slug: z.string(),
900
+ status: z.string(),
901
+ type: z.string(),
902
+ link: z.string(),
903
+ title: RenderedContentSchema,
904
+ content: RenderedContentSchema,
905
+ excerpt: RenderedContentSchema,
906
+ author: z.number(),
907
+ featured_media: z.number(),
908
+ comment_status: z.string(),
909
+ ping_status: z.string(),
910
+ sticky: z.boolean(),
911
+ template: z.string(),
912
+ format: z.string(),
913
+ meta: z.any().optional(),
914
+ categories: z.array(z.number()),
915
+ tags: z.array(z.number()).optional(),
916
+ _links: HalLinksSchema.optional()
917
+ });
918
+ var PostsListSchema = z.array(PostSchema);
919
+ paginationParamsSchema.extend({
920
+ context: z.enum(["view", "embed", "edit"]).optional(),
921
+ search: z.string().optional(),
922
+ after: z.string().optional(),
923
+ before: z.string().optional(),
924
+ author: z.union([z.number(), z.array(z.number())]).optional(),
925
+ author_exclude: z.array(z.number()).optional(),
926
+ exclude: z.array(z.number()).optional(),
927
+ include: z.array(z.number()).optional(),
928
+ categories: z.union([z.number(), z.array(z.number())]).optional(),
929
+ categories_exclude: z.array(z.number()).optional(),
930
+ tags: z.union([z.number(), z.array(z.number())]).optional(),
931
+ tags_exclude: z.array(z.number()).optional(),
932
+ sticky: z.boolean().optional()
933
+ });
934
+ var EmbeddedPostSchema = z.object({
935
+ id: z.number(),
936
+ title: z.string(),
937
+ content: z.string(),
938
+ featured_image: z.string().optional(),
939
+ published_date: z.string(),
940
+ categories: z.array(
941
+ z.object({
942
+ id: z.number(),
943
+ name: z.string(),
944
+ slug: z.string(),
945
+ taxonomy: z.string().optional(),
946
+ description: z.string().optional(),
947
+ parent: z.number().optional(),
948
+ count: z.number().optional()
949
+ })
950
+ ).optional()
951
+ });
952
+ z.array(EmbeddedPostSchema);
953
+ var AvatarUrlsSchema = z.object({
954
+ "24": z.string().optional(),
955
+ "48": z.string().optional(),
956
+ "96": z.string().optional()
957
+ });
958
+ var UserSchema = z.object({
959
+ id: z.number(),
960
+ username: z.string().optional(),
961
+ // Only in edit context
962
+ name: z.string(),
963
+ first_name: z.string().optional(),
964
+ last_name: z.string().optional(),
965
+ email: z.string().optional(),
966
+ // Only for current user or admin
967
+ url: z.string(),
968
+ description: z.string(),
969
+ link: z.string(),
970
+ locale: z.string().optional(),
971
+ // Only for current user
972
+ nickname: z.string().optional(),
973
+ // Only in edit context
974
+ slug: z.string(),
975
+ registered_date: z.string().optional(),
976
+ // Only in edit context
977
+ roles: z.array(z.string()).optional(),
978
+ // Only in edit context
979
+ capabilities: z.record(z.boolean()).optional(),
980
+ // Only in edit context
981
+ extra_capabilities: z.record(z.boolean()).optional(),
982
+ // Only in edit context
983
+ avatar_urls: AvatarUrlsSchema.optional(),
984
+ meta: z.any().optional(),
985
+ _links: HalLinksSchema.optional()
986
+ });
987
+ z.array(UserSchema);
988
+ var CurrentUserSchema = UserSchema.extend({
989
+ username: z.string(),
990
+ email: z.string(),
991
+ locale: z.string(),
992
+ nickname: z.string(),
993
+ registered_date: z.string(),
994
+ roles: z.array(z.string())
995
+ });
996
+
997
+ // src/api/auth.ts
998
+ var createAuthAPI = (axios2, options) => ({
999
+ /**
1000
+ * Login with username/password and get JWT token
1001
+ */
1002
+ async login(input) {
1003
+ const response = await axios2.post(ENDPOINTS.JWT_TOKEN, input);
1004
+ return handleApiResponse(response, JwtTokenResponseSchema, options);
1005
+ },
1006
+ /**
1007
+ * Validate current JWT token
1008
+ *
1009
+ * Requires Authorization header to be set
1010
+ */
1011
+ async validateToken() {
1012
+ try {
1013
+ const response = await axios2.post(ENDPOINTS.JWT_VALIDATE);
1014
+ return response.data?.code === "jwt_auth_valid_token";
1015
+ } catch {
1016
+ return false;
1017
+ }
1018
+ }
1019
+ });
1020
+
1021
+ // src/api/categories.ts
1022
+ var createCategoriesAPI = (axios2, options) => ({
1023
+ /**
1024
+ * Get list of categories with pagination
1025
+ */
1026
+ async list(params) {
1027
+ const response = await axios2.get(ENDPOINTS.CATEGORIES, { params });
1028
+ return handlePaginatedApiResponse(response, CategoriesListSchema, params, options);
1029
+ },
1030
+ /**
1031
+ * Get single category by ID
1032
+ */
1033
+ async get(id) {
1034
+ const response = await axios2.get(ENDPOINTS.CATEGORY(id));
1035
+ return handleApiResponse(response, CategorySchema, options);
1036
+ }
1037
+ });
1038
+
1039
+ // src/api/media.ts
1040
+ var createMediaAPI = (axios2, options) => ({
1041
+ /**
1042
+ * Get single media item by ID
1043
+ */
1044
+ async get(id) {
1045
+ const response = await axios2.get(ENDPOINTS.MEDIA_ITEM(id));
1046
+ return handleApiResponse(response, MediaSchema, options);
1047
+ }
1048
+ });
1049
+
1050
+ // src/api/posts.ts
1051
+ var createPostsAPI = (axios2, options) => ({
1052
+ /**
1053
+ * Get list of posts with pagination
1054
+ */
1055
+ async list(params) {
1056
+ const response = await axios2.get(ENDPOINTS.POSTS, { params });
1057
+ return handlePaginatedApiResponse(response, PostsListSchema, params, options);
1058
+ },
1059
+ /**
1060
+ * Get single post by ID
1061
+ */
1062
+ async get(id) {
1063
+ const response = await axios2.get(ENDPOINTS.POST(id));
1064
+ return handleApiResponse(response, PostSchema, options);
1065
+ },
1066
+ /**
1067
+ * Get single post by slug
1068
+ */
1069
+ async getBySlug(slug) {
1070
+ const response = await axios2.get(ENDPOINTS.POSTS, {
1071
+ params: { slug, per_page: 1 }
1072
+ });
1073
+ const posts = handleApiResponse(response, PostsListSchema, options);
1074
+ return posts[0] || null;
1075
+ }
1076
+ });
1077
+
1078
+ // src/api/users.ts
1079
+ var createUsersAPI = (axios2, options) => ({
1080
+ /**
1081
+ * Get current authenticated user
1082
+ */
1083
+ async me() {
1084
+ const response = await axios2.get(ENDPOINTS.USERS_ME);
1085
+ return handleApiResponse(response, CurrentUserSchema, options);
1086
+ },
1087
+ /**
1088
+ * Get user by ID
1089
+ */
1090
+ async get(id) {
1091
+ const response = await axios2.get(ENDPOINTS.USER(id));
1092
+ return handleApiResponse(response, UserSchema, options);
1093
+ }
1094
+ });
1095
+
1096
+ // src/client/createClient.ts
1097
+ var DEFAULT_ERROR_REPORTER = {
1098
+ report: (error2) => {
1099
+ console.error("[wordpress-utils]", error2);
1100
+ }
1101
+ };
1102
+ var createClient = (config) => {
1103
+ const fullConfig = {
1104
+ ...config,
1105
+ errorReporter: config.errorReporter ?? DEFAULT_ERROR_REPORTER
1106
+ };
1107
+ const axiosInstance = axios.create({
1108
+ baseURL: fullConfig.baseURL,
1109
+ timeout: fullConfig.timeout || 3e4,
1110
+ headers: {
1111
+ "Content-Type": "application/json",
1112
+ ...fullConfig.headers
1113
+ }
1114
+ });
1115
+ const responseOptions = {
1116
+ errorReporter: fullConfig.errorReporter?.report,
1117
+ onValidationError: fullConfig.onValidationError,
1118
+ validationMode: fullConfig.validationMode
1119
+ };
1120
+ const client = {
1121
+ config: fullConfig,
1122
+ axios: axiosInstance,
1123
+ posts: createPostsAPI(axiosInstance, responseOptions),
1124
+ categories: createCategoriesAPI(axiosInstance, responseOptions),
1125
+ media: createMediaAPI(axiosInstance, responseOptions),
1126
+ users: createUsersAPI(axiosInstance, responseOptions),
1127
+ auth: createAuthAPI(axiosInstance, responseOptions)
1128
+ };
1129
+ setupRequestInterceptor(axiosInstance, fullConfig, client);
1130
+ setupResponseInterceptor(axiosInstance, fullConfig, client);
1131
+ setupErrorInterceptor(axiosInstance, fullConfig, client);
1132
+ return client;
1133
+ };
1134
+
1135
+ export { createClient };
1136
+ //# sourceMappingURL=index.js.map
1137
+ //# sourceMappingURL=index.js.map