create-thally-docs 0.9.0 → 0.10.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/chunk-ENQIF6MA.js +164 -0
- package/dist/{chunk-YWNWLSZ2.js → chunk-GAFCLXU4.js} +1 -1
- package/dist/{chunk-JNDRCCJV.js → chunk-GTGHYJXS.js} +15 -87
- package/dist/chunk-KXJT2MG5.js +520 -0
- package/dist/chunk-ORUAMPNF.js +187 -0
- package/dist/chunk-WMHBVXWW.js +65 -0
- package/dist/customize.d.ts +23 -77
- package/dist/customize.js +9 -29
- package/dist/index.js +7 -5
- package/dist/migrate/index.js +6 -4
- package/dist/release.d.ts +31 -23
- package/dist/release.js +3 -1
- package/dist/scaffold.d.ts +25 -10
- package/dist/scaffold.js +15 -12
- package/dist/starter-sync.d.ts +68 -0
- package/dist/starter-sync.js +22 -0
- package/dist/starter-update.d.ts +43 -0
- package/dist/starter-update.js +155 -0
- package/package.json +10 -2
- package/dist/chunk-BW7J7DJV.js +0 -769
- package/dist/chunk-PJW4JJIT.js +0 -31
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
readStarterReleaseManifest
|
|
4
|
+
} from "./chunk-KXJT2MG5.js";
|
|
5
|
+
import {
|
|
6
|
+
STABLE_SCAFFOLD_RELEASE
|
|
7
|
+
} from "./chunk-WMHBVXWW.js";
|
|
8
|
+
|
|
9
|
+
// src/download.ts
|
|
10
|
+
import { Readable, Transform, pipeline } from "stream";
|
|
11
|
+
import { promisify } from "util";
|
|
12
|
+
import {
|
|
13
|
+
cpSync,
|
|
14
|
+
lstatSync,
|
|
15
|
+
mkdtempSync,
|
|
16
|
+
readdirSync,
|
|
17
|
+
renameSync,
|
|
18
|
+
rmdirSync,
|
|
19
|
+
rmSync
|
|
20
|
+
} from "fs";
|
|
21
|
+
import { tmpdir } from "os";
|
|
22
|
+
import { dirname, join } from "path";
|
|
23
|
+
import { extract } from "tar";
|
|
24
|
+
var pipelineAsync = promisify(pipeline);
|
|
25
|
+
var STARTER_FETCH_TIMEOUT_MS = 15e3;
|
|
26
|
+
var MAX_COMPRESSED_ARCHIVE_BYTES = 64 * 1024 * 1024;
|
|
27
|
+
var MAX_EXTRACTED_ARCHIVE_BYTES = 256 * 1024 * 1024;
|
|
28
|
+
var MAX_ARCHIVE_FILE_BYTES = 32 * 1024 * 1024;
|
|
29
|
+
var MAX_ARCHIVE_ENTRIES = 1e4;
|
|
30
|
+
var STARTER_REPOSITORY = STABLE_SCAFFOLD_RELEASE.source.repository;
|
|
31
|
+
var STARTER_COMMIT_SHA = STABLE_SCAFFOLD_RELEASE.source.commitSha;
|
|
32
|
+
var STARTER_ARCHIVE_ROOT = `${STARTER_REPOSITORY.split("/").at(-1)}-${STARTER_COMMIT_SHA}`;
|
|
33
|
+
function starterArchiveRoot(release) {
|
|
34
|
+
return `${release.source.repository.split("/").at(-1)}-${release.source.commitSha}`;
|
|
35
|
+
}
|
|
36
|
+
function validateStarterArchiveEntry(archivePath, entry, archiveRoot = STARTER_ARCHIVE_ROOT) {
|
|
37
|
+
if (!archivePath || archivePath.includes("\0") || archivePath.includes("\\") || archivePath.startsWith("/")) {
|
|
38
|
+
throw new Error("The stable Thally starter archive contains an unsafe path.");
|
|
39
|
+
}
|
|
40
|
+
const pathWithoutTrailingSlash = archivePath.endsWith("/") ? archivePath.slice(0, -1) : archivePath;
|
|
41
|
+
const parts = pathWithoutTrailingSlash.split("/");
|
|
42
|
+
const [root, ...relativeParts] = parts;
|
|
43
|
+
if (root !== archiveRoot || relativeParts.some((part) => !part || part === "." || part === "..")) {
|
|
44
|
+
throw new Error("The stable Thally starter archive contains an unsafe path.");
|
|
45
|
+
}
|
|
46
|
+
if (relativeParts.length === 0 && entry.type !== "Directory") {
|
|
47
|
+
throw new Error("The stable Thally starter archive has an invalid root entry.");
|
|
48
|
+
}
|
|
49
|
+
if (!["File", "OldFile", "Directory"].includes(entry.type)) {
|
|
50
|
+
throw new Error(
|
|
51
|
+
`The stable Thally starter archive contains unsupported entry type ${entry.type}.`
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
if (!Number.isSafeInteger(entry.size) || entry.size < 0 || entry.size > MAX_ARCHIVE_FILE_BYTES) {
|
|
55
|
+
throw new Error("The stable Thally starter archive contains an oversized file.");
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function starterArchiveFilter(archiveRoot) {
|
|
59
|
+
let entryCount = 0;
|
|
60
|
+
let extractedBytes = 0;
|
|
61
|
+
return (path, entry) => {
|
|
62
|
+
if (!("type" in entry)) {
|
|
63
|
+
throw new Error("The starter archive filter requires extraction metadata.");
|
|
64
|
+
}
|
|
65
|
+
validateStarterArchiveEntry(path, entry, archiveRoot);
|
|
66
|
+
entryCount += 1;
|
|
67
|
+
extractedBytes += entry.size;
|
|
68
|
+
if (entryCount > MAX_ARCHIVE_ENTRIES || extractedBytes > MAX_EXTRACTED_ARCHIVE_BYTES) {
|
|
69
|
+
throw new Error("The stable Thally starter archive exceeds extraction limits.");
|
|
70
|
+
}
|
|
71
|
+
return true;
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
function compressedArchiveLimit() {
|
|
75
|
+
let compressedBytes = 0;
|
|
76
|
+
return new Transform({
|
|
77
|
+
transform(chunk, _encoding, callback) {
|
|
78
|
+
compressedBytes += chunk.length;
|
|
79
|
+
if (compressedBytes > MAX_COMPRESSED_ARCHIVE_BYTES) {
|
|
80
|
+
callback(new Error("The stable Thally starter archive is too large."));
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
callback(null, chunk);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
async function downloadStarter(targetDir, siteName, release = STABLE_SCAFFOLD_RELEASE, options = {}) {
|
|
88
|
+
if (options.announce !== false) {
|
|
89
|
+
console.log("");
|
|
90
|
+
console.log(` \u23F3 Creating ${siteName?.trim() || "your docs site"}...`);
|
|
91
|
+
}
|
|
92
|
+
const targetEntry = lstatSync(targetDir);
|
|
93
|
+
if (!targetEntry.isDirectory() || targetEntry.isSymbolicLink()) {
|
|
94
|
+
throw new Error("The Thally starter target must be a regular directory.");
|
|
95
|
+
}
|
|
96
|
+
if (readdirSync(targetDir).length > 0) {
|
|
97
|
+
throw new Error("The Thally starter target directory must be empty.");
|
|
98
|
+
}
|
|
99
|
+
const response = await fetch(release.source.archiveUrl, {
|
|
100
|
+
headers: { accept: "application/gzip, application/octet-stream;q=0.9" },
|
|
101
|
+
cache: "no-store",
|
|
102
|
+
signal: AbortSignal.timeout(STARTER_FETCH_TIMEOUT_MS)
|
|
103
|
+
});
|
|
104
|
+
if (!response.ok) {
|
|
105
|
+
throw new Error(
|
|
106
|
+
`Failed to download the stable Thally starter: ${response.status} ${response.statusText}`
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
if (!response.body) {
|
|
110
|
+
throw new Error("The stable Thally starter response body is empty.");
|
|
111
|
+
}
|
|
112
|
+
const contentLength = Number(response.headers.get("content-length"));
|
|
113
|
+
if (Number.isFinite(contentLength) && contentLength > MAX_COMPRESSED_ARCHIVE_BYTES) {
|
|
114
|
+
throw new Error("The stable Thally starter archive is too large.");
|
|
115
|
+
}
|
|
116
|
+
const nodeStream = Readable.fromWeb(
|
|
117
|
+
response.body
|
|
118
|
+
);
|
|
119
|
+
const stagingDir = mkdtempSync(join(tmpdir(), "thally-starter-download-"));
|
|
120
|
+
try {
|
|
121
|
+
await pipelineAsync(
|
|
122
|
+
nodeStream,
|
|
123
|
+
compressedArchiveLimit(),
|
|
124
|
+
extract({
|
|
125
|
+
cwd: stagingDir,
|
|
126
|
+
strip: 1,
|
|
127
|
+
filter: starterArchiveFilter(starterArchiveRoot(release)),
|
|
128
|
+
preservePaths: false,
|
|
129
|
+
strict: true,
|
|
130
|
+
unlink: true,
|
|
131
|
+
maxDecompressionRatio: 100
|
|
132
|
+
})
|
|
133
|
+
);
|
|
134
|
+
readStarterReleaseManifest(stagingDir, release);
|
|
135
|
+
const deliveryDir = mkdtempSync(
|
|
136
|
+
join(dirname(targetDir), ".thally-starter-delivery-")
|
|
137
|
+
);
|
|
138
|
+
try {
|
|
139
|
+
for (const entry of readdirSync(stagingDir)) {
|
|
140
|
+
cpSync(join(stagingDir, entry), join(deliveryDir, entry), {
|
|
141
|
+
recursive: true,
|
|
142
|
+
errorOnExist: true
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
if (readdirSync(targetDir).length > 0) {
|
|
146
|
+
throw new Error("The Thally starter target changed during download.");
|
|
147
|
+
}
|
|
148
|
+
rmdirSync(targetDir);
|
|
149
|
+
renameSync(deliveryDir, targetDir);
|
|
150
|
+
} finally {
|
|
151
|
+
rmSync(deliveryDir, { recursive: true, force: true });
|
|
152
|
+
}
|
|
153
|
+
} finally {
|
|
154
|
+
rmSync(stagingDir, { recursive: true, force: true });
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export {
|
|
159
|
+
STARTER_REPOSITORY,
|
|
160
|
+
STARTER_COMMIT_SHA,
|
|
161
|
+
STARTER_ARCHIVE_ROOT,
|
|
162
|
+
validateStarterArchiveEntry,
|
|
163
|
+
downloadStarter
|
|
164
|
+
};
|
|
@@ -1,83 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
patchPackageJson,
|
|
7
|
-
patchTopBarNavigation,
|
|
8
|
-
updateEnvExample,
|
|
9
|
-
updateSiteConfig,
|
|
10
|
-
writeCloudflareRuntimeConfig,
|
|
11
|
-
writeStarterAgentGuide,
|
|
12
|
-
writeStarterContent,
|
|
13
|
-
writeStarterReadme
|
|
14
|
-
} from "./chunk-BW7J7DJV.js";
|
|
3
|
+
personalizeStarter,
|
|
4
|
+
updateEnvExample
|
|
5
|
+
} from "./chunk-ORUAMPNF.js";
|
|
15
6
|
import {
|
|
16
|
-
|
|
17
|
-
} from "./chunk-
|
|
7
|
+
downloadStarter
|
|
8
|
+
} from "./chunk-ENQIF6MA.js";
|
|
18
9
|
|
|
19
10
|
// src/scaffold.ts
|
|
20
11
|
import { existsSync, mkdirSync, readdirSync } from "fs";
|
|
21
12
|
import { resolve } from "path";
|
|
22
13
|
|
|
23
|
-
// src/download.ts
|
|
24
|
-
import { Readable, pipeline } from "stream";
|
|
25
|
-
import { promisify } from "util";
|
|
26
|
-
import { extract } from "tar";
|
|
27
|
-
var pipelineAsync = promisify(pipeline);
|
|
28
|
-
var TEMPLATE_REPOSITORY = STABLE_SCAFFOLD_RELEASE.source.repository;
|
|
29
|
-
var TEMPLATE_COMMIT_SHA = STABLE_SCAFFOLD_RELEASE.source.commitSha;
|
|
30
|
-
var TARBALL_URL = STABLE_SCAFFOLD_RELEASE.source.archiveUrl;
|
|
31
|
-
var EXCLUDE_PATHS = [
|
|
32
|
-
// Match both the directory entry itself and every nested file. Tar invokes
|
|
33
|
-
// the filter for `.../node_modules` before descendants, without a trailing `/`.
|
|
34
|
-
"/node_modules",
|
|
35
|
-
// The canonical docs repository may temporarily retain package sources while
|
|
36
|
-
// runtime work is being upstreamed. A scaffold consumes the published
|
|
37
|
-
// packages declared in package.json; it must never inherit those sources.
|
|
38
|
-
"/packages",
|
|
39
|
-
"/.git/",
|
|
40
|
-
"/.next/",
|
|
41
|
-
"/.data/",
|
|
42
|
-
"/.thally/",
|
|
43
|
-
// The first-party docs deployment pins its own canonical origin.
|
|
44
|
-
"/.env.production",
|
|
45
|
-
"/thally-track.yml",
|
|
46
|
-
"/CODEOWNERS",
|
|
47
|
-
"/CLAUDE.md",
|
|
48
|
-
"/notes/",
|
|
49
|
-
"/public/images/",
|
|
50
|
-
"/src/public/",
|
|
51
|
-
"/snippets/",
|
|
52
|
-
"/.github/ISSUE_TEMPLATE/",
|
|
53
|
-
"/.github/PULL_REQUEST_TEMPLATE.md",
|
|
54
|
-
"/README.md"
|
|
55
|
-
];
|
|
56
|
-
function shouldInclude(path) {
|
|
57
|
-
for (const excluded of EXCLUDE_PATHS) {
|
|
58
|
-
if (path.includes(excluded)) {
|
|
59
|
-
return false;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
return true;
|
|
63
|
-
}
|
|
64
|
-
async function downloadTemplate(targetDir, siteName) {
|
|
65
|
-
console.log("");
|
|
66
|
-
console.log(` \u23F3 Creating ${siteName?.trim() || "your docs site"}...`);
|
|
67
|
-
const response = await fetch(TARBALL_URL);
|
|
68
|
-
if (!response.ok) {
|
|
69
|
-
throw new Error(`Failed to download template: ${response.status} ${response.statusText}`);
|
|
70
|
-
}
|
|
71
|
-
if (!response.body) {
|
|
72
|
-
throw new Error("Response body is empty");
|
|
73
|
-
}
|
|
74
|
-
const nodeStream = Readable.fromWeb(response.body);
|
|
75
|
-
await pipelineAsync(
|
|
76
|
-
nodeStream,
|
|
77
|
-
extract({ cwd: targetDir, strip: 1, filter: shouldInclude })
|
|
78
|
-
);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
14
|
// src/docs-json.ts
|
|
82
15
|
import { readFileSync, writeFileSync } from "fs";
|
|
83
16
|
import { join } from "path";
|
|
@@ -189,7 +122,16 @@ async function scaffold(options) {
|
|
|
189
122
|
}
|
|
190
123
|
mkdirSync(targetDir, { recursive: true });
|
|
191
124
|
const slug = slugify(projectName);
|
|
192
|
-
await
|
|
125
|
+
await downloadStarter(targetDir, projectName);
|
|
126
|
+
personalizeStarter(targetDir, {
|
|
127
|
+
projectName,
|
|
128
|
+
packageName: slug,
|
|
129
|
+
description,
|
|
130
|
+
brandPreset,
|
|
131
|
+
repoUrl,
|
|
132
|
+
enableAiChat,
|
|
133
|
+
i18nLocales
|
|
134
|
+
});
|
|
193
135
|
resetTrackingConfig(targetDir);
|
|
194
136
|
if (trackRepos?.length) {
|
|
195
137
|
writeTrackingConfig(targetDir, trackRepos);
|
|
@@ -198,16 +140,6 @@ async function scaffold(options) {
|
|
|
198
140
|
console.log(" To finish wiring it: `thally track setup` (pick a trigger) + `thally agent init`,");
|
|
199
141
|
console.log(" then add your ANTHROPIC_API_KEY. See /guides/thally-track.");
|
|
200
142
|
}
|
|
201
|
-
writeStarterContent(targetDir, projectName, enableAiChat, repoUrl, i18nLocales);
|
|
202
|
-
writeStarterReadme(targetDir, projectName);
|
|
203
|
-
writeStarterAgentGuide(targetDir, projectName);
|
|
204
|
-
updateSiteConfig(targetDir, projectName, description, brandPreset, repoUrl);
|
|
205
|
-
patchApiReferenceGuard(targetDir);
|
|
206
|
-
patchTopBarNavigation(targetDir);
|
|
207
|
-
patchOpenApiFetch(targetDir);
|
|
208
|
-
patchPackageJson(targetDir, slug);
|
|
209
|
-
writeCloudflareRuntimeConfig(targetDir, slug);
|
|
210
|
-
patchGitignore(targetDir);
|
|
211
143
|
updateEnvExample(targetDir);
|
|
212
144
|
if (doInstall) {
|
|
213
145
|
installDeps(targetDir);
|
|
@@ -222,10 +154,6 @@ export {
|
|
|
222
154
|
installDeps,
|
|
223
155
|
logo,
|
|
224
156
|
success,
|
|
225
|
-
TEMPLATE_REPOSITORY,
|
|
226
|
-
TEMPLATE_COMMIT_SHA,
|
|
227
|
-
EXCLUDE_PATHS,
|
|
228
|
-
shouldInclude,
|
|
229
157
|
readDocsJson,
|
|
230
158
|
writeDocsJson,
|
|
231
159
|
scaffold
|