@treeseed/core 0.4.13 → 0.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/dist/agents/adapters/notification.d.ts +16 -1
  2. package/dist/agents/adapters/notification.js +31 -1
  3. package/dist/agents/adapters/research.d.ts +13 -1
  4. package/dist/agents/adapters/research.js +35 -1
  5. package/dist/agents/contracts/run.d.ts +1 -0
  6. package/dist/agents/kernel/agent-kernel.d.ts +2 -2
  7. package/dist/agents/kernel/agent-kernel.js +10 -3
  8. package/dist/agents/kernel/trigger-resolver.d.ts +1 -0
  9. package/dist/agents/kernel/trigger-resolver.js +5 -1
  10. package/dist/agents/runtime-types.d.ts +1 -0
  11. package/dist/api/app.js +10 -0
  12. package/dist/api/auth/d1-store.js +5 -0
  13. package/dist/api/auth/memory-provider.js +6 -1
  14. package/dist/api/auth/rbac.d.ts +2 -2
  15. package/dist/api/auth/rbac.js +2 -0
  16. package/dist/api/capabilities.d.ts +9 -0
  17. package/dist/api/capabilities.js +33 -0
  18. package/dist/api/operations-routes.d.ts +4 -0
  19. package/dist/api/operations-routes.js +49 -1
  20. package/dist/api/project-routes.d.ts +8 -0
  21. package/dist/api/project-routes.js +586 -0
  22. package/dist/api/types.d.ts +7 -0
  23. package/dist/components/site/NotesList.astro +13 -2
  24. package/dist/components/site/PublishedContentBody.astro +5 -0
  25. package/dist/content.js +77 -9
  26. package/dist/dev.d.ts +2 -2
  27. package/dist/dev.js +0 -15
  28. package/dist/env.yaml +39 -26
  29. package/dist/index.d.ts +1 -0
  30. package/dist/index.js +7 -1
  31. package/dist/launch.d.ts +3 -0
  32. package/dist/launch.js +8 -0
  33. package/dist/layouts/AuthoredEntryLayout.astro +76 -28
  34. package/dist/layouts/ProfileLayout.astro +9 -5
  35. package/dist/middleware.js +11 -0
  36. package/dist/pages/[slug].astro +10 -6
  37. package/dist/pages/agents/[slug].astro +17 -7
  38. package/dist/pages/agents/index.astro +2 -1
  39. package/dist/pages/books/[slug].astro +10 -5
  40. package/dist/pages/books/index.astro +4 -1
  41. package/dist/pages/decisions/[slug].astro +73 -0
  42. package/dist/pages/decisions/index.astro +47 -0
  43. package/dist/pages/docs-runtime/[...slug].astro +102 -0
  44. package/dist/pages/docs-runtime/index.astro +89 -0
  45. package/dist/pages/feed.xml.js +2 -1
  46. package/dist/pages/index.astro +160 -16
  47. package/dist/pages/notes/[slug].astro +10 -5
  48. package/dist/pages/notes/index.astro +6 -3
  49. package/dist/pages/objectives/[slug].astro +27 -9
  50. package/dist/pages/objectives/index.astro +19 -2
  51. package/dist/pages/people/[slug].astro +17 -7
  52. package/dist/pages/people/index.astro +2 -1
  53. package/dist/pages/proposals/[slug].astro +72 -0
  54. package/dist/pages/proposals/index.astro +47 -0
  55. package/dist/pages/questions/[slug].astro +27 -9
  56. package/dist/pages/questions/index.astro +19 -2
  57. package/dist/scripts/dev-platform.js +0 -1
  58. package/dist/scripts/release-verify.js +29 -2
  59. package/dist/scripts/tenant-build.js +4 -1
  60. package/dist/scripts/tenant-check.js +4 -1
  61. package/dist/services/agents.d.ts +1 -12
  62. package/dist/services/agents.js +28 -9
  63. package/dist/services/index.d.ts +0 -2
  64. package/dist/services/index.js +0 -6
  65. package/dist/services/manager.d.ts +4 -4
  66. package/dist/services/manager.js +123 -50
  67. package/dist/services/workday-report.d.ts +3 -3
  68. package/dist/services/workday-start.d.ts +3 -3
  69. package/dist/services/worker-capacity.d.ts +58 -0
  70. package/dist/services/worker-capacity.js +208 -0
  71. package/dist/services/worker.js +70 -13
  72. package/dist/site.js +18 -5
  73. package/dist/tenant/runtime-config.js +8 -1
  74. package/dist/utils/hub-content.js +14 -0
  75. package/dist/utils/published-content.js +13 -0
  76. package/dist/utils/site-config.js +20 -0
  77. package/dist/utils/site-content-runtime.js +185 -0
  78. package/dist/utils/web-cache.js +149 -0
  79. package/package.json +11 -6
  80. package/scripts/verify-driver.mjs +34 -0
  81. package/templates/github/deploy.workflow.yml +11 -1
@@ -1,7 +1,11 @@
1
1
  ---
2
2
  import MainLayout from './MainLayout.astro';
3
3
  import StatusBadge from '../components/content/StatusBadge.astro';
4
- import type { CollectionEntry } from 'astro:content';
4
+ import type { RuntimeReferenceEntry } from '../utils/site-content-runtime';
5
+
6
+ function entryTitle(entry: RuntimeReferenceEntry) {
7
+ return entry.data.title ?? entry.data.name ?? entry.id;
8
+ }
5
9
 
6
10
  const {
7
11
  entry,
@@ -22,8 +26,8 @@ const {
22
26
  currentPath: string;
23
27
  metaLabel: string;
24
28
  metaValue: string;
25
- relatedQuestions?: CollectionEntry<'questions'>[];
26
- relatedObjectives?: CollectionEntry<'objectives'>[];
29
+ relatedQuestions?: RuntimeReferenceEntry[];
30
+ relatedObjectives?: RuntimeReferenceEntry[];
27
31
  };
28
32
  ---
29
33
 
@@ -60,7 +64,7 @@ const {
60
64
  <p class="text-sm font-semibold uppercase tracking-[0.14em] text-[color:var(--site-blue-strong)]">Questions</p>
61
65
  <ul class="mt-3 space-y-2 text-[color:var(--site-text-muted)]">
62
66
  {relatedQuestions.map((question) => (
63
- <li><a href={`/questions/${question.id}/`} class="hover:text-[color:var(--site-text)]">{question.data.title}</a></li>
67
+ <li><a href={`/questions/${question.id}/`} class="hover:text-[color:var(--site-text)]">{entryTitle(question)}</a></li>
64
68
  ))}
65
69
  </ul>
66
70
  </div>
@@ -70,7 +74,7 @@ const {
70
74
  <p class="text-sm font-semibold uppercase tracking-[0.14em] text-[color:var(--site-blue-strong)]">Objectives</p>
71
75
  <ul class="mt-3 space-y-2 text-[color:var(--site-text-muted)]">
72
76
  {relatedObjectives.map((objective) => (
73
- <li><a href={`/objectives/${objective.id}/`} class="hover:text-[color:var(--site-text)]">{objective.data.title}</a></li>
77
+ <li><a href={`/objectives/${objective.id}/`} class="hover:text-[color:var(--site-text)]">{entryTitle(objective)}</a></li>
74
78
  ))}
75
79
  </ul>
76
80
  </div>
@@ -0,0 +1,11 @@
1
+ import { defineMiddleware } from "astro:middleware";
2
+ import { resolveEditorialPreview } from "./middleware/editorial-preview.js";
3
+ import { applyTreeseedWebCacheHeaders } from "./utils/web-cache.js";
4
+ const onRequest = defineMiddleware(async (context, next) => {
5
+ resolveEditorialPreview(context);
6
+ const response = await next();
7
+ return applyTreeseedWebCacheHeaders(context.request, context.url, response);
8
+ });
9
+ export {
10
+ onRequest
11
+ };
@@ -2,31 +2,35 @@
2
2
  import { getCollection, render } from 'astro:content';
3
3
  import ContentLayout from '../layouts/ContentLayout.astro';
4
4
  import BridgeLayout from '../layouts/BridgeLayout.astro';
5
+ import PublishedContentBody from '../components/site/PublishedContentBody.astro';
5
6
  import RouteNotFound from '../components/site/RouteNotFound.astro';
7
+ import { isPublishedRuntimeContentMode, loadPublishedEntry } from '../utils/site-content-runtime';
6
8
 
7
9
  export const prerender = false;
8
10
 
9
11
  const slug = String(Astro.params.slug ?? '');
10
- const entries = await getCollection('pages');
11
- const entry = entries.find((candidate) => candidate.data.slug === slug) ?? null;
12
+ const publishedRuntime = isPublishedRuntimeContentMode();
13
+ const localEntry = publishedRuntime ? null : (await getCollection('pages')).find((candidate) => candidate.data.slug === slug) ?? null;
14
+ const publishedEntry = publishedRuntime ? await loadPublishedEntry(Astro.locals, 'pages', slug) : null;
15
+ const entry = publishedRuntime ? publishedEntry?.entry ?? null : localEntry;
12
16
  if (!entry) {
13
17
  Astro.response.status = 404;
14
18
  }
15
- const rendered = entry ? await render(entry) : null;
19
+ const rendered = !publishedRuntime && localEntry ? await render(localEntry) : null;
16
20
  const Content = rendered?.Content ?? null;
17
21
  const currentPath = Astro.url.pathname;
18
22
  ---
19
23
 
20
24
  {
21
- !entry || !Content ? (
25
+ !entry || (!Content && !publishedEntry?.html) ? (
22
26
  <RouteNotFound title="Page not found" description="The requested page is not available in this Treeseed." currentPath={currentPath} />
23
27
  ) : entry.data.pageLayout === 'bridge' ? (
24
28
  <BridgeLayout entry={entry.data} currentPath={currentPath}>
25
- <Content />
29
+ {publishedRuntime ? <PublishedContentBody html={publishedEntry?.html ?? ''} /> : <Content />}
26
30
  </BridgeLayout>
27
31
  ) : (
28
32
  <ContentLayout entry={entry.data} currentPath={currentPath}>
29
- <Content />
33
+ {publishedRuntime ? <PublishedContentBody html={publishedEntry?.html ?? ''} /> : <Content />}
30
34
  </ContentLayout>
31
35
  )
32
36
  }
@@ -2,24 +2,34 @@
2
2
  import { getCollection, render } from 'astro:content';
3
3
  import ProfileLayout from '../../layouts/ProfileLayout.astro';
4
4
  import { resolveReferences } from '../../utils/hub-content';
5
+ import PublishedContentBody from '../../components/site/PublishedContentBody.astro';
5
6
  import RouteNotFound from '../../components/site/RouteNotFound.astro';
7
+ import { isPublishedRuntimeContentMode, loadPublishedEntry, metadataFromPublishedContent, resolvePublishedReferences } from '../../utils/site-content-runtime';
6
8
 
7
9
  export const prerender = false;
8
10
 
9
11
  const slug = String(Astro.params.slug ?? '');
10
- const agents = await getCollection('agents');
11
- const agent = agents.find((candidate) => candidate.id === slug) ?? null;
12
+ const publishedRuntime = isPublishedRuntimeContentMode();
13
+ const agents = publishedRuntime ? [] : await getCollection('agents');
14
+ const localAgent = publishedRuntime ? null : agents.find((candidate) => candidate.id === slug) ?? null;
15
+ const publishedAgent = publishedRuntime ? await loadPublishedEntry(Astro.locals, 'agents', slug) : null;
16
+ const agent = publishedRuntime ? publishedAgent?.entry ?? null : localAgent;
17
+ const metadata = publishedRuntime ? metadataFromPublishedContent(publishedAgent?.content) : null;
12
18
  if (!agent) {
13
19
  Astro.response.status = 404;
14
20
  }
15
- const rendered = agent ? await render(agent) : null;
21
+ const rendered = !publishedRuntime && localAgent ? await render(localAgent) : null;
16
22
  const Content = rendered?.Content ?? null;
17
- const relatedQuestions = agent ? await resolveReferences(agent.data.relatedQuestions) : [];
18
- const relatedObjectives = agent ? await resolveReferences(agent.data.relatedObjectives) : [];
23
+ const relatedQuestions = publishedRuntime
24
+ ? await resolvePublishedReferences(Astro.locals, 'questions', metadata?.relatedQuestions)
25
+ : agent ? await resolveReferences(agent.data.relatedQuestions) : [];
26
+ const relatedObjectives = publishedRuntime
27
+ ? await resolvePublishedReferences(Astro.locals, 'objectives', metadata?.relatedObjectives)
28
+ : agent ? await resolveReferences(agent.data.relatedObjectives) : [];
19
29
  ---
20
30
 
21
31
  {
22
- !agent || !Content ? (
32
+ !agent || (!Content && !publishedAgent?.html) ? (
23
33
  <RouteNotFound title="Agent not found" description="The requested agent profile could not be found in this Treeseed." currentPath="/agents/" />
24
34
  ) : (
25
35
  <ProfileLayout
@@ -30,7 +40,7 @@ const relatedObjectives = agent ? await resolveReferences(agent.data.relatedObje
30
40
  relatedQuestions={relatedQuestions}
31
41
  relatedObjectives={relatedObjectives}
32
42
  >
33
- <Content />
43
+ {publishedRuntime ? <PublishedContentBody html={publishedAgent?.html ?? ''} /> : <Content />}
34
44
  </ProfileLayout>
35
45
  )
36
46
  }
@@ -3,8 +3,9 @@ import { getCollection } from 'astro:content';
3
3
  import MainLayout from '../../layouts/MainLayout.astro';
4
4
  import SectionIntro from '../../components/site/SectionIntro.astro';
5
5
  import ProfileList from '../../components/site/ProfileList.astro';
6
+ import { isPublishedRuntimeContentMode, loadPublishedCollection } from '../../utils/site-content-runtime';
6
7
 
7
- const agents = await getCollection('agents');
8
+ const agents = isPublishedRuntimeContentMode() ? await loadPublishedCollection(Astro.locals, 'agents') : await getCollection('agents');
8
9
  ---
9
10
 
10
11
  <MainLayout title="Agents" description="Software contributors participating in the TreeSeed working site." currentPath="/agents/">
@@ -1,26 +1,31 @@
1
1
  ---
2
2
  import { getCollection, render } from 'astro:content';
3
3
  import BookLayout from '../../layouts/BookLayout.astro';
4
+ import PublishedContentBody from '../../components/site/PublishedContentBody.astro';
4
5
  import RouteNotFound from '../../components/site/RouteNotFound.astro';
6
+ import { isPublishedRuntimeContentMode, loadPublishedEntry } from '../../utils/site-content-runtime';
5
7
 
6
8
  export const prerender = false;
7
9
 
8
10
  const slug = String(Astro.params.slug ?? '');
9
- const books = (await getCollection('books')).sort((a, b) => a.data.order - b.data.order);
10
- const book = books.find((candidate) => candidate.id === slug) ?? null;
11
+ const publishedRuntime = isPublishedRuntimeContentMode();
12
+ const books = publishedRuntime ? [] : (await getCollection('books')).sort((a, b) => a.data.order - b.data.order);
13
+ const localBook = publishedRuntime ? null : books.find((candidate) => candidate.id === slug) ?? null;
14
+ const publishedBook = publishedRuntime ? await loadPublishedEntry(Astro.locals, 'books', slug) : null;
15
+ const book = publishedRuntime ? publishedBook?.entry ?? null : localBook;
11
16
  if (!book) {
12
17
  Astro.response.status = 404;
13
18
  }
14
- const rendered = book ? await render(book) : null;
19
+ const rendered = !publishedRuntime && localBook ? await render(localBook) : null;
15
20
  const Content = rendered?.Content ?? null;
16
21
  ---
17
22
 
18
23
  {
19
- !book || !Content ? (
24
+ !book || (!Content && !publishedBook?.html) ? (
20
25
  <RouteNotFound title="Book not found" description="The requested book could not be found in this Treeseed." currentPath="/books/" />
21
26
  ) : (
22
27
  <BookLayout entry={book.data} currentPath="/books/">
23
- <Content />
28
+ {publishedRuntime ? <PublishedContentBody html={publishedBook?.html ?? ''} /> : <Content />}
24
29
  </BookLayout>
25
30
  )
26
31
  }
@@ -3,8 +3,11 @@ import { getCollection } from 'astro:content';
3
3
  import MainLayout from '../../layouts/MainLayout.astro';
4
4
  import SectionIntro from '../../components/site/SectionIntro.astro';
5
5
  import BookList from '../../components/site/BookList.astro';
6
+ import { isPublishedRuntimeContentMode, loadPublishedCollection } from '../../utils/site-content-runtime';
6
7
 
7
- const books = (await getCollection('books')).sort((a, b) => a.data.order - b.data.order);
8
+ const books = isPublishedRuntimeContentMode()
9
+ ? (await loadPublishedCollection(Astro.locals, 'books')).sort((a, b) => Number(a.data.order ?? 0) - Number(b.data.order ?? 0))
10
+ : (await getCollection('books')).sort((a, b) => a.data.order - b.data.order);
8
11
  ---
9
12
 
10
13
  <MainLayout title="Books" description="Queryable book metadata for the TreeSeed working site." currentPath="/books/">
@@ -0,0 +1,73 @@
1
+ ---
2
+ import { getCollection, render } from 'astro:content';
3
+ import AuthoredEntryLayout from '../../layouts/AuthoredEntryLayout.astro';
4
+ import { resolveContributor, resolveReferences } from '../../utils/hub-content';
5
+ import PublishedContentBody from '../../components/site/PublishedContentBody.astro';
6
+ import RouteNotFound from '../../components/site/RouteNotFound.astro';
7
+ import {
8
+ isPublishedRuntimeContentMode,
9
+ loadPublishedEntry,
10
+ metadataFromPublishedContent,
11
+ resolvePublishedContributor,
12
+ resolvePublishedReferences,
13
+ } from '../../utils/site-content-runtime';
14
+
15
+ export const prerender = false;
16
+
17
+ const slug = String(Astro.params.slug ?? '');
18
+ const publishedRuntime = isPublishedRuntimeContentMode();
19
+ const decisions = publishedRuntime ? [] : await getCollection('decisions', ({ data }) => !data.draft);
20
+ const localDecision = publishedRuntime ? null : decisions.find((candidate) => candidate.id === slug) ?? null;
21
+ const publishedDecision = publishedRuntime ? await loadPublishedEntry(Astro.locals, 'decisions', slug) : null;
22
+ const decision = publishedRuntime ? publishedDecision?.entry ?? null : localDecision;
23
+ const metadata = publishedRuntime ? metadataFromPublishedContent(publishedDecision?.content) : null;
24
+ if (!decision) {
25
+ Astro.response.status = 404;
26
+ }
27
+ const rendered = !publishedRuntime && localDecision ? await render(localDecision) : null;
28
+ const Content = rendered?.Content ?? null;
29
+ const contributor = publishedRuntime
30
+ ? await resolvePublishedContributor(Astro.locals, metadata?.primaryContributor)
31
+ : decision ? await resolveContributor(decision.data.primaryContributor) : null;
32
+ const relatedObjectives = publishedRuntime
33
+ ? await resolvePublishedReferences(Astro.locals, 'objectives', metadata?.relatedObjectives)
34
+ : decision ? await resolveReferences(decision.data.relatedObjectives) : [];
35
+ const relatedQuestions = publishedRuntime
36
+ ? await resolvePublishedReferences(Astro.locals, 'questions', metadata?.relatedQuestions)
37
+ : decision ? await resolveReferences(decision.data.relatedQuestions) : [];
38
+ const relatedNotes = publishedRuntime
39
+ ? await resolvePublishedReferences(Astro.locals, 'notes', metadata?.relatedNotes)
40
+ : decision ? await resolveReferences(decision.data.relatedNotes) : [];
41
+ const relatedProposals = publishedRuntime
42
+ ? await resolvePublishedReferences(Astro.locals, 'proposals', metadata?.relatedProposals)
43
+ : decision ? await resolveReferences(decision.data.relatedProposals) : [];
44
+ const relatedBooks = publishedRuntime
45
+ ? await resolvePublishedReferences(Astro.locals, 'books', metadata?.relatedBooks)
46
+ : decision ? await resolveReferences(decision.data.relatedBooks) : [];
47
+ const supersededDecisions = publishedRuntime
48
+ ? await resolvePublishedReferences(Astro.locals, 'decisions', metadata?.supersedes)
49
+ : decision ? await resolveReferences(decision.data.supersedes) : [];
50
+ ---
51
+
52
+ {
53
+ !decision || (!Content && !publishedDecision?.html) ? (
54
+ <RouteNotFound title="Decision not found" description="The requested decision could not be found in this Treeseed." currentPath="/decisions/" />
55
+ ) : (
56
+ <AuthoredEntryLayout
57
+ entry={decision.data}
58
+ currentPath="/decisions/"
59
+ contributor={contributor}
60
+ metaLabel="Decision type"
61
+ metaValue={String(decision.data.decisionType ?? '')}
62
+ introText={`${decision.data.rationale ?? ''} Authority: ${decision.data.authority ?? ''}.`}
63
+ relatedObjectives={relatedObjectives}
64
+ relatedQuestions={relatedQuestions}
65
+ relatedNotes={relatedNotes}
66
+ relatedProposals={relatedProposals}
67
+ relatedDecisions={supersededDecisions}
68
+ relatedBooks={relatedBooks}
69
+ >
70
+ {publishedRuntime ? <PublishedContentBody html={publishedDecision?.html ?? ''} /> : <Content />}
71
+ </AuthoredEntryLayout>
72
+ )
73
+ }
@@ -0,0 +1,47 @@
1
+ ---
2
+ import MainLayout from '../../layouts/MainLayout.astro';
3
+ import SectionIntro from '../../components/site/SectionIntro.astro';
4
+ import ChronicleList from '../../components/site/ChronicleList.astro';
5
+ import { getPublishedDecisions, resolveContributorsForEntries } from '../../utils/hub-content';
6
+ import { isPublishedRuntimeContentMode, loadPublishedCollection, resolvePublishedContributor, metadataFromPublishedContent, loadPublishedEntry } from '../../utils/site-content-runtime';
7
+
8
+ const publishedRuntime = isPublishedRuntimeContentMode();
9
+ const decisions = publishedRuntime
10
+ ? (await loadPublishedCollection(Astro.locals, 'decisions')).sort((a, b) => b.data.date.valueOf() - a.data.date.valueOf())
11
+ : await getPublishedDecisions();
12
+ const contributors = publishedRuntime
13
+ ? new Map(
14
+ await Promise.all(
15
+ decisions.map(async (decision) => {
16
+ const detail = await loadPublishedEntry(Astro.locals, 'decisions', decision.id);
17
+ const contributor = await resolvePublishedContributor(
18
+ Astro.locals,
19
+ metadataFromPublishedContent(detail?.content)?.primaryContributor,
20
+ );
21
+ return [decision.id, contributor ?? null] as const;
22
+ }),
23
+ ),
24
+ )
25
+ : await resolveContributorsForEntries(decisions);
26
+ ---
27
+
28
+ <MainLayout title="Decisions" description="Decisions that record what was chosen, authorized, deferred, or rejected." currentPath="/decisions/">
29
+ <div class="space-y-10">
30
+ <SectionIntro
31
+ eyebrow="Decisions"
32
+ title="Institutional memory for chosen paths"
33
+ description="Decisions close the loop. They record what was accepted, rejected, deferred, or superseded so the site can move from context to action without losing the rationale."
34
+ />
35
+ <ChronicleList
36
+ items={decisions.map((decision) => ({
37
+ href: `/decisions/${decision.id}/`,
38
+ title: decision.data.title,
39
+ summary: decision.data.summary,
40
+ status: decision.data.status,
41
+ date: decision.data.date,
42
+ meta: contributors.get(decision.id)?.data.name,
43
+ tags: decision.data.tags,
44
+ }))}
45
+ />
46
+ </div>
47
+ </MainLayout>
@@ -0,0 +1,102 @@
1
+ ---
2
+ import MainLayout from '../../layouts/MainLayout.astro';
3
+ import PublishedContentBody from '../../components/site/PublishedContentBody.astro';
4
+ import RouteNotFound from '../../components/site/RouteNotFound.astro';
5
+ import { loadHostedBookRuntime, loadHostedDocsTree } from '../../utils/published-content';
6
+ import { loadPublishedEntry } from '../../utils/site-content-runtime';
7
+
8
+ const slug = String(Astro.params.slug ?? '').replace(/^\/+|\/+$/gu, '');
9
+ const publishedSlug = slug ? `knowledge/${slug}` : 'knowledge';
10
+ const currentPath = slug ? `/knowledge/${slug}/` : '/knowledge/';
11
+ const runtime = await loadHostedBookRuntime(Astro.locals);
12
+ const docsTree = await loadHostedDocsTree(Astro.locals);
13
+ const document = await loadPublishedEntry(Astro.locals, 'docs', publishedSlug);
14
+
15
+ const pathStartsWith = (candidate, prefix) =>
16
+ String(candidate ?? '').replace(/\/+$/u, '').startsWith(String(prefix ?? '').replace(/\/+$/u, ''));
17
+
18
+ const activeBook = runtime?.BOOKS.find((book) => pathStartsWith(currentPath, book.basePath)) ?? null;
19
+ const sidebarEntries = activeBook
20
+ ? (docsTree ?? []).filter((entry) => pathStartsWith(entry.path, activeBook.basePath))
21
+ : docsTree ?? [];
22
+ const download = activeBook
23
+ ? {
24
+ title: activeBook.downloadTitle,
25
+ href: activeBook.downloadHref,
26
+ }
27
+ : runtime?.TREESEED_LIBRARY_DOWNLOAD
28
+ ? {
29
+ title: runtime.TREESEED_LIBRARY_DOWNLOAD.downloadTitle,
30
+ href: runtime.TREESEED_LIBRARY_DOWNLOAD.downloadHref,
31
+ }
32
+ : null;
33
+ ---
34
+
35
+ {
36
+ !document ? (
37
+ <RouteNotFound
38
+ title="Knowledge page not found"
39
+ description="The requested knowledge page is not available in published runtime content."
40
+ currentPath={currentPath}
41
+ />
42
+ ) : (
43
+ <MainLayout
44
+ title={document.entry.data.title ?? 'Knowledge page'}
45
+ description={document.entry.data.summary ?? 'Published knowledge page'}
46
+ currentPath={currentPath}
47
+ >
48
+ <div class="grid gap-8 lg:grid-cols-[0.75fr_1.25fr]">
49
+ <aside class="space-y-6 border border-[color:var(--site-border)] bg-[color:var(--site-surface)] p-6">
50
+ <div class="space-y-3">
51
+ <p class="text-sm font-semibold uppercase tracking-[0.16em] text-[color:var(--site-blue-strong)]">
52
+ {activeBook?.sectionLabel ?? 'Knowledge'}
53
+ </p>
54
+ <a href="/knowledge/" class="block text-sm font-medium text-[color:var(--site-text-muted)] hover:text-[color:var(--site-text)]">Knowledge home</a>
55
+ {activeBook?.landingPath && (
56
+ <a href={activeBook.landingPath} class="block text-sm font-medium text-[color:var(--site-text-muted)] hover:text-[color:var(--site-text)]">Open book landing page</a>
57
+ )}
58
+ {download && (
59
+ <a
60
+ href={download.href}
61
+ class="inline-flex border border-[color:var(--site-accent)] bg-[color:var(--site-accent)] px-4 py-2 text-sm font-semibold text-[color:var(--site-text)] transition hover:border-[color:var(--site-blue)] hover:bg-[color:var(--site-blue-soft)]"
62
+ >
63
+ {download.title}
64
+ </a>
65
+ )}
66
+ </div>
67
+ <ul class="space-y-2 text-sm text-[color:var(--site-text-muted)]">
68
+ {sidebarEntries.map((entry) => (
69
+ <li>
70
+ <a
71
+ href={entry.path}
72
+ class:list={[
73
+ 'hover:text-[color:var(--site-text)]',
74
+ entry.path === currentPath && 'font-semibold text-[color:var(--site-text)]',
75
+ ]}
76
+ >
77
+ {entry.title ?? entry.slug}
78
+ </a>
79
+ </li>
80
+ ))}
81
+ </ul>
82
+ </aside>
83
+ <article class="space-y-6">
84
+ <header class="space-y-4 border-b border-[color:var(--site-border)] pb-6">
85
+ <p class="text-sm font-semibold uppercase tracking-[0.16em] text-[color:var(--site-blue-strong)]">
86
+ {activeBook?.sectionLabel ?? 'Knowledge'}
87
+ </p>
88
+ <h1 class="font-serif text-5xl font-bold tracking-tight text-[color:var(--site-text)] md:text-6xl">
89
+ {document.entry.data.title ?? document.entry.slug}
90
+ </h1>
91
+ {document.entry.data.summary && (
92
+ <p class="max-w-3xl text-xl leading-10 text-[color:var(--site-text-muted)]">{document.entry.data.summary}</p>
93
+ )}
94
+ </header>
95
+ <div class="prose-karyon max-w-none">
96
+ <PublishedContentBody html={document.html} />
97
+ </div>
98
+ </article>
99
+ </div>
100
+ </MainLayout>
101
+ )
102
+ }
@@ -0,0 +1,89 @@
1
+ ---
2
+ import MainLayout from '../../layouts/MainLayout.astro';
3
+ import PublishedContentBody from '../../components/site/PublishedContentBody.astro';
4
+ import RouteNotFound from '../../components/site/RouteNotFound.astro';
5
+ import { loadHostedBookRuntime, loadHostedDocsTree } from '../../utils/published-content';
6
+ import { loadPublishedEntry } from '../../utils/site-content-runtime';
7
+
8
+ const runtime = await loadHostedBookRuntime(Astro.locals);
9
+ const docsTree = await loadHostedDocsTree(Astro.locals);
10
+ const document = await loadPublishedEntry(Astro.locals, 'docs', 'knowledge');
11
+
12
+ const pathStartsWith = (candidate, prefix) =>
13
+ String(candidate ?? '').replace(/\/+$/u, '').startsWith(String(prefix ?? '').replace(/\/+$/u, ''));
14
+
15
+ const bookGroups = runtime?.BOOKS.map((book) => ({
16
+ book,
17
+ entries: (docsTree ?? []).filter((entry) => pathStartsWith(entry.path, book.basePath)),
18
+ })) ?? [];
19
+
20
+ const ungroupedEntries = (docsTree ?? []).filter((entry) =>
21
+ !bookGroups.some((group) => group.entries.some((candidate) => candidate.path === entry.path)),
22
+ );
23
+ ---
24
+
25
+ {
26
+ !document ? (
27
+ <RouteNotFound
28
+ title="Knowledge home not found"
29
+ description="The knowledge hub home page is not available in published runtime content."
30
+ currentPath="/knowledge/"
31
+ />
32
+ ) : (
33
+ <MainLayout
34
+ title={document.entry.data.title ?? 'Knowledge'}
35
+ description={document.entry.data.summary ?? 'Published knowledge hub'}
36
+ currentPath="/knowledge/"
37
+ >
38
+ <div class="grid gap-8 lg:grid-cols-[0.75fr_1.25fr]">
39
+ <aside class="space-y-6 border border-[color:var(--site-border)] bg-[color:var(--site-surface)] p-6">
40
+ <div class="space-y-3">
41
+ <p class="text-sm font-semibold uppercase tracking-[0.16em] text-[color:var(--site-blue-strong)]">Knowledge hub</p>
42
+ {runtime?.TREESEED_LIBRARY_DOWNLOAD && (
43
+ <a
44
+ href={runtime.TREESEED_LIBRARY_DOWNLOAD.downloadHref}
45
+ class="inline-flex border border-[color:var(--site-accent)] bg-[color:var(--site-accent)] px-4 py-2 text-sm font-semibold text-[color:var(--site-text)] transition hover:border-[color:var(--site-blue)] hover:bg-[color:var(--site-blue-soft)]"
46
+ >
47
+ {runtime.TREESEED_LIBRARY_DOWNLOAD.downloadTitle}
48
+ </a>
49
+ )}
50
+ </div>
51
+ {bookGroups.map((group) => (
52
+ <div class="space-y-3">
53
+ <p class="text-sm font-semibold uppercase tracking-[0.14em] text-[color:var(--site-blue-strong)]">{group.book.sectionLabel}</p>
54
+ <ul class="space-y-2 text-sm text-[color:var(--site-text-muted)]">
55
+ {group.entries.map((entry) => (
56
+ <li><a href={entry.path} class="hover:text-[color:var(--site-text)]">{entry.title ?? entry.slug}</a></li>
57
+ ))}
58
+ </ul>
59
+ </div>
60
+ ))}
61
+ {ungroupedEntries.length > 0 && (
62
+ <div class="space-y-3">
63
+ <p class="text-sm font-semibold uppercase tracking-[0.14em] text-[color:var(--site-blue-strong)]">More knowledge</p>
64
+ <ul class="space-y-2 text-sm text-[color:var(--site-text-muted)]">
65
+ {ungroupedEntries.map((entry) => (
66
+ <li><a href={entry.path} class="hover:text-[color:var(--site-text)]">{entry.title ?? entry.slug}</a></li>
67
+ ))}
68
+ </ul>
69
+ </div>
70
+ )}
71
+ </aside>
72
+ <article class="space-y-6">
73
+ <header class="space-y-4 border-b border-[color:var(--site-border)] pb-6">
74
+ <p class="text-sm font-semibold uppercase tracking-[0.16em] text-[color:var(--site-blue-strong)]">Published docs</p>
75
+ <h1 class="font-serif text-5xl font-bold tracking-tight text-[color:var(--site-text)] md:text-6xl">
76
+ {document.entry.data.title ?? 'Knowledge'}
77
+ </h1>
78
+ {document.entry.data.summary && (
79
+ <p class="max-w-3xl text-xl leading-10 text-[color:var(--site-text-muted)]">{document.entry.data.summary}</p>
80
+ )}
81
+ </header>
82
+ <div class="prose-karyon max-w-none">
83
+ <PublishedContentBody html={document.html} />
84
+ </div>
85
+ </article>
86
+ </div>
87
+ </MainLayout>
88
+ )
89
+ }
@@ -1,10 +1,11 @@
1
1
  import { getPublishedNotes } from "../utils/hub-content.js";
2
+ import { isPublishedRuntimeContentMode, loadPublishedCollection } from "../utils/site-content-runtime.js";
2
3
  import { siteModelRendered } from "../utils/site-models.js";
3
4
  async function GET(context) {
4
5
  if (!siteModelRendered("notes")) {
5
6
  return new Response("Not found", { status: 404 });
6
7
  }
7
- const notes = await getPublishedNotes();
8
+ const notes = isPublishedRuntimeContentMode() ? (await loadPublishedCollection(context.locals, "notes")).sort((a, b) => b.data.date.valueOf() - a.data.date.valueOf()) : await getPublishedNotes();
8
9
  const origin = context.site?.origin ?? "https://treeseed.dev";
9
10
  const items = notes.map(
10
11
  (note) => `