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.
- package/LICENSE +21 -0
- package/README.md +74 -0
- package/dist/adapters/auth.d.ts +18 -0
- package/dist/adapters/auth.js +15 -0
- package/dist/adapters/captcha.d.ts +28 -0
- package/dist/adapters/captcha.js +86 -0
- package/dist/adapters/geo.d.ts +18 -0
- package/dist/adapters/geo.js +18 -0
- package/dist/adapters/geo.vercel.d.ts +6 -0
- package/dist/adapters/geo.vercel.js +41 -0
- package/dist/adapters/logger.d.ts +31 -0
- package/dist/adapters/logger.js +30 -0
- package/dist/cli/banner.js +26 -0
- package/dist/cli/commands/generate.js +34 -0
- package/dist/cli/commands/init.js +313 -0
- package/dist/cli/commands/test.js +68 -0
- package/dist/cli/commands/watch.js +53 -0
- package/dist/cli/commands/wp.js +66 -0
- package/dist/cli/daemon/config.js +87 -0
- package/dist/cli/daemon/generate.js +40 -0
- package/dist/cli/daemon/lock.js +31 -0
- package/dist/cli/daemon/logger.js +8 -0
- package/dist/cli/daemon/watch.js +37 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +22 -0
- package/dist/cli/presets/base.js +23 -0
- package/dist/cli/presets/index.js +16 -0
- package/dist/cli/presets/nextjs.js +39 -0
- package/dist/cli/utils.js +182 -0
- package/dist/cli/wp/bootstrap.js +114 -0
- package/dist/cli/wp/compose/docker-compose.yml +73 -0
- package/dist/cli/wp/constants.js +52 -0
- package/dist/cli/wp/docker.d.ts +24 -0
- package/dist/cli/wp/docker.js +69 -0
- package/dist/cli/wp/setup.js +39 -0
- package/dist/cli/wp/types.d.ts +72 -0
- package/dist/cli/wp/types.js +8 -0
- package/dist/cli/wp/utils.d.ts +39 -0
- package/dist/cli/wp/utils.js +105 -0
- package/dist/client.d.ts +24 -0
- package/dist/client.js +55 -0
- package/dist/comment/errors.js +47 -0
- package/dist/comment/index.js +138 -0
- package/dist/comment/schema.js +72 -0
- package/dist/comment/utils.js +35 -0
- package/dist/config.d.ts +39 -0
- package/dist/config.js +7 -0
- package/dist/context.d.ts +73 -0
- package/dist/context.js +145 -0
- package/dist/cookie.d.ts +19 -0
- package/dist/cookie.js +25 -0
- package/dist/email/service.d.ts +11 -0
- package/dist/email/service.interface.d.ts +8 -0
- package/dist/email/service.js +20 -0
- package/dist/index.d.ts +39 -0
- package/dist/index.js +31 -0
- package/dist/integrations/nextjs/kizlo.client.d.ts +10 -0
- package/dist/integrations/nextjs/kizlo.client.js +13 -0
- package/dist/integrations/nextjs/kizlo.server.d.ts +12 -0
- package/dist/integrations/nextjs/kizlo.server.js +13 -0
- package/dist/integrations/nextjs/utils.js +11 -0
- package/dist/kizlo.d.ts +73 -0
- package/dist/kizlo.js +144 -0
- package/dist/menu/errors.js +16 -0
- package/dist/menu/fixture.d.ts +8 -0
- package/dist/menu/fixture.js +62 -0
- package/dist/menu/index.js +70 -0
- package/dist/menu/schema.js +70 -0
- package/dist/menu/utils.js +57 -0
- package/dist/nextjs/server.d.ts +2 -0
- package/dist/nextjs/server.js +3 -0
- package/dist/nextjs.d.ts +2 -0
- package/dist/nextjs.js +3 -0
- package/dist/package.js +5 -0
- package/dist/post/error.js +30 -0
- package/dist/post/fixture.d.ts +8 -0
- package/dist/post/fixture.js +71 -0
- package/dist/post/index.js +123 -0
- package/dist/post/schema.js +120 -0
- package/dist/post/utils.js +42 -0
- package/dist/post-type/service.js +48 -0
- package/dist/router.d.ts +624 -0
- package/dist/router.js +15 -0
- package/dist/seo/index.js +54 -0
- package/dist/seo/schema.d.ts +196 -0
- package/dist/seo/schema.js +96 -0
- package/dist/seo/types.d.ts +80 -0
- package/dist/seo/utils.js +44 -0
- package/dist/service.d.ts +17 -0
- package/dist/service.js +17 -0
- package/dist/shared/constants.js +10 -0
- package/dist/shared/contract.d.ts +21 -0
- package/dist/shared/contract.js +43 -0
- package/dist/shared/crypto.d.ts +28 -0
- package/dist/shared/crypto.js +34 -0
- package/dist/shared/error.d.ts +128 -0
- package/dist/shared/error.js +147 -0
- package/dist/shared/extension.d.ts +25 -0
- package/dist/shared/extension.js +7 -0
- package/dist/shared/identifier.d.ts +11 -0
- package/dist/shared/identifier.js +26 -0
- package/dist/shared/middleware.d.ts +29 -0
- package/dist/shared/procedure.d.ts +114 -0
- package/dist/shared/procedure.js +114 -0
- package/dist/shared/result.d.ts +24 -0
- package/dist/shared/result.js +49 -0
- package/dist/shared/router.d.ts +7 -0
- package/dist/shared/router.js +26 -0
- package/dist/shared/schema.d.ts +55 -0
- package/dist/shared/schema.js +43 -0
- package/dist/shared/serialize.d.ts +12 -0
- package/dist/shared/serialize.js +51 -0
- package/dist/shared/types.d.ts +35 -0
- package/dist/shared/utils.js +17 -0
- package/dist/test/auth.d.ts +7 -0
- package/dist/test/auth.js +15 -0
- package/dist/test/captcha.d.ts +7 -0
- package/dist/test/captcha.js +8 -0
- package/dist/test/client.d.ts +11 -0
- package/dist/test/client.js +14 -0
- package/dist/test/fixtures.d.ts +14 -0
- package/dist/test/fixtures.js +13 -0
- package/dist/test/server.d.ts +10 -0
- package/dist/test/server.js +32 -0
- package/dist/test/users.d.ts +14 -0
- package/dist/test/users.js +17 -0
- package/dist/test.d.ts +12 -0
- package/dist/test.js +13 -0
- package/dist/webhook/index.d.ts +38 -0
- package/dist/webhook/index.js +44 -0
- package/dist/webhook/schema.d.ts +45 -0
- package/dist/webhook/schema.js +72 -0
- package/dist/webhook/utils.js +21 -0
- package/dist/wordpress/comment/service.d.ts +28 -0
- package/dist/wordpress/comment/service.js +55 -0
- package/dist/wordpress/comment/types.d.ts +206 -0
- package/dist/wordpress/constants.d.ts +14 -0
- package/dist/wordpress/constants.js +13 -0
- package/dist/wordpress/error.d.ts +9 -0
- package/dist/wordpress/error.js +12 -0
- package/dist/wordpress/index.d.ts +15 -0
- package/dist/wordpress/menu/items/service.d.ts +28 -0
- package/dist/wordpress/menu/items/service.js +55 -0
- package/dist/wordpress/menu/items/types.d.ts +214 -0
- package/dist/wordpress/menu/service.d.ts +30 -0
- package/dist/wordpress/menu/service.js +58 -0
- package/dist/wordpress/menu/types.d.ts +115 -0
- package/dist/wordpress/post/service.d.ts +28 -0
- package/dist/wordpress/post/service.js +55 -0
- package/dist/wordpress/post/types.d.ts +290 -0
- package/dist/wordpress/service.d.ts +77 -0
- package/dist/wordpress/service.js +180 -0
- package/dist/wordpress/types.d.ts +67 -0
- package/dist/wordpress/user/service.d.ts +28 -0
- package/dist/wordpress/user/service.js +55 -0
- package/dist/wordpress/user/types.d.ts +221 -0
- package/dist/wordpress/utils.d.ts +7 -0
- package/dist/wordpress/utils.js +9 -0
- package/package.json +109 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { createProcedure } from "../shared/procedure.js";
|
|
2
|
+
import { WP_KIZLO_BASE } from "../wordpress/constants.js";
|
|
3
|
+
import { ListSitemapUrlInput, Robots, SitemapList, SitemapUrlList } from "./schema.js";
|
|
4
|
+
|
|
5
|
+
//#region src/seo/index.ts
|
|
6
|
+
const SEO_ROUTER_MAP = {
|
|
7
|
+
sitemaps: createProcedure({
|
|
8
|
+
scope: "internal",
|
|
9
|
+
output: SitemapList
|
|
10
|
+
}, async ({ context, errors }) => {
|
|
11
|
+
const response = await context.service.wordpress.get("/seo/sitemaps", { base: WP_KIZLO_BASE });
|
|
12
|
+
if (response.error) {
|
|
13
|
+
context.logger.error("List sitemaps unhandled error", response.error);
|
|
14
|
+
throw errors.INTERNAL_SERVER_ERROR();
|
|
15
|
+
}
|
|
16
|
+
return response.data;
|
|
17
|
+
}),
|
|
18
|
+
robots: createProcedure({
|
|
19
|
+
scope: "internal",
|
|
20
|
+
output: Robots
|
|
21
|
+
}, async ({ context, errors }) => {
|
|
22
|
+
const response = await context.service.wordpress.get(`/seo/robots`, { base: WP_KIZLO_BASE });
|
|
23
|
+
if (response.error) {
|
|
24
|
+
context.logger.error("List robots unhandled error", response.error);
|
|
25
|
+
throw errors.INTERNAL_SERVER_ERROR();
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
sitemaps: response.data.sitemaps ?? [],
|
|
29
|
+
rules: response.data.rules.map((item) => ({
|
|
30
|
+
allow: item.allow,
|
|
31
|
+
disallow: item.disallow,
|
|
32
|
+
userAgent: item.user_agent
|
|
33
|
+
}))
|
|
34
|
+
};
|
|
35
|
+
}),
|
|
36
|
+
urls: createProcedure({
|
|
37
|
+
scope: "internal",
|
|
38
|
+
output: SitemapUrlList,
|
|
39
|
+
input: ListSitemapUrlInput
|
|
40
|
+
}, async ({ context, input, errors }) => {
|
|
41
|
+
const response = await context.service.wordpress.get(`/seo/sitemaps/${input.type}/${input.type !== "author" ? input.key : ""}`, {
|
|
42
|
+
base: WP_KIZLO_BASE,
|
|
43
|
+
searchParams: { page: input.page ?? 1 }
|
|
44
|
+
});
|
|
45
|
+
if (response.error) {
|
|
46
|
+
context.logger.error("List sitemap urls unhandled error", response.error);
|
|
47
|
+
throw errors.INTERNAL_SERVER_ERROR();
|
|
48
|
+
}
|
|
49
|
+
return response.data;
|
|
50
|
+
})
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
//#endregion
|
|
54
|
+
export { SEO_ROUTER_MAP };
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import z from "zod/v4";
|
|
2
|
+
|
|
3
|
+
//#region src/seo/schema.d.ts
|
|
4
|
+
declare const SeoHead: z.ZodObject<{
|
|
5
|
+
title: z.ZodString;
|
|
6
|
+
canonical: z.ZodString;
|
|
7
|
+
robots: z.ZodObject<{
|
|
8
|
+
index: z.ZodString;
|
|
9
|
+
follow: z.ZodString;
|
|
10
|
+
maxSnippet: z.ZodString;
|
|
11
|
+
maxImagePreview: z.ZodString;
|
|
12
|
+
maxVideoPreview: z.ZodString;
|
|
13
|
+
}, z.core.$strip>;
|
|
14
|
+
og: z.ZodObject<{
|
|
15
|
+
locale: z.ZodString;
|
|
16
|
+
type: z.ZodString;
|
|
17
|
+
title: z.ZodString;
|
|
18
|
+
url: z.ZodString;
|
|
19
|
+
siteName: z.ZodString;
|
|
20
|
+
description: z.ZodString;
|
|
21
|
+
image: z.ZodNullable<z.ZodObject<{
|
|
22
|
+
url: z.ZodString;
|
|
23
|
+
width: z.ZodString;
|
|
24
|
+
height: z.ZodString;
|
|
25
|
+
type: z.ZodString;
|
|
26
|
+
alt: z.ZodNullable<z.ZodString>;
|
|
27
|
+
}, z.core.$strip>>;
|
|
28
|
+
}, z.core.$strip>;
|
|
29
|
+
twitter: z.ZodObject<{
|
|
30
|
+
card: z.ZodEnum<{
|
|
31
|
+
summary: "summary";
|
|
32
|
+
summary_large_image: "summary_large_image";
|
|
33
|
+
}>;
|
|
34
|
+
title: z.ZodString;
|
|
35
|
+
site: z.ZodNullable<z.ZodString>;
|
|
36
|
+
creator: z.ZodNullable<z.ZodString>;
|
|
37
|
+
description: z.ZodString;
|
|
38
|
+
image: z.ZodNullable<z.ZodString>;
|
|
39
|
+
}, z.core.$strip>;
|
|
40
|
+
article: z.ZodNullable<z.ZodObject<{
|
|
41
|
+
publishedTime: z.ZodString;
|
|
42
|
+
modifiedTime: z.ZodString;
|
|
43
|
+
author: z.ZodString;
|
|
44
|
+
authorUrl: z.ZodString;
|
|
45
|
+
section: z.ZodString;
|
|
46
|
+
}, z.core.$strip>>;
|
|
47
|
+
}, z.core.$strip>;
|
|
48
|
+
type SeoHead = z.infer<typeof SeoHead>;
|
|
49
|
+
declare const SeoSchema: z.ZodObject<{
|
|
50
|
+
"@context": z.ZodLiteral<"https://schema.org">;
|
|
51
|
+
"@graph": z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
52
|
+
}, z.core.$strip>;
|
|
53
|
+
type SeoSchema = z.infer<typeof SeoSchema>;
|
|
54
|
+
declare const Seo: z.ZodObject<{
|
|
55
|
+
head: z.ZodObject<{
|
|
56
|
+
title: z.ZodString;
|
|
57
|
+
canonical: z.ZodString;
|
|
58
|
+
robots: z.ZodObject<{
|
|
59
|
+
index: z.ZodString;
|
|
60
|
+
follow: z.ZodString;
|
|
61
|
+
maxSnippet: z.ZodString;
|
|
62
|
+
maxImagePreview: z.ZodString;
|
|
63
|
+
maxVideoPreview: z.ZodString;
|
|
64
|
+
}, z.core.$strip>;
|
|
65
|
+
og: z.ZodObject<{
|
|
66
|
+
locale: z.ZodString;
|
|
67
|
+
type: z.ZodString;
|
|
68
|
+
title: z.ZodString;
|
|
69
|
+
url: z.ZodString;
|
|
70
|
+
siteName: z.ZodString;
|
|
71
|
+
description: z.ZodString;
|
|
72
|
+
image: z.ZodNullable<z.ZodObject<{
|
|
73
|
+
url: z.ZodString;
|
|
74
|
+
width: z.ZodString;
|
|
75
|
+
height: z.ZodString;
|
|
76
|
+
type: z.ZodString;
|
|
77
|
+
alt: z.ZodNullable<z.ZodString>;
|
|
78
|
+
}, z.core.$strip>>;
|
|
79
|
+
}, z.core.$strip>;
|
|
80
|
+
twitter: z.ZodObject<{
|
|
81
|
+
card: z.ZodEnum<{
|
|
82
|
+
summary: "summary";
|
|
83
|
+
summary_large_image: "summary_large_image";
|
|
84
|
+
}>;
|
|
85
|
+
title: z.ZodString;
|
|
86
|
+
site: z.ZodNullable<z.ZodString>;
|
|
87
|
+
creator: z.ZodNullable<z.ZodString>;
|
|
88
|
+
description: z.ZodString;
|
|
89
|
+
image: z.ZodNullable<z.ZodString>;
|
|
90
|
+
}, z.core.$strip>;
|
|
91
|
+
article: z.ZodNullable<z.ZodObject<{
|
|
92
|
+
publishedTime: z.ZodString;
|
|
93
|
+
modifiedTime: z.ZodString;
|
|
94
|
+
author: z.ZodString;
|
|
95
|
+
authorUrl: z.ZodString;
|
|
96
|
+
section: z.ZodString;
|
|
97
|
+
}, z.core.$strip>>;
|
|
98
|
+
}, z.core.$strip>;
|
|
99
|
+
schema: z.ZodObject<{
|
|
100
|
+
"@context": z.ZodLiteral<"https://schema.org">;
|
|
101
|
+
"@graph": z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
102
|
+
}, z.core.$strip>;
|
|
103
|
+
}, z.core.$strip>;
|
|
104
|
+
type Seo = z.infer<typeof Seo>;
|
|
105
|
+
declare const CONTENT_TYPES: readonly ["post_type", "taxonomy", "author"];
|
|
106
|
+
declare const ContentType: z.ZodEnum<{
|
|
107
|
+
author: "author";
|
|
108
|
+
post_type: "post_type";
|
|
109
|
+
taxonomy: "taxonomy";
|
|
110
|
+
}>;
|
|
111
|
+
type ContentType = z.infer<typeof ContentType>;
|
|
112
|
+
declare const Sitemap: z.ZodObject<{
|
|
113
|
+
key: z.ZodString;
|
|
114
|
+
type: z.ZodEnum<{
|
|
115
|
+
author: "author";
|
|
116
|
+
post_type: "post_type";
|
|
117
|
+
taxonomy: "taxonomy";
|
|
118
|
+
}>;
|
|
119
|
+
pages: z.ZodNumber;
|
|
120
|
+
lastmod: z.ZodString;
|
|
121
|
+
}, z.core.$strip>;
|
|
122
|
+
type Sitemap = z.infer<typeof Sitemap>;
|
|
123
|
+
declare const SitemapList: z.ZodArray<z.ZodObject<{
|
|
124
|
+
key: z.ZodString;
|
|
125
|
+
type: z.ZodEnum<{
|
|
126
|
+
author: "author";
|
|
127
|
+
post_type: "post_type";
|
|
128
|
+
taxonomy: "taxonomy";
|
|
129
|
+
}>;
|
|
130
|
+
pages: z.ZodNumber;
|
|
131
|
+
lastmod: z.ZodString;
|
|
132
|
+
}, z.core.$strip>>;
|
|
133
|
+
type SitemapList = z.infer<typeof SitemapList>;
|
|
134
|
+
declare const SitemapUrlImage: z.ZodObject<{
|
|
135
|
+
loc: z.ZodString;
|
|
136
|
+
title: z.ZodString;
|
|
137
|
+
}, z.core.$strip>;
|
|
138
|
+
type SitemapUrlImage = z.infer<typeof SitemapUrlImage>;
|
|
139
|
+
declare const SitemapUrl: z.ZodObject<{
|
|
140
|
+
loc: z.ZodString;
|
|
141
|
+
lastmod: z.ZodString;
|
|
142
|
+
images: z.ZodArray<z.ZodObject<{
|
|
143
|
+
loc: z.ZodString;
|
|
144
|
+
title: z.ZodString;
|
|
145
|
+
}, z.core.$strip>>;
|
|
146
|
+
}, z.core.$strip>;
|
|
147
|
+
type SitemapUrl = z.infer<typeof SitemapUrl>;
|
|
148
|
+
declare const SitemapUrlList: z.ZodArray<z.ZodObject<{
|
|
149
|
+
loc: z.ZodString;
|
|
150
|
+
lastmod: z.ZodString;
|
|
151
|
+
images: z.ZodArray<z.ZodObject<{
|
|
152
|
+
loc: z.ZodString;
|
|
153
|
+
title: z.ZodString;
|
|
154
|
+
}, z.core.$strip>>;
|
|
155
|
+
}, z.core.$strip>>;
|
|
156
|
+
type SitemapUrlList = z.infer<typeof SitemapUrlList>;
|
|
157
|
+
declare const ListSitemapUrlInputBase: z.ZodObject<{
|
|
158
|
+
type: z.ZodEnum<{
|
|
159
|
+
author: "author";
|
|
160
|
+
post_type: "post_type";
|
|
161
|
+
taxonomy: "taxonomy";
|
|
162
|
+
}>;
|
|
163
|
+
page: z.ZodOptional<z.ZodPipe<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>, z.ZodCoercedNumber<string | number>>>;
|
|
164
|
+
}, z.core.$strip>;
|
|
165
|
+
type ListSitemapUrlInputBase = z.input<typeof ListSitemapUrlInputBase>;
|
|
166
|
+
declare const ListSitemapUrlInput: z.ZodUnion<readonly [z.ZodObject<{
|
|
167
|
+
page: z.ZodOptional<z.ZodPipe<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>, z.ZodCoercedNumber<string | number>>>;
|
|
168
|
+
type: z.ZodEnum<{
|
|
169
|
+
post_type: "post_type";
|
|
170
|
+
taxonomy: "taxonomy";
|
|
171
|
+
}>;
|
|
172
|
+
key: z.ZodString;
|
|
173
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
174
|
+
page: z.ZodOptional<z.ZodPipe<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>, z.ZodCoercedNumber<string | number>>>;
|
|
175
|
+
type: z.ZodEnum<{
|
|
176
|
+
author: "author";
|
|
177
|
+
}>;
|
|
178
|
+
}, z.core.$strip>]>;
|
|
179
|
+
type ListSitemapUrlInput = z.infer<typeof ListSitemapUrlInput>;
|
|
180
|
+
declare const RobotsRule: z.ZodObject<{
|
|
181
|
+
userAgent: z.ZodString;
|
|
182
|
+
allow: z.ZodArray<z.ZodString>;
|
|
183
|
+
disallow: z.ZodArray<z.ZodString>;
|
|
184
|
+
}, z.core.$strip>;
|
|
185
|
+
type RobotsRule = z.infer<typeof RobotsRule>;
|
|
186
|
+
declare const Robots: z.ZodObject<{
|
|
187
|
+
sitemaps: z.ZodArray<z.ZodString>;
|
|
188
|
+
rules: z.ZodArray<z.ZodObject<{
|
|
189
|
+
userAgent: z.ZodString;
|
|
190
|
+
allow: z.ZodArray<z.ZodString>;
|
|
191
|
+
disallow: z.ZodArray<z.ZodString>;
|
|
192
|
+
}, z.core.$strip>>;
|
|
193
|
+
}, z.core.$strip>;
|
|
194
|
+
type Robots = z.infer<typeof Robots>;
|
|
195
|
+
//#endregion
|
|
196
|
+
export { CONTENT_TYPES, ContentType, ListSitemapUrlInput, ListSitemapUrlInputBase, Robots, RobotsRule, Seo, SeoHead, SeoSchema, Sitemap, SitemapList, SitemapUrl, SitemapUrlImage, SitemapUrlList };
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { NumberLike } from "@kizlo/shared";
|
|
2
|
+
import z from "zod/v4";
|
|
3
|
+
|
|
4
|
+
//#region src/seo/schema.ts
|
|
5
|
+
const SeoHead = z.object({
|
|
6
|
+
title: z.string(),
|
|
7
|
+
canonical: z.string(),
|
|
8
|
+
robots: z.object({
|
|
9
|
+
index: z.string(),
|
|
10
|
+
follow: z.string(),
|
|
11
|
+
maxSnippet: z.string(),
|
|
12
|
+
maxImagePreview: z.string(),
|
|
13
|
+
maxVideoPreview: z.string()
|
|
14
|
+
}),
|
|
15
|
+
og: z.object({
|
|
16
|
+
locale: z.string(),
|
|
17
|
+
type: z.string(),
|
|
18
|
+
title: z.string(),
|
|
19
|
+
url: z.string(),
|
|
20
|
+
siteName: z.string(),
|
|
21
|
+
description: z.string(),
|
|
22
|
+
image: z.object({
|
|
23
|
+
url: z.string(),
|
|
24
|
+
width: z.string(),
|
|
25
|
+
height: z.string(),
|
|
26
|
+
type: z.string(),
|
|
27
|
+
alt: z.string().nullable()
|
|
28
|
+
}).nullable()
|
|
29
|
+
}),
|
|
30
|
+
twitter: z.object({
|
|
31
|
+
card: z.enum(["summary", "summary_large_image"]),
|
|
32
|
+
title: z.string(),
|
|
33
|
+
site: z.string().nullable(),
|
|
34
|
+
creator: z.string().nullable(),
|
|
35
|
+
description: z.string(),
|
|
36
|
+
image: z.string().nullable()
|
|
37
|
+
}),
|
|
38
|
+
article: z.object({
|
|
39
|
+
publishedTime: z.string(),
|
|
40
|
+
modifiedTime: z.string(),
|
|
41
|
+
author: z.string(),
|
|
42
|
+
authorUrl: z.string(),
|
|
43
|
+
section: z.string()
|
|
44
|
+
}).nullable()
|
|
45
|
+
});
|
|
46
|
+
const SeoSchema = z.object({
|
|
47
|
+
"@context": z.literal("https://schema.org"),
|
|
48
|
+
"@graph": z.array(z.record(z.string(), z.unknown()))
|
|
49
|
+
});
|
|
50
|
+
const Seo = z.object({
|
|
51
|
+
head: SeoHead,
|
|
52
|
+
schema: SeoSchema
|
|
53
|
+
});
|
|
54
|
+
const CONTENT_TYPES = [
|
|
55
|
+
"post_type",
|
|
56
|
+
"taxonomy",
|
|
57
|
+
"author"
|
|
58
|
+
];
|
|
59
|
+
const ContentType = z.enum(CONTENT_TYPES);
|
|
60
|
+
const Sitemap = z.object({
|
|
61
|
+
key: z.string(),
|
|
62
|
+
type: ContentType,
|
|
63
|
+
pages: z.number(),
|
|
64
|
+
lastmod: z.string()
|
|
65
|
+
});
|
|
66
|
+
const SitemapList = z.array(Sitemap);
|
|
67
|
+
const SitemapUrlImage = z.object({
|
|
68
|
+
loc: z.string(),
|
|
69
|
+
title: z.string()
|
|
70
|
+
});
|
|
71
|
+
const SitemapUrl = z.object({
|
|
72
|
+
loc: z.string(),
|
|
73
|
+
lastmod: z.string(),
|
|
74
|
+
images: z.array(SitemapUrlImage)
|
|
75
|
+
});
|
|
76
|
+
const SitemapUrlList = z.array(SitemapUrl);
|
|
77
|
+
const ListSitemapUrlInputBase = z.object({
|
|
78
|
+
type: ContentType,
|
|
79
|
+
page: NumberLike.optional()
|
|
80
|
+
});
|
|
81
|
+
const ListSitemapUrlInput = z.union([ListSitemapUrlInputBase.extend({
|
|
82
|
+
type: ContentType.exclude(["author"]),
|
|
83
|
+
key: z.string()
|
|
84
|
+
}), ListSitemapUrlInputBase.extend({ type: ContentType.extract(["author"]) })]);
|
|
85
|
+
const RobotsRule = z.object({
|
|
86
|
+
userAgent: z.string(),
|
|
87
|
+
allow: z.array(z.string()),
|
|
88
|
+
disallow: z.array(z.string())
|
|
89
|
+
});
|
|
90
|
+
const Robots = z.object({
|
|
91
|
+
sitemaps: z.array(z.string()),
|
|
92
|
+
rules: z.array(RobotsRule)
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
//#endregion
|
|
96
|
+
export { CONTENT_TYPES, ContentType, ListSitemapUrlInput, ListSitemapUrlInputBase, Robots, RobotsRule, Seo, SeoHead, SeoSchema, Sitemap, SitemapList, SitemapUrl, SitemapUrlImage, SitemapUrlList };
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
//#region src/seo/types.d.ts
|
|
2
|
+
interface WPK_Seo {
|
|
3
|
+
head: {
|
|
4
|
+
title: string;
|
|
5
|
+
canonical: string;
|
|
6
|
+
robots: {
|
|
7
|
+
index: string;
|
|
8
|
+
follow: string;
|
|
9
|
+
"max-snippet": string;
|
|
10
|
+
"max-image-preview": string;
|
|
11
|
+
"max-video-preview": string;
|
|
12
|
+
};
|
|
13
|
+
og: {
|
|
14
|
+
locale: string;
|
|
15
|
+
type: string;
|
|
16
|
+
title: string;
|
|
17
|
+
url: string;
|
|
18
|
+
site_name: string;
|
|
19
|
+
description: string;
|
|
20
|
+
image: {
|
|
21
|
+
url: string;
|
|
22
|
+
width: string;
|
|
23
|
+
height: string;
|
|
24
|
+
type: string;
|
|
25
|
+
alt: string | null;
|
|
26
|
+
} | null;
|
|
27
|
+
};
|
|
28
|
+
twitter: {
|
|
29
|
+
card: "summary_large_image";
|
|
30
|
+
title: string;
|
|
31
|
+
site: string | null;
|
|
32
|
+
creator: string | null;
|
|
33
|
+
description: string;
|
|
34
|
+
image: string | null;
|
|
35
|
+
};
|
|
36
|
+
article: {
|
|
37
|
+
published_time: string;
|
|
38
|
+
modified_time: string;
|
|
39
|
+
author: string;
|
|
40
|
+
author_url: string;
|
|
41
|
+
section: string;
|
|
42
|
+
} | null;
|
|
43
|
+
};
|
|
44
|
+
schema: {
|
|
45
|
+
"@context": "https://schema.org";
|
|
46
|
+
"@graph": Record<string, unknown>[];
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
type WPK_SitemapContentType = "post_type" | "taxonomy" | "author";
|
|
50
|
+
interface WPK_RobotRule {
|
|
51
|
+
user_agent: string;
|
|
52
|
+
allow: string[];
|
|
53
|
+
disallow: string[];
|
|
54
|
+
}
|
|
55
|
+
interface WPK_Robots {
|
|
56
|
+
rules: WPK_RobotRule[];
|
|
57
|
+
sitemaps?: string[];
|
|
58
|
+
}
|
|
59
|
+
interface WPK_Sitemap {
|
|
60
|
+
key: string;
|
|
61
|
+
type: WPK_SitemapContentType;
|
|
62
|
+
pages: number;
|
|
63
|
+
lastmod: string;
|
|
64
|
+
}
|
|
65
|
+
interface WPK_SitemapUrlImage {
|
|
66
|
+
loc: string;
|
|
67
|
+
title: string;
|
|
68
|
+
}
|
|
69
|
+
interface WPK_SitemapUrl {
|
|
70
|
+
loc: string;
|
|
71
|
+
lastmod: string;
|
|
72
|
+
images: WPK_SitemapUrlImage[];
|
|
73
|
+
}
|
|
74
|
+
interface WPK_ListSitemapUrlInput {
|
|
75
|
+
type: WPK_SitemapContentType;
|
|
76
|
+
key?: string;
|
|
77
|
+
page?: number;
|
|
78
|
+
}
|
|
79
|
+
//#endregion
|
|
80
|
+
export { WPK_ListSitemapUrlInput, WPK_RobotRule, WPK_Robots, WPK_Seo, WPK_Sitemap, WPK_SitemapContentType, WPK_SitemapUrl, WPK_SitemapUrlImage };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
//#region src/seo/utils.ts
|
|
2
|
+
function deserializeSeo(data) {
|
|
3
|
+
return {
|
|
4
|
+
head: {
|
|
5
|
+
article: data.head.article ? {
|
|
6
|
+
author: data.head.article.author,
|
|
7
|
+
authorUrl: data.head.article.author_url,
|
|
8
|
+
modifiedTime: data.head.article.modified_time,
|
|
9
|
+
publishedTime: data.head.article.published_time,
|
|
10
|
+
section: data.head.article.section
|
|
11
|
+
} : null,
|
|
12
|
+
canonical: data.head.canonical,
|
|
13
|
+
og: {
|
|
14
|
+
description: data.head.og.description,
|
|
15
|
+
image: data.head.og.image ?? null,
|
|
16
|
+
locale: data.head.og.locale,
|
|
17
|
+
siteName: data.head.og.site_name,
|
|
18
|
+
title: data.head.og.title,
|
|
19
|
+
type: data.head.og.type,
|
|
20
|
+
url: data.head.og.url
|
|
21
|
+
},
|
|
22
|
+
robots: {
|
|
23
|
+
index: data.head.robots.index,
|
|
24
|
+
follow: data.head.robots.follow,
|
|
25
|
+
maxSnippet: data.head.robots["max-snippet"],
|
|
26
|
+
maxImagePreview: data.head.robots["max-image-preview"],
|
|
27
|
+
maxVideoPreview: data.head.robots["max-video-preview"]
|
|
28
|
+
},
|
|
29
|
+
title: data.head.title,
|
|
30
|
+
twitter: {
|
|
31
|
+
card: data.head.twitter.card,
|
|
32
|
+
creator: data.head.twitter.creator,
|
|
33
|
+
description: data.head.twitter.description,
|
|
34
|
+
image: data.head.twitter.image ?? null,
|
|
35
|
+
site: data.head.twitter.site,
|
|
36
|
+
title: data.head.twitter.title
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
schema: data.schema
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
//#endregion
|
|
44
|
+
export { deserializeSeo };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { WordPressCredentials } from "./wordpress/types.js";
|
|
2
|
+
import { WordPressService } from "./wordpress/service.js";
|
|
3
|
+
import "./wordpress/index.js";
|
|
4
|
+
import { EmailService } from "./email/service.js";
|
|
5
|
+
|
|
6
|
+
//#region src/service.d.ts
|
|
7
|
+
interface ServiceConfig {
|
|
8
|
+
credentials: WordPressCredentials;
|
|
9
|
+
}
|
|
10
|
+
declare class Service {
|
|
11
|
+
readonly email: EmailService;
|
|
12
|
+
readonly wordpress: WordPressService;
|
|
13
|
+
private readonly config;
|
|
14
|
+
constructor(config: ServiceConfig);
|
|
15
|
+
}
|
|
16
|
+
//#endregion
|
|
17
|
+
export { Service, ServiceConfig };
|
package/dist/service.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { WordPressService } from "./wordpress/service.js";
|
|
2
|
+
import { EmailService } from "./email/service.js";
|
|
3
|
+
|
|
4
|
+
//#region src/service.ts
|
|
5
|
+
var Service = class {
|
|
6
|
+
email;
|
|
7
|
+
wordpress;
|
|
8
|
+
config;
|
|
9
|
+
constructor(config) {
|
|
10
|
+
this.config = config;
|
|
11
|
+
this.email = new EmailService(this);
|
|
12
|
+
this.wordpress = new WordPressService(this.config);
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
//#endregion
|
|
17
|
+
export { Service };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { version } from "../package.js";
|
|
2
|
+
import { milliseconds } from "@kizlo/shared";
|
|
3
|
+
|
|
4
|
+
//#region src/shared/constants.ts
|
|
5
|
+
const VERSION = version;
|
|
6
|
+
const RPC_PROTOCOL_HEADER = "x-rpc-protocol";
|
|
7
|
+
const SESSION_EXPIRY_MARGIN_MS = milliseconds("5 minutes");
|
|
8
|
+
|
|
9
|
+
//#endregion
|
|
10
|
+
export { RPC_PROTOCOL_HEADER };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { HTTPMethod } from "./types.js";
|
|
2
|
+
import { AnyProcedureRouter, InvocationScope } from "./procedure.js";
|
|
3
|
+
import { AnyContractRouter } from "@orpc/contract";
|
|
4
|
+
import { Pathname } from "@kizlo/shared";
|
|
5
|
+
|
|
6
|
+
//#region src/shared/contract.d.ts
|
|
7
|
+
type Contract = {
|
|
8
|
+
[key: string]: ContractProcedure | Contract;
|
|
9
|
+
};
|
|
10
|
+
type ContractProcedure = {
|
|
11
|
+
scope: InvocationScope;
|
|
12
|
+
route: {
|
|
13
|
+
method?: HTTPMethod;
|
|
14
|
+
path?: Pathname;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
declare function isContractProcedure$1(node: unknown): node is ContractProcedure;
|
|
18
|
+
declare function generateContract<TRouter extends AnyProcedureRouter>(router: TRouter): Promise<Contract>;
|
|
19
|
+
declare function restoreContract(contract: Contract): AnyContractRouter;
|
|
20
|
+
//#endregion
|
|
21
|
+
export { Contract, ContractProcedure, generateContract, isContractProcedure$1 as isContractProcedure, restoreContract };
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { createOrpcRouter } from "./router.js";
|
|
2
|
+
import { isContractProcedure, minifyContractRouter } from "@orpc/contract";
|
|
3
|
+
|
|
4
|
+
//#region src/shared/contract.ts
|
|
5
|
+
function isContractProcedure$1(node) {
|
|
6
|
+
if (!node || typeof node !== "object") return false;
|
|
7
|
+
return "route" in node || "meta" in node;
|
|
8
|
+
}
|
|
9
|
+
async function generateContract(router) {
|
|
10
|
+
const contract = minifyContractRouter(createOrpcRouter(router));
|
|
11
|
+
const walk = (node) => {
|
|
12
|
+
const out = {};
|
|
13
|
+
for (const [key, value] of Object.entries(node)) if (isContractProcedure(value)) {
|
|
14
|
+
const { route, meta } = value["~orpc"];
|
|
15
|
+
const scope = meta?.scope ?? null;
|
|
16
|
+
if (!scope) throw new Error("Scope not defined in procedure metadata.");
|
|
17
|
+
out[key] = {
|
|
18
|
+
scope,
|
|
19
|
+
route: route ?? {}
|
|
20
|
+
};
|
|
21
|
+
} else out[key] = walk(value);
|
|
22
|
+
return out;
|
|
23
|
+
};
|
|
24
|
+
return walk(contract);
|
|
25
|
+
}
|
|
26
|
+
function restoreContract(contract) {
|
|
27
|
+
const walk = (node) => {
|
|
28
|
+
const out = {};
|
|
29
|
+
for (const [key, value] of Object.entries(node)) if (isContractProcedure$1(value)) {
|
|
30
|
+
const procedure = value;
|
|
31
|
+
out[key] = { "~orpc": {
|
|
32
|
+
meta: {},
|
|
33
|
+
errorMap: {},
|
|
34
|
+
route: procedure.route ?? {}
|
|
35
|
+
} };
|
|
36
|
+
} else out[key] = walk(value);
|
|
37
|
+
return out;
|
|
38
|
+
};
|
|
39
|
+
return walk(contract);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
//#endregion
|
|
43
|
+
export { generateContract, isContractProcedure$1 as isContractProcedure, restoreContract };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { BinaryLike } from "node:crypto";
|
|
2
|
+
|
|
3
|
+
//#region src/shared/crypto.d.ts
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Compares two strings in a timing-safe manner to prevent timing attacks.
|
|
7
|
+
*
|
|
8
|
+
* @param a - The first string to compare.
|
|
9
|
+
* @param b - The second string to compare.
|
|
10
|
+
* @returns `true` if the strings match, `false` otherwise.
|
|
11
|
+
*/
|
|
12
|
+
declare function compare(a: string, b: string): boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Hashes a secret API key with SHA-256 for secure database storage.
|
|
15
|
+
* Safe to use in place of bcrypt/argon2 because API keys are high-entropy
|
|
16
|
+
* random strings, unlike user-chosen passwords.
|
|
17
|
+
* Only secret keys should be hashed — publishable keys are not sensitive.
|
|
18
|
+
*
|
|
19
|
+
* @param key - The raw secret API key to hash.
|
|
20
|
+
* @returns A SHA-256 hex string suitable for database storage.
|
|
21
|
+
*/
|
|
22
|
+
declare function hash(key: string): string;
|
|
23
|
+
/**
|
|
24
|
+
* Create hmac using node crypto lib.
|
|
25
|
+
*/
|
|
26
|
+
declare function hmac(key: string, value: BinaryLike): string;
|
|
27
|
+
//#endregion
|
|
28
|
+
export { compare, hash, hmac };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { createHash, createHmac, timingSafeEqual } from "node:crypto";
|
|
2
|
+
|
|
3
|
+
//#region src/shared/crypto.ts
|
|
4
|
+
/**
|
|
5
|
+
* Compares two strings in a timing-safe manner to prevent timing attacks.
|
|
6
|
+
*
|
|
7
|
+
* @param a - The first string to compare.
|
|
8
|
+
* @param b - The second string to compare.
|
|
9
|
+
* @returns `true` if the strings match, `false` otherwise.
|
|
10
|
+
*/
|
|
11
|
+
function compare(a, b) {
|
|
12
|
+
return timingSafeEqual(Buffer.from(a), Buffer.from(b));
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Hashes a secret API key with SHA-256 for secure database storage.
|
|
16
|
+
* Safe to use in place of bcrypt/argon2 because API keys are high-entropy
|
|
17
|
+
* random strings, unlike user-chosen passwords.
|
|
18
|
+
* Only secret keys should be hashed — publishable keys are not sensitive.
|
|
19
|
+
*
|
|
20
|
+
* @param key - The raw secret API key to hash.
|
|
21
|
+
* @returns A SHA-256 hex string suitable for database storage.
|
|
22
|
+
*/
|
|
23
|
+
function hash(key) {
|
|
24
|
+
return createHash("sha256").update(key).digest("hex");
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Create hmac using node crypto lib.
|
|
28
|
+
*/
|
|
29
|
+
function hmac(key, value) {
|
|
30
|
+
return createHmac("sha256", key).update(value).digest("hex");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
//#endregion
|
|
34
|
+
export { compare, hash, hmac };
|