create-specra 0.2.0 → 0.2.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 +11 -14
- package/dist/api-client-4XZPF7GL.js +15 -0
- package/dist/api-client-4XZPF7GL.js.map +1 -0
- package/dist/chunk-ATRUBLRX.js +102 -0
- package/dist/chunk-ATRUBLRX.js.map +1 -0
- package/dist/{chunk-MA7QG54W.js → chunk-CIM73PDF.js} +22 -2
- package/dist/chunk-CIM73PDF.js.map +1 -0
- package/dist/cli.js +17 -10
- package/dist/cli.js.map +1 -1
- package/dist/{deploy-SCEMUQNS.js → deploy-ETWFNB4Z.js} +48 -36
- package/dist/deploy-ETWFNB4Z.js.map +1 -0
- package/dist/doctor-IFELWGQB.js +270 -0
- package/dist/doctor-IFELWGQB.js.map +1 -0
- package/dist/index.js +43 -9
- package/dist/index.js.map +1 -1
- package/dist/{login-NKDRQXRE.js → login-DRPP77G4.js} +24 -9
- package/dist/login-DRPP77G4.js.map +1 -0
- package/dist/logout-UJFYUAQC.js +24 -0
- package/dist/logout-UJFYUAQC.js.map +1 -0
- package/dist/{logs-YDAUCMAV.js → logs-K2CSCKOE.js} +26 -20
- package/dist/logs-K2CSCKOE.js.map +1 -0
- package/dist/{projects-3TAY7EDJ.js → projects-NORBBC4D.js} +13 -6
- package/dist/projects-NORBBC4D.js.map +1 -0
- package/package.json +5 -3
- package/templates/book-docs/package.json +1 -1
- package/templates/book-docs/src/app.css +5 -0
- package/templates/book-docs/src/app.html +5 -0
- package/templates/jbrains-docs/package.json +1 -1
- package/templates/jbrains-docs/src/app.css +5 -0
- package/templates/jbrains-docs/src/app.html +5 -0
- package/templates/minimal/package.json +2 -2
- package/templates/minimal/specra.config.json +13 -1
- package/templates/minimal/src/app.css +5 -0
- package/templates/minimal/src/app.html +5 -0
- package/templates/minimal/src/hooks.server.ts +8 -0
- package/templates/minimal/src/routes/+error.svelte +10 -0
- package/templates/minimal/src/routes/+layout.server.ts +3 -0
- package/templates/minimal/src/routes/+page.svelte +149 -0
- package/templates/minimal/src/routes/docs/[version]/+page.server.ts +14 -0
- package/templates/minimal/src/routes/docs/[version]/[...slug]/+page.server.ts +1 -1
- package/templates/minimal/svelte.config.js +6 -1
- package/dist/chunk-3DKWECRK.js +0 -45
- package/dist/chunk-3DKWECRK.js.map +0 -1
- package/dist/chunk-MA7QG54W.js.map +0 -1
- package/dist/deploy-SCEMUQNS.js.map +0 -1
- package/dist/login-NKDRQXRE.js.map +0 -1
- package/dist/logout-H543QEKU.js +0 -20
- package/dist/logout-H543QEKU.js.map +0 -1
- package/dist/logs-YDAUCMAV.js.map +0 -1
- package/dist/projects-3TAY7EDJ.js.map +0 -1
- /package/templates/minimal/{public → static}/api-specs/openapi-example.json +0 -0
- /package/templates/minimal/{public → static}/api-specs/postman-example.json +0 -0
- /package/templates/minimal/{public → static}/api-specs/test-api.json +0 -0
- /package/templates/minimal/{public → static}/api-specs/users-api.json +0 -0
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
-
apiRequest
|
|
4
|
-
|
|
3
|
+
apiRequest,
|
|
4
|
+
formatError
|
|
5
|
+
} from "./chunk-CIM73PDF.js";
|
|
5
6
|
import {
|
|
6
7
|
isAuthenticated
|
|
7
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-ATRUBLRX.js";
|
|
8
9
|
|
|
9
10
|
// src/commands/logs.ts
|
|
10
11
|
import pc from "picocolors";
|
|
@@ -13,23 +14,28 @@ async function logs(projectId, options) {
|
|
|
13
14
|
console.error(pc.red("Not authenticated. Run `specra login` first."));
|
|
14
15
|
process.exit(1);
|
|
15
16
|
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
17
|
+
try {
|
|
18
|
+
if (options.deployment) {
|
|
19
|
+
const deploy = await apiRequest(
|
|
20
|
+
`/api/projects/${projectId}/deployments/${options.deployment}`
|
|
21
|
+
);
|
|
22
|
+
printDeployment(deploy);
|
|
23
|
+
} else {
|
|
24
|
+
const data = await apiRequest(
|
|
25
|
+
`/api/projects/${projectId}/deployments?limit=1`
|
|
26
|
+
);
|
|
27
|
+
if (data.deployments.length === 0) {
|
|
28
|
+
console.log(pc.yellow("No deployments found."));
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const deploy = await apiRequest(
|
|
32
|
+
`/api/projects/${projectId}/deployments/${data.deployments[0].id}`
|
|
33
|
+
);
|
|
34
|
+
printDeployment(deploy);
|
|
28
35
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
);
|
|
32
|
-
printDeployment(deploy);
|
|
36
|
+
} catch (err) {
|
|
37
|
+
console.error(pc.red(formatError("Failed to fetch logs", err)));
|
|
38
|
+
process.exit(1);
|
|
33
39
|
}
|
|
34
40
|
}
|
|
35
41
|
function printDeployment(deploy) {
|
|
@@ -68,4 +74,4 @@ function colorStatus(status) {
|
|
|
68
74
|
export {
|
|
69
75
|
logs
|
|
70
76
|
};
|
|
71
|
-
//# sourceMappingURL=logs-
|
|
77
|
+
//# sourceMappingURL=logs-K2CSCKOE.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/logs.ts"],"sourcesContent":["import pc from 'picocolors'\nimport { apiRequest, formatError } from '../api-client.js'\nimport { isAuthenticated } from '../config.js'\n\ninterface Deployment {\n id: string\n status: string\n buildLogs: string | null\n containerLogs?: string\n trigger: string\n createdAt: string\n}\n\ninterface DeploymentList {\n deployments: Deployment[]\n}\n\nexport async function logs(\n projectId: string,\n options: { deployment?: string }\n) {\n if (!isAuthenticated()) {\n console.error(pc.red('Not authenticated. Run `specra login` first.'))\n process.exit(1)\n }\n\n try {\n if (options.deployment) {\n // Get specific deployment\n const deploy = await apiRequest<Deployment>(\n `/api/projects/${projectId}/deployments/${options.deployment}`\n )\n\n printDeployment(deploy)\n } else {\n // Get latest deployment\n const data = await apiRequest<DeploymentList>(\n `/api/projects/${projectId}/deployments?limit=1`\n )\n\n if (data.deployments.length === 0) {\n console.log(pc.yellow('No deployments found.'))\n return\n }\n\n // Fetch full details with logs\n const deploy = await apiRequest<Deployment>(\n `/api/projects/${projectId}/deployments/${data.deployments[0].id}`\n )\n\n printDeployment(deploy)\n }\n } catch (err) {\n console.error(pc.red(formatError('Failed to fetch logs', err)))\n process.exit(1)\n }\n}\n\nfunction printDeployment(deploy: Deployment) {\n console.log(pc.bold(`Deployment ${deploy.id.slice(0, 8)}`))\n console.log(` Status: ${colorStatus(deploy.status)}`)\n console.log(` Trigger: ${deploy.trigger.toLowerCase()}`)\n console.log(` Created: ${new Date(deploy.createdAt).toLocaleString()}`)\n console.log()\n\n if (deploy.buildLogs) {\n console.log(pc.bold('Build Logs:'))\n console.log(pc.dim('─'.repeat(60)))\n console.log(deploy.buildLogs)\n console.log(pc.dim('─'.repeat(60)))\n }\n\n if (deploy.containerLogs) {\n console.log()\n console.log(pc.bold('Container Logs:'))\n console.log(pc.dim('─'.repeat(60)))\n console.log(deploy.containerLogs)\n console.log(pc.dim('─'.repeat(60)))\n }\n}\n\nfunction colorStatus(status: string) {\n switch (status) {\n case 'RUNNING':\n return pc.green(status.toLowerCase())\n case 'FAILED':\n return pc.red(status.toLowerCase())\n case 'BUILDING':\n case 'DEPLOYING':\n return pc.yellow(status.toLowerCase())\n default:\n return pc.dim(status.toLowerCase())\n }\n}\n"],"mappings":";;;;;;;;;;AAAA,OAAO,QAAQ;AAiBf,eAAsB,KACpB,WACA,SACA;AACA,MAAI,CAAC,gBAAgB,GAAG;AACtB,YAAQ,MAAM,GAAG,IAAI,8CAA8C,CAAC;AACpE,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI;AACF,QAAI,QAAQ,YAAY;AAEtB,YAAM,SAAS,MAAM;AAAA,QACnB,iBAAiB,SAAS,gBAAgB,QAAQ,UAAU;AAAA,MAC9D;AAEA,sBAAgB,MAAM;AAAA,IACxB,OAAO;AAEL,YAAM,OAAO,MAAM;AAAA,QACjB,iBAAiB,SAAS;AAAA,MAC5B;AAEA,UAAI,KAAK,YAAY,WAAW,GAAG;AACjC,gBAAQ,IAAI,GAAG,OAAO,uBAAuB,CAAC;AAC9C;AAAA,MACF;AAGA,YAAM,SAAS,MAAM;AAAA,QACnB,iBAAiB,SAAS,gBAAgB,KAAK,YAAY,CAAC,EAAE,EAAE;AAAA,MAClE;AAEA,sBAAgB,MAAM;AAAA,IACxB;AAAA,EACF,SAAS,KAAK;AACZ,YAAQ,MAAM,GAAG,IAAI,YAAY,wBAAwB,GAAG,CAAC,CAAC;AAC9D,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;AAEA,SAAS,gBAAgB,QAAoB;AAC3C,UAAQ,IAAI,GAAG,KAAK,cAAc,OAAO,GAAG,MAAM,GAAG,CAAC,CAAC,EAAE,CAAC;AAC1D,UAAQ,IAAI,cAAc,YAAY,OAAO,MAAM,CAAC,EAAE;AACtD,UAAQ,IAAI,cAAc,OAAO,QAAQ,YAAY,CAAC,EAAE;AACxD,UAAQ,IAAI,cAAc,IAAI,KAAK,OAAO,SAAS,EAAE,eAAe,CAAC,EAAE;AACvE,UAAQ,IAAI;AAEZ,MAAI,OAAO,WAAW;AACpB,YAAQ,IAAI,GAAG,KAAK,aAAa,CAAC;AAClC,YAAQ,IAAI,GAAG,IAAI,SAAI,OAAO,EAAE,CAAC,CAAC;AAClC,YAAQ,IAAI,OAAO,SAAS;AAC5B,YAAQ,IAAI,GAAG,IAAI,SAAI,OAAO,EAAE,CAAC,CAAC;AAAA,EACpC;AAEA,MAAI,OAAO,eAAe;AACxB,YAAQ,IAAI;AACZ,YAAQ,IAAI,GAAG,KAAK,iBAAiB,CAAC;AACtC,YAAQ,IAAI,GAAG,IAAI,SAAI,OAAO,EAAE,CAAC,CAAC;AAClC,YAAQ,IAAI,OAAO,aAAa;AAChC,YAAQ,IAAI,GAAG,IAAI,SAAI,OAAO,EAAE,CAAC,CAAC;AAAA,EACpC;AACF;AAEA,SAAS,YAAY,QAAgB;AACnC,UAAQ,QAAQ;AAAA,IACd,KAAK;AACH,aAAO,GAAG,MAAM,OAAO,YAAY,CAAC;AAAA,IACtC,KAAK;AACH,aAAO,GAAG,IAAI,OAAO,YAAY,CAAC;AAAA,IACpC,KAAK;AAAA,IACL,KAAK;AACH,aAAO,GAAG,OAAO,OAAO,YAAY,CAAC;AAAA,IACvC;AACE,aAAO,GAAG,IAAI,OAAO,YAAY,CAAC;AAAA,EACtC;AACF;","names":[]}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
-
apiRequest
|
|
4
|
-
|
|
3
|
+
apiRequest,
|
|
4
|
+
formatError
|
|
5
|
+
} from "./chunk-CIM73PDF.js";
|
|
5
6
|
import {
|
|
6
7
|
isAuthenticated
|
|
7
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-ATRUBLRX.js";
|
|
8
9
|
|
|
9
10
|
// src/commands/projects.ts
|
|
10
11
|
import pc from "picocolors";
|
|
@@ -13,11 +14,17 @@ async function projects() {
|
|
|
13
14
|
console.error(pc.red("Not authenticated. Run `specra login` first."));
|
|
14
15
|
process.exit(1);
|
|
15
16
|
}
|
|
16
|
-
|
|
17
|
+
let list;
|
|
18
|
+
try {
|
|
19
|
+
list = await apiRequest("/api/projects");
|
|
20
|
+
} catch (err) {
|
|
21
|
+
console.error(pc.red(formatError("Failed to fetch projects", err)));
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
17
24
|
if (list.length === 0) {
|
|
18
25
|
console.log(pc.yellow("No projects found."));
|
|
19
26
|
console.log(
|
|
20
|
-
`Create one at ${pc.dim("https://specra.
|
|
27
|
+
`Create one at ${pc.dim("https://specra-docs.com/dashboard/projects/new")}`
|
|
21
28
|
);
|
|
22
29
|
return;
|
|
23
30
|
}
|
|
@@ -39,4 +46,4 @@ async function projects() {
|
|
|
39
46
|
export {
|
|
40
47
|
projects
|
|
41
48
|
};
|
|
42
|
-
//# sourceMappingURL=projects-
|
|
49
|
+
//# sourceMappingURL=projects-NORBBC4D.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/projects.ts"],"sourcesContent":["import pc from 'picocolors'\nimport { apiRequest, formatError } from '../api-client.js'\nimport { isAuthenticated } from '../config.js'\n\ninterface Project {\n id: string\n name: string\n subdomain: string\n customDomain: string | null\n deployments: Array<{ status: string }>\n}\n\nexport async function projects() {\n if (!isAuthenticated()) {\n console.error(pc.red('Not authenticated. Run `specra login` first.'))\n process.exit(1)\n }\n\n let list: Project[]\n try {\n list = await apiRequest<Project[]>('/api/projects')\n } catch (err) {\n console.error(pc.red(formatError('Failed to fetch projects', err)))\n process.exit(1)\n }\n\n if (list.length === 0) {\n console.log(pc.yellow('No projects found.'))\n console.log(\n `Create one at ${pc.dim('https://specra-docs.com/dashboard/projects/new')}`\n )\n return\n }\n\n console.log(pc.bold(`Projects (${list.length}):`))\n console.log()\n\n for (const p of list) {\n const status = p.deployments[0]?.status || 'NOT_DEPLOYED'\n const statusColor =\n status === 'RUNNING'\n ? pc.green\n : status === 'FAILED'\n ? pc.red\n : pc.dim\n\n console.log(\n ` ${pc.bold(p.name)} ${pc.dim(`(${p.id.slice(0, 8)})`)} ${statusColor(status.toLowerCase())}`\n )\n console.log(` ${pc.dim(`${p.subdomain}.docs.specra.dev`)}`)\n if (p.customDomain) {\n console.log(` ${pc.dim(p.customDomain)}`)\n }\n console.log()\n }\n}\n"],"mappings":";;;;;;;;;;AAAA,OAAO,QAAQ;AAYf,eAAsB,WAAW;AAC/B,MAAI,CAAC,gBAAgB,GAAG;AACtB,YAAQ,MAAM,GAAG,IAAI,8CAA8C,CAAC;AACpE,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI;AACJ,MAAI;AACF,WAAO,MAAM,WAAsB,eAAe;AAAA,EACpD,SAAS,KAAK;AACZ,YAAQ,MAAM,GAAG,IAAI,YAAY,4BAA4B,GAAG,CAAC,CAAC;AAClE,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI,KAAK,WAAW,GAAG;AACrB,YAAQ,IAAI,GAAG,OAAO,oBAAoB,CAAC;AAC3C,YAAQ;AAAA,MACN,iBAAiB,GAAG,IAAI,gDAAgD,CAAC;AAAA,IAC3E;AACA;AAAA,EACF;AAEA,UAAQ,IAAI,GAAG,KAAK,aAAa,KAAK,MAAM,IAAI,CAAC;AACjD,UAAQ,IAAI;AAEZ,aAAW,KAAK,MAAM;AACpB,UAAM,SAAS,EAAE,YAAY,CAAC,GAAG,UAAU;AAC3C,UAAM,cACJ,WAAW,YACP,GAAG,QACH,WAAW,WACT,GAAG,MACH,GAAG;AAEX,YAAQ;AAAA,MACN,KAAK,GAAG,KAAK,EAAE,IAAI,CAAC,IAAI,GAAG,IAAI,IAAI,EAAE,GAAG,MAAM,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,YAAY,OAAO,YAAY,CAAC,CAAC;AAAA,IAC/F;AACA,YAAQ,IAAI,OAAO,GAAG,IAAI,GAAG,EAAE,SAAS,kBAAkB,CAAC,EAAE;AAC7D,QAAI,EAAE,cAAc;AAClB,cAAQ,IAAI,OAAO,GAAG,IAAI,EAAE,YAAY,CAAC,EAAE;AAAA,IAC7C;AACA,YAAQ,IAAI;AAAA,EACd;AACF;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-specra",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "CLI tool to scaffold a new Specra documentation site with Next.js",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -19,7 +19,8 @@
|
|
|
19
19
|
"keywords": [
|
|
20
20
|
"documentation",
|
|
21
21
|
"docs",
|
|
22
|
-
"
|
|
22
|
+
"sveltekit",
|
|
23
|
+
"svelte",
|
|
23
24
|
"mdx",
|
|
24
25
|
"specra",
|
|
25
26
|
"create-app",
|
|
@@ -28,8 +29,9 @@
|
|
|
28
29
|
"author": "dalmasonto, arthur-kamau",
|
|
29
30
|
"repository": {
|
|
30
31
|
"type": "git",
|
|
31
|
-
"url": "https://github.com/
|
|
32
|
+
"url": "https://github.com/SpecraDocs/specra-cli"
|
|
32
33
|
},
|
|
34
|
+
"homepage": "https://github.com/SpecraDocs",
|
|
33
35
|
"license": "SEE LICENSE IN LICENSE.MD",
|
|
34
36
|
"dependencies": {
|
|
35
37
|
"commander": "^12.1.0",
|
|
@@ -4,6 +4,11 @@
|
|
|
4
4
|
<meta charset="utf-8" />
|
|
5
5
|
<link rel="icon" href="%sveltekit.assets%/favicon.svg" type="image/svg+xml" />
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
7
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
8
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
9
|
+
<link
|
|
10
|
+
href="https://fonts.googleapis.com/css2?family=Geist+Mono:wght@100..900&family=Geist:wght@100..900&display=swap"
|
|
11
|
+
rel="stylesheet">
|
|
7
12
|
%sveltekit.head%
|
|
8
13
|
</head>
|
|
9
14
|
<body data-sveltekit-preload-data="hover" class="font-sans antialiased">
|
|
@@ -4,6 +4,11 @@
|
|
|
4
4
|
<meta charset="utf-8" />
|
|
5
5
|
<link rel="icon" href="%sveltekit.assets%/favicon.svg" type="image/svg+xml" />
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
7
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
8
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
9
|
+
<link
|
|
10
|
+
href="https://fonts.googleapis.com/css2?family=Geist+Mono:wght@100..900&family=Geist:wght@100..900&display=swap"
|
|
11
|
+
rel="stylesheet">
|
|
7
12
|
%sveltekit.head%
|
|
8
13
|
</head>
|
|
9
14
|
<body data-sveltekit-preload-data="hover" class="font-sans antialiased">
|
|
@@ -10,10 +10,10 @@
|
|
|
10
10
|
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json"
|
|
11
11
|
},
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"specra": "^0.2.
|
|
13
|
+
"specra": "^0.2.1"
|
|
14
14
|
},
|
|
15
15
|
"devDependencies": {
|
|
16
|
-
"@sveltejs/adapter-
|
|
16
|
+
"@sveltejs/adapter-static": "^3.0.0",
|
|
17
17
|
"@sveltejs/kit": "^2.0.0",
|
|
18
18
|
"@sveltejs/vite-plugin-svelte": "^6.0.0",
|
|
19
19
|
"@tailwindcss/postcss": "^4.1.9",
|
|
@@ -66,7 +66,19 @@
|
|
|
66
66
|
}
|
|
67
67
|
]
|
|
68
68
|
}
|
|
69
|
-
]
|
|
69
|
+
],
|
|
70
|
+
"branding": {
|
|
71
|
+
"showBranding": true,
|
|
72
|
+
"logo": "https://specra-docs.com/favicon.svg",
|
|
73
|
+
"title": "Specra",
|
|
74
|
+
"url": "https://specra-docs.com"
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
"banner": {
|
|
78
|
+
"enabled": false,
|
|
79
|
+
"message": "🎉 This is a development version. Some features may not work as expected.",
|
|
80
|
+
"type": "error",
|
|
81
|
+
"dismissible": true
|
|
70
82
|
},
|
|
71
83
|
"features": {
|
|
72
84
|
"showLastUpdated": true,
|
|
@@ -4,6 +4,11 @@
|
|
|
4
4
|
<meta charset="utf-8" />
|
|
5
5
|
<link rel="icon" href="%sveltekit.assets%/favicon.svg" type="image/svg+xml" />
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
7
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
8
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
9
|
+
<link
|
|
10
|
+
href="https://fonts.googleapis.com/css2?family=Geist+Mono:wght@100..900&family=Geist:wght@100..900&display=swap"
|
|
11
|
+
rel="stylesheet">
|
|
7
12
|
%sveltekit.head%
|
|
8
13
|
</head>
|
|
9
14
|
<body data-sveltekit-preload-data="hover" class="font-sans antialiased">
|
|
@@ -5,6 +5,9 @@ import type { SpecraConfig } from 'specra';
|
|
|
5
5
|
|
|
6
6
|
initConfig(specraConfig as unknown as Partial<SpecraConfig>);
|
|
7
7
|
|
|
8
|
+
export const prerender = true;
|
|
9
|
+
export const trailingSlash = 'always';
|
|
10
|
+
|
|
8
11
|
export const load: LayoutServerLoad = async () => {
|
|
9
12
|
const config = getConfig();
|
|
10
13
|
return { config };
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { ArrowRight, BookOpen, Zap, Code, Github, Twitter, Linkedin } from 'lucide-svelte';
|
|
3
|
+
import { Button, SiteBanner, Logo } from 'specra/components';
|
|
4
|
+
|
|
5
|
+
let { data } = $props();
|
|
6
|
+
const config = data.config;
|
|
7
|
+
const activeVersion = config.site.activeVersion || 'v1.0.0';
|
|
8
|
+
const docsUrl = `/docs/${activeVersion}/about`;
|
|
9
|
+
</script>
|
|
10
|
+
|
|
11
|
+
<svelte:head>
|
|
12
|
+
<title>{config.site.title}</title>
|
|
13
|
+
<meta name="description" content={config.site.description || 'Modern documentation platform'} />
|
|
14
|
+
</svelte:head>
|
|
15
|
+
|
|
16
|
+
<div class="min-h-screen bg-background">
|
|
17
|
+
<SiteBanner {config} />
|
|
18
|
+
<header class="border-b border-border">
|
|
19
|
+
<div class="container flex h-16 items-center justify-between px-6 mx-auto">
|
|
20
|
+
<a href="/" class="flex items-center gap-2">
|
|
21
|
+
<Logo logo={config.site.logo} alt={config.site.title} class="w-18 object-contain" />
|
|
22
|
+
<span class="font-semibold text-lg text-foreground">Specra</span>
|
|
23
|
+
</a>
|
|
24
|
+
<div class="flex items-center gap-6">
|
|
25
|
+
<a href={docsUrl} class="text-sm text-muted-foreground hover:text-foreground transition-colors">
|
|
26
|
+
Documentation
|
|
27
|
+
</a>
|
|
28
|
+
{#if config?.social?.github}
|
|
29
|
+
<a href={config.social.github} target="_blank" rel="noopener noreferrer" class="text-muted-foreground hover:text-foreground transition-colors">
|
|
30
|
+
<Github class="h-5 w-5" />
|
|
31
|
+
</a>
|
|
32
|
+
{/if}
|
|
33
|
+
<Button href={docsUrl}>Get Started</Button>
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
36
|
+
</header>
|
|
37
|
+
|
|
38
|
+
<main class="container px-6 mx-auto">
|
|
39
|
+
<!-- Hero Section -->
|
|
40
|
+
<div class="mx-auto text-center space-y-6 py-20 max-w-4xl">
|
|
41
|
+
<h1 class="text-5xl md:text-6xl font-bold tracking-tight text-foreground">
|
|
42
|
+
Beautiful documentation made <span class="text-primary">simple</span>
|
|
43
|
+
</h1>
|
|
44
|
+
<p class="text-xl md:text-2xl text-muted-foreground leading-relaxed max-w-3xl mx-auto">
|
|
45
|
+
The modern documentation framework that grows with your project
|
|
46
|
+
</p>
|
|
47
|
+
<p class="text-base md:text-lg text-muted-foreground leading-relaxed max-w-2xl mx-auto">
|
|
48
|
+
Built for developers who want powerful, flexible documentation without the complexity.
|
|
49
|
+
Write in MDX, configure with ease, and ship beautiful docs in minutes.
|
|
50
|
+
</p>
|
|
51
|
+
<div class="flex items-center justify-center gap-4 pt-4">
|
|
52
|
+
<Button href={docsUrl} size="lg">
|
|
53
|
+
Get Started
|
|
54
|
+
<ArrowRight class="ml-2 h-4 w-4" />
|
|
55
|
+
</Button>
|
|
56
|
+
{#if config?.social?.github}
|
|
57
|
+
<Button href={config.social.github} size="lg" variant="outline">
|
|
58
|
+
<Github class="mr-2 h-4 w-4" />
|
|
59
|
+
View on GitHub
|
|
60
|
+
</Button>
|
|
61
|
+
{/if}
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
64
|
+
|
|
65
|
+
<!-- Key Features -->
|
|
66
|
+
<div class="grid md:grid-cols-3 gap-6 py-16 max-w-6xl mx-auto">
|
|
67
|
+
<div class="p-6 rounded-lg border border-border bg-card hover:shadow-md transition-shadow">
|
|
68
|
+
<div class="h-12 w-12 rounded-lg bg-primary/10 flex items-center justify-center mb-4">
|
|
69
|
+
<BookOpen class="h-6 w-6 text-primary" />
|
|
70
|
+
</div>
|
|
71
|
+
<h3 class="text-lg font-semibold text-foreground mb-2">MDX-Powered</h3>
|
|
72
|
+
<p class="text-sm text-muted-foreground leading-relaxed">
|
|
73
|
+
Write your docs in MDX with support for custom components, code blocks with syntax highlighting, and rich interactive content.
|
|
74
|
+
</p>
|
|
75
|
+
</div>
|
|
76
|
+
|
|
77
|
+
<div class="p-6 rounded-lg border border-border bg-card hover:shadow-md transition-shadow">
|
|
78
|
+
<div class="h-12 w-12 rounded-lg bg-primary/10 flex items-center justify-center mb-4">
|
|
79
|
+
<Zap class="h-6 w-6 text-primary" />
|
|
80
|
+
</div>
|
|
81
|
+
<h3 class="text-lg font-semibold text-foreground mb-2">Lightning Fast</h3>
|
|
82
|
+
<p class="text-sm text-muted-foreground leading-relaxed">
|
|
83
|
+
Built on SvelteKit for optimal performance. Server-side rendering, instant page loads, and seamless navigation out of the box.
|
|
84
|
+
</p>
|
|
85
|
+
</div>
|
|
86
|
+
|
|
87
|
+
<div class="p-6 rounded-lg border border-border bg-card hover:shadow-md transition-shadow">
|
|
88
|
+
<div class="h-12 w-12 rounded-lg bg-primary/10 flex items-center justify-center mb-4">
|
|
89
|
+
<Code class="h-6 w-6 text-primary" />
|
|
90
|
+
</div>
|
|
91
|
+
<h3 class="text-lg font-semibold text-foreground mb-2">Developer First</h3>
|
|
92
|
+
<p class="text-sm text-muted-foreground leading-relaxed">
|
|
93
|
+
Version control friendly, Git-based workflow, and full TypeScript support. Your docs live alongside your code.
|
|
94
|
+
</p>
|
|
95
|
+
</div>
|
|
96
|
+
</div>
|
|
97
|
+
|
|
98
|
+
<!-- CTA Section -->
|
|
99
|
+
<div class="py-16 max-w-4xl mx-auto">
|
|
100
|
+
<div class="rounded-xl border border-border bg-card p-8 md:p-12 text-center space-y-6">
|
|
101
|
+
<h2 class="text-3xl md:text-4xl font-bold text-foreground">Ready to Get Started?</h2>
|
|
102
|
+
<p class="text-muted-foreground max-w-2xl mx-auto">
|
|
103
|
+
Create beautiful, versioned documentation for your project in minutes.<br />
|
|
104
|
+
Specra gives you everything you need to write, organize, and publish great docs.
|
|
105
|
+
</p>
|
|
106
|
+
<div class="flex flex-col sm:flex-row items-center justify-center gap-4 pt-2">
|
|
107
|
+
<Button href={docsUrl} size="lg" variant="default">
|
|
108
|
+
Read the Docs
|
|
109
|
+
<ArrowRight class="ml-2 h-4 w-4" />
|
|
110
|
+
</Button>
|
|
111
|
+
{#if config?.social?.github}
|
|
112
|
+
<Button href={config.social.github} size="lg" variant="outline">
|
|
113
|
+
<Github class="mr-2 h-4 w-4" />
|
|
114
|
+
Star on GitHub
|
|
115
|
+
</Button>
|
|
116
|
+
{/if}
|
|
117
|
+
</div>
|
|
118
|
+
</div>
|
|
119
|
+
</div>
|
|
120
|
+
|
|
121
|
+
<!-- Community Section -->
|
|
122
|
+
<div class="py-16 max-w-3xl mx-auto text-center space-y-6">
|
|
123
|
+
<h2 class="text-2xl md:text-3xl font-bold text-foreground">Join the Community</h2>
|
|
124
|
+
<p class="text-muted-foreground">
|
|
125
|
+
Specra is open source and community-driven. Connect with developers and stay updated.
|
|
126
|
+
</p>
|
|
127
|
+
<div class="flex items-center justify-center gap-4 pt-4">
|
|
128
|
+
{#if config?.social?.github}
|
|
129
|
+
<Button href={config.social.github} variant="outline" size="lg">
|
|
130
|
+
<Github class="mr-2 h-5 w-5" />
|
|
131
|
+
GitHub
|
|
132
|
+
</Button>
|
|
133
|
+
{/if}
|
|
134
|
+
{#if config?.social?.twitter}
|
|
135
|
+
<Button href={config.social.twitter} variant="outline" size="lg">
|
|
136
|
+
<Twitter class="mr-2 h-5 w-5" />
|
|
137
|
+
Twitter
|
|
138
|
+
</Button>
|
|
139
|
+
{/if}
|
|
140
|
+
{#if config?.social?.linkedin}
|
|
141
|
+
<Button href={config.social.linkedin} variant="outline" size="lg">
|
|
142
|
+
<Linkedin class="mr-2 h-5 w-5" />
|
|
143
|
+
LinkedIn
|
|
144
|
+
</Button>
|
|
145
|
+
{/if}
|
|
146
|
+
</div>
|
|
147
|
+
</div>
|
|
148
|
+
</main>
|
|
149
|
+
</div>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { redirect } from '@sveltejs/kit';
|
|
2
|
+
import { getCachedVersions, getCachedAllDocs } from 'specra';
|
|
3
|
+
import type { PageServerLoad } from './$types';
|
|
4
|
+
|
|
5
|
+
export const load: PageServerLoad = async ({ params }) => {
|
|
6
|
+
const { version } = params;
|
|
7
|
+
const docs = await getCachedAllDocs(version);
|
|
8
|
+
|
|
9
|
+
if (docs.length === 0) {
|
|
10
|
+
redirect(302, '/docs/v1.0.0');
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
redirect(302, `/docs/${version}/${docs[0].slug}`);
|
|
14
|
+
};
|
|
@@ -12,7 +12,7 @@ import type { PageServerLoad } from './$types';
|
|
|
12
12
|
|
|
13
13
|
export const load: PageServerLoad = async ({ params }) => {
|
|
14
14
|
const { version, slug: slugArray } = params;
|
|
15
|
-
const slug = slugArray;
|
|
15
|
+
const slug = slugArray.replace(/\/$/, '');
|
|
16
16
|
|
|
17
17
|
const i18nConfig = getI18nConfig();
|
|
18
18
|
const slugParts = slug.split('/');
|
|
@@ -1,8 +1,13 @@
|
|
|
1
|
+
import adapter from '@sveltejs/adapter-static';
|
|
1
2
|
import { specraConfig } from 'specra/svelte-config';
|
|
2
3
|
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
|
|
3
4
|
|
|
4
5
|
const config = specraConfig({
|
|
5
|
-
vitePreprocess: { vitePreprocess }
|
|
6
|
+
vitePreprocess: { vitePreprocess },
|
|
7
|
+
kit: {
|
|
8
|
+
adapter: adapter({ fallback: '404.html' }),
|
|
9
|
+
prerender: { handleHttpError: 'warn', handleMissingId: 'warn' }
|
|
10
|
+
}
|
|
6
11
|
});
|
|
7
12
|
|
|
8
13
|
export default config;
|
package/dist/chunk-3DKWECRK.js
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
// src/config.ts
|
|
4
|
-
import { readFileSync, writeFileSync, mkdirSync, existsSync, unlinkSync } from "fs";
|
|
5
|
-
import { join } from "path";
|
|
6
|
-
import { homedir } from "os";
|
|
7
|
-
var CONFIG_DIR = join(homedir(), ".specra");
|
|
8
|
-
var CONFIG_FILE = join(CONFIG_DIR, "config.json");
|
|
9
|
-
var DEFAULT_CONFIG = {
|
|
10
|
-
apiUrl: "https://specra.dev"
|
|
11
|
-
};
|
|
12
|
-
function getConfig() {
|
|
13
|
-
try {
|
|
14
|
-
const raw = readFileSync(CONFIG_FILE, "utf-8");
|
|
15
|
-
return { ...DEFAULT_CONFIG, ...JSON.parse(raw) };
|
|
16
|
-
} catch {
|
|
17
|
-
return DEFAULT_CONFIG;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
function saveConfig(config) {
|
|
21
|
-
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
22
|
-
const current = getConfig();
|
|
23
|
-
const merged = { ...current, ...config };
|
|
24
|
-
writeFileSync(CONFIG_FILE, JSON.stringify(merged, null, 2));
|
|
25
|
-
}
|
|
26
|
-
function clearConfig() {
|
|
27
|
-
if (existsSync(CONFIG_FILE)) {
|
|
28
|
-
unlinkSync(CONFIG_FILE);
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
function getToken() {
|
|
32
|
-
return getConfig().token;
|
|
33
|
-
}
|
|
34
|
-
function isAuthenticated() {
|
|
35
|
-
return !!getToken();
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export {
|
|
39
|
-
getConfig,
|
|
40
|
-
saveConfig,
|
|
41
|
-
clearConfig,
|
|
42
|
-
getToken,
|
|
43
|
-
isAuthenticated
|
|
44
|
-
};
|
|
45
|
-
//# sourceMappingURL=chunk-3DKWECRK.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/config.ts"],"sourcesContent":["import { readFileSync, writeFileSync, mkdirSync, existsSync, unlinkSync } from 'fs'\nimport { join } from 'path'\nimport { homedir } from 'os'\n\nconst CONFIG_DIR = join(homedir(), '.specra')\nconst CONFIG_FILE = join(CONFIG_DIR, 'config.json')\n\ninterface SpecraConfig {\n apiUrl: string\n token?: string\n defaultProject?: string\n}\n\nconst DEFAULT_CONFIG: SpecraConfig = {\n apiUrl: 'https://specra.dev',\n}\n\nexport function getConfig(): SpecraConfig {\n try {\n const raw = readFileSync(CONFIG_FILE, 'utf-8')\n return { ...DEFAULT_CONFIG, ...JSON.parse(raw) }\n } catch {\n return DEFAULT_CONFIG\n }\n}\n\nexport function saveConfig(config: Partial<SpecraConfig>) {\n mkdirSync(CONFIG_DIR, { recursive: true })\n const current = getConfig()\n const merged = { ...current, ...config }\n writeFileSync(CONFIG_FILE, JSON.stringify(merged, null, 2))\n}\n\nexport function clearConfig() {\n if (existsSync(CONFIG_FILE)) {\n unlinkSync(CONFIG_FILE)\n }\n}\n\nexport function getToken(): string | undefined {\n return getConfig().token\n}\n\nexport function isAuthenticated(): boolean {\n return !!getToken()\n}\n"],"mappings":";;;AAAA,SAAS,cAAc,eAAe,WAAW,YAAY,kBAAkB;AAC/E,SAAS,YAAY;AACrB,SAAS,eAAe;AAExB,IAAM,aAAa,KAAK,QAAQ,GAAG,SAAS;AAC5C,IAAM,cAAc,KAAK,YAAY,aAAa;AAQlD,IAAM,iBAA+B;AAAA,EACnC,QAAQ;AACV;AAEO,SAAS,YAA0B;AACxC,MAAI;AACF,UAAM,MAAM,aAAa,aAAa,OAAO;AAC7C,WAAO,EAAE,GAAG,gBAAgB,GAAG,KAAK,MAAM,GAAG,EAAE;AAAA,EACjD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,WAAW,QAA+B;AACxD,YAAU,YAAY,EAAE,WAAW,KAAK,CAAC;AACzC,QAAM,UAAU,UAAU;AAC1B,QAAM,SAAS,EAAE,GAAG,SAAS,GAAG,OAAO;AACvC,gBAAc,aAAa,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AAC5D;AAEO,SAAS,cAAc;AAC5B,MAAI,WAAW,WAAW,GAAG;AAC3B,eAAW,WAAW;AAAA,EACxB;AACF;AAEO,SAAS,WAA+B;AAC7C,SAAO,UAAU,EAAE;AACrB;AAEO,SAAS,kBAA2B;AACzC,SAAO,CAAC,CAAC,SAAS;AACpB;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/api-client.ts"],"sourcesContent":["import { getConfig, getToken } from './config.js'\n\nclass ApiError extends Error {\n constructor(public status: number, message: string) {\n super(message)\n this.name = 'ApiError'\n }\n}\n\nexport async function apiRequest<T = unknown>(\n path: string,\n options: RequestInit = {}\n): Promise<T> {\n const config = getConfig()\n const token = getToken()\n\n if (!token) {\n throw new Error('Not authenticated. Run `specra login` first.')\n }\n\n const url = `${config.apiUrl}${path}`\n const res = await fetch(url, {\n ...options,\n headers: {\n Authorization: `Bearer ${token}`,\n ...options.headers,\n },\n })\n\n if (!res.ok) {\n let message: string\n try {\n const data = (await res.json()) as Record<string, string>\n message = data.error || res.statusText\n } catch {\n message = res.statusText\n }\n throw new ApiError(res.status, message)\n }\n\n return res.json() as Promise<T>\n}\n\nexport async function apiUpload(\n path: string,\n body: Buffer | ReadableStream,\n headers: Record<string, string> = {}\n): Promise<unknown> {\n const config = getConfig()\n const token = getToken()\n\n if (!token) {\n throw new Error('Not authenticated. Run `specra login` first.')\n }\n\n const url = `${config.apiUrl}${path}`\n const res = await fetch(url, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/octet-stream',\n ...headers,\n },\n body,\n })\n\n if (!res.ok) {\n let message: string\n try {\n const data = (await res.json()) as Record<string, string>\n message = data.error || res.statusText\n } catch {\n message = res.statusText\n }\n throw new ApiError(res.status, message)\n }\n\n return res.json()\n}\n"],"mappings":";;;;;;;AAEA,IAAM,WAAN,cAAuB,MAAM;AAAA,EAC3B,YAAmB,QAAgB,SAAiB;AAClD,UAAM,OAAO;AADI;AAEjB,SAAK,OAAO;AAAA,EACd;AACF;AAEA,eAAsB,WACpB,MACA,UAAuB,CAAC,GACZ;AACZ,QAAM,SAAS,UAAU;AACzB,QAAM,QAAQ,SAAS;AAEvB,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,MAAM,8CAA8C;AAAA,EAChE;AAEA,QAAM,MAAM,GAAG,OAAO,MAAM,GAAG,IAAI;AACnC,QAAM,MAAM,MAAM,MAAM,KAAK;AAAA,IAC3B,GAAG;AAAA,IACH,SAAS;AAAA,MACP,eAAe,UAAU,KAAK;AAAA,MAC9B,GAAG,QAAQ;AAAA,IACb;AAAA,EACF,CAAC;AAED,MAAI,CAAC,IAAI,IAAI;AACX,QAAI;AACJ,QAAI;AACF,YAAM,OAAQ,MAAM,IAAI,KAAK;AAC7B,gBAAU,KAAK,SAAS,IAAI;AAAA,IAC9B,QAAQ;AACN,gBAAU,IAAI;AAAA,IAChB;AACA,UAAM,IAAI,SAAS,IAAI,QAAQ,OAAO;AAAA,EACxC;AAEA,SAAO,IAAI,KAAK;AAClB;AAEA,eAAsB,UACpB,MACA,MACA,UAAkC,CAAC,GACjB;AAClB,QAAM,SAAS,UAAU;AACzB,QAAM,QAAQ,SAAS;AAEvB,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,MAAM,8CAA8C;AAAA,EAChE;AAEA,QAAM,MAAM,GAAG,OAAO,MAAM,GAAG,IAAI;AACnC,QAAM,MAAM,MAAM,MAAM,KAAK;AAAA,IAC3B,QAAQ;AAAA,IACR,SAAS;AAAA,MACP,eAAe,UAAU,KAAK;AAAA,MAC9B,gBAAgB;AAAA,MAChB,GAAG;AAAA,IACL;AAAA,IACA;AAAA,EACF,CAAC;AAED,MAAI,CAAC,IAAI,IAAI;AACX,QAAI;AACJ,QAAI;AACF,YAAM,OAAQ,MAAM,IAAI,KAAK;AAC7B,gBAAU,KAAK,SAAS,IAAI;AAAA,IAC9B,QAAQ;AACN,gBAAU,IAAI;AAAA,IAChB;AACA,UAAM,IAAI,SAAS,IAAI,QAAQ,OAAO;AAAA,EACxC;AAEA,SAAO,IAAI,KAAK;AAClB;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/deploy.ts","../src/archive.ts"],"sourcesContent":["import pc from 'picocolors'\nimport ora from 'ora'\nimport { createArchive } from '../archive.js'\nimport { apiUpload, apiRequest } from '../api-client.js'\nimport { isAuthenticated, getConfig } from '../config.js'\nimport { existsSync, readFileSync } from 'fs'\nimport { join, resolve } from 'path'\n\ninterface DeployOptions {\n project?: string\n dir: string\n}\n\nexport async function deploy(options: DeployOptions) {\n if (!isAuthenticated()) {\n console.error(pc.red('Not authenticated. Run `specra login` first.'))\n process.exit(1)\n }\n\n const dir = resolve(options.dir)\n let projectId = options.project\n\n // If no project specified, try to read from specra.config.json\n if (!projectId) {\n const configPath = join(dir, 'specra.config.json')\n if (existsSync(configPath)) {\n try {\n const config = JSON.parse(readFileSync(configPath, 'utf-8'))\n projectId = config.projectId\n } catch {\n // ignore\n }\n }\n }\n\n if (!projectId) {\n // List projects and ask user to pick\n const projects = await apiRequest<Array<{ id: string; name: string; subdomain: string }>>(\n '/api/projects'\n )\n\n if (projects.length === 0) {\n console.error(\n pc.red('No projects found. Create one at https://specra.dev/dashboard/projects/new')\n )\n process.exit(1)\n }\n\n console.log(pc.bold('Select a project to deploy to:'))\n projects.forEach((p, i) => {\n console.log(` ${pc.dim(`${i + 1}.`)} ${p.name} ${pc.dim(`(${p.subdomain}.docs.specra.dev)`)}`)\n })\n\n const prompts = await import('prompts')\n const response = await prompts.default({\n type: 'select',\n name: 'project',\n message: 'Project',\n choices: projects.map((p) => ({\n title: p.name,\n value: p.id,\n description: `${p.subdomain}.docs.specra.dev`,\n })),\n })\n\n if (!response.project) {\n console.log('Aborted.')\n process.exit(1)\n }\n\n projectId = response.project\n }\n\n // Create archive\n const spinner = ora('Packaging docs...').start()\n\n try {\n const archive = await createArchive(dir)\n spinner.text = `Uploading (${(archive.length / 1024).toFixed(0)}KB)...`\n\n // Get git commit SHA if available\n let commitSha: string | undefined\n try {\n const { execSync } = await import('child_process')\n commitSha = execSync('git rev-parse HEAD', { cwd: dir })\n .toString()\n .trim()\n } catch {\n // not a git repo\n }\n\n const result = await apiUpload(\n `/api/projects/${projectId}/deploy`,\n archive,\n {\n 'X-Deploy-Trigger': 'CLI',\n ...(commitSha ? { 'X-Commit-Sha': commitSha } : {}),\n }\n ) as { deploymentId: string }\n\n spinner.succeed(pc.green('Deployment triggered!'))\n console.log()\n console.log(` Deployment ID: ${pc.cyan(result.deploymentId)}`)\n\n const config = getConfig()\n console.log(\n ` View status: ${pc.dim(`${config.apiUrl}/dashboard/projects/${projectId}/deployments`)}`\n )\n console.log()\n } catch (err) {\n spinner.fail(pc.red('Deploy failed'))\n console.error(err instanceof Error ? err.message : err)\n process.exit(1)\n }\n}\n","import * as tar from 'tar'\nimport { existsSync, statSync } from 'fs'\nimport { join, resolve } from 'path'\n\nexport async function createArchive(dir: string): Promise<Buffer> {\n const absDir = resolve(dir)\n\n if (!existsSync(absDir)) {\n throw new Error(`Directory not found: ${absDir}`)\n }\n\n if (!statSync(absDir).isDirectory()) {\n throw new Error(`Not a directory: ${absDir}`)\n }\n\n // Check for docs content - look for common indicators\n const hasPackageJson = existsSync(join(absDir, 'package.json'))\n const hasDocsDir = existsSync(join(absDir, 'docs'))\n const hasSpecraConfig = existsSync(join(absDir, 'specra.config.json'))\n\n if (!hasPackageJson && !hasDocsDir && !hasSpecraConfig) {\n throw new Error(\n 'No Specra project found. Ensure the directory contains package.json, docs/, or specra.config.json'\n )\n }\n\n // Build the file list — include relevant files, exclude node_modules/.next/.git\n const chunks: Buffer[] = []\n\n await tar.create(\n {\n gzip: true,\n cwd: absDir,\n filter: (path: string) => {\n if (path.includes('node_modules/')) return false\n if (path.includes('.next/')) return false\n if (path.includes('.git/')) return false\n if (path.includes('.env')) return false\n return true\n },\n },\n ['.']\n ).on('data', (chunk: Buffer) => {\n chunks.push(chunk)\n })\n\n // Wait for stream to finish\n return Buffer.concat(chunks)\n}\n"],"mappings":"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","names":["existsSync","join","resolve"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/login.ts"],"sourcesContent":["import pc from 'picocolors'\nimport open from 'open'\nimport { createServer } from 'http'\nimport { getConfig, saveConfig } from '../config.js'\nimport { apiRequest } from '../api-client.js'\nimport { randomBytes } from 'crypto'\n\nexport async function login() {\n console.log(pc.bold('Specra Login'))\n console.log()\n\n const state = randomBytes(16).toString('hex')\n const port = 9876\n const { apiUrl } = getConfig()\n\n return new Promise<void>((resolve) => {\n const server = createServer(async (req, res) => {\n const url = new URL(req.url!, `http://localhost:${port}`)\n\n // Backend redirects to root path with token as query param\n const token = url.searchParams.get('token')\n const returnedState = url.searchParams.get('state')\n\n if (token || returnedState) {\n if (returnedState !== state) {\n res.writeHead(400, { 'Content-Type': 'text/html' })\n res.end('<html><body><h1>State mismatch. Please try again.</h1></body></html>')\n server.close()\n resolve()\n return\n }\n\n if (token) {\n saveConfig({ token })\n\n // Verify the token works\n try {\n const user = await apiRequest<{ email: string }>('/api/auth/verify')\n console.log(pc.green(`Authenticated as ${user.email}`))\n } catch {\n console.log(pc.green('Token saved.'))\n }\n\n res.writeHead(200, { 'Content-Type': 'text/html' })\n res.end(\n '<html><body><h1>Authenticated!</h1><p>You can close this window and return to the terminal.</p></body></html>'\n )\n } else {\n res.writeHead(400, { 'Content-Type': 'text/html' })\n res.end('<html><body><h1>Authentication failed.</h1></body></html>')\n }\n\n server.close()\n resolve()\n }\n })\n\n server.listen(port, () => {\n const loginUrl = `${apiUrl}/auth/cli?port=${port}&state=${state}`\n console.log(`Opening browser to authenticate...`)\n console.log(pc.dim(`If the browser doesn't open, visit: ${loginUrl}`))\n console.log()\n open(loginUrl)\n })\n\n // Timeout after 5 minutes\n setTimeout(() => {\n console.log(pc.yellow('Login timed out.'))\n server.close()\n resolve()\n }, 300000)\n })\n}\n"],"mappings":";;;;;;;;;;AAAA,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,SAAS,oBAAoB;AAG7B,SAAS,mBAAmB;AAE5B,eAAsB,QAAQ;AAC5B,UAAQ,IAAI,GAAG,KAAK,cAAc,CAAC;AACnC,UAAQ,IAAI;AAEZ,QAAM,QAAQ,YAAY,EAAE,EAAE,SAAS,KAAK;AAC5C,QAAM,OAAO;AACb,QAAM,EAAE,OAAO,IAAI,UAAU;AAE7B,SAAO,IAAI,QAAc,CAAC,YAAY;AACpC,UAAM,SAAS,aAAa,OAAO,KAAK,QAAQ;AAC9C,YAAM,MAAM,IAAI,IAAI,IAAI,KAAM,oBAAoB,IAAI,EAAE;AAGxD,YAAM,QAAQ,IAAI,aAAa,IAAI,OAAO;AAC1C,YAAM,gBAAgB,IAAI,aAAa,IAAI,OAAO;AAElD,UAAI,SAAS,eAAe;AAC1B,YAAI,kBAAkB,OAAO;AAC3B,cAAI,UAAU,KAAK,EAAE,gBAAgB,YAAY,CAAC;AAClD,cAAI,IAAI,sEAAsE;AAC9E,iBAAO,MAAM;AACb,kBAAQ;AACR;AAAA,QACF;AAEA,YAAI,OAAO;AACT,qBAAW,EAAE,MAAM,CAAC;AAGpB,cAAI;AACF,kBAAM,OAAO,MAAM,WAA8B,kBAAkB;AACnE,oBAAQ,IAAI,GAAG,MAAM,oBAAoB,KAAK,KAAK,EAAE,CAAC;AAAA,UACxD,QAAQ;AACN,oBAAQ,IAAI,GAAG,MAAM,cAAc,CAAC;AAAA,UACtC;AAEA,cAAI,UAAU,KAAK,EAAE,gBAAgB,YAAY,CAAC;AAClD,cAAI;AAAA,YACF;AAAA,UACF;AAAA,QACF,OAAO;AACL,cAAI,UAAU,KAAK,EAAE,gBAAgB,YAAY,CAAC;AAClD,cAAI,IAAI,2DAA2D;AAAA,QACrE;AAEA,eAAO,MAAM;AACb,gBAAQ;AAAA,MACV;AAAA,IACF,CAAC;AAED,WAAO,OAAO,MAAM,MAAM;AACxB,YAAM,WAAW,GAAG,MAAM,kBAAkB,IAAI,UAAU,KAAK;AAC/D,cAAQ,IAAI,oCAAoC;AAChD,cAAQ,IAAI,GAAG,IAAI,uCAAuC,QAAQ,EAAE,CAAC;AACrE,cAAQ,IAAI;AACZ,WAAK,QAAQ;AAAA,IACf,CAAC;AAGD,eAAW,MAAM;AACf,cAAQ,IAAI,GAAG,OAAO,kBAAkB,CAAC;AACzC,aAAO,MAAM;AACb,cAAQ;AAAA,IACV,GAAG,GAAM;AAAA,EACX,CAAC;AACH;","names":[]}
|
package/dist/logout-H543QEKU.js
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
clearConfig,
|
|
4
|
-
isAuthenticated
|
|
5
|
-
} from "./chunk-3DKWECRK.js";
|
|
6
|
-
|
|
7
|
-
// src/commands/logout.ts
|
|
8
|
-
import pc from "picocolors";
|
|
9
|
-
async function logout() {
|
|
10
|
-
if (!isAuthenticated()) {
|
|
11
|
-
console.log(pc.yellow("Not currently logged in."));
|
|
12
|
-
return;
|
|
13
|
-
}
|
|
14
|
-
clearConfig();
|
|
15
|
-
console.log(pc.green("Logged out successfully."));
|
|
16
|
-
}
|
|
17
|
-
export {
|
|
18
|
-
logout
|
|
19
|
-
};
|
|
20
|
-
//# sourceMappingURL=logout-H543QEKU.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/logout.ts"],"sourcesContent":["import pc from 'picocolors'\nimport { clearConfig, isAuthenticated } from '../config.js'\n\nexport async function logout() {\n if (!isAuthenticated()) {\n console.log(pc.yellow('Not currently logged in.'))\n return\n }\n\n clearConfig()\n console.log(pc.green('Logged out successfully.'))\n}\n"],"mappings":";;;;;;;AAAA,OAAO,QAAQ;AAGf,eAAsB,SAAS;AAC7B,MAAI,CAAC,gBAAgB,GAAG;AACtB,YAAQ,IAAI,GAAG,OAAO,0BAA0B,CAAC;AACjD;AAAA,EACF;AAEA,cAAY;AACZ,UAAQ,IAAI,GAAG,MAAM,0BAA0B,CAAC;AAClD;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/logs.ts"],"sourcesContent":["import pc from 'picocolors'\nimport { apiRequest } from '../api-client.js'\nimport { isAuthenticated } from '../config.js'\n\ninterface Deployment {\n id: string\n status: string\n buildLogs: string | null\n containerLogs?: string\n trigger: string\n createdAt: string\n}\n\ninterface DeploymentList {\n deployments: Deployment[]\n}\n\nexport async function logs(\n projectId: string,\n options: { deployment?: string }\n) {\n if (!isAuthenticated()) {\n console.error(pc.red('Not authenticated. Run `specra login` first.'))\n process.exit(1)\n }\n\n if (options.deployment) {\n // Get specific deployment\n const deploy = await apiRequest<Deployment>(\n `/api/projects/${projectId}/deployments/${options.deployment}`\n )\n\n printDeployment(deploy)\n } else {\n // Get latest deployment\n const data = await apiRequest<DeploymentList>(\n `/api/projects/${projectId}/deployments?limit=1`\n )\n\n if (data.deployments.length === 0) {\n console.log(pc.yellow('No deployments found.'))\n return\n }\n\n // Fetch full details with logs\n const deploy = await apiRequest<Deployment>(\n `/api/projects/${projectId}/deployments/${data.deployments[0].id}`\n )\n\n printDeployment(deploy)\n }\n}\n\nfunction printDeployment(deploy: Deployment) {\n console.log(pc.bold(`Deployment ${deploy.id.slice(0, 8)}`))\n console.log(` Status: ${colorStatus(deploy.status)}`)\n console.log(` Trigger: ${deploy.trigger.toLowerCase()}`)\n console.log(` Created: ${new Date(deploy.createdAt).toLocaleString()}`)\n console.log()\n\n if (deploy.buildLogs) {\n console.log(pc.bold('Build Logs:'))\n console.log(pc.dim('─'.repeat(60)))\n console.log(deploy.buildLogs)\n console.log(pc.dim('─'.repeat(60)))\n }\n\n if (deploy.containerLogs) {\n console.log()\n console.log(pc.bold('Container Logs:'))\n console.log(pc.dim('─'.repeat(60)))\n console.log(deploy.containerLogs)\n console.log(pc.dim('─'.repeat(60)))\n }\n}\n\nfunction colorStatus(status: string) {\n switch (status) {\n case 'RUNNING':\n return pc.green(status.toLowerCase())\n case 'FAILED':\n return pc.red(status.toLowerCase())\n case 'BUILDING':\n case 'DEPLOYING':\n return pc.yellow(status.toLowerCase())\n default:\n return pc.dim(status.toLowerCase())\n }\n}\n"],"mappings":";;;;;;;;;AAAA,OAAO,QAAQ;AAiBf,eAAsB,KACpB,WACA,SACA;AACA,MAAI,CAAC,gBAAgB,GAAG;AACtB,YAAQ,MAAM,GAAG,IAAI,8CAA8C,CAAC;AACpE,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI,QAAQ,YAAY;AAEtB,UAAM,SAAS,MAAM;AAAA,MACnB,iBAAiB,SAAS,gBAAgB,QAAQ,UAAU;AAAA,IAC9D;AAEA,oBAAgB,MAAM;AAAA,EACxB,OAAO;AAEL,UAAM,OAAO,MAAM;AAAA,MACjB,iBAAiB,SAAS;AAAA,IAC5B;AAEA,QAAI,KAAK,YAAY,WAAW,GAAG;AACjC,cAAQ,IAAI,GAAG,OAAO,uBAAuB,CAAC;AAC9C;AAAA,IACF;AAGA,UAAM,SAAS,MAAM;AAAA,MACnB,iBAAiB,SAAS,gBAAgB,KAAK,YAAY,CAAC,EAAE,EAAE;AAAA,IAClE;AAEA,oBAAgB,MAAM;AAAA,EACxB;AACF;AAEA,SAAS,gBAAgB,QAAoB;AAC3C,UAAQ,IAAI,GAAG,KAAK,cAAc,OAAO,GAAG,MAAM,GAAG,CAAC,CAAC,EAAE,CAAC;AAC1D,UAAQ,IAAI,cAAc,YAAY,OAAO,MAAM,CAAC,EAAE;AACtD,UAAQ,IAAI,cAAc,OAAO,QAAQ,YAAY,CAAC,EAAE;AACxD,UAAQ,IAAI,cAAc,IAAI,KAAK,OAAO,SAAS,EAAE,eAAe,CAAC,EAAE;AACvE,UAAQ,IAAI;AAEZ,MAAI,OAAO,WAAW;AACpB,YAAQ,IAAI,GAAG,KAAK,aAAa,CAAC;AAClC,YAAQ,IAAI,GAAG,IAAI,SAAI,OAAO,EAAE,CAAC,CAAC;AAClC,YAAQ,IAAI,OAAO,SAAS;AAC5B,YAAQ,IAAI,GAAG,IAAI,SAAI,OAAO,EAAE,CAAC,CAAC;AAAA,EACpC;AAEA,MAAI,OAAO,eAAe;AACxB,YAAQ,IAAI;AACZ,YAAQ,IAAI,GAAG,KAAK,iBAAiB,CAAC;AACtC,YAAQ,IAAI,GAAG,IAAI,SAAI,OAAO,EAAE,CAAC,CAAC;AAClC,YAAQ,IAAI,OAAO,aAAa;AAChC,YAAQ,IAAI,GAAG,IAAI,SAAI,OAAO,EAAE,CAAC,CAAC;AAAA,EACpC;AACF;AAEA,SAAS,YAAY,QAAgB;AACnC,UAAQ,QAAQ;AAAA,IACd,KAAK;AACH,aAAO,GAAG,MAAM,OAAO,YAAY,CAAC;AAAA,IACtC,KAAK;AACH,aAAO,GAAG,IAAI,OAAO,YAAY,CAAC;AAAA,IACpC,KAAK;AAAA,IACL,KAAK;AACH,aAAO,GAAG,OAAO,OAAO,YAAY,CAAC;AAAA,IACvC;AACE,aAAO,GAAG,IAAI,OAAO,YAAY,CAAC;AAAA,EACtC;AACF;","names":[]}
|