kizlo 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 (159) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +74 -0
  3. package/dist/adapters/auth.d.ts +18 -0
  4. package/dist/adapters/auth.js +15 -0
  5. package/dist/adapters/captcha.d.ts +28 -0
  6. package/dist/adapters/captcha.js +86 -0
  7. package/dist/adapters/geo.d.ts +18 -0
  8. package/dist/adapters/geo.js +18 -0
  9. package/dist/adapters/geo.vercel.d.ts +6 -0
  10. package/dist/adapters/geo.vercel.js +41 -0
  11. package/dist/adapters/logger.d.ts +31 -0
  12. package/dist/adapters/logger.js +30 -0
  13. package/dist/cli/banner.js +26 -0
  14. package/dist/cli/commands/generate.js +34 -0
  15. package/dist/cli/commands/init.js +313 -0
  16. package/dist/cli/commands/test.js +68 -0
  17. package/dist/cli/commands/watch.js +53 -0
  18. package/dist/cli/commands/wp.js +66 -0
  19. package/dist/cli/daemon/config.js +87 -0
  20. package/dist/cli/daemon/generate.js +40 -0
  21. package/dist/cli/daemon/lock.js +31 -0
  22. package/dist/cli/daemon/logger.js +8 -0
  23. package/dist/cli/daemon/watch.js +37 -0
  24. package/dist/cli/index.d.ts +1 -0
  25. package/dist/cli/index.js +22 -0
  26. package/dist/cli/presets/base.js +23 -0
  27. package/dist/cli/presets/index.js +16 -0
  28. package/dist/cli/presets/nextjs.js +39 -0
  29. package/dist/cli/utils.js +182 -0
  30. package/dist/cli/wp/bootstrap.js +114 -0
  31. package/dist/cli/wp/compose/docker-compose.yml +73 -0
  32. package/dist/cli/wp/constants.js +52 -0
  33. package/dist/cli/wp/docker.d.ts +24 -0
  34. package/dist/cli/wp/docker.js +69 -0
  35. package/dist/cli/wp/setup.js +39 -0
  36. package/dist/cli/wp/types.d.ts +72 -0
  37. package/dist/cli/wp/types.js +8 -0
  38. package/dist/cli/wp/utils.d.ts +39 -0
  39. package/dist/cli/wp/utils.js +105 -0
  40. package/dist/client.d.ts +24 -0
  41. package/dist/client.js +55 -0
  42. package/dist/comment/errors.js +47 -0
  43. package/dist/comment/index.js +138 -0
  44. package/dist/comment/schema.js +72 -0
  45. package/dist/comment/utils.js +35 -0
  46. package/dist/config.d.ts +39 -0
  47. package/dist/config.js +7 -0
  48. package/dist/context.d.ts +73 -0
  49. package/dist/context.js +145 -0
  50. package/dist/cookie.d.ts +19 -0
  51. package/dist/cookie.js +25 -0
  52. package/dist/email/service.d.ts +11 -0
  53. package/dist/email/service.interface.d.ts +8 -0
  54. package/dist/email/service.js +20 -0
  55. package/dist/index.d.ts +39 -0
  56. package/dist/index.js +31 -0
  57. package/dist/integrations/nextjs/kizlo.client.d.ts +10 -0
  58. package/dist/integrations/nextjs/kizlo.client.js +13 -0
  59. package/dist/integrations/nextjs/kizlo.server.d.ts +12 -0
  60. package/dist/integrations/nextjs/kizlo.server.js +13 -0
  61. package/dist/integrations/nextjs/utils.js +11 -0
  62. package/dist/kizlo.d.ts +73 -0
  63. package/dist/kizlo.js +144 -0
  64. package/dist/menu/errors.js +16 -0
  65. package/dist/menu/fixture.d.ts +8 -0
  66. package/dist/menu/fixture.js +62 -0
  67. package/dist/menu/index.js +70 -0
  68. package/dist/menu/schema.js +70 -0
  69. package/dist/menu/utils.js +57 -0
  70. package/dist/nextjs/server.d.ts +2 -0
  71. package/dist/nextjs/server.js +3 -0
  72. package/dist/nextjs.d.ts +2 -0
  73. package/dist/nextjs.js +3 -0
  74. package/dist/package.js +5 -0
  75. package/dist/post/error.js +30 -0
  76. package/dist/post/fixture.d.ts +8 -0
  77. package/dist/post/fixture.js +71 -0
  78. package/dist/post/index.js +123 -0
  79. package/dist/post/schema.js +120 -0
  80. package/dist/post/utils.js +42 -0
  81. package/dist/post-type/service.js +48 -0
  82. package/dist/router.d.ts +624 -0
  83. package/dist/router.js +15 -0
  84. package/dist/seo/index.js +54 -0
  85. package/dist/seo/schema.d.ts +196 -0
  86. package/dist/seo/schema.js +96 -0
  87. package/dist/seo/types.d.ts +80 -0
  88. package/dist/seo/utils.js +44 -0
  89. package/dist/service.d.ts +17 -0
  90. package/dist/service.js +17 -0
  91. package/dist/shared/constants.js +10 -0
  92. package/dist/shared/contract.d.ts +21 -0
  93. package/dist/shared/contract.js +43 -0
  94. package/dist/shared/crypto.d.ts +28 -0
  95. package/dist/shared/crypto.js +34 -0
  96. package/dist/shared/error.d.ts +128 -0
  97. package/dist/shared/error.js +147 -0
  98. package/dist/shared/extension.d.ts +25 -0
  99. package/dist/shared/extension.js +7 -0
  100. package/dist/shared/identifier.d.ts +11 -0
  101. package/dist/shared/identifier.js +26 -0
  102. package/dist/shared/middleware.d.ts +29 -0
  103. package/dist/shared/procedure.d.ts +114 -0
  104. package/dist/shared/procedure.js +114 -0
  105. package/dist/shared/result.d.ts +24 -0
  106. package/dist/shared/result.js +49 -0
  107. package/dist/shared/router.d.ts +7 -0
  108. package/dist/shared/router.js +26 -0
  109. package/dist/shared/schema.d.ts +55 -0
  110. package/dist/shared/schema.js +43 -0
  111. package/dist/shared/serialize.d.ts +12 -0
  112. package/dist/shared/serialize.js +51 -0
  113. package/dist/shared/types.d.ts +35 -0
  114. package/dist/shared/utils.js +17 -0
  115. package/dist/test/auth.d.ts +7 -0
  116. package/dist/test/auth.js +15 -0
  117. package/dist/test/captcha.d.ts +7 -0
  118. package/dist/test/captcha.js +8 -0
  119. package/dist/test/client.d.ts +11 -0
  120. package/dist/test/client.js +14 -0
  121. package/dist/test/fixtures.d.ts +14 -0
  122. package/dist/test/fixtures.js +13 -0
  123. package/dist/test/server.d.ts +10 -0
  124. package/dist/test/server.js +32 -0
  125. package/dist/test/users.d.ts +14 -0
  126. package/dist/test/users.js +17 -0
  127. package/dist/test.d.ts +12 -0
  128. package/dist/test.js +13 -0
  129. package/dist/webhook/index.d.ts +38 -0
  130. package/dist/webhook/index.js +44 -0
  131. package/dist/webhook/schema.d.ts +45 -0
  132. package/dist/webhook/schema.js +72 -0
  133. package/dist/webhook/utils.js +21 -0
  134. package/dist/wordpress/comment/service.d.ts +28 -0
  135. package/dist/wordpress/comment/service.js +55 -0
  136. package/dist/wordpress/comment/types.d.ts +206 -0
  137. package/dist/wordpress/constants.d.ts +14 -0
  138. package/dist/wordpress/constants.js +13 -0
  139. package/dist/wordpress/error.d.ts +9 -0
  140. package/dist/wordpress/error.js +12 -0
  141. package/dist/wordpress/index.d.ts +15 -0
  142. package/dist/wordpress/menu/items/service.d.ts +28 -0
  143. package/dist/wordpress/menu/items/service.js +55 -0
  144. package/dist/wordpress/menu/items/types.d.ts +214 -0
  145. package/dist/wordpress/menu/service.d.ts +30 -0
  146. package/dist/wordpress/menu/service.js +58 -0
  147. package/dist/wordpress/menu/types.d.ts +115 -0
  148. package/dist/wordpress/post/service.d.ts +28 -0
  149. package/dist/wordpress/post/service.js +55 -0
  150. package/dist/wordpress/post/types.d.ts +290 -0
  151. package/dist/wordpress/service.d.ts +77 -0
  152. package/dist/wordpress/service.js +180 -0
  153. package/dist/wordpress/types.d.ts +67 -0
  154. package/dist/wordpress/user/service.d.ts +28 -0
  155. package/dist/wordpress/user/service.js +55 -0
  156. package/dist/wordpress/user/types.d.ts +221 -0
  157. package/dist/wordpress/utils.d.ts +7 -0
  158. package/dist/wordpress/utils.js +9 -0
  159. package/package.json +109 -0
@@ -0,0 +1,290 @@
1
+ import { WP_Context, WP_Link, WP_ListOrder } from "../types.js";
2
+
3
+ //#region src/wordpress/post/types.d.ts
4
+ /** A named status for the post. Used in responses and as the input on create/update. */
5
+ type WP_PostStatus = "publish" | "future" | "draft" | "pending" | "private" | "trash";
6
+ /** Status filter values accepted by `GET /wp/v2/posts`. Widens `WP_PostStatus` with WP-internal statuses and the `any` alias. */
7
+ type WP_PostStatusFilter = WP_PostStatus | "auto-draft" | "inherit" | "any";
8
+ /** Whether or not comments / pings are open on the post. */
9
+ type WP_PostCommentStatus = "open" | "closed";
10
+ /** The format for the post. */
11
+ type WP_PostFormat = "standard" | "aside" | "chat" | "gallery" | "link" | "image" | "quote" | "status" | "video" | "audio";
12
+ /** Sort collection by post attribute. */
13
+ type WP_PostOrderBy = "author" | "date" | "id" | "include" | "modified" | "parent" | "relevance" | "slug" | "include_slugs" | "title";
14
+ /** Limit result set based on relationship between multiple taxonomies. */
15
+ type WP_PostTaxRelation = "AND" | "OR";
16
+ /** The globally unique identifier for the post. */
17
+ interface WP_PostGuid {
18
+ /** GUID for the post, transformed for display. */
19
+ rendered: string;
20
+ /** GUID for the post, as it exists in the database. */
21
+ raw: string;
22
+ }
23
+ /** The title for the post. */
24
+ interface WP_PostTitle {
25
+ /** HTML title for the post, transformed for display. */
26
+ rendered: string;
27
+ /** Title for the post, as it exists in the database. */
28
+ raw: string;
29
+ }
30
+ /** The content for the post. */
31
+ interface WP_PostContent {
32
+ /** HTML content for the post, transformed for display. */
33
+ rendered: string;
34
+ /** Content for the post, as it exists in the database. */
35
+ raw: string;
36
+ /** Version of the block format used in the post. */
37
+ block_version: number;
38
+ /** Whether the content is protected with a password. */
39
+ protected: boolean;
40
+ }
41
+ /** The excerpt for the post. */
42
+ interface WP_PostExcerpt {
43
+ /** HTML excerpt for the post, transformed for display. */
44
+ rendered: string;
45
+ /** Excerpt for the post, as it exists in the database. */
46
+ raw: string;
47
+ /** Whether the excerpt is protected with a password. */
48
+ protected: boolean;
49
+ }
50
+ /** Hypermedia links for the post resource. */
51
+ interface WP_PostLinks {
52
+ self?: WP_Link[];
53
+ collection?: WP_Link[];
54
+ about?: WP_Link[];
55
+ author?: WP_Link[];
56
+ replies?: WP_Link[];
57
+ "version-history"?: WP_Link[];
58
+ "predecessor-version"?: WP_Link[];
59
+ "wp:featuredmedia"?: WP_Link[];
60
+ "wp:attachment"?: WP_Link[];
61
+ "wp:term"?: WP_Link[];
62
+ curies?: WP_Link[];
63
+ [rel: string]: WP_Link[] | undefined;
64
+ }
65
+ interface WP_Post {
66
+ /** Unique identifier for the post. */
67
+ id: number;
68
+ /** The date the post was published, in the site's timezone. */
69
+ date: string | null;
70
+ /** The date the post was published, as GMT. */
71
+ date_gmt: string | null;
72
+ /** The globally unique identifier for the post. */
73
+ guid: WP_PostGuid;
74
+ /** The date the post was last modified, in the site's timezone. */
75
+ modified: string;
76
+ /** The date the post was last modified, as GMT. */
77
+ modified_gmt: string;
78
+ /** An alphanumeric identifier for the post unique to its type. */
79
+ slug: string;
80
+ /** A named status for the post. */
81
+ status: WP_PostStatus;
82
+ /** Type of post. */
83
+ type: string;
84
+ /** A password to protect access to the content and excerpt. */
85
+ password: string;
86
+ /** Permalink template for the post. */
87
+ permalink_template: string;
88
+ /** Slug automatically generated from the post title. */
89
+ generated_slug: string;
90
+ /** URL to the post. */
91
+ link: string;
92
+ /** The title for the post. */
93
+ title: WP_PostTitle;
94
+ /** The content for the post. */
95
+ content: WP_PostContent;
96
+ /** The ID for the author of the post. */
97
+ author: number;
98
+ /** The excerpt for the post. */
99
+ excerpt: WP_PostExcerpt;
100
+ /** The ID of the featured media for the post. */
101
+ featured_media: number;
102
+ /** Whether or not comments are open on the post. */
103
+ comment_status: WP_PostCommentStatus;
104
+ /** Whether or not the post can be pinged. */
105
+ ping_status: WP_PostCommentStatus;
106
+ /** The format for the post. */
107
+ format: WP_PostFormat;
108
+ /** Meta fields. */
109
+ meta: Record<string, unknown>;
110
+ /** Whether or not the post should be treated as sticky. */
111
+ sticky: boolean;
112
+ /** The theme file to use to display the post. */
113
+ template: string;
114
+ /** The terms assigned to the post in the category taxonomy. */
115
+ categories: number[];
116
+ /** The terms assigned to the post in the post_tag taxonomy. */
117
+ tags: number[];
118
+ /** Hypermedia links. */
119
+ _links?: WP_PostLinks;
120
+ }
121
+ interface WP_PostListInput {
122
+ /** Scope under which the request is made; determines fields present in response. */
123
+ context?: WP_Context;
124
+ /** Current page of the collection. */
125
+ page?: number;
126
+ /** Maximum number of items to be returned in result set. */
127
+ per_page?: number;
128
+ /** Limit results to those matching a string. */
129
+ search?: string;
130
+ /** Limit response to posts published after a given ISO8601 compliant date. */
131
+ after?: string;
132
+ /** Limit response to posts modified after a given ISO8601 compliant date. */
133
+ modified_after?: string;
134
+ /** Limit result set to posts assigned to specific authors. */
135
+ author?: number | number[];
136
+ /** Ensure result set excludes posts assigned to specific authors. */
137
+ author_exclude?: number | number[];
138
+ /** Limit response to posts published before a given ISO8601 compliant date. */
139
+ before?: string;
140
+ /** Limit response to posts modified before a given ISO8601 compliant date. */
141
+ modified_before?: string;
142
+ /** Ensure result set excludes specific IDs. */
143
+ exclude?: number | number[];
144
+ /** Limit result set to specific IDs. */
145
+ include?: number | number[];
146
+ /** Offset the result set by a specific number of items. */
147
+ offset?: number;
148
+ /** Order sort attribute ascending or descending. */
149
+ order?: WP_ListOrder;
150
+ /** Sort collection by post attribute. */
151
+ orderby?: WP_PostOrderBy;
152
+ /** Array of column names to be searched. */
153
+ search_columns?: string[];
154
+ /** Limit result set to posts with one or more specific slugs. */
155
+ slug?: string | string[];
156
+ /** Limit result set to posts assigned one or more statuses. */
157
+ status?: WP_PostStatusFilter | WP_PostStatusFilter[];
158
+ /** Limit result set based on relationship between multiple taxonomies. */
159
+ tax_relation?: WP_PostTaxRelation;
160
+ /** Limit result set to items with specific terms assigned in the categories taxonomy. */
161
+ categories?: number | number[];
162
+ /** Limit result set to items except those with specific terms assigned in the categories taxonomy. */
163
+ categories_exclude?: number | number[];
164
+ /** Limit result set to items with specific terms assigned in the tags taxonomy. */
165
+ tags?: number | number[];
166
+ /** Limit result set to items except those with specific terms assigned in the tags taxonomy. */
167
+ tags_exclude?: number | number[];
168
+ /** Limit result set to items that are sticky. */
169
+ sticky?: boolean;
170
+ }
171
+ type WP_PostListErrorCode = "rest_no_search_term_defined" | "rest_orderby_include_missing_include" | "rest_post_invalid_page_number" | "rest_forbidden_context" | "rest_forbidden_status";
172
+ /** Title payload accepted when creating or updating a post. */
173
+ type WP_PostTitleInput = string | {
174
+ raw: string;
175
+ };
176
+ /** Content payload accepted when creating or updating a post. */
177
+ type WP_PostContentInput = string | {
178
+ raw: string;
179
+ };
180
+ /** Excerpt payload accepted when creating or updating a post. */
181
+ type WP_PostExcerptInput = string | {
182
+ raw: string;
183
+ };
184
+ interface WP_PostCreateInput {
185
+ /** The date the post was published, in the site's timezone. */
186
+ date?: string | null;
187
+ /** The date the post was published, as GMT. */
188
+ date_gmt?: string | null;
189
+ /** An alphanumeric identifier for the post unique to its type. */
190
+ slug?: string;
191
+ /** A named status for the post. */
192
+ status?: WP_PostStatus;
193
+ /** A password to protect access to the content and excerpt. */
194
+ password?: string;
195
+ /** The title for the post. */
196
+ title?: WP_PostTitleInput;
197
+ /** The content for the post. */
198
+ content?: WP_PostContentInput;
199
+ /** The ID for the author of the post. */
200
+ author?: number;
201
+ /** The excerpt for the post. */
202
+ excerpt?: WP_PostExcerptInput;
203
+ /** The ID of the featured media for the post. */
204
+ featured_media?: number;
205
+ /** Whether or not comments are open on the post. */
206
+ comment_status?: WP_PostCommentStatus;
207
+ /** Whether or not the post can be pinged. */
208
+ ping_status?: WP_PostCommentStatus;
209
+ /** The format for the post. */
210
+ format?: WP_PostFormat;
211
+ /** Meta fields. */
212
+ meta?: Record<string, unknown>;
213
+ /** Whether or not the post should be treated as sticky. */
214
+ sticky?: boolean;
215
+ /** The theme file to use to display the post. */
216
+ template?: string;
217
+ /** The terms assigned to the post in the category taxonomy. */
218
+ categories?: number[];
219
+ /** The terms assigned to the post in the post_tag taxonomy. */
220
+ tags?: number[];
221
+ }
222
+ type WP_PostCreateErrorCode = "rest_invalid_author" | "rest_invalid_featured_media" | "rest_invalid_field" | "rest_post_exists" | "rest_post_invalid_id" | "rest_cannot_assign_sticky" | "rest_cannot_assign_term" | "rest_cannot_create" | "rest_cannot_edit_others" | "rest_cannot_publish";
223
+ interface WP_PostRetrieveInput {
224
+ /** Unique identifier for the post. */
225
+ id: number;
226
+ /** Scope under which the request is made; determines fields present in response. */
227
+ context?: WP_Context;
228
+ /** The password for the post if it is password protected. */
229
+ password?: string;
230
+ }
231
+ type WP_PostRetrieveErrorCode = "rest_forbidden_context" | "rest_post_incorrect_password" | "rest_post_invalid_id";
232
+ interface WP_PostUpdateInput {
233
+ /** Unique identifier for the post. */
234
+ id: number;
235
+ /** The date the post was published, in the site's timezone. */
236
+ date?: string | null;
237
+ /** The date the post was published, as GMT. */
238
+ date_gmt?: string | null;
239
+ /** An alphanumeric identifier for the post unique to its type. */
240
+ slug?: string;
241
+ /** A named status for the post. */
242
+ status?: WP_PostStatus;
243
+ /** A password to protect access to the content and excerpt. */
244
+ password?: string;
245
+ /** The title for the post. */
246
+ title?: WP_PostTitleInput;
247
+ /** The content for the post. */
248
+ content?: WP_PostContentInput;
249
+ /** The ID for the author of the post. */
250
+ author?: number;
251
+ /** The excerpt for the post. */
252
+ excerpt?: WP_PostExcerptInput;
253
+ /** The ID of the featured media for the post. */
254
+ featured_media?: number;
255
+ /** Whether or not comments are open on the post. */
256
+ comment_status?: WP_PostCommentStatus;
257
+ /** Whether or not the post can be pinged. */
258
+ ping_status?: WP_PostCommentStatus;
259
+ /** The format for the post. */
260
+ format?: WP_PostFormat;
261
+ /** Meta fields. */
262
+ meta?: Record<string, unknown>;
263
+ /** Whether or not the post should be treated as sticky. */
264
+ sticky?: boolean;
265
+ /** The theme file to use to display the post. */
266
+ template?: string;
267
+ /** The terms assigned to the post in the category taxonomy. */
268
+ categories?: number[];
269
+ /** The terms assigned to the post in the post_tag taxonomy. */
270
+ tags?: number[];
271
+ }
272
+ type WP_PostUpdateErrorCode = "rest_invalid_author" | "rest_invalid_featured_media" | "rest_invalid_field" | "rest_post_invalid_id" | "rest_cannot_assign_sticky" | "rest_cannot_assign_term" | "rest_cannot_edit" | "rest_cannot_edit_others" | "rest_cannot_publish";
273
+ interface WP_PostDeleteInput {
274
+ /** Unique identifier for the post. */
275
+ id: number;
276
+ /** Whether to bypass Trash and force deletion. */
277
+ force?: boolean;
278
+ }
279
+ /**
280
+ * Response from `DELETE /wp/v2/posts/<id>`.
281
+ * - When `force=true`: returns `{ deleted: true, previous: WP_Post }`.
282
+ * - When `force=false` (trashed): returns the updated `WP_Post` with `status: "trash"`.
283
+ */
284
+ type WP_PostDeleteResponse = WP_Post | {
285
+ deleted: true;
286
+ previous: WP_Post;
287
+ };
288
+ type WP_PostDeleteErrorCode = "rest_cannot_delete" | "rest_user_cannot_delete_post" | "rest_post_invalid_id" | "rest_already_trashed" | "rest_trash_not_supported";
289
+ //#endregion
290
+ export { WP_Post, WP_PostCommentStatus, WP_PostContent, WP_PostContentInput, WP_PostCreateErrorCode, WP_PostCreateInput, WP_PostDeleteErrorCode, WP_PostDeleteInput, WP_PostDeleteResponse, WP_PostExcerpt, WP_PostExcerptInput, WP_PostFormat, WP_PostGuid, WP_PostLinks, WP_PostListErrorCode, WP_PostListInput, WP_PostOrderBy, WP_PostRetrieveErrorCode, WP_PostRetrieveInput, WP_PostStatus, WP_PostStatusFilter, WP_PostTaxRelation, WP_PostTitle, WP_PostTitleInput, WP_PostUpdateErrorCode, WP_PostUpdateInput };
@@ -0,0 +1,77 @@
1
+ import { WP_List, WP_ListMetadata, WP_RequestInput, WP_Result, WordPressCredentials } from "./types.js";
2
+ import { MenuService } from "./menu/service.js";
3
+ import { PostService } from "./post/service.js";
4
+ import { UserService } from "./user/service.js";
5
+ import { CommentService } from "./comment/service.js";
6
+ import { Duration, SearchParamsLike } from "@kizlo/shared";
7
+
8
+ //#region src/wordpress/service.d.ts
9
+ declare class WordPressService {
10
+ readonly users: UserService;
11
+ readonly comments: CommentService;
12
+ readonly posts: PostService;
13
+ readonly menus: MenuService;
14
+ private readonly siteBase;
15
+ private readonly authHeader;
16
+ private readonly defaultTimeout;
17
+ constructor(context: {
18
+ credentials: WordPressCredentials;
19
+ timeout?: Duration;
20
+ });
21
+ post<TData, TCode extends string = never>(path: string, input: {
22
+ body?: unknown;
23
+ base?: string;
24
+ headers?: Record<string, string>;
25
+ searchParams?: SearchParamsLike;
26
+ signal?: AbortSignal;
27
+ timeout?: Duration;
28
+ }): Promise<WP_Result<TData, TCode>>;
29
+ get<TData, TCode extends string = never>(path: string, input: {
30
+ base?: string;
31
+ headers?: Record<string, string>;
32
+ searchParams?: SearchParamsLike;
33
+ signal?: AbortSignal;
34
+ timeout?: Duration;
35
+ }): Promise<WP_Result<TData, TCode>>;
36
+ put<TData, TCode extends string = never>(path: string, input: {
37
+ body: unknown;
38
+ base?: string;
39
+ headers?: Record<string, string>;
40
+ searchParams?: SearchParamsLike;
41
+ signal?: AbortSignal;
42
+ timeout?: Duration;
43
+ }): Promise<WP_Result<TData, TCode>>;
44
+ patch<TData, TCode extends string = never>(path: string, input: {
45
+ body: unknown;
46
+ base?: string;
47
+ headers?: Record<string, string>;
48
+ searchParams?: SearchParamsLike;
49
+ signal?: AbortSignal;
50
+ timeout?: Duration;
51
+ }): Promise<WP_Result<TData, TCode>>;
52
+ delete<TData, TCode extends string = never>(path: string, input: {
53
+ body?: unknown;
54
+ base?: string;
55
+ headers?: Record<string, string>;
56
+ searchParams?: SearchParamsLike;
57
+ signal?: AbortSignal;
58
+ timeout?: Duration;
59
+ }): Promise<WP_Result<TData, TCode>>;
60
+ resolveList<T>(response: {
61
+ data: T[];
62
+ headers: Headers;
63
+ searchParams?: SearchParamsLike;
64
+ }): WP_List<T>;
65
+ resolveListMetadata(params: {
66
+ page: number;
67
+ totalPages: number;
68
+ totalItems: number;
69
+ }): WP_ListMetadata;
70
+ request<TData, TCode extends string = never>(input: WP_RequestInput): Promise<WP_Result<TData, TCode>>;
71
+ private fetch;
72
+ private resolveSignal;
73
+ resolveUrl(requestConfig: WP_RequestInput): URL;
74
+ private snippet;
75
+ }
76
+ //#endregion
77
+ export { WordPressService };
@@ -0,0 +1,180 @@
1
+ import { SAFE_REQUEST_TIMEOUT, UNEXPECTED_BODY_SNIPPET_LENGTH, WP_AUTH_HEADER_KEY, WP_AUTH_TYPE } from "./constants.js";
2
+ import { CommentService } from "./comment/service.js";
3
+ import { WP_Error } from "./error.js";
4
+ import { MenuService } from "./menu/service.js";
5
+ import { PostService } from "./post/service.js";
6
+ import { UserService } from "./user/service.js";
7
+ import { isWordPressResourceError } from "./utils.js";
8
+ import { milliseconds, stringifyQueryString, trimLeadingTrailingSlashes, tryCatch, tryCatchSync } from "@kizlo/shared";
9
+
10
+ //#region src/wordpress/service.ts
11
+ var WordPressService = class {
12
+ users = new UserService(this);
13
+ comments = new CommentService(this);
14
+ posts = new PostService(this);
15
+ menus = new MenuService(this);
16
+ siteBase;
17
+ authHeader;
18
+ defaultTimeout;
19
+ constructor(context) {
20
+ this.authHeader = `${WP_AUTH_TYPE} ${Buffer.from(`${context.credentials.username}:${context.credentials.password}`, "utf8").toString("base64")}`;
21
+ this.siteBase = context.credentials.url.endsWith("/") ? context.credentials.url : `${context.credentials.url}/`;
22
+ this.defaultTimeout = context.timeout ?? SAFE_REQUEST_TIMEOUT;
23
+ }
24
+ async post(path, input) {
25
+ return this.request({
26
+ ...input,
27
+ path,
28
+ method: "POST"
29
+ });
30
+ }
31
+ async get(path, input) {
32
+ return this.request({
33
+ ...input,
34
+ path,
35
+ method: "GET"
36
+ });
37
+ }
38
+ async put(path, input) {
39
+ return this.request({
40
+ ...input,
41
+ path,
42
+ method: "PUT"
43
+ });
44
+ }
45
+ async patch(path, input) {
46
+ return this.request({
47
+ ...input,
48
+ path,
49
+ method: "PATCH"
50
+ });
51
+ }
52
+ async delete(path, input) {
53
+ return this.request({
54
+ ...input,
55
+ path,
56
+ method: "DELETE"
57
+ });
58
+ }
59
+ resolveList(response) {
60
+ const totalItems = Number(response.headers.get("x-wp-total") ?? "0");
61
+ const totalPages = Number(response.headers.get("x-wp-totalpages") ?? "0");
62
+ const page = +(response.searchParams?.page ?? "1");
63
+ const meta = this.resolveListMetadata({
64
+ page,
65
+ totalItems,
66
+ totalPages
67
+ });
68
+ return {
69
+ items: response.data,
70
+ meta
71
+ };
72
+ }
73
+ resolveListMetadata(params) {
74
+ const page = Math.max(params.page, 1);
75
+ const next_page = params.totalPages > page ? page + 1 : null;
76
+ return {
77
+ page,
78
+ next_page,
79
+ prev_page: page > 1 ? page - 1 : null,
80
+ has_next_page: !!next_page,
81
+ has_prev_page: page > 1,
82
+ total_pages: params.totalPages,
83
+ total_items: params.totalItems
84
+ };
85
+ }
86
+ async request(input) {
87
+ const [transportErr, response] = await this.fetch(input);
88
+ if (transportErr) return {
89
+ data: null,
90
+ status: 0,
91
+ headers: new Headers(),
92
+ error: new WP_Error({
93
+ code: "unknown_error",
94
+ message: transportErr.message
95
+ })
96
+ };
97
+ const [readErr, text] = await tryCatch(response.text());
98
+ if (readErr) return {
99
+ data: null,
100
+ status: response.status,
101
+ headers: response.headers,
102
+ error: new WP_Error({
103
+ code: "unknown_error",
104
+ message: readErr.message
105
+ })
106
+ };
107
+ const [parseErr, data] = tryCatchSync(() => JSON.parse(text));
108
+ if (!response.ok) {
109
+ if (isWordPressResourceError(data)) return {
110
+ data: null,
111
+ status: response.status,
112
+ headers: response.headers,
113
+ error: new WP_Error({
114
+ code: data.code,
115
+ message: data.message
116
+ })
117
+ };
118
+ return {
119
+ data: null,
120
+ status: response.status,
121
+ headers: response.headers,
122
+ error: new WP_Error({
123
+ code: "unexpected_error",
124
+ message: this.snippet(text)
125
+ })
126
+ };
127
+ }
128
+ if (parseErr) return {
129
+ data: null,
130
+ status: response.status,
131
+ headers: response.headers,
132
+ error: new WP_Error({
133
+ code: "unexpected_error",
134
+ message: this.snippet(text)
135
+ })
136
+ };
137
+ return {
138
+ data,
139
+ status: response.status,
140
+ headers: response.headers,
141
+ error: null
142
+ };
143
+ }
144
+ async fetch(input) {
145
+ const url = this.resolveUrl(input);
146
+ if (input.searchParams) {
147
+ const params = new URLSearchParams(stringifyQueryString(input.searchParams));
148
+ for (const [k, v] of params) url.searchParams.append(k, v);
149
+ }
150
+ if (!url.searchParams.has("context")) url.searchParams.set("context", "edit");
151
+ const isFormData = input.body instanceof FormData;
152
+ const headers = {
153
+ ...!isFormData && { "Content-Type": "application/json" },
154
+ ...input.headers,
155
+ [WP_AUTH_HEADER_KEY]: this.authHeader
156
+ };
157
+ const hasBody = input.method !== "GET" && input.method !== "DELETE";
158
+ return tryCatch(fetch(url.toString(), {
159
+ headers,
160
+ method: input.method,
161
+ signal: this.resolveSignal(input),
162
+ ...hasBody && input.body ? { body: isFormData ? input.body : JSON.stringify(input.body) } : {}
163
+ }));
164
+ }
165
+ resolveSignal(input) {
166
+ const timeoutSignal = AbortSignal.timeout(milliseconds(input.timeout ?? this.defaultTimeout));
167
+ return input.signal ? AbortSignal.any([input.signal, timeoutSignal]) : timeoutSignal;
168
+ }
169
+ resolveUrl(requestConfig) {
170
+ const baseSegment = requestConfig.base ? `${trimLeadingTrailingSlashes(requestConfig.base)}/` : "";
171
+ const pathSegment = trimLeadingTrailingSlashes(requestConfig.path);
172
+ return new URL(`${baseSegment}${pathSegment}`, this.siteBase);
173
+ }
174
+ snippet(text) {
175
+ return text.length > UNEXPECTED_BODY_SNIPPET_LENGTH ? `${text.slice(0, UNEXPECTED_BODY_SNIPPET_LENGTH)}…` : text;
176
+ }
177
+ };
178
+
179
+ //#endregion
180
+ export { WordPressService };
@@ -0,0 +1,67 @@
1
+ import { WP_Error } from "./error.js";
2
+ import { Duration, SearchParamsLike } from "@kizlo/shared";
3
+
4
+ //#region src/wordpress/types.d.ts
5
+ interface WP_Link {
6
+ href: string;
7
+ embeddable?: boolean;
8
+ taxonomy?: string;
9
+ templated?: boolean;
10
+ }
11
+ type WP_ListOrder = "asc" | "desc";
12
+ type WP_Context = "view" | "embed" | "edit";
13
+ type WP_CommonErrorCode = "rest_invalid_param" | "rest_missing_callback_param" | "rest_forbidden" | "rest_no_route" | "rest_cookie_invalid_nonce" | "unexpected_error" | "unknown_error";
14
+ interface WP_ErrorData<T extends string> {
15
+ code: T | WP_CommonErrorCode;
16
+ message: string;
17
+ }
18
+ interface WP_ListMetadata {
19
+ page: number;
20
+ total_items: number;
21
+ total_pages: number;
22
+ next_page: number | null;
23
+ prev_page: number | null;
24
+ has_next_page: boolean;
25
+ has_prev_page: boolean;
26
+ }
27
+ interface WP_List<T> {
28
+ items: T[];
29
+ meta: WP_ListMetadata;
30
+ }
31
+ interface WP_CurrencyFormat {
32
+ currency_code: string;
33
+ currency_symbol: string;
34
+ currency_minor_unit: number;
35
+ currency_decimal_separator: string;
36
+ currency_thousand_separator: string;
37
+ currency_prefix: string;
38
+ currency_suffix: string;
39
+ }
40
+ interface WP_RequestInput {
41
+ path: string;
42
+ body?: unknown;
43
+ base?: string;
44
+ searchParams?: SearchParamsLike;
45
+ headers?: Record<string, string>;
46
+ method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
47
+ signal?: AbortSignal;
48
+ timeout?: Duration;
49
+ }
50
+ interface WordPressCredentials {
51
+ url: string;
52
+ username: string;
53
+ password: string;
54
+ }
55
+ type WP_Result<TData, TError extends string> = {
56
+ data: TData;
57
+ status: number;
58
+ headers: Headers;
59
+ error: null;
60
+ } | {
61
+ data: null;
62
+ status: number;
63
+ headers: Headers;
64
+ error: WP_Error<TError>;
65
+ };
66
+ //#endregion
67
+ export { WP_CommonErrorCode, WP_Context, WP_CurrencyFormat, WP_ErrorData, WP_Link, WP_List, WP_ListMetadata, WP_ListOrder, WP_RequestInput, WP_Result, WordPressCredentials };
@@ -0,0 +1,28 @@
1
+ import { WP_Error } from "../error.js";
2
+ import { WP_List, WP_Result } from "../types.js";
3
+ import { WP_User, WP_UserCreateErrorCode, WP_UserCreateInput, WP_UserDeleteErrorCode, WP_UserDeleteInput, WP_UserDeleteResponse, WP_UserListErrorCode, WP_UserListInput, WP_UserRetrieveErrorCode, WP_UserRetrieveInput, WP_UserUpdateErrorCode, WP_UserUpdateInput } from "./types.js";
4
+ import { WordPressService } from "../service.js";
5
+ import "../index.js";
6
+
7
+ //#region src/wordpress/user/service.d.ts
8
+ declare class UserService {
9
+ private readonly wordpress;
10
+ constructor(wordpress: WordPressService);
11
+ list(input: WP_UserListInput): Promise<{
12
+ data: null;
13
+ status: number;
14
+ headers: Headers;
15
+ error: WP_Error<WP_UserListErrorCode>;
16
+ } | {
17
+ data: WP_List<WP_User>;
18
+ status: number;
19
+ headers: Headers;
20
+ error: null;
21
+ }>;
22
+ create(input: WP_UserCreateInput): Promise<WP_Result<WP_User, WP_UserCreateErrorCode>>;
23
+ get(input: WP_UserRetrieveInput): Promise<WP_Result<WP_User, WP_UserRetrieveErrorCode>>;
24
+ update(input: WP_UserUpdateInput): Promise<WP_Result<WP_User, WP_UserUpdateErrorCode>>;
25
+ delete(input: WP_UserDeleteInput): Promise<WP_Result<WP_UserDeleteResponse, WP_UserDeleteErrorCode>>;
26
+ }
27
+ //#endregion
28
+ export { UserService };
@@ -0,0 +1,55 @@
1
+ import { WP_CORE_BASE } from "../constants.js";
2
+
3
+ //#region src/wordpress/user/service.ts
4
+ var UserService = class {
5
+ wordpress;
6
+ constructor(wordpress) {
7
+ this.wordpress = wordpress;
8
+ }
9
+ async list(input) {
10
+ const { ...searchParams } = input;
11
+ const result = await this.wordpress.get("/users", {
12
+ base: WP_CORE_BASE,
13
+ searchParams
14
+ });
15
+ if (result.error) return result;
16
+ return {
17
+ ...result,
18
+ data: this.wordpress.resolveList({
19
+ data: result.data,
20
+ headers: result.headers,
21
+ searchParams
22
+ })
23
+ };
24
+ }
25
+ async create(input) {
26
+ return this.wordpress.post("/users", {
27
+ base: WP_CORE_BASE,
28
+ body: input
29
+ });
30
+ }
31
+ async get(input) {
32
+ const { id,...searchParams } = input;
33
+ return this.wordpress.get(`/users/${id}`, {
34
+ base: WP_CORE_BASE,
35
+ searchParams
36
+ });
37
+ }
38
+ async update(input) {
39
+ const { id,...body } = input;
40
+ return this.wordpress.post(`/users/${id}`, {
41
+ base: WP_CORE_BASE,
42
+ body
43
+ });
44
+ }
45
+ async delete(input) {
46
+ const { id,...searchParams } = input;
47
+ return this.wordpress.delete(`/users/${id}`, {
48
+ base: WP_CORE_BASE,
49
+ searchParams
50
+ });
51
+ }
52
+ };
53
+
54
+ //#endregion
55
+ export { UserService };