create-thally-docs 0.9.0 → 0.10.1
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-JNDRCCJV.js → chunk-2N74PW2Y.js} +15 -87
- package/dist/{chunk-YWNWLSZ2.js → chunk-DGK6XREP.js} +1 -1
- package/dist/chunk-IJXZ4YRL.js +520 -0
- package/dist/chunk-ORUAMPNF.js +187 -0
- package/dist/chunk-RLGB6QF3.js +85 -0
- package/dist/chunk-TMXBINSI.js +164 -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,187 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/customize.ts
|
|
4
|
+
import { cpSync, existsSync, readFileSync, writeFileSync } from "fs";
|
|
5
|
+
import { join } from "path";
|
|
6
|
+
function readJsonObject(filePath, label) {
|
|
7
|
+
let value;
|
|
8
|
+
try {
|
|
9
|
+
value = JSON.parse(readFileSync(filePath, "utf8"));
|
|
10
|
+
} catch {
|
|
11
|
+
throw new Error(`The stable Thally starter contains invalid ${label}.`);
|
|
12
|
+
}
|
|
13
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
14
|
+
throw new Error(`The stable Thally starter contains invalid ${label}.`);
|
|
15
|
+
}
|
|
16
|
+
return value;
|
|
17
|
+
}
|
|
18
|
+
function isRecord(value) {
|
|
19
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
20
|
+
}
|
|
21
|
+
function normalizedStarterLocales(locales) {
|
|
22
|
+
const normalized = [{ code: "en", label: "English" }];
|
|
23
|
+
const seen = /* @__PURE__ */ new Set(["en"]);
|
|
24
|
+
for (const locale of locales ?? []) {
|
|
25
|
+
const code = locale.code.trim().toLowerCase();
|
|
26
|
+
const label = locale.label.trim();
|
|
27
|
+
if (!/^[a-z]{2,3}(?:-[a-z0-9]{2,8})*$/.test(code) || !label || label.length > 80 || seen.has(code)) {
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
seen.add(code);
|
|
31
|
+
normalized.push({ code, label });
|
|
32
|
+
}
|
|
33
|
+
return normalized;
|
|
34
|
+
}
|
|
35
|
+
function updateStarterDocsConfig(targetDir, enableAiChat, repoUrl, i18nLocales) {
|
|
36
|
+
const configPath = join(targetDir, "docs.json");
|
|
37
|
+
const config = readJsonObject(configPath, "docs.json");
|
|
38
|
+
const ai = isRecord(config.ai) ? { ...config.ai } : {};
|
|
39
|
+
ai.chat = enableAiChat;
|
|
40
|
+
config.ai = ai;
|
|
41
|
+
const navbar = isRecord(config.navbar) ? { ...config.navbar } : {};
|
|
42
|
+
const existingLinks = Array.isArray(navbar.links) ? navbar.links : [];
|
|
43
|
+
const links = existingLinks.filter(
|
|
44
|
+
(link) => !isRecord(link) || link.type !== "github" && link.label !== "GitHub"
|
|
45
|
+
);
|
|
46
|
+
if (repoUrl) {
|
|
47
|
+
links.push({ label: "GitHub", href: repoUrl, type: "github" });
|
|
48
|
+
}
|
|
49
|
+
if (links.length > 0) navbar.links = links;
|
|
50
|
+
else delete navbar.links;
|
|
51
|
+
if (Object.keys(navbar).length > 0) config.navbar = navbar;
|
|
52
|
+
else delete config.navbar;
|
|
53
|
+
config.i18n = {
|
|
54
|
+
defaultLocale: "en",
|
|
55
|
+
locales: normalizedStarterLocales(i18nLocales)
|
|
56
|
+
};
|
|
57
|
+
writeFileSync(configPath, `${JSON.stringify(config, null, 2)}
|
|
58
|
+
`, "utf8");
|
|
59
|
+
}
|
|
60
|
+
function escapeTypeScriptString(value) {
|
|
61
|
+
return value.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/\r/g, "\\r").replace(/\n/g, "\\n").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
|
|
62
|
+
}
|
|
63
|
+
function replaceRequired(source, pattern, replacement, field) {
|
|
64
|
+
if (!pattern.test(source)) {
|
|
65
|
+
throw new Error(`The stable Thally starter is missing owner field ${field}.`);
|
|
66
|
+
}
|
|
67
|
+
return source.replace(pattern, replacement);
|
|
68
|
+
}
|
|
69
|
+
function updateSiteConfig(targetDir, projectName, description, brandPreset, repoUrl) {
|
|
70
|
+
if (!projectName.trim() || /[\0\r\n]/.test(projectName)) {
|
|
71
|
+
throw new Error("The documentation project name is invalid.");
|
|
72
|
+
}
|
|
73
|
+
if (!["primary", "secondary"].includes(brandPreset)) {
|
|
74
|
+
throw new Error("The documentation brand preset is invalid.");
|
|
75
|
+
}
|
|
76
|
+
const siteFile = join(targetDir, "src", "data", "site.ts");
|
|
77
|
+
if (!existsSync(siteFile)) {
|
|
78
|
+
throw new Error("The stable Thally starter is missing src/data/site.ts.");
|
|
79
|
+
}
|
|
80
|
+
const escapedName = escapeTypeScriptString(projectName);
|
|
81
|
+
const escapedDescription = escapeTypeScriptString(description);
|
|
82
|
+
const escapedRepoUrl = escapeTypeScriptString(repoUrl);
|
|
83
|
+
let source = readFileSync(siteFile, "utf8");
|
|
84
|
+
source = replaceRequired(
|
|
85
|
+
source,
|
|
86
|
+
/name:[ \t]*'(?:\\.|[^'\\\r\n])*'/,
|
|
87
|
+
`name: '${escapedName}'`,
|
|
88
|
+
"site.name"
|
|
89
|
+
);
|
|
90
|
+
source = replaceRequired(
|
|
91
|
+
source,
|
|
92
|
+
/description:[ \t]*(?:\r?\n[ \t]*)?'(?:\\.|[^'\\\r\n])*'/,
|
|
93
|
+
`description:
|
|
94
|
+
'${escapedDescription}'`,
|
|
95
|
+
"site.description"
|
|
96
|
+
);
|
|
97
|
+
source = replaceRequired(
|
|
98
|
+
source,
|
|
99
|
+
/const brandPreset:[ \t]*BrandPresetKey[ \t]*=[ \t]*'(?:\\.|[^'\\\r\n])*'/,
|
|
100
|
+
`const brandPreset: BrandPresetKey = '${brandPreset}'`,
|
|
101
|
+
"site.brandPreset"
|
|
102
|
+
);
|
|
103
|
+
source = replaceRequired(
|
|
104
|
+
source,
|
|
105
|
+
/repoUrl:[ \t]*'(?:\\.|[^'\\\r\n])*'/,
|
|
106
|
+
`repoUrl: '${escapedRepoUrl}'`,
|
|
107
|
+
"site.repoUrl"
|
|
108
|
+
);
|
|
109
|
+
source = source.replace(
|
|
110
|
+
/\{[ \t]*label:[ \t]*'GitHub',[ \t]*href:[ \t]*'(?:\\.|[^'\\\r\n])*'[ \t]*\}/,
|
|
111
|
+
`{ label: 'GitHub', href: '${escapedRepoUrl}' }`
|
|
112
|
+
);
|
|
113
|
+
source = source.replace(
|
|
114
|
+
/\{[ \t]*label:[ \t]*'Support',[ \t]*href:[ \t]*'(?:\\.|[^'\\\r\n])*'[ \t]*\}/,
|
|
115
|
+
`{ label: 'Support', href: '${escapedRepoUrl ? `${escapedRepoUrl}/issues/new` : ""}' }`
|
|
116
|
+
);
|
|
117
|
+
if (!repoUrl) {
|
|
118
|
+
source = source.replace(
|
|
119
|
+
/\r?\n[ \t]*\{[ \t]*label:[ \t]*'(?:GitHub|Support)',[ \t]*href:[ \t]*''[ \t]*\},?/g,
|
|
120
|
+
""
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
writeFileSync(siteFile, source, "utf8");
|
|
124
|
+
}
|
|
125
|
+
function updatePackageIdentity(targetDir, packageName) {
|
|
126
|
+
const packagePath = join(targetDir, "package.json");
|
|
127
|
+
const packageJson = readJsonObject(packagePath, "package.json");
|
|
128
|
+
packageJson.name = packageName;
|
|
129
|
+
writeFileSync(packagePath, `${JSON.stringify(packageJson, null, 2)}
|
|
130
|
+
`, "utf8");
|
|
131
|
+
const lockPath = join(targetDir, "package-lock.json");
|
|
132
|
+
if (!existsSync(lockPath)) return;
|
|
133
|
+
const lock = readJsonObject(lockPath, "package-lock.json");
|
|
134
|
+
lock.name = packageName;
|
|
135
|
+
if (isRecord(lock.packages) && isRecord(lock.packages[""])) {
|
|
136
|
+
lock.packages[""].name = packageName;
|
|
137
|
+
}
|
|
138
|
+
writeFileSync(lockPath, `${JSON.stringify(lock, null, 2)}
|
|
139
|
+
`, "utf8");
|
|
140
|
+
}
|
|
141
|
+
function updateCloudflareRuntimeName(targetDir, packageName) {
|
|
142
|
+
const configPath = join(targetDir, "wrangler.jsonc");
|
|
143
|
+
if (!existsSync(configPath)) {
|
|
144
|
+
throw new Error("The stable Thally starter is missing wrangler.jsonc.");
|
|
145
|
+
}
|
|
146
|
+
const source = readFileSync(configPath, "utf8");
|
|
147
|
+
const pattern = /("name"\s*:\s*)"(?:\\.|[^"\\])*"/;
|
|
148
|
+
const updated = replaceRequired(
|
|
149
|
+
source,
|
|
150
|
+
pattern,
|
|
151
|
+
`$1${JSON.stringify(packageName)}`,
|
|
152
|
+
"wrangler.name"
|
|
153
|
+
);
|
|
154
|
+
writeFileSync(configPath, updated, "utf8");
|
|
155
|
+
}
|
|
156
|
+
function updateEnvExample(targetDir) {
|
|
157
|
+
const envFile = join(targetDir, ".env.example");
|
|
158
|
+
if (!existsSync(envFile)) return;
|
|
159
|
+
const envLocal = join(targetDir, ".env.local");
|
|
160
|
+
if (!existsSync(envLocal)) cpSync(envFile, envLocal);
|
|
161
|
+
}
|
|
162
|
+
function personalizeStarter(targetDir, options) {
|
|
163
|
+
updateStarterDocsConfig(
|
|
164
|
+
targetDir,
|
|
165
|
+
options.enableAiChat,
|
|
166
|
+
options.repoUrl,
|
|
167
|
+
options.i18nLocales
|
|
168
|
+
);
|
|
169
|
+
updateSiteConfig(
|
|
170
|
+
targetDir,
|
|
171
|
+
options.projectName,
|
|
172
|
+
options.description,
|
|
173
|
+
options.brandPreset,
|
|
174
|
+
options.repoUrl
|
|
175
|
+
);
|
|
176
|
+
updatePackageIdentity(targetDir, options.packageName);
|
|
177
|
+
updateCloudflareRuntimeName(targetDir, options.packageName);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export {
|
|
181
|
+
updateStarterDocsConfig,
|
|
182
|
+
updateSiteConfig,
|
|
183
|
+
updatePackageIdentity,
|
|
184
|
+
updateCloudflareRuntimeName,
|
|
185
|
+
updateEnvExample,
|
|
186
|
+
personalizeStarter
|
|
187
|
+
};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/stable-scaffold-release.json
|
|
4
|
+
var stable_scaffold_release_default = {
|
|
5
|
+
schemaVersion: 1,
|
|
6
|
+
id: "2026-08-05.9d281a7c.d44ad701",
|
|
7
|
+
source: {
|
|
8
|
+
repository: "thallylabs/starter",
|
|
9
|
+
commitSha: "9d281a7c209fa3ee187e325f87e72e7ad816f191",
|
|
10
|
+
treeSha: "8a46fb5d8f601251270ba3b477b6c786403c628b",
|
|
11
|
+
archiveUrl: "https://codeload.github.com/thallylabs/starter/tar.gz/9d281a7c209fa3ee187e325f87e72e7ad816f191",
|
|
12
|
+
manifestPath: "starter-release.json",
|
|
13
|
+
manifestSha256: "b662c9d156e04918ea5ff6713c3b6f93db544de6822d7bb8ea3b78ca9c3f6779"
|
|
14
|
+
},
|
|
15
|
+
runtime: {
|
|
16
|
+
repository: "thallylabs/thally",
|
|
17
|
+
commitSha: "d44ad701f7045e019be71717b6fddde451372509",
|
|
18
|
+
treeSha: "0d2a4379dcca251373c802b7c3ea04fffd87fe7a",
|
|
19
|
+
contentSource: "assets",
|
|
20
|
+
identityContractVersion: 1
|
|
21
|
+
},
|
|
22
|
+
starterVersion: 2
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// src/previous-scaffold-releases.json
|
|
26
|
+
var previous_scaffold_releases_default = [
|
|
27
|
+
{
|
|
28
|
+
schemaVersion: 1,
|
|
29
|
+
id: "2026-08-05.04438581.baf29307",
|
|
30
|
+
source: {
|
|
31
|
+
repository: "thallylabs/starter",
|
|
32
|
+
commitSha: "04438581a402d02e9f4308ff047c4c4a48d8169f",
|
|
33
|
+
treeSha: "c962a01b3b8b24ffdb65671c2f7805a47175ba42",
|
|
34
|
+
archiveUrl: "https://codeload.github.com/thallylabs/starter/tar.gz/04438581a402d02e9f4308ff047c4c4a48d8169f",
|
|
35
|
+
manifestPath: "starter-release.json",
|
|
36
|
+
manifestSha256: "e019c6416c6c6872114cdf9d9b5eec6579873f46f826748cf39254fd16628392"
|
|
37
|
+
},
|
|
38
|
+
runtime: {
|
|
39
|
+
repository: "thallylabs/thally",
|
|
40
|
+
commitSha: "baf2930771baf449238bd6c68101ceb3af33ee96",
|
|
41
|
+
treeSha: "56f8217b4da6740015e4775d2ce0aeb60bc167a0",
|
|
42
|
+
contentSource: "assets",
|
|
43
|
+
identityContractVersion: 1
|
|
44
|
+
},
|
|
45
|
+
starterVersion: 2
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
schemaVersion: 1,
|
|
49
|
+
id: "2026-08-05.d5fef916.f3817263",
|
|
50
|
+
source: {
|
|
51
|
+
repository: "thallylabs/starter",
|
|
52
|
+
commitSha: "d5fef9167ea81f12a861deec5515a78a0f756781",
|
|
53
|
+
treeSha: "31c1e49342ce9f4122dc0cd60fe714868ca03150",
|
|
54
|
+
archiveUrl: "https://codeload.github.com/thallylabs/starter/tar.gz/d5fef9167ea81f12a861deec5515a78a0f756781",
|
|
55
|
+
manifestPath: "starter-release.json",
|
|
56
|
+
manifestSha256: "06cccd3496c368ef95a11b3afc15add610f261b8106e8371720b084adbf7fa5a"
|
|
57
|
+
},
|
|
58
|
+
runtime: {
|
|
59
|
+
repository: "thallylabs/thally",
|
|
60
|
+
commitSha: "f38172638cd2cc944ac9ecda19c096ce4d9b4599",
|
|
61
|
+
treeSha: "d33ad60ad0acabee1cf950880c8bd908e83a002a",
|
|
62
|
+
contentSource: "assets",
|
|
63
|
+
identityContractVersion: 1
|
|
64
|
+
},
|
|
65
|
+
starterVersion: 1
|
|
66
|
+
}
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
// src/release.ts
|
|
70
|
+
var STABLE_SCAFFOLD_RELEASE = stable_scaffold_release_default;
|
|
71
|
+
var SUPPORTED_SCAFFOLD_RELEASES = [
|
|
72
|
+
STABLE_SCAFFOLD_RELEASE,
|
|
73
|
+
...previous_scaffold_releases_default
|
|
74
|
+
];
|
|
75
|
+
function isStableScaffoldRelease(value) {
|
|
76
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
77
|
+
const candidate = value;
|
|
78
|
+
return candidate.schemaVersion === STABLE_SCAFFOLD_RELEASE.schemaVersion && candidate.id === STABLE_SCAFFOLD_RELEASE.id && candidate.starterVersion === STABLE_SCAFFOLD_RELEASE.starterVersion && candidate.source?.repository === STABLE_SCAFFOLD_RELEASE.source.repository && candidate.source?.commitSha === STABLE_SCAFFOLD_RELEASE.source.commitSha && candidate.source?.treeSha === STABLE_SCAFFOLD_RELEASE.source.treeSha && candidate.source?.archiveUrl === STABLE_SCAFFOLD_RELEASE.source.archiveUrl && candidate.source?.manifestPath === STABLE_SCAFFOLD_RELEASE.source.manifestPath && candidate.source?.manifestSha256 === STABLE_SCAFFOLD_RELEASE.source.manifestSha256 && candidate.runtime?.repository === STABLE_SCAFFOLD_RELEASE.runtime.repository && candidate.runtime?.commitSha === STABLE_SCAFFOLD_RELEASE.runtime.commitSha && candidate.runtime?.treeSha === STABLE_SCAFFOLD_RELEASE.runtime.treeSha && candidate.runtime?.contentSource === STABLE_SCAFFOLD_RELEASE.runtime.contentSource && candidate.runtime?.identityContractVersion === STABLE_SCAFFOLD_RELEASE.runtime.identityContractVersion;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export {
|
|
82
|
+
STABLE_SCAFFOLD_RELEASE,
|
|
83
|
+
SUPPORTED_SCAFFOLD_RELEASES,
|
|
84
|
+
isStableScaffoldRelease
|
|
85
|
+
};
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
readStarterReleaseManifest
|
|
4
|
+
} from "./chunk-IJXZ4YRL.js";
|
|
5
|
+
import {
|
|
6
|
+
STABLE_SCAFFOLD_RELEASE
|
|
7
|
+
} from "./chunk-RLGB6QF3.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
|
+
};
|
package/dist/customize.d.ts
CHANGED
|
@@ -1,93 +1,39 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Documented owner-field personalization for the immutable starter tree.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* tracks the newest published CLI, and overwriting its pin with a constant that
|
|
8
|
-
* has since aged past it silently downgrades every generated site. Raise it
|
|
9
|
-
* only alongside a released CLI — `scaffold-hygiene.test.ts` fails if it ever
|
|
10
|
-
* climbs above the version this monorepo publishes.
|
|
4
|
+
* Runtime source, authored pages, navigation structure, dependencies, CI, and
|
|
5
|
+
* repository policy all belong to `thallylabs/starter`. This module may change
|
|
6
|
+
* only the small set of fields an owner answers during creation.
|
|
11
7
|
*/
|
|
12
|
-
declare const MIN_CLI_VERSION = "0.7.0";
|
|
13
|
-
/**
|
|
14
|
-
* Lowest `@thallylabs/mcp` release a scaffold may declare. Same floor semantics
|
|
15
|
-
* as {@link MIN_CLI_VERSION}; it also stands in for the unresolvable `*` range
|
|
16
|
-
* that older workspace-based templates used.
|
|
17
|
-
*/
|
|
18
|
-
declare const MIN_MCP_VERSION = "0.9.0";
|
|
19
|
-
/**
|
|
20
|
-
* Resolve a dependency pin to at least `floor`, preferring what the template
|
|
21
|
-
* already ships. An absent, unresolvable, or demonstrably older pin becomes the
|
|
22
|
-
* floor; anything newer — or anything the comparison cannot rank — survives.
|
|
23
|
-
*/
|
|
24
|
-
declare function raisePinToFloor(current: string | undefined, floor: string): string;
|
|
25
8
|
interface StarterLocale {
|
|
26
9
|
code: string;
|
|
27
10
|
label: string;
|
|
28
11
|
}
|
|
29
|
-
interface
|
|
30
|
-
path: string;
|
|
31
|
-
content: string;
|
|
32
|
-
}
|
|
33
|
-
interface BuildStarterFilesOptions {
|
|
12
|
+
interface StarterPersonalizationOptions {
|
|
34
13
|
projectName: string;
|
|
35
|
-
|
|
36
|
-
|
|
14
|
+
packageName: string;
|
|
15
|
+
description: string;
|
|
16
|
+
brandPreset: string;
|
|
17
|
+
repoUrl: string;
|
|
18
|
+
enableAiChat: boolean;
|
|
37
19
|
i18nLocales?: Array<StarterLocale>;
|
|
38
20
|
}
|
|
39
|
-
declare function buildStarterDocsJson({ enableAiChat, repoUrl, i18nLocales, }: {
|
|
40
|
-
enableAiChat: boolean;
|
|
41
|
-
repoUrl?: string;
|
|
42
|
-
i18nLocales?: Array<{
|
|
43
|
-
code: string;
|
|
44
|
-
label: string;
|
|
45
|
-
}>;
|
|
46
|
-
}): string;
|
|
47
21
|
/**
|
|
48
|
-
*
|
|
49
|
-
* filesystem. CLI, MCP, and Thally Cloud consume these exact bytes so a new
|
|
50
|
-
* site cannot vary by which creation entry point the user chose.
|
|
51
|
-
*/
|
|
52
|
-
declare function buildStarterFiles({ projectName, enableAiChat, repoUrl, i18nLocales, }: BuildStarterFilesOptions): Array<StarterFile>;
|
|
53
|
-
declare function writeStarterContent(targetDir: string, projectName: string, enableAiChat?: boolean, repoUrl?: string, i18nLocales?: Array<{
|
|
54
|
-
code: string;
|
|
55
|
-
label: string;
|
|
56
|
-
}>): void;
|
|
57
|
-
declare function writeStarterAgentGuide(targetDir: string, projectName: string): void;
|
|
58
|
-
declare function writeStarterReadme(targetDir: string, projectName: string): void;
|
|
59
|
-
/**
|
|
60
|
-
* Write provider-portable Cloudflare Workers configuration into every scaffold.
|
|
22
|
+
* Personalize only the owner-controlled settings in `docs.json`.
|
|
61
23
|
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
* same source tree to their own account without editing application internals.
|
|
24
|
+
* Authored tabs, groups, pages, theme choices, and every unrelated key remain
|
|
25
|
+
* byte-for-byte equivalent after JSON normalization.
|
|
65
26
|
*/
|
|
66
|
-
declare function
|
|
27
|
+
declare function updateStarterDocsConfig(targetDir: string, enableAiChat: boolean, repoUrl: string, i18nLocales?: Array<StarterLocale>): void;
|
|
28
|
+
/** Personalize the documented identity and repository fields in `site.ts`. */
|
|
67
29
|
declare function updateSiteConfig(targetDir: string, projectName: string, description: string, brandPreset: string, repoUrl: string): void;
|
|
68
|
-
|
|
69
|
-
declare function
|
|
70
|
-
|
|
30
|
+
/** Rename only the root package identity; dependency pins belong to starter. */
|
|
31
|
+
declare function updatePackageIdentity(targetDir: string, packageName: string): void;
|
|
32
|
+
/** Update the provider-portable Worker name without recreating its config. */
|
|
33
|
+
declare function updateCloudflareRuntimeName(targetDir: string, packageName: string): void;
|
|
34
|
+
/** Copy the canonical environment guide to the ignored local filename. */
|
|
71
35
|
declare function updateEnvExample(targetDir: string): void;
|
|
72
|
-
/**
|
|
73
|
-
|
|
74
|
-
* `thallylabs/docs` is already standalone, while this defensive cleanup also
|
|
75
|
-
* keeps local or older monorepo-based sources safe to scaffold:
|
|
76
|
-
*
|
|
77
|
-
* - `workspaces` points at a directory that doesn't exist in scaffolds.
|
|
78
|
-
* - `prebuild`/`pretest` invoke `packages:build`, which builds those absent
|
|
79
|
-
* workspaces. Runtime sources and embeddings stay in `prebuild`: both run
|
|
80
|
-
* standalone and supply request-time data in filesystem-free Workers.
|
|
81
|
-
* - The copied `package-lock.json` still resolves the monorepo's workspace
|
|
82
|
-
* graph; deleting it lets the scaffold's own `npm install` write a clean
|
|
83
|
-
* lockfile. Workspace-linked deps (e.g. @thallylabs/core) resolve from the
|
|
84
|
-
* npm registry instead, which is why the template must depend on published
|
|
85
|
-
* versions, never `workspace:*` specs.
|
|
86
|
-
*
|
|
87
|
-
* Also names the package after the site so `npm ls`/lockfiles read correctly.
|
|
88
|
-
*/
|
|
89
|
-
declare function patchPackageJson(targetDir: string, slug: string): void;
|
|
90
|
-
/** Ensure dependency folders are ignored at any depth in generated sites. */
|
|
91
|
-
declare function patchGitignore(targetDir: string): void;
|
|
36
|
+
/** Apply the complete, intentionally narrow owner-personalization contract. */
|
|
37
|
+
declare function personalizeStarter(targetDir: string, options: StarterPersonalizationOptions): void;
|
|
92
38
|
|
|
93
|
-
export { type
|
|
39
|
+
export { type StarterLocale, type StarterPersonalizationOptions, personalizeStarter, updateCloudflareRuntimeName, updateEnvExample, updatePackageIdentity, updateSiteConfig, updateStarterDocsConfig };
|
package/dist/customize.js
CHANGED
|
@@ -1,37 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
buildStarterDocsJson,
|
|
6
|
-
buildStarterFiles,
|
|
7
|
-
patchApiReferenceGuard,
|
|
8
|
-
patchGitignore,
|
|
9
|
-
patchOpenApiFetch,
|
|
10
|
-
patchPackageJson,
|
|
11
|
-
patchTopBarNavigation,
|
|
12
|
-
raisePinToFloor,
|
|
3
|
+
personalizeStarter,
|
|
4
|
+
updateCloudflareRuntimeName,
|
|
13
5
|
updateEnvExample,
|
|
6
|
+
updatePackageIdentity,
|
|
14
7
|
updateSiteConfig,
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
writeStarterContent,
|
|
18
|
-
writeStarterReadme
|
|
19
|
-
} from "./chunk-BW7J7DJV.js";
|
|
8
|
+
updateStarterDocsConfig
|
|
9
|
+
} from "./chunk-ORUAMPNF.js";
|
|
20
10
|
export {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
buildStarterDocsJson,
|
|
24
|
-
buildStarterFiles,
|
|
25
|
-
patchApiReferenceGuard,
|
|
26
|
-
patchGitignore,
|
|
27
|
-
patchOpenApiFetch,
|
|
28
|
-
patchPackageJson,
|
|
29
|
-
patchTopBarNavigation,
|
|
30
|
-
raisePinToFloor,
|
|
11
|
+
personalizeStarter,
|
|
12
|
+
updateCloudflareRuntimeName,
|
|
31
13
|
updateEnvExample,
|
|
14
|
+
updatePackageIdentity,
|
|
32
15
|
updateSiteConfig,
|
|
33
|
-
|
|
34
|
-
writeStarterAgentGuide,
|
|
35
|
-
writeStarterContent,
|
|
36
|
-
writeStarterReadme
|
|
16
|
+
updateStarterDocsConfig
|
|
37
17
|
};
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
migrateDocs
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-DGK6XREP.js";
|
|
5
5
|
import {
|
|
6
6
|
logo,
|
|
7
7
|
readDocsJson,
|
|
@@ -9,9 +9,11 @@ import {
|
|
|
9
9
|
slugify,
|
|
10
10
|
success,
|
|
11
11
|
writeDocsJson
|
|
12
|
-
} from "./chunk-
|
|
13
|
-
import "./chunk-
|
|
14
|
-
import "./chunk-
|
|
12
|
+
} from "./chunk-2N74PW2Y.js";
|
|
13
|
+
import "./chunk-ORUAMPNF.js";
|
|
14
|
+
import "./chunk-TMXBINSI.js";
|
|
15
|
+
import "./chunk-IJXZ4YRL.js";
|
|
16
|
+
import "./chunk-RLGB6QF3.js";
|
|
15
17
|
|
|
16
18
|
// src/index.ts
|
|
17
19
|
import { existsSync as existsSync3, readdirSync as readdirSync2 } from "fs";
|
|
@@ -151,7 +153,7 @@ async function gatherAnswers(dirArg, useDefaults, installPreference) {
|
|
|
151
153
|
let i18nLocales;
|
|
152
154
|
if (!useDefaults) {
|
|
153
155
|
const enableI18n = await input({
|
|
154
|
-
message: " Add
|
|
156
|
+
message: " Add another documentation language? (y/N):",
|
|
155
157
|
default: "N"
|
|
156
158
|
});
|
|
157
159
|
if (enableI18n.toLowerCase() === "y") {
|
package/dist/migrate/index.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
migrateDocs
|
|
4
|
-
} from "../chunk-
|
|
5
|
-
import "../chunk-
|
|
6
|
-
import "../chunk-
|
|
7
|
-
import "../chunk-
|
|
4
|
+
} from "../chunk-DGK6XREP.js";
|
|
5
|
+
import "../chunk-2N74PW2Y.js";
|
|
6
|
+
import "../chunk-ORUAMPNF.js";
|
|
7
|
+
import "../chunk-TMXBINSI.js";
|
|
8
|
+
import "../chunk-IJXZ4YRL.js";
|
|
9
|
+
import "../chunk-RLGB6QF3.js";
|
|
8
10
|
export {
|
|
9
11
|
migrateDocs
|
|
10
12
|
};
|