@treeseed/core 0.4.7 → 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.
@@ -1,112 +1,17 @@
1
- import fs from 'node:fs';
2
1
  import path from 'node:path';
3
- import { BOOKS, TREESEED_LIBRARY_DOWNLOAD } from '@treeseed/sdk/platform/books-data';
2
+ import { exportTenantBookPackages } from '@treeseed/sdk/platform/book-export';
4
3
  import { PROJECT_TENANT } from '../tenant/bridge.js';
5
- const projectRoot = PROJECT_TENANT.__tenantRoot ?? process.cwd();
6
- const outputDir = path.join(projectRoot, 'public', 'books');
7
- const legacyOutputFile = path.join(projectRoot, 'public', 'book.md');
8
- function sortPaths(paths) {
9
- return [...paths].sort((left, right) => left.localeCompare(right, undefined, { numeric: true, sensitivity: 'base' }));
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 resolveNodeModulesRoot() {
28
- let lastCandidate = null;
29
- let current = packageRoot;
30
- while (true) {
31
- const candidate = resolve(current, 'node_modules');
32
- try {
33
- readdirSync(candidate);
34
- lastCandidate = candidate;
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
- catch {
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
- break;
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 sharedNodeModules = resolveNodeModulesRoot();
50
- for (const entry of readdirSync(sharedNodeModules, { withFileTypes: true })) {
51
- if (entry.name === '.bin' || entry.name === '@treeseed') {
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(resolve(sharedNodeModules, entry.name), targetPath, 'dir');
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.7",
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.6",
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",