@treeseed/core 0.4.8 → 0.4.9
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/scripts/aggregate-book.js +13 -108
- package/dist/scripts/test-smoke.js +80 -23
- package/package.json +2 -2
|
@@ -1,112 +1,17 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
1
|
import path from 'node:path';
|
|
3
|
-
import {
|
|
2
|
+
import { exportTenantBookPackages } from '@treeseed/sdk/platform/book-export';
|
|
4
3
|
import { PROJECT_TENANT } from '../tenant/bridge.js';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
}
|
|
11
|
-
function getSidebarOrder(filePath) {
|
|
12
|
-
const rawContent = fs.readFileSync(filePath, 'utf8');
|
|
13
|
-
const frontmatterMatch = rawContent.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
14
|
-
if (!frontmatterMatch)
|
|
15
|
-
return Number.POSITIVE_INFINITY;
|
|
16
|
-
const orderMatch = frontmatterMatch[1].match(/^\s{2}order:\s*(\d+)\s*$/m);
|
|
17
|
-
return orderMatch ? Number.parseInt(orderMatch[1], 10) : Number.POSITIVE_INFINITY;
|
|
18
|
-
}
|
|
19
|
-
function collectMarkdownFiles(rootPath) {
|
|
20
|
-
if (!fs.existsSync(rootPath)) {
|
|
21
|
-
throw new Error(`Book export root not found: ${rootPath}`);
|
|
22
|
-
}
|
|
23
|
-
const stats = fs.statSync(rootPath);
|
|
24
|
-
if (stats.isFile()) {
|
|
25
|
-
return [rootPath];
|
|
26
|
-
}
|
|
27
|
-
const entries = fs.readdirSync(rootPath, { withFileTypes: true });
|
|
28
|
-
return sortPaths(entries.flatMap((entry) => {
|
|
29
|
-
const fullPath = path.join(rootPath, entry.name);
|
|
30
|
-
if (entry.isDirectory()) {
|
|
31
|
-
return collectMarkdownFiles(fullPath);
|
|
32
|
-
}
|
|
33
|
-
if (entry.isFile() && (entry.name.endsWith('.md') || entry.name.endsWith('.mdx'))) {
|
|
34
|
-
return [fullPath];
|
|
35
|
-
}
|
|
36
|
-
return [];
|
|
37
|
-
}));
|
|
38
|
-
}
|
|
39
|
-
function stripFrontmatter(content) {
|
|
40
|
-
return content.replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/, '').trim();
|
|
41
|
-
}
|
|
42
|
-
function stripMdxOnlySyntax(content) {
|
|
43
|
-
return content
|
|
44
|
-
.replace(/^import\s.+$/gm, '')
|
|
45
|
-
.replace(/^\s*<\/?[A-Z][^>]*>\s*$/gm, '')
|
|
46
|
-
.replace(/\n{3,}/g, '\n\n')
|
|
47
|
-
.trim();
|
|
48
|
-
}
|
|
49
|
-
function inferExportRootFromBasePath(book) {
|
|
50
|
-
const normalizedBasePath = String(book.basePath || '').trim();
|
|
51
|
-
const knowledgePrefix = '/knowledge/';
|
|
52
|
-
if (!normalizedBasePath.startsWith(knowledgePrefix)) {
|
|
53
|
-
throw new Error(`Book basePath must start with "${knowledgePrefix}" to infer exports: ${book.basePath}`);
|
|
54
|
-
}
|
|
55
|
-
const relativeKnowledgePath = normalizedBasePath
|
|
56
|
-
.slice(knowledgePrefix.length)
|
|
57
|
-
.replace(/^\/+|\/+$/g, '');
|
|
58
|
-
if (!relativeKnowledgePath) {
|
|
59
|
-
throw new Error(`Book basePath must identify a knowledge directory: ${book.basePath}`);
|
|
60
|
-
}
|
|
61
|
-
return path.join(PROJECT_TENANT.content.docs, relativeKnowledgePath);
|
|
62
|
-
}
|
|
63
|
-
function resolveExportRoots(book) {
|
|
64
|
-
if (Array.isArray(book.exportRoots) && book.exportRoots.length > 0) {
|
|
65
|
-
return book.exportRoots;
|
|
66
|
-
}
|
|
67
|
-
return [inferExportRootFromBasePath(book)];
|
|
68
|
-
}
|
|
69
|
-
function resolveBookFiles(book) {
|
|
70
|
-
const files = resolveExportRoots(book).flatMap((root) => collectMarkdownFiles(path.resolve(projectRoot, root)).sort((left, right) => {
|
|
71
|
-
const orderDelta = getSidebarOrder(left) - getSidebarOrder(right);
|
|
72
|
-
if (orderDelta !== 0)
|
|
73
|
-
return orderDelta;
|
|
74
|
-
return left.localeCompare(right, undefined, { numeric: true, sensitivity: 'base' });
|
|
75
|
-
}));
|
|
76
|
-
return Array.from(new Set(files));
|
|
77
|
-
}
|
|
78
|
-
function buildBookMarkdown(book) {
|
|
79
|
-
const sections = resolveBookFiles(book).map((filePath) => {
|
|
80
|
-
const rawContent = fs.readFileSync(filePath, 'utf8');
|
|
81
|
-
return stripMdxOnlySyntax(stripFrontmatter(rawContent));
|
|
82
|
-
});
|
|
83
|
-
return `# ${book.downloadTitle}\n\n> This document is auto-generated from the TreeSeed knowledge source.\n\n${sections.join('\n\n---\n\n')}\n`;
|
|
84
|
-
}
|
|
85
|
-
function ensureOutputDir() {
|
|
86
|
-
fs.mkdirSync(outputDir, { recursive: true });
|
|
87
|
-
}
|
|
88
|
-
function writeBookOutput(fileName, content) {
|
|
89
|
-
const outputPath = path.join(outputDir, fileName);
|
|
90
|
-
fs.writeFileSync(outputPath, content);
|
|
91
|
-
return outputPath;
|
|
92
|
-
}
|
|
93
|
-
function main() {
|
|
94
|
-
console.log('Generating TreeSeed knowledge exports...');
|
|
95
|
-
ensureOutputDir();
|
|
96
|
-
const bookOutputs = BOOKS.map((book) => {
|
|
97
|
-
const content = buildBookMarkdown(book);
|
|
98
|
-
const outputPath = writeBookOutput(book.downloadFileName, content);
|
|
99
|
-
console.log(`Generated ${path.relative(projectRoot, outputPath)}`);
|
|
100
|
-
return { book, content };
|
|
101
|
-
});
|
|
102
|
-
const compositeContent = `# ${TREESEED_LIBRARY_DOWNLOAD.downloadTitle}\n\n> This document is auto-generated from the TreeSeed knowledge source.\n\n${bookOutputs
|
|
103
|
-
.map(({ content }) => content.trim())
|
|
104
|
-
.join('\n\n---\n\n')}\n`;
|
|
105
|
-
const compositeOutputPath = writeBookOutput(TREESEED_LIBRARY_DOWNLOAD.downloadFileName, compositeContent);
|
|
106
|
-
console.log(`Generated ${path.relative(projectRoot, compositeOutputPath)}`);
|
|
107
|
-
if (fs.existsSync(legacyOutputFile)) {
|
|
108
|
-
fs.rmSync(legacyOutputFile);
|
|
109
|
-
console.log(`Removed legacy export ${path.relative(projectRoot, legacyOutputFile)}`);
|
|
4
|
+
async function main() {
|
|
5
|
+
console.log('Generating Treeseed AI book packages...');
|
|
6
|
+
const result = await exportTenantBookPackages({ projectRoot: PROJECT_TENANT.__tenantRoot ?? process.cwd() });
|
|
7
|
+
for (const entry of result.bookPackages) {
|
|
8
|
+
console.log(`Generated ${path.relative(result.projectRoot, entry.markdownPath)}`);
|
|
9
|
+
console.log(`Generated ${path.relative(result.projectRoot, entry.indexPath)}`);
|
|
110
10
|
}
|
|
11
|
+
console.log(`Generated ${path.relative(result.projectRoot, result.libraryPackage.markdownPath)}`);
|
|
12
|
+
console.log(`Generated ${path.relative(result.projectRoot, result.libraryPackage.indexPath)}`);
|
|
111
13
|
}
|
|
112
|
-
main()
|
|
14
|
+
main().catch((error) => {
|
|
15
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
16
|
+
process.exitCode = 1;
|
|
17
|
+
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readdirSync, rmSync, symlinkSync, writeFileSync } from 'node:fs';
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, symlinkSync, writeFileSync } from 'node:fs';
|
|
2
2
|
import { mkdtempSync } from 'node:fs';
|
|
3
3
|
import { tmpdir } from 'node:os';
|
|
4
4
|
import { dirname, join, resolve } from 'node:path';
|
|
@@ -24,36 +24,92 @@ function run(command, args, cwd = packageRoot, capture = false) {
|
|
|
24
24
|
}
|
|
25
25
|
return (result.stdout ?? '').trim();
|
|
26
26
|
}
|
|
27
|
-
function
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
27
|
+
function runtimeDependencyNamesFor(root) {
|
|
28
|
+
const packageJson = JSON.parse(readFileSync(resolve(root, 'package.json'), 'utf8'));
|
|
29
|
+
return Object.keys(packageJson.dependencies ?? {});
|
|
30
|
+
}
|
|
31
|
+
function resolveWorkspaceRuntimePackageRoots() {
|
|
32
|
+
const roots = new Map();
|
|
33
|
+
for (const dependencyName of runtimeDependencyNamesFor(packageRoot)) {
|
|
34
|
+
const dependencyRoot = resolveTreeseedRuntimeDependencyRoot(dependencyName);
|
|
35
|
+
if (dependencyRoot) {
|
|
36
|
+
roots.set(dependencyName, dependencyRoot);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return roots;
|
|
40
|
+
}
|
|
41
|
+
function collectRuntimeDependenciesForPackaging() {
|
|
42
|
+
const dependencyNames = new Set(runtimeDependencyNamesFor(packageRoot));
|
|
43
|
+
const searchRoots = [
|
|
44
|
+
packageRoot,
|
|
45
|
+
sdkPackageRoot,
|
|
46
|
+
];
|
|
47
|
+
const queue = [...resolveWorkspaceRuntimePackageRoots().values()];
|
|
48
|
+
const visited = new Set();
|
|
49
|
+
while (queue.length > 0) {
|
|
50
|
+
const nextRoot = queue.shift();
|
|
51
|
+
if (!nextRoot || visited.has(nextRoot)) {
|
|
52
|
+
continue;
|
|
35
53
|
}
|
|
36
|
-
|
|
54
|
+
visited.add(nextRoot);
|
|
55
|
+
for (const dependencyName of runtimeDependencyNamesFor(nextRoot)) {
|
|
56
|
+
dependencyNames.add(dependencyName);
|
|
57
|
+
const dependencyRoot = resolveTreeseedRuntimeDependencyRoot(dependencyName, searchRoots);
|
|
58
|
+
if (dependencyRoot) {
|
|
59
|
+
queue.push(dependencyRoot);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return dependencyNames;
|
|
64
|
+
}
|
|
65
|
+
function resolveTreeseedRuntimeDependencyRoot(packageName, searchRoots = [packageRoot, sdkPackageRoot]) {
|
|
66
|
+
if (!packageName.startsWith('@treeseed/')) {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
const folderName = packageName.slice('@treeseed/'.length);
|
|
70
|
+
const workspaceCandidateRoot = resolve(packageRoot, '..', folderName);
|
|
71
|
+
if (existsSync(resolve(workspaceCandidateRoot, 'package.json'))) {
|
|
72
|
+
return workspaceCandidateRoot;
|
|
73
|
+
}
|
|
74
|
+
try {
|
|
75
|
+
return resolveInstalledPackageRoot(packageName, searchRoots);
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function resolveInstalledPackageRoot(packageName, searchRoots) {
|
|
82
|
+
let resolvedEntry = require.resolve(packageName, { paths: searchRoots });
|
|
83
|
+
let current = dirname(resolvedEntry);
|
|
84
|
+
while (true) {
|
|
85
|
+
const packageJsonPath = resolve(current, 'package.json');
|
|
86
|
+
if (existsSync(packageJsonPath)) {
|
|
87
|
+
return current;
|
|
37
88
|
}
|
|
38
89
|
const parent = resolve(current, '..');
|
|
39
|
-
if (parent === current)
|
|
40
|
-
|
|
90
|
+
if (parent === current) {
|
|
91
|
+
throw new Error(`Unable to resolve installed package root for ${packageName}.`);
|
|
92
|
+
}
|
|
41
93
|
current = parent;
|
|
42
94
|
}
|
|
43
|
-
if (lastCandidate) {
|
|
44
|
-
return lastCandidate;
|
|
45
|
-
}
|
|
46
|
-
throw new Error(`Unable to locate node_modules for ${packageRoot}.`);
|
|
47
95
|
}
|
|
48
|
-
function mirrorDependencies(tempRoot) {
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
96
|
+
function mirrorDependencies(tempRoot, excludedPackages = new Set()) {
|
|
97
|
+
const runtimeDependencies = collectRuntimeDependenciesForPackaging();
|
|
98
|
+
const searchRoots = [
|
|
99
|
+
packageRoot,
|
|
100
|
+
...resolveWorkspaceRuntimePackageRoots().values(),
|
|
101
|
+
];
|
|
102
|
+
for (const packageName of runtimeDependencies) {
|
|
103
|
+
if (excludedPackages.has(packageName) || packageName === '@treeseed/core') {
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
const sourcePath = resolveInstalledPackageRoot(packageName, searchRoots);
|
|
107
|
+
const targetPath = resolve(tempRoot, 'node_modules', ...packageName.split('/'));
|
|
108
|
+
if (existsSync(targetPath)) {
|
|
52
109
|
continue;
|
|
53
110
|
}
|
|
54
|
-
const targetPath = resolve(tempRoot, 'node_modules', entry.name);
|
|
55
111
|
mkdirSync(dirname(targetPath), { recursive: true });
|
|
56
|
-
symlinkSync(
|
|
112
|
+
symlinkSync(sourcePath, targetPath, 'dir');
|
|
57
113
|
}
|
|
58
114
|
}
|
|
59
115
|
function pack(root, outputRoot, fallbackName) {
|
|
@@ -98,6 +154,7 @@ try {
|
|
|
98
154
|
mkdirSync(packRoot, { recursive: true });
|
|
99
155
|
mkdirSync(extractRoot, { recursive: true });
|
|
100
156
|
const coreTarball = pack(packageRoot, packRoot, 'treeseed-core.tgz');
|
|
157
|
+
const workspaceRuntimePackageRoots = resolveWorkspaceRuntimePackageRoots();
|
|
101
158
|
if (existsSync(resolve(sdkPackageRoot, 'scripts', 'run-ts.mjs'))) {
|
|
102
159
|
const sdkTarball = pack(sdkPackageRoot, packRoot, 'treeseed-sdk.tgz');
|
|
103
160
|
installPackagedPackage(extractRoot, installRoot, sdkTarball, 'sdk');
|
|
@@ -106,7 +163,7 @@ try {
|
|
|
106
163
|
installPackageDirectory(installRoot, sdkPackageRoot, 'sdk');
|
|
107
164
|
}
|
|
108
165
|
installPackagedPackage(extractRoot, installRoot, coreTarball, 'core');
|
|
109
|
-
mirrorDependencies(installRoot);
|
|
166
|
+
mirrorDependencies(installRoot, new Set(workspaceRuntimePackageRoots.keys()));
|
|
110
167
|
writeFileSync(resolve(installRoot, 'package.json'), `${JSON.stringify({ name: 'treeseed-core-smoke', private: true, type: 'module' }, null, 2)}\n`, 'utf8');
|
|
111
168
|
run(process.execPath, [
|
|
112
169
|
'--input-type=module',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@treeseed/core",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.9",
|
|
4
4
|
"description": "Treeseed integrated platform starter for Astro/Starlight web runtimes and Hono API runtimes.",
|
|
5
5
|
"license": "AGPL-3.0-only",
|
|
6
6
|
"repository": {
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
"release:publish": "node ./scripts/run-ts.mjs ./scripts/publish-package.ts"
|
|
68
68
|
},
|
|
69
69
|
"dependencies": {
|
|
70
|
-
"@treeseed/sdk": "^0.4.
|
|
70
|
+
"@treeseed/sdk": "^0.4.8",
|
|
71
71
|
"@astrojs/check": "^0.9.8",
|
|
72
72
|
"@astrojs/cloudflare": "^12.6.13",
|
|
73
73
|
"@astrojs/sitemap": "3.7.0",
|