@treeseed/core 0.1.2 → 0.3.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.
@@ -1,282 +1 @@
1
- import { parse as parseYaml } from 'yaml';
2
-
3
- function isRecord(value) {
4
- return typeof value === 'object' && value !== null && !Array.isArray(value);
5
- }
6
-
7
- function expectRecord(value, path) {
8
- if (!isRecord(value)) {
9
- throw new Error(`Expected ${path} to be an object.`);
10
- }
11
-
12
- return value;
13
- }
14
-
15
- function expectString(value, path) {
16
- if (typeof value !== 'string' || value.trim().length === 0) {
17
- throw new Error(`Expected ${path} to be a non-empty string.`);
18
- }
19
-
20
- return value.trim();
21
- }
22
-
23
- function optionalString(value, path) {
24
- if (value === undefined || value === null || value === '') {
25
- return undefined;
26
- }
27
-
28
- return expectString(value, path);
29
- }
30
-
31
- function optionalBoolean(value, path) {
32
- if (value === undefined || value === null) {
33
- return undefined;
34
- }
35
-
36
- if (typeof value !== 'boolean') {
37
- throw new Error(`Expected ${path} to be a boolean.`);
38
- }
39
-
40
- return value;
41
- }
42
-
43
- function optionalRecord(value, path) {
44
- if (value === undefined || value === null) {
45
- return undefined;
46
- }
47
-
48
- return expectRecord(value, path);
49
- }
50
-
51
- function stringArray(value, path) {
52
- if (value === undefined || value === null) {
53
- return [];
54
- }
55
-
56
- if (!Array.isArray(value)) {
57
- throw new Error(`Expected ${path} to be an array.`);
58
- }
59
-
60
- return value.map((entry, index) => expectString(entry, `${path}[${index}]`));
61
- }
62
-
63
- function parseMenuGroups(value, path) {
64
- if (!Array.isArray(value)) {
65
- throw new Error(`Expected ${path} to be an array.`);
66
- }
67
-
68
- return value.map((group, groupIndex) => {
69
- const parsedGroup = expectRecord(group, `${path}[${groupIndex}]`);
70
- const items = parsedGroup.items;
71
- if (!Array.isArray(items) || items.length === 0) {
72
- throw new Error(`Expected ${path}[${groupIndex}].items to contain at least one menu item.`);
73
- }
74
-
75
- return {
76
- label: expectString(parsedGroup.label, `${path}[${groupIndex}].label`),
77
- items: items.map((item, itemIndex) => {
78
- const parsedItem = expectRecord(item, `${path}[${groupIndex}].items[${itemIndex}]`);
79
- return {
80
- label: expectString(parsedItem.label, `${path}[${groupIndex}].items[${itemIndex}].label`),
81
- href: expectString(parsedItem.href, `${path}[${groupIndex}].items[${itemIndex}].href`),
82
- };
83
- }),
84
- };
85
- });
86
- }
87
-
88
- function parseContactRouting(value, path) {
89
- const parsedValue = expectRecord(value ?? {}, path);
90
- const keys = ['default', 'question', 'feedback', 'collaboration', 'issue'];
91
-
92
- return Object.fromEntries(
93
- keys.flatMap((key) => {
94
- if (!(key in parsedValue)) {
95
- return [];
96
- }
97
-
98
- return [[key, stringArray(parsedValue[key], `${path}.${key}`)]];
99
- }),
100
- );
101
- }
102
-
103
- function parseTheme(value, path) {
104
- const theme = optionalRecord(value, path);
105
- if (!theme) {
106
- return undefined;
107
- }
108
-
109
- const surfaces = optionalRecord(theme.surfaces, `${path}.surfaces`);
110
- const text = optionalRecord(theme.text, `${path}.text`);
111
- const border = optionalRecord(theme.border, `${path}.border`);
112
- const accent = optionalRecord(theme.accent, `${path}.accent`);
113
- const info = optionalRecord(theme.info, `${path}.info`);
114
- const warm = optionalRecord(theme.warm, `${path}.warm`);
115
-
116
- return {
117
- surfaces: surfaces
118
- ? {
119
- background: optionalString(surfaces.background, `${path}.surfaces.background`),
120
- backgroundElevated: optionalString(
121
- surfaces.backgroundElevated,
122
- `${path}.surfaces.backgroundElevated`,
123
- ),
124
- backgroundSoft: optionalString(surfaces.backgroundSoft, `${path}.surfaces.backgroundSoft`),
125
- panel: optionalString(surfaces.panel, `${path}.surfaces.panel`),
126
- panelStrong: optionalString(surfaces.panelStrong, `${path}.surfaces.panelStrong`),
127
- }
128
- : undefined,
129
- text: text
130
- ? {
131
- body: optionalString(text.body, `${path}.text.body`),
132
- muted: optionalString(text.muted, `${path}.text.muted`),
133
- soft: optionalString(text.soft, `${path}.text.soft`),
134
- }
135
- : undefined,
136
- border: border
137
- ? {
138
- base: optionalString(border.base, `${path}.border.base`),
139
- strong: optionalString(border.strong, `${path}.border.strong`),
140
- grid: optionalString(border.grid, `${path}.border.grid`),
141
- }
142
- : undefined,
143
- accent: accent
144
- ? {
145
- base: optionalString(accent.base, `${path}.accent.base`),
146
- strong: optionalString(accent.strong, `${path}.accent.strong`),
147
- soft: optionalString(accent.soft, `${path}.accent.soft`),
148
- }
149
- : undefined,
150
- info: info
151
- ? {
152
- base: optionalString(info.base, `${path}.info.base`),
153
- strong: optionalString(info.strong, `${path}.info.strong`),
154
- soft: optionalString(info.soft, `${path}.info.soft`),
155
- }
156
- : undefined,
157
- warm: warm
158
- ? {
159
- base: optionalString(warm.base, `${path}.warm.base`),
160
- strong: optionalString(warm.strong, `${path}.warm.strong`),
161
- }
162
- : undefined,
163
- };
164
- }
165
-
166
- /**
167
- * @param {string} source
168
- */
169
- export function parseSiteConfig(source) {
170
- const parsed = expectRecord(parseYaml(source), 'config');
171
- const site = expectRecord(parsed.site, 'site');
172
- const models = expectRecord(parsed.models ?? {}, 'models');
173
- const pageModel = expectRecord(models.pages ?? {}, 'models.pages');
174
- const noteModel = expectRecord(models.notes ?? {}, 'models.notes');
175
- const questionModel = expectRecord(models.questions ?? {}, 'models.questions');
176
- const objectiveModel = expectRecord(models.objectives ?? {}, 'models.objectives');
177
- const peopleModel = expectRecord(models.people ?? {}, 'models.people');
178
- const agentModel = expectRecord(models.agents ?? {}, 'models.agents');
179
- const bookModel = expectRecord(models.books ?? {}, 'models.books');
180
- const docsModel = expectRecord(models.docs ?? {}, 'models.docs');
181
- const pageDefaults = expectRecord(pageModel.defaults ?? {}, 'models.pages.defaults');
182
- const noteDefaults = expectRecord(noteModel.defaults ?? {}, 'models.notes.defaults');
183
- const questionDefaults = expectRecord(questionModel.defaults ?? {}, 'models.questions.defaults');
184
- const objectiveDefaults = expectRecord(objectiveModel.defaults ?? {}, 'models.objectives.defaults');
185
- const peopleDefaults = expectRecord(peopleModel.defaults ?? {}, 'models.people.defaults');
186
- const agentDefaults = expectRecord(agentModel.defaults ?? {}, 'models.agents.defaults');
187
- const bookDefaults = expectRecord(bookModel.defaults ?? {}, 'models.books.defaults');
188
- const docsDefaults = expectRecord(docsModel.defaults ?? {}, 'models.docs.defaults');
189
- const logo = expectRecord(site.logo, 'site.logo');
190
- const forms = expectRecord(site.forms ?? {}, 'site.forms');
191
- const emailNotifications = expectRecord(site.emailNotifications, 'site.emailNotifications');
192
-
193
- return {
194
- site: {
195
- logo: {
196
- src: expectString(logo.src, 'site.logo.src'),
197
- alt: expectString(logo.alt, 'site.logo.alt'),
198
- },
199
- name: expectString(site.name, 'site.name'),
200
- statement: expectString(site.statement, 'site.statement'),
201
- siteUrl: expectString(site.siteUrl, 'site.siteUrl'),
202
- githubRepository: expectString(site.githubRepository, 'site.githubRepository'),
203
- discordLink: expectString(site.discordLink, 'site.discordLink'),
204
- headerMenu: parseMenuGroups(site.headerMenu, 'site.headerMenu'),
205
- footerMenu: parseMenuGroups(site.footerMenu, 'site.footerMenu'),
206
- forms: {
207
- apiBaseUrl: optionalString(forms.apiBaseUrl, 'site.forms.apiBaseUrl'),
208
- },
209
- emailNotifications: {
210
- contactRouting: parseContactRouting(
211
- emailNotifications.contactRouting,
212
- 'site.emailNotifications.contactRouting',
213
- ),
214
- subscribeRecipients: stringArray(
215
- emailNotifications.subscribeRecipients,
216
- 'site.emailNotifications.subscribeRecipients',
217
- ),
218
- },
219
- summary: expectString(site.summary, 'site.summary'),
220
- projectStage: expectString(site.projectStage, 'site.projectStage'),
221
- projectStageDetail: expectString(site.projectStageDetail, 'site.projectStageDetail'),
222
- theme: parseTheme(site.theme, 'site.theme'),
223
- },
224
- models: {
225
- pages: {
226
- defaults: {
227
- pageLayout: optionalString(pageDefaults.pageLayout, 'models.pages.defaults.pageLayout'),
228
- status: optionalString(pageDefaults.status, 'models.pages.defaults.status'),
229
- stage: optionalString(pageDefaults.stage, 'models.pages.defaults.stage'),
230
- audience: stringArray(pageDefaults.audience, 'models.pages.defaults.audience'),
231
- },
232
- },
233
- notes: {
234
- defaults: {
235
- author: optionalString(noteDefaults.author, 'models.notes.defaults.author'),
236
- draft: optionalBoolean(noteDefaults.draft, 'models.notes.defaults.draft'),
237
- tags: stringArray(noteDefaults.tags, 'models.notes.defaults.tags'),
238
- status: optionalString(noteDefaults.status, 'models.notes.defaults.status'),
239
- },
240
- },
241
- questions: {
242
- defaults: {
243
- draft: optionalBoolean(questionDefaults.draft, 'models.questions.defaults.draft'),
244
- tags: stringArray(questionDefaults.tags, 'models.questions.defaults.tags'),
245
- status: optionalString(questionDefaults.status, 'models.questions.defaults.status'),
246
- },
247
- },
248
- objectives: {
249
- defaults: {
250
- draft: optionalBoolean(objectiveDefaults.draft, 'models.objectives.defaults.draft'),
251
- tags: stringArray(objectiveDefaults.tags, 'models.objectives.defaults.tags'),
252
- status: optionalString(objectiveDefaults.status, 'models.objectives.defaults.status'),
253
- },
254
- },
255
- people: {
256
- defaults: {
257
- status: optionalString(peopleDefaults.status, 'models.people.defaults.status'),
258
- tags: stringArray(peopleDefaults.tags, 'models.people.defaults.tags'),
259
- },
260
- },
261
- agents: {
262
- defaults: {
263
- tags: stringArray(agentDefaults.tags, 'models.agents.defaults.tags'),
264
- runtimeStatus: optionalString(
265
- agentDefaults.runtimeStatus,
266
- 'models.agents.defaults.runtimeStatus',
267
- ),
268
- },
269
- },
270
- books: {
271
- defaults: {
272
- tags: stringArray(bookDefaults.tags, 'models.books.defaults.tags'),
273
- },
274
- },
275
- docs: {
276
- defaults: {
277
- tags: stringArray(docsDefaults.tags, 'models.docs.defaults.tags'),
278
- },
279
- },
280
- },
281
- };
282
- }
1
+ export { parseSiteConfig } from '@treeseed/sdk/platform/site-config-schema';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@treeseed/core",
3
- "version": "0.1.2",
3
+ "version": "0.3.0",
4
4
  "description": "Treeseed platform runtime package for Astro, Starlight, forms, books, plugins, and shared platform behavior.",
5
5
  "license": "AGPL-3.0-only",
6
6
  "repository": {
@@ -14,7 +14,7 @@
14
14
  },
15
15
  "type": "module",
16
16
  "engines": {
17
- "node": ">=20"
17
+ "node": ">=22"
18
18
  },
19
19
  "types": "./dist/index.d.ts",
20
20
  "files": [
@@ -27,12 +27,14 @@
27
27
  ],
28
28
  "publishConfig": {
29
29
  "access": "public"
30
+ },
31
+ "bin": {
32
+ "treeseed-workspace-bootstrap": "./dist/scripts/workspace-bootstrap.js"
30
33
  },
31
34
  "scripts": {
32
35
  "setup": "npm install",
33
36
  "setup:ci": "npm ci",
34
37
  "build:dist": "node ./scripts/run-ts.mjs ./scripts/build-dist.ts",
35
- "prepare": "npm run build:dist",
36
38
  "prepack": "npm run build:dist",
37
39
  "starlight:patch": "node ./scripts/run-ts.mjs ./scripts/patch-starlight-content-path.ts",
38
40
  "precheck": "npm run starlight:patch",
@@ -42,7 +44,9 @@
42
44
  "preview": "node ./scripts/run-ts.mjs ./scripts/run-fixture-astro-command.ts preview",
43
45
  "test:unit": "vitest run --config ./vitest.config.ts",
44
46
  "test": "npm run test:unit",
45
- "verify": "npm run release:verify",
47
+ "lint": "npm run fixtures:check && npm run starlight:patch && npm run build:dist",
48
+ "verify:direct": "npm run release:verify",
49
+ "verify": "node --input-type=module -e \"await import('@treeseed/sdk/scripts/verify-driver')\"",
46
50
  "test:smoke": "node ./scripts/run-ts.mjs ./scripts/test-smoke.ts",
47
51
  "fixtures:resolve": "node ./scripts/run-ts.mjs ./scripts/fixture-tools.ts resolve",
48
52
  "fixtures:check": "node ./scripts/run-ts.mjs ./scripts/fixture-tools.ts check",
@@ -52,7 +56,7 @@
52
56
  "release:publish": "node ./scripts/run-ts.mjs ./scripts/publish-package.ts"
53
57
  },
54
58
  "dependencies": {
55
- "@treeseed/sdk": "^0.1.1",
59
+ "@treeseed/sdk": "^0.3.0",
56
60
  "@astrojs/check": "^0.9.8",
57
61
  "@astrojs/cloudflare": "^12.6.13",
58
62
  "@astrojs/sitemap": "3.7.0",
@@ -129,6 +133,7 @@
129
133
  "types": "./dist/environment.d.ts",
130
134
  "default": "./dist/environment.js"
131
135
  },
136
+ "./scripts/workspace-bootstrap": "./dist/scripts/workspace-bootstrap.js",
132
137
  "./site-resources": {
133
138
  "types": "./dist/site-resources.d.ts",
134
139
  "default": "./dist/site-resources.js"
@@ -33,7 +33,7 @@ __WORKING_DIRECTORY_BLOCK__ environment: ${{ github.ref_name == 'main' && 'pr
33
33
  - name: Setup Node
34
34
  uses: actions/setup-node@v4
35
35
  with:
36
- node-version: 20
36
+ node-version: 22
37
37
  cache: npm
38
38
  cache-dependency-path: __CACHE_DEPENDENCY_PATH__
39
39
 
@@ -1,21 +0,0 @@
1
- declare const _default: {
2
- id: string;
3
- provides: {
4
- forms: string[];
5
- agents: {
6
- execution: string[];
7
- mutation: string[];
8
- repository: string[];
9
- verification: string[];
10
- notification: string[];
11
- research: string[];
12
- handlers: string[];
13
- };
14
- deploy: string[];
15
- content: {
16
- docs: string[];
17
- };
18
- site: string[];
19
- };
20
- };
21
- export default _default;
@@ -1,9 +0,0 @@
1
- import type { TreeseedTenantConfig } from '../contracts';
2
- declare function resolveTenantRoot(): string;
3
- export declare function defineTreeseedTenant<T>(tenantConfig: T): T;
4
- export declare function loadTreeseedManifest(manifestPath?: string): TreeseedTenantConfig;
5
- export declare const loadTreeseedTenantManifest: typeof loadTreeseedManifest;
6
- export declare const resolveTreeseedTenantRoot: typeof resolveTenantRoot;
7
- export declare function getTenantContentRoot(tenantConfig: Pick<TreeseedTenantConfig, 'content'>, collectionName: string): string;
8
- export declare function tenantFeatureEnabled(tenantConfig: Pick<TreeseedTenantConfig, 'features'>, featureName: string): boolean;
9
- export {};