@lupinum/ginko-content 1.0.0-beta.4 → 1.0.0-beta.6
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/README.md +4 -4
- package/dist/module.d.mts +11 -2
- package/dist/module.json +1 -1
- package/dist/module.mjs +61 -17
- package/dist/runtime/server/api/agent-raw.js +7 -3
- package/dist/runtime/server/plugins/agent-errors.js +5 -8
- package/dist/types/module.d.ts +11 -2
- package/dist/web-types.json +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
</p>
|
|
16
16
|
|
|
17
17
|
> [!WARNING]
|
|
18
|
-
> Version `1.0.0-beta.
|
|
18
|
+
> Version `1.0.0-beta.6` is a prerelease. Install it from npm's `next`
|
|
19
19
|
> channel. The stable `0.3` line remains on `latest`.
|
|
20
20
|
|
|
21
21
|
## Why use this package?
|
|
@@ -32,7 +32,7 @@ output.
|
|
|
32
32
|
|
|
33
33
|
- Node.js 22.18–22.x, 24.11–24.x, or 26+
|
|
34
34
|
- Nuxt 4.5.1 through Nuxt 4.x
|
|
35
|
-
- Vue 3.5.
|
|
35
|
+
- Vue 3.5.40 through Vue 3.x
|
|
36
36
|
- ESM; CommonJS `require()` is not supported
|
|
37
37
|
|
|
38
38
|
## Installation
|
|
@@ -40,13 +40,13 @@ output.
|
|
|
40
40
|
Use the Nuxt CLI:
|
|
41
41
|
|
|
42
42
|
```bash
|
|
43
|
-
npx nuxi module add @lupinum/ginko-content@1.0.0-beta.
|
|
43
|
+
npx nuxi module add @lupinum/ginko-content@1.0.0-beta.6
|
|
44
44
|
```
|
|
45
45
|
|
|
46
46
|
Or install and register the module by hand:
|
|
47
47
|
|
|
48
48
|
```bash
|
|
49
|
-
pnpm add @lupinum/ginko-content@1.0.0-beta.
|
|
49
|
+
pnpm add @lupinum/ginko-content@1.0.0-beta.6
|
|
50
50
|
```
|
|
51
51
|
|
|
52
52
|
```ts
|
package/dist/module.d.mts
CHANGED
|
@@ -850,8 +850,17 @@ type ContentLinksOptions = Record<string, Record<string, ContentLinkRouteTarget>
|
|
|
850
850
|
interface ContentAgentRouteOptions {
|
|
851
851
|
routes?: boolean;
|
|
852
852
|
linkHeaders?: boolean;
|
|
853
|
-
|
|
854
|
-
|
|
853
|
+
/**
|
|
854
|
+
* Select static page delivery or request-time Nitro negotiation.
|
|
855
|
+
*
|
|
856
|
+
* `static` prerenders public pages and keeps raw Markdown as the canonical
|
|
857
|
+
* agent representation. `runtime` lets Nitro crawl pages to retain their
|
|
858
|
+
* data dependencies, then removes the generated HTML so Nitro can negotiate
|
|
859
|
+
* each page request.
|
|
860
|
+
*
|
|
861
|
+
* @default 'static'
|
|
862
|
+
*/
|
|
863
|
+
delivery?: 'static' | 'runtime';
|
|
855
864
|
}
|
|
856
865
|
interface ModuleOptions {
|
|
857
866
|
api: {
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { addTypeTemplate, addImports, addServerImports, addComponentsDir, getLayerDirectories, addPlugin, addServerHandler, hasNuxtModule, defineNuxtModule, createResolver, useLogger, addTemplate, resolvePath } from '@nuxt/kit';
|
|
2
2
|
import { readFile, readdir, rm } from 'node:fs/promises';
|
|
3
|
-
import { join as join$1, basename, resolve as resolve$1, dirname, isAbsolute as isAbsolute$1 } from 'node:path';
|
|
3
|
+
import { join as join$1, basename, resolve as resolve$1, dirname, relative as relative$1, isAbsolute as isAbsolute$1 } from 'node:path';
|
|
4
4
|
import { buildResolvedContentContract } from '../dist/cms-contract/index.js';
|
|
5
5
|
import { writeResolvedContentContractArtifact } from '../dist/cms-contract-node/artifact.js';
|
|
6
6
|
import fs, { existsSync } from 'fs';
|
|
@@ -34,7 +34,7 @@ import { assertCanonicalHighlightOptionNames } from '../dist/parsers/markdown-pl
|
|
|
34
34
|
import { BUILTIN_MARKDOWN_RENDER_CONTRACTS } from '../dist/core/markdown/builtin-render-contracts.js';
|
|
35
35
|
|
|
36
36
|
const name = "@lupinum/ginko-content";
|
|
37
|
-
const version = "1.0.0-beta.
|
|
37
|
+
const version = "1.0.0-beta.6";
|
|
38
38
|
const peerDependencies = {
|
|
39
39
|
nuxt: ">=4.5.1 <5"};
|
|
40
40
|
|
|
@@ -439,15 +439,13 @@ const normalizeAgentRouteOptions = (options) => {
|
|
|
439
439
|
return {
|
|
440
440
|
routes: false,
|
|
441
441
|
linkHeaders: false,
|
|
442
|
-
|
|
443
|
-
prerender: false
|
|
442
|
+
delivery: "static"
|
|
444
443
|
};
|
|
445
444
|
}
|
|
446
445
|
return {
|
|
447
446
|
routes: options.agent?.routes !== false,
|
|
448
447
|
linkHeaders: options.agent?.linkHeaders !== false,
|
|
449
|
-
|
|
450
|
-
prerender: options.agent?.prerender !== false
|
|
448
|
+
delivery: options.agent?.delivery || "static"
|
|
451
449
|
};
|
|
452
450
|
};
|
|
453
451
|
|
|
@@ -540,7 +538,7 @@ const registerContentServerHandlers = (nuxt, options, resolveRuntimeModule, buil
|
|
|
540
538
|
handler: resolveRuntimeModule("./server/api/agent-raw.js")
|
|
541
539
|
});
|
|
542
540
|
}
|
|
543
|
-
if (agent.
|
|
541
|
+
if (agent.delivery === "runtime") {
|
|
544
542
|
addServerHandler({
|
|
545
543
|
middleware: true,
|
|
546
544
|
handler: resolveRuntimeModule("./server/middleware/agent-markdown.js")
|
|
@@ -1111,6 +1109,7 @@ const collectRawMarkdownRoutesFromGeneratedFrontmatter = (markdown) => {
|
|
|
1111
1109
|
};
|
|
1112
1110
|
const publicOutputPath = (publicDir, route) => join(publicDir, normalizeStaticRoutePath(route).replace(/^\//, ""));
|
|
1113
1111
|
|
|
1112
|
+
const runtimeModuleImportURL = (modulePath, toFileURL = pathToFileURL) => toFileURL(modulePath).href;
|
|
1114
1113
|
const assertPrerenderedContentSnapshot = (buildDir) => {
|
|
1115
1114
|
const snapshotPath = resolve$1(buildDir, "content-cache/snapshot.json");
|
|
1116
1115
|
let raw;
|
|
@@ -1168,12 +1167,12 @@ ${details}` : "")
|
|
|
1168
1167
|
writeFileSync(indexPath, json, "utf8");
|
|
1169
1168
|
if (searchRuntime.engine === "pagefind") {
|
|
1170
1169
|
const records = JSON.parse(json);
|
|
1171
|
-
const { writePagefindIndex } = await import(resolveRuntimeModule("./server/pagefind.js"));
|
|
1170
|
+
const { writePagefindIndex } = await import(runtimeModuleImportURL(resolveRuntimeModule("./server/pagefind.js")));
|
|
1172
1171
|
await writePagefindIndex(records, resolve$1(publicDir, "pagefind"), resolvedI18n.defaultLocale);
|
|
1173
1172
|
}
|
|
1174
1173
|
}
|
|
1175
1174
|
const agentRoutes = normalizeAgentRouteOptions(options);
|
|
1176
|
-
if (agentRoutes.routes &&
|
|
1175
|
+
if (agentRoutes.routes && appContentConfig.agent) {
|
|
1177
1176
|
const defaultLocale = resolvedI18n.defaultLocale;
|
|
1178
1177
|
const locales = resolvedI18n.locales;
|
|
1179
1178
|
const llmsRoutes = [
|
|
@@ -1306,17 +1305,33 @@ const registerContentNitroIntegrationHooks = (nitroConfig, options, contentConte
|
|
|
1306
1305
|
};
|
|
1307
1306
|
nitroConfig.hooks ||= {};
|
|
1308
1307
|
const usesFilesystemProviderAtRegistration = !contentContext.provider || contentContext.provider === "filesystem";
|
|
1309
|
-
if (usesFilesystemProviderAtRegistration) {
|
|
1308
|
+
if (usesFilesystemProviderAtRegistration || options.removePrerenderedHtml) {
|
|
1310
1309
|
let prerenderingPublicDir;
|
|
1311
1310
|
appendHook(nitroConfig.hooks, "prerender:init", async (nitro) => {
|
|
1312
1311
|
prerenderingPublicDir = nitro.options.output.publicDir;
|
|
1313
1312
|
});
|
|
1314
|
-
appendHook(nitroConfig.hooks, "prerender:done", async () => {
|
|
1313
|
+
appendHook(nitroConfig.hooks, "prerender:done", async (result) => {
|
|
1315
1314
|
if (!prerenderingPublicDir) {
|
|
1316
1315
|
return;
|
|
1317
1316
|
}
|
|
1318
|
-
const
|
|
1319
|
-
|
|
1317
|
+
const publicDir = prerenderingPublicDir;
|
|
1318
|
+
if (usesFilesystemProviderAtRegistration) {
|
|
1319
|
+
const staleArtifactPath = join$1(publicDir, options.cacheRoute);
|
|
1320
|
+
await rm(staleArtifactPath, { recursive: true, force: true });
|
|
1321
|
+
}
|
|
1322
|
+
if (options.removePrerenderedHtml) {
|
|
1323
|
+
await Promise.all((result.prerenderedRoutes || []).map(async (route) => {
|
|
1324
|
+
if (!route.contentType?.includes("text/html") || !route.fileName) {
|
|
1325
|
+
return;
|
|
1326
|
+
}
|
|
1327
|
+
const artifactPath = resolve$1(publicDir, route.fileName.replace(/^\/+/, ""));
|
|
1328
|
+
const relativeArtifactPath = relative$1(publicDir, artifactPath);
|
|
1329
|
+
if (relativeArtifactPath.startsWith("..") || isAbsolute$1(relativeArtifactPath)) {
|
|
1330
|
+
throw new Error(`[content] refused to remove a prerendered HTML artifact outside publicDir: ${route.fileName}`);
|
|
1331
|
+
}
|
|
1332
|
+
await rm(artifactPath, { recursive: true, force: true });
|
|
1333
|
+
}));
|
|
1334
|
+
}
|
|
1320
1335
|
});
|
|
1321
1336
|
}
|
|
1322
1337
|
if (contentContext.sitemap && contentContext.sitemap.assert?.enabled) {
|
|
@@ -1350,6 +1365,29 @@ const registerContentNitroIntegrationHooks = (nitroConfig, options, contentConte
|
|
|
1350
1365
|
});
|
|
1351
1366
|
};
|
|
1352
1367
|
|
|
1368
|
+
const NUXT_NITRO_CACHE_DRIVER_SUFFIX = "/node_modules/@nuxt/nitro-server/dist/runtime/utils/cache-driver.mjs";
|
|
1369
|
+
const createNuxtWindowsCacheDriverResolver = (platform = process.platform, fromFileURL = fileURLToPath) => {
|
|
1370
|
+
if (platform !== "win32") {
|
|
1371
|
+
return void 0;
|
|
1372
|
+
}
|
|
1373
|
+
return {
|
|
1374
|
+
name: "ginko-content:nuxt-windows-cache-driver",
|
|
1375
|
+
resolveId(id) {
|
|
1376
|
+
if (!id.startsWith("file:")) {
|
|
1377
|
+
return null;
|
|
1378
|
+
}
|
|
1379
|
+
try {
|
|
1380
|
+
const url = new URL(id);
|
|
1381
|
+
if (url.protocol !== "file:" || url.hostname !== "" || url.search !== "" || url.hash !== "" || !url.pathname.toLowerCase().endsWith(NUXT_NITRO_CACHE_DRIVER_SUFFIX)) {
|
|
1382
|
+
return null;
|
|
1383
|
+
}
|
|
1384
|
+
return fromFileURL(url);
|
|
1385
|
+
} catch {
|
|
1386
|
+
return null;
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
};
|
|
1390
|
+
};
|
|
1353
1391
|
const hookNuxtBoundary$1 = (nuxt, name, callback) => {
|
|
1354
1392
|
const hook = nuxt.hook;
|
|
1355
1393
|
hook(name, callback);
|
|
@@ -1369,6 +1407,7 @@ const registerContentNitroConfig = ({
|
|
|
1369
1407
|
}) => {
|
|
1370
1408
|
hookNuxtBoundary$1(nuxt, "nitro:config", (nitroConfig) => {
|
|
1371
1409
|
const searchRuntime = getSearchRuntime();
|
|
1410
|
+
const agentRoutes = normalizeAgentRouteOptions(options);
|
|
1372
1411
|
nitroConfig.prerender = nitroConfig.prerender || {};
|
|
1373
1412
|
nitroConfig.prerender.routes = nitroConfig.prerender.routes || [];
|
|
1374
1413
|
const usesFilesystemProvider = !contentContext.provider || contentContext.provider === "filesystem";
|
|
@@ -1399,6 +1438,12 @@ const registerContentNitroConfig = ({
|
|
|
1399
1438
|
}
|
|
1400
1439
|
nitroConfig.bundledStorage = nitroConfig.bundledStorage || [];
|
|
1401
1440
|
nitroConfig.bundledStorage.push("cache:content");
|
|
1441
|
+
const windowsCacheDriverResolver = createNuxtWindowsCacheDriverResolver();
|
|
1442
|
+
if (windowsCacheDriverResolver) {
|
|
1443
|
+
nitroConfig.rollupConfig ||= {};
|
|
1444
|
+
nitroConfig.rollupConfig.plugins ||= [];
|
|
1445
|
+
nitroConfig.rollupConfig.plugins.push(windowsCacheDriverResolver);
|
|
1446
|
+
}
|
|
1402
1447
|
nitroConfig.externals = defu(typeof nitroConfig.externals === "object" ? nitroConfig.externals : {}, {
|
|
1403
1448
|
inline: [
|
|
1404
1449
|
resolveModuleFile("."),
|
|
@@ -1432,6 +1477,7 @@ const registerContentNitroConfig = ({
|
|
|
1432
1477
|
}
|
|
1433
1478
|
registerContentNitroIntegrationHooks(nitroConfig, {
|
|
1434
1479
|
cacheRoute,
|
|
1480
|
+
removePrerenderedHtml: agentRoutes.delivery === "runtime" && !("_generate" in nuxt.options && nuxt.options._generate === true),
|
|
1435
1481
|
sitemapPrerenderRoutes: () => contentContext.sitemap === false ? [] : resolveNuxtSitemapPrerenderRoutes(nuxt),
|
|
1436
1482
|
resolveContentContext: () => {
|
|
1437
1483
|
const resolved = getResolvedContentContext();
|
|
@@ -1441,7 +1487,6 @@ const registerContentNitroConfig = ({
|
|
|
1441
1487
|
sitemap: contentContext.sitemap,
|
|
1442
1488
|
provider: contentContext.provider
|
|
1443
1489
|
});
|
|
1444
|
-
const agentRoutes = normalizeAgentRouteOptions(options);
|
|
1445
1490
|
if (agentRoutes.routes && appContentConfig.agent) {
|
|
1446
1491
|
nitroConfig.plugins ||= [];
|
|
1447
1492
|
const agentErrorsPlugin = resolveRuntimeModule("server/plugins/agent-errors.js");
|
|
@@ -1449,7 +1494,7 @@ const registerContentNitroConfig = ({
|
|
|
1449
1494
|
nitroConfig.plugins.push(agentErrorsPlugin);
|
|
1450
1495
|
}
|
|
1451
1496
|
}
|
|
1452
|
-
if (agentRoutes.routes &&
|
|
1497
|
+
if (agentRoutes.routes && appContentConfig.agent) {
|
|
1453
1498
|
nitroConfig.prerender.routes.push("/llms.txt", "/llms-full.txt");
|
|
1454
1499
|
for (const locale of resolvedI18n.locales || []) {
|
|
1455
1500
|
if (locale && locale !== resolvedI18n.defaultLocale) {
|
|
@@ -1658,8 +1703,7 @@ const contentModuleDefaults = {
|
|
|
1658
1703
|
agent: {
|
|
1659
1704
|
routes: true,
|
|
1660
1705
|
linkHeaders: true,
|
|
1661
|
-
|
|
1662
|
-
prerender: true
|
|
1706
|
+
delivery: "static"
|
|
1663
1707
|
},
|
|
1664
1708
|
respectPathCase: false
|
|
1665
1709
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { createError, defineEventHandler } from "h3";
|
|
1
|
+
import { createError, defineEventHandler, setResponseStatus } from "h3";
|
|
2
2
|
import { localeFromAgentPath, resolveMarkdownForPublicRoute } from "../agent-site.js";
|
|
3
|
-
import { setAgentMarkdownHeaders } from "../agent-http.js";
|
|
3
|
+
import { addVaryHeader, renderAgentNotFoundMarkdown, setAgentMarkdownHeaders } from "../agent-http.js";
|
|
4
4
|
import { agentRoutePathFromRawSlug, isUnsafeAgentRoutePath } from "../../../features/agent/agent-paths.js";
|
|
5
5
|
export default defineEventHandler(async (event) => {
|
|
6
6
|
const routePath = agentRoutePathFromRawSlug(event.context.params?.slug);
|
|
@@ -10,8 +10,12 @@ export default defineEventHandler(async (event) => {
|
|
|
10
10
|
const locale = localeFromAgentPath(routePath);
|
|
11
11
|
const page = await resolveMarkdownForPublicRoute(event, routePath, locale);
|
|
12
12
|
if (!page) {
|
|
13
|
-
|
|
13
|
+
setResponseStatus(event, 404, "Markdown page not found");
|
|
14
|
+
addVaryHeader(event, "accept");
|
|
15
|
+
setAgentMarkdownHeaders(event, { noindex: true });
|
|
16
|
+
return renderAgentNotFoundMarkdown(routePath);
|
|
14
17
|
}
|
|
18
|
+
addVaryHeader(event, "accept");
|
|
15
19
|
setAgentMarkdownHeaders(event, { noindex: true });
|
|
16
20
|
return page.markdown;
|
|
17
21
|
});
|
|
@@ -7,17 +7,14 @@ import {
|
|
|
7
7
|
shouldSkipAgentMarkdownPath
|
|
8
8
|
} from "../agent-http.js";
|
|
9
9
|
export default defineNitroPlugin((nitro) => {
|
|
10
|
-
nitro.hooks.hook("
|
|
10
|
+
nitro.hooks.hook("beforeResponse", (event, response) => {
|
|
11
11
|
const { pathname } = getRequestURL(event);
|
|
12
|
-
if (
|
|
12
|
+
if (event.node.res.statusCode !== 404 || shouldSkipAgentMarkdownPath(pathname) || !acceptsMarkdown(event)) {
|
|
13
13
|
return;
|
|
14
14
|
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
"x-robots-tag": "noindex",
|
|
19
|
-
"vary": mergeVaryHeader(response.headers?.vary, "accept")
|
|
20
|
-
};
|
|
15
|
+
event.node.res.setHeader("content-type", "text/markdown; charset=utf-8");
|
|
16
|
+
event.node.res.setHeader("x-robots-tag", "noindex");
|
|
17
|
+
event.node.res.setHeader("vary", mergeVaryHeader(event.node.res.getHeader("vary"), "accept"));
|
|
21
18
|
response.body = renderAgentNotFoundMarkdown(pathname);
|
|
22
19
|
});
|
|
23
20
|
});
|
package/dist/types/module.d.ts
CHANGED
|
@@ -296,8 +296,17 @@ export interface ContentValidationRouteFacts {
|
|
|
296
296
|
export interface ContentAgentRouteOptions {
|
|
297
297
|
routes?: boolean;
|
|
298
298
|
linkHeaders?: boolean;
|
|
299
|
-
|
|
300
|
-
|
|
299
|
+
/**
|
|
300
|
+
* Select static page delivery or request-time Nitro negotiation.
|
|
301
|
+
*
|
|
302
|
+
* `static` prerenders public pages and keeps raw Markdown as the canonical
|
|
303
|
+
* agent representation. `runtime` lets Nitro crawl pages to retain their
|
|
304
|
+
* data dependencies, then removes the generated HTML so Nitro can negotiate
|
|
305
|
+
* each page request.
|
|
306
|
+
*
|
|
307
|
+
* @default 'static'
|
|
308
|
+
*/
|
|
309
|
+
delivery?: 'static' | 'runtime';
|
|
301
310
|
}
|
|
302
311
|
export interface ModuleOptions {
|
|
303
312
|
api: {
|
package/dist/web-types.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lupinum/ginko-content",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.6",
|
|
4
4
|
"description": "Collection-first content engine for Nuxt",
|
|
5
5
|
"homepage": "https://ginko-content.lupinum.com",
|
|
6
6
|
"bugs": {
|
|
@@ -172,7 +172,7 @@
|
|
|
172
172
|
"nuxt": ">=4.5.1 <5",
|
|
173
173
|
"pagefind": "^1.5.2",
|
|
174
174
|
"vitest": ">=4.1.6 <5",
|
|
175
|
-
"vue": "^3.5.
|
|
175
|
+
"vue": "^3.5.40"
|
|
176
176
|
},
|
|
177
177
|
"peerDependenciesMeta": {
|
|
178
178
|
"beautiful-mermaid": {
|