@silicajs/next 0.4.0 → 0.5.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.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- export { TemplateFile, getSilicaTemplates, nextConfigTemplate, packageJsonTemplate, proxyTemplate, themeModuleTemplate, tsconfigTemplate } from './templates.js';
2
- export { LoadedVaultDb, getAllSlugs, getBacklinks, getBreadcrumbs, getCacheState, getConfig, getEntriesForTag, getNavigation, getPage, getPageRuntimeData, getPrerenderSlugs, getProjectRoot, getRelatedTagsForEntries, getRenderKey, getSilicaRoot, getTagSlugs, getVaultDatabasePath, loadRenderEnvironmentHash, loadSearchIndex, loadVaultDb, normalizeRouteSlug, resolveAssetFromDb, resolveWikiLinkFromDb } from './server-data.js';
1
+ export { TemplateFile, assistantModuleTemplate, assistantRouteTemplate, getSilicaTemplates, nextConfigTemplate, packageJsonTemplate, proxyTemplate, themeModuleTemplate, tsconfigTemplate } from './templates.js';
2
+ export { LoadedVaultDb, getAllSlugs, getBacklinks, getBreadcrumbs, getCacheState, getConfig, getEntriesForTag, getNavigation, getPage, getPageBySourcePath, getPageRuntimeData, getPrerenderSlugs, getProjectRoot, getRelatedTagsForEntries, getRenderKey, getSilicaRoot, getTagSlugs, getVaultDatabasePath, loadRenderEnvironmentHash, loadSearchIndex, loadVaultDb, normalizeRouteSlug, resolveAssetFromDb, resolveWikiLinkFromDb } from './server-data.js';
3
3
  export { SilicaNextRoutingProvider } from './routing-provider.js';
4
4
  import '@silicajs/core/runtime';
5
5
  import 'better-sqlite3';
package/dist/index.js CHANGED
@@ -1,4 +1,6 @@
1
1
  import {
2
+ assistantModuleTemplate,
3
+ assistantRouteTemplate,
2
4
  getSilicaTemplates,
3
5
  nextConfigTemplate,
4
6
  packageJsonTemplate,
@@ -15,6 +17,7 @@ import {
15
17
  getEntriesForTag,
16
18
  getNavigation,
17
19
  getPage,
20
+ getPageBySourcePath,
18
21
  getPageRuntimeData,
19
22
  getProjectRoot,
20
23
  getPrerenderSlugs,
@@ -33,6 +36,8 @@ import {
33
36
  import { SilicaNextRoutingProvider } from "./routing-provider.js";
34
37
  export {
35
38
  SilicaNextRoutingProvider,
39
+ assistantModuleTemplate,
40
+ assistantRouteTemplate,
36
41
  getAllSlugs,
37
42
  getBacklinks,
38
43
  getBreadcrumbs,
@@ -41,6 +46,7 @@ export {
41
46
  getEntriesForTag,
42
47
  getNavigation,
43
48
  getPage,
49
+ getPageBySourcePath,
44
50
  getPageRuntimeData,
45
51
  getPrerenderSlugs,
46
52
  getProjectRoot,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["export {\n getSilicaTemplates,\n nextConfigTemplate,\n packageJsonTemplate,\n proxyTemplate,\n themeModuleTemplate,\n tsconfigTemplate,\n type TemplateFile,\n} from \"./templates.js\";\nexport {\n getAllSlugs,\n getBacklinks,\n getBreadcrumbs,\n getCacheState,\n getConfig,\n getEntriesForTag,\n getNavigation,\n getPage,\n getPageRuntimeData,\n getProjectRoot,\n getPrerenderSlugs,\n getRenderKey,\n getRelatedTagsForEntries,\n getSilicaRoot,\n getTagSlugs,\n getVaultDatabasePath,\n loadSearchIndex,\n loadRenderEnvironmentHash,\n loadVaultDb,\n normalizeRouteSlug,\n resolveAssetFromDb,\n resolveWikiLinkFromDb,\n type LoadedVaultDb,\n} from \"./server-data.js\";\nexport { SilicaNextRoutingProvider } from \"./routing-provider.js\";\n"],"mappings":"AAAA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AACP,SAAS,iCAAiC;","names":[]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["export {\n assistantModuleTemplate,\n assistantRouteTemplate,\n getSilicaTemplates,\n nextConfigTemplate,\n packageJsonTemplate,\n proxyTemplate,\n themeModuleTemplate,\n tsconfigTemplate,\n type TemplateFile,\n} from \"./templates.js\";\nexport {\n getAllSlugs,\n getBacklinks,\n getBreadcrumbs,\n getCacheState,\n getConfig,\n getEntriesForTag,\n getNavigation,\n getPage,\n getPageBySourcePath,\n getPageRuntimeData,\n getProjectRoot,\n getPrerenderSlugs,\n getRenderKey,\n getRelatedTagsForEntries,\n getSilicaRoot,\n getTagSlugs,\n getVaultDatabasePath,\n loadSearchIndex,\n loadRenderEnvironmentHash,\n loadVaultDb,\n normalizeRouteSlug,\n resolveAssetFromDb,\n resolveWikiLinkFromDb,\n type LoadedVaultDb,\n} from \"./server-data.js\";\nexport { SilicaNextRoutingProvider } from \"./routing-provider.js\";\n"],"mappings":"AAAA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AACP,SAAS,iCAAiC;","names":[]}
@@ -13,6 +13,7 @@ declare function getLayoutProps(): Promise<{
13
13
  logo: string | undefined;
14
14
  baseUrl: string | undefined;
15
15
  authEnabled: boolean;
16
+ assistantEnabled: boolean;
16
17
  };
17
18
  }>;
18
19
 
@@ -30,7 +30,8 @@ async function getCachedLayoutProps(renderEnvironmentHash) {
30
30
  description: config.description,
31
31
  logo: config.logo,
32
32
  baseUrl: config.baseUrl,
33
- authEnabled: auth.authEnabled
33
+ authEnabled: auth.authEnabled,
34
+ assistantEnabled: Boolean(config.assistant)
34
35
  }
35
36
  };
36
37
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/routes/layout.tsx"],"sourcesContent":["import { cacheLife, cacheTag } from \"next/cache\";\nimport { resolveRuntimeAuthConfig } from \"../auth-config.js\";\nimport { getCacheState, getConfig } from \"../server-data.js\";\n\nexport async function generateMetadata() {\n const { config } = await getLayoutProps();\n return {\n title: {\n default: config.title,\n template: `%s · ${config.title}`,\n },\n description: config.description,\n };\n}\n\nexport async function getLayoutProps() {\n const cacheState = getCacheState();\n return getCachedLayoutProps(cacheState.renderEnvironmentHash);\n}\n\nasync function getCachedLayoutProps(renderEnvironmentHash: string) {\n \"use cache\";\n cacheLife(\"max\");\n cacheTag(`environment:${renderEnvironmentHash}`);\n const config = getConfig();\n const auth = resolveRuntimeAuthConfig(config);\n return {\n navigationEndpoint: `/api/navigation?build=${encodeURIComponent(\n renderEnvironmentHash,\n )}`,\n config: {\n title: config.title,\n description: config.description,\n logo: config.logo,\n baseUrl: config.baseUrl,\n authEnabled: auth.authEnabled,\n },\n };\n}\n"],"mappings":"AAAA,SAAS,WAAW,gBAAgB;AACpC,SAAS,gCAAgC;AACzC,SAAS,eAAe,iBAAiB;AAEzC,eAAsB,mBAAmB;AACvC,QAAM,EAAE,OAAO,IAAI,MAAM,eAAe;AACxC,SAAO;AAAA,IACL,OAAO;AAAA,MACL,SAAS,OAAO;AAAA,MAChB,UAAU,WAAQ,OAAO,KAAK;AAAA,IAChC;AAAA,IACA,aAAa,OAAO;AAAA,EACtB;AACF;AAEA,eAAsB,iBAAiB;AACrC,QAAM,aAAa,cAAc;AACjC,SAAO,qBAAqB,WAAW,qBAAqB;AAC9D;AAEA,eAAe,qBAAqB,uBAA+B;AACjE;AACA,YAAU,KAAK;AACf,WAAS,eAAe,qBAAqB,EAAE;AAC/C,QAAM,SAAS,UAAU;AACzB,QAAM,OAAO,yBAAyB,MAAM;AAC5C,SAAO;AAAA,IACL,oBAAoB,yBAAyB;AAAA,MAC3C;AAAA,IACF,CAAC;AAAA,IACD,QAAQ;AAAA,MACN,OAAO,OAAO;AAAA,MACd,aAAa,OAAO;AAAA,MACpB,MAAM,OAAO;AAAA,MACb,SAAS,OAAO;AAAA,MAChB,aAAa,KAAK;AAAA,IACpB;AAAA,EACF;AACF;","names":[]}
1
+ {"version":3,"sources":["../../src/routes/layout.tsx"],"sourcesContent":["import { cacheLife, cacheTag } from \"next/cache\";\nimport { resolveRuntimeAuthConfig } from \"../auth-config.js\";\nimport { getCacheState, getConfig } from \"../server-data.js\";\n\nexport async function generateMetadata() {\n const { config } = await getLayoutProps();\n return {\n title: {\n default: config.title,\n template: `%s · ${config.title}`,\n },\n description: config.description,\n };\n}\n\nexport async function getLayoutProps() {\n const cacheState = getCacheState();\n return getCachedLayoutProps(cacheState.renderEnvironmentHash);\n}\n\nasync function getCachedLayoutProps(renderEnvironmentHash: string) {\n \"use cache\";\n cacheLife(\"max\");\n cacheTag(`environment:${renderEnvironmentHash}`);\n const config = getConfig();\n const auth = resolveRuntimeAuthConfig(config);\n return {\n navigationEndpoint: `/api/navigation?build=${encodeURIComponent(\n renderEnvironmentHash,\n )}`,\n config: {\n title: config.title,\n description: config.description,\n logo: config.logo,\n baseUrl: config.baseUrl,\n authEnabled: auth.authEnabled,\n assistantEnabled: Boolean(config.assistant),\n },\n };\n}\n"],"mappings":"AAAA,SAAS,WAAW,gBAAgB;AACpC,SAAS,gCAAgC;AACzC,SAAS,eAAe,iBAAiB;AAEzC,eAAsB,mBAAmB;AACvC,QAAM,EAAE,OAAO,IAAI,MAAM,eAAe;AACxC,SAAO;AAAA,IACL,OAAO;AAAA,MACL,SAAS,OAAO;AAAA,MAChB,UAAU,WAAQ,OAAO,KAAK;AAAA,IAChC;AAAA,IACA,aAAa,OAAO;AAAA,EACtB;AACF;AAEA,eAAsB,iBAAiB;AACrC,QAAM,aAAa,cAAc;AACjC,SAAO,qBAAqB,WAAW,qBAAqB;AAC9D;AAEA,eAAe,qBAAqB,uBAA+B;AACjE;AACA,YAAU,KAAK;AACf,WAAS,eAAe,qBAAqB,EAAE;AAC/C,QAAM,SAAS,UAAU;AACzB,QAAM,OAAO,yBAAyB,MAAM;AAC5C,SAAO;AAAA,IACL,oBAAoB,yBAAyB;AAAA,MAC3C;AAAA,IACF,CAAC;AAAA,IACD,QAAQ;AAAA,MACN,OAAO,OAAO;AAAA,MACd,aAAa,OAAO;AAAA,MACpB,MAAM,OAAO;AAAA,MACb,SAAS,OAAO;AAAA,MAChB,aAAa,KAAK;AAAA,MAClB,kBAAkB,QAAQ,OAAO,SAAS;AAAA,IAC5C;AAAA,EACF;AACF;","names":[]}
@@ -26,6 +26,7 @@ declare function getSilicaRoot(): string;
26
26
  declare function getVaultDatabasePath(): string;
27
27
  declare function loadVaultDb(): LoadedVaultDb;
28
28
  declare function getPage(slug: string): ManifestEntry | undefined;
29
+ declare function getPageBySourcePath(sourcePath: string): ManifestEntry | undefined;
29
30
  declare function getPageRuntimeData(slug: string): VaultPageData | undefined;
30
31
  declare function getRenderKey(slug: string): {
31
32
  renderHash: string;
@@ -54,4 +55,4 @@ declare function getBreadcrumbs(slug: string): {
54
55
  declare function loadSearchIndex(): LoadedSearchIndex;
55
56
  declare function normalizeRouteSlug(slug?: string[]): string;
56
57
 
57
- export { type LoadedVaultDb, type VaultPageData, getAllSlugs, getBacklinks, getBreadcrumbs, getCacheState, getConfig, getEntriesForTag, getNavigation, getPage, getPageRuntimeData, getPrerenderSlugs, getProjectRoot, getRelatedTagsForEntries, getRenderKey, getSilicaRoot, getTagSlugs, getVaultDatabasePath, loadRenderEnvironmentHash, loadSearchIndex, loadVaultDb, normalizeRouteSlug, resolveAssetFromDb, resolveWikiLinkFromDb };
58
+ export { type LoadedVaultDb, type VaultPageData, getAllSlugs, getBacklinks, getBreadcrumbs, getCacheState, getConfig, getEntriesForTag, getNavigation, getPage, getPageBySourcePath, getPageRuntimeData, getPrerenderSlugs, getProjectRoot, getRelatedTagsForEntries, getRenderKey, getSilicaRoot, getTagSlugs, getVaultDatabasePath, loadRenderEnvironmentHash, loadSearchIndex, loadVaultDb, normalizeRouteSlug, resolveAssetFromDb, resolveWikiLinkFromDb };
@@ -56,6 +56,10 @@ function getPage(slug) {
56
56
  const row = loadVaultDb().db.prepare("SELECT * FROM notes WHERE slug = ?").get(slug);
57
57
  return row ? noteRowToEntry(row) : void 0;
58
58
  }
59
+ function getPageBySourcePath(sourcePath) {
60
+ const row = loadVaultDb().db.prepare("SELECT * FROM notes WHERE source_path = ?").get(normalizeDbSourcePath(sourcePath));
61
+ return row ? noteRowToEntry(row) : void 0;
62
+ }
59
63
  function getPageRuntimeData(slug) {
60
64
  const entry = getPage(slug);
61
65
  if (!entry) return void 0;
@@ -336,6 +340,9 @@ function noteRowToEntry(row) {
336
340
  embeds: getEmbeds(row.slug)
337
341
  };
338
342
  }
343
+ function normalizeDbSourcePath(value) {
344
+ return value.replace(/\\/g, "/").replace(/^\/+/, "").replace(/^content\//, "");
345
+ }
339
346
  function getEmbeds(slug) {
340
347
  return loadVaultDb().db.prepare(
341
348
  "SELECT target_slug FROM links WHERE source_slug = ? AND kind = 'embed' ORDER BY target_slug"
@@ -365,6 +372,7 @@ export {
365
372
  getEntriesForTag,
366
373
  getNavigation,
367
374
  getPage,
375
+ getPageBySourcePath,
368
376
  getPageRuntimeData,
369
377
  getPrerenderSlugs,
370
378
  getProjectRoot,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/server-data.ts"],"sourcesContent":["import path from \"node:path\";\nimport fs from \"node:fs\";\nimport Database from \"better-sqlite3\";\nimport type { LoadedSearchIndex } from \"@silicajs/search\";\nimport type {\n Navigation,\n ManifestEntry,\n RenderCacheState,\n ResolvedSilicaConfig,\n} from \"@silicajs/core/runtime\";\nimport {\n asFullSlug,\n normalizeAssetReference,\n normalizeSlug,\n resolveRelativeAsset,\n resolveRelative,\n slugToHref,\n} from \"@silicajs/core/runtime\";\n\nconst VAULT_DATABASE_FILENAME = \"vault.db\";\n\nexport type LoadedVaultDb = {\n databasePath: string;\n generatedAt: string;\n renderEnvironmentHash: string;\n config: ResolvedSilicaConfig;\n cacheState: RenderCacheState;\n mtimeMs: number;\n db: Database.Database;\n close(): void;\n};\n\nexport type VaultPageData = {\n cacheState: RenderCacheState;\n config: ResolvedSilicaConfig;\n entry: ManifestEntry;\n};\n\ntype MetadataRows = {\n generatedAt: string;\n renderEnvironmentHash: string;\n config: ResolvedSilicaConfig;\n cacheState: RenderCacheState;\n};\n\ntype NoteRow = {\n slug: string;\n file: string;\n source_path: string;\n title: string;\n menu_label: string;\n description: string | null;\n generated_description: string | null;\n frontmatter_json: string;\n tags_json: string;\n created: string | null;\n modified: string | null;\n sort_key: string | null;\n listed: 0 | 1;\n content_hash: string;\n render_hash: string;\n prerender: 0 | 1;\n};\n\ntype NavigationRow = {\n slug: string;\n title: string;\n sort_key: string | null;\n};\n\ntype BacklinkRow = {\n slug: string;\n title: string;\n};\n\nlet loadedVaultDb: LoadedVaultDb | undefined;\n\nexport function getProjectRoot(): string {\n const projectRoot = process.env.SILICA_PROJECT_ROOT;\n if (!projectRoot) {\n throw new Error(\"SILICA_PROJECT_ROOT must be set by the Silica CLI.\");\n }\n\n return projectRoot;\n}\n\nexport function getSilicaRoot(): string {\n return path.join(getProjectRoot(), \".silica\");\n}\n\nexport function getVaultDatabasePath(): string {\n return path.join(getSilicaRoot(), VAULT_DATABASE_FILENAME);\n}\n\nexport function loadVaultDb(): LoadedVaultDb {\n const databasePath = getVaultDatabasePath();\n const stat = fs.statSync(databasePath);\n if (\n loadedVaultDb?.databasePath === databasePath &&\n loadedVaultDb.mtimeMs === stat.mtimeMs\n ) {\n return loadedVaultDb;\n }\n\n loadedVaultDb?.close();\n\n const db = new Database(databasePath, {\n fileMustExist: true,\n readonly: true,\n });\n db.pragma(\"query_only = ON\");\n const metadata = readMetadata(db);\n loadedVaultDb = {\n databasePath,\n generatedAt: metadata.generatedAt,\n renderEnvironmentHash: metadata.renderEnvironmentHash,\n config: metadata.config,\n cacheState: metadata.cacheState,\n mtimeMs: stat.mtimeMs,\n db,\n close: () => {\n db.close();\n if (loadedVaultDb?.db === db) loadedVaultDb = undefined;\n },\n };\n return loadedVaultDb;\n}\n\nexport function getPage(slug: string): ManifestEntry | undefined {\n const row = loadVaultDb()\n .db.prepare(\"SELECT * FROM notes WHERE slug = ?\")\n .get(slug) as NoteRow | undefined;\n return row ? noteRowToEntry(row) : undefined;\n}\n\nexport function getPageRuntimeData(slug: string): VaultPageData | undefined {\n const entry = getPage(slug);\n if (!entry) return undefined;\n return {\n entry,\n config: getConfig(),\n cacheState: getCacheState(),\n };\n}\n\nexport function getRenderKey(slug: string): {\n renderHash: string;\n renderEnvironmentHash: string;\n} {\n const loaded = loadVaultDb();\n const row = loaded.db\n .prepare(\"SELECT render_hash FROM notes WHERE slug = ?\")\n .get(slug) as { render_hash: string } | undefined;\n return {\n renderHash: row?.render_hash ?? \"missing\",\n renderEnvironmentHash: loaded.renderEnvironmentHash,\n };\n}\n\nexport function loadRenderEnvironmentHash(): string {\n return loadVaultDb().renderEnvironmentHash;\n}\n\nexport function getPrerenderSlugs(): string[] {\n return loadVaultDb()\n .db.prepare(\n \"SELECT slug FROM notes WHERE prerender = 1 ORDER BY COALESCE(sort_key, slug), slug\",\n )\n .all()\n .map((row) => (row as { slug: string }).slug);\n}\n\nexport function getAllSlugs(): string[] {\n return loadVaultDb()\n .db.prepare(\"SELECT slug FROM notes ORDER BY slug\")\n .all()\n .map((row) => (row as { slug: string }).slug);\n}\n\nexport function getNavigation(): Navigation {\n const entries = loadVaultDb()\n .db.prepare(\n `\n SELECT slug, menu_label AS title, sort_key\n FROM notes\n WHERE listed = 1\n ORDER BY COALESCE(sort_key, slug), slug\n `,\n )\n .all() as NavigationRow[];\n return {\n version: 1,\n entries: entries.map((entry) => ({\n slug: entry.slug,\n title: entry.title,\n sortKey: entry.sort_key ?? undefined,\n })),\n };\n}\n\nexport function getBacklinks(slug: string): BacklinkRow[] {\n return loadVaultDb()\n .db.prepare(\n `\n SELECT n.slug, n.title\n FROM links l\n JOIN notes n ON n.slug = l.source_slug\n WHERE l.target_slug = ?\n AND l.kind = 'link'\n ORDER BY n.title COLLATE NOCASE ASC, n.slug\n `,\n )\n .all(slug) as BacklinkRow[];\n}\n\nexport function getConfig(): ResolvedSilicaConfig {\n return loadVaultDb().config;\n}\n\nexport function getCacheState(): RenderCacheState {\n return loadVaultDb().cacheState;\n}\n\nexport function getTagSlugs(): string[] {\n return loadVaultDb()\n .db.prepare(\"SELECT DISTINCT tag FROM note_tags ORDER BY tag\")\n .all()\n .map((row) => (row as { tag: string }).tag);\n}\n\nexport function getEntriesForTag(tag: string): ManifestEntry[] {\n return loadVaultDb()\n .db.prepare(\n `\n SELECT n.*\n FROM notes n\n WHERE n.listed = 1\n AND EXISTS (\n SELECT 1\n FROM note_tags nt\n WHERE nt.slug = n.slug\n AND nt.tag = ?\n )\n ORDER BY n.title COLLATE NOCASE ASC, n.slug\n `,\n )\n .all(tag)\n .map((row) => noteRowToEntry(row as NoteRow));\n}\n\nexport function getRelatedTagsForEntries(\n slugs: string[],\n tag: string,\n): string[] {\n if (slugs.length === 0) return [];\n return loadVaultDb()\n .db.prepare(\n `\n SELECT nt.tag, COUNT(*) AS count\n FROM note_tags nt\n WHERE nt.slug IN (${slugs.map(() => \"?\").join(\", \")})\n AND nt.tag != ?\n GROUP BY nt.tag\n ORDER BY count DESC, nt.tag ASC\n LIMIT 12\n `,\n )\n .all(...slugs, tag)\n .map((row) => (row as { tag: string }).tag);\n}\n\nexport function resolveWikiLinkFromDb(\n currentSlug: string,\n target: string,\n strategy: \"absolute\" | \"relative\" | \"shortest\",\n ordering?: { numericPrefixes?: boolean },\n): string | undefined {\n const [rawPath] = target.split(\"#\");\n const slugOptions = ordering ?? getConfig().ordering;\n const normalizedTarget = normalizeSlug(rawPath ?? target, slugOptions);\n const db = loadVaultDb().db;\n\n if (strategy === \"absolute\") {\n return lookupAlias(db, \"absolute\", normalizedTarget);\n }\n\n if (strategy === \"relative\") {\n const relative = resolveRelative(\n asFullSlug(currentSlug),\n normalizedTarget,\n slugOptions,\n );\n const resolved = lookupAlias(db, \"absolute\", relative);\n if (resolved) return resolved;\n }\n\n return (\n lookupAlias(db, \"absolute\", normalizedTarget) ??\n lookupAlias(db, \"absolute\", `${normalizedTarget}/index`) ??\n lookupClosestAlias(\n db,\n currentSlug,\n normalizedTarget.split(\"/\").at(-1) ?? \"\",\n )\n );\n}\n\nexport function resolveAssetFromDb(\n currentSourcePath: string,\n target: string,\n strategy: \"absolute\" | \"relative\" | \"shortest\",\n ordering?: { numericPrefixes?: boolean },\n): string | undefined {\n const assetOptions = ordering ?? getConfig().ordering;\n const normalizedTarget = normalizeAssetReference(target, assetOptions);\n if (!normalizedTarget) return undefined;\n const db = loadVaultDb().db;\n const isExplicitRelative = isExplicitRelativeAssetReference(target);\n\n if (isExplicitRelative || strategy === \"relative\") {\n const relative = resolveRelativeAsset(\n currentSourcePath,\n target,\n assetOptions,\n );\n const resolved = lookupAssetAlias(db, \"absolute\", relative);\n if (resolved || isExplicitRelative) return resolved;\n }\n\n if (strategy === \"absolute\") {\n return lookupAssetAlias(db, \"absolute\", normalizedTarget);\n }\n\n return (\n lookupAssetAlias(db, \"absolute\", normalizedTarget) ??\n lookupAssetAlias(db, \"shortest\", normalizedTarget.split(\"/\").at(-1) ?? \"\")\n );\n}\n\nexport function getBreadcrumbs(slug: string) {\n if (slug === \"index\" || !slug.includes(\"/\")) return [];\n\n const breadcrumbs: Array<{ label: string; href?: string }> = [\n { label: \"Home\", href: \"/\" },\n ];\n const segments = slug.split(\"/\").slice(0, -1);\n let acc = \"\";\n for (const segment of segments) {\n acc = acc ? `${acc}/${segment}` : segment;\n breadcrumbs.push({\n label: prettySegment(segment),\n href: breadcrumbSegmentHref(acc),\n });\n }\n return breadcrumbs;\n}\n\nexport function loadSearchIndex(): LoadedSearchIndex {\n const loaded = loadVaultDb();\n return {\n databasePath: loaded.databasePath,\n db: loaded.db,\n close: () => undefined,\n };\n}\n\nexport function normalizeRouteSlug(slug?: string[]): string {\n return slug?.length ? slug.join(\"/\") : \"index\";\n}\n\nfunction readMetadata(db: Database.Database): MetadataRows {\n const rows = db\n .prepare(\"SELECT key, value FROM vault_metadata\")\n .all() as Array<{ key: string; value: string }>;\n const metadata = Object.fromEntries(rows.map((row) => [row.key, row.value]));\n return {\n generatedAt: metadata.generatedAt ?? \"\",\n renderEnvironmentHash: metadata.renderEnvironmentHash ?? \"silica\",\n config: parseConfigMetadata(metadata.configJson),\n cacheState: JSON.parse(metadata.cacheStateJson ?? \"{}\") as RenderCacheState,\n };\n}\n\nfunction parseConfigMetadata(value: string | undefined): ResolvedSilicaConfig {\n if (!value) throw new Error(\"vault.db is missing configJson metadata.\");\n return JSON.parse(value) as ResolvedSilicaConfig;\n}\n\nfunction lookupAlias(\n db: Database.Database,\n strategy: string,\n alias: string,\n): string | undefined {\n const row = db\n .prepare(\n `\n SELECT slug\n FROM slug_aliases\n WHERE strategy_key = ?\n AND alias = ?\n ORDER BY sort_key, slug\n LIMIT 2\n `,\n )\n .all(strategy, alias) as Array<{ slug: string }>;\n return row.length === 1 ? row[0]?.slug : undefined;\n}\n\nfunction lookupClosestAlias(\n db: Database.Database,\n currentSlug: string,\n alias: string,\n): string | undefined {\n const rows = db\n .prepare(\n `\n SELECT slug\n FROM slug_aliases\n WHERE strategy_key = 'shortest'\n AND alias = ?\n ORDER BY slug\n `,\n )\n .all(alias) as Array<{ slug: string }>;\n\n return closestWikiLinkCandidate(\n currentSlug,\n rows.map((row) => row.slug),\n );\n}\n\nfunction lookupAssetAlias(\n db: Database.Database,\n strategy: string,\n alias: string,\n): string | undefined {\n const row = db\n .prepare(\n `\n SELECT asset_path\n FROM asset_aliases\n WHERE strategy_key = ?\n AND alias = ?\n ORDER BY sort_key, asset_path\n LIMIT 2\n `,\n )\n .all(strategy, alias) as Array<{ asset_path: string }>;\n return row.length === 1 ? row[0]?.asset_path : undefined;\n}\n\nfunction closestWikiLinkCandidate(\n currentSlug: string,\n candidates: readonly string[],\n): string | undefined {\n if (candidates.length === 0) return undefined;\n\n const currentDirectory = normalizeSlug(currentSlug, {\n numericPrefixes: false,\n })\n .split(\"/\")\n .slice(0, -1);\n const [bestCandidate] = [...candidates].sort((left, right) => {\n const leftScore = wikiLinkCandidateScore(currentDirectory, left);\n const rightScore = wikiLinkCandidateScore(currentDirectory, right);\n\n return (\n rightScore.sharedPrefixLength - leftScore.sharedPrefixLength ||\n leftScore.depth - rightScore.depth ||\n compareSlugs(left, right)\n );\n });\n\n return bestCandidate;\n}\n\nfunction wikiLinkCandidateScore(currentDirectory: string[], slug: string) {\n const simplified = slug === \"index\" ? \"\" : slug.replace(/\\/index$/, \"\");\n const segments = simplified ? simplified.split(\"/\") : [];\n\n return {\n sharedPrefixLength: sharedPrefixLength(\n currentDirectory,\n segments.slice(0, -1),\n ),\n depth: segments.length,\n };\n}\n\nfunction sharedPrefixLength(left: readonly string[], right: readonly string[]) {\n let length = 0;\n while (left[length] !== undefined && left[length] === right[length]) {\n length += 1;\n }\n return length;\n}\n\nfunction compareSlugs(left: string, right: string) {\n if (left < right) return -1;\n if (left > right) return 1;\n return 0;\n}\n\nfunction isExplicitRelativeAssetReference(value: string): boolean {\n const withoutSuffix = value.split(/[?#]/)[0] ?? \"\";\n const normalized = withoutSuffix\n .replace(/\\\\/g, \"/\")\n .replace(/^\\/+/, \"\")\n .replace(/\\/+$/, \"\");\n return /^\\.{1,2}\\//.test(normalized);\n}\n\nfunction noteRowToEntry(row: NoteRow): ManifestEntry {\n return {\n slug: row.slug,\n title: row.title,\n menuLabel: row.menu_label,\n description: row.description ?? undefined,\n generatedDescription: row.generated_description ?? undefined,\n tags: parseJsonArray(row.tags_json),\n file: path.isAbsolute(row.file)\n ? row.file\n : path.join(getProjectRoot(), row.file),\n sourcePath: row.source_path,\n sortKey: row.sort_key ?? undefined,\n created: row.created ?? undefined,\n modified: row.modified ?? undefined,\n frontmatter: parseObject(row.frontmatter_json),\n contentHash: row.content_hash,\n embeds: getEmbeds(row.slug),\n };\n}\n\nfunction getEmbeds(slug: string): string[] {\n return loadVaultDb()\n .db.prepare(\n \"SELECT target_slug FROM links WHERE source_slug = ? AND kind = 'embed' ORDER BY target_slug\",\n )\n .all(slug)\n .map((row) => (row as { target_slug: string }).target_slug);\n}\n\nfunction breadcrumbSegmentHref(segmentPath: string): string | undefined {\n if (getPage(segmentPath)) return slugToHref(segmentPath);\n const indexSlug = `${segmentPath}/index`;\n if (getPage(indexSlug)) return slugToHref(indexSlug);\n return undefined;\n}\n\nfunction prettySegment(segment: string): string {\n return segment\n .replace(/[-_]/g, \" \")\n .replace(/\\b\\w/g, (letter) => letter.toUpperCase());\n}\n\nfunction parseObject(value: string): Record<string, unknown> {\n return JSON.parse(value) as Record<string, unknown>;\n}\n\nfunction parseJsonArray(value: string): string[] {\n return JSON.parse(value) as string[];\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/server-data.ts"],"sourcesContent":["import path from \"node:path\";\nimport fs from \"node:fs\";\nimport Database from \"better-sqlite3\";\nimport type { LoadedSearchIndex } from \"@silicajs/search\";\nimport type {\n Navigation,\n ManifestEntry,\n RenderCacheState,\n ResolvedSilicaConfig,\n} from \"@silicajs/core/runtime\";\nimport {\n asFullSlug,\n normalizeAssetReference,\n normalizeSlug,\n resolveRelativeAsset,\n resolveRelative,\n slugToHref,\n} from \"@silicajs/core/runtime\";\n\nconst VAULT_DATABASE_FILENAME = \"vault.db\";\n\nexport type LoadedVaultDb = {\n databasePath: string;\n generatedAt: string;\n renderEnvironmentHash: string;\n config: ResolvedSilicaConfig;\n cacheState: RenderCacheState;\n mtimeMs: number;\n db: Database.Database;\n close(): void;\n};\n\nexport type VaultPageData = {\n cacheState: RenderCacheState;\n config: ResolvedSilicaConfig;\n entry: ManifestEntry;\n};\n\ntype MetadataRows = {\n generatedAt: string;\n renderEnvironmentHash: string;\n config: ResolvedSilicaConfig;\n cacheState: RenderCacheState;\n};\n\ntype NoteRow = {\n slug: string;\n file: string;\n source_path: string;\n title: string;\n menu_label: string;\n description: string | null;\n generated_description: string | null;\n frontmatter_json: string;\n tags_json: string;\n created: string | null;\n modified: string | null;\n sort_key: string | null;\n listed: 0 | 1;\n content_hash: string;\n render_hash: string;\n prerender: 0 | 1;\n};\n\ntype NavigationRow = {\n slug: string;\n title: string;\n sort_key: string | null;\n};\n\ntype BacklinkRow = {\n slug: string;\n title: string;\n};\n\nlet loadedVaultDb: LoadedVaultDb | undefined;\n\nexport function getProjectRoot(): string {\n const projectRoot = process.env.SILICA_PROJECT_ROOT;\n if (!projectRoot) {\n throw new Error(\"SILICA_PROJECT_ROOT must be set by the Silica CLI.\");\n }\n\n return projectRoot;\n}\n\nexport function getSilicaRoot(): string {\n return path.join(getProjectRoot(), \".silica\");\n}\n\nexport function getVaultDatabasePath(): string {\n return path.join(getSilicaRoot(), VAULT_DATABASE_FILENAME);\n}\n\nexport function loadVaultDb(): LoadedVaultDb {\n const databasePath = getVaultDatabasePath();\n const stat = fs.statSync(databasePath);\n if (\n loadedVaultDb?.databasePath === databasePath &&\n loadedVaultDb.mtimeMs === stat.mtimeMs\n ) {\n return loadedVaultDb;\n }\n\n loadedVaultDb?.close();\n\n const db = new Database(databasePath, {\n fileMustExist: true,\n readonly: true,\n });\n db.pragma(\"query_only = ON\");\n const metadata = readMetadata(db);\n loadedVaultDb = {\n databasePath,\n generatedAt: metadata.generatedAt,\n renderEnvironmentHash: metadata.renderEnvironmentHash,\n config: metadata.config,\n cacheState: metadata.cacheState,\n mtimeMs: stat.mtimeMs,\n db,\n close: () => {\n db.close();\n if (loadedVaultDb?.db === db) loadedVaultDb = undefined;\n },\n };\n return loadedVaultDb;\n}\n\nexport function getPage(slug: string): ManifestEntry | undefined {\n const row = loadVaultDb()\n .db.prepare(\"SELECT * FROM notes WHERE slug = ?\")\n .get(slug) as NoteRow | undefined;\n return row ? noteRowToEntry(row) : undefined;\n}\n\nexport function getPageBySourcePath(\n sourcePath: string,\n): ManifestEntry | undefined {\n const row = loadVaultDb()\n .db.prepare(\"SELECT * FROM notes WHERE source_path = ?\")\n .get(normalizeDbSourcePath(sourcePath)) as NoteRow | undefined;\n return row ? noteRowToEntry(row) : undefined;\n}\n\nexport function getPageRuntimeData(slug: string): VaultPageData | undefined {\n const entry = getPage(slug);\n if (!entry) return undefined;\n return {\n entry,\n config: getConfig(),\n cacheState: getCacheState(),\n };\n}\n\nexport function getRenderKey(slug: string): {\n renderHash: string;\n renderEnvironmentHash: string;\n} {\n const loaded = loadVaultDb();\n const row = loaded.db\n .prepare(\"SELECT render_hash FROM notes WHERE slug = ?\")\n .get(slug) as { render_hash: string } | undefined;\n return {\n renderHash: row?.render_hash ?? \"missing\",\n renderEnvironmentHash: loaded.renderEnvironmentHash,\n };\n}\n\nexport function loadRenderEnvironmentHash(): string {\n return loadVaultDb().renderEnvironmentHash;\n}\n\nexport function getPrerenderSlugs(): string[] {\n return loadVaultDb()\n .db.prepare(\n \"SELECT slug FROM notes WHERE prerender = 1 ORDER BY COALESCE(sort_key, slug), slug\",\n )\n .all()\n .map((row) => (row as { slug: string }).slug);\n}\n\nexport function getAllSlugs(): string[] {\n return loadVaultDb()\n .db.prepare(\"SELECT slug FROM notes ORDER BY slug\")\n .all()\n .map((row) => (row as { slug: string }).slug);\n}\n\nexport function getNavigation(): Navigation {\n const entries = loadVaultDb()\n .db.prepare(\n `\n SELECT slug, menu_label AS title, sort_key\n FROM notes\n WHERE listed = 1\n ORDER BY COALESCE(sort_key, slug), slug\n `,\n )\n .all() as NavigationRow[];\n return {\n version: 1,\n entries: entries.map((entry) => ({\n slug: entry.slug,\n title: entry.title,\n sortKey: entry.sort_key ?? undefined,\n })),\n };\n}\n\nexport function getBacklinks(slug: string): BacklinkRow[] {\n return loadVaultDb()\n .db.prepare(\n `\n SELECT n.slug, n.title\n FROM links l\n JOIN notes n ON n.slug = l.source_slug\n WHERE l.target_slug = ?\n AND l.kind = 'link'\n ORDER BY n.title COLLATE NOCASE ASC, n.slug\n `,\n )\n .all(slug) as BacklinkRow[];\n}\n\nexport function getConfig(): ResolvedSilicaConfig {\n return loadVaultDb().config;\n}\n\nexport function getCacheState(): RenderCacheState {\n return loadVaultDb().cacheState;\n}\n\nexport function getTagSlugs(): string[] {\n return loadVaultDb()\n .db.prepare(\"SELECT DISTINCT tag FROM note_tags ORDER BY tag\")\n .all()\n .map((row) => (row as { tag: string }).tag);\n}\n\nexport function getEntriesForTag(tag: string): ManifestEntry[] {\n return loadVaultDb()\n .db.prepare(\n `\n SELECT n.*\n FROM notes n\n WHERE n.listed = 1\n AND EXISTS (\n SELECT 1\n FROM note_tags nt\n WHERE nt.slug = n.slug\n AND nt.tag = ?\n )\n ORDER BY n.title COLLATE NOCASE ASC, n.slug\n `,\n )\n .all(tag)\n .map((row) => noteRowToEntry(row as NoteRow));\n}\n\nexport function getRelatedTagsForEntries(\n slugs: string[],\n tag: string,\n): string[] {\n if (slugs.length === 0) return [];\n return loadVaultDb()\n .db.prepare(\n `\n SELECT nt.tag, COUNT(*) AS count\n FROM note_tags nt\n WHERE nt.slug IN (${slugs.map(() => \"?\").join(\", \")})\n AND nt.tag != ?\n GROUP BY nt.tag\n ORDER BY count DESC, nt.tag ASC\n LIMIT 12\n `,\n )\n .all(...slugs, tag)\n .map((row) => (row as { tag: string }).tag);\n}\n\nexport function resolveWikiLinkFromDb(\n currentSlug: string,\n target: string,\n strategy: \"absolute\" | \"relative\" | \"shortest\",\n ordering?: { numericPrefixes?: boolean },\n): string | undefined {\n const [rawPath] = target.split(\"#\");\n const slugOptions = ordering ?? getConfig().ordering;\n const normalizedTarget = normalizeSlug(rawPath ?? target, slugOptions);\n const db = loadVaultDb().db;\n\n if (strategy === \"absolute\") {\n return lookupAlias(db, \"absolute\", normalizedTarget);\n }\n\n if (strategy === \"relative\") {\n const relative = resolveRelative(\n asFullSlug(currentSlug),\n normalizedTarget,\n slugOptions,\n );\n const resolved = lookupAlias(db, \"absolute\", relative);\n if (resolved) return resolved;\n }\n\n return (\n lookupAlias(db, \"absolute\", normalizedTarget) ??\n lookupAlias(db, \"absolute\", `${normalizedTarget}/index`) ??\n lookupClosestAlias(\n db,\n currentSlug,\n normalizedTarget.split(\"/\").at(-1) ?? \"\",\n )\n );\n}\n\nexport function resolveAssetFromDb(\n currentSourcePath: string,\n target: string,\n strategy: \"absolute\" | \"relative\" | \"shortest\",\n ordering?: { numericPrefixes?: boolean },\n): string | undefined {\n const assetOptions = ordering ?? getConfig().ordering;\n const normalizedTarget = normalizeAssetReference(target, assetOptions);\n if (!normalizedTarget) return undefined;\n const db = loadVaultDb().db;\n const isExplicitRelative = isExplicitRelativeAssetReference(target);\n\n if (isExplicitRelative || strategy === \"relative\") {\n const relative = resolveRelativeAsset(\n currentSourcePath,\n target,\n assetOptions,\n );\n const resolved = lookupAssetAlias(db, \"absolute\", relative);\n if (resolved || isExplicitRelative) return resolved;\n }\n\n if (strategy === \"absolute\") {\n return lookupAssetAlias(db, \"absolute\", normalizedTarget);\n }\n\n return (\n lookupAssetAlias(db, \"absolute\", normalizedTarget) ??\n lookupAssetAlias(db, \"shortest\", normalizedTarget.split(\"/\").at(-1) ?? \"\")\n );\n}\n\nexport function getBreadcrumbs(slug: string) {\n if (slug === \"index\" || !slug.includes(\"/\")) return [];\n\n const breadcrumbs: Array<{ label: string; href?: string }> = [\n { label: \"Home\", href: \"/\" },\n ];\n const segments = slug.split(\"/\").slice(0, -1);\n let acc = \"\";\n for (const segment of segments) {\n acc = acc ? `${acc}/${segment}` : segment;\n breadcrumbs.push({\n label: prettySegment(segment),\n href: breadcrumbSegmentHref(acc),\n });\n }\n return breadcrumbs;\n}\n\nexport function loadSearchIndex(): LoadedSearchIndex {\n const loaded = loadVaultDb();\n return {\n databasePath: loaded.databasePath,\n db: loaded.db,\n close: () => undefined,\n };\n}\n\nexport function normalizeRouteSlug(slug?: string[]): string {\n return slug?.length ? slug.join(\"/\") : \"index\";\n}\n\nfunction readMetadata(db: Database.Database): MetadataRows {\n const rows = db\n .prepare(\"SELECT key, value FROM vault_metadata\")\n .all() as Array<{ key: string; value: string }>;\n const metadata = Object.fromEntries(rows.map((row) => [row.key, row.value]));\n return {\n generatedAt: metadata.generatedAt ?? \"\",\n renderEnvironmentHash: metadata.renderEnvironmentHash ?? \"silica\",\n config: parseConfigMetadata(metadata.configJson),\n cacheState: JSON.parse(metadata.cacheStateJson ?? \"{}\") as RenderCacheState,\n };\n}\n\nfunction parseConfigMetadata(value: string | undefined): ResolvedSilicaConfig {\n if (!value) throw new Error(\"vault.db is missing configJson metadata.\");\n return JSON.parse(value) as ResolvedSilicaConfig;\n}\n\nfunction lookupAlias(\n db: Database.Database,\n strategy: string,\n alias: string,\n): string | undefined {\n const row = db\n .prepare(\n `\n SELECT slug\n FROM slug_aliases\n WHERE strategy_key = ?\n AND alias = ?\n ORDER BY sort_key, slug\n LIMIT 2\n `,\n )\n .all(strategy, alias) as Array<{ slug: string }>;\n return row.length === 1 ? row[0]?.slug : undefined;\n}\n\nfunction lookupClosestAlias(\n db: Database.Database,\n currentSlug: string,\n alias: string,\n): string | undefined {\n const rows = db\n .prepare(\n `\n SELECT slug\n FROM slug_aliases\n WHERE strategy_key = 'shortest'\n AND alias = ?\n ORDER BY slug\n `,\n )\n .all(alias) as Array<{ slug: string }>;\n\n return closestWikiLinkCandidate(\n currentSlug,\n rows.map((row) => row.slug),\n );\n}\n\nfunction lookupAssetAlias(\n db: Database.Database,\n strategy: string,\n alias: string,\n): string | undefined {\n const row = db\n .prepare(\n `\n SELECT asset_path\n FROM asset_aliases\n WHERE strategy_key = ?\n AND alias = ?\n ORDER BY sort_key, asset_path\n LIMIT 2\n `,\n )\n .all(strategy, alias) as Array<{ asset_path: string }>;\n return row.length === 1 ? row[0]?.asset_path : undefined;\n}\n\nfunction closestWikiLinkCandidate(\n currentSlug: string,\n candidates: readonly string[],\n): string | undefined {\n if (candidates.length === 0) return undefined;\n\n const currentDirectory = normalizeSlug(currentSlug, {\n numericPrefixes: false,\n })\n .split(\"/\")\n .slice(0, -1);\n const [bestCandidate] = [...candidates].sort((left, right) => {\n const leftScore = wikiLinkCandidateScore(currentDirectory, left);\n const rightScore = wikiLinkCandidateScore(currentDirectory, right);\n\n return (\n rightScore.sharedPrefixLength - leftScore.sharedPrefixLength ||\n leftScore.depth - rightScore.depth ||\n compareSlugs(left, right)\n );\n });\n\n return bestCandidate;\n}\n\nfunction wikiLinkCandidateScore(currentDirectory: string[], slug: string) {\n const simplified = slug === \"index\" ? \"\" : slug.replace(/\\/index$/, \"\");\n const segments = simplified ? simplified.split(\"/\") : [];\n\n return {\n sharedPrefixLength: sharedPrefixLength(\n currentDirectory,\n segments.slice(0, -1),\n ),\n depth: segments.length,\n };\n}\n\nfunction sharedPrefixLength(left: readonly string[], right: readonly string[]) {\n let length = 0;\n while (left[length] !== undefined && left[length] === right[length]) {\n length += 1;\n }\n return length;\n}\n\nfunction compareSlugs(left: string, right: string) {\n if (left < right) return -1;\n if (left > right) return 1;\n return 0;\n}\n\nfunction isExplicitRelativeAssetReference(value: string): boolean {\n const withoutSuffix = value.split(/[?#]/)[0] ?? \"\";\n const normalized = withoutSuffix\n .replace(/\\\\/g, \"/\")\n .replace(/^\\/+/, \"\")\n .replace(/\\/+$/, \"\");\n return /^\\.{1,2}\\//.test(normalized);\n}\n\nfunction noteRowToEntry(row: NoteRow): ManifestEntry {\n return {\n slug: row.slug,\n title: row.title,\n menuLabel: row.menu_label,\n description: row.description ?? undefined,\n generatedDescription: row.generated_description ?? undefined,\n tags: parseJsonArray(row.tags_json),\n file: path.isAbsolute(row.file)\n ? row.file\n : path.join(getProjectRoot(), row.file),\n sourcePath: row.source_path,\n sortKey: row.sort_key ?? undefined,\n created: row.created ?? undefined,\n modified: row.modified ?? undefined,\n frontmatter: parseObject(row.frontmatter_json),\n contentHash: row.content_hash,\n embeds: getEmbeds(row.slug),\n };\n}\n\nfunction normalizeDbSourcePath(value: string): string {\n return value\n .replace(/\\\\/g, \"/\")\n .replace(/^\\/+/, \"\")\n .replace(/^content\\//, \"\");\n}\n\nfunction getEmbeds(slug: string): string[] {\n return loadVaultDb()\n .db.prepare(\n \"SELECT target_slug FROM links WHERE source_slug = ? AND kind = 'embed' ORDER BY target_slug\",\n )\n .all(slug)\n .map((row) => (row as { target_slug: string }).target_slug);\n}\n\nfunction breadcrumbSegmentHref(segmentPath: string): string | undefined {\n if (getPage(segmentPath)) return slugToHref(segmentPath);\n const indexSlug = `${segmentPath}/index`;\n if (getPage(indexSlug)) return slugToHref(indexSlug);\n return undefined;\n}\n\nfunction prettySegment(segment: string): string {\n return segment\n .replace(/[-_]/g, \" \")\n .replace(/\\b\\w/g, (letter) => letter.toUpperCase());\n}\n\nfunction parseObject(value: string): Record<string, unknown> {\n return JSON.parse(value) as Record<string, unknown>;\n}\n\nfunction parseJsonArray(value: string): string[] {\n return JSON.parse(value) as string[];\n}\n"],"mappings":"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","names":[]}
@@ -1,5 +1,6 @@
1
1
  import type { ReactNode } from "react";
2
2
  import theme from "../../silica-theme";
3
+ import { assistant } from "../../silica-assistant";
3
4
  import { getLayoutProps } from "@silicajs/next/routes/layout";
4
5
 
5
6
  export default async function SiteLayout({
@@ -8,5 +9,9 @@ export default async function SiteLayout({
8
9
  children: ReactNode;
9
10
  }) {
10
11
  const props = await getLayoutProps();
11
- return <theme.SiteLayout {...props}>{children}</theme.SiteLayout>;
12
+ return (
13
+ <theme.SiteLayout {...props} assistant={assistant}>
14
+ {children}
15
+ </theme.SiteLayout>
16
+ );
12
17
  }
@@ -10,10 +10,18 @@ const nextRoot = path.dirname(fileURLToPath(import.meta.url));
10
10
  const silicaRoot = path.resolve(nextRoot, "..");
11
11
  const vaultMetadata = readVaultMetadata(path.join(silicaRoot, "vault.db"));
12
12
  type VaultConfig = {
13
+ assistant?: { provider?: { package?: string } };
13
14
  render?: { cache?: { storage?: "memory" | "filesystem" } };
14
15
  };
15
16
  const resolvedConfig = parseJson<VaultConfig>(vaultMetadata.configJson);
16
17
  const useFilesystemCache = resolvedConfig?.render?.cache?.storage !== "memory";
18
+ const serverExternalPackages = [
19
+ "better-sqlite3",
20
+ "just-bash",
21
+ resolvedConfig?.assistant?.provider?.package,
22
+ ].filter((packageName, index, packages): packageName is string => {
23
+ return Boolean(packageName) && packages.indexOf(packageName) === index;
24
+ });
17
25
 
18
26
  const baseNextConfig: NextConfig = {
19
27
  cacheComponents: true,
@@ -37,7 +45,7 @@ const baseNextConfig: NextConfig = {
37
45
  "@silicajs/ui",
38
46
  "@silicajs/theme-amethyst",
39
47
  ],
40
- serverExternalPackages: ["better-sqlite3"],
48
+ serverExternalPackages,
41
49
  outputFileTracingIncludes: {
42
50
  "/*": ["../content/**/*", "../vault.db"],
43
51
  },
@@ -1,4 +1,4 @@
1
- import { ResolvedSilicaConfig } from '@silicajs/core/runtime';
1
+ import { ResolvedSilicaAssistantConfig, ResolvedSilicaConfig } from '@silicajs/core/runtime';
2
2
 
3
3
  type TemplateFile = {
4
4
  path: string;
@@ -7,8 +7,10 @@ type TemplateFile = {
7
7
  declare function getSilicaTemplates(): TemplateFile[];
8
8
  declare function nextConfigTemplate(userConfigImport?: string): string;
9
9
  declare function themeModuleTemplate(themeValue: unknown): string;
10
+ declare function assistantModuleTemplate(assistantEnabled: boolean): string;
11
+ declare function assistantRouteTemplate(assistant: ResolvedSilicaAssistantConfig): string;
10
12
  declare function proxyTemplate(config: ResolvedSilicaConfig): string;
11
13
  declare function tsconfigTemplate(hasUserTsconfig: boolean): string;
12
14
  declare function packageJsonTemplate(): string;
13
15
 
14
- export { type TemplateFile, getSilicaTemplates, nextConfigTemplate, packageJsonTemplate, proxyTemplate, themeModuleTemplate, tsconfigTemplate };
16
+ export { type TemplateFile, assistantModuleTemplate, assistantRouteTemplate, getSilicaTemplates, nextConfigTemplate, packageJsonTemplate, proxyTemplate, themeModuleTemplate, tsconfigTemplate };
package/dist/templates.js CHANGED
@@ -23,6 +23,36 @@ function themeModuleTemplate(themeValue) {
23
23
  JSON.stringify(resolveThemeSpecifier(themeValue))
24
24
  );
25
25
  }
26
+ function assistantModuleTemplate(assistantEnabled) {
27
+ if (!assistantEnabled) {
28
+ return `import type { ThemeAssistantSlots } from "@silicajs/core/theme";
29
+
30
+ export const assistant: ThemeAssistantSlots | undefined = undefined;
31
+ `;
32
+ }
33
+ return `import {
34
+ AssistantPanel,
35
+ AssistantProvider,
36
+ AssistantTrigger,
37
+ } from "@silicajs/assistant/ui";
38
+ import type { ThemeAssistantSlots } from "@silicajs/core/theme";
39
+
40
+ export const assistant: ThemeAssistantSlots | undefined = {
41
+ Provider: AssistantProvider,
42
+ Trigger: AssistantTrigger,
43
+ Panel: AssistantPanel,
44
+ };
45
+ `;
46
+ }
47
+ function assistantRouteTemplate(assistant) {
48
+ return `import * as assistantProvider from ${JSON.stringify(assistant.provider.package)};
49
+ import { createAssistantRouteHandler } from "@silicajs/assistant/next";
50
+
51
+ export const POST = createAssistantRouteHandler({
52
+ providerModule: assistantProvider,
53
+ });
54
+ `;
55
+ }
26
56
  function proxyTemplate(config) {
27
57
  return `import type { NextRequest } from "next/server";
28
58
  import { silicaProxy } from "@silicajs/next/proxy";
@@ -137,6 +167,8 @@ function readTemplateDirectory(root, current = root) {
137
167
  });
138
168
  }
139
169
  export {
170
+ assistantModuleTemplate,
171
+ assistantRouteTemplate,
140
172
  getSilicaTemplates,
141
173
  nextConfigTemplate,
142
174
  packageJsonTemplate,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/templates.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport type { ResolvedSilicaConfig } from \"@silicajs/core/runtime\";\n\nexport type TemplateFile = {\n path: string;\n content: string;\n};\n\nconst templateFilesRoot = fileURLToPath(\n new URL(\"./template-files\", import.meta.url),\n);\n\nexport function getSilicaTemplates(): TemplateFile[] {\n return readTemplateDirectory(path.join(templateFilesRoot, \"generated-app\"));\n}\n\nexport function nextConfigTemplate(userConfigImport?: string): string {\n const template = readTemplateFile(\"next.config.ts\");\n return template\n .replace(\n \"/* __SILICA_CONFIG_IMPORT__ */\",\n userConfigImport ? `import { createJiti } from \"jiti\";` : \"\",\n )\n .replace(\n \"/* __SILICA_CONFIG_OVERRIDE__ */\",\n nextConfigOverride(userConfigImport),\n );\n}\n\nexport function themeModuleTemplate(themeValue: unknown): string {\n return readTemplateFile(\"silica-theme.ts\").replace(\n '\"{{themeSpecifier}}\"',\n JSON.stringify(resolveThemeSpecifier(themeValue)),\n );\n}\n\nexport function proxyTemplate(config: ResolvedSilicaConfig): string {\n return `import type { NextRequest } from \"next/server\";\nimport { silicaProxy } from \"@silicajs/next/proxy\";\n\nconst silicaProxyConfig = ${JSON.stringify(\n {\n authEnabled: Boolean(config.auth),\n allowedDomains: config.auth?.allowedDomains ?? [],\n allowedEmails: config.auth?.allowedEmails ?? [],\n publicPaths: config.logo ? [config.logo] : [],\n },\n null,\n 2,\n )} as const;\n\nexport function proxy(request: NextRequest) {\n return silicaProxy(request, silicaProxyConfig);\n}\n\nexport const config = {\n matcher: [\"/((?!_next/static|_next/image|favicon.ico).*)\"],\n};\n`;\n}\n\nexport function tsconfigTemplate(hasUserTsconfig: boolean): string {\n const template = readTemplateFile(\"tsconfig.json\");\n const rendered = hasUserTsconfig\n ? template.replaceAll(\"{{extends}}\", \"../../tsconfig.json\")\n : template.replace(' \"extends\": \"{{extends}}\",\\n', \"\");\n return rendered.trimEnd();\n}\n\nexport function packageJsonTemplate(): string {\n return readTemplateFile(\"package.json\");\n}\n\nfunction readTemplateFile(filename: string): string {\n return fs.readFileSync(path.join(templateFilesRoot, filename), \"utf8\");\n}\n\nfunction nextConfigOverride(userConfigImport: string | undefined): string {\n if (!userConfigImport) return \"const nextConfig = baseNextConfig;\";\n\n return `type SilicaNextConfigOverride =\n | NextConfig\n | ((base: NextConfig) => NextConfig);\n\ntype SilicaUserConfig = {\n default?: { nextConfig?: SilicaNextConfigOverride };\n nextConfig?: SilicaNextConfigOverride;\n};\n\nconst silicaUserConfig = loadSilicaUserConfig();\nconst silicaNextConfig = silicaUserConfig.nextConfig;\n\nconst nextConfig =\n typeof silicaNextConfig === \"function\"\n ? silicaNextConfig(baseNextConfig)\n : mergeNextConfig(baseNextConfig, silicaNextConfig);\n\nfunction loadSilicaUserConfig(): { nextConfig?: SilicaNextConfigOverride } {\n const jiti = createJiti(import.meta.url, { interopDefault: true });\n const loaded = jiti(${JSON.stringify(userConfigImport)}) as SilicaUserConfig;\n return loaded.default ?? loaded;\n}\n\nfunction mergeNextConfig(\n base: NextConfig,\n override: NextConfig | undefined,\n): NextConfig {\n if (!override) return base;\n return deepMerge(\n base as Record<string, unknown>,\n override as Record<string, unknown>,\n ) as NextConfig;\n}\n\nfunction deepMerge(\n base: Record<string, unknown>,\n override: Record<string, unknown>,\n): Record<string, unknown> {\n const merged = { ...base };\n for (const [key, value] of Object.entries(override)) {\n const baseValue = merged[key];\n merged[key] =\n isPlainObject(baseValue) && isPlainObject(value)\n ? deepMerge(baseValue, value)\n : value;\n }\n return merged;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return (\n typeof value === \"object\" &&\n value !== null &&\n !Array.isArray(value)\n );\n}`;\n}\n\nfunction resolveThemeSpecifier(themeValue: unknown): string {\n const themeName =\n typeof themeValue === \"object\" &&\n themeValue !== null &&\n \"name\" in themeValue\n ? String((themeValue as { name?: string }).name ?? \"default\")\n : typeof themeValue === \"string\"\n ? themeValue\n : \"default\";\n\n if (!themeName || themeName === \"default\") return \"@silicajs/theme-amethyst\";\n if (themeName.startsWith(\".\"))\n return `../../${themeName.replace(/^\\.\\//, \"\")}`;\n return themeName;\n}\n\nfunction readTemplateDirectory(root: string, current = root): TemplateFile[] {\n const entries = fs\n .readdirSync(current, { withFileTypes: true })\n .sort((left, right) => left.name.localeCompare(right.name));\n\n return entries.flatMap((entry) => {\n const absolutePath = path.join(current, entry.name);\n if (entry.isDirectory()) return readTemplateDirectory(root, absolutePath);\n if (!entry.isFile()) return [];\n\n return {\n path: path.relative(root, absolutePath).split(path.sep).join(\"/\"),\n content: fs.readFileSync(absolutePath, \"utf8\"),\n };\n });\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/templates.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport type {\n ResolvedSilicaAssistantConfig,\n ResolvedSilicaConfig,\n} from \"@silicajs/core/runtime\";\n\nexport type TemplateFile = {\n path: string;\n content: string;\n};\n\nconst templateFilesRoot = fileURLToPath(\n new URL(\"./template-files\", import.meta.url),\n);\n\nexport function getSilicaTemplates(): TemplateFile[] {\n return readTemplateDirectory(path.join(templateFilesRoot, \"generated-app\"));\n}\n\nexport function nextConfigTemplate(userConfigImport?: string): string {\n const template = readTemplateFile(\"next.config.ts\");\n return template\n .replace(\n \"/* __SILICA_CONFIG_IMPORT__ */\",\n userConfigImport ? `import { createJiti } from \"jiti\";` : \"\",\n )\n .replace(\n \"/* __SILICA_CONFIG_OVERRIDE__ */\",\n nextConfigOverride(userConfigImport),\n );\n}\n\nexport function themeModuleTemplate(themeValue: unknown): string {\n return readTemplateFile(\"silica-theme.ts\").replace(\n '\"{{themeSpecifier}}\"',\n JSON.stringify(resolveThemeSpecifier(themeValue)),\n );\n}\n\nexport function assistantModuleTemplate(assistantEnabled: boolean): string {\n if (!assistantEnabled) {\n return `import type { ThemeAssistantSlots } from \"@silicajs/core/theme\";\n\nexport const assistant: ThemeAssistantSlots | undefined = undefined;\n`;\n }\n\n return `import {\n AssistantPanel,\n AssistantProvider,\n AssistantTrigger,\n} from \"@silicajs/assistant/ui\";\nimport type { ThemeAssistantSlots } from \"@silicajs/core/theme\";\n\nexport const assistant: ThemeAssistantSlots | undefined = {\n Provider: AssistantProvider,\n Trigger: AssistantTrigger,\n Panel: AssistantPanel,\n};\n`;\n}\n\nexport function assistantRouteTemplate(\n assistant: ResolvedSilicaAssistantConfig,\n): string {\n return `import * as assistantProvider from ${JSON.stringify(assistant.provider.package)};\nimport { createAssistantRouteHandler } from \"@silicajs/assistant/next\";\n\nexport const POST = createAssistantRouteHandler({\n providerModule: assistantProvider,\n});\n`;\n}\n\nexport function proxyTemplate(config: ResolvedSilicaConfig): string {\n return `import type { NextRequest } from \"next/server\";\nimport { silicaProxy } from \"@silicajs/next/proxy\";\n\nconst silicaProxyConfig = ${JSON.stringify(\n {\n authEnabled: Boolean(config.auth),\n allowedDomains: config.auth?.allowedDomains ?? [],\n allowedEmails: config.auth?.allowedEmails ?? [],\n publicPaths: config.logo ? [config.logo] : [],\n },\n null,\n 2,\n )} as const;\n\nexport function proxy(request: NextRequest) {\n return silicaProxy(request, silicaProxyConfig);\n}\n\nexport const config = {\n matcher: [\"/((?!_next/static|_next/image|favicon.ico).*)\"],\n};\n`;\n}\n\nexport function tsconfigTemplate(hasUserTsconfig: boolean): string {\n const template = readTemplateFile(\"tsconfig.json\");\n const rendered = hasUserTsconfig\n ? template.replaceAll(\"{{extends}}\", \"../../tsconfig.json\")\n : template.replace(' \"extends\": \"{{extends}}\",\\n', \"\");\n return rendered.trimEnd();\n}\n\nexport function packageJsonTemplate(): string {\n return readTemplateFile(\"package.json\");\n}\n\nfunction readTemplateFile(filename: string): string {\n return fs.readFileSync(path.join(templateFilesRoot, filename), \"utf8\");\n}\n\nfunction nextConfigOverride(userConfigImport: string | undefined): string {\n if (!userConfigImport) return \"const nextConfig = baseNextConfig;\";\n\n return `type SilicaNextConfigOverride =\n | NextConfig\n | ((base: NextConfig) => NextConfig);\n\ntype SilicaUserConfig = {\n default?: { nextConfig?: SilicaNextConfigOverride };\n nextConfig?: SilicaNextConfigOverride;\n};\n\nconst silicaUserConfig = loadSilicaUserConfig();\nconst silicaNextConfig = silicaUserConfig.nextConfig;\n\nconst nextConfig =\n typeof silicaNextConfig === \"function\"\n ? silicaNextConfig(baseNextConfig)\n : mergeNextConfig(baseNextConfig, silicaNextConfig);\n\nfunction loadSilicaUserConfig(): { nextConfig?: SilicaNextConfigOverride } {\n const jiti = createJiti(import.meta.url, { interopDefault: true });\n const loaded = jiti(${JSON.stringify(userConfigImport)}) as SilicaUserConfig;\n return loaded.default ?? loaded;\n}\n\nfunction mergeNextConfig(\n base: NextConfig,\n override: NextConfig | undefined,\n): NextConfig {\n if (!override) return base;\n return deepMerge(\n base as Record<string, unknown>,\n override as Record<string, unknown>,\n ) as NextConfig;\n}\n\nfunction deepMerge(\n base: Record<string, unknown>,\n override: Record<string, unknown>,\n): Record<string, unknown> {\n const merged = { ...base };\n for (const [key, value] of Object.entries(override)) {\n const baseValue = merged[key];\n merged[key] =\n isPlainObject(baseValue) && isPlainObject(value)\n ? deepMerge(baseValue, value)\n : value;\n }\n return merged;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return (\n typeof value === \"object\" &&\n value !== null &&\n !Array.isArray(value)\n );\n}`;\n}\n\nfunction resolveThemeSpecifier(themeValue: unknown): string {\n const themeName =\n typeof themeValue === \"object\" &&\n themeValue !== null &&\n \"name\" in themeValue\n ? String((themeValue as { name?: string }).name ?? \"default\")\n : typeof themeValue === \"string\"\n ? themeValue\n : \"default\";\n\n if (!themeName || themeName === \"default\") return \"@silicajs/theme-amethyst\";\n if (themeName.startsWith(\".\"))\n return `../../${themeName.replace(/^\\.\\//, \"\")}`;\n return themeName;\n}\n\nfunction readTemplateDirectory(root: string, current = root): TemplateFile[] {\n const entries = fs\n .readdirSync(current, { withFileTypes: true })\n .sort((left, right) => left.name.localeCompare(right.name));\n\n return entries.flatMap((entry) => {\n const absolutePath = path.join(current, entry.name);\n if (entry.isDirectory()) return readTemplateDirectory(root, absolutePath);\n if (!entry.isFile()) return [];\n\n return {\n path: path.relative(root, absolutePath).split(path.sep).join(\"/\"),\n content: fs.readFileSync(absolutePath, \"utf8\"),\n };\n });\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@silicajs/next",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Next.js runtime, routes, templates, and proxy for Silica.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -85,6 +85,13 @@
85
85
  "import": "./dist/routing-provider.js"
86
86
  }
87
87
  },
88
+ "typesVersions": {
89
+ "*": {
90
+ "server-data": [
91
+ "dist/server-data.d.ts"
92
+ ]
93
+ }
94
+ },
88
95
  "files": [
89
96
  "dist"
90
97
  ],
@@ -96,8 +103,8 @@
96
103
  },
97
104
  "dependencies": {
98
105
  "@silicajs/auth": "^0.1.2",
99
- "@silicajs/components": "^0.3.0",
100
- "@silicajs/core": "^0.7.0",
106
+ "@silicajs/components": "^0.4.0",
107
+ "@silicajs/core": "^0.8.0",
101
108
  "@silicajs/remark-obsidian": "^0.1.0",
102
109
  "@silicajs/search": "^0.3.1",
103
110
  "better-auth": "1.6.16",