@haruhimemoe/next-kit 0.5.0 → 0.6.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/CHANGELOG.md +14 -1
- package/README.md +105 -4
- package/dist/check/cli.d.ts +5 -5
- package/dist/check/cli.js +11 -9
- package/dist/check/standards.d.ts +15 -8
- package/dist/check/standards.js +66 -32
- package/dist/docs/crawl.d.ts +81 -0
- package/dist/docs/crawl.js +115 -0
- package/dist/docs/files/index.d.ts +43 -0
- package/dist/docs/files/index.js +74 -0
- package/dist/docs/index.d.ts +15 -0
- package/dist/docs/index.js +15 -0
- package/dist/docs/markdown-segments.d.ts +37 -0
- package/dist/docs/markdown-segments.js +147 -0
- package/dist/docs/markdown.d.ts +31 -0
- package/dist/docs/markdown.js +117 -0
- package/dist/docs/registry.d.ts +94 -0
- package/dist/docs/registry.js +102 -0
- package/package.json +10 -2
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file src/docs/registry.ts
|
|
3
|
+
* @desc The content registry every haruhime.moe site builds its docs, guides and legal pages
|
|
4
|
+
* from: a frozen list of sections, the entry shape each page fills in, the app-made extra
|
|
5
|
+
* entries a section's nav and search also carry (like bb's tag pages), and the validation
|
|
6
|
+
* that catches a bad slug, a duplicate, a made-up date or a blank title at build time
|
|
7
|
+
* instead of at a 404. Pure: no node: imports, so it runs anywhere.
|
|
8
|
+
* @author David @dvhsh (https://dvh.sh)
|
|
9
|
+
* @created Sun Oct 4, 2026
|
|
10
|
+
* @modified Sun Oct 4, 2026
|
|
11
|
+
*/
|
|
12
|
+
/** A slug is lowercase words separated by single hyphens, like "make-a-pack". */
|
|
13
|
+
const SLUG_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
14
|
+
/** A date is YYYY-MM-DD and a real calendar day. */
|
|
15
|
+
const DATE_PATTERN = /^\d{4}-\d{2}-\d{2}$/;
|
|
16
|
+
/** Every content section a haruhime.moe site can have, in the order it's shown. */
|
|
17
|
+
export const CONTENT_SECTIONS = ["docs", "guides", "legal"];
|
|
18
|
+
/** The nav label for each section. */
|
|
19
|
+
export const SECTION_LABELS = {
|
|
20
|
+
docs: "Docs",
|
|
21
|
+
guides: "Guides",
|
|
22
|
+
legal: "Legal",
|
|
23
|
+
};
|
|
24
|
+
const assertEntries = (section, entries) => {
|
|
25
|
+
const seen = new Set();
|
|
26
|
+
for (const entry of entries) {
|
|
27
|
+
if (!SLUG_PATTERN.test(entry.slug))
|
|
28
|
+
throw new Error(`docs: bad slug "${entry.slug}" in ${section}`);
|
|
29
|
+
if (seen.has(entry.slug))
|
|
30
|
+
throw new Error(`docs: duplicate slug "${entry.slug}" in ${section}`);
|
|
31
|
+
seen.add(entry.slug);
|
|
32
|
+
if (!DATE_PATTERN.test(entry.lastUpdated) || Number.isNaN(Date.parse(entry.lastUpdated)))
|
|
33
|
+
throw new Error(`docs: bad lastUpdated "${entry.lastUpdated}" in ${section} "${entry.slug}"`);
|
|
34
|
+
if (!entry.title.trim())
|
|
35
|
+
throw new Error(`docs: blank title in ${section} "${entry.slug}"`);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
const assertExtras = (section, extras) => {
|
|
39
|
+
const seen = new Set();
|
|
40
|
+
for (const extra of extras) {
|
|
41
|
+
if (!extra.href.startsWith("/"))
|
|
42
|
+
throw new Error(`docs: extra href "${extra.href}" must start with "/" in ${section}`);
|
|
43
|
+
if (seen.has(extra.href))
|
|
44
|
+
throw new Error(`docs: duplicate extra href "${extra.href}" in ${section}`);
|
|
45
|
+
seen.add(extra.href);
|
|
46
|
+
if (!extra.title.trim())
|
|
47
|
+
throw new Error(`docs: blank title for extra "${extra.href}" in ${section}`);
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* @function defineContent
|
|
52
|
+
* @param input {ContentInput} each section's entries, and the app-made extras under `extra`
|
|
53
|
+
* @returns {Content} every section filled in (empty arrays for the ones left out), and
|
|
54
|
+
* `sections` listing only the non-empty ones, in `CONTENT_SECTIONS` order
|
|
55
|
+
* @throws {Error} naming the section and slug or extra href, for a bad slug, a duplicate slug,
|
|
56
|
+
* a duplicate extra href, a `lastUpdated` that isn't a real `YYYY-MM-DD` date, or a blank title
|
|
57
|
+
*/
|
|
58
|
+
export const defineContent = (input) => {
|
|
59
|
+
const entries = {};
|
|
60
|
+
const extra = {};
|
|
61
|
+
const sections = [];
|
|
62
|
+
for (const section of CONTENT_SECTIONS) {
|
|
63
|
+
const sectionEntries = input[section] ?? [];
|
|
64
|
+
const sectionExtras = input.extra?.[section] ?? [];
|
|
65
|
+
assertEntries(section, sectionEntries);
|
|
66
|
+
assertExtras(section, sectionExtras);
|
|
67
|
+
entries[section] = sectionEntries;
|
|
68
|
+
extra[section] = sectionExtras;
|
|
69
|
+
if (sectionEntries.length > 0 || sectionExtras.length > 0)
|
|
70
|
+
sections.push(section);
|
|
71
|
+
}
|
|
72
|
+
return { sections, entries, extra };
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* @function contentPath
|
|
76
|
+
* @param section {ContentSection} the section the page lives under
|
|
77
|
+
* @param slug {string} the entry's slug
|
|
78
|
+
* @returns {string} the page's path, like "/guides/make-a-pack"
|
|
79
|
+
*/
|
|
80
|
+
export const contentPath = (section, slug) => `/${section}/${slug}`;
|
|
81
|
+
/**
|
|
82
|
+
* @function markdownPath
|
|
83
|
+
* @param section {ContentSection} the section the page lives under
|
|
84
|
+
* @param slug {string} the entry's slug
|
|
85
|
+
* @returns {string} the page's raw markdown path, like "/guides/make-a-pack.md"
|
|
86
|
+
*/
|
|
87
|
+
export const markdownPath = (section, slug) => `${contentPath(section, slug)}.md`;
|
|
88
|
+
/**
|
|
89
|
+
* @function findEntry
|
|
90
|
+
* @param content {Content} a registry from `defineContent`
|
|
91
|
+
* @param section {ContentSection} the section to search
|
|
92
|
+
* @param slug {string} the entry's slug
|
|
93
|
+
* @returns {ContentEntry | undefined} the matching entry, or undefined
|
|
94
|
+
*/
|
|
95
|
+
export const findEntry = (content, section, slug) => content.entries[section].find((entry) => entry.slug === slug);
|
|
96
|
+
/**
|
|
97
|
+
* @function contentParams
|
|
98
|
+
* @param content {Content} a registry from `defineContent`
|
|
99
|
+
* @param section {ContentSection} the section to list
|
|
100
|
+
* @returns {{ slug: string }[]} one param per entry, for a dynamic route's `generateStaticParams`
|
|
101
|
+
*/
|
|
102
|
+
export const contentParams = (content, section) => content.entries[section].map((entry) => ({ slug: entry.slug }));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@haruhimemoe/next-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "The Next.js plumbing the haruhime.moe tools share: JSON route helpers, rate limits and budgets in MongoDB, bearer machine auth, zod env parsing, a connect-once MongoDB client with safe index builds, better-auth with osu! sign-in, the signed-in marker and account store for the browser, SEO: metadata, robots.txt, sitemaps, JSON-LD and llms.txt, and per-app API keys and the /api/v1 guard.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"nextjs",
|
|
@@ -62,6 +62,14 @@
|
|
|
62
62
|
"types": "./dist/api-keys/index.d.ts",
|
|
63
63
|
"default": "./dist/api-keys/index.js"
|
|
64
64
|
},
|
|
65
|
+
"./docs": {
|
|
66
|
+
"types": "./dist/docs/index.d.ts",
|
|
67
|
+
"default": "./dist/docs/index.js"
|
|
68
|
+
},
|
|
69
|
+
"./docs/files": {
|
|
70
|
+
"types": "./dist/docs/files/index.d.ts",
|
|
71
|
+
"default": "./dist/docs/files/index.js"
|
|
72
|
+
},
|
|
65
73
|
"./package.json": "./package.json"
|
|
66
74
|
},
|
|
67
75
|
"files": [
|
|
@@ -92,7 +100,7 @@
|
|
|
92
100
|
},
|
|
93
101
|
"peerDependencies": {
|
|
94
102
|
"@haruhimemoe/osu": "^0.2.0 || ^0.3.0 || ^0.4.0",
|
|
95
|
-
"@haruhimemoe/ui": "^0.5.0 || ^0.6.0 || ^0.7.0 || ^0.8.0 || ^0.9.0",
|
|
103
|
+
"@haruhimemoe/ui": "^0.5.0 || ^0.6.0 || ^0.7.0 || ^0.8.0 || ^0.9.0 || ^0.10.0",
|
|
96
104
|
"better-auth": "^1.7.5",
|
|
97
105
|
"mongodb": "^7.6.0",
|
|
98
106
|
"mongodb-memory-server": "^11.3.0",
|