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
package/README.md
CHANGED
|
@@ -108,21 +108,18 @@ The CLI creates a new SvelteKit project with Specra pre-configured:
|
|
|
108
108
|
```
|
|
109
109
|
my-docs/
|
|
110
110
|
├── src/
|
|
111
|
-
│ ├──
|
|
112
|
-
│ ├──
|
|
113
|
-
│
|
|
114
|
-
│
|
|
115
|
-
│
|
|
116
|
-
│
|
|
117
|
-
│
|
|
118
|
-
│
|
|
119
|
-
│ └── [...slug]/
|
|
120
|
-
│ ├── +page.server.ts
|
|
121
|
-
│ └── +page.svelte
|
|
111
|
+
│ ├── routes/
|
|
112
|
+
│ │ ├── +layout.svelte
|
|
113
|
+
│ │ ├── +page.svelte
|
|
114
|
+
│ │ └── docs/
|
|
115
|
+
│ │ └── [version]/[...slug]/
|
|
116
|
+
│ │ ├── +page.svelte
|
|
117
|
+
│ │ └── +page.ts
|
|
118
|
+
│ └── app.html
|
|
122
119
|
├── docs/
|
|
123
|
-
│
|
|
124
|
-
│
|
|
125
|
-
├──
|
|
120
|
+
│ └── v1.0.0/
|
|
121
|
+
│ └── index.mdx
|
|
122
|
+
├── static/
|
|
126
123
|
├── specra.config.json
|
|
127
124
|
├── svelte.config.js
|
|
128
125
|
├── vite.config.ts
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
ApiError,
|
|
4
|
+
apiRequest,
|
|
5
|
+
apiUpload,
|
|
6
|
+
formatError
|
|
7
|
+
} from "./chunk-CIM73PDF.js";
|
|
8
|
+
import "./chunk-ATRUBLRX.js";
|
|
9
|
+
export {
|
|
10
|
+
ApiError,
|
|
11
|
+
apiRequest,
|
|
12
|
+
apiUpload,
|
|
13
|
+
formatError
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=api-client-4XZPF7GL.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/config.ts
|
|
4
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync, unlinkSync } from "fs";
|
|
5
|
+
import { join, resolve } from "path";
|
|
6
|
+
import { homedir } from "os";
|
|
7
|
+
var GLOBAL_CONFIG_DIR = join(homedir(), ".specra");
|
|
8
|
+
var GLOBAL_CONFIG_FILE = join(GLOBAL_CONFIG_DIR, "config.json");
|
|
9
|
+
var DEFAULT_GLOBAL_CONFIG = {
|
|
10
|
+
apiUrl: "https://specra-docs.com"
|
|
11
|
+
};
|
|
12
|
+
function getGlobalConfig() {
|
|
13
|
+
try {
|
|
14
|
+
const raw = readFileSync(GLOBAL_CONFIG_FILE, "utf-8");
|
|
15
|
+
return { ...DEFAULT_GLOBAL_CONFIG, ...JSON.parse(raw) };
|
|
16
|
+
} catch {
|
|
17
|
+
return DEFAULT_GLOBAL_CONFIG;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function saveGlobalConfig(config) {
|
|
21
|
+
mkdirSync(GLOBAL_CONFIG_DIR, { recursive: true });
|
|
22
|
+
const current = getGlobalConfig();
|
|
23
|
+
const merged = { ...current, ...config };
|
|
24
|
+
writeFileSync(GLOBAL_CONFIG_FILE, JSON.stringify(merged, null, 2));
|
|
25
|
+
}
|
|
26
|
+
function clearGlobalToken() {
|
|
27
|
+
const config = getGlobalConfig();
|
|
28
|
+
delete config.token;
|
|
29
|
+
mkdirSync(GLOBAL_CONFIG_DIR, { recursive: true });
|
|
30
|
+
writeFileSync(GLOBAL_CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
31
|
+
}
|
|
32
|
+
function getLocalConfigPath(dir) {
|
|
33
|
+
return join(resolve(dir || "."), "specra.config.json");
|
|
34
|
+
}
|
|
35
|
+
function readLocalConfig(dir) {
|
|
36
|
+
const configPath = getLocalConfigPath(dir);
|
|
37
|
+
try {
|
|
38
|
+
return JSON.parse(readFileSync(configPath, "utf-8"));
|
|
39
|
+
} catch {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function getLocalToken(dir) {
|
|
44
|
+
const config = readLocalConfig(dir);
|
|
45
|
+
return config?.auth?.token;
|
|
46
|
+
}
|
|
47
|
+
function saveLocalToken(token, dir) {
|
|
48
|
+
const configPath = getLocalConfigPath(dir);
|
|
49
|
+
if (!existsSync(configPath)) {
|
|
50
|
+
throw new Error(`specra.config.json not found in ${resolve(dir || ".")}. Are you in a Specra project?`);
|
|
51
|
+
}
|
|
52
|
+
const config = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
53
|
+
config.auth = { ...config.auth, token };
|
|
54
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
55
|
+
}
|
|
56
|
+
function clearLocalToken(dir) {
|
|
57
|
+
const configPath = getLocalConfigPath(dir);
|
|
58
|
+
if (!existsSync(configPath)) return;
|
|
59
|
+
try {
|
|
60
|
+
const config = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
61
|
+
if (config.auth) {
|
|
62
|
+
delete config.auth.token;
|
|
63
|
+
if (Object.keys(config.auth).length === 0) {
|
|
64
|
+
delete config.auth;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
68
|
+
} catch {
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function getToken(dir) {
|
|
72
|
+
return getLocalToken(dir) || getGlobalConfig().token;
|
|
73
|
+
}
|
|
74
|
+
function getConfig() {
|
|
75
|
+
return getGlobalConfig();
|
|
76
|
+
}
|
|
77
|
+
function isAuthenticated(dir) {
|
|
78
|
+
return !!getToken(dir);
|
|
79
|
+
}
|
|
80
|
+
function saveToken(token, options) {
|
|
81
|
+
if (options?.global) {
|
|
82
|
+
saveGlobalConfig({ token });
|
|
83
|
+
} else {
|
|
84
|
+
saveLocalToken(token, options?.dir);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
function clearToken(options) {
|
|
88
|
+
if (options?.global) {
|
|
89
|
+
clearGlobalToken();
|
|
90
|
+
} else {
|
|
91
|
+
clearLocalToken(options?.dir);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export {
|
|
96
|
+
getToken,
|
|
97
|
+
getConfig,
|
|
98
|
+
isAuthenticated,
|
|
99
|
+
saveToken,
|
|
100
|
+
clearToken
|
|
101
|
+
};
|
|
102
|
+
//# sourceMappingURL=chunk-ATRUBLRX.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/config.ts"],"sourcesContent":["import { readFileSync, writeFileSync, mkdirSync, existsSync, unlinkSync } from 'fs'\nimport { join, resolve } from 'path'\nimport { homedir } from 'os'\n\n// Global config: ~/.specra/config.json\nconst GLOBAL_CONFIG_DIR = join(homedir(), '.specra')\nconst GLOBAL_CONFIG_FILE = join(GLOBAL_CONFIG_DIR, 'config.json')\n\ninterface GlobalConfig {\n apiUrl: string\n token?: string\n defaultProject?: string\n}\n\nconst DEFAULT_GLOBAL_CONFIG: GlobalConfig = {\n apiUrl: 'https://specra-docs.com',\n}\n\n// --------------- Global config ---------------\n\nexport function getGlobalConfig(): GlobalConfig {\n try {\n const raw = readFileSync(GLOBAL_CONFIG_FILE, 'utf-8')\n return { ...DEFAULT_GLOBAL_CONFIG, ...JSON.parse(raw) }\n } catch {\n return DEFAULT_GLOBAL_CONFIG\n }\n}\n\nexport function saveGlobalConfig(config: Partial<GlobalConfig>) {\n mkdirSync(GLOBAL_CONFIG_DIR, { recursive: true })\n const current = getGlobalConfig()\n const merged = { ...current, ...config }\n writeFileSync(GLOBAL_CONFIG_FILE, JSON.stringify(merged, null, 2))\n}\n\nexport function clearGlobalToken() {\n const config = getGlobalConfig()\n delete config.token\n mkdirSync(GLOBAL_CONFIG_DIR, { recursive: true })\n writeFileSync(GLOBAL_CONFIG_FILE, JSON.stringify(config, null, 2))\n}\n\n// --------------- Local config (specra.config.json) ---------------\n\nfunction getLocalConfigPath(dir?: string): string {\n return join(resolve(dir || '.'), 'specra.config.json')\n}\n\nfunction readLocalConfig(dir?: string): Record<string, any> | null {\n const configPath = getLocalConfigPath(dir)\n try {\n return JSON.parse(readFileSync(configPath, 'utf-8'))\n } catch {\n return null\n }\n}\n\nexport function getLocalToken(dir?: string): string | undefined {\n const config = readLocalConfig(dir)\n return config?.auth?.token\n}\n\nexport function saveLocalToken(token: string, dir?: string) {\n const configPath = getLocalConfigPath(dir)\n if (!existsSync(configPath)) {\n throw new Error(`specra.config.json not found in ${resolve(dir || '.')}. Are you in a Specra project?`)\n }\n const config = JSON.parse(readFileSync(configPath, 'utf-8'))\n config.auth = { ...config.auth, token }\n writeFileSync(configPath, JSON.stringify(config, null, 2) + '\\n')\n}\n\nexport function clearLocalToken(dir?: string) {\n const configPath = getLocalConfigPath(dir)\n if (!existsSync(configPath)) return\n try {\n const config = JSON.parse(readFileSync(configPath, 'utf-8'))\n if (config.auth) {\n delete config.auth.token\n if (Object.keys(config.auth).length === 0) {\n delete config.auth\n }\n }\n writeFileSync(configPath, JSON.stringify(config, null, 2) + '\\n')\n } catch {\n // ignore parse errors\n }\n}\n\n// --------------- Unified accessors ---------------\n\n/** Get token: checks local specra.config.json first, then global ~/.specra/config.json */\nexport function getToken(dir?: string): string | undefined {\n return getLocalToken(dir) || getGlobalConfig().token\n}\n\n/** Get the API URL from global config */\nexport function getConfig(): GlobalConfig {\n return getGlobalConfig()\n}\n\nexport function isAuthenticated(dir?: string): boolean {\n return !!getToken(dir)\n}\n\n/** Save token to local config (default) or global config */\nexport function saveToken(token: string, options?: { global?: boolean; dir?: string }) {\n if (options?.global) {\n saveGlobalConfig({ token })\n } else {\n saveLocalToken(token, options?.dir)\n }\n}\n\n/** Clear token from local config (default) or global config */\nexport function clearToken(options?: { global?: boolean; dir?: string }) {\n if (options?.global) {\n clearGlobalToken()\n } else {\n clearLocalToken(options?.dir)\n }\n}\n\n// Legacy exports for backwards compat\nexport function saveConfig(config: Partial<GlobalConfig>) {\n saveGlobalConfig(config)\n}\n\nexport function clearConfig() {\n if (existsSync(GLOBAL_CONFIG_FILE)) {\n unlinkSync(GLOBAL_CONFIG_FILE)\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
getConfig,
|
|
4
4
|
getToken
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-ATRUBLRX.js";
|
|
6
6
|
|
|
7
7
|
// src/api-client.ts
|
|
8
8
|
var ApiError = class extends Error {
|
|
@@ -12,6 +12,24 @@ var ApiError = class extends Error {
|
|
|
12
12
|
this.name = "ApiError";
|
|
13
13
|
}
|
|
14
14
|
};
|
|
15
|
+
var STATUS_HINTS = {
|
|
16
|
+
401: "Try running `specra login` to re-authenticate.",
|
|
17
|
+
403: "You may not have permission for this action. Check your plan or project access.",
|
|
18
|
+
502: "The server is temporarily unavailable. Try again in a moment.",
|
|
19
|
+
503: "The server is temporarily unavailable. Try again in a moment."
|
|
20
|
+
};
|
|
21
|
+
function formatError(context, err) {
|
|
22
|
+
const prefix = context ? `${context}: ` : "";
|
|
23
|
+
if (err instanceof ApiError) {
|
|
24
|
+
const hint = STATUS_HINTS[err.status] || "";
|
|
25
|
+
return `${prefix}${err.message} (${err.status})${hint ? `
|
|
26
|
+
${hint}` : ""}`;
|
|
27
|
+
}
|
|
28
|
+
if (err instanceof Error) {
|
|
29
|
+
return `${prefix}${err.message}`;
|
|
30
|
+
}
|
|
31
|
+
return `${prefix}${String(err)}`;
|
|
32
|
+
}
|
|
15
33
|
async function apiRequest(path, options = {}) {
|
|
16
34
|
const config = getConfig();
|
|
17
35
|
const token = getToken();
|
|
@@ -68,7 +86,9 @@ async function apiUpload(path, body, headers = {}) {
|
|
|
68
86
|
}
|
|
69
87
|
|
|
70
88
|
export {
|
|
89
|
+
ApiError,
|
|
90
|
+
formatError,
|
|
71
91
|
apiRequest,
|
|
72
92
|
apiUpload
|
|
73
93
|
};
|
|
74
|
-
//# sourceMappingURL=chunk-
|
|
94
|
+
//# sourceMappingURL=chunk-CIM73PDF.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/api-client.ts"],"sourcesContent":["import { getConfig, getToken } from './config.js'\n\nexport class ApiError extends Error {\n constructor(public status: number, message: string) {\n super(message)\n this.name = 'ApiError'\n }\n}\n\nconst STATUS_HINTS: Record<number, string> = {\n 401: 'Try running `specra login` to re-authenticate.',\n 403: 'You may not have permission for this action. Check your plan or project access.',\n 502: 'The server is temporarily unavailable. Try again in a moment.',\n 503: 'The server is temporarily unavailable. Try again in a moment.',\n}\n\n/** Format an error for CLI display (no stack traces). */\nexport function formatError(context: string, err: unknown): string {\n const prefix = context ? `${context}: ` : ''\n if (err instanceof ApiError) {\n const hint = STATUS_HINTS[err.status] || ''\n return `${prefix}${err.message} (${err.status})${hint ? `\\n ${hint}` : ''}`\n }\n if (err instanceof Error) {\n return `${prefix}${err.message}`\n }\n return `${prefix}${String(err)}`\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":";;;;;;;AAEO,IAAM,WAAN,cAAuB,MAAM;AAAA,EAClC,YAAmB,QAAgB,SAAiB;AAClD,UAAM,OAAO;AADI;AAEjB,SAAK,OAAO;AAAA,EACd;AACF;AAEA,IAAM,eAAuC;AAAA,EAC3C,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AACP;AAGO,SAAS,YAAY,SAAiB,KAAsB;AACjE,QAAM,SAAS,UAAU,GAAG,OAAO,OAAO;AAC1C,MAAI,eAAe,UAAU;AAC3B,UAAM,OAAO,aAAa,IAAI,MAAM,KAAK;AACzC,WAAO,GAAG,MAAM,GAAG,IAAI,OAAO,KAAK,IAAI,MAAM,IAAI,OAAO;AAAA,IAAO,IAAI,KAAK,EAAE;AAAA,EAC5E;AACA,MAAI,eAAe,OAAO;AACxB,WAAO,GAAG,MAAM,GAAG,IAAI,OAAO;AAAA,EAChC;AACA,SAAO,GAAG,MAAM,GAAG,OAAO,GAAG,CAAC;AAChC;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":[]}
|
package/dist/cli.js
CHANGED
|
@@ -3,28 +3,35 @@
|
|
|
3
3
|
// src/cli.ts
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
import pc from "picocolors";
|
|
6
|
+
import { createRequire } from "module";
|
|
7
|
+
var require2 = createRequire(import.meta.url);
|
|
8
|
+
var { version } = require2("../package.json");
|
|
6
9
|
var program = new Command();
|
|
7
|
-
program.name("specra").description("Specra CLI - Deploy and manage your documentation sites").version(
|
|
8
|
-
program.command("login").description("Authenticate with your Specra account").action(async () => {
|
|
9
|
-
const { login } = await import("./login-
|
|
10
|
-
await login();
|
|
10
|
+
program.name("specra").description("Specra CLI - Deploy and manage your documentation sites").version(version);
|
|
11
|
+
program.command("login").description("Authenticate with your Specra account").option("-g, --global", "Store credentials in ~/.specra/ instead of local specra.config.json").action(async (options) => {
|
|
12
|
+
const { login } = await import("./login-DRPP77G4.js");
|
|
13
|
+
await login(options);
|
|
11
14
|
});
|
|
12
|
-
program.command("logout").description("Clear stored credentials").action(async () => {
|
|
13
|
-
const { logout } = await import("./logout-
|
|
14
|
-
await logout();
|
|
15
|
+
program.command("logout").description("Clear stored credentials").option("-g, --global", "Clear credentials from ~/.specra/ instead of local specra.config.json").action(async (options) => {
|
|
16
|
+
const { logout } = await import("./logout-UJFYUAQC.js");
|
|
17
|
+
await logout(options);
|
|
15
18
|
});
|
|
16
19
|
program.command("deploy").description("Deploy your docs project").option("-p, --project <id>", "Project ID to deploy to").option("-d, --dir <directory>", "Docs directory to deploy", ".").action(async (options) => {
|
|
17
|
-
const { deploy } = await import("./deploy-
|
|
20
|
+
const { deploy } = await import("./deploy-ETWFNB4Z.js");
|
|
18
21
|
await deploy(options);
|
|
19
22
|
});
|
|
20
23
|
program.command("projects").description("List your projects").action(async () => {
|
|
21
|
-
const { projects } = await import("./projects-
|
|
24
|
+
const { projects } = await import("./projects-NORBBC4D.js");
|
|
22
25
|
await projects();
|
|
23
26
|
});
|
|
24
27
|
program.command("logs").description("View deployment logs").argument("<projectId>", "Project ID").option("--deployment <id>", "Specific deployment ID").action(async (projectId, options) => {
|
|
25
|
-
const { logs } = await import("./logs-
|
|
28
|
+
const { logs } = await import("./logs-K2CSCKOE.js");
|
|
26
29
|
await logs(projectId, options);
|
|
27
30
|
});
|
|
31
|
+
program.command("doctor").description("Check specra.config.json for issues").option("-d, --dir <directory>", "Project directory to check", ".").action(async (options) => {
|
|
32
|
+
const { doctor } = await import("./doctor-IFELWGQB.js");
|
|
33
|
+
await doctor(options);
|
|
34
|
+
});
|
|
28
35
|
program.parse();
|
|
29
36
|
process.on("unhandledRejection", (err) => {
|
|
30
37
|
console.error(pc.red("Error:"), err);
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli.ts"],"sourcesContent":["import { Command } from 'commander'\nimport pc from 'picocolors'\n\nconst program = new Command()\n\nprogram\n .name('specra')\n .description('Specra CLI - Deploy and manage your documentation sites')\n .version(
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts"],"sourcesContent":["import { Command } from 'commander'\nimport pc from 'picocolors'\nimport { createRequire } from 'module'\n\nconst require = createRequire(import.meta.url)\nconst { version } = require('../package.json')\n\nconst program = new Command()\n\nprogram\n .name('specra')\n .description('Specra CLI - Deploy and manage your documentation sites')\n .version(version)\n\nprogram\n .command('login')\n .description('Authenticate with your Specra account')\n .option('-g, --global', 'Store credentials in ~/.specra/ instead of local specra.config.json')\n .action(async (options) => {\n const { login } = await import('./commands/login.js')\n await login(options)\n })\n\nprogram\n .command('logout')\n .description('Clear stored credentials')\n .option('-g, --global', 'Clear credentials from ~/.specra/ instead of local specra.config.json')\n .action(async (options) => {\n const { logout } = await import('./commands/logout.js')\n await logout(options)\n })\n\nprogram\n .command('deploy')\n .description('Deploy your docs project')\n .option('-p, --project <id>', 'Project ID to deploy to')\n .option('-d, --dir <directory>', 'Docs directory to deploy', '.')\n .action(async (options) => {\n const { deploy } = await import('./commands/deploy.js')\n await deploy(options)\n })\n\nprogram\n .command('projects')\n .description('List your projects')\n .action(async () => {\n const { projects } = await import('./commands/projects.js')\n await projects()\n })\n\nprogram\n .command('logs')\n .description('View deployment logs')\n .argument('<projectId>', 'Project ID')\n .option('--deployment <id>', 'Specific deployment ID')\n .action(async (projectId: string, options) => {\n const { logs } = await import('./commands/logs.js')\n await logs(projectId, options)\n })\n\nprogram\n .command('doctor')\n .description('Check specra.config.json for issues')\n .option('-d, --dir <directory>', 'Project directory to check', '.')\n .action(async (options) => {\n const { doctor } = await import('./commands/doctor.js')\n await doctor(options)\n })\n\nprogram.parse()\n\n// Handle unhandled rejections\nprocess.on('unhandledRejection', (err) => {\n console.error(pc.red('Error:'), err)\n process.exit(1)\n})\n"],"mappings":";;;AAAA,SAAS,eAAe;AACxB,OAAO,QAAQ;AACf,SAAS,qBAAqB;AAE9B,IAAMA,WAAU,cAAc,YAAY,GAAG;AAC7C,IAAM,EAAE,QAAQ,IAAIA,SAAQ,iBAAiB;AAE7C,IAAM,UAAU,IAAI,QAAQ;AAE5B,QACG,KAAK,QAAQ,EACb,YAAY,yDAAyD,EACrE,QAAQ,OAAO;AAElB,QACG,QAAQ,OAAO,EACf,YAAY,uCAAuC,EACnD,OAAO,gBAAgB,qEAAqE,EAC5F,OAAO,OAAO,YAAY;AACzB,QAAM,EAAE,MAAM,IAAI,MAAM,OAAO,qBAAqB;AACpD,QAAM,MAAM,OAAO;AACrB,CAAC;AAEH,QACG,QAAQ,QAAQ,EAChB,YAAY,0BAA0B,EACtC,OAAO,gBAAgB,uEAAuE,EAC9F,OAAO,OAAO,YAAY;AACzB,QAAM,EAAE,OAAO,IAAI,MAAM,OAAO,sBAAsB;AACtD,QAAM,OAAO,OAAO;AACtB,CAAC;AAEH,QACG,QAAQ,QAAQ,EAChB,YAAY,0BAA0B,EACtC,OAAO,sBAAsB,yBAAyB,EACtD,OAAO,yBAAyB,4BAA4B,GAAG,EAC/D,OAAO,OAAO,YAAY;AACzB,QAAM,EAAE,OAAO,IAAI,MAAM,OAAO,sBAAsB;AACtD,QAAM,OAAO,OAAO;AACtB,CAAC;AAEH,QACG,QAAQ,UAAU,EAClB,YAAY,oBAAoB,EAChC,OAAO,YAAY;AAClB,QAAM,EAAE,SAAS,IAAI,MAAM,OAAO,wBAAwB;AAC1D,QAAM,SAAS;AACjB,CAAC;AAEH,QACG,QAAQ,MAAM,EACd,YAAY,sBAAsB,EAClC,SAAS,eAAe,YAAY,EACpC,OAAO,qBAAqB,wBAAwB,EACpD,OAAO,OAAO,WAAmB,YAAY;AAC5C,QAAM,EAAE,KAAK,IAAI,MAAM,OAAO,oBAAoB;AAClD,QAAM,KAAK,WAAW,OAAO;AAC/B,CAAC;AAEH,QACG,QAAQ,QAAQ,EAChB,YAAY,qCAAqC,EACjD,OAAO,yBAAyB,8BAA8B,GAAG,EACjE,OAAO,OAAO,YAAY;AACzB,QAAM,EAAE,OAAO,IAAI,MAAM,OAAO,sBAAsB;AACtD,QAAM,OAAO,OAAO;AACtB,CAAC;AAEH,QAAQ,MAAM;AAGd,QAAQ,GAAG,sBAAsB,CAAC,QAAQ;AACxC,UAAQ,MAAM,GAAG,IAAI,QAAQ,GAAG,GAAG;AACnC,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":["require"]}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
apiRequest,
|
|
4
|
-
apiUpload
|
|
5
|
-
|
|
4
|
+
apiUpload,
|
|
5
|
+
formatError
|
|
6
|
+
} from "./chunk-CIM73PDF.js";
|
|
6
7
|
import {
|
|
7
8
|
getConfig,
|
|
8
9
|
isAuthenticated
|
|
9
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-ATRUBLRX.js";
|
|
10
11
|
|
|
11
12
|
// src/commands/deploy.ts
|
|
12
13
|
import pc from "picocolors";
|
|
@@ -15,7 +16,7 @@ import ora from "ora";
|
|
|
15
16
|
// src/archive.ts
|
|
16
17
|
import * as tar from "tar";
|
|
17
18
|
import { existsSync, statSync } from "fs";
|
|
18
|
-
import {
|
|
19
|
+
import { resolve } from "path";
|
|
19
20
|
async function createArchive(dir) {
|
|
20
21
|
const absDir = resolve(dir);
|
|
21
22
|
if (!existsSync(absDir)) {
|
|
@@ -24,37 +25,27 @@ async function createArchive(dir) {
|
|
|
24
25
|
if (!statSync(absDir).isDirectory()) {
|
|
25
26
|
throw new Error(`Not a directory: ${absDir}`);
|
|
26
27
|
}
|
|
27
|
-
const hasPackageJson = existsSync(join(absDir, "package.json"));
|
|
28
|
-
const hasDocsDir = existsSync(join(absDir, "docs"));
|
|
29
|
-
const hasSpecraConfig = existsSync(join(absDir, "specra.config.json"));
|
|
30
|
-
if (!hasPackageJson && !hasDocsDir && !hasSpecraConfig) {
|
|
31
|
-
throw new Error(
|
|
32
|
-
"No Specra project found. Ensure the directory contains package.json, docs/, or specra.config.json"
|
|
33
|
-
);
|
|
34
|
-
}
|
|
35
28
|
const chunks = [];
|
|
36
|
-
|
|
29
|
+
const stream = tar.create(
|
|
37
30
|
{
|
|
38
31
|
gzip: true,
|
|
39
|
-
cwd: absDir
|
|
40
|
-
filter: (path) => {
|
|
41
|
-
if (path.includes("node_modules/")) return false;
|
|
42
|
-
if (path.includes(".next/")) return false;
|
|
43
|
-
if (path.includes(".git/")) return false;
|
|
44
|
-
if (path.includes(".env")) return false;
|
|
45
|
-
return true;
|
|
46
|
-
}
|
|
32
|
+
cwd: absDir
|
|
47
33
|
},
|
|
48
34
|
["."]
|
|
49
|
-
)
|
|
35
|
+
);
|
|
36
|
+
for await (const chunk of stream) {
|
|
50
37
|
chunks.push(chunk);
|
|
51
|
-
}
|
|
52
|
-
|
|
38
|
+
}
|
|
39
|
+
const result = Buffer.concat(chunks);
|
|
40
|
+
if (result.length === 0) {
|
|
41
|
+
throw new Error("Archive is empty \u2014 no files found in the build output");
|
|
42
|
+
}
|
|
43
|
+
return result;
|
|
53
44
|
}
|
|
54
45
|
|
|
55
46
|
// src/commands/deploy.ts
|
|
56
47
|
import { existsSync as existsSync2, readFileSync } from "fs";
|
|
57
|
-
import { join
|
|
48
|
+
import { join, resolve as resolve2 } from "path";
|
|
58
49
|
async function deploy(options) {
|
|
59
50
|
if (!isAuthenticated()) {
|
|
60
51
|
console.error(pc.red("Not authenticated. Run `specra login` first."));
|
|
@@ -63,7 +54,7 @@ async function deploy(options) {
|
|
|
63
54
|
const dir = resolve2(options.dir);
|
|
64
55
|
let projectId = options.project;
|
|
65
56
|
if (!projectId) {
|
|
66
|
-
const configPath =
|
|
57
|
+
const configPath = join(dir, "specra.config.json");
|
|
67
58
|
if (existsSync2(configPath)) {
|
|
68
59
|
try {
|
|
69
60
|
const config = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
@@ -73,12 +64,18 @@ async function deploy(options) {
|
|
|
73
64
|
}
|
|
74
65
|
}
|
|
75
66
|
if (!projectId) {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
67
|
+
let projects;
|
|
68
|
+
try {
|
|
69
|
+
projects = await apiRequest(
|
|
70
|
+
"/api/projects"
|
|
71
|
+
);
|
|
72
|
+
} catch (err) {
|
|
73
|
+
console.error(pc.red(formatError("Failed to fetch projects", err)));
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
79
76
|
if (projects.length === 0) {
|
|
80
77
|
console.error(
|
|
81
|
-
pc.red("No projects found. Create one at https://specra.
|
|
78
|
+
pc.red("No projects found. Create one at https://specra-docs.com/dashboard/projects/new")
|
|
82
79
|
);
|
|
83
80
|
process.exit(1);
|
|
84
81
|
}
|
|
@@ -103,13 +100,27 @@ async function deploy(options) {
|
|
|
103
100
|
}
|
|
104
101
|
projectId = response.project;
|
|
105
102
|
}
|
|
106
|
-
const spinner = ora("
|
|
103
|
+
const spinner = ora("Building project...").start();
|
|
107
104
|
try {
|
|
108
|
-
const
|
|
105
|
+
const buildDir = join(dir, "build");
|
|
106
|
+
const { execSync } = await import("child_process");
|
|
107
|
+
try {
|
|
108
|
+
execSync("npm run build", { cwd: dir, stdio: "pipe" });
|
|
109
|
+
} catch (err) {
|
|
110
|
+
const stderr = err instanceof Error && "stderr" in err ? err.stderr?.toString() : "";
|
|
111
|
+
throw new Error(`Build failed:
|
|
112
|
+
${stderr}`);
|
|
113
|
+
}
|
|
114
|
+
if (!existsSync2(buildDir)) {
|
|
115
|
+
throw new Error(
|
|
116
|
+
"Build output not found. Expected a `build/` directory.\nMake sure your project uses @sveltejs/adapter-static."
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
spinner.text = "Packaging build output...";
|
|
120
|
+
const archive = await createArchive(buildDir);
|
|
109
121
|
spinner.text = `Uploading (${(archive.length / 1024).toFixed(0)}KB)...`;
|
|
110
122
|
let commitSha;
|
|
111
123
|
try {
|
|
112
|
-
const { execSync } = await import("child_process");
|
|
113
124
|
commitSha = execSync("git rev-parse HEAD", { cwd: dir }).toString().trim();
|
|
114
125
|
} catch {
|
|
115
126
|
}
|
|
@@ -118,10 +129,11 @@ async function deploy(options) {
|
|
|
118
129
|
archive,
|
|
119
130
|
{
|
|
120
131
|
"X-Deploy-Trigger": "CLI",
|
|
132
|
+
"X-Pre-Built": "true",
|
|
121
133
|
...commitSha ? { "X-Commit-Sha": commitSha } : {}
|
|
122
134
|
}
|
|
123
135
|
);
|
|
124
|
-
spinner.succeed(pc.green("
|
|
136
|
+
spinner.succeed(pc.green("Deployed!"));
|
|
125
137
|
console.log();
|
|
126
138
|
console.log(` Deployment ID: ${pc.cyan(result.deploymentId)}`);
|
|
127
139
|
const config = getConfig();
|
|
@@ -131,11 +143,11 @@ async function deploy(options) {
|
|
|
131
143
|
console.log();
|
|
132
144
|
} catch (err) {
|
|
133
145
|
spinner.fail(pc.red("Deploy failed"));
|
|
134
|
-
console.error(
|
|
146
|
+
console.error(pc.red(formatError("", err)));
|
|
135
147
|
process.exit(1);
|
|
136
148
|
}
|
|
137
149
|
}
|
|
138
150
|
export {
|
|
139
151
|
deploy
|
|
140
152
|
};
|
|
141
|
-
//# sourceMappingURL=deploy-
|
|
153
|
+
//# sourceMappingURL=deploy-ETWFNB4Z.js.map
|
|
@@ -0,0 +1 @@
|
|
|
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, formatError } 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 let projects: Array<{ id: string; name: string; subdomain: string }>\n try {\n projects = await apiRequest<Array<{ id: string; name: string; subdomain: string }>>(\n '/api/projects'\n )\n } catch (err) {\n console.error(pc.red(formatError('Failed to fetch projects', err)))\n process.exit(1)\n }\n\n if (projects.length === 0) {\n console.error(\n pc.red('No projects found. Create one at https://specra-docs.com/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 const spinner = ora('Building project...').start()\n\n try {\n // 1. Build the project locally\n const buildDir = join(dir, 'build')\n const { execSync } = await import('child_process')\n\n try {\n execSync('npm run build', { cwd: dir, stdio: 'pipe' })\n } catch (err) {\n const stderr = err instanceof Error && 'stderr' in err\n ? (err as { stderr: Buffer }).stderr?.toString()\n : ''\n throw new Error(`Build failed:\\n${stderr}`)\n }\n\n if (!existsSync(buildDir)) {\n throw new Error(\n 'Build output not found. Expected a `build/` directory.\\n' +\n 'Make sure your project uses @sveltejs/adapter-static.'\n )\n }\n\n // 2. Archive the build output\n spinner.text = 'Packaging build output...'\n const archive = await createArchive(buildDir)\n spinner.text = `Uploading (${(archive.length / 1024).toFixed(0)}KB)...`\n\n // 3. Get git commit SHA if available\n let commitSha: string | undefined\n try {\n commitSha = execSync('git rev-parse HEAD', { cwd: dir })\n .toString()\n .trim()\n } catch {\n // not a git repo\n }\n\n // 4. Upload as pre-built\n const result = await apiUpload(\n `/api/projects/${projectId}/deploy`,\n archive,\n {\n 'X-Deploy-Trigger': 'CLI',\n 'X-Pre-Built': 'true',\n ...(commitSha ? { 'X-Commit-Sha': commitSha } : {}),\n }\n ) as { deploymentId: string }\n\n spinner.succeed(pc.green('Deployed!'))\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(pc.red(formatError('', err)))\n process.exit(1)\n }\n}\n","import * as tar from 'tar'\nimport { existsSync, statSync } from 'fs'\nimport { 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 const chunks: Buffer[] = []\n\n const stream = tar.create(\n {\n gzip: true,\n cwd: absDir,\n },\n ['.']\n )\n\n for await (const chunk of stream) {\n chunks.push(chunk as Buffer)\n }\n\n const result = Buffer.concat(chunks)\n if (result.length === 0) {\n throw new Error('Archive is empty — no files found in the build output')\n }\n\n return result\n}\n"],"mappings":"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","names":["existsSync","resolve"]}
|