@jxrstudios/jxr 1.2.17 → 1.2.18
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/bin/jxr.js +14 -86
- package/dist/deployer.d.ts +17 -0
- package/dist/deployer.d.ts.map +1 -1
- package/dist/index.js +100 -2
- package/package.json +1 -1
- package/src/deployer.ts +130 -2
package/bin/jxr.js
CHANGED
|
@@ -275,75 +275,19 @@ if (command === "init") {
|
|
|
275
275
|
console.log(`🚀 Deploying to ${target === "auto" ? "auto-detected platform" : target}...`);
|
|
276
276
|
|
|
277
277
|
try {
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
let buildDir = path.join(projectPath, "dist");
|
|
283
|
-
if (!fs.existsSync(buildDir)) {
|
|
284
|
-
buildDir = path.join(projectPath, "build");
|
|
285
|
-
}
|
|
286
|
-
if (!fs.existsSync(buildDir)) {
|
|
287
|
-
buildDir = path.join(projectPath, "out");
|
|
288
|
-
}
|
|
289
|
-
if (!fs.existsSync(buildDir)) {
|
|
290
|
-
// Look for any directory with index.html
|
|
291
|
-
const dirs = fs.readdirSync(projectPath, { withFileTypes: true })
|
|
292
|
-
.filter(d => d.isDirectory() && !d.name.startsWith(".") && !d.name === "node_modules")
|
|
293
|
-
.map(d => path.join(projectPath, d.name));
|
|
294
|
-
|
|
295
|
-
for (const dir of dirs) {
|
|
296
|
-
if (fs.existsSync(path.join(dir, "index.html"))) {
|
|
297
|
-
buildDir = dir;
|
|
298
|
-
break;
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
if (!fs.existsSync(buildDir)) {
|
|
304
|
-
console.error("❌ No build output found. Run 'jxr build' first.");
|
|
305
|
-
process.exit(1);
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
// Check for jxr-manifest.json for verification
|
|
309
|
-
const manifestPath = path.join(buildDir, "jxr-manifest.json");
|
|
310
|
-
if (fs.existsSync(manifestPath)) {
|
|
311
|
-
const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
|
|
312
|
-
console.log(`📋 Build manifest: ${manifest.platform} platform`);
|
|
313
|
-
console.log(` Files: ${manifest.files.length}`);
|
|
314
|
-
console.log(` Signed: ${manifest.algorithm}`);
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
// Detect if running on Cloudflare Pages
|
|
318
|
-
const isCloudflarePages = process.env.CF_PAGES === "1" || process.env.CF_PAGES_URL !== undefined;
|
|
319
|
-
|
|
320
|
-
if (target === "cloudflare" || target === "auto" && isCloudflarePages) {
|
|
321
|
-
console.log("☁️ Deploying to Cloudflare Pages...");
|
|
278
|
+
// Use JXRDeployer for Cloudflare Pages deployment
|
|
279
|
+
if (target === "cloudflare" || target === "auto") {
|
|
280
|
+
const deployer = new JXRDeployer(process.env.JXR_API_KEY || '', process.env.JXR_PROJECT_ID);
|
|
281
|
+
const result = await deployer.deployToCloudflarePages(projectPath, { environment: env });
|
|
322
282
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
const pkg = JSON.parse(fs.readFileSync(path.join(projectPath, "package.json"), "utf-8"));
|
|
328
|
-
projectName = pkg.name;
|
|
329
|
-
} catch {
|
|
330
|
-
projectName = path.basename(path.resolve(projectPath));
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
// On Cloudflare Pages, the build output is automatically deployed
|
|
335
|
-
// We just need to ensure it's in the right location
|
|
336
|
-
console.log(` Project: ${projectName}`);
|
|
337
|
-
console.log(` Environment: ${env}`);
|
|
338
|
-
|
|
339
|
-
if (isCloudflarePages) {
|
|
340
|
-
console.log(` URL: https://${projectName}.app.jxrstudios.online`);
|
|
341
|
-
console.log("✅ Deployment ready for Cloudflare Pages");
|
|
342
|
-
console.log(" The build output will be deployed automatically.");
|
|
283
|
+
if (result.success) {
|
|
284
|
+
console.log("✅ Deployed successfully!");
|
|
285
|
+
console.log(` URL: ${result.url}`);
|
|
286
|
+
result.logs.forEach(log => console.log(` ${log}`));
|
|
343
287
|
} else {
|
|
344
|
-
|
|
345
|
-
console.
|
|
346
|
-
|
|
288
|
+
console.error("❌ Deploy failed");
|
|
289
|
+
result.logs.forEach(log => console.error(` ${log}`));
|
|
290
|
+
process.exit(1);
|
|
347
291
|
}
|
|
348
292
|
|
|
349
293
|
} else if (target === "deno") {
|
|
@@ -355,25 +299,9 @@ if (command === "init") {
|
|
|
355
299
|
console.log(" Copy the dist/ folder to your Node.js server");
|
|
356
300
|
|
|
357
301
|
} else {
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
console.error(" Get your key at: https://jxrstudios.online/dashboard");
|
|
362
|
-
process.exit(1);
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
const deployer = new JXRDeployer(process.env.JXR_API_KEY, process.env.JXR_PROJECT_ID);
|
|
366
|
-
const result = await deployer.deploy(buildDir, { environment: env });
|
|
367
|
-
|
|
368
|
-
if (result.success) {
|
|
369
|
-
console.log("✅ Deployed successfully!");
|
|
370
|
-
console.log(` URL: ${result.url}`);
|
|
371
|
-
result.logs.forEach(log => console.log(` ${log}`));
|
|
372
|
-
} else {
|
|
373
|
-
console.error("❌ Deploy failed");
|
|
374
|
-
result.logs.forEach(log => console.error(` ${log}`));
|
|
375
|
-
process.exit(1);
|
|
376
|
-
}
|
|
302
|
+
console.error(`❌ Unknown target: ${target}`);
|
|
303
|
+
console.error(" Supported: cloudflare, deno, node, auto");
|
|
304
|
+
process.exit(1);
|
|
377
305
|
}
|
|
378
306
|
|
|
379
307
|
} catch (err) {
|
package/dist/deployer.d.ts
CHANGED
|
@@ -29,6 +29,23 @@ export declare class JXRDeployer {
|
|
|
29
29
|
private apiKey;
|
|
30
30
|
private projectId;
|
|
31
31
|
constructor(apiKey: string, projectId?: string);
|
|
32
|
+
/**
|
|
33
|
+
* Auto-detect build output directory
|
|
34
|
+
* Checks dist/, build/, out/, or any directory with index.html
|
|
35
|
+
*/
|
|
36
|
+
detectBuildDir(projectPath?: string): Promise<string | null>;
|
|
37
|
+
/**
|
|
38
|
+
* Detect if running on Cloudflare Pages
|
|
39
|
+
*/
|
|
40
|
+
isCloudflarePages(): boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Get project name from package.json or directory
|
|
43
|
+
*/
|
|
44
|
+
getProjectName(projectPath?: string): Promise<string>;
|
|
45
|
+
/**
|
|
46
|
+
* Deploy to Cloudflare Pages
|
|
47
|
+
*/
|
|
48
|
+
deployToCloudflarePages(projectPath?: string, config?: DeployConfig): Promise<DeployResult>;
|
|
32
49
|
/**
|
|
33
50
|
* Deploy the current project to JXR infrastructure
|
|
34
51
|
*/
|
package/dist/deployer.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deployer.d.ts","sourceRoot":"","sources":["../src/deployer.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAOH,MAAM,WAAW,YAAY;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,YAAY,GAAG,SAAS,GAAG,SAAS,CAAC;IACnD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,SAAS,GAAG,UAAU,GAAG,UAAU,GAAG,QAAQ,CAAC;IACvD,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,SAAS,CAAS;gBAEd,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM;IAK9C;;OAEG;IACG,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,GAAE,YAAiB,GAAG,OAAO,CAAC,YAAY,CAAC;IA+CnF;;OAEG;YACW,aAAa;IAkB3B;;OAEG;YACW,aAAa;IAgC3B;;OAEG;IACG,SAAS,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAchE;;OAEG;IACG,eAAe,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;QACvD,EAAE,EAAE,MAAM,CAAC;QACX,GAAG,EAAE,MAAM,CAAC;QACZ,WAAW,EAAE,MAAM,CAAC;QACpB,SAAS,EAAE,MAAM,CAAC;QAClB,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC,CAAC;IAgBH;;OAEG;IACG,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAuB3D,OAAO,CAAC,iBAAiB;CAK1B;AAED,gCAAgC;AAChC,eAAO,MAAM,WAAW,aAGvB,CAAC"}
|
|
1
|
+
{"version":3,"file":"deployer.d.ts","sourceRoot":"","sources":["../src/deployer.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAOH,MAAM,WAAW,YAAY;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,YAAY,GAAG,SAAS,GAAG,SAAS,CAAC;IACnD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,SAAS,GAAG,UAAU,GAAG,UAAU,GAAG,QAAQ,CAAC;IACvD,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,SAAS,CAAS;gBAEd,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM;IAK9C;;;OAGG;IACG,cAAc,CAAC,WAAW,GAAE,MAAY,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAgCvE;;OAEG;IACH,iBAAiB,IAAI,OAAO;IAI5B;;OAEG;IACG,cAAc,CAAC,WAAW,GAAE,MAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IAsBhE;;OAEG;IACG,uBAAuB,CAAC,WAAW,GAAE,MAAY,EAAE,MAAM,GAAE,YAAiB,GAAG,OAAO,CAAC,YAAY,CAAC;IAyD1G;;OAEG;IACG,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,GAAE,YAAiB,GAAG,OAAO,CAAC,YAAY,CAAC;IA+CnF;;OAEG;YACW,aAAa;IAkB3B;;OAEG;YACW,aAAa;IAgC3B;;OAEG;IACG,SAAS,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAchE;;OAEG;IACG,eAAe,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;QACvD,EAAE,EAAE,MAAM,CAAC;QACX,GAAG,EAAE,MAAM,CAAC;QACZ,WAAW,EAAE,MAAM,CAAC;QACpB,SAAS,EAAE,MAAM,CAAC;QAClB,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC,CAAC;IAgBH;;OAEG;IACG,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAuB3D,OAAO,CAAC,iBAAiB;CAK1B;AAED,gCAAgC;AAChC,eAAO,MAAM,WAAW,aAGvB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -2042,8 +2042,9 @@ export default \`${escapedCSS}\`;
|
|
|
2042
2042
|
};
|
|
2043
2043
|
|
|
2044
2044
|
// src/deployer.ts
|
|
2045
|
-
import { readFile as readFile2, stat } from "fs/promises";
|
|
2045
|
+
import { readFile as readFile2, readdir as readdir2, stat } from "fs/promises";
|
|
2046
2046
|
import path2 from "path";
|
|
2047
|
+
import { existsSync } from "fs";
|
|
2047
2048
|
import { spawn } from "child_process";
|
|
2048
2049
|
var JXRDeployer = class {
|
|
2049
2050
|
apiKey;
|
|
@@ -2052,6 +2053,103 @@ var JXRDeployer = class {
|
|
|
2052
2053
|
this.apiKey = apiKey;
|
|
2053
2054
|
this.projectId = projectId || this.generateProjectId();
|
|
2054
2055
|
}
|
|
2056
|
+
/**
|
|
2057
|
+
* Auto-detect build output directory
|
|
2058
|
+
* Checks dist/, build/, out/, or any directory with index.html
|
|
2059
|
+
*/
|
|
2060
|
+
async detectBuildDir(projectPath = ".") {
|
|
2061
|
+
const possibleDirs = ["dist", "build", "out"];
|
|
2062
|
+
for (const dir of possibleDirs) {
|
|
2063
|
+
const fullPath = path2.resolve(projectPath, dir);
|
|
2064
|
+
if (existsSync(fullPath)) {
|
|
2065
|
+
if (existsSync(path2.join(fullPath, "index.html"))) {
|
|
2066
|
+
return fullPath;
|
|
2067
|
+
}
|
|
2068
|
+
}
|
|
2069
|
+
}
|
|
2070
|
+
try {
|
|
2071
|
+
const entries = await readdir2(projectPath, { withFileTypes: true });
|
|
2072
|
+
for (const entry of entries) {
|
|
2073
|
+
if (entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules") {
|
|
2074
|
+
const dirPath = path2.join(projectPath, entry.name);
|
|
2075
|
+
if (existsSync(path2.join(dirPath, "index.html"))) {
|
|
2076
|
+
return dirPath;
|
|
2077
|
+
}
|
|
2078
|
+
}
|
|
2079
|
+
}
|
|
2080
|
+
} catch {
|
|
2081
|
+
}
|
|
2082
|
+
return null;
|
|
2083
|
+
}
|
|
2084
|
+
/**
|
|
2085
|
+
* Detect if running on Cloudflare Pages
|
|
2086
|
+
*/
|
|
2087
|
+
isCloudflarePages() {
|
|
2088
|
+
return process.env.CF_PAGES === "1" || !!process.env.CF_PAGES_URL;
|
|
2089
|
+
}
|
|
2090
|
+
/**
|
|
2091
|
+
* Get project name from package.json or directory
|
|
2092
|
+
*/
|
|
2093
|
+
async getProjectName(projectPath = ".") {
|
|
2094
|
+
if (process.env.CF_PAGES_PROJECT_NAME) {
|
|
2095
|
+
return process.env.CF_PAGES_PROJECT_NAME;
|
|
2096
|
+
}
|
|
2097
|
+
try {
|
|
2098
|
+
const pkgPath = path2.join(projectPath, "package.json");
|
|
2099
|
+
const pkgContent = await readFile2(pkgPath, "utf-8");
|
|
2100
|
+
const pkg = JSON.parse(pkgContent);
|
|
2101
|
+
if (pkg.name) {
|
|
2102
|
+
return pkg.name;
|
|
2103
|
+
}
|
|
2104
|
+
} catch {
|
|
2105
|
+
}
|
|
2106
|
+
return path2.basename(path2.resolve(projectPath));
|
|
2107
|
+
}
|
|
2108
|
+
/**
|
|
2109
|
+
* Deploy to Cloudflare Pages
|
|
2110
|
+
*/
|
|
2111
|
+
async deployToCloudflarePages(projectPath = ".", config = {}) {
|
|
2112
|
+
const logs = [];
|
|
2113
|
+
try {
|
|
2114
|
+
const buildDir = await this.detectBuildDir(projectPath);
|
|
2115
|
+
if (!buildDir) {
|
|
2116
|
+
throw new Error('No build output found. Run "jxr build" first.');
|
|
2117
|
+
}
|
|
2118
|
+
logs.push(`\u{1F4C1} Build directory: ${buildDir}`);
|
|
2119
|
+
const projectName = await this.getProjectName(projectPath);
|
|
2120
|
+
logs.push(`\u{1F4E6} Project: ${projectName}`);
|
|
2121
|
+
const manifestPath = path2.join(buildDir, "jxr-manifest.json");
|
|
2122
|
+
if (existsSync(manifestPath)) {
|
|
2123
|
+
const manifest = JSON.parse(await readFile2(manifestPath, "utf-8"));
|
|
2124
|
+
logs.push(`\u{1F4CB} Manifest: ${manifest.platform} platform, ${manifest.files?.length || 0} files`);
|
|
2125
|
+
}
|
|
2126
|
+
const env = config.environment || "production";
|
|
2127
|
+
const url = `https://${projectName}.app.jxrstudios.online`;
|
|
2128
|
+
logs.push(`\u{1F310} URL: ${url}`);
|
|
2129
|
+
if (this.isCloudflarePages()) {
|
|
2130
|
+
logs.push("\u2601\uFE0F Cloudflare Pages auto-detected");
|
|
2131
|
+
logs.push("\u2705 Deployment ready - build output will be deployed automatically");
|
|
2132
|
+
return {
|
|
2133
|
+
success: true,
|
|
2134
|
+
url,
|
|
2135
|
+
deploymentId: `cf-pages-${Date.now()}`,
|
|
2136
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2137
|
+
logs
|
|
2138
|
+
};
|
|
2139
|
+
}
|
|
2140
|
+
return await this.deploy(buildDir, { ...config, projectId: projectName });
|
|
2141
|
+
} catch (error) {
|
|
2142
|
+
const errorMsg = error instanceof Error ? error.message : "Unknown error";
|
|
2143
|
+
logs.push(`\u274C Error: ${errorMsg}`);
|
|
2144
|
+
return {
|
|
2145
|
+
success: false,
|
|
2146
|
+
url: "",
|
|
2147
|
+
deploymentId: "",
|
|
2148
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2149
|
+
logs
|
|
2150
|
+
};
|
|
2151
|
+
}
|
|
2152
|
+
}
|
|
2055
2153
|
/**
|
|
2056
2154
|
* Deploy the current project to JXR infrastructure
|
|
2057
2155
|
*/
|
|
@@ -2068,7 +2166,7 @@ var JXRDeployer = class {
|
|
|
2068
2166
|
logs.push(`\u{1F4E6} Created tarball: ${tarballPath}`);
|
|
2069
2167
|
const uploadResult = await this.uploadTarball(tarballPath, pid, env, branch);
|
|
2070
2168
|
logs.push(`\u2601\uFE0F Uploaded to JXR`);
|
|
2071
|
-
const url = `https://${pid}.
|
|
2169
|
+
const url = `https://${pid}.app.jxrstudios.online`;
|
|
2072
2170
|
logs.push(`\u{1F310} Live at: ${url}`);
|
|
2073
2171
|
return {
|
|
2074
2172
|
success: true,
|
package/package.json
CHANGED
package/src/deployer.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
import { readFile, readdir, stat } from "fs/promises";
|
|
8
8
|
import path from "path";
|
|
9
|
-
import { createWriteStream } from "fs";
|
|
9
|
+
import { createWriteStream, existsSync } from "fs";
|
|
10
10
|
import { spawn } from "child_process";
|
|
11
11
|
|
|
12
12
|
export interface DeployConfig {
|
|
@@ -43,6 +43,134 @@ export class JXRDeployer {
|
|
|
43
43
|
this.projectId = projectId || this.generateProjectId();
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
/**
|
|
47
|
+
* Auto-detect build output directory
|
|
48
|
+
* Checks dist/, build/, out/, or any directory with index.html
|
|
49
|
+
*/
|
|
50
|
+
async detectBuildDir(projectPath: string = '.'): Promise<string | null> {
|
|
51
|
+
const possibleDirs = ['dist', 'build', 'out'];
|
|
52
|
+
|
|
53
|
+
// Check standard build directories
|
|
54
|
+
for (const dir of possibleDirs) {
|
|
55
|
+
const fullPath = path.resolve(projectPath, dir);
|
|
56
|
+
if (existsSync(fullPath)) {
|
|
57
|
+
// Verify it has index.html
|
|
58
|
+
if (existsSync(path.join(fullPath, 'index.html'))) {
|
|
59
|
+
return fullPath;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Search for any directory with index.html
|
|
65
|
+
try {
|
|
66
|
+
const entries = await readdir(projectPath, { withFileTypes: true });
|
|
67
|
+
for (const entry of entries) {
|
|
68
|
+
if (entry.isDirectory() && !entry.name.startsWith('.') && entry.name !== 'node_modules') {
|
|
69
|
+
const dirPath = path.join(projectPath, entry.name);
|
|
70
|
+
if (existsSync(path.join(dirPath, 'index.html'))) {
|
|
71
|
+
return dirPath;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
} catch {
|
|
76
|
+
// Ignore errors
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Detect if running on Cloudflare Pages
|
|
84
|
+
*/
|
|
85
|
+
isCloudflarePages(): boolean {
|
|
86
|
+
return process.env.CF_PAGES === '1' || !!process.env.CF_PAGES_URL;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Get project name from package.json or directory
|
|
91
|
+
*/
|
|
92
|
+
async getProjectName(projectPath: string = '.'): Promise<string> {
|
|
93
|
+
// Check CF_PAGES_PROJECT_NAME first
|
|
94
|
+
if (process.env.CF_PAGES_PROJECT_NAME) {
|
|
95
|
+
return process.env.CF_PAGES_PROJECT_NAME;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Try to read from package.json
|
|
99
|
+
try {
|
|
100
|
+
const pkgPath = path.join(projectPath, 'package.json');
|
|
101
|
+
const pkgContent = await readFile(pkgPath, 'utf-8');
|
|
102
|
+
const pkg = JSON.parse(pkgContent);
|
|
103
|
+
if (pkg.name) {
|
|
104
|
+
return pkg.name;
|
|
105
|
+
}
|
|
106
|
+
} catch {
|
|
107
|
+
// Ignore errors
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Fall back to directory name
|
|
111
|
+
return path.basename(path.resolve(projectPath));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Deploy to Cloudflare Pages
|
|
116
|
+
*/
|
|
117
|
+
async deployToCloudflarePages(projectPath: string = '.', config: DeployConfig = {}): Promise<DeployResult> {
|
|
118
|
+
const logs: string[] = [];
|
|
119
|
+
|
|
120
|
+
try {
|
|
121
|
+
// Auto-detect build directory
|
|
122
|
+
const buildDir = await this.detectBuildDir(projectPath);
|
|
123
|
+
if (!buildDir) {
|
|
124
|
+
throw new Error('No build output found. Run "jxr build" first.');
|
|
125
|
+
}
|
|
126
|
+
logs.push(`📁 Build directory: ${buildDir}`);
|
|
127
|
+
|
|
128
|
+
// Get project name
|
|
129
|
+
const projectName = await this.getProjectName(projectPath);
|
|
130
|
+
logs.push(`📦 Project: ${projectName}`);
|
|
131
|
+
|
|
132
|
+
// Check for manifest
|
|
133
|
+
const manifestPath = path.join(buildDir, 'jxr-manifest.json');
|
|
134
|
+
if (existsSync(manifestPath)) {
|
|
135
|
+
const manifest = JSON.parse(await readFile(manifestPath, 'utf-8'));
|
|
136
|
+
logs.push(`📋 Manifest: ${manifest.platform} platform, ${manifest.files?.length || 0} files`);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Determine URL
|
|
140
|
+
const env = config.environment || 'production';
|
|
141
|
+
const url = `https://${projectName}.app.jxrstudios.online`;
|
|
142
|
+
logs.push(`🌐 URL: ${url}`);
|
|
143
|
+
|
|
144
|
+
// If running on Cloudflare Pages, the deployment is automatic
|
|
145
|
+
if (this.isCloudflarePages()) {
|
|
146
|
+
logs.push('☁️ Cloudflare Pages auto-detected');
|
|
147
|
+
logs.push('✅ Deployment ready - build output will be deployed automatically');
|
|
148
|
+
|
|
149
|
+
return {
|
|
150
|
+
success: true,
|
|
151
|
+
url,
|
|
152
|
+
deploymentId: `cf-pages-${Date.now()}`,
|
|
153
|
+
timestamp: new Date().toISOString(),
|
|
154
|
+
logs,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Manual deployment via JXR API
|
|
159
|
+
return await this.deploy(buildDir, { ...config, projectId: projectName });
|
|
160
|
+
|
|
161
|
+
} catch (error) {
|
|
162
|
+
const errorMsg = error instanceof Error ? error.message : 'Unknown error';
|
|
163
|
+
logs.push(`❌ Error: ${errorMsg}`);
|
|
164
|
+
return {
|
|
165
|
+
success: false,
|
|
166
|
+
url: '',
|
|
167
|
+
deploymentId: '',
|
|
168
|
+
timestamp: new Date().toISOString(),
|
|
169
|
+
logs,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
46
174
|
/**
|
|
47
175
|
* Deploy the current project to JXR infrastructure
|
|
48
176
|
*/
|
|
@@ -69,7 +197,7 @@ export class JXRDeployer {
|
|
|
69
197
|
logs.push(`☁️ Uploaded to JXR`);
|
|
70
198
|
|
|
71
199
|
// Get deployment URL
|
|
72
|
-
const url = `https://${pid}.
|
|
200
|
+
const url = `https://${pid}.app.jxrstudios.online`;
|
|
73
201
|
logs.push(`🌐 Live at: ${url}`);
|
|
74
202
|
|
|
75
203
|
return {
|