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