@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.
Files changed (147) hide show
  1. package/LICENSE +21 -0
  2. package/dist/cdn/cloudfront.d.ts +9 -0
  3. package/dist/cdn/cloudfront.d.ts.map +1 -0
  4. package/dist/cdn/cloudfront.js +35 -0
  5. package/dist/cdn/cloudfront.js.map +1 -0
  6. package/dist/cdn/gcp.d.ts +15 -0
  7. package/dist/cdn/gcp.d.ts.map +1 -0
  8. package/dist/cdn/gcp.js +60 -0
  9. package/dist/cdn/gcp.js.map +1 -0
  10. package/dist/cdn/index.d.ts +9 -0
  11. package/dist/cdn/index.d.ts.map +1 -0
  12. package/dist/cdn/index.js +30 -0
  13. package/dist/cdn/index.js.map +1 -0
  14. package/dist/cdn/listener.d.ts +14 -0
  15. package/dist/cdn/listener.d.ts.map +1 -0
  16. package/dist/cdn/listener.js +53 -0
  17. package/dist/cdn/listener.js.map +1 -0
  18. package/dist/cdn/retry.d.ts +3 -0
  19. package/dist/cdn/retry.d.ts.map +1 -0
  20. package/dist/cdn/retry.js +17 -0
  21. package/dist/cdn/retry.js.map +1 -0
  22. package/dist/cdn/types.d.ts +12 -0
  23. package/dist/cdn/types.d.ts.map +1 -0
  24. package/dist/cdn/types.js +2 -0
  25. package/dist/cdn/types.js.map +1 -0
  26. package/dist/cdn/vercel.d.ts +9 -0
  27. package/dist/cdn/vercel.d.ts.map +1 -0
  28. package/dist/cdn/vercel.js +31 -0
  29. package/dist/cdn/vercel.js.map +1 -0
  30. package/dist/content-conflicts.d.ts +18 -0
  31. package/dist/content-conflicts.d.ts.map +1 -0
  32. package/dist/content-conflicts.js +22 -0
  33. package/dist/content-conflicts.js.map +1 -0
  34. package/dist/content-dates.d.ts +35 -0
  35. package/dist/content-dates.d.ts.map +1 -0
  36. package/dist/content-dates.js +85 -0
  37. package/dist/content-dates.js.map +1 -0
  38. package/dist/content-loader.d.ts +37 -0
  39. package/dist/content-loader.d.ts.map +1 -0
  40. package/dist/content-loader.js +223 -0
  41. package/dist/content-loader.js.map +1 -0
  42. package/dist/content-migration.d.ts +101 -0
  43. package/dist/content-migration.d.ts.map +1 -0
  44. package/dist/content-migration.js +291 -0
  45. package/dist/content-migration.js.map +1 -0
  46. package/dist/convert.d.ts +18 -0
  47. package/dist/convert.d.ts.map +1 -0
  48. package/dist/convert.js +152 -0
  49. package/dist/convert.js.map +1 -0
  50. package/dist/file-store.d.ts +11 -0
  51. package/dist/file-store.d.ts.map +1 -0
  52. package/dist/file-store.js +39 -0
  53. package/dist/file-store.js.map +1 -0
  54. package/dist/file-watcher.d.ts +19 -0
  55. package/dist/file-watcher.d.ts.map +1 -0
  56. package/dist/file-watcher.js +99 -0
  57. package/dist/file-watcher.js.map +1 -0
  58. package/dist/handler.d.ts +61 -0
  59. package/dist/handler.d.ts.map +1 -0
  60. package/dist/handler.js +197 -0
  61. package/dist/handler.js.map +1 -0
  62. package/dist/import.d.ts +25 -0
  63. package/dist/import.d.ts.map +1 -0
  64. package/dist/import.js +71 -0
  65. package/dist/import.js.map +1 -0
  66. package/dist/index.d.ts +28 -0
  67. package/dist/index.d.ts.map +1 -0
  68. package/dist/index.js +19 -0
  69. package/dist/index.js.map +1 -0
  70. package/dist/indexnow.d.ts +38 -0
  71. package/dist/indexnow.d.ts.map +1 -0
  72. package/dist/indexnow.js +89 -0
  73. package/dist/indexnow.js.map +1 -0
  74. package/dist/lazy-handler.d.ts +7 -0
  75. package/dist/lazy-handler.d.ts.map +1 -0
  76. package/dist/lazy-handler.js +16 -0
  77. package/dist/lazy-handler.js.map +1 -0
  78. package/dist/llm-seed.d.ts +25 -0
  79. package/dist/llm-seed.d.ts.map +1 -0
  80. package/dist/llm-seed.js +42 -0
  81. package/dist/llm-seed.js.map +1 -0
  82. package/dist/lock-adapters.d.ts +21 -0
  83. package/dist/lock-adapters.d.ts.map +1 -0
  84. package/dist/lock-adapters.js +69 -0
  85. package/dist/lock-adapters.js.map +1 -0
  86. package/dist/manifest.d.ts +15 -0
  87. package/dist/manifest.d.ts.map +1 -0
  88. package/dist/manifest.js +39 -0
  89. package/dist/manifest.js.map +1 -0
  90. package/dist/mime-types.d.ts +3 -0
  91. package/dist/mime-types.d.ts.map +1 -0
  92. package/dist/mime-types.js +15 -0
  93. package/dist/mime-types.js.map +1 -0
  94. package/dist/page-cache-rules.d.ts +41 -0
  95. package/dist/page-cache-rules.d.ts.map +1 -0
  96. package/dist/page-cache-rules.js +94 -0
  97. package/dist/page-cache-rules.js.map +1 -0
  98. package/dist/page-cache-store.d.ts +11 -0
  99. package/dist/page-cache-store.d.ts.map +1 -0
  100. package/dist/page-cache-store.js +46 -0
  101. package/dist/page-cache-store.js.map +1 -0
  102. package/dist/page-request.d.ts +26 -0
  103. package/dist/page-request.d.ts.map +1 -0
  104. package/dist/page-request.js +29 -0
  105. package/dist/page-request.js.map +1 -0
  106. package/dist/persistence.d.ts +36 -0
  107. package/dist/persistence.d.ts.map +1 -0
  108. package/dist/persistence.js +108 -0
  109. package/dist/persistence.js.map +1 -0
  110. package/dist/preview-token.d.ts +9 -0
  111. package/dist/preview-token.d.ts.map +1 -0
  112. package/dist/preview-token.js +14 -0
  113. package/dist/preview-token.js.map +1 -0
  114. package/dist/read-cache.d.ts +17 -0
  115. package/dist/read-cache.d.ts.map +1 -0
  116. package/dist/read-cache.js +33 -0
  117. package/dist/read-cache.js.map +1 -0
  118. package/dist/route-matcher.d.ts +21 -0
  119. package/dist/route-matcher.d.ts.map +1 -0
  120. package/dist/route-matcher.js +75 -0
  121. package/dist/route-matcher.js.map +1 -0
  122. package/dist/secret-vault.d.ts +20 -0
  123. package/dist/secret-vault.d.ts.map +1 -0
  124. package/dist/secret-vault.js +60 -0
  125. package/dist/secret-vault.js.map +1 -0
  126. package/dist/seo-config-io.d.ts +51 -0
  127. package/dist/seo-config-io.d.ts.map +1 -0
  128. package/dist/seo-config-io.js +151 -0
  129. package/dist/seo-config-io.js.map +1 -0
  130. package/dist/seo-seed.d.ts +12 -0
  131. package/dist/seo-seed.d.ts.map +1 -0
  132. package/dist/seo-seed.js +100 -0
  133. package/dist/seo-seed.js.map +1 -0
  134. package/dist/studio-gate.d.ts +25 -0
  135. package/dist/studio-gate.d.ts.map +1 -0
  136. package/dist/studio-gate.js +70 -0
  137. package/dist/studio-gate.js.map +1 -0
  138. package/dist/studio.d.ts +17 -0
  139. package/dist/studio.d.ts.map +1 -0
  140. package/dist/studio.js +110 -0
  141. package/dist/studio.js.map +1 -0
  142. package/dist/webhook-sweep.d.ts +22 -0
  143. package/dist/webhook-sweep.d.ts.map +1 -0
  144. package/dist/webhook-sweep.js +33 -0
  145. package/dist/webhook-sweep.js.map +1 -0
  146. package/package.json +89 -3
  147. 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"}
@@ -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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"}
@@ -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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"}
@@ -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"}