@dualmark/astro 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -16,29 +16,43 @@ var DualmarkConfigError = class extends Error {
16
16
  this.name = "DualmarkConfigError";
17
17
  }
18
18
  };
19
- function resolveConfig(input) {
19
+ function formatError(msg, filePath) {
20
+ return filePath ? `[${filePath}] Dualmark config error: ${msg}` : `Dualmark config error: ${msg}`;
21
+ }
22
+ function resolveConfig(input, filePath) {
20
23
  if (!input || typeof input !== "object") {
21
- throw new DualmarkConfigError("Dualmark config must be an object");
24
+ throw new DualmarkConfigError(formatError("Config must be an object", filePath));
22
25
  }
23
26
  if (typeof input.siteUrl !== "string" || !input.siteUrl) {
24
- throw new DualmarkConfigError("Dualmark config: siteUrl is required (e.g. 'https://example.com')");
27
+ throw new DualmarkConfigError(
28
+ formatError("siteUrl is required (e.g., 'https://example.com')", filePath)
29
+ );
25
30
  }
26
31
  try {
27
32
  new URL(input.siteUrl);
28
33
  } catch {
29
- throw new DualmarkConfigError(`Dualmark config: siteUrl is not a valid URL: ${input.siteUrl}`);
34
+ throw new DualmarkConfigError(
35
+ formatError(`siteUrl is not a valid URL: '${input.siteUrl}'`, filePath)
36
+ );
30
37
  }
31
38
  if (input.siteUrl.endsWith("/")) {
32
- throw new DualmarkConfigError(`Dualmark config: siteUrl must not end with '/': ${input.siteUrl}`);
39
+ throw new DualmarkConfigError(
40
+ formatError(`siteUrl must not end with '/': '${input.siteUrl}'`, filePath)
41
+ );
33
42
  }
34
43
  const collections = input.collections ?? {};
35
44
  for (const [name, c] of Object.entries(collections)) {
36
45
  if (!c.converter) {
37
- throw new DualmarkConfigError(`Dualmark config: collection '${name}' is missing 'converter'`);
46
+ throw new DualmarkConfigError(
47
+ formatError(`Collection '${name}' is missing 'converter'`, filePath)
48
+ );
38
49
  }
39
50
  if (c.route && c.route.startsWith("/")) {
40
51
  throw new DualmarkConfigError(
41
- `Dualmark config: collection '${name}' route should not start with '/' (got '${c.route}')`
52
+ formatError(
53
+ `Collection '${name}' route should not start with '/' (got '${c.route}')`,
54
+ filePath
55
+ )
42
56
  );
43
57
  }
44
58
  }
@@ -46,12 +60,12 @@ function resolveConfig(input) {
46
60
  for (const sp of staticPages) {
47
61
  if (!sp.pattern.startsWith("/")) {
48
62
  throw new DualmarkConfigError(
49
- `Dualmark config: staticPages.pattern must start with '/' (got '${sp.pattern}')`
63
+ formatError(`staticPages.pattern must start with '/' (got '${sp.pattern}')`, filePath)
50
64
  );
51
65
  }
52
66
  if (typeof sp.render !== "function") {
53
67
  throw new DualmarkConfigError(
54
- `Dualmark config: staticPages.render for '${sp.pattern}' must be a function`
68
+ formatError(`staticPages.render for '${sp.pattern}' must be a function`, filePath)
55
69
  );
56
70
  }
57
71
  }
@@ -59,7 +73,10 @@ function resolveConfig(input) {
59
73
  for (const pr of parameterizedRoutes) {
60
74
  if (!pr.pattern.includes("[")) {
61
75
  throw new DualmarkConfigError(
62
- `Dualmark config: parameterizedRoutes.pattern must contain at least one [param] (got '${pr.pattern}')`
76
+ formatError(
77
+ `parameterizedRoutes.pattern must contain at least one [param] (got '${pr.pattern}')`,
78
+ filePath
79
+ )
63
80
  );
64
81
  }
65
82
  }
@@ -86,18 +103,26 @@ function rel(from, to) {
86
103
  return r.split(path.sep).join("/");
87
104
  }
88
105
  function createDualmarkIntegration(input) {
89
- let resolved;
90
- try {
91
- resolved = resolveConfig(input);
92
- } catch (e) {
93
- if (e instanceof DualmarkConfigError) throw e;
94
- throw e;
95
- }
96
106
  return {
97
107
  name: "@dualmark/astro",
98
108
  hooks: {
99
109
  "astro:config:setup"(opts) {
100
110
  const root = url.fileURLToPath(opts.config.root);
111
+ const candidates = [
112
+ "astro.config.ts",
113
+ "astro.config.mjs",
114
+ "astro.config.js",
115
+ "astro.config.mts",
116
+ "astro.config.cjs"
117
+ ];
118
+ const configPath = candidates.map((f) => path.join(root, f)).find(fs.existsSync) ?? path.join(root, "astro.config.mjs");
119
+ let resolved;
120
+ try {
121
+ resolved = resolveConfig(input, configPath);
122
+ } catch (e) {
123
+ opts.logger.error(`[@dualmark/astro] ${e instanceof Error ? e.message : String(e)}`);
124
+ throw e;
125
+ }
101
126
  const generatedDir = path.join(root, "node_modules", GENERATED_DIR_NAME);
102
127
  if (!fs.existsSync(generatedDir)) fs.mkdirSync(generatedDir, { recursive: true });
103
128
  fs.writeFileSync(
@@ -164,9 +189,11 @@ const endpoint = makeListingEndpoint({
164
189
 
165
190
  export const GET = endpoint.GET;
166
191
  `;
167
- routes.push(
168
- { pattern: detailPattern, fileName: `collection-${collectionName}-detail.mjs`, source: detailSource }
169
- );
192
+ routes.push({
193
+ pattern: detailPattern,
194
+ fileName: `collection-${collectionName}-detail.mjs`,
195
+ source: detailSource
196
+ });
170
197
  if (c.emitListing !== false) {
171
198
  routes.push({
172
199
  pattern: listingPattern,
@@ -181,12 +208,8 @@ export const GET = endpoint.GET;
181
208
  const safe = sp.pattern.replace(/[^a-z0-9]/gi, "_");
182
209
  const fileName = `static-${i}-${safe}.mjs`;
183
210
  const renderModulePath = path.join(generatedDir, `static-${i}-${safe}-render.mjs`);
184
- fs.writeFileSync(
185
- renderModulePath,
186
- `export default ${sp.render.toString()};
187
- `,
188
- "utf8"
189
- );
211
+ fs.writeFileSync(renderModulePath, `export default ${sp.render.toString()};
212
+ `, "utf8");
190
213
  const source = `import { makeStaticEndpoint } from "@dualmark/astro/endpoints/static";
191
214
  import dualmarkConfig from "./config.mjs";
192
215
  import render from "./${rel(generatedDir, renderModulePath)}";
@@ -209,8 +232,12 @@ export const GET = endpoint.GET;
209
232
  const pathsModulePath = path.join(generatedDir, `param-${i}-${safe}-paths.mjs`);
210
233
  fs.writeFileSync(renderModulePath, `export default ${pr.render.toString()};
211
234
  `, "utf8");
212
- fs.writeFileSync(pathsModulePath, `export default ${pr.getStaticPaths.toString()};
213
- `, "utf8");
235
+ fs.writeFileSync(
236
+ pathsModulePath,
237
+ `export default ${pr.getStaticPaths.toString()};
238
+ `,
239
+ "utf8"
240
+ );
214
241
  const source = `import { makeParameterizedEndpoint } from "@dualmark/astro/endpoints/parameterized";
215
242
  import dualmarkConfig from "./config.mjs";
216
243
  import render from "./${rel(generatedDir, renderModulePath)}";
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/config-validation.ts","../src/integration.ts","../src/converter-registry.ts"],"names":["relative","sep","fileURLToPath","join","existsSync","writeFileSync","blogConverter","caseStudyConverter","changelogConverter","compareConverter","docsConverter","featureConverter","glossaryConverter","integrationConverter","legalConverter","pricingConverter","pseoConverter","statusPageConverter","toolConverter","videoConverter"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { DualmarkAstroConfig, ResolvedDualmarkConfig } from \"./types.js\";\n\nexport class DualmarkConfigError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"DualmarkConfigError\";\n }\n}\n\nexport function resolveConfig(input: DualmarkAstroConfig): ResolvedDualmarkConfig {\n if (!input || typeof input !== \"object\") {\n throw new DualmarkConfigError(\"Dualmark config must be an object\");\n }\n if (typeof input.siteUrl !== \"string\" || !input.siteUrl) {\n throw new DualmarkConfigError(\"Dualmark config: siteUrl is required (e.g. 'https://example.com')\");\n }\n try {\n new URL(input.siteUrl);\n } catch {\n throw new DualmarkConfigError(`Dualmark config: siteUrl is not a valid URL: ${input.siteUrl}`);\n }\n if (input.siteUrl.endsWith(\"/\")) {\n throw new DualmarkConfigError(`Dualmark config: siteUrl must not end with '/': ${input.siteUrl}`);\n }\n\n const collections = input.collections ?? {};\n for (const [name, c] of Object.entries(collections)) {\n if (!c.converter) {\n throw new DualmarkConfigError(`Dualmark config: collection '${name}' is missing 'converter'`);\n }\n if (c.route && c.route.startsWith(\"/\")) {\n throw new DualmarkConfigError(\n `Dualmark config: collection '${name}' route should not start with '/' (got '${c.route}')`,\n );\n }\n }\n\n const staticPages = input.staticPages ?? [];\n for (const sp of staticPages) {\n if (!sp.pattern.startsWith(\"/\")) {\n throw new DualmarkConfigError(\n `Dualmark config: staticPages.pattern must start with '/' (got '${sp.pattern}')`,\n );\n }\n if (typeof sp.render !== \"function\") {\n throw new DualmarkConfigError(\n `Dualmark config: staticPages.render for '${sp.pattern}' must be a function`,\n );\n }\n }\n\n const parameterizedRoutes = input.parameterizedRoutes ?? [];\n for (const pr of parameterizedRoutes) {\n if (!pr.pattern.includes(\"[\")) {\n throw new DualmarkConfigError(\n `Dualmark config: parameterizedRoutes.pattern must contain at least one [param] (got '${pr.pattern}')`,\n );\n }\n }\n\n return {\n siteUrl: input.siteUrl,\n collections,\n staticPages,\n parameterizedRoutes,\n llmsTxt: input.llmsTxt,\n middleware: {\n injectLinkHeader: input.middleware?.injectLinkHeader !== false,\n },\n headers: {\n cacheControl: input.headers?.cacheControl ?? \"public, max-age=3600\",\n noindex: input.headers?.noindex !== false,\n },\n };\n}\n","import { mkdirSync, writeFileSync, existsSync } from \"node:fs\";\nimport { fileURLToPath } from \"node:url\";\nimport { join, relative, resolve, sep } from \"node:path\";\nimport { resolveConfig, DualmarkConfigError } from \"./config-validation.js\";\nimport type { DualmarkAstroConfig, ResolvedDualmarkConfig } from \"./types.js\";\n\ninterface AstroIntegrationLogger {\n info: (msg: string) => void;\n warn: (msg: string) => void;\n error: (msg: string) => void;\n}\n\ninterface InjectedRoute {\n pattern: string;\n entrypoint: string | URL;\n prerender?: boolean;\n}\n\ninterface AstroIntegrationMiddleware {\n order: \"pre\" | \"post\";\n entrypoint: string | URL;\n}\n\ninterface ConfigSetupHookOptions {\n config: { root: URL; srcDir?: URL };\n command: string;\n isRestart?: boolean;\n injectRoute: (route: InjectedRoute) => void;\n addMiddleware?: (mw: AstroIntegrationMiddleware) => void;\n logger: AstroIntegrationLogger;\n updateConfig?: (cfg: unknown) => unknown;\n}\n\nexport interface AstroIntegrationLike {\n name: string;\n hooks: {\n \"astro:config:setup\": (opts: ConfigSetupHookOptions) => void | Promise<void>;\n };\n}\n\nconst GENERATED_DIR_NAME = \".dualmark-generated\";\n\ninterface RouteSpec {\n pattern: string;\n fileName: string;\n source: string;\n}\n\nfunction rel(from: string, to: string): string {\n const r = relative(from, to);\n return r.split(sep).join(\"/\");\n}\n\nexport function createDualmarkIntegration(input: DualmarkAstroConfig): AstroIntegrationLike {\n let resolved: ResolvedDualmarkConfig;\n try {\n resolved = resolveConfig(input);\n } catch (e) {\n if (e instanceof DualmarkConfigError) throw e;\n throw e;\n }\n\n return {\n name: \"@dualmark/astro\",\n hooks: {\n \"astro:config:setup\"(opts) {\n const root = fileURLToPath(opts.config.root);\n const generatedDir = join(root, \"node_modules\", GENERATED_DIR_NAME);\n if (!existsSync(generatedDir)) mkdirSync(generatedDir, { recursive: true });\n\n writeFileSync(\n join(generatedDir, \"config.mjs\"),\n `export default ${JSON.stringify(\n {\n siteUrl: resolved.siteUrl,\n cacheControl: resolved.headers.cacheControl,\n noindex: resolved.headers.noindex,\n },\n null,\n 2,\n )};\\n`,\n \"utf8\",\n );\n\n const routes: RouteSpec[] = [];\n\n for (const [collectionName, c] of Object.entries(resolved.collections)) {\n const route = c.route ?? collectionName;\n const slugSeg = c.slugStrategy === \"single\" ? \"[slug]\" : \"[...slug]\";\n const detailPattern = `/${route}/${slugSeg}.md`;\n const listingPattern = `/${route}.md`;\n if (typeof c.converter !== \"string\") {\n opts.logger.warn(\n `[@dualmark/astro] Collection '${collectionName}' uses an inline converter function — this isn't yet serializable into a generated route. Use a built-in converter name (e.g. 'blog') for now.`,\n );\n continue;\n }\n const converterImport = `import { resolveBuiltInConverter } from \"@dualmark/astro\";`;\n const detailSource = `${converterImport}\nimport { makeCollectionDetailEndpoint } from \"@dualmark/astro/endpoints/collection\";\nimport { getCollection } from \"astro:content\";\nimport dualmarkConfig from \"./config.mjs\";\n\nconst converter = resolveBuiltInConverter({\n name: ${JSON.stringify(c.converter)},\n collectionName: ${JSON.stringify(collectionName)},\n baseConfig: { siteUrl: dualmarkConfig.siteUrl },\n});\n\nconst endpoint = makeCollectionDetailEndpoint({\n collectionName: ${JSON.stringify(collectionName)},\n converter,\n getCollection: (name, filter) => getCollection(name, filter),\n responseOptions: { cacheControl: dualmarkConfig.cacheControl, noindex: dualmarkConfig.noindex },\n});\n\nexport const getStaticPaths = endpoint.getStaticPaths;\nexport const GET = endpoint.GET;\n`;\n\n const listingSource = `import { makeListingEndpoint } from \"@dualmark/astro/endpoints/listing\";\nimport { getCollection } from \"astro:content\";\nimport dualmarkConfig from \"./config.mjs\";\n\nconst endpoint = makeListingEndpoint({\n collectionName: ${JSON.stringify(collectionName)},\n siteUrl: dualmarkConfig.siteUrl,\n basePath: ${JSON.stringify(\"/\" + route)},\n title: ${JSON.stringify(c.listingMetadata?.title ?? collectionName)},\n description: ${JSON.stringify(c.listingMetadata?.description ?? `All ${collectionName} entries.`)},\n getCollection: (name, filter) => getCollection(name, filter),\n responseOptions: { cacheControl: dualmarkConfig.cacheControl, noindex: dualmarkConfig.noindex },\n});\n\nexport const GET = endpoint.GET;\n`;\n\n routes.push(\n { pattern: detailPattern, fileName: `collection-${collectionName}-detail.mjs`, source: detailSource },\n );\n if (c.emitListing !== false) {\n routes.push({\n pattern: listingPattern,\n fileName: `collection-${collectionName}-listing.mjs`,\n source: listingSource,\n });\n }\n }\n\n for (let i = 0; i < resolved.staticPages.length; i++) {\n const sp = resolved.staticPages[i];\n if (!sp) continue;\n const safe = sp.pattern.replace(/[^a-z0-9]/gi, \"_\");\n const fileName = `static-${i}-${safe}.mjs`;\n const renderModulePath = join(generatedDir, `static-${i}-${safe}-render.mjs`);\n writeFileSync(\n renderModulePath,\n `export default ${sp.render.toString()};\\n`,\n \"utf8\",\n );\n const source = `import { makeStaticEndpoint } from \"@dualmark/astro/endpoints/static\";\nimport dualmarkConfig from \"./config.mjs\";\nimport render from \"./${rel(generatedDir, renderModulePath)}\";\n\nconst endpoint = makeStaticEndpoint({\n render,\n responseOptions: { cacheControl: dualmarkConfig.cacheControl, noindex: dualmarkConfig.noindex },\n});\n\nexport const GET = endpoint.GET;\n`;\n const mdPattern = sp.pattern === \"/\" ? \"/index.md\" : sp.pattern.replace(/\\/$/, \"\") + \".md\";\n routes.push({ pattern: mdPattern, fileName, source });\n }\n\n for (let i = 0; i < resolved.parameterizedRoutes.length; i++) {\n const pr = resolved.parameterizedRoutes[i];\n if (!pr) continue;\n const safe = pr.pattern.replace(/[^a-z0-9]/gi, \"_\");\n const renderModulePath = join(generatedDir, `param-${i}-${safe}-render.mjs`);\n const pathsModulePath = join(generatedDir, `param-${i}-${safe}-paths.mjs`);\n writeFileSync(renderModulePath, `export default ${pr.render.toString()};\\n`, \"utf8\");\n writeFileSync(pathsModulePath, `export default ${pr.getStaticPaths.toString()};\\n`, \"utf8\");\n const source = `import { makeParameterizedEndpoint } from \"@dualmark/astro/endpoints/parameterized\";\nimport dualmarkConfig from \"./config.mjs\";\nimport render from \"./${rel(generatedDir, renderModulePath)}\";\nimport getStaticPaths from \"./${rel(generatedDir, pathsModulePath)}\";\n\nconst endpoint = makeParameterizedEndpoint({\n getStaticPaths: () => getStaticPaths(),\n render,\n responseOptions: { cacheControl: dualmarkConfig.cacheControl, noindex: dualmarkConfig.noindex },\n});\n\nexport const getStaticPaths = endpoint.getStaticPaths;\nexport const GET = endpoint.GET;\n`;\n const pattern = pr.pattern.startsWith(\"/\") ? pr.pattern + \".md\" : `/${pr.pattern}.md`;\n routes.push({\n pattern,\n fileName: `param-${i}-${safe}.mjs`,\n source,\n });\n }\n\n if (resolved.llmsTxt?.enabled) {\n const sections = resolved.llmsTxt.sections ?? [];\n const source = `import { makeLlmsTxtEndpoint } from \"@dualmark/astro/endpoints/llms-txt\";\n\nconst endpoint = makeLlmsTxtEndpoint({\n brandName: ${JSON.stringify(resolved.llmsTxt.brandName ?? \"Site\")},\n description: ${JSON.stringify(resolved.llmsTxt.description ?? \"\")},\n sections: ${JSON.stringify(sections)},\n});\n\nexport const GET = endpoint.GET;\n`;\n routes.push({ pattern: \"/llms.txt\", fileName: `llms-txt.mjs`, source });\n }\n\n for (const r of routes) {\n const filePath = join(generatedDir, r.fileName);\n writeFileSync(filePath, r.source, \"utf8\");\n opts.injectRoute({\n pattern: r.pattern,\n entrypoint: filePath,\n prerender: true,\n });\n }\n\n if (resolved.middleware.injectLinkHeader && opts.addMiddleware) {\n opts.addMiddleware({\n order: \"post\",\n entrypoint: \"@dualmark/astro/middleware\",\n });\n }\n\n opts.logger.info(\n `[@dualmark/astro] Injected ${routes.length} route(s) and ${\n resolved.middleware.injectLinkHeader ? \"1\" : \"0\"\n } middleware`,\n );\n\n void resolve;\n },\n },\n };\n}\n\nexport default function dualmarkAstro(config: DualmarkAstroConfig): AstroIntegrationLike {\n return createDualmarkIntegration(config);\n}\n","import {\n blogConverter,\n caseStudyConverter,\n changelogConverter,\n compareConverter,\n docsConverter,\n featureConverter,\n glossaryConverter,\n integrationConverter,\n legalConverter,\n pricingConverter,\n pseoConverter,\n statusPageConverter,\n toolConverter,\n videoConverter,\n type BaseConverterConfig,\n type CollectionEntry,\n type Converter,\n} from \"@dualmark/converters\";\n\nexport type BuiltInConverterName =\n | \"blog\"\n | \"case-study\"\n | \"changelog\"\n | \"compare\"\n | \"docs\"\n | \"feature\"\n | \"glossary\"\n | \"integration\"\n | \"legal\"\n | \"pricing\"\n | \"pseo\"\n | \"status-page\"\n | \"tool\"\n | \"video\";\n\nexport interface ResolveConverterArgs {\n name: string;\n collectionName: string;\n baseConfig: BaseConverterConfig;\n}\n\nexport function resolveBuiltInConverter(\n args: ResolveConverterArgs,\n): Converter<CollectionEntry<unknown>> {\n const basePath = `/${args.collectionName}`;\n const cfg = { ...args.baseConfig, basePath };\n switch (args.name as BuiltInConverterName) {\n case \"blog\":\n return blogConverter(cfg) as Converter<CollectionEntry<unknown>>;\n case \"case-study\":\n return caseStudyConverter(cfg) as Converter<CollectionEntry<unknown>>;\n case \"changelog\":\n return changelogConverter(cfg) as Converter<CollectionEntry<unknown>>;\n case \"compare\":\n return compareConverter({ ...cfg, ourBrandColumn: \"Us\" }) as Converter<\n CollectionEntry<unknown>\n >;\n case \"docs\":\n return docsConverter(cfg) as Converter<CollectionEntry<unknown>>;\n case \"feature\":\n return featureConverter(cfg) as Converter<CollectionEntry<unknown>>;\n case \"glossary\":\n return glossaryConverter(cfg) as Converter<CollectionEntry<unknown>>;\n case \"integration\":\n return integrationConverter(cfg) as Converter<CollectionEntry<unknown>>;\n case \"legal\":\n return legalConverter(cfg) as Converter<CollectionEntry<unknown>>;\n case \"pricing\":\n return pricingConverter(cfg) as Converter<CollectionEntry<unknown>>;\n case \"pseo\":\n return pseoConverter(cfg) as Converter<CollectionEntry<unknown>>;\n case \"status-page\":\n return statusPageConverter(cfg) as Converter<CollectionEntry<unknown>>;\n case \"tool\":\n return toolConverter(cfg) as Converter<CollectionEntry<unknown>>;\n case \"video\":\n return videoConverter(cfg) as Converter<CollectionEntry<unknown>>;\n default:\n throw new Error(\n `Dualmark: unknown built-in converter '${args.name}'. Valid names: blog, case-study, changelog, compare, docs, feature, glossary, integration, legal, pricing, pseo, status-page, tool, video. Or pass a function.`,\n );\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/config-validation.ts","../src/integration.ts","../src/converter-registry.ts"],"names":["relative","sep","fileURLToPath","join","existsSync","writeFileSync","blogConverter","caseStudyConverter","changelogConverter","compareConverter","docsConverter","featureConverter","glossaryConverter","integrationConverter","legalConverter","pricingConverter","pseoConverter","statusPageConverter","toolConverter","videoConverter"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { DualmarkAstroConfig, ResolvedDualmarkConfig } from \"./types.js\";\n\nexport class DualmarkConfigError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"DualmarkConfigError\";\n }\n}\n\nfunction formatError(msg: string, filePath?: string): string {\n return filePath ? `[${filePath}] Dualmark config error: ${msg}` : `Dualmark config error: ${msg}`;\n}\n\nexport function resolveConfig(\n input: DualmarkAstroConfig,\n filePath?: string,\n): ResolvedDualmarkConfig {\n if (!input || typeof input !== \"object\") {\n throw new DualmarkConfigError(formatError(\"Config must be an object\", filePath));\n }\n if (typeof input.siteUrl !== \"string\" || !input.siteUrl) {\n throw new DualmarkConfigError(\n formatError(\"siteUrl is required (e.g., 'https://example.com')\", filePath),\n );\n }\n try {\n new URL(input.siteUrl);\n } catch {\n throw new DualmarkConfigError(\n formatError(`siteUrl is not a valid URL: '${input.siteUrl}'`, filePath),\n );\n }\n if (input.siteUrl.endsWith(\"/\")) {\n throw new DualmarkConfigError(\n formatError(`siteUrl must not end with '/': '${input.siteUrl}'`, filePath),\n );\n }\n const collections = input.collections ?? {};\n for (const [name, c] of Object.entries(collections)) {\n if (!c.converter) {\n throw new DualmarkConfigError(\n formatError(`Collection '${name}' is missing 'converter'`, filePath),\n );\n }\n if (c.route && c.route.startsWith(\"/\")) {\n throw new DualmarkConfigError(\n formatError(\n `Collection '${name}' route should not start with '/' (got '${c.route}')`,\n filePath,\n ),\n );\n }\n }\n const staticPages = input.staticPages ?? [];\n for (const sp of staticPages) {\n if (!sp.pattern.startsWith(\"/\")) {\n throw new DualmarkConfigError(\n formatError(`staticPages.pattern must start with '/' (got '${sp.pattern}')`, filePath),\n );\n }\n if (typeof sp.render !== \"function\") {\n throw new DualmarkConfigError(\n formatError(`staticPages.render for '${sp.pattern}' must be a function`, filePath),\n );\n }\n }\n const parameterizedRoutes = input.parameterizedRoutes ?? [];\n for (const pr of parameterizedRoutes) {\n if (!pr.pattern.includes(\"[\")) {\n throw new DualmarkConfigError(\n formatError(\n `parameterizedRoutes.pattern must contain at least one [param] (got '${pr.pattern}')`,\n filePath,\n ),\n );\n }\n }\n return {\n siteUrl: input.siteUrl,\n collections,\n staticPages,\n parameterizedRoutes,\n llmsTxt: input.llmsTxt,\n middleware: {\n injectLinkHeader: input.middleware?.injectLinkHeader !== false,\n },\n headers: {\n cacheControl: input.headers?.cacheControl ?? \"public, max-age=3600\",\n noindex: input.headers?.noindex !== false,\n },\n };\n}\n","import { mkdirSync, writeFileSync, existsSync } from \"node:fs\";\nimport { fileURLToPath } from \"node:url\";\nimport { join, relative, sep } from \"node:path\";\nimport { resolveConfig, DualmarkConfigError } from \"./config-validation.js\";\nimport type { DualmarkAstroConfig, ResolvedDualmarkConfig } from \"./types.js\";\n\ninterface AstroIntegrationLogger {\n info: (msg: string) => void;\n warn: (msg: string) => void;\n error: (msg: string) => void;\n}\n\ninterface InjectedRoute {\n pattern: string;\n entrypoint: string | URL;\n prerender?: boolean;\n}\n\ninterface AstroIntegrationMiddleware {\n order: \"pre\" | \"post\";\n entrypoint: string | URL;\n}\n\ninterface ConfigSetupHookOptions {\n config: { root: URL; srcDir?: URL };\n command: string;\n isRestart?: boolean;\n injectRoute: (route: InjectedRoute) => void;\n addMiddleware?: (mw: AstroIntegrationMiddleware) => void;\n logger: AstroIntegrationLogger;\n updateConfig?: (cfg: unknown) => unknown;\n}\n\nexport interface AstroIntegrationLike {\n name: string;\n hooks: {\n \"astro:config:setup\": (opts: ConfigSetupHookOptions) => void | Promise<void>;\n };\n}\n\nconst GENERATED_DIR_NAME = \".dualmark-generated\";\n\ninterface RouteSpec {\n pattern: string;\n fileName: string;\n source: string;\n}\n\nfunction rel(from: string, to: string): string {\n const r = relative(from, to);\n return r.split(sep).join(\"/\");\n}\n\nexport function createDualmarkIntegration(input: DualmarkAstroConfig): AstroIntegrationLike {\n return {\n name: \"@dualmark/astro\",\n hooks: {\n \"astro:config:setup\"(opts) {\n const root = fileURLToPath(opts.config.root);\n\n const candidates = [\n \"astro.config.ts\",\n \"astro.config.mjs\",\n \"astro.config.js\",\n \"astro.config.mts\",\n \"astro.config.cjs\",\n ];\n const configPath =\n candidates.map((f) => join(root, f)).find(existsSync) ?? join(root, \"astro.config.mjs\");\n\n let resolved: ResolvedDualmarkConfig;\n try {\n resolved = resolveConfig(input, configPath);\n } catch (e) {\n opts.logger.error(`[@dualmark/astro] ${e instanceof Error ? e.message : String(e)}`);\n throw e;\n }\n\n const generatedDir = join(root, \"node_modules\", GENERATED_DIR_NAME);\n if (!existsSync(generatedDir)) mkdirSync(generatedDir, { recursive: true });\n\n writeFileSync(\n join(generatedDir, \"config.mjs\"),\n `export default ${JSON.stringify(\n {\n siteUrl: resolved.siteUrl,\n cacheControl: resolved.headers.cacheControl,\n noindex: resolved.headers.noindex,\n },\n null,\n 2,\n )};\\n`,\n \"utf8\",\n );\n\n const routes: RouteSpec[] = [];\n\n for (const [collectionName, c] of Object.entries(resolved.collections)) {\n const route = c.route ?? collectionName;\n const slugSeg = c.slugStrategy === \"single\" ? \"[slug]\" : \"[...slug]\";\n const detailPattern = `/${route}/${slugSeg}.md`;\n const listingPattern = `/${route}.md`;\n if (typeof c.converter !== \"string\") {\n opts.logger.warn(\n `[@dualmark/astro] Collection '${collectionName}' uses an inline converter function — this isn't yet serializable into a generated route. Use a built-in converter name (e.g. 'blog') for now.`,\n );\n continue;\n }\n const converterImport = `import { resolveBuiltInConverter } from \"@dualmark/astro\";`;\n const detailSource = `${converterImport}\nimport { makeCollectionDetailEndpoint } from \"@dualmark/astro/endpoints/collection\";\nimport { getCollection } from \"astro:content\";\nimport dualmarkConfig from \"./config.mjs\";\n\nconst converter = resolveBuiltInConverter({\n name: ${JSON.stringify(c.converter)},\n collectionName: ${JSON.stringify(collectionName)},\n baseConfig: { siteUrl: dualmarkConfig.siteUrl },\n});\n\nconst endpoint = makeCollectionDetailEndpoint({\n collectionName: ${JSON.stringify(collectionName)},\n converter,\n getCollection: (name, filter) => getCollection(name, filter),\n responseOptions: { cacheControl: dualmarkConfig.cacheControl, noindex: dualmarkConfig.noindex },\n});\n\nexport const getStaticPaths = endpoint.getStaticPaths;\nexport const GET = endpoint.GET;\n`;\n\n const listingSource = `import { makeListingEndpoint } from \"@dualmark/astro/endpoints/listing\";\nimport { getCollection } from \"astro:content\";\nimport dualmarkConfig from \"./config.mjs\";\n\nconst endpoint = makeListingEndpoint({\n collectionName: ${JSON.stringify(collectionName)},\n siteUrl: dualmarkConfig.siteUrl,\n basePath: ${JSON.stringify(\"/\" + route)},\n title: ${JSON.stringify(c.listingMetadata?.title ?? collectionName)},\n description: ${JSON.stringify(c.listingMetadata?.description ?? `All ${collectionName} entries.`)},\n getCollection: (name, filter) => getCollection(name, filter),\n responseOptions: { cacheControl: dualmarkConfig.cacheControl, noindex: dualmarkConfig.noindex },\n});\n\nexport const GET = endpoint.GET;\n`;\n\n routes.push({\n pattern: detailPattern,\n fileName: `collection-${collectionName}-detail.mjs`,\n source: detailSource,\n });\n if (c.emitListing !== false) {\n routes.push({\n pattern: listingPattern,\n fileName: `collection-${collectionName}-listing.mjs`,\n source: listingSource,\n });\n }\n }\n\n for (let i = 0; i < resolved.staticPages.length; i++) {\n const sp = resolved.staticPages[i];\n if (!sp) continue;\n const safe = sp.pattern.replace(/[^a-z0-9]/gi, \"_\");\n const fileName = `static-${i}-${safe}.mjs`;\n const renderModulePath = join(generatedDir, `static-${i}-${safe}-render.mjs`);\n writeFileSync(renderModulePath, `export default ${sp.render.toString()};\\n`, \"utf8\");\n const source = `import { makeStaticEndpoint } from \"@dualmark/astro/endpoints/static\";\nimport dualmarkConfig from \"./config.mjs\";\nimport render from \"./${rel(generatedDir, renderModulePath)}\";\n\nconst endpoint = makeStaticEndpoint({\n render,\n responseOptions: { cacheControl: dualmarkConfig.cacheControl, noindex: dualmarkConfig.noindex },\n});\n\nexport const GET = endpoint.GET;\n`;\n const mdPattern =\n sp.pattern === \"/\" ? \"/index.md\" : sp.pattern.replace(/\\/$/, \"\") + \".md\";\n routes.push({ pattern: mdPattern, fileName, source });\n }\n\n for (let i = 0; i < resolved.parameterizedRoutes.length; i++) {\n const pr = resolved.parameterizedRoutes[i];\n if (!pr) continue;\n const safe = pr.pattern.replace(/[^a-z0-9]/gi, \"_\");\n const renderModulePath = join(generatedDir, `param-${i}-${safe}-render.mjs`);\n const pathsModulePath = join(generatedDir, `param-${i}-${safe}-paths.mjs`);\n writeFileSync(renderModulePath, `export default ${pr.render.toString()};\\n`, \"utf8\");\n writeFileSync(\n pathsModulePath,\n `export default ${pr.getStaticPaths.toString()};\\n`,\n \"utf8\",\n );\n const source = `import { makeParameterizedEndpoint } from \"@dualmark/astro/endpoints/parameterized\";\nimport dualmarkConfig from \"./config.mjs\";\nimport render from \"./${rel(generatedDir, renderModulePath)}\";\nimport getStaticPaths from \"./${rel(generatedDir, pathsModulePath)}\";\n\nconst endpoint = makeParameterizedEndpoint({\n getStaticPaths: () => getStaticPaths(),\n render,\n responseOptions: { cacheControl: dualmarkConfig.cacheControl, noindex: dualmarkConfig.noindex },\n});\n\nexport const getStaticPaths = endpoint.getStaticPaths;\nexport const GET = endpoint.GET;\n`;\n const pattern = pr.pattern.startsWith(\"/\") ? pr.pattern + \".md\" : `/${pr.pattern}.md`;\n routes.push({\n pattern,\n fileName: `param-${i}-${safe}.mjs`,\n source,\n });\n }\n\n if (resolved.llmsTxt?.enabled) {\n const sections = resolved.llmsTxt.sections ?? [];\n const source = `import { makeLlmsTxtEndpoint } from \"@dualmark/astro/endpoints/llms-txt\";\n\nconst endpoint = makeLlmsTxtEndpoint({\n brandName: ${JSON.stringify(resolved.llmsTxt.brandName ?? \"Site\")},\n description: ${JSON.stringify(resolved.llmsTxt.description ?? \"\")},\n sections: ${JSON.stringify(sections)},\n});\n\nexport const GET = endpoint.GET;\n`;\n routes.push({ pattern: \"/llms.txt\", fileName: `llms-txt.mjs`, source });\n }\n\n for (const r of routes) {\n const filePath = join(generatedDir, r.fileName);\n writeFileSync(filePath, r.source, \"utf8\");\n opts.injectRoute({\n pattern: r.pattern,\n entrypoint: filePath,\n prerender: true,\n });\n }\n\n if (resolved.middleware.injectLinkHeader && opts.addMiddleware) {\n opts.addMiddleware({\n order: \"post\",\n entrypoint: \"@dualmark/astro/middleware\",\n });\n }\n\n opts.logger.info(\n `[@dualmark/astro] Injected ${routes.length} route(s) and ${\n resolved.middleware.injectLinkHeader ? \"1\" : \"0\"\n } middleware`,\n );\n },\n },\n };\n}\n\nexport default function dualmarkAstro(config: DualmarkAstroConfig): AstroIntegrationLike {\n return createDualmarkIntegration(config);\n}\n","import {\n blogConverter,\n caseStudyConverter,\n changelogConverter,\n compareConverter,\n docsConverter,\n featureConverter,\n glossaryConverter,\n integrationConverter,\n legalConverter,\n pricingConverter,\n pseoConverter,\n statusPageConverter,\n toolConverter,\n videoConverter,\n type BaseConverterConfig,\n type CollectionEntry,\n type Converter,\n} from \"@dualmark/converters\";\n\nexport type BuiltInConverterName =\n | \"blog\"\n | \"case-study\"\n | \"changelog\"\n | \"compare\"\n | \"docs\"\n | \"feature\"\n | \"glossary\"\n | \"integration\"\n | \"legal\"\n | \"pricing\"\n | \"pseo\"\n | \"status-page\"\n | \"tool\"\n | \"video\";\n\nexport interface ResolveConverterArgs {\n name: string;\n collectionName: string;\n baseConfig: BaseConverterConfig;\n}\n\nexport function resolveBuiltInConverter(\n args: ResolveConverterArgs,\n): Converter<CollectionEntry<unknown>> {\n const basePath = `/${args.collectionName}`;\n const cfg = { ...args.baseConfig, basePath };\n switch (args.name as BuiltInConverterName) {\n case \"blog\":\n return blogConverter(cfg) as Converter<CollectionEntry<unknown>>;\n case \"case-study\":\n return caseStudyConverter(cfg) as Converter<CollectionEntry<unknown>>;\n case \"changelog\":\n return changelogConverter(cfg) as Converter<CollectionEntry<unknown>>;\n case \"compare\":\n return compareConverter({ ...cfg, ourBrandColumn: \"Us\" }) as Converter<\n CollectionEntry<unknown>\n >;\n case \"docs\":\n return docsConverter(cfg) as Converter<CollectionEntry<unknown>>;\n case \"feature\":\n return featureConverter(cfg) as Converter<CollectionEntry<unknown>>;\n case \"glossary\":\n return glossaryConverter(cfg) as Converter<CollectionEntry<unknown>>;\n case \"integration\":\n return integrationConverter(cfg) as Converter<CollectionEntry<unknown>>;\n case \"legal\":\n return legalConverter(cfg) as Converter<CollectionEntry<unknown>>;\n case \"pricing\":\n return pricingConverter(cfg) as Converter<CollectionEntry<unknown>>;\n case \"pseo\":\n return pseoConverter(cfg) as Converter<CollectionEntry<unknown>>;\n case \"status-page\":\n return statusPageConverter(cfg) as Converter<CollectionEntry<unknown>>;\n case \"tool\":\n return toolConverter(cfg) as Converter<CollectionEntry<unknown>>;\n case \"video\":\n return videoConverter(cfg) as Converter<CollectionEntry<unknown>>;\n default:\n throw new Error(\n `Dualmark: unknown built-in converter '${args.name}'. Valid names: blog, case-study, changelog, compare, docs, feature, glossary, integration, legal, pricing, pseo, status-page, tool, video. Or pass a function.`,\n );\n }\n}\n"]}
package/dist/index.d.cts CHANGED
@@ -41,7 +41,7 @@ declare function dualmarkAstro(config: DualmarkAstroConfig): AstroIntegrationLik
41
41
  declare class DualmarkConfigError extends Error {
42
42
  constructor(message: string);
43
43
  }
44
- declare function resolveConfig(input: DualmarkAstroConfig): ResolvedDualmarkConfig;
44
+ declare function resolveConfig(input: DualmarkAstroConfig, filePath?: string): ResolvedDualmarkConfig;
45
45
 
46
46
  type BuiltInConverterName = "blog" | "case-study" | "changelog" | "compare" | "docs" | "feature" | "glossary" | "integration" | "legal" | "pricing" | "pseo" | "status-page" | "tool" | "video";
47
47
  interface ResolveConverterArgs {
package/dist/index.d.ts CHANGED
@@ -41,7 +41,7 @@ declare function dualmarkAstro(config: DualmarkAstroConfig): AstroIntegrationLik
41
41
  declare class DualmarkConfigError extends Error {
42
42
  constructor(message: string);
43
43
  }
44
- declare function resolveConfig(input: DualmarkAstroConfig): ResolvedDualmarkConfig;
44
+ declare function resolveConfig(input: DualmarkAstroConfig, filePath?: string): ResolvedDualmarkConfig;
45
45
 
46
46
  type BuiltInConverterName = "blog" | "case-study" | "changelog" | "compare" | "docs" | "feature" | "glossary" | "integration" | "legal" | "pricing" | "pseo" | "status-page" | "tool" | "video";
47
47
  interface ResolveConverterArgs {
package/dist/index.js CHANGED
@@ -12,29 +12,43 @@ var DualmarkConfigError = class extends Error {
12
12
  this.name = "DualmarkConfigError";
13
13
  }
14
14
  };
15
- function resolveConfig(input) {
15
+ function formatError(msg, filePath) {
16
+ return filePath ? `[${filePath}] Dualmark config error: ${msg}` : `Dualmark config error: ${msg}`;
17
+ }
18
+ function resolveConfig(input, filePath) {
16
19
  if (!input || typeof input !== "object") {
17
- throw new DualmarkConfigError("Dualmark config must be an object");
20
+ throw new DualmarkConfigError(formatError("Config must be an object", filePath));
18
21
  }
19
22
  if (typeof input.siteUrl !== "string" || !input.siteUrl) {
20
- throw new DualmarkConfigError("Dualmark config: siteUrl is required (e.g. 'https://example.com')");
23
+ throw new DualmarkConfigError(
24
+ formatError("siteUrl is required (e.g., 'https://example.com')", filePath)
25
+ );
21
26
  }
22
27
  try {
23
28
  new URL(input.siteUrl);
24
29
  } catch {
25
- throw new DualmarkConfigError(`Dualmark config: siteUrl is not a valid URL: ${input.siteUrl}`);
30
+ throw new DualmarkConfigError(
31
+ formatError(`siteUrl is not a valid URL: '${input.siteUrl}'`, filePath)
32
+ );
26
33
  }
27
34
  if (input.siteUrl.endsWith("/")) {
28
- throw new DualmarkConfigError(`Dualmark config: siteUrl must not end with '/': ${input.siteUrl}`);
35
+ throw new DualmarkConfigError(
36
+ formatError(`siteUrl must not end with '/': '${input.siteUrl}'`, filePath)
37
+ );
29
38
  }
30
39
  const collections = input.collections ?? {};
31
40
  for (const [name, c] of Object.entries(collections)) {
32
41
  if (!c.converter) {
33
- throw new DualmarkConfigError(`Dualmark config: collection '${name}' is missing 'converter'`);
42
+ throw new DualmarkConfigError(
43
+ formatError(`Collection '${name}' is missing 'converter'`, filePath)
44
+ );
34
45
  }
35
46
  if (c.route && c.route.startsWith("/")) {
36
47
  throw new DualmarkConfigError(
37
- `Dualmark config: collection '${name}' route should not start with '/' (got '${c.route}')`
48
+ formatError(
49
+ `Collection '${name}' route should not start with '/' (got '${c.route}')`,
50
+ filePath
51
+ )
38
52
  );
39
53
  }
40
54
  }
@@ -42,12 +56,12 @@ function resolveConfig(input) {
42
56
  for (const sp of staticPages) {
43
57
  if (!sp.pattern.startsWith("/")) {
44
58
  throw new DualmarkConfigError(
45
- `Dualmark config: staticPages.pattern must start with '/' (got '${sp.pattern}')`
59
+ formatError(`staticPages.pattern must start with '/' (got '${sp.pattern}')`, filePath)
46
60
  );
47
61
  }
48
62
  if (typeof sp.render !== "function") {
49
63
  throw new DualmarkConfigError(
50
- `Dualmark config: staticPages.render for '${sp.pattern}' must be a function`
64
+ formatError(`staticPages.render for '${sp.pattern}' must be a function`, filePath)
51
65
  );
52
66
  }
53
67
  }
@@ -55,7 +69,10 @@ function resolveConfig(input) {
55
69
  for (const pr of parameterizedRoutes) {
56
70
  if (!pr.pattern.includes("[")) {
57
71
  throw new DualmarkConfigError(
58
- `Dualmark config: parameterizedRoutes.pattern must contain at least one [param] (got '${pr.pattern}')`
72
+ formatError(
73
+ `parameterizedRoutes.pattern must contain at least one [param] (got '${pr.pattern}')`,
74
+ filePath
75
+ )
59
76
  );
60
77
  }
61
78
  }
@@ -82,18 +99,26 @@ function rel(from, to) {
82
99
  return r.split(sep).join("/");
83
100
  }
84
101
  function createDualmarkIntegration(input) {
85
- let resolved;
86
- try {
87
- resolved = resolveConfig(input);
88
- } catch (e) {
89
- if (e instanceof DualmarkConfigError) throw e;
90
- throw e;
91
- }
92
102
  return {
93
103
  name: "@dualmark/astro",
94
104
  hooks: {
95
105
  "astro:config:setup"(opts) {
96
106
  const root = fileURLToPath(opts.config.root);
107
+ const candidates = [
108
+ "astro.config.ts",
109
+ "astro.config.mjs",
110
+ "astro.config.js",
111
+ "astro.config.mts",
112
+ "astro.config.cjs"
113
+ ];
114
+ const configPath = candidates.map((f) => join(root, f)).find(existsSync) ?? join(root, "astro.config.mjs");
115
+ let resolved;
116
+ try {
117
+ resolved = resolveConfig(input, configPath);
118
+ } catch (e) {
119
+ opts.logger.error(`[@dualmark/astro] ${e instanceof Error ? e.message : String(e)}`);
120
+ throw e;
121
+ }
97
122
  const generatedDir = join(root, "node_modules", GENERATED_DIR_NAME);
98
123
  if (!existsSync(generatedDir)) mkdirSync(generatedDir, { recursive: true });
99
124
  writeFileSync(
@@ -160,9 +185,11 @@ const endpoint = makeListingEndpoint({
160
185
 
161
186
  export const GET = endpoint.GET;
162
187
  `;
163
- routes.push(
164
- { pattern: detailPattern, fileName: `collection-${collectionName}-detail.mjs`, source: detailSource }
165
- );
188
+ routes.push({
189
+ pattern: detailPattern,
190
+ fileName: `collection-${collectionName}-detail.mjs`,
191
+ source: detailSource
192
+ });
166
193
  if (c.emitListing !== false) {
167
194
  routes.push({
168
195
  pattern: listingPattern,
@@ -177,12 +204,8 @@ export const GET = endpoint.GET;
177
204
  const safe = sp.pattern.replace(/[^a-z0-9]/gi, "_");
178
205
  const fileName = `static-${i}-${safe}.mjs`;
179
206
  const renderModulePath = join(generatedDir, `static-${i}-${safe}-render.mjs`);
180
- writeFileSync(
181
- renderModulePath,
182
- `export default ${sp.render.toString()};
183
- `,
184
- "utf8"
185
- );
207
+ writeFileSync(renderModulePath, `export default ${sp.render.toString()};
208
+ `, "utf8");
186
209
  const source = `import { makeStaticEndpoint } from "@dualmark/astro/endpoints/static";
187
210
  import dualmarkConfig from "./config.mjs";
188
211
  import render from "./${rel(generatedDir, renderModulePath)}";
@@ -205,8 +228,12 @@ export const GET = endpoint.GET;
205
228
  const pathsModulePath = join(generatedDir, `param-${i}-${safe}-paths.mjs`);
206
229
  writeFileSync(renderModulePath, `export default ${pr.render.toString()};
207
230
  `, "utf8");
208
- writeFileSync(pathsModulePath, `export default ${pr.getStaticPaths.toString()};
209
- `, "utf8");
231
+ writeFileSync(
232
+ pathsModulePath,
233
+ `export default ${pr.getStaticPaths.toString()};
234
+ `,
235
+ "utf8"
236
+ );
210
237
  const source = `import { makeParameterizedEndpoint } from "@dualmark/astro/endpoints/parameterized";
211
238
  import dualmarkConfig from "./config.mjs";
212
239
  import render from "./${rel(generatedDir, renderModulePath)}";
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/config-validation.ts","../src/integration.ts","../src/converter-registry.ts"],"names":[],"mappings":";;;;;;;;AAEO,IAAM,mBAAA,GAAN,cAAkC,KAAA,CAAM;AAAA,EAC7C,YAAY,OAAA,EAAiB;AAC3B,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,IAAA,GAAO,qBAAA;AAAA,EACd;AACF;AAEO,SAAS,cAAc,KAAA,EAAoD;AAChF,EAAA,IAAI,CAAC,KAAA,IAAS,OAAO,KAAA,KAAU,QAAA,EAAU;AACvC,IAAA,MAAM,IAAI,oBAAoB,mCAAmC,CAAA;AAAA,EACnE;AACA,EAAA,IAAI,OAAO,KAAA,CAAM,OAAA,KAAY,QAAA,IAAY,CAAC,MAAM,OAAA,EAAS;AACvD,IAAA,MAAM,IAAI,oBAAoB,mEAAmE,CAAA;AAAA,EACnG;AACA,EAAA,IAAI;AACF,IAAA,IAAI,GAAA,CAAI,MAAM,OAAO,CAAA;AAAA,EACvB,CAAA,CAAA,MAAQ;AACN,IAAA,MAAM,IAAI,mBAAA,CAAoB,CAAA,6CAAA,EAAgD,KAAA,CAAM,OAAO,CAAA,CAAE,CAAA;AAAA,EAC/F;AACA,EAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,QAAA,CAAS,GAAG,CAAA,EAAG;AAC/B,IAAA,MAAM,IAAI,mBAAA,CAAoB,CAAA,gDAAA,EAAmD,KAAA,CAAM,OAAO,CAAA,CAAE,CAAA;AAAA,EAClG;AAEA,EAAA,MAAM,WAAA,GAAc,KAAA,CAAM,WAAA,IAAe,EAAC;AAC1C,EAAA,KAAA,MAAW,CAAC,IAAA,EAAM,CAAC,KAAK,MAAA,CAAO,OAAA,CAAQ,WAAW,CAAA,EAAG;AACnD,IAAA,IAAI,CAAC,EAAE,SAAA,EAAW;AAChB,MAAA,MAAM,IAAI,mBAAA,CAAoB,CAAA,6BAAA,EAAgC,IAAI,CAAA,wBAAA,CAA0B,CAAA;AAAA,IAC9F;AACA,IAAA,IAAI,EAAE,KAAA,IAAS,CAAA,CAAE,KAAA,CAAM,UAAA,CAAW,GAAG,CAAA,EAAG;AACtC,MAAA,MAAM,IAAI,mBAAA;AAAA,QACR,CAAA,6BAAA,EAAgC,IAAI,CAAA,wCAAA,EAA2C,CAAA,CAAE,KAAK,CAAA,EAAA;AAAA,OACxF;AAAA,IACF;AAAA,EACF;AAEA,EAAA,MAAM,WAAA,GAAc,KAAA,CAAM,WAAA,IAAe,EAAC;AAC1C,EAAA,KAAA,MAAW,MAAM,WAAA,EAAa;AAC5B,IAAA,IAAI,CAAC,EAAA,CAAG,OAAA,CAAQ,UAAA,CAAW,GAAG,CAAA,EAAG;AAC/B,MAAA,MAAM,IAAI,mBAAA;AAAA,QACR,CAAA,+DAAA,EAAkE,GAAG,OAAO,CAAA,EAAA;AAAA,OAC9E;AAAA,IACF;AACA,IAAA,IAAI,OAAO,EAAA,CAAG,MAAA,KAAW,UAAA,EAAY;AACnC,MAAA,MAAM,IAAI,mBAAA;AAAA,QACR,CAAA,yCAAA,EAA4C,GAAG,OAAO,CAAA,oBAAA;AAAA,OACxD;AAAA,IACF;AAAA,EACF;AAEA,EAAA,MAAM,mBAAA,GAAsB,KAAA,CAAM,mBAAA,IAAuB,EAAC;AAC1D,EAAA,KAAA,MAAW,MAAM,mBAAA,EAAqB;AACpC,IAAA,IAAI,CAAC,EAAA,CAAG,OAAA,CAAQ,QAAA,CAAS,GAAG,CAAA,EAAG;AAC7B,MAAA,MAAM,IAAI,mBAAA;AAAA,QACR,CAAA,qFAAA,EAAwF,GAAG,OAAO,CAAA,EAAA;AAAA,OACpG;AAAA,IACF;AAAA,EACF;AAEA,EAAA,OAAO;AAAA,IACL,SAAS,KAAA,CAAM,OAAA;AAAA,IACf,WAAA;AAAA,IACA,WAAA;AAAA,IACA,mBAAA;AAAA,IACA,SAAS,KAAA,CAAM,OAAA;AAAA,IACf,UAAA,EAAY;AAAA,MACV,gBAAA,EAAkB,KAAA,CAAM,UAAA,EAAY,gBAAA,KAAqB;AAAA,KAC3D;AAAA,IACA,OAAA,EAAS;AAAA,MACP,YAAA,EAAc,KAAA,CAAM,OAAA,EAAS,YAAA,IAAgB,sBAAA;AAAA,MAC7C,OAAA,EAAS,KAAA,CAAM,OAAA,EAAS,OAAA,KAAY;AAAA;AACtC,GACF;AACF;;;AClCA,IAAM,kBAAA,GAAqB,qBAAA;AAQ3B,SAAS,GAAA,CAAI,MAAc,EAAA,EAAoB;AAC7C,EAAA,MAAM,CAAA,GAAI,QAAA,CAAS,IAAA,EAAM,EAAE,CAAA;AAC3B,EAAA,OAAO,CAAA,CAAE,KAAA,CAAM,GAAG,CAAA,CAAE,KAAK,GAAG,CAAA;AAC9B;AAEO,SAAS,0BAA0B,KAAA,EAAkD;AAC1F,EAAA,IAAI,QAAA;AACJ,EAAA,IAAI;AACF,IAAA,QAAA,GAAW,cAAc,KAAK,CAAA;AAAA,EAChC,SAAS,CAAA,EAAG;AACV,IAAA,IAAI,CAAA,YAAa,qBAAqB,MAAM,CAAA;AAC5C,IAAA,MAAM,CAAA;AAAA,EACR;AAEA,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,iBAAA;AAAA,IACN,KAAA,EAAO;AAAA,MACL,qBAAqB,IAAA,EAAM;AACzB,QAAA,MAAM,IAAA,GAAO,aAAA,CAAc,IAAA,CAAK,MAAA,CAAO,IAAI,CAAA;AAC3C,QAAA,MAAM,YAAA,GAAe,IAAA,CAAK,IAAA,EAAM,cAAA,EAAgB,kBAAkB,CAAA;AAClE,QAAA,IAAI,CAAC,WAAW,YAAY,CAAA,YAAa,YAAA,EAAc,EAAE,SAAA,EAAW,IAAA,EAAM,CAAA;AAE1E,QAAA,aAAA;AAAA,UACE,IAAA,CAAK,cAAc,YAAY,CAAA;AAAA,UAC/B,kBAAkB,IAAA,CAAK,SAAA;AAAA,YACrB;AAAA,cACE,SAAS,QAAA,CAAS,OAAA;AAAA,cAClB,YAAA,EAAc,SAAS,OAAA,CAAQ,YAAA;AAAA,cAC/B,OAAA,EAAS,SAAS,OAAA,CAAQ;AAAA,aAC5B;AAAA,YACA,IAAA;AAAA,YACA;AAAA,WACD,CAAA;AAAA,CAAA;AAAA,UACD;AAAA,SACF;AAEA,QAAA,MAAM,SAAsB,EAAC;AAE7B,QAAA,KAAA,MAAW,CAAC,gBAAgB,CAAC,CAAA,IAAK,OAAO,OAAA,CAAQ,QAAA,CAAS,WAAW,CAAA,EAAG;AACtE,UAAA,MAAM,KAAA,GAAQ,EAAE,KAAA,IAAS,cAAA;AACzB,UAAA,MAAM,OAAA,GAAU,CAAA,CAAE,YAAA,KAAiB,QAAA,GAAW,QAAA,GAAW,WAAA;AACzD,UAAA,MAAM,aAAA,GAAgB,CAAA,CAAA,EAAI,KAAK,CAAA,CAAA,EAAI,OAAO,CAAA,GAAA,CAAA;AAC1C,UAAA,MAAM,cAAA,GAAiB,IAAI,KAAK,CAAA,GAAA,CAAA;AAChC,UAAA,IAAI,OAAO,CAAA,CAAE,SAAA,KAAc,QAAA,EAAU;AACnC,YAAA,IAAA,CAAK,MAAA,CAAO,IAAA;AAAA,cACV,iCAAiC,cAAc,CAAA,mJAAA;AAAA,aACjD;AACA,YAAA;AAAA,UACF;AACA,UAAA,MAAM,eAAA,GAAkB,CAAA,0DAAA,CAAA;AACxB,UAAA,MAAM,YAAA,GAAe,GAAG,eAAe;AAAA;AAAA;AAAA;;AAAA;AAAA,QAAA,EAMvC,IAAA,CAAK,SAAA,CAAU,CAAA,CAAE,SAAS,CAAC,CAAA;AAAA,kBAAA,EACjB,IAAA,CAAK,SAAA,CAAU,cAAc,CAAC,CAAA;AAAA;AAAA;;AAAA;AAAA,kBAAA,EAK9B,IAAA,CAAK,SAAA,CAAU,cAAc,CAAC,CAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA,CAAA;AAUxC,UAAA,MAAM,aAAA,GAAgB,CAAA;AAAA;AAAA;;AAAA;AAAA,kBAAA,EAKZ,IAAA,CAAK,SAAA,CAAU,cAAc,CAAC,CAAA;AAAA;AAAA,YAAA,EAEpC,IAAA,CAAK,SAAA,CAAU,GAAA,GAAM,KAAK,CAAC,CAAA;AAAA,SAAA,EAC9B,KAAK,SAAA,CAAU,CAAA,CAAE,eAAA,EAAiB,KAAA,IAAS,cAAc,CAAC,CAAA;AAAA,eAAA,EACpD,IAAA,CAAK,UAAU,CAAA,CAAE,eAAA,EAAiB,eAAe,CAAA,IAAA,EAAO,cAAc,WAAW,CAAC,CAAA;AAAA;AAAA;AAAA;;AAAA;AAAA,CAAA;AAQzF,UAAA,MAAA,CAAO,IAAA;AAAA,YACL,EAAE,SAAS,aAAA,EAAe,QAAA,EAAU,cAAc,cAAc,CAAA,WAAA,CAAA,EAAe,QAAQ,YAAA;AAAa,WACtG;AACA,UAAA,IAAI,CAAA,CAAE,gBAAgB,KAAA,EAAO;AAC3B,YAAA,MAAA,CAAO,IAAA,CAAK;AAAA,cACV,OAAA,EAAS,cAAA;AAAA,cACT,QAAA,EAAU,cAAc,cAAc,CAAA,YAAA,CAAA;AAAA,cACtC,MAAA,EAAQ;AAAA,aACT,CAAA;AAAA,UACH;AAAA,QACF;AAEA,QAAA,KAAA,IAAS,IAAI,CAAA,EAAG,CAAA,GAAI,QAAA,CAAS,WAAA,CAAY,QAAQ,CAAA,EAAA,EAAK;AACpD,UAAA,MAAM,EAAA,GAAK,QAAA,CAAS,WAAA,CAAY,CAAC,CAAA;AACjC,UAAA,IAAI,CAAC,EAAA,EAAI;AACT,UAAA,MAAM,IAAA,GAAO,EAAA,CAAG,OAAA,CAAQ,OAAA,CAAQ,eAAe,GAAG,CAAA;AAClD,UAAA,MAAM,QAAA,GAAW,CAAA,OAAA,EAAU,CAAC,CAAA,CAAA,EAAI,IAAI,CAAA,IAAA,CAAA;AACpC,UAAA,MAAM,mBAAmB,IAAA,CAAK,YAAA,EAAc,UAAU,CAAC,CAAA,CAAA,EAAI,IAAI,CAAA,WAAA,CAAa,CAAA;AAC5E,UAAA,aAAA;AAAA,YACE,gBAAA;AAAA,YACA,CAAA,eAAA,EAAkB,EAAA,CAAG,MAAA,CAAO,QAAA,EAAU,CAAA;AAAA,CAAA;AAAA,YACtC;AAAA,WACF;AACA,UAAA,MAAM,MAAA,GAAS,CAAA;AAAA;AAAA,sBAAA,EAED,GAAA,CAAI,YAAA,EAAc,gBAAgB,CAAC,CAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA,CAAA;AASjD,UAAA,MAAM,SAAA,GAAY,EAAA,CAAG,OAAA,KAAY,GAAA,GAAM,WAAA,GAAc,GAAG,OAAA,CAAQ,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAA,GAAI,KAAA;AACrF,UAAA,MAAA,CAAO,KAAK,EAAE,OAAA,EAAS,SAAA,EAAW,QAAA,EAAU,QAAQ,CAAA;AAAA,QACtD;AAEA,QAAA,KAAA,IAAS,IAAI,CAAA,EAAG,CAAA,GAAI,QAAA,CAAS,mBAAA,CAAoB,QAAQ,CAAA,EAAA,EAAK;AAC5D,UAAA,MAAM,EAAA,GAAK,QAAA,CAAS,mBAAA,CAAoB,CAAC,CAAA;AACzC,UAAA,IAAI,CAAC,EAAA,EAAI;AACT,UAAA,MAAM,IAAA,GAAO,EAAA,CAAG,OAAA,CAAQ,OAAA,CAAQ,eAAe,GAAG,CAAA;AAClD,UAAA,MAAM,mBAAmB,IAAA,CAAK,YAAA,EAAc,SAAS,CAAC,CAAA,CAAA,EAAI,IAAI,CAAA,WAAA,CAAa,CAAA;AAC3E,UAAA,MAAM,kBAAkB,IAAA,CAAK,YAAA,EAAc,SAAS,CAAC,CAAA,CAAA,EAAI,IAAI,CAAA,UAAA,CAAY,CAAA;AACzE,UAAA,aAAA,CAAc,gBAAA,EAAkB,CAAA,eAAA,EAAkB,EAAA,CAAG,MAAA,CAAO,UAAU,CAAA;AAAA,CAAA,EAAO,MAAM,CAAA;AACnF,UAAA,aAAA,CAAc,eAAA,EAAiB,CAAA,eAAA,EAAkB,EAAA,CAAG,cAAA,CAAe,UAAU,CAAA;AAAA,CAAA,EAAO,MAAM,CAAA;AAC1F,UAAA,MAAM,MAAA,GAAS,CAAA;AAAA;AAAA,sBAAA,EAED,GAAA,CAAI,YAAA,EAAc,gBAAgB,CAAC,CAAA;AAAA,8BAAA,EAC3B,GAAA,CAAI,YAAA,EAAc,eAAe,CAAC,CAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA,CAAA;AAWxD,UAAA,MAAM,OAAA,GAAU,EAAA,CAAG,OAAA,CAAQ,UAAA,CAAW,GAAG,CAAA,GAAI,EAAA,CAAG,OAAA,GAAU,KAAA,GAAQ,CAAA,CAAA,EAAI,EAAA,CAAG,OAAO,CAAA,GAAA,CAAA;AAChF,UAAA,MAAA,CAAO,IAAA,CAAK;AAAA,YACV,OAAA;AAAA,YACA,QAAA,EAAU,CAAA,MAAA,EAAS,CAAC,CAAA,CAAA,EAAI,IAAI,CAAA,IAAA,CAAA;AAAA,YAC5B;AAAA,WACD,CAAA;AAAA,QACH;AAEA,QAAA,IAAI,QAAA,CAAS,SAAS,OAAA,EAAS;AAC7B,UAAA,MAAM,QAAA,GAAW,QAAA,CAAS,OAAA,CAAQ,QAAA,IAAY,EAAC;AAC/C,UAAA,MAAM,MAAA,GAAS,CAAA;;AAAA;AAAA,aAAA,EAGV,KAAK,SAAA,CAAU,QAAA,CAAS,OAAA,CAAQ,SAAA,IAAa,MAAM,CAAC,CAAA;AAAA,eAAA,EAClD,KAAK,SAAA,CAAU,QAAA,CAAS,OAAA,CAAQ,WAAA,IAAe,EAAE,CAAC,CAAA;AAAA,YAAA,EACrD,IAAA,CAAK,SAAA,CAAU,QAAQ,CAAC,CAAA;AAAA;;AAAA;AAAA,CAAA;AAK5B,UAAA,MAAA,CAAO,KAAK,EAAE,OAAA,EAAS,aAAa,QAAA,EAAU,CAAA,YAAA,CAAA,EAAgB,QAAQ,CAAA;AAAA,QACxE;AAEA,QAAA,KAAA,MAAW,KAAK,MAAA,EAAQ;AACtB,UAAA,MAAM,QAAA,GAAW,IAAA,CAAK,YAAA,EAAc,CAAA,CAAE,QAAQ,CAAA;AAC9C,UAAA,aAAA,CAAc,QAAA,EAAU,CAAA,CAAE,MAAA,EAAQ,MAAM,CAAA;AACxC,UAAA,IAAA,CAAK,WAAA,CAAY;AAAA,YACf,SAAS,CAAA,CAAE,OAAA;AAAA,YACX,UAAA,EAAY,QAAA;AAAA,YACZ,SAAA,EAAW;AAAA,WACZ,CAAA;AAAA,QACH;AAEA,QAAA,IAAI,QAAA,CAAS,UAAA,CAAW,gBAAA,IAAoB,IAAA,CAAK,aAAA,EAAe;AAC9D,UAAA,IAAA,CAAK,aAAA,CAAc;AAAA,YACjB,KAAA,EAAO,MAAA;AAAA,YACP,UAAA,EAAY;AAAA,WACb,CAAA;AAAA,QACH;AAEA,QAAA,IAAA,CAAK,MAAA,CAAO,IAAA;AAAA,UACV,CAAA,2BAAA,EAA8B,OAAO,MAAM,CAAA,cAAA,EACzC,SAAS,UAAA,CAAW,gBAAA,GAAmB,MAAM,GAC/C,CAAA,WAAA;AAAA,SACF;AAEK,MACP;AAAA;AACF,GACF;AACF;AAEe,SAAR,cAA+B,MAAA,EAAmD;AACvF,EAAA,OAAO,0BAA0B,MAAM,CAAA;AACzC;ACjNO,SAAS,wBACd,IAAA,EACqC;AACrC,EAAA,MAAM,QAAA,GAAW,CAAA,CAAA,EAAI,IAAA,CAAK,cAAc,CAAA,CAAA;AACxC,EAAA,MAAM,GAAA,GAAM,EAAE,GAAG,IAAA,CAAK,YAAY,QAAA,EAAS;AAC3C,EAAA,QAAQ,KAAK,IAAA;AAA8B,IACzC,KAAK,MAAA;AACH,MAAA,OAAO,cAAc,GAAG,CAAA;AAAA,IAC1B,KAAK,YAAA;AACH,MAAA,OAAO,mBAAmB,GAAG,CAAA;AAAA,IAC/B,KAAK,WAAA;AACH,MAAA,OAAO,mBAAmB,GAAG,CAAA;AAAA,IAC/B,KAAK,SAAA;AACH,MAAA,OAAO,iBAAiB,EAAE,GAAG,GAAA,EAAK,cAAA,EAAgB,MAAM,CAAA;AAAA,IAG1D,KAAK,MAAA;AACH,MAAA,OAAO,cAAc,GAAG,CAAA;AAAA,IAC1B,KAAK,SAAA;AACH,MAAA,OAAO,iBAAiB,GAAG,CAAA;AAAA,IAC7B,KAAK,UAAA;AACH,MAAA,OAAO,kBAAkB,GAAG,CAAA;AAAA,IAC9B,KAAK,aAAA;AACH,MAAA,OAAO,qBAAqB,GAAG,CAAA;AAAA,IACjC,KAAK,OAAA;AACH,MAAA,OAAO,eAAe,GAAG,CAAA;AAAA,IAC3B,KAAK,SAAA;AACH,MAAA,OAAO,iBAAiB,GAAG,CAAA;AAAA,IAC7B,KAAK,MAAA;AACH,MAAA,OAAO,cAAc,GAAG,CAAA;AAAA,IAC1B,KAAK,aAAA;AACH,MAAA,OAAO,oBAAoB,GAAG,CAAA;AAAA,IAChC,KAAK,MAAA;AACH,MAAA,OAAO,cAAc,GAAG,CAAA;AAAA,IAC1B,KAAK,OAAA;AACH,MAAA,OAAO,eAAe,GAAG,CAAA;AAAA,IAC3B;AACE,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAA,sCAAA,EAAyC,KAAK,IAAI,CAAA,+JAAA;AAAA,OACpD;AAAA;AAEN","file":"index.js","sourcesContent":["import type { DualmarkAstroConfig, ResolvedDualmarkConfig } from \"./types.js\";\n\nexport class DualmarkConfigError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"DualmarkConfigError\";\n }\n}\n\nexport function resolveConfig(input: DualmarkAstroConfig): ResolvedDualmarkConfig {\n if (!input || typeof input !== \"object\") {\n throw new DualmarkConfigError(\"Dualmark config must be an object\");\n }\n if (typeof input.siteUrl !== \"string\" || !input.siteUrl) {\n throw new DualmarkConfigError(\"Dualmark config: siteUrl is required (e.g. 'https://example.com')\");\n }\n try {\n new URL(input.siteUrl);\n } catch {\n throw new DualmarkConfigError(`Dualmark config: siteUrl is not a valid URL: ${input.siteUrl}`);\n }\n if (input.siteUrl.endsWith(\"/\")) {\n throw new DualmarkConfigError(`Dualmark config: siteUrl must not end with '/': ${input.siteUrl}`);\n }\n\n const collections = input.collections ?? {};\n for (const [name, c] of Object.entries(collections)) {\n if (!c.converter) {\n throw new DualmarkConfigError(`Dualmark config: collection '${name}' is missing 'converter'`);\n }\n if (c.route && c.route.startsWith(\"/\")) {\n throw new DualmarkConfigError(\n `Dualmark config: collection '${name}' route should not start with '/' (got '${c.route}')`,\n );\n }\n }\n\n const staticPages = input.staticPages ?? [];\n for (const sp of staticPages) {\n if (!sp.pattern.startsWith(\"/\")) {\n throw new DualmarkConfigError(\n `Dualmark config: staticPages.pattern must start with '/' (got '${sp.pattern}')`,\n );\n }\n if (typeof sp.render !== \"function\") {\n throw new DualmarkConfigError(\n `Dualmark config: staticPages.render for '${sp.pattern}' must be a function`,\n );\n }\n }\n\n const parameterizedRoutes = input.parameterizedRoutes ?? [];\n for (const pr of parameterizedRoutes) {\n if (!pr.pattern.includes(\"[\")) {\n throw new DualmarkConfigError(\n `Dualmark config: parameterizedRoutes.pattern must contain at least one [param] (got '${pr.pattern}')`,\n );\n }\n }\n\n return {\n siteUrl: input.siteUrl,\n collections,\n staticPages,\n parameterizedRoutes,\n llmsTxt: input.llmsTxt,\n middleware: {\n injectLinkHeader: input.middleware?.injectLinkHeader !== false,\n },\n headers: {\n cacheControl: input.headers?.cacheControl ?? \"public, max-age=3600\",\n noindex: input.headers?.noindex !== false,\n },\n };\n}\n","import { mkdirSync, writeFileSync, existsSync } from \"node:fs\";\nimport { fileURLToPath } from \"node:url\";\nimport { join, relative, resolve, sep } from \"node:path\";\nimport { resolveConfig, DualmarkConfigError } from \"./config-validation.js\";\nimport type { DualmarkAstroConfig, ResolvedDualmarkConfig } from \"./types.js\";\n\ninterface AstroIntegrationLogger {\n info: (msg: string) => void;\n warn: (msg: string) => void;\n error: (msg: string) => void;\n}\n\ninterface InjectedRoute {\n pattern: string;\n entrypoint: string | URL;\n prerender?: boolean;\n}\n\ninterface AstroIntegrationMiddleware {\n order: \"pre\" | \"post\";\n entrypoint: string | URL;\n}\n\ninterface ConfigSetupHookOptions {\n config: { root: URL; srcDir?: URL };\n command: string;\n isRestart?: boolean;\n injectRoute: (route: InjectedRoute) => void;\n addMiddleware?: (mw: AstroIntegrationMiddleware) => void;\n logger: AstroIntegrationLogger;\n updateConfig?: (cfg: unknown) => unknown;\n}\n\nexport interface AstroIntegrationLike {\n name: string;\n hooks: {\n \"astro:config:setup\": (opts: ConfigSetupHookOptions) => void | Promise<void>;\n };\n}\n\nconst GENERATED_DIR_NAME = \".dualmark-generated\";\n\ninterface RouteSpec {\n pattern: string;\n fileName: string;\n source: string;\n}\n\nfunction rel(from: string, to: string): string {\n const r = relative(from, to);\n return r.split(sep).join(\"/\");\n}\n\nexport function createDualmarkIntegration(input: DualmarkAstroConfig): AstroIntegrationLike {\n let resolved: ResolvedDualmarkConfig;\n try {\n resolved = resolveConfig(input);\n } catch (e) {\n if (e instanceof DualmarkConfigError) throw e;\n throw e;\n }\n\n return {\n name: \"@dualmark/astro\",\n hooks: {\n \"astro:config:setup\"(opts) {\n const root = fileURLToPath(opts.config.root);\n const generatedDir = join(root, \"node_modules\", GENERATED_DIR_NAME);\n if (!existsSync(generatedDir)) mkdirSync(generatedDir, { recursive: true });\n\n writeFileSync(\n join(generatedDir, \"config.mjs\"),\n `export default ${JSON.stringify(\n {\n siteUrl: resolved.siteUrl,\n cacheControl: resolved.headers.cacheControl,\n noindex: resolved.headers.noindex,\n },\n null,\n 2,\n )};\\n`,\n \"utf8\",\n );\n\n const routes: RouteSpec[] = [];\n\n for (const [collectionName, c] of Object.entries(resolved.collections)) {\n const route = c.route ?? collectionName;\n const slugSeg = c.slugStrategy === \"single\" ? \"[slug]\" : \"[...slug]\";\n const detailPattern = `/${route}/${slugSeg}.md`;\n const listingPattern = `/${route}.md`;\n if (typeof c.converter !== \"string\") {\n opts.logger.warn(\n `[@dualmark/astro] Collection '${collectionName}' uses an inline converter function — this isn't yet serializable into a generated route. Use a built-in converter name (e.g. 'blog') for now.`,\n );\n continue;\n }\n const converterImport = `import { resolveBuiltInConverter } from \"@dualmark/astro\";`;\n const detailSource = `${converterImport}\nimport { makeCollectionDetailEndpoint } from \"@dualmark/astro/endpoints/collection\";\nimport { getCollection } from \"astro:content\";\nimport dualmarkConfig from \"./config.mjs\";\n\nconst converter = resolveBuiltInConverter({\n name: ${JSON.stringify(c.converter)},\n collectionName: ${JSON.stringify(collectionName)},\n baseConfig: { siteUrl: dualmarkConfig.siteUrl },\n});\n\nconst endpoint = makeCollectionDetailEndpoint({\n collectionName: ${JSON.stringify(collectionName)},\n converter,\n getCollection: (name, filter) => getCollection(name, filter),\n responseOptions: { cacheControl: dualmarkConfig.cacheControl, noindex: dualmarkConfig.noindex },\n});\n\nexport const getStaticPaths = endpoint.getStaticPaths;\nexport const GET = endpoint.GET;\n`;\n\n const listingSource = `import { makeListingEndpoint } from \"@dualmark/astro/endpoints/listing\";\nimport { getCollection } from \"astro:content\";\nimport dualmarkConfig from \"./config.mjs\";\n\nconst endpoint = makeListingEndpoint({\n collectionName: ${JSON.stringify(collectionName)},\n siteUrl: dualmarkConfig.siteUrl,\n basePath: ${JSON.stringify(\"/\" + route)},\n title: ${JSON.stringify(c.listingMetadata?.title ?? collectionName)},\n description: ${JSON.stringify(c.listingMetadata?.description ?? `All ${collectionName} entries.`)},\n getCollection: (name, filter) => getCollection(name, filter),\n responseOptions: { cacheControl: dualmarkConfig.cacheControl, noindex: dualmarkConfig.noindex },\n});\n\nexport const GET = endpoint.GET;\n`;\n\n routes.push(\n { pattern: detailPattern, fileName: `collection-${collectionName}-detail.mjs`, source: detailSource },\n );\n if (c.emitListing !== false) {\n routes.push({\n pattern: listingPattern,\n fileName: `collection-${collectionName}-listing.mjs`,\n source: listingSource,\n });\n }\n }\n\n for (let i = 0; i < resolved.staticPages.length; i++) {\n const sp = resolved.staticPages[i];\n if (!sp) continue;\n const safe = sp.pattern.replace(/[^a-z0-9]/gi, \"_\");\n const fileName = `static-${i}-${safe}.mjs`;\n const renderModulePath = join(generatedDir, `static-${i}-${safe}-render.mjs`);\n writeFileSync(\n renderModulePath,\n `export default ${sp.render.toString()};\\n`,\n \"utf8\",\n );\n const source = `import { makeStaticEndpoint } from \"@dualmark/astro/endpoints/static\";\nimport dualmarkConfig from \"./config.mjs\";\nimport render from \"./${rel(generatedDir, renderModulePath)}\";\n\nconst endpoint = makeStaticEndpoint({\n render,\n responseOptions: { cacheControl: dualmarkConfig.cacheControl, noindex: dualmarkConfig.noindex },\n});\n\nexport const GET = endpoint.GET;\n`;\n const mdPattern = sp.pattern === \"/\" ? \"/index.md\" : sp.pattern.replace(/\\/$/, \"\") + \".md\";\n routes.push({ pattern: mdPattern, fileName, source });\n }\n\n for (let i = 0; i < resolved.parameterizedRoutes.length; i++) {\n const pr = resolved.parameterizedRoutes[i];\n if (!pr) continue;\n const safe = pr.pattern.replace(/[^a-z0-9]/gi, \"_\");\n const renderModulePath = join(generatedDir, `param-${i}-${safe}-render.mjs`);\n const pathsModulePath = join(generatedDir, `param-${i}-${safe}-paths.mjs`);\n writeFileSync(renderModulePath, `export default ${pr.render.toString()};\\n`, \"utf8\");\n writeFileSync(pathsModulePath, `export default ${pr.getStaticPaths.toString()};\\n`, \"utf8\");\n const source = `import { makeParameterizedEndpoint } from \"@dualmark/astro/endpoints/parameterized\";\nimport dualmarkConfig from \"./config.mjs\";\nimport render from \"./${rel(generatedDir, renderModulePath)}\";\nimport getStaticPaths from \"./${rel(generatedDir, pathsModulePath)}\";\n\nconst endpoint = makeParameterizedEndpoint({\n getStaticPaths: () => getStaticPaths(),\n render,\n responseOptions: { cacheControl: dualmarkConfig.cacheControl, noindex: dualmarkConfig.noindex },\n});\n\nexport const getStaticPaths = endpoint.getStaticPaths;\nexport const GET = endpoint.GET;\n`;\n const pattern = pr.pattern.startsWith(\"/\") ? pr.pattern + \".md\" : `/${pr.pattern}.md`;\n routes.push({\n pattern,\n fileName: `param-${i}-${safe}.mjs`,\n source,\n });\n }\n\n if (resolved.llmsTxt?.enabled) {\n const sections = resolved.llmsTxt.sections ?? [];\n const source = `import { makeLlmsTxtEndpoint } from \"@dualmark/astro/endpoints/llms-txt\";\n\nconst endpoint = makeLlmsTxtEndpoint({\n brandName: ${JSON.stringify(resolved.llmsTxt.brandName ?? \"Site\")},\n description: ${JSON.stringify(resolved.llmsTxt.description ?? \"\")},\n sections: ${JSON.stringify(sections)},\n});\n\nexport const GET = endpoint.GET;\n`;\n routes.push({ pattern: \"/llms.txt\", fileName: `llms-txt.mjs`, source });\n }\n\n for (const r of routes) {\n const filePath = join(generatedDir, r.fileName);\n writeFileSync(filePath, r.source, \"utf8\");\n opts.injectRoute({\n pattern: r.pattern,\n entrypoint: filePath,\n prerender: true,\n });\n }\n\n if (resolved.middleware.injectLinkHeader && opts.addMiddleware) {\n opts.addMiddleware({\n order: \"post\",\n entrypoint: \"@dualmark/astro/middleware\",\n });\n }\n\n opts.logger.info(\n `[@dualmark/astro] Injected ${routes.length} route(s) and ${\n resolved.middleware.injectLinkHeader ? \"1\" : \"0\"\n } middleware`,\n );\n\n void resolve;\n },\n },\n };\n}\n\nexport default function dualmarkAstro(config: DualmarkAstroConfig): AstroIntegrationLike {\n return createDualmarkIntegration(config);\n}\n","import {\n blogConverter,\n caseStudyConverter,\n changelogConverter,\n compareConverter,\n docsConverter,\n featureConverter,\n glossaryConverter,\n integrationConverter,\n legalConverter,\n pricingConverter,\n pseoConverter,\n statusPageConverter,\n toolConverter,\n videoConverter,\n type BaseConverterConfig,\n type CollectionEntry,\n type Converter,\n} from \"@dualmark/converters\";\n\nexport type BuiltInConverterName =\n | \"blog\"\n | \"case-study\"\n | \"changelog\"\n | \"compare\"\n | \"docs\"\n | \"feature\"\n | \"glossary\"\n | \"integration\"\n | \"legal\"\n | \"pricing\"\n | \"pseo\"\n | \"status-page\"\n | \"tool\"\n | \"video\";\n\nexport interface ResolveConverterArgs {\n name: string;\n collectionName: string;\n baseConfig: BaseConverterConfig;\n}\n\nexport function resolveBuiltInConverter(\n args: ResolveConverterArgs,\n): Converter<CollectionEntry<unknown>> {\n const basePath = `/${args.collectionName}`;\n const cfg = { ...args.baseConfig, basePath };\n switch (args.name as BuiltInConverterName) {\n case \"blog\":\n return blogConverter(cfg) as Converter<CollectionEntry<unknown>>;\n case \"case-study\":\n return caseStudyConverter(cfg) as Converter<CollectionEntry<unknown>>;\n case \"changelog\":\n return changelogConverter(cfg) as Converter<CollectionEntry<unknown>>;\n case \"compare\":\n return compareConverter({ ...cfg, ourBrandColumn: \"Us\" }) as Converter<\n CollectionEntry<unknown>\n >;\n case \"docs\":\n return docsConverter(cfg) as Converter<CollectionEntry<unknown>>;\n case \"feature\":\n return featureConverter(cfg) as Converter<CollectionEntry<unknown>>;\n case \"glossary\":\n return glossaryConverter(cfg) as Converter<CollectionEntry<unknown>>;\n case \"integration\":\n return integrationConverter(cfg) as Converter<CollectionEntry<unknown>>;\n case \"legal\":\n return legalConverter(cfg) as Converter<CollectionEntry<unknown>>;\n case \"pricing\":\n return pricingConverter(cfg) as Converter<CollectionEntry<unknown>>;\n case \"pseo\":\n return pseoConverter(cfg) as Converter<CollectionEntry<unknown>>;\n case \"status-page\":\n return statusPageConverter(cfg) as Converter<CollectionEntry<unknown>>;\n case \"tool\":\n return toolConverter(cfg) as Converter<CollectionEntry<unknown>>;\n case \"video\":\n return videoConverter(cfg) as Converter<CollectionEntry<unknown>>;\n default:\n throw new Error(\n `Dualmark: unknown built-in converter '${args.name}'. Valid names: blog, case-study, changelog, compare, docs, feature, glossary, integration, legal, pricing, pseo, status-page, tool, video. Or pass a function.`,\n );\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/config-validation.ts","../src/integration.ts","../src/converter-registry.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { DualmarkAstroConfig, ResolvedDualmarkConfig } from \"./types.js\";\n\nexport class DualmarkConfigError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"DualmarkConfigError\";\n }\n}\n\nfunction formatError(msg: string, filePath?: string): string {\n return filePath ? `[${filePath}] Dualmark config error: ${msg}` : `Dualmark config error: ${msg}`;\n}\n\nexport function resolveConfig(\n input: DualmarkAstroConfig,\n filePath?: string,\n): ResolvedDualmarkConfig {\n if (!input || typeof input !== \"object\") {\n throw new DualmarkConfigError(formatError(\"Config must be an object\", filePath));\n }\n if (typeof input.siteUrl !== \"string\" || !input.siteUrl) {\n throw new DualmarkConfigError(\n formatError(\"siteUrl is required (e.g., 'https://example.com')\", filePath),\n );\n }\n try {\n new URL(input.siteUrl);\n } catch {\n throw new DualmarkConfigError(\n formatError(`siteUrl is not a valid URL: '${input.siteUrl}'`, filePath),\n );\n }\n if (input.siteUrl.endsWith(\"/\")) {\n throw new DualmarkConfigError(\n formatError(`siteUrl must not end with '/': '${input.siteUrl}'`, filePath),\n );\n }\n const collections = input.collections ?? {};\n for (const [name, c] of Object.entries(collections)) {\n if (!c.converter) {\n throw new DualmarkConfigError(\n formatError(`Collection '${name}' is missing 'converter'`, filePath),\n );\n }\n if (c.route && c.route.startsWith(\"/\")) {\n throw new DualmarkConfigError(\n formatError(\n `Collection '${name}' route should not start with '/' (got '${c.route}')`,\n filePath,\n ),\n );\n }\n }\n const staticPages = input.staticPages ?? [];\n for (const sp of staticPages) {\n if (!sp.pattern.startsWith(\"/\")) {\n throw new DualmarkConfigError(\n formatError(`staticPages.pattern must start with '/' (got '${sp.pattern}')`, filePath),\n );\n }\n if (typeof sp.render !== \"function\") {\n throw new DualmarkConfigError(\n formatError(`staticPages.render for '${sp.pattern}' must be a function`, filePath),\n );\n }\n }\n const parameterizedRoutes = input.parameterizedRoutes ?? [];\n for (const pr of parameterizedRoutes) {\n if (!pr.pattern.includes(\"[\")) {\n throw new DualmarkConfigError(\n formatError(\n `parameterizedRoutes.pattern must contain at least one [param] (got '${pr.pattern}')`,\n filePath,\n ),\n );\n }\n }\n return {\n siteUrl: input.siteUrl,\n collections,\n staticPages,\n parameterizedRoutes,\n llmsTxt: input.llmsTxt,\n middleware: {\n injectLinkHeader: input.middleware?.injectLinkHeader !== false,\n },\n headers: {\n cacheControl: input.headers?.cacheControl ?? \"public, max-age=3600\",\n noindex: input.headers?.noindex !== false,\n },\n };\n}\n","import { mkdirSync, writeFileSync, existsSync } from \"node:fs\";\nimport { fileURLToPath } from \"node:url\";\nimport { join, relative, sep } from \"node:path\";\nimport { resolveConfig, DualmarkConfigError } from \"./config-validation.js\";\nimport type { DualmarkAstroConfig, ResolvedDualmarkConfig } from \"./types.js\";\n\ninterface AstroIntegrationLogger {\n info: (msg: string) => void;\n warn: (msg: string) => void;\n error: (msg: string) => void;\n}\n\ninterface InjectedRoute {\n pattern: string;\n entrypoint: string | URL;\n prerender?: boolean;\n}\n\ninterface AstroIntegrationMiddleware {\n order: \"pre\" | \"post\";\n entrypoint: string | URL;\n}\n\ninterface ConfigSetupHookOptions {\n config: { root: URL; srcDir?: URL };\n command: string;\n isRestart?: boolean;\n injectRoute: (route: InjectedRoute) => void;\n addMiddleware?: (mw: AstroIntegrationMiddleware) => void;\n logger: AstroIntegrationLogger;\n updateConfig?: (cfg: unknown) => unknown;\n}\n\nexport interface AstroIntegrationLike {\n name: string;\n hooks: {\n \"astro:config:setup\": (opts: ConfigSetupHookOptions) => void | Promise<void>;\n };\n}\n\nconst GENERATED_DIR_NAME = \".dualmark-generated\";\n\ninterface RouteSpec {\n pattern: string;\n fileName: string;\n source: string;\n}\n\nfunction rel(from: string, to: string): string {\n const r = relative(from, to);\n return r.split(sep).join(\"/\");\n}\n\nexport function createDualmarkIntegration(input: DualmarkAstroConfig): AstroIntegrationLike {\n return {\n name: \"@dualmark/astro\",\n hooks: {\n \"astro:config:setup\"(opts) {\n const root = fileURLToPath(opts.config.root);\n\n const candidates = [\n \"astro.config.ts\",\n \"astro.config.mjs\",\n \"astro.config.js\",\n \"astro.config.mts\",\n \"astro.config.cjs\",\n ];\n const configPath =\n candidates.map((f) => join(root, f)).find(existsSync) ?? join(root, \"astro.config.mjs\");\n\n let resolved: ResolvedDualmarkConfig;\n try {\n resolved = resolveConfig(input, configPath);\n } catch (e) {\n opts.logger.error(`[@dualmark/astro] ${e instanceof Error ? e.message : String(e)}`);\n throw e;\n }\n\n const generatedDir = join(root, \"node_modules\", GENERATED_DIR_NAME);\n if (!existsSync(generatedDir)) mkdirSync(generatedDir, { recursive: true });\n\n writeFileSync(\n join(generatedDir, \"config.mjs\"),\n `export default ${JSON.stringify(\n {\n siteUrl: resolved.siteUrl,\n cacheControl: resolved.headers.cacheControl,\n noindex: resolved.headers.noindex,\n },\n null,\n 2,\n )};\\n`,\n \"utf8\",\n );\n\n const routes: RouteSpec[] = [];\n\n for (const [collectionName, c] of Object.entries(resolved.collections)) {\n const route = c.route ?? collectionName;\n const slugSeg = c.slugStrategy === \"single\" ? \"[slug]\" : \"[...slug]\";\n const detailPattern = `/${route}/${slugSeg}.md`;\n const listingPattern = `/${route}.md`;\n if (typeof c.converter !== \"string\") {\n opts.logger.warn(\n `[@dualmark/astro] Collection '${collectionName}' uses an inline converter function — this isn't yet serializable into a generated route. Use a built-in converter name (e.g. 'blog') for now.`,\n );\n continue;\n }\n const converterImport = `import { resolveBuiltInConverter } from \"@dualmark/astro\";`;\n const detailSource = `${converterImport}\nimport { makeCollectionDetailEndpoint } from \"@dualmark/astro/endpoints/collection\";\nimport { getCollection } from \"astro:content\";\nimport dualmarkConfig from \"./config.mjs\";\n\nconst converter = resolveBuiltInConverter({\n name: ${JSON.stringify(c.converter)},\n collectionName: ${JSON.stringify(collectionName)},\n baseConfig: { siteUrl: dualmarkConfig.siteUrl },\n});\n\nconst endpoint = makeCollectionDetailEndpoint({\n collectionName: ${JSON.stringify(collectionName)},\n converter,\n getCollection: (name, filter) => getCollection(name, filter),\n responseOptions: { cacheControl: dualmarkConfig.cacheControl, noindex: dualmarkConfig.noindex },\n});\n\nexport const getStaticPaths = endpoint.getStaticPaths;\nexport const GET = endpoint.GET;\n`;\n\n const listingSource = `import { makeListingEndpoint } from \"@dualmark/astro/endpoints/listing\";\nimport { getCollection } from \"astro:content\";\nimport dualmarkConfig from \"./config.mjs\";\n\nconst endpoint = makeListingEndpoint({\n collectionName: ${JSON.stringify(collectionName)},\n siteUrl: dualmarkConfig.siteUrl,\n basePath: ${JSON.stringify(\"/\" + route)},\n title: ${JSON.stringify(c.listingMetadata?.title ?? collectionName)},\n description: ${JSON.stringify(c.listingMetadata?.description ?? `All ${collectionName} entries.`)},\n getCollection: (name, filter) => getCollection(name, filter),\n responseOptions: { cacheControl: dualmarkConfig.cacheControl, noindex: dualmarkConfig.noindex },\n});\n\nexport const GET = endpoint.GET;\n`;\n\n routes.push({\n pattern: detailPattern,\n fileName: `collection-${collectionName}-detail.mjs`,\n source: detailSource,\n });\n if (c.emitListing !== false) {\n routes.push({\n pattern: listingPattern,\n fileName: `collection-${collectionName}-listing.mjs`,\n source: listingSource,\n });\n }\n }\n\n for (let i = 0; i < resolved.staticPages.length; i++) {\n const sp = resolved.staticPages[i];\n if (!sp) continue;\n const safe = sp.pattern.replace(/[^a-z0-9]/gi, \"_\");\n const fileName = `static-${i}-${safe}.mjs`;\n const renderModulePath = join(generatedDir, `static-${i}-${safe}-render.mjs`);\n writeFileSync(renderModulePath, `export default ${sp.render.toString()};\\n`, \"utf8\");\n const source = `import { makeStaticEndpoint } from \"@dualmark/astro/endpoints/static\";\nimport dualmarkConfig from \"./config.mjs\";\nimport render from \"./${rel(generatedDir, renderModulePath)}\";\n\nconst endpoint = makeStaticEndpoint({\n render,\n responseOptions: { cacheControl: dualmarkConfig.cacheControl, noindex: dualmarkConfig.noindex },\n});\n\nexport const GET = endpoint.GET;\n`;\n const mdPattern =\n sp.pattern === \"/\" ? \"/index.md\" : sp.pattern.replace(/\\/$/, \"\") + \".md\";\n routes.push({ pattern: mdPattern, fileName, source });\n }\n\n for (let i = 0; i < resolved.parameterizedRoutes.length; i++) {\n const pr = resolved.parameterizedRoutes[i];\n if (!pr) continue;\n const safe = pr.pattern.replace(/[^a-z0-9]/gi, \"_\");\n const renderModulePath = join(generatedDir, `param-${i}-${safe}-render.mjs`);\n const pathsModulePath = join(generatedDir, `param-${i}-${safe}-paths.mjs`);\n writeFileSync(renderModulePath, `export default ${pr.render.toString()};\\n`, \"utf8\");\n writeFileSync(\n pathsModulePath,\n `export default ${pr.getStaticPaths.toString()};\\n`,\n \"utf8\",\n );\n const source = `import { makeParameterizedEndpoint } from \"@dualmark/astro/endpoints/parameterized\";\nimport dualmarkConfig from \"./config.mjs\";\nimport render from \"./${rel(generatedDir, renderModulePath)}\";\nimport getStaticPaths from \"./${rel(generatedDir, pathsModulePath)}\";\n\nconst endpoint = makeParameterizedEndpoint({\n getStaticPaths: () => getStaticPaths(),\n render,\n responseOptions: { cacheControl: dualmarkConfig.cacheControl, noindex: dualmarkConfig.noindex },\n});\n\nexport const getStaticPaths = endpoint.getStaticPaths;\nexport const GET = endpoint.GET;\n`;\n const pattern = pr.pattern.startsWith(\"/\") ? pr.pattern + \".md\" : `/${pr.pattern}.md`;\n routes.push({\n pattern,\n fileName: `param-${i}-${safe}.mjs`,\n source,\n });\n }\n\n if (resolved.llmsTxt?.enabled) {\n const sections = resolved.llmsTxt.sections ?? [];\n const source = `import { makeLlmsTxtEndpoint } from \"@dualmark/astro/endpoints/llms-txt\";\n\nconst endpoint = makeLlmsTxtEndpoint({\n brandName: ${JSON.stringify(resolved.llmsTxt.brandName ?? \"Site\")},\n description: ${JSON.stringify(resolved.llmsTxt.description ?? \"\")},\n sections: ${JSON.stringify(sections)},\n});\n\nexport const GET = endpoint.GET;\n`;\n routes.push({ pattern: \"/llms.txt\", fileName: `llms-txt.mjs`, source });\n }\n\n for (const r of routes) {\n const filePath = join(generatedDir, r.fileName);\n writeFileSync(filePath, r.source, \"utf8\");\n opts.injectRoute({\n pattern: r.pattern,\n entrypoint: filePath,\n prerender: true,\n });\n }\n\n if (resolved.middleware.injectLinkHeader && opts.addMiddleware) {\n opts.addMiddleware({\n order: \"post\",\n entrypoint: \"@dualmark/astro/middleware\",\n });\n }\n\n opts.logger.info(\n `[@dualmark/astro] Injected ${routes.length} route(s) and ${\n resolved.middleware.injectLinkHeader ? \"1\" : \"0\"\n } middleware`,\n );\n },\n },\n };\n}\n\nexport default function dualmarkAstro(config: DualmarkAstroConfig): AstroIntegrationLike {\n return createDualmarkIntegration(config);\n}\n","import {\n blogConverter,\n caseStudyConverter,\n changelogConverter,\n compareConverter,\n docsConverter,\n featureConverter,\n glossaryConverter,\n integrationConverter,\n legalConverter,\n pricingConverter,\n pseoConverter,\n statusPageConverter,\n toolConverter,\n videoConverter,\n type BaseConverterConfig,\n type CollectionEntry,\n type Converter,\n} from \"@dualmark/converters\";\n\nexport type BuiltInConverterName =\n | \"blog\"\n | \"case-study\"\n | \"changelog\"\n | \"compare\"\n | \"docs\"\n | \"feature\"\n | \"glossary\"\n | \"integration\"\n | \"legal\"\n | \"pricing\"\n | \"pseo\"\n | \"status-page\"\n | \"tool\"\n | \"video\";\n\nexport interface ResolveConverterArgs {\n name: string;\n collectionName: string;\n baseConfig: BaseConverterConfig;\n}\n\nexport function resolveBuiltInConverter(\n args: ResolveConverterArgs,\n): Converter<CollectionEntry<unknown>> {\n const basePath = `/${args.collectionName}`;\n const cfg = { ...args.baseConfig, basePath };\n switch (args.name as BuiltInConverterName) {\n case \"blog\":\n return blogConverter(cfg) as Converter<CollectionEntry<unknown>>;\n case \"case-study\":\n return caseStudyConverter(cfg) as Converter<CollectionEntry<unknown>>;\n case \"changelog\":\n return changelogConverter(cfg) as Converter<CollectionEntry<unknown>>;\n case \"compare\":\n return compareConverter({ ...cfg, ourBrandColumn: \"Us\" }) as Converter<\n CollectionEntry<unknown>\n >;\n case \"docs\":\n return docsConverter(cfg) as Converter<CollectionEntry<unknown>>;\n case \"feature\":\n return featureConverter(cfg) as Converter<CollectionEntry<unknown>>;\n case \"glossary\":\n return glossaryConverter(cfg) as Converter<CollectionEntry<unknown>>;\n case \"integration\":\n return integrationConverter(cfg) as Converter<CollectionEntry<unknown>>;\n case \"legal\":\n return legalConverter(cfg) as Converter<CollectionEntry<unknown>>;\n case \"pricing\":\n return pricingConverter(cfg) as Converter<CollectionEntry<unknown>>;\n case \"pseo\":\n return pseoConverter(cfg) as Converter<CollectionEntry<unknown>>;\n case \"status-page\":\n return statusPageConverter(cfg) as Converter<CollectionEntry<unknown>>;\n case \"tool\":\n return toolConverter(cfg) as Converter<CollectionEntry<unknown>>;\n case \"video\":\n return videoConverter(cfg) as Converter<CollectionEntry<unknown>>;\n default:\n throw new Error(\n `Dualmark: unknown built-in converter '${args.name}'. Valid names: blog, case-study, changelog, compare, docs, feature, glossary, integration, legal, pricing, pseo, status-page, tool, video. Or pass a function.`,\n );\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dualmark/astro",
3
- "version": "0.8.0",
3
+ "version": "0.9.0",
4
4
  "description": "Astro integration for the Dualmark AEO framework. Auto-generates .md endpoints from collection config + injects Link rel=alternate middleware + generates llms.txt.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Dodo Payments <opensource@dodopayments.com> (https://dodopayments.com)",