@treeseed/cli 0.1.1 → 0.4.2
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/README.md +27 -26
- package/dist/cli/handlers/auth-login.d.ts +2 -0
- package/dist/cli/handlers/auth-login.js +67 -0
- package/dist/cli/handlers/auth-logout.d.ts +2 -0
- package/dist/cli/handlers/auth-logout.js +20 -0
- package/dist/cli/handlers/auth-whoami.d.ts +2 -0
- package/dist/cli/handlers/auth-whoami.js +24 -0
- package/dist/cli/handlers/close.js +19 -53
- package/dist/cli/handlers/config.js +33 -53
- package/dist/cli/handlers/destroy.js +34 -79
- package/dist/{src/cli/handlers/ship.d.ts → cli/handlers/dev.d.ts} +1 -1
- package/dist/cli/handlers/dev.js +19 -0
- package/dist/cli/handlers/doctor.js +13 -6
- package/dist/cli/handlers/init.js +32 -8
- package/dist/cli/handlers/release.js +21 -53
- package/dist/cli/handlers/rollback.js +8 -8
- package/dist/cli/handlers/save.js +21 -79
- package/dist/cli/handlers/stage.d.ts +2 -0
- package/dist/cli/handlers/stage.js +28 -0
- package/dist/cli/handlers/status.js +35 -26
- package/dist/{src/cli/handlers/deploy.d.ts → cli/handlers/switch.d.ts} +1 -1
- package/dist/cli/handlers/switch.js +29 -0
- package/dist/{src/cli/handlers/next.d.ts → cli/handlers/sync.d.ts} +1 -1
- package/dist/cli/handlers/sync.js +26 -0
- package/dist/cli/handlers/tasks.d.ts +2 -0
- package/dist/cli/handlers/tasks.js +31 -0
- package/dist/cli/handlers/template.d.ts +2 -0
- package/dist/cli/handlers/template.js +27 -0
- package/dist/cli/handlers/workflow.d.ts +6 -0
- package/dist/cli/handlers/workflow.js +71 -0
- package/dist/{src/cli → cli}/help.d.ts +2 -2
- package/dist/cli/help.js +36 -24
- package/dist/cli/main.d.ts +6 -0
- package/dist/cli/main.js +14 -19
- package/dist/cli/operations-help.d.ts +1 -0
- package/dist/cli/operations-help.js +1 -0
- package/dist/cli/operations-parser.d.ts +1 -0
- package/dist/cli/operations-parser.js +1 -0
- package/dist/cli/operations-registry.d.ts +5 -0
- package/dist/cli/operations-registry.js +260 -0
- package/dist/cli/operations-types.d.ts +72 -0
- package/dist/cli/parser.d.ts +3 -0
- package/dist/cli/parser.js +1 -6
- package/dist/cli/registry.d.ts +25 -0
- package/dist/cli/registry.js +28 -416
- package/dist/cli/repair.js +6 -4
- package/dist/cli/runtime.d.ts +31 -0
- package/dist/cli/runtime.js +240 -111
- package/dist/cli/types.d.ts +1 -0
- package/dist/{src/cli → cli}/workflow-state.d.ts +9 -0
- package/dist/cli/workflow-state.js +45 -21
- package/package.json +13 -13
- package/dist/cli/handlers/continue.js +0 -23
- package/dist/cli/handlers/deploy.js +0 -139
- package/dist/cli/handlers/next.js +0 -27
- package/dist/cli/handlers/prepare.js +0 -8
- package/dist/cli/handlers/promote.js +0 -8
- package/dist/cli/handlers/publish.js +0 -8
- package/dist/cli/handlers/setup.js +0 -48
- package/dist/cli/handlers/ship.js +0 -49
- package/dist/cli/handlers/start.js +0 -97
- package/dist/cli/handlers/teardown.js +0 -50
- package/dist/cli/handlers/work.js +0 -85
- package/dist/scripts/aggregate-book.d.ts +0 -1
- package/dist/scripts/aggregate-book.js +0 -121
- package/dist/scripts/assert-release-tag-version.d.ts +0 -1
- package/dist/scripts/assert-release-tag-version.js +0 -21
- package/dist/scripts/build-dist.d.ts +0 -1
- package/dist/scripts/build-dist.js +0 -108
- package/dist/scripts/build-tenant-worker.d.ts +0 -1
- package/dist/scripts/build-tenant-worker.js +0 -36
- package/dist/scripts/cleanup-markdown.d.ts +0 -2
- package/dist/scripts/cleanup-markdown.js +0 -373
- package/dist/scripts/config-runtime-lib.d.ts +0 -122
- package/dist/scripts/config-runtime-lib.js +0 -505
- package/dist/scripts/config-treeseed.d.ts +0 -2
- package/dist/scripts/config-treeseed.js +0 -81
- package/dist/scripts/d1-migration-lib.d.ts +0 -6
- package/dist/scripts/d1-migration-lib.js +0 -90
- package/dist/scripts/deploy-lib.d.ts +0 -127
- package/dist/scripts/deploy-lib.js +0 -841
- package/dist/scripts/ensure-mailpit.d.ts +0 -1
- package/dist/scripts/ensure-mailpit.js +0 -29
- package/dist/scripts/git-workflow-lib.d.ts +0 -25
- package/dist/scripts/git-workflow-lib.js +0 -136
- package/dist/scripts/github-automation-lib.d.ts +0 -156
- package/dist/scripts/github-automation-lib.js +0 -242
- package/dist/scripts/local-dev-lib.d.ts +0 -9
- package/dist/scripts/local-dev-lib.js +0 -84
- package/dist/scripts/local-dev.d.ts +0 -1
- package/dist/scripts/local-dev.js +0 -129
- package/dist/scripts/logs-mailpit.d.ts +0 -1
- package/dist/scripts/logs-mailpit.js +0 -2
- package/dist/scripts/mailpit-runtime.d.ts +0 -4
- package/dist/scripts/mailpit-runtime.js +0 -57
- package/dist/scripts/package-tools.d.ts +0 -22
- package/dist/scripts/package-tools.js +0 -255
- package/dist/scripts/patch-starlight-content-path.d.ts +0 -1
- package/dist/scripts/patch-starlight-content-path.js +0 -172
- package/dist/scripts/paths.d.ts +0 -17
- package/dist/scripts/paths.js +0 -26
- package/dist/scripts/publish-package.d.ts +0 -1
- package/dist/scripts/publish-package.js +0 -19
- package/dist/scripts/release-verify.d.ts +0 -1
- package/dist/scripts/release-verify.js +0 -136
- package/dist/scripts/run-fixture-astro-command.d.ts +0 -1
- package/dist/scripts/run-fixture-astro-command.js +0 -18
- package/dist/scripts/save-deploy-preflight-lib.d.ts +0 -34
- package/dist/scripts/save-deploy-preflight-lib.js +0 -69
- package/dist/scripts/scaffold-site.d.ts +0 -2
- package/dist/scripts/scaffold-site.js +0 -92
- package/dist/scripts/stop-mailpit.d.ts +0 -1
- package/dist/scripts/stop-mailpit.js +0 -5
- package/dist/scripts/sync-dev-vars.d.ts +0 -1
- package/dist/scripts/sync-dev-vars.js +0 -6
- package/dist/scripts/template-registry-lib.d.ts +0 -47
- package/dist/scripts/template-registry-lib.js +0 -137
- package/dist/scripts/tenant-astro-command.d.ts +0 -1
- package/dist/scripts/tenant-astro-command.js +0 -3
- package/dist/scripts/tenant-build.d.ts +0 -1
- package/dist/scripts/tenant-build.js +0 -16
- package/dist/scripts/tenant-check.d.ts +0 -1
- package/dist/scripts/tenant-check.js +0 -7
- package/dist/scripts/tenant-d1-migrate-local.d.ts +0 -1
- package/dist/scripts/tenant-d1-migrate-local.js +0 -11
- package/dist/scripts/tenant-deploy.d.ts +0 -2
- package/dist/scripts/tenant-deploy.js +0 -180
- package/dist/scripts/tenant-destroy.d.ts +0 -2
- package/dist/scripts/tenant-destroy.js +0 -104
- package/dist/scripts/tenant-dev.d.ts +0 -1
- package/dist/scripts/tenant-dev.js +0 -171
- package/dist/scripts/tenant-lint.d.ts +0 -1
- package/dist/scripts/tenant-lint.js +0 -4
- package/dist/scripts/tenant-test.d.ts +0 -1
- package/dist/scripts/tenant-test.js +0 -4
- package/dist/scripts/test-cloudflare-local.d.ts +0 -1
- package/dist/scripts/test-cloudflare-local.js +0 -212
- package/dist/scripts/test-scaffold.d.ts +0 -2
- package/dist/scripts/test-scaffold.js +0 -297
- package/dist/scripts/treeseed.d.ts +0 -2
- package/dist/scripts/treeseed.js +0 -4
- package/dist/scripts/validate-templates.d.ts +0 -2
- package/dist/scripts/validate-templates.js +0 -4
- package/dist/scripts/watch-dev-lib.d.ts +0 -21
- package/dist/scripts/watch-dev-lib.js +0 -277
- package/dist/scripts/workspace-close.d.ts +0 -2
- package/dist/scripts/workspace-close.js +0 -24
- package/dist/scripts/workspace-command-e2e.d.ts +0 -2
- package/dist/scripts/workspace-command-e2e.js +0 -718
- package/dist/scripts/workspace-lint.d.ts +0 -1
- package/dist/scripts/workspace-lint.js +0 -9
- package/dist/scripts/workspace-preflight-lib.d.ts +0 -36
- package/dist/scripts/workspace-preflight-lib.js +0 -179
- package/dist/scripts/workspace-preflight.d.ts +0 -2
- package/dist/scripts/workspace-preflight.js +0 -22
- package/dist/scripts/workspace-publish-changed-packages.d.ts +0 -1
- package/dist/scripts/workspace-publish-changed-packages.js +0 -16
- package/dist/scripts/workspace-release-verify.d.ts +0 -1
- package/dist/scripts/workspace-release-verify.js +0 -81
- package/dist/scripts/workspace-release.d.ts +0 -2
- package/dist/scripts/workspace-release.js +0 -42
- package/dist/scripts/workspace-save-lib.d.ts +0 -42
- package/dist/scripts/workspace-save-lib.js +0 -220
- package/dist/scripts/workspace-save.d.ts +0 -2
- package/dist/scripts/workspace-save.js +0 -124
- package/dist/scripts/workspace-start-warning.js +0 -3
- package/dist/scripts/workspace-start.d.ts +0 -2
- package/dist/scripts/workspace-start.js +0 -71
- package/dist/scripts/workspace-test-unit.d.ts +0 -1
- package/dist/scripts/workspace-test-unit.js +0 -4
- package/dist/scripts/workspace-test.d.ts +0 -1
- package/dist/scripts/workspace-test.js +0 -11
- package/dist/scripts/workspace-tools.d.ts +0 -13
- package/dist/scripts/workspace-tools.js +0 -226
- package/dist/src/cli/handlers/continue.d.ts +0 -2
- package/dist/src/cli/handlers/prepare.d.ts +0 -2
- package/dist/src/cli/handlers/promote.d.ts +0 -2
- package/dist/src/cli/handlers/publish.d.ts +0 -2
- package/dist/src/cli/handlers/setup.d.ts +0 -2
- package/dist/src/cli/handlers/start.d.ts +0 -3
- package/dist/src/cli/handlers/teardown.d.ts +0 -2
- package/dist/src/cli/handlers/work.d.ts +0 -2
- package/dist/src/cli/main.d.ts +0 -6
- package/dist/src/cli/parser.d.ts +0 -3
- package/dist/src/cli/registry.d.ts +0 -27
- package/dist/src/cli/runtime.d.ts +0 -4
- package/dist/src/cli/types.d.ts +0 -71
- /package/dist/{src/cli → cli}/handlers/close.d.ts +0 -0
- /package/dist/{src/cli → cli}/handlers/config.d.ts +0 -0
- /package/dist/{src/cli → cli}/handlers/destroy.d.ts +0 -0
- /package/dist/{src/cli → cli}/handlers/doctor.d.ts +0 -0
- /package/dist/{src/cli → cli}/handlers/init.d.ts +0 -0
- /package/dist/{src/cli → cli}/handlers/release.d.ts +0 -0
- /package/dist/{src/cli → cli}/handlers/rollback.d.ts +0 -0
- /package/dist/{src/cli → cli}/handlers/save.d.ts +0 -0
- /package/dist/{src/cli → cli}/handlers/status.d.ts +0 -0
- /package/dist/{src/cli → cli}/handlers/utils.d.ts +0 -0
- /package/dist/{scripts/workspace-start-warning.d.ts → cli/operations-types.js} +0 -0
- /package/dist/{src/cli → cli}/repair.d.ts +0 -0
- /package/dist/{src/index.d.ts → index.d.ts} +0 -0
|
@@ -1,212 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert/strict';
|
|
2
|
-
import { mkdirSync } from 'node:fs';
|
|
3
|
-
import { resolve } from 'node:path';
|
|
4
|
-
import { setTimeout as delay } from 'node:timers/promises';
|
|
5
|
-
import { prepareCloudflareLocalRuntime, spawnProcess, startWranglerDev } from './local-dev-lib.js';
|
|
6
|
-
import { fixtureRoot, fixtureWranglerConfig } from './paths.js';
|
|
7
|
-
const TEST_PORT = 8791;
|
|
8
|
-
const BASE_URL = `http://127.0.0.1:${TEST_PORT}`;
|
|
9
|
-
const RUN_ID = `${Date.now()}-${process.pid}`;
|
|
10
|
-
const ipSeed = Date.now() + process.pid;
|
|
11
|
-
const TEST_IP = `198.51.${(ipSeed >>> 8) % 250}.${ipSeed % 250}`;
|
|
12
|
-
const TEST_EMAIL = `integration-subscriber-${RUN_ID}@localhost.test`;
|
|
13
|
-
const PERSIST_TO = resolve(process.cwd(), '.local', 'cloudflare-integration', RUN_ID);
|
|
14
|
-
const workerLogs = [];
|
|
15
|
-
mkdirSync(PERSIST_TO, { recursive: true });
|
|
16
|
-
function captureLogs(stream, label) {
|
|
17
|
-
if (!stream) {
|
|
18
|
-
return;
|
|
19
|
-
}
|
|
20
|
-
stream.setEncoding('utf8');
|
|
21
|
-
stream.on('data', (chunk) => {
|
|
22
|
-
workerLogs.push(`[${label}] ${chunk}`);
|
|
23
|
-
if (workerLogs.length > 40) {
|
|
24
|
-
workerLogs.shift();
|
|
25
|
-
}
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
function readBody(message) {
|
|
29
|
-
return new Promise((resolve) => {
|
|
30
|
-
let body = '';
|
|
31
|
-
message.setEncoding('utf8');
|
|
32
|
-
message.on('data', (chunk) => {
|
|
33
|
-
body += chunk;
|
|
34
|
-
});
|
|
35
|
-
message.on('end', () => resolve(body));
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
function extractSetCookie(response) {
|
|
39
|
-
if (typeof response.headers.getSetCookie === 'function') {
|
|
40
|
-
return response.headers.getSetCookie();
|
|
41
|
-
}
|
|
42
|
-
const cookie = response.headers.get('set-cookie');
|
|
43
|
-
return cookie ? [cookie] : [];
|
|
44
|
-
}
|
|
45
|
-
async function waitForWorker(worker) {
|
|
46
|
-
for (let attempt = 0; attempt < 60; attempt += 1) {
|
|
47
|
-
if (worker.exitCode !== null) {
|
|
48
|
-
throw new Error(`Wrangler exited before the worker became ready (exit ${worker.exitCode}).\n${workerLogs.join('')}`);
|
|
49
|
-
}
|
|
50
|
-
try {
|
|
51
|
-
const response = await fetch(`${BASE_URL}/api/form/submit?formType=subscribe`, {
|
|
52
|
-
signal: AbortSignal.timeout(5000),
|
|
53
|
-
headers: {
|
|
54
|
-
'x-forwarded-for': TEST_IP,
|
|
55
|
-
},
|
|
56
|
-
});
|
|
57
|
-
if (response.ok) {
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
catch {
|
|
62
|
-
// Keep polling until the local worker is ready.
|
|
63
|
-
}
|
|
64
|
-
await delay(1000);
|
|
65
|
-
}
|
|
66
|
-
throw new Error(`Timed out waiting for local Wrangler dev to start.\n${workerLogs.join('')}`);
|
|
67
|
-
}
|
|
68
|
-
async function issueToken() {
|
|
69
|
-
const response = await fetch(`${BASE_URL}/api/form/submit?formType=subscribe`, {
|
|
70
|
-
signal: AbortSignal.timeout(10000),
|
|
71
|
-
headers: {
|
|
72
|
-
'x-forwarded-for': TEST_IP,
|
|
73
|
-
},
|
|
74
|
-
});
|
|
75
|
-
assert.equal(response.status, 200, 'token endpoint should return HTTP 200');
|
|
76
|
-
const payload = await response.json();
|
|
77
|
-
const cookies = extractSetCookie(response);
|
|
78
|
-
assert.equal(payload.ok, true, 'token payload should be ok');
|
|
79
|
-
assert.ok(payload.formToken, 'token endpoint should return a form token');
|
|
80
|
-
assert.ok(payload.sessionId, 'token endpoint should return a session id');
|
|
81
|
-
assert.ok(cookies.length > 0, 'token endpoint should set a session cookie');
|
|
82
|
-
return {
|
|
83
|
-
formToken: payload.formToken,
|
|
84
|
-
sessionId: payload.sessionId,
|
|
85
|
-
cookieHeader: cookies.map((cookie) => cookie.split(';', 1)[0]).join('; '),
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
async function submitSubscribeForm({ formToken, sessionId, cookieHeader }, email = TEST_EMAIL) {
|
|
89
|
-
const form = new FormData();
|
|
90
|
-
form.set('formType', 'subscribe');
|
|
91
|
-
form.set('name', 'Integration Test');
|
|
92
|
-
form.set('email', email);
|
|
93
|
-
form.set('formToken', formToken);
|
|
94
|
-
form.set('formSession', sessionId);
|
|
95
|
-
form.set('redirectTo', '/');
|
|
96
|
-
return fetch(`${BASE_URL}/api/form/submit`, {
|
|
97
|
-
method: 'POST',
|
|
98
|
-
redirect: 'manual',
|
|
99
|
-
signal: AbortSignal.timeout(10000),
|
|
100
|
-
headers: {
|
|
101
|
-
cookie: cookieHeader,
|
|
102
|
-
origin: BASE_URL,
|
|
103
|
-
referer: `${BASE_URL}/`,
|
|
104
|
-
'x-forwarded-for': TEST_IP,
|
|
105
|
-
},
|
|
106
|
-
body: form,
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
async function querySubscribers() {
|
|
110
|
-
const query = `SELECT lookup_key AS email, status, json_extract(payload_json, '$.source') AS source FROM runtime_records WHERE record_type = 'subscription' AND lookup_key = '${TEST_EMAIL}'`;
|
|
111
|
-
const child = spawnProcess('wrangler', [
|
|
112
|
-
'd1',
|
|
113
|
-
'execute',
|
|
114
|
-
'karyon-docs-site-data',
|
|
115
|
-
'--local',
|
|
116
|
-
'--config',
|
|
117
|
-
fixtureWranglerConfig,
|
|
118
|
-
'--persist-to',
|
|
119
|
-
PERSIST_TO,
|
|
120
|
-
'--json',
|
|
121
|
-
'--command',
|
|
122
|
-
query,
|
|
123
|
-
], { stdio: ['ignore', 'pipe', 'inherit'], cwd: fixtureRoot });
|
|
124
|
-
const stdout = await readBody(child.stdout);
|
|
125
|
-
const exitCode = await new Promise((resolve) => {
|
|
126
|
-
child.on('exit', resolve);
|
|
127
|
-
});
|
|
128
|
-
assert.equal(exitCode, 0, 'local D1 query should succeed');
|
|
129
|
-
const parsed = JSON.parse(stdout);
|
|
130
|
-
const results = Array.isArray(parsed) ? parsed : [parsed];
|
|
131
|
-
const rows = results.flatMap((entry) => entry.results ?? []);
|
|
132
|
-
return rows;
|
|
133
|
-
}
|
|
134
|
-
async function main() {
|
|
135
|
-
prepareCloudflareLocalRuntime({
|
|
136
|
-
persistTo: PERSIST_TO,
|
|
137
|
-
envOverrides: {
|
|
138
|
-
TREESEED_LOCAL_DEV_MODE: 'cloudflare',
|
|
139
|
-
TREESEED_FORMS_LOCAL_BYPASS_TURNSTILE: 'true',
|
|
140
|
-
TREESEED_PUBLIC_FORMS_LOCAL_BYPASS_TURNSTILE: 'true',
|
|
141
|
-
TREESEED_FORMS_LOCAL_BYPASS_CLOUDFLARE_GUARDS: 'false',
|
|
142
|
-
TREESEED_FORMS_LOCAL_USE_MAILPIT: 'true',
|
|
143
|
-
TREESEED_MAILPIT_SMTP_HOST: '127.0.0.1',
|
|
144
|
-
TREESEED_MAILPIT_SMTP_PORT: '1025',
|
|
145
|
-
},
|
|
146
|
-
});
|
|
147
|
-
const worker = startWranglerDev(['--port', String(TEST_PORT), '--persist-to', PERSIST_TO], {
|
|
148
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
149
|
-
});
|
|
150
|
-
captureLogs(worker.stdout, 'stdout');
|
|
151
|
-
captureLogs(worker.stderr, 'stderr');
|
|
152
|
-
const teardown = async () => {
|
|
153
|
-
if (worker.exitCode === null && !worker.killed) {
|
|
154
|
-
worker.kill('SIGTERM');
|
|
155
|
-
await Promise.race([
|
|
156
|
-
new Promise((resolve) => worker.once('exit', resolve)),
|
|
157
|
-
delay(5000),
|
|
158
|
-
]);
|
|
159
|
-
if (worker.exitCode === null && !worker.killed) {
|
|
160
|
-
worker.kill('SIGKILL');
|
|
161
|
-
await Promise.race([
|
|
162
|
-
new Promise((resolve) => worker.once('exit', resolve)),
|
|
163
|
-
delay(5000),
|
|
164
|
-
]);
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
};
|
|
168
|
-
process.on('SIGINT', async () => {
|
|
169
|
-
await teardown();
|
|
170
|
-
process.exit(130);
|
|
171
|
-
});
|
|
172
|
-
process.on('SIGTERM', async () => {
|
|
173
|
-
await teardown();
|
|
174
|
-
process.exit(143);
|
|
175
|
-
});
|
|
176
|
-
let shouldQuerySubscribers = false;
|
|
177
|
-
try {
|
|
178
|
-
await waitForWorker(worker);
|
|
179
|
-
const firstToken = await issueToken();
|
|
180
|
-
const firstResponse = await submitSubscribeForm(firstToken);
|
|
181
|
-
assert.equal(firstResponse.status, 303, 'subscribe submit should redirect');
|
|
182
|
-
assert.match(firstResponse.headers.get('location') ?? '', /\/\?formStatus=success&formCode=success#site-subscribe$/, 'subscribe success redirect should include success markers');
|
|
183
|
-
shouldQuerySubscribers = true;
|
|
184
|
-
const replayResponse = await submitSubscribeForm(firstToken);
|
|
185
|
-
assert.equal(replayResponse.status, 303, 'replayed submit should still redirect');
|
|
186
|
-
assert.match(replayResponse.headers.get('location') ?? '', /\/\?formStatus=error&formCode=token_replayed#site-subscribe$/, 'replayed token should be rejected by KV-backed nonce storage');
|
|
187
|
-
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
188
|
-
const token = await issueToken();
|
|
189
|
-
const response = await submitSubscribeForm(token);
|
|
190
|
-
assert.equal(response.status, 303, 'pre-limit submissions should redirect');
|
|
191
|
-
assert.match(response.headers.get('location') ?? '', /\/\?formStatus=success&formCode=success#site-subscribe$/, 'pre-limit submissions should succeed');
|
|
192
|
-
}
|
|
193
|
-
const limitedToken = await issueToken();
|
|
194
|
-
const limitedResponse = await submitSubscribeForm(limitedToken);
|
|
195
|
-
assert.equal(limitedResponse.status, 303, 'rate-limited submit should redirect');
|
|
196
|
-
assert.match(limitedResponse.headers.get('location') ?? '', /\/\?formStatus=error&formCode=rate_limited#site-subscribe$/, 'local KV-backed rate limiting should reject the fourth submission');
|
|
197
|
-
}
|
|
198
|
-
finally {
|
|
199
|
-
await teardown();
|
|
200
|
-
}
|
|
201
|
-
if (shouldQuerySubscribers) {
|
|
202
|
-
const rows = await querySubscribers();
|
|
203
|
-
assert.equal(rows.length, 1, 'subscriber should be written to local D1');
|
|
204
|
-
assert.equal(rows[0].email, TEST_EMAIL, 'subscriber email should match the submitted address');
|
|
205
|
-
assert.equal(rows[0].status, 'active', 'subscriber should be active');
|
|
206
|
-
assert.equal(rows[0].source, 'footer', 'subscriber source should be tracked');
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
main().catch((error) => {
|
|
210
|
-
console.error(error);
|
|
211
|
-
process.exit(1);
|
|
212
|
-
});
|
|
@@ -1,297 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { cpSync, existsSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, rmSync, symlinkSync, writeFileSync } from 'node:fs';
|
|
3
|
-
import { tmpdir } from 'node:os';
|
|
4
|
-
import { dirname, join, resolve } from 'node:path';
|
|
5
|
-
import { spawnSync } from 'node:child_process';
|
|
6
|
-
import { agentPackageRoot, corePackageRoot, packageRoot, packageScriptPath, sdkPackageRoot } from './package-tools.js';
|
|
7
|
-
import { validateAllTemplateDefinitions } from './template-registry-lib.js';
|
|
8
|
-
const npmCacheDir = process.env.TREESEED_SCAFFOLD_NPM_CACHE_DIR
|
|
9
|
-
? resolve(process.env.TREESEED_SCAFFOLD_NPM_CACHE_DIR)
|
|
10
|
-
: resolve(tmpdir(), 'treeseed-npm-cache');
|
|
11
|
-
const packageJson = JSON.parse(readFileSync(resolve(corePackageRoot, 'package.json'), 'utf8'));
|
|
12
|
-
const sdkPackageJson = JSON.parse(readFileSync(resolve(sdkPackageRoot, 'package.json'), 'utf8'));
|
|
13
|
-
const cliPackageJson = JSON.parse(readFileSync(resolve(packageRoot, 'package.json'), 'utf8'));
|
|
14
|
-
const agentPackageJson = JSON.parse(readFileSync(resolve(agentPackageRoot, 'package.json'), 'utf8'));
|
|
15
|
-
const workspaceTarballs = (() => {
|
|
16
|
-
try {
|
|
17
|
-
return JSON.parse(process.env.TREESEED_WORKSPACE_TARBALLS ?? '{}');
|
|
18
|
-
}
|
|
19
|
-
catch {
|
|
20
|
-
return {};
|
|
21
|
-
}
|
|
22
|
-
})();
|
|
23
|
-
const externalCoreTarball = process.env.TREESEED_SCAFFOLD_CORE_TARBALL
|
|
24
|
-
? resolve(process.env.TREESEED_SCAFFOLD_CORE_TARBALL)
|
|
25
|
-
: typeof workspaceTarballs['@treeseed/core'] === 'string'
|
|
26
|
-
? resolve(workspaceTarballs['@treeseed/core'])
|
|
27
|
-
: null;
|
|
28
|
-
const externalSdkTarball = process.env.TREESEED_SCAFFOLD_SDK_TARBALL
|
|
29
|
-
? resolve(process.env.TREESEED_SCAFFOLD_SDK_TARBALL)
|
|
30
|
-
: typeof workspaceTarballs['@treeseed/sdk'] === 'string'
|
|
31
|
-
? resolve(workspaceTarballs['@treeseed/sdk'])
|
|
32
|
-
: null;
|
|
33
|
-
const externalCliTarball = process.env.TREESEED_SCAFFOLD_CLI_TARBALL
|
|
34
|
-
? resolve(process.env.TREESEED_SCAFFOLD_CLI_TARBALL)
|
|
35
|
-
: typeof workspaceTarballs['@treeseed/cli'] === 'string'
|
|
36
|
-
? resolve(workspaceTarballs['@treeseed/cli'])
|
|
37
|
-
: null;
|
|
38
|
-
const externalAgentTarball = process.env.TREESEED_SCAFFOLD_AGENT_TARBALL
|
|
39
|
-
? resolve(process.env.TREESEED_SCAFFOLD_AGENT_TARBALL)
|
|
40
|
-
: typeof workspaceTarballs['@treeseed/agent'] === 'string'
|
|
41
|
-
? resolve(workspaceTarballs['@treeseed/agent'])
|
|
42
|
-
: null;
|
|
43
|
-
const reusesExternalTarballs = Boolean(externalCoreTarball || externalSdkTarball || externalCliTarball || externalAgentTarball);
|
|
44
|
-
const scaffoldChecks = new Set((process.env.TREESEED_SCAFFOLD_CHECKS ?? 'build,deploy')
|
|
45
|
-
.split(',')
|
|
46
|
-
.map((value) => value.trim())
|
|
47
|
-
.filter(Boolean));
|
|
48
|
-
const timings = [];
|
|
49
|
-
const resetScaffoldCache = process.env.TREESEED_SCAFFOLD_RESET_CACHE === '1';
|
|
50
|
-
const scaffoldTempRoot = resolve(process.env.TREESEED_SCAFFOLD_TEMP_ROOT ?? resolve(packageRoot, '.local', 'tmp'));
|
|
51
|
-
mkdirSync(scaffoldTempRoot, { recursive: true });
|
|
52
|
-
function logStep(message) {
|
|
53
|
-
console.log(`[treeseed:test-scaffold] ${message}`);
|
|
54
|
-
}
|
|
55
|
-
function withTiming(label, action) {
|
|
56
|
-
const startedAt = Date.now();
|
|
57
|
-
logStep(`${label} started`);
|
|
58
|
-
try {
|
|
59
|
-
const result = action();
|
|
60
|
-
const durationMs = Date.now() - startedAt;
|
|
61
|
-
timings.push({ label, durationMs, status: 'completed' });
|
|
62
|
-
logStep(`${label} completed in ${(durationMs / 1000).toFixed(1)}s`);
|
|
63
|
-
return result;
|
|
64
|
-
}
|
|
65
|
-
catch (error) {
|
|
66
|
-
const durationMs = Date.now() - startedAt;
|
|
67
|
-
timings.push({ label, durationMs, status: 'failed' });
|
|
68
|
-
logStep(`${label} failed in ${(durationMs / 1000).toFixed(1)}s`);
|
|
69
|
-
throw error;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
function printSummary() {
|
|
73
|
-
if (timings.length === 0) {
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
console.log('[treeseed:test-scaffold] Stage summary');
|
|
77
|
-
for (const entry of timings) {
|
|
78
|
-
console.log(`[treeseed:test-scaffold] ${entry.status === 'completed' ? 'ok ' : 'fail'} ${entry.label} (${(entry.durationMs / 1000).toFixed(1)}s)`);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
function resetNpmCache() {
|
|
82
|
-
rmSync(npmCacheDir, { recursive: true, force: true });
|
|
83
|
-
}
|
|
84
|
-
function runStep(command, args, { cwd = packageRoot, env = {}, capture = false } = {}) {
|
|
85
|
-
const result = spawnSync(command, args, {
|
|
86
|
-
cwd,
|
|
87
|
-
env: { ...process.env, ...env },
|
|
88
|
-
stdio: capture ? 'pipe' : 'inherit',
|
|
89
|
-
encoding: 'utf8',
|
|
90
|
-
});
|
|
91
|
-
if (result.status !== 0) {
|
|
92
|
-
const message = capture ? (result.stderr?.trim() || result.stdout?.trim() || `${command} ${args.join(' ')} failed`) : `${command} ${args.join(' ')} failed`;
|
|
93
|
-
throw new Error(message);
|
|
94
|
-
}
|
|
95
|
-
return result;
|
|
96
|
-
}
|
|
97
|
-
function createTempSiteRoot() {
|
|
98
|
-
return mkdtempSync(join(scaffoldTempRoot, 'treeseed-scaffold-'));
|
|
99
|
-
}
|
|
100
|
-
function rewriteScaffoldDependency(siteRoot, tarballPath, cliTarballPath) {
|
|
101
|
-
const packageJsonPath = resolve(siteRoot, 'package.json');
|
|
102
|
-
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
|
|
103
|
-
packageJson.dependencies = packageJson.dependencies ?? {};
|
|
104
|
-
packageJson.dependencies['@treeseed/core'] = tarballPath;
|
|
105
|
-
packageJson.dependencies['@treeseed/cli'] = cliTarballPath;
|
|
106
|
-
writeFileSync(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\n`, 'utf8');
|
|
107
|
-
}
|
|
108
|
-
function installManualPackageTarball(siteRoot, tarballPath, packageName) {
|
|
109
|
-
const extractRoot = mkdtempSync(join(scaffoldTempRoot, 'treeseed-scaffold-package-'));
|
|
110
|
-
try {
|
|
111
|
-
runStep('tar', ['-xzf', tarballPath, '-C', extractRoot]);
|
|
112
|
-
const scopeRoot = resolve(siteRoot, 'node_modules', '@treeseed');
|
|
113
|
-
mkdirSync(scopeRoot, { recursive: true });
|
|
114
|
-
cpSync(resolve(extractRoot, 'package'), resolve(scopeRoot, packageName.split('/')[1]), { recursive: true });
|
|
115
|
-
}
|
|
116
|
-
finally {
|
|
117
|
-
rmSync(extractRoot, { recursive: true, force: true });
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
function linkWorkspacePackage(siteRoot, packageName, sourceRoot) {
|
|
121
|
-
const scopeRoot = resolve(siteRoot, 'node_modules', '@treeseed');
|
|
122
|
-
mkdirSync(scopeRoot, { recursive: true });
|
|
123
|
-
symlinkSync(sourceRoot, resolve(scopeRoot, packageName.split('/')[1]), 'dir');
|
|
124
|
-
}
|
|
125
|
-
function resolveSharedNodeModulesRoot() {
|
|
126
|
-
let current = packageRoot;
|
|
127
|
-
while (true) {
|
|
128
|
-
const candidate = resolve(current, 'node_modules');
|
|
129
|
-
if (existsSync(candidate)) {
|
|
130
|
-
return candidate;
|
|
131
|
-
}
|
|
132
|
-
const parent = resolve(current, '..');
|
|
133
|
-
if (parent === current) {
|
|
134
|
-
break;
|
|
135
|
-
}
|
|
136
|
-
current = parent;
|
|
137
|
-
}
|
|
138
|
-
throw new Error(`Unable to locate a shared node_modules directory for ${packageRoot}.`);
|
|
139
|
-
}
|
|
140
|
-
function mirrorSharedNodeModules(siteRoot) {
|
|
141
|
-
const sharedNodeModules = resolveSharedNodeModulesRoot();
|
|
142
|
-
for (const entry of readdirSync(sharedNodeModules, { withFileTypes: true })) {
|
|
143
|
-
if (entry.name === '.bin' || entry.name === '@treeseed' || entry.name === '@astrojs') {
|
|
144
|
-
continue;
|
|
145
|
-
}
|
|
146
|
-
const sourcePath = resolve(sharedNodeModules, entry.name);
|
|
147
|
-
const targetPath = resolve(siteRoot, 'node_modules', entry.name);
|
|
148
|
-
mkdirSync(dirname(targetPath), { recursive: true });
|
|
149
|
-
if (entry.name === 'astro') {
|
|
150
|
-
cpSync(sourcePath, targetPath, { recursive: true });
|
|
151
|
-
continue;
|
|
152
|
-
}
|
|
153
|
-
symlinkSync(sourcePath, targetPath, 'dir');
|
|
154
|
-
}
|
|
155
|
-
const sharedAstroScope = resolve(sharedNodeModules, '@astrojs');
|
|
156
|
-
const targetAstroScope = resolve(siteRoot, 'node_modules', '@astrojs');
|
|
157
|
-
mkdirSync(targetAstroScope, { recursive: true });
|
|
158
|
-
for (const packageName of ['cloudflare', 'mdx', 'sitemap', 'starlight']) {
|
|
159
|
-
cpSync(resolve(sharedAstroScope, packageName), resolve(targetAstroScope, packageName), { recursive: true });
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
function linkTreeseedBins(siteRoot) {
|
|
163
|
-
const binRoot = resolve(siteRoot, 'node_modules', '.bin');
|
|
164
|
-
mkdirSync(binRoot, { recursive: true });
|
|
165
|
-
for (const [name, relativeTarget] of [
|
|
166
|
-
['treeseed', '../@treeseed/cli/dist/cli/main.js'],
|
|
167
|
-
['treeseed-agents', '../@treeseed/agent/dist/scripts/treeseed-agents.js'],
|
|
168
|
-
]) {
|
|
169
|
-
symlinkSync(relativeTarget, resolve(binRoot, name));
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
function createTarball(root, pkg) {
|
|
173
|
-
return withTiming(`${pkg.name} build+pack`, () => {
|
|
174
|
-
runStep('npm', ['run', 'build:dist'], { cwd: root });
|
|
175
|
-
const output = runStep('npm', ['pack', '--silent', '--ignore-scripts', '--cache', npmCacheDir], {
|
|
176
|
-
cwd: root,
|
|
177
|
-
capture: true,
|
|
178
|
-
env: {
|
|
179
|
-
npm_config_cache: npmCacheDir,
|
|
180
|
-
NPM_CONFIG_CACHE: npmCacheDir,
|
|
181
|
-
},
|
|
182
|
-
});
|
|
183
|
-
const filename = output.stdout
|
|
184
|
-
.split('\n')
|
|
185
|
-
.map((line) => line.trim())
|
|
186
|
-
.filter(Boolean)
|
|
187
|
-
.at(-1)
|
|
188
|
-
|| `${pkg.name.replace(/^@/, '').replaceAll('/', '-')}-${pkg.version}.tgz`;
|
|
189
|
-
return resolve(root, filename);
|
|
190
|
-
});
|
|
191
|
-
}
|
|
192
|
-
function scaffoldSite(siteRoot) {
|
|
193
|
-
validateAllTemplateDefinitions();
|
|
194
|
-
runStep(process.execPath, [packageScriptPath('scaffold-site'), siteRoot, '--template', 'starter-basic', '--name', 'Smoke Site', '--site-url', 'https://smoke.example.com', '--contact-email', 'hello@example.com']);
|
|
195
|
-
}
|
|
196
|
-
function installScaffold(siteRoot, { coreTarballPath, sdkTarballPath, cliTarballPath, agentTarballPath }) {
|
|
197
|
-
if (coreTarballPath && sdkTarballPath && cliTarballPath && agentTarballPath) {
|
|
198
|
-
linkWorkspacePackage(siteRoot, sdkPackageJson.name, sdkPackageRoot);
|
|
199
|
-
linkWorkspacePackage(siteRoot, packageJson.name, corePackageRoot);
|
|
200
|
-
linkWorkspacePackage(siteRoot, cliPackageJson.name, packageRoot);
|
|
201
|
-
linkWorkspacePackage(siteRoot, agentPackageJson.name, agentPackageRoot);
|
|
202
|
-
mirrorSharedNodeModules(siteRoot);
|
|
203
|
-
linkTreeseedBins(siteRoot);
|
|
204
|
-
return;
|
|
205
|
-
}
|
|
206
|
-
runStep('npm', ['install', '--cache', npmCacheDir, '--prefer-offline', '--no-audit', '--no-fund'], {
|
|
207
|
-
cwd: siteRoot,
|
|
208
|
-
env: {
|
|
209
|
-
npm_config_cache: npmCacheDir,
|
|
210
|
-
NPM_CONFIG_CACHE: npmCacheDir,
|
|
211
|
-
npm_config_prefer_offline: 'true',
|
|
212
|
-
npm_config_audit: 'false',
|
|
213
|
-
npm_config_fund: 'false',
|
|
214
|
-
},
|
|
215
|
-
});
|
|
216
|
-
}
|
|
217
|
-
function runScaffoldChecks(siteRoot) {
|
|
218
|
-
if (scaffoldChecks.has('build')) {
|
|
219
|
-
withTiming('scaffold build', () => {
|
|
220
|
-
runStep('npm', ['run', 'build'], { cwd: siteRoot });
|
|
221
|
-
});
|
|
222
|
-
}
|
|
223
|
-
if (scaffoldChecks.has('deploy')) {
|
|
224
|
-
withTiming('scaffold deploy dry-run', () => {
|
|
225
|
-
runStep('npm', ['run', 'deploy', '--', '--dry-run'], { cwd: siteRoot });
|
|
226
|
-
});
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
const siteRoot = createTempSiteRoot();
|
|
230
|
-
let tarballPath = externalCoreTarball;
|
|
231
|
-
let sdkTarballPath = externalSdkTarball;
|
|
232
|
-
let cliTarballPath = externalCliTarball;
|
|
233
|
-
let agentTarballPath = externalAgentTarball;
|
|
234
|
-
try {
|
|
235
|
-
if (!reusesExternalTarballs && resetScaffoldCache) {
|
|
236
|
-
logStep(`resetting npm cache at ${npmCacheDir}`);
|
|
237
|
-
resetNpmCache();
|
|
238
|
-
}
|
|
239
|
-
if (!sdkTarballPath) {
|
|
240
|
-
logStep('building and packing @treeseed/sdk');
|
|
241
|
-
sdkTarballPath = createTarball(sdkPackageRoot, sdkPackageJson);
|
|
242
|
-
}
|
|
243
|
-
else {
|
|
244
|
-
logStep(`reusing provided @treeseed/sdk tarball: ${sdkTarballPath}`);
|
|
245
|
-
}
|
|
246
|
-
if (!tarballPath) {
|
|
247
|
-
logStep('building and packing @treeseed/core');
|
|
248
|
-
tarballPath = createTarball(corePackageRoot, packageJson);
|
|
249
|
-
}
|
|
250
|
-
else {
|
|
251
|
-
logStep(`reusing provided @treeseed/core tarball: ${tarballPath}`);
|
|
252
|
-
}
|
|
253
|
-
if (!agentTarballPath) {
|
|
254
|
-
logStep('building and packing @treeseed/agent');
|
|
255
|
-
agentTarballPath = createTarball(agentPackageRoot, agentPackageJson);
|
|
256
|
-
}
|
|
257
|
-
else {
|
|
258
|
-
logStep(`reusing provided @treeseed/agent tarball: ${agentTarballPath}`);
|
|
259
|
-
}
|
|
260
|
-
if (!cliTarballPath) {
|
|
261
|
-
logStep('building and packing @treeseed/cli');
|
|
262
|
-
cliTarballPath = createTarball(packageRoot, cliPackageJson);
|
|
263
|
-
}
|
|
264
|
-
else {
|
|
265
|
-
logStep(`reusing provided @treeseed/cli tarball: ${cliTarballPath}`);
|
|
266
|
-
}
|
|
267
|
-
logStep(`scaffolding temporary tenant at ${siteRoot}`);
|
|
268
|
-
withTiming('scaffold tenant generation', () => {
|
|
269
|
-
scaffoldSite(siteRoot);
|
|
270
|
-
});
|
|
271
|
-
rewriteScaffoldDependency(siteRoot, tarballPath, cliTarballPath);
|
|
272
|
-
logStep(`installing scaffolded tenant dependencies with checks: ${[...scaffoldChecks].join(', ') || 'none'}`);
|
|
273
|
-
withTiming('scaffold dependency install', () => {
|
|
274
|
-
installScaffold(siteRoot, {
|
|
275
|
-
coreTarballPath: tarballPath,
|
|
276
|
-
sdkTarballPath,
|
|
277
|
-
cliTarballPath,
|
|
278
|
-
agentTarballPath,
|
|
279
|
-
});
|
|
280
|
-
});
|
|
281
|
-
logStep('running scaffold smoke checks');
|
|
282
|
-
runScaffoldChecks(siteRoot);
|
|
283
|
-
console.log(`Scaffold smoke test passed in ${dirname(siteRoot) ? siteRoot : '.'}`);
|
|
284
|
-
}
|
|
285
|
-
finally {
|
|
286
|
-
printSummary();
|
|
287
|
-
rmSync(siteRoot, { recursive: true, force: true });
|
|
288
|
-
if (sdkTarballPath && !externalSdkTarball) {
|
|
289
|
-
rmSync(sdkTarballPath, { force: true });
|
|
290
|
-
}
|
|
291
|
-
if (tarballPath && !externalCoreTarball) {
|
|
292
|
-
rmSync(tarballPath, { force: true });
|
|
293
|
-
}
|
|
294
|
-
if (!reusesExternalTarballs && resetScaffoldCache) {
|
|
295
|
-
resetNpmCache();
|
|
296
|
-
}
|
|
297
|
-
}
|
package/dist/scripts/treeseed.js
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
export declare function isEditablePackageWorkspace(): boolean;
|
|
2
|
-
export declare function workspaceSdkRoot(): string | null;
|
|
3
|
-
export declare function writeDevReloadStamp(projectRoot: any): void;
|
|
4
|
-
export declare function readDevReloadState(projectRoot: any): any;
|
|
5
|
-
export declare function createTenantWatchEntries(tenantRoot: any): {
|
|
6
|
-
kind: string;
|
|
7
|
-
root: string;
|
|
8
|
-
}[];
|
|
9
|
-
export declare function startPollingWatch({ watchEntries, onChange }: {
|
|
10
|
-
watchEntries: any;
|
|
11
|
-
onChange: any;
|
|
12
|
-
}): () => void;
|
|
13
|
-
export declare function stopManagedProcess(child: any, signal?: string): Promise<void>;
|
|
14
|
-
export declare function createWatchBuildPaths(projectRoot: any): {
|
|
15
|
-
watchRoot: string;
|
|
16
|
-
stagedDistRoot: string;
|
|
17
|
-
backupDistRoot: string;
|
|
18
|
-
liveDistRoot: string;
|
|
19
|
-
};
|
|
20
|
-
export declare function swapStagedBuildOutput(projectRoot: any): void;
|
|
21
|
-
export declare function clearStagedBuildOutput(projectRoot: any): void;
|