@krantho/server 0.0.0-stage → 1.0.1
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/dist/cdn/cloudfront.d.ts +9 -0
- package/dist/cdn/cloudfront.d.ts.map +1 -0
- package/dist/cdn/cloudfront.js +35 -0
- package/dist/cdn/cloudfront.js.map +1 -0
- package/dist/cdn/gcp.d.ts +15 -0
- package/dist/cdn/gcp.d.ts.map +1 -0
- package/dist/cdn/gcp.js +60 -0
- package/dist/cdn/gcp.js.map +1 -0
- package/dist/cdn/index.d.ts +9 -0
- package/dist/cdn/index.d.ts.map +1 -0
- package/dist/cdn/index.js +30 -0
- package/dist/cdn/index.js.map +1 -0
- package/dist/cdn/listener.d.ts +14 -0
- package/dist/cdn/listener.d.ts.map +1 -0
- package/dist/cdn/listener.js +53 -0
- package/dist/cdn/listener.js.map +1 -0
- package/dist/cdn/retry.d.ts +3 -0
- package/dist/cdn/retry.d.ts.map +1 -0
- package/dist/cdn/retry.js +17 -0
- package/dist/cdn/retry.js.map +1 -0
- package/dist/cdn/types.d.ts +12 -0
- package/dist/cdn/types.d.ts.map +1 -0
- package/dist/cdn/types.js +2 -0
- package/dist/cdn/types.js.map +1 -0
- package/dist/cdn/vercel.d.ts +9 -0
- package/dist/cdn/vercel.d.ts.map +1 -0
- package/dist/cdn/vercel.js +31 -0
- package/dist/cdn/vercel.js.map +1 -0
- package/dist/content-conflicts.d.ts +18 -0
- package/dist/content-conflicts.d.ts.map +1 -0
- package/dist/content-conflicts.js +22 -0
- package/dist/content-conflicts.js.map +1 -0
- package/dist/content-dates.d.ts +35 -0
- package/dist/content-dates.d.ts.map +1 -0
- package/dist/content-dates.js +85 -0
- package/dist/content-dates.js.map +1 -0
- package/dist/content-loader.d.ts +37 -0
- package/dist/content-loader.d.ts.map +1 -0
- package/dist/content-loader.js +223 -0
- package/dist/content-loader.js.map +1 -0
- package/dist/content-migration.d.ts +101 -0
- package/dist/content-migration.d.ts.map +1 -0
- package/dist/content-migration.js +291 -0
- package/dist/content-migration.js.map +1 -0
- package/dist/convert.d.ts +18 -0
- package/dist/convert.d.ts.map +1 -0
- package/dist/convert.js +152 -0
- package/dist/convert.js.map +1 -0
- package/dist/file-store.d.ts +11 -0
- package/dist/file-store.d.ts.map +1 -0
- package/dist/file-store.js +39 -0
- package/dist/file-store.js.map +1 -0
- package/dist/file-watcher.d.ts +19 -0
- package/dist/file-watcher.d.ts.map +1 -0
- package/dist/file-watcher.js +99 -0
- package/dist/file-watcher.js.map +1 -0
- package/dist/handler.d.ts +61 -0
- package/dist/handler.d.ts.map +1 -0
- package/dist/handler.js +197 -0
- package/dist/handler.js.map +1 -0
- package/dist/import.d.ts +25 -0
- package/dist/import.d.ts.map +1 -0
- package/dist/import.js +71 -0
- package/dist/import.js.map +1 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/indexnow.d.ts +38 -0
- package/dist/indexnow.d.ts.map +1 -0
- package/dist/indexnow.js +89 -0
- package/dist/indexnow.js.map +1 -0
- package/dist/lazy-handler.d.ts +7 -0
- package/dist/lazy-handler.d.ts.map +1 -0
- package/dist/lazy-handler.js +16 -0
- package/dist/lazy-handler.js.map +1 -0
- package/dist/llm-seed.d.ts +25 -0
- package/dist/llm-seed.d.ts.map +1 -0
- package/dist/llm-seed.js +42 -0
- package/dist/llm-seed.js.map +1 -0
- package/dist/lock-adapters.d.ts +21 -0
- package/dist/lock-adapters.d.ts.map +1 -0
- package/dist/lock-adapters.js +69 -0
- package/dist/lock-adapters.js.map +1 -0
- package/dist/manifest.d.ts +15 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +39 -0
- package/dist/manifest.js.map +1 -0
- package/dist/mime-types.d.ts +3 -0
- package/dist/mime-types.d.ts.map +1 -0
- package/dist/mime-types.js +15 -0
- package/dist/mime-types.js.map +1 -0
- package/dist/page-cache-rules.d.ts +41 -0
- package/dist/page-cache-rules.d.ts.map +1 -0
- package/dist/page-cache-rules.js +94 -0
- package/dist/page-cache-rules.js.map +1 -0
- package/dist/page-cache-store.d.ts +11 -0
- package/dist/page-cache-store.d.ts.map +1 -0
- package/dist/page-cache-store.js +46 -0
- package/dist/page-cache-store.js.map +1 -0
- package/dist/page-request.d.ts +26 -0
- package/dist/page-request.d.ts.map +1 -0
- package/dist/page-request.js +29 -0
- package/dist/page-request.js.map +1 -0
- package/dist/persistence.d.ts +36 -0
- package/dist/persistence.d.ts.map +1 -0
- package/dist/persistence.js +108 -0
- package/dist/persistence.js.map +1 -0
- package/dist/preview-token.d.ts +9 -0
- package/dist/preview-token.d.ts.map +1 -0
- package/dist/preview-token.js +14 -0
- package/dist/preview-token.js.map +1 -0
- package/dist/read-cache.d.ts +17 -0
- package/dist/read-cache.d.ts.map +1 -0
- package/dist/read-cache.js +33 -0
- package/dist/read-cache.js.map +1 -0
- package/dist/route-matcher.d.ts +21 -0
- package/dist/route-matcher.d.ts.map +1 -0
- package/dist/route-matcher.js +75 -0
- package/dist/route-matcher.js.map +1 -0
- package/dist/secret-vault.d.ts +20 -0
- package/dist/secret-vault.d.ts.map +1 -0
- package/dist/secret-vault.js +60 -0
- package/dist/secret-vault.js.map +1 -0
- package/dist/seo-config-io.d.ts +51 -0
- package/dist/seo-config-io.d.ts.map +1 -0
- package/dist/seo-config-io.js +151 -0
- package/dist/seo-config-io.js.map +1 -0
- package/dist/seo-seed.d.ts +12 -0
- package/dist/seo-seed.d.ts.map +1 -0
- package/dist/seo-seed.js +100 -0
- package/dist/seo-seed.js.map +1 -0
- package/dist/studio-gate.d.ts +25 -0
- package/dist/studio-gate.d.ts.map +1 -0
- package/dist/studio-gate.js +70 -0
- package/dist/studio-gate.js.map +1 -0
- package/dist/studio.d.ts +17 -0
- package/dist/studio.d.ts.map +1 -0
- package/dist/studio.js +110 -0
- package/dist/studio.js.map +1 -0
- package/dist/webhook-sweep.d.ts +22 -0
- package/dist/webhook-sweep.d.ts.map +1 -0
- package/dist/webhook-sweep.js +33 -0
- package/dist/webhook-sweep.js.map +1 -0
- package/package.json +89 -3
- package/README.md +0 -3
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { basename } from 'node:path';
|
|
3
|
+
import { documentChecksum, parseRepositoryContent, resolveCollectionContract, } from '@krantho/schema';
|
|
4
|
+
import matter from 'gray-matter';
|
|
5
|
+
/** Parse a repository edit and save a draft through the common document write path. */
|
|
6
|
+
async function importFile(filePath, collection, adapter, save) {
|
|
7
|
+
const raw = await readFile(filePath, 'utf-8');
|
|
8
|
+
const parsed = matter(raw);
|
|
9
|
+
const fields = parsed.data;
|
|
10
|
+
// Validate against collection schema (parity with auto-import)
|
|
11
|
+
const contract = resolveCollectionContract(collection, (await adapter.getRuntimeSchema?.(collection.name)) ?? null);
|
|
12
|
+
const result = parseRepositoryContent(collection, basename(filePath, '.md'), fields, parsed.content, collection.schema, contract);
|
|
13
|
+
if (!result.success) {
|
|
14
|
+
console.warn(`[watch] Skipping ${collection.name}/${basename(filePath, '.md')}: schema validation failed`);
|
|
15
|
+
for (const issue of result.error.issues) {
|
|
16
|
+
console.warn(` - ${issue.path.join('.') || '(root)'}: ${issue.message}`);
|
|
17
|
+
}
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const body = parsed.content;
|
|
21
|
+
const slug = basename(filePath, '.md');
|
|
22
|
+
const read = await adapter.getContent?.(collection.name, slug, result.data.locale);
|
|
23
|
+
const current = result.data.locale !== undefined && read?.meta.locale !== result.data.locale ? null : read;
|
|
24
|
+
if (current && current.repositoryBasis?.checksum !== documentChecksum(current)) {
|
|
25
|
+
console.warn(`[watch] ${collection.name}/${slug} has database edits; review an import before replacing them`);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
if (current && documentChecksum(current) === documentChecksum(result.data))
|
|
29
|
+
return;
|
|
30
|
+
const saved = await save({
|
|
31
|
+
collection: collection.name,
|
|
32
|
+
slug,
|
|
33
|
+
locale: result.data.locale,
|
|
34
|
+
fields: result.data.fields,
|
|
35
|
+
body,
|
|
36
|
+
metadata: result.data.metadata,
|
|
37
|
+
defaults: result.data.defaults,
|
|
38
|
+
repositoryBasis: result.data.repositoryBasis,
|
|
39
|
+
isNew: !current,
|
|
40
|
+
expectedVersion: current?.meta.version ?? 0,
|
|
41
|
+
});
|
|
42
|
+
if (!saved.ok)
|
|
43
|
+
throw new Error(`Repository edit rejected (${saved.response.status})`);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Start watching filesystem collection directories for changes.
|
|
47
|
+
*
|
|
48
|
+
* When a `.md` file is added or modified, it is parsed, validated against
|
|
49
|
+
* the collection contract, and saved as a draft through the common document write
|
|
50
|
+
* path. When a file is deleted, the document stays in the database until explicitly archived.
|
|
51
|
+
*
|
|
52
|
+
* Returns a cleanup function to stop watching.
|
|
53
|
+
*/
|
|
54
|
+
export async function startFileWatcher(options) {
|
|
55
|
+
const { contentConfig, adapter, saveDocument } = options;
|
|
56
|
+
// Build directory → collection mapping for filesystem collections
|
|
57
|
+
const dirToCollection = new Map();
|
|
58
|
+
for (const [, collection] of contentConfig.collections) {
|
|
59
|
+
if (collection.source === 'filesystem' && collection.directory) {
|
|
60
|
+
dirToCollection.set(collection.directory, collection);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
const dirs = [...dirToCollection.keys()];
|
|
64
|
+
if (dirs.length === 0)
|
|
65
|
+
return () => { };
|
|
66
|
+
// Dynamic import so chokidar is only loaded when watching is enabled
|
|
67
|
+
const { watch } = await import('chokidar');
|
|
68
|
+
const watcher = watch(dirs, {
|
|
69
|
+
ignoreInitial: true,
|
|
70
|
+
awaitWriteFinish: { stabilityThreshold: 100, pollInterval: 50 },
|
|
71
|
+
});
|
|
72
|
+
function findCollection(filePath) {
|
|
73
|
+
for (const [dir, col] of dirToCollection) {
|
|
74
|
+
if (filePath.startsWith(dir + '/') || filePath === dir)
|
|
75
|
+
return col;
|
|
76
|
+
}
|
|
77
|
+
return undefined;
|
|
78
|
+
}
|
|
79
|
+
function handleEvent(filePath, action) {
|
|
80
|
+
if (!filePath.endsWith('.md'))
|
|
81
|
+
return;
|
|
82
|
+
const collection = findCollection(filePath);
|
|
83
|
+
if (!collection)
|
|
84
|
+
return;
|
|
85
|
+
// A failed write is logged; a successful one is announced (onChange never throws)
|
|
86
|
+
action(filePath, collection, adapter, saveDocument).then(() => undefined, (err) => {
|
|
87
|
+
console.error(`[watch] Error processing ${filePath}:`, err);
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
watcher.on('add', (fp) => handleEvent(fp, importFile));
|
|
91
|
+
watcher.on('change', (fp) => handleEvent(fp, importFile));
|
|
92
|
+
watcher.on('error', (err) => console.error('[watch] File watcher error:', err));
|
|
93
|
+
const watchedDirs = dirs.map((d) => basename(d)).join(', ');
|
|
94
|
+
console.log(`[krantho] Watching filesystem collections: ${watchedDirs}`);
|
|
95
|
+
return () => {
|
|
96
|
+
void watcher.close();
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=file-watcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-watcher.js","sourceRoot":"","sources":["../src/file-watcher.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { ContentHooks, KranthoPlugin, ServerFramework, SitePreviewMode } from '@krantho/api';
|
|
2
|
+
import type { KranthoConfigInput } from '@krantho/config';
|
|
3
|
+
import type { ContentChangeListener } from '@krantho/core';
|
|
4
|
+
import type { ContentConfigInput, PageDefinition } from '@krantho/schema';
|
|
5
|
+
/**
|
|
6
|
+
* What a host application hands krantho: one configuration object and the
|
|
7
|
+
* wiring that cannot be expressed as data.
|
|
8
|
+
*/
|
|
9
|
+
export interface KranthoHandlerOptions {
|
|
10
|
+
/** The object `defineKranthoConfig()` returned. Resolved here, once. */
|
|
11
|
+
config: KranthoConfigInput;
|
|
12
|
+
/** Collections from `defineCollection()`, keyed by collection name */
|
|
13
|
+
collections: ContentConfigInput['collections'];
|
|
14
|
+
/** Page definitions from `definePage()` — enables resolvePageRoute */
|
|
15
|
+
pages?: readonly PageDefinition[] | undefined;
|
|
16
|
+
/** Plugins contributing routes, hooks and field types */
|
|
17
|
+
plugins?: KranthoPlugin[] | undefined;
|
|
18
|
+
/** Lifecycle hooks */
|
|
19
|
+
hooks?: ContentHooks | undefined;
|
|
20
|
+
/**
|
|
21
|
+
* Told about every committed write — through the API, the startup content
|
|
22
|
+
* import or the file watcher — so the host can refresh what it caches.
|
|
23
|
+
*/
|
|
24
|
+
onContentChange?: ContentChangeListener | undefined;
|
|
25
|
+
/**
|
|
26
|
+
* Keep work that outlives a request — a background job — running until it
|
|
27
|
+
* settles, on hosts that end a request's work with its response
|
|
28
|
+
*/
|
|
29
|
+
waitUntil?: ((work: Promise<unknown>) => void) | undefined;
|
|
30
|
+
/**
|
|
31
|
+
* How the host renders drafts for editors on the site: enables
|
|
32
|
+
* `GET /api/preview`. `signed-url` pages verify with `verifyPreviewRequest()`.
|
|
33
|
+
*/
|
|
34
|
+
sitePreview?: SitePreviewMode | undefined;
|
|
35
|
+
/**
|
|
36
|
+
* The server package hosting the handler, reported by `GET /api/capabilities`.
|
|
37
|
+
* Each server package sets it when it creates the handler.
|
|
38
|
+
*/
|
|
39
|
+
framework?: ServerFramework | undefined;
|
|
40
|
+
}
|
|
41
|
+
/** A handler that processes Web Requests via the krantho API */
|
|
42
|
+
export interface KranthoHandler {
|
|
43
|
+
/** Handle a Web Request and return a Web Response */
|
|
44
|
+
handle(request: Request): Promise<Response>;
|
|
45
|
+
/** Stop the file watcher and release resources */
|
|
46
|
+
close(): void;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Create a krantho server handler.
|
|
50
|
+
*
|
|
51
|
+
* Resolves the configuration once — defaults, then the developer's code, then
|
|
52
|
+
* the environment — then initializes the persistence adapter, the content
|
|
53
|
+
* engine and the API handlers from the resolved tree. The returned handler
|
|
54
|
+
* converts Web Requests to ApiRequests, dispatches them through the API
|
|
55
|
+
* router, and returns Web Responses.
|
|
56
|
+
*
|
|
57
|
+
* Call this once at module level — the handler is designed to be reused
|
|
58
|
+
* across requests (important for serverless environments).
|
|
59
|
+
*/
|
|
60
|
+
export declare function createKranthoHandler(options: KranthoHandlerOptions): Promise<KranthoHandler>;
|
|
61
|
+
//# sourceMappingURL=handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../src/handler.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,YAAY,EAEZ,aAAa,EACb,eAAe,EACf,eAAe,EAChB,MAAM,cAAc,CAAA;AAErB,OAAO,KAAK,EAAiB,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AAGxE,OAAO,KAAK,EAAiB,qBAAqB,EAAE,MAAM,eAAe,CAAA;AAGzE,OAAO,KAAK,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAiBzE;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC,wEAAwE;IACxE,MAAM,EAAE,kBAAkB,CAAA;IAC1B,sEAAsE;IACtE,WAAW,EAAE,kBAAkB,CAAC,aAAa,CAAC,CAAA;IAC9C,sEAAsE;IACtE,KAAK,CAAC,EAAE,SAAS,cAAc,EAAE,GAAG,SAAS,CAAA;IAC7C,yDAAyD;IACzD,OAAO,CAAC,EAAE,aAAa,EAAE,GAAG,SAAS,CAAA;IACrC,sBAAsB;IACtB,KAAK,CAAC,EAAE,YAAY,GAAG,SAAS,CAAA;IAChC;;;OAGG;IACH,eAAe,CAAC,EAAE,qBAAqB,GAAG,SAAS,CAAA;IACnD;;;OAGG;IACH,SAAS,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC,GAAG,SAAS,CAAA;IAC1D;;;OAGG;IACH,WAAW,CAAC,EAAE,eAAe,GAAG,SAAS,CAAA;IACzC;;;OAGG;IACH,SAAS,CAAC,EAAE,eAAe,GAAG,SAAS,CAAA;CACxC;AAED,gEAAgE;AAChE,MAAM,WAAW,cAAc;IAC7B,qDAAqD;IACrD,MAAM,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAA;IAC3C,kDAAkD;IAClD,KAAK,IAAI,IAAI,CAAA;CACd;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,cAAc,CAAC,CA+IzB"}
|
package/dist/handler.js
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { mkdir } from 'node:fs/promises';
|
|
2
|
+
import { dirname } from 'node:path';
|
|
3
|
+
import { createApiHandlers, deliverChange, startSeo } from '@krantho/api';
|
|
4
|
+
import { productionWarnings } from '@krantho/config';
|
|
5
|
+
import { loadConfig } from '@krantho/config/node';
|
|
6
|
+
import { ContentEngineFactory } from '@krantho/core';
|
|
7
|
+
import { defineConfig } from '@krantho/schema';
|
|
8
|
+
import { createCdnInvalidator } from './cdn/index.js';
|
|
9
|
+
import { cdnChangeListener } from './cdn/listener.js';
|
|
10
|
+
import { claimContentAnnouncement, importRepositoryContent } from './content-migration.js';
|
|
11
|
+
import { toApiRequest, toResponse } from './convert.js';
|
|
12
|
+
import { createFileStore } from './file-store.js';
|
|
13
|
+
import { changedUrlsFor, indexNowChangeListener } from './indexnow.js';
|
|
14
|
+
import { seedLlmConfig, shouldSeedLlm } from './llm-seed.js';
|
|
15
|
+
import { createLockAdapters } from './lock-adapters.js';
|
|
16
|
+
import { createAdapter } from './persistence.js';
|
|
17
|
+
import { createSecretVault } from './secret-vault.js';
|
|
18
|
+
import { seedSeoOnce } from './seo-seed.js';
|
|
19
|
+
import { createWebhookSweep } from './webhook-sweep.js';
|
|
20
|
+
/**
|
|
21
|
+
* Create a krantho server handler.
|
|
22
|
+
*
|
|
23
|
+
* Resolves the configuration once — defaults, then the developer's code, then
|
|
24
|
+
* the environment — then initializes the persistence adapter, the content
|
|
25
|
+
* engine and the API handlers from the resolved tree. The returned handler
|
|
26
|
+
* converts Web Requests to ApiRequests, dispatches them through the API
|
|
27
|
+
* router, and returns Web Responses.
|
|
28
|
+
*
|
|
29
|
+
* Call this once at module level — the handler is designed to be reused
|
|
30
|
+
* across requests (important for serverless environments).
|
|
31
|
+
*/
|
|
32
|
+
export async function createKranthoHandler(options) {
|
|
33
|
+
const config = loadConfig({ code: options.config, env: process.env });
|
|
34
|
+
for (const warning of productionWarnings(config, process.env['NODE_ENV'])) {
|
|
35
|
+
console.warn(`[krantho] WARNING: ${warning}`);
|
|
36
|
+
}
|
|
37
|
+
await startSeo(config);
|
|
38
|
+
const contentConfig = defineConfig({ collections: options.collections });
|
|
39
|
+
if (config.persistence.provider === 'sqlite') {
|
|
40
|
+
await mkdir(dirname(config.persistence.sqlite.path), { recursive: true });
|
|
41
|
+
}
|
|
42
|
+
const { adapter, checkpointAdapter, contentMigrationAdapter, authAdapter, lockDb, schemaVersion, } = await createAdapter(config.persistence, config.site, config.content, contentConfig.collections);
|
|
43
|
+
// Import repository content: new and changed files when the content manifest is newer, or every file into an empty database
|
|
44
|
+
const importedAtStartup = await importRepositoryContent(adapter, contentConfig, contentMigrationAdapter, checkpointAdapter, config.paths.contentDir, config.content.defaultStatus.filesystem);
|
|
45
|
+
const engine = ContentEngineFactory.create(adapter, {
|
|
46
|
+
cacheEnabled: config.content.cache.enabled,
|
|
47
|
+
cacheTTL: config.content.cache.ttlMs,
|
|
48
|
+
maxCacheSize: config.content.cache.maxEntries,
|
|
49
|
+
enableAtNotation: config.content.atNotation,
|
|
50
|
+
});
|
|
51
|
+
// IndexNow lists the sitemap through the API, which is created below
|
|
52
|
+
let listSitemap = async () => [];
|
|
53
|
+
const indexNow = indexNowChangeListener(config, process.env['NODE_ENV'], changedUrlsFor(config.site.baseUrl, Object.fromEntries(contentConfig.collections), () => listSitemap()), options.waitUntil);
|
|
54
|
+
const onContentChange = withPublishers(options.onContentChange, config, options.waitUntil, indexNow);
|
|
55
|
+
const changeListeners = onContentChange ? [onContentChange] : undefined;
|
|
56
|
+
const notifyChange = (change) => deliverChange(engine, changeListeners, change);
|
|
57
|
+
// Create lock, webhook delivery, settings, SEO and media adapters from the DB handle
|
|
58
|
+
const adapters = await createLockAdapters(lockDb, config.persistence.provider);
|
|
59
|
+
const { lockAdapter, webhookDeliveryAdapter, retentionAdapter, siteSettingsAdapter, seoConfigAdapter, pageCacheStore, } = adapters;
|
|
60
|
+
const media = await buildMedia(config, adapters);
|
|
61
|
+
// Seed SEO config from the seo section before serving (once per process, shared with the content loader)
|
|
62
|
+
if (seoConfigAdapter)
|
|
63
|
+
await seedSeoOnce(config, seoConfigAdapter);
|
|
64
|
+
// Seed LLM config from developer-defined defaults (idempotent — Studio overrides preserved)
|
|
65
|
+
if (shouldSeedLlm(options.config, config) && siteSettingsAdapter) {
|
|
66
|
+
await seedLlmConfig(config, siteSettingsAdapter);
|
|
67
|
+
}
|
|
68
|
+
const apiConfig = {
|
|
69
|
+
contentConfig,
|
|
70
|
+
config,
|
|
71
|
+
adapter,
|
|
72
|
+
engine,
|
|
73
|
+
pages: options.pages,
|
|
74
|
+
plugins: options.plugins,
|
|
75
|
+
hooks: options.hooks,
|
|
76
|
+
onContentChange,
|
|
77
|
+
waitUntil: options.waitUntil,
|
|
78
|
+
sitePreview: options.sitePreview,
|
|
79
|
+
framework: options.framework,
|
|
80
|
+
webhooks: config.webhooks,
|
|
81
|
+
media,
|
|
82
|
+
checkpoints: checkpointAdapter,
|
|
83
|
+
contentMigration: contentMigrationAdapter,
|
|
84
|
+
authAdapter,
|
|
85
|
+
schemaVersion,
|
|
86
|
+
persistenceType: config.persistence.provider,
|
|
87
|
+
lockAdapter,
|
|
88
|
+
webhookDeliveryAdapter,
|
|
89
|
+
retentionAdapter,
|
|
90
|
+
siteSettingsAdapter,
|
|
91
|
+
secrets: await createSecretVault(config),
|
|
92
|
+
seoConfigAdapter,
|
|
93
|
+
pageCacheStore: config.cache.shared === 'database' ? pageCacheStore : undefined,
|
|
94
|
+
};
|
|
95
|
+
const api = createApiHandlers(apiConfig);
|
|
96
|
+
// Watched repository edits use the same hooks, locks and draft saves as Studio.
|
|
97
|
+
let stopWatcher;
|
|
98
|
+
if (config.content.watch && 'upsertFromFile' in adapter) {
|
|
99
|
+
const { startFileWatcher } = await import('./file-watcher.js');
|
|
100
|
+
stopWatcher = await startFileWatcher({
|
|
101
|
+
contentConfig,
|
|
102
|
+
adapter: adapter,
|
|
103
|
+
saveDocument: api.saveRepositoryDocument,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
const sweepWebhooks = webhookSweep(config, webhookDeliveryAdapter, options.waitUntil);
|
|
107
|
+
listSitemap = () => sitemapLocations(api);
|
|
108
|
+
// Announce imported content once, also when the page loader imported it first
|
|
109
|
+
const newContent = await claimContentAnnouncement(contentMigrationAdapter, siteSettingsAdapter);
|
|
110
|
+
if (importedAtStartup > 0 || newContent)
|
|
111
|
+
await notifyChange({ kind: 'site' });
|
|
112
|
+
return {
|
|
113
|
+
async handle(request) {
|
|
114
|
+
try {
|
|
115
|
+
sweepWebhooks?.();
|
|
116
|
+
const apiRequest = await toApiRequest(request);
|
|
117
|
+
const apiResponse = await api.handle(apiRequest);
|
|
118
|
+
return toResponse(apiResponse);
|
|
119
|
+
}
|
|
120
|
+
catch (err) {
|
|
121
|
+
const message = err instanceof Error ? err.message : 'Internal server error';
|
|
122
|
+
return toResponse({ status: 500, body: { error: message } });
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
close() {
|
|
126
|
+
if (stopWatcher)
|
|
127
|
+
stopWatcher();
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
/** The retry sweep for failed webhook deliveries: only when webhooks are configured and their delivery log exists */
|
|
132
|
+
function webhookSweep(config, deliveries, waitUntil) {
|
|
133
|
+
if (config.webhooks.length === 0 || deliveries === undefined)
|
|
134
|
+
return undefined;
|
|
135
|
+
return createWebhookSweep({ webhooks: config.webhooks, deliveries, waitUntil });
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* The host's change listener, followed by what the site publishes a change to:
|
|
139
|
+
* the CDN purge when a CDN is configured, then IndexNow when its key is set.
|
|
140
|
+
* Each runs even when one before it throws.
|
|
141
|
+
*/
|
|
142
|
+
function withPublishers(host, config, waitUntil, indexNow) {
|
|
143
|
+
const invalidator = createCdnInvalidator(config.cache);
|
|
144
|
+
const purge = invalidator ? cdnChangeListener(config.cache, invalidator, waitUntil) : null;
|
|
145
|
+
const listeners = [host, purge, indexNow].filter((listener) => Boolean(listener));
|
|
146
|
+
if (listeners.length <= 1)
|
|
147
|
+
return listeners[0];
|
|
148
|
+
return async (change) => {
|
|
149
|
+
const errors = [];
|
|
150
|
+
for (const listener of listeners) {
|
|
151
|
+
try {
|
|
152
|
+
await listener(change);
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
errors.push(error);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
if (errors.length > 0)
|
|
159
|
+
throw errors[0];
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
/** Every page the sitemap lists, read through the site's own API */
|
|
163
|
+
async function sitemapLocations(api) {
|
|
164
|
+
const response = await api.handle({
|
|
165
|
+
method: 'GET',
|
|
166
|
+
path: '/api/seo/sitemap.json',
|
|
167
|
+
params: {},
|
|
168
|
+
query: {},
|
|
169
|
+
});
|
|
170
|
+
const body = response.body;
|
|
171
|
+
if (response.status !== 200 || typeof body !== 'object' || body === null)
|
|
172
|
+
return [];
|
|
173
|
+
const urls = 'urls' in body ? body.urls : undefined;
|
|
174
|
+
if (!Array.isArray(urls))
|
|
175
|
+
return [];
|
|
176
|
+
return urls.flatMap((url) => typeof url === 'object' && url !== null && 'loc' in url && typeof url.loc === 'string'
|
|
177
|
+
? [url.loc]
|
|
178
|
+
: []);
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Media is on exactly when `media.provider` is set: metadata and folders from
|
|
182
|
+
* the database, bytes from the selected file store.
|
|
183
|
+
*/
|
|
184
|
+
async function buildMedia(config, adapters) {
|
|
185
|
+
if (config.media.provider === undefined)
|
|
186
|
+
return undefined;
|
|
187
|
+
const { mediaAdapter, mediaFolderAdapter } = adapters;
|
|
188
|
+
if (!mediaAdapter || !mediaFolderAdapter) {
|
|
189
|
+
throw new Error('media.provider is set, but this database provides no media adapter');
|
|
190
|
+
}
|
|
191
|
+
return {
|
|
192
|
+
metadata: mediaAdapter,
|
|
193
|
+
folders: mediaFolderAdapter,
|
|
194
|
+
files: await createFileStore(config.media),
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
//# sourceMappingURL=handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler.js","sourceRoot":"","sources":["../src/handler.ts"],"names":[],"mappings":"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"}
|
package/dist/import.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type AuthoredMetadata, type Publication } from '@krantho/persistence';
|
|
2
|
+
import { type ContentConfig } from '@krantho/schema';
|
|
3
|
+
import type { ContentDateOf } from './content-dates.js';
|
|
4
|
+
export interface ImportableAdapter {
|
|
5
|
+
listSlugs(): Promise<string[]>;
|
|
6
|
+
getRuntimeSchema?(collection: string): Promise<import('@krantho/persistence').RuntimeSchemaDefinition | null>;
|
|
7
|
+
getContent?(collection: string, slug: string, locale?: string | undefined): Promise<import('@krantho/persistence').ContentDocument | null>;
|
|
8
|
+
upsertFromFile(collection: string, slug: string, fields: Record<string, unknown>, body: string, html: string, sections: Record<string, string>, filePath: string, locale?: string | undefined, modifiedAt?: string | undefined, authored?: (Partial<AuthoredMetadata> & {
|
|
9
|
+
publication: Publication | undefined;
|
|
10
|
+
expectedVersion?: number | undefined;
|
|
11
|
+
}) | undefined): Promise<void>;
|
|
12
|
+
/** Explicit document deletion; repository removal never invokes this method. */
|
|
13
|
+
deleteContent?(collection: string, slug: string, locale?: string | undefined, changedBy?: string | undefined): Promise<void>;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Import .md files from content directories into the database.
|
|
17
|
+
*
|
|
18
|
+
* Reads collection directories from ContentConfig, parses each .md file,
|
|
19
|
+
* and upserts into the database via the adapter. Skips import if the
|
|
20
|
+
* database already has content. `dateOf` dates each imported version by when
|
|
21
|
+
* its file last changed (see `generateContentDates`); without a date the
|
|
22
|
+
* version is dated now.
|
|
23
|
+
*/
|
|
24
|
+
export declare function autoImport(adapter: ImportableAdapter, contentConfig: ContentConfig, dateOf?: ContentDateOf, defaultStatus?: string): Promise<number>;
|
|
25
|
+
//# sourceMappingURL=import.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"import.d.ts","sourceRoot":"","sources":["../src/import.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,gBAAgB,EAAE,KAAK,WAAW,EAAqB,MAAM,sBAAsB,CAAA;AACjG,OAAO,EACL,KAAK,aAAa,EAGnB,MAAM,iBAAiB,CAAA;AAExB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAEvD,MAAM,WAAW,iBAAiB;IAChC,SAAS,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAAA;IAC9B,gBAAgB,CAAC,CACf,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,OAAO,sBAAsB,EAAE,uBAAuB,GAAG,IAAI,CAAC,CAAA;IACzE,UAAU,CAAC,CACT,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,GAC1B,OAAO,CAAC,OAAO,sBAAsB,EAAE,eAAe,GAAG,IAAI,CAAC,CAAA;IACjE,cAAc,CACZ,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAChC,QAAQ,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,EAC3B,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,EAC/B,QAAQ,CAAC,EACL,CAAC,OAAO,CAAC,gBAAgB,CAAC,GAAG;QAC3B,WAAW,EAAE,WAAW,GAAG,SAAS,CAAA;QACpC,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KACrC,CAAC,GACF,SAAS,GACZ,OAAO,CAAC,IAAI,CAAC,CAAA;IAChB,gFAAgF;IAChF,aAAa,CAAC,CACZ,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,EAC3B,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,GAC7B,OAAO,CAAC,IAAI,CAAC,CAAA;CACjB;AAED;;;;;;;;GAQG;AACH,wBAAsB,UAAU,CAC9B,OAAO,EAAE,iBAAiB,EAC1B,aAAa,EAAE,aAAa,EAC5B,MAAM,GAAE,aAA+B,EACvC,aAAa,GAAE,MAAoB,GAClC,OAAO,CAAC,MAAM,CAAC,CAsFjB"}
|
package/dist/import.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { readdir, readFile } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { renderContent } from '@krantho/core';
|
|
4
|
+
import { publicationOfFile } from '@krantho/persistence';
|
|
5
|
+
import { parseRepositoryContent, resolveCollectionContract, } from '@krantho/schema';
|
|
6
|
+
import matter from 'gray-matter';
|
|
7
|
+
/**
|
|
8
|
+
* Import .md files from content directories into the database.
|
|
9
|
+
*
|
|
10
|
+
* Reads collection directories from ContentConfig, parses each .md file,
|
|
11
|
+
* and upserts into the database via the adapter. Skips import if the
|
|
12
|
+
* database already has content. `dateOf` dates each imported version by when
|
|
13
|
+
* its file last changed (see `generateContentDates`); without a date the
|
|
14
|
+
* version is dated now.
|
|
15
|
+
*/
|
|
16
|
+
export async function autoImport(adapter, contentConfig, dateOf = () => undefined, defaultStatus = 'published') {
|
|
17
|
+
// Skip if database already has content
|
|
18
|
+
const existing = await adapter.listSlugs();
|
|
19
|
+
if (existing.length > 0)
|
|
20
|
+
return 0;
|
|
21
|
+
let imported = 0;
|
|
22
|
+
for (const [, collection] of contentConfig.collections) {
|
|
23
|
+
if (!collection.directory)
|
|
24
|
+
continue;
|
|
25
|
+
let entries;
|
|
26
|
+
try {
|
|
27
|
+
entries = await readdir(collection.directory);
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
// A missing directory is usually content left out of a deployment bundle
|
|
31
|
+
console.warn(`[import] Skipping ${collection.name}: directory not found: ${collection.directory}`);
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
const contract = resolveCollectionContract(collection, (await adapter.getRuntimeSchema?.(collection.name)) ?? null);
|
|
35
|
+
const mdFiles = entries.filter((e) => e.endsWith('.md'));
|
|
36
|
+
for (const file of mdFiles) {
|
|
37
|
+
const slug = file.slice(0, -3);
|
|
38
|
+
const filePath = join(collection.directory, file);
|
|
39
|
+
try {
|
|
40
|
+
const raw = await readFile(filePath, 'utf-8');
|
|
41
|
+
const parsed = matter(raw);
|
|
42
|
+
// At the filesystem boundary: YAML frontmatter becomes fields
|
|
43
|
+
const fields = parsed.data;
|
|
44
|
+
// Validate fields against collection schema
|
|
45
|
+
const result = parseRepositoryContent(collection, slug, fields, parsed.content, collection.schema, contract);
|
|
46
|
+
if (!result.success) {
|
|
47
|
+
console.warn(`[import] Skipping ${collection.name}/${slug}: schema validation failed`);
|
|
48
|
+
for (const issue of result.error.issues) {
|
|
49
|
+
console.warn(` - ${issue.path.join('.') || '(root)'}: ${issue.message}`);
|
|
50
|
+
}
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
const body = parsed.content;
|
|
54
|
+
const { html, sections } = await renderContent(result.data.fields, body, {
|
|
55
|
+
atNotation: false,
|
|
56
|
+
});
|
|
57
|
+
await adapter.upsertFromFile(collection.name, slug, result.data.fields, body, html, sections, filePath, result.data.locale, dateOf(filePath), {
|
|
58
|
+
...result.data,
|
|
59
|
+
publication: publicationOfFile(typeof fields['status'] === 'string' ? fields['status'] : defaultStatus),
|
|
60
|
+
expectedVersion: 0,
|
|
61
|
+
});
|
|
62
|
+
imported++;
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
console.warn(`[import] Failed to parse ${file}:`, err instanceof Error ? err.message : err);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return imported;
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=import.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"import.js","sourceRoot":"","sources":["../src/import.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export type { IPageCacheStore, PageCacheValue } from '@krantho/persistence';
|
|
2
|
+
export type { ContentConfig } from '@krantho/schema';
|
|
3
|
+
export type { CdnInvalidator } from './cdn/index.js';
|
|
4
|
+
export { createCdnInvalidator } from './cdn/index.js';
|
|
5
|
+
export { CONTENT_DATES_FILE, type ContentDates, contentCommit, contentVersion, generateContentDates, loadContentDates, } from './content-dates.js';
|
|
6
|
+
export type { ContentLoader } from './content-loader.js';
|
|
7
|
+
export { createContentLoader } from './content-loader.js';
|
|
8
|
+
export { toApiRequest, toResponse } from './convert.js';
|
|
9
|
+
export type { KranthoHandler, KranthoHandlerOptions } from './handler.js';
|
|
10
|
+
export { createKranthoHandler } from './handler.js';
|
|
11
|
+
export { autoImport } from './import.js';
|
|
12
|
+
export { describeSubmission, INDEXNOW_ENDPOINT, type IndexNowSite, submitIndexNow, } from './indexnow.js';
|
|
13
|
+
export { createLazyHandler } from './lazy-handler.js';
|
|
14
|
+
export { CONTENT_MANIFEST_FILE, generateManifest } from './manifest.js';
|
|
15
|
+
export { MIME_TYPES } from './mime-types.js';
|
|
16
|
+
export type { PageCacheRequest, RenderedPageInfo, ServedPage } from './page-cache-rules.js';
|
|
17
|
+
export { servePageRequest } from './page-cache-rules.js';
|
|
18
|
+
export { openPageCacheStore } from './page-cache-store.js';
|
|
19
|
+
export type { PageRequestOptions, PageRequestResult } from './page-request.js';
|
|
20
|
+
export { runPageRequest } from './page-request.js';
|
|
21
|
+
export { createAdapter } from './persistence.js';
|
|
22
|
+
export { verifyPreviewRequest } from './preview-token.js';
|
|
23
|
+
export { matchRoute, routeSpecificity } from './route-matcher.js';
|
|
24
|
+
export type { ExportResult, ImportResult, SeoSeedConfig } from './seo-config-io.js';
|
|
25
|
+
export { exportSeoConfig, importSeoConfig } from './seo-config-io.js';
|
|
26
|
+
export type { StudioRequest, StudioServer } from './studio-gate.js';
|
|
27
|
+
export { createStudioServer } from './studio-gate.js';
|
|
28
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAC3E,YAAY,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AACpD,YAAY,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AACpD,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAA;AACrD,OAAO,EACL,kBAAkB,EAClB,KAAK,YAAY,EACjB,aAAa,EACb,cAAc,EACd,oBAAoB,EACpB,gBAAgB,GACjB,MAAM,oBAAoB,CAAA;AAC3B,YAAY,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAA;AACzD,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AACvD,YAAY,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA;AACzE,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EACjB,KAAK,YAAY,EACjB,cAAc,GACf,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AACrD,OAAO,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AACvE,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAC5C,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AAC3F,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAA;AAC1D,YAAY,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AAC9E,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAChD,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AACzD,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AACjE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AACnF,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AACrE,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AACnE,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export { createCdnInvalidator } from './cdn/index.js';
|
|
2
|
+
export { CONTENT_DATES_FILE, contentCommit, contentVersion, generateContentDates, loadContentDates, } from './content-dates.js';
|
|
3
|
+
export { createContentLoader } from './content-loader.js';
|
|
4
|
+
export { toApiRequest, toResponse } from './convert.js';
|
|
5
|
+
export { createKranthoHandler } from './handler.js';
|
|
6
|
+
export { autoImport } from './import.js';
|
|
7
|
+
export { describeSubmission, INDEXNOW_ENDPOINT, submitIndexNow, } from './indexnow.js';
|
|
8
|
+
export { createLazyHandler } from './lazy-handler.js';
|
|
9
|
+
export { CONTENT_MANIFEST_FILE, generateManifest } from './manifest.js';
|
|
10
|
+
export { MIME_TYPES } from './mime-types.js';
|
|
11
|
+
export { servePageRequest } from './page-cache-rules.js';
|
|
12
|
+
export { openPageCacheStore } from './page-cache-store.js';
|
|
13
|
+
export { runPageRequest } from './page-request.js';
|
|
14
|
+
export { createAdapter } from './persistence.js';
|
|
15
|
+
export { verifyPreviewRequest } from './preview-token.js';
|
|
16
|
+
export { matchRoute, routeSpecificity } from './route-matcher.js';
|
|
17
|
+
export { exportSeoConfig, importSeoConfig } from './seo-config-io.js';
|
|
18
|
+
export { createStudioServer } from './studio-gate.js';
|
|
19
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAA;AACrD,OAAO,EACL,kBAAkB,EAElB,aAAa,EACb,cAAc,EACd,oBAAoB,EACpB,gBAAgB,GACjB,MAAM,oBAAoB,CAAA;AAE3B,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAA;AACzD,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAEvD,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EAEjB,cAAc,GACf,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AACrD,OAAO,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AACvE,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAE5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAA;AAE1D,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAChD,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AACzD,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AAEjE,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AAErE,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { KranthoConfig } from '@krantho/config';
|
|
2
|
+
import type { ContentChange, ContentChangeListener, PageCollection } from '@krantho/core';
|
|
3
|
+
/** The shared IndexNow endpoint; participating engines (Bing among them) share submissions */
|
|
4
|
+
export declare const INDEXNOW_ENDPOINT = "https://api.indexnow.org/indexnow";
|
|
5
|
+
/** A site's IndexNow identity */
|
|
6
|
+
export interface IndexNowSite {
|
|
7
|
+
/** The site's canonical origin (`site.baseUrl`) */
|
|
8
|
+
baseUrl: string;
|
|
9
|
+
/** The key the site serves at `<baseUrl>/<key>.txt` */
|
|
10
|
+
key: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Tell IndexNow that these URLs changed (added, updated or removed). Every URL
|
|
14
|
+
* must be on the site's host. Resolves with the HTTP status of each request
|
|
15
|
+
* when IndexNow accepted them all (200, or 202 while it validates the key),
|
|
16
|
+
* and throws otherwise.
|
|
17
|
+
*/
|
|
18
|
+
export declare function submitIndexNow(site: IndexNowSite, urls: readonly string[], fetchFn?: typeof fetch): Promise<number[]>;
|
|
19
|
+
/** One log line for an accepted submission: how many URLs, IndexNow's answer, and the URLs */
|
|
20
|
+
export declare function describeSubmission(urls: readonly string[], statuses: readonly number[]): string;
|
|
21
|
+
/** The URLs a content change touched, or none to submit */
|
|
22
|
+
export type ChangedUrls = (change: ContentChange) => Promise<readonly string[]>;
|
|
23
|
+
/**
|
|
24
|
+
* The pages a change touched: a document's page when its published version
|
|
25
|
+
* changed (a draft save touches none), every page in the sitemap after a
|
|
26
|
+
* site-wide change such as the startup import.
|
|
27
|
+
*/
|
|
28
|
+
export declare function changedUrlsFor(baseUrl: string, collections: Readonly<Record<string, PageCollection>>, listSitemap: () => Promise<readonly string[]>): ChangedUrls;
|
|
29
|
+
/**
|
|
30
|
+
* A change listener that notifies IndexNow of the pages a committed write
|
|
31
|
+
* touched, when `seo.indexNow.key` is set, the site is not on a local host and
|
|
32
|
+
* the server runs in production: a development server configured for the real
|
|
33
|
+
* site would otherwise announce its draft pages under the site's domain.
|
|
34
|
+
* The submission runs alongside the response (`waitUntil` on serverless hosts)
|
|
35
|
+
* and never fails the write; every submission is logged with IndexNow's answer.
|
|
36
|
+
*/
|
|
37
|
+
export declare function indexNowChangeListener(config: KranthoConfig, nodeEnv: string | undefined, changedUrls: ChangedUrls, waitUntil?: ((work: Promise<unknown>) => void) | undefined, fetchFn?: typeof fetch): ContentChangeListener | null;
|
|
38
|
+
//# sourceMappingURL=indexnow.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"indexnow.d.ts","sourceRoot":"","sources":["../src/indexnow.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AACpD,OAAO,KAAK,EAAE,aAAa,EAAE,qBAAqB,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAGzF,8FAA8F;AAC9F,eAAO,MAAM,iBAAiB,sCAAsC,CAAA;AAKpE,iCAAiC;AACjC,MAAM,WAAW,YAAY;IAC3B,mDAAmD;IACnD,OAAO,EAAE,MAAM,CAAA;IACf,uDAAuD;IACvD,GAAG,EAAE,MAAM,CAAA;CACZ;AAED;;;;;GAKG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE,YAAY,EAClB,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,OAAO,GAAE,OAAO,KAAa,GAC5B,OAAO,CAAC,MAAM,EAAE,CAAC,CAoBnB;AAED,8FAA8F;AAC9F,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAG/F;AAED,2DAA2D;AAC3D,MAAM,MAAM,WAAW,GAAG,CAAC,MAAM,EAAE,aAAa,KAAK,OAAO,CAAC,SAAS,MAAM,EAAE,CAAC,CAAA;AAE/E;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,EACrD,WAAW,EAAE,MAAM,OAAO,CAAC,SAAS,MAAM,EAAE,CAAC,GAC5C,WAAW,CAQb;AAUD;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,aAAa,EACrB,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,WAAW,EAAE,WAAW,EACxB,SAAS,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC,GAAG,SAAS,EAC1D,OAAO,GAAE,OAAO,KAAa,GAC5B,qBAAqB,GAAG,IAAI,CAmB9B"}
|