@tothalex/nulljs 0.0.57 → 0.0.59
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +156 -142
- package/package.json +4 -4
package/dist/cli.js
CHANGED
|
@@ -53,6 +53,10 @@ var buildUrl = (path, options) => {
|
|
|
53
53
|
var init_deployments = () => {};
|
|
54
54
|
|
|
55
55
|
// ../../packages/api/actions/deploy.ts
|
|
56
|
+
import { mkdtempSync, writeFileSync, rmSync } from "fs";
|
|
57
|
+
import { tmpdir } from "os";
|
|
58
|
+
import { join } from "path";
|
|
59
|
+
var {$ } = globalThis.Bun;
|
|
56
60
|
var getMimeType = (fileName) => {
|
|
57
61
|
if (fileName.endsWith(".js"))
|
|
58
62
|
return "application/javascript";
|
|
@@ -79,15 +83,45 @@ var getMimeType = (fileName) => {
|
|
|
79
83
|
const dataString = sortedBlobs.map((b) => `${b.fileName}:${b.size}:${b.type}`).join("|");
|
|
80
84
|
const sign = await crypto.subtle.sign("Ed25519", privateKey, Buffer.from(dataString));
|
|
81
85
|
return btoa(String.fromCharCode(...new Uint8Array(sign)));
|
|
86
|
+
}, postWithCurl = async (url, signature, assets2) => {
|
|
87
|
+
const tempDir = mkdtempSync(join(tmpdir(), "nulljs-deploy-"));
|
|
88
|
+
try {
|
|
89
|
+
const formParts = [];
|
|
90
|
+
for (const asset of assets2) {
|
|
91
|
+
const filePath = join(tempDir, asset.fileName);
|
|
92
|
+
writeFileSync(filePath, asset.code);
|
|
93
|
+
const mime = getMimeType(asset.fileName);
|
|
94
|
+
formParts.push(`-F`);
|
|
95
|
+
formParts.push(`${asset.fileName}=@${filePath};type=${mime}`);
|
|
96
|
+
}
|
|
97
|
+
const result = await $`curl -s -w '\n%{http_code}' -X POST -H "authorization: ${signature}" ${formParts} ${url}`.text();
|
|
98
|
+
const lines = result.trim().split(`
|
|
99
|
+
`);
|
|
100
|
+
const statusCode = parseInt(lines.pop() || "0", 10);
|
|
101
|
+
const body = lines.join(`
|
|
102
|
+
`);
|
|
103
|
+
return { ok: statusCode >= 200 && statusCode < 300, status: statusCode, text: body };
|
|
104
|
+
} finally {
|
|
105
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
106
|
+
}
|
|
82
107
|
}, createFunctionDeployment = async (deployment2, options) => {
|
|
83
108
|
const handler = deployment2.assets.find((asset) => asset.fileName === "handler.js");
|
|
84
109
|
if (!handler) {
|
|
85
110
|
throw new Error(`Handler not found for ${deployment2.name}`);
|
|
86
111
|
}
|
|
87
|
-
const {
|
|
112
|
+
const { blobs } = createFormDataWithInfo([handler]);
|
|
88
113
|
const signature = await createSignature(blobs, options.privateKey);
|
|
89
114
|
const baseUrl = options.url || "";
|
|
90
|
-
const
|
|
115
|
+
const url = `${baseUrl}/api/deployment`;
|
|
116
|
+
if (options.useCurl) {
|
|
117
|
+
const result = await postWithCurl(url, signature, [handler]);
|
|
118
|
+
if (!result.ok) {
|
|
119
|
+
throw new Error(`Deployment failed (${result.status}): ${result.text}`);
|
|
120
|
+
}
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
const { form } = createFormDataWithInfo([handler]);
|
|
124
|
+
const response = await fetch(url, {
|
|
91
125
|
method: "POST",
|
|
92
126
|
headers: {
|
|
93
127
|
authorization: signature
|
|
@@ -99,10 +133,19 @@ var getMimeType = (fileName) => {
|
|
|
99
133
|
throw new Error(`Deployment failed (${response.status}): ${errorText}`);
|
|
100
134
|
}
|
|
101
135
|
}, createReactDeployment = async (deployment2, options) => {
|
|
102
|
-
const {
|
|
136
|
+
const { blobs } = createFormDataWithInfo(deployment2.assets);
|
|
103
137
|
const signature = await createSignature(blobs, options.privateKey);
|
|
104
138
|
const baseUrl = options.url || "";
|
|
105
|
-
const
|
|
139
|
+
const url = `${baseUrl}/api/deployment/react`;
|
|
140
|
+
if (options.useCurl) {
|
|
141
|
+
const result = await postWithCurl(url, signature, deployment2.assets);
|
|
142
|
+
if (!result.ok) {
|
|
143
|
+
throw new Error(`Deployment failed (${result.status}): ${result.text}`);
|
|
144
|
+
}
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
const { form } = createFormDataWithInfo(deployment2.assets);
|
|
148
|
+
const response = await fetch(url, {
|
|
106
149
|
method: "POST",
|
|
107
150
|
headers: {
|
|
108
151
|
authorization: signature
|
|
@@ -114,6 +157,7 @@ var getMimeType = (fileName) => {
|
|
|
114
157
|
throw new Error(`Deployment failed (${response.status}): ${errorText}`);
|
|
115
158
|
}
|
|
116
159
|
};
|
|
160
|
+
var init_deploy = () => {};
|
|
117
161
|
|
|
118
162
|
// ../../packages/api/actions/invocations.ts
|
|
119
163
|
var init_invocations = () => {};
|
|
@@ -195,6 +239,7 @@ var createSignatureHeader = async (privateKey, props) => {
|
|
|
195
239
|
// ../../packages/api/actions/index.ts
|
|
196
240
|
var init_actions = __esm(() => {
|
|
197
241
|
init_deployments();
|
|
242
|
+
init_deploy();
|
|
198
243
|
init_invocations();
|
|
199
244
|
init_logs();
|
|
200
245
|
init_assets();
|
|
@@ -208,10 +253,10 @@ var init_api = __esm(() => {
|
|
|
208
253
|
});
|
|
209
254
|
|
|
210
255
|
// src/config/index.ts
|
|
211
|
-
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
|
|
212
|
-
import { join } from "path";
|
|
256
|
+
import { readFileSync, writeFileSync as writeFileSync2, existsSync, mkdirSync } from "fs";
|
|
257
|
+
import { join as join2 } from "path";
|
|
213
258
|
import chalk from "chalk";
|
|
214
|
-
var getLocalConfigDir = () =>
|
|
259
|
+
var getLocalConfigDir = () => join2(process.cwd(), ".nulljs"), getLocalConfigFile = () => join2(getLocalConfigDir(), "config.json"), ensureLocalConfigDir = () => {
|
|
215
260
|
const localDir = getLocalConfigDir();
|
|
216
261
|
if (!existsSync(localDir)) {
|
|
217
262
|
mkdirSync(localDir, { recursive: true });
|
|
@@ -229,7 +274,7 @@ var getLocalConfigDir = () => join(process.cwd(), ".nulljs"), getLocalConfigFile
|
|
|
229
274
|
}
|
|
230
275
|
}, writeConfigStore = (store) => {
|
|
231
276
|
ensureLocalConfigDir();
|
|
232
|
-
|
|
277
|
+
writeFileSync2(getLocalConfigFile(), JSON.stringify(store, null, 2));
|
|
233
278
|
}, readLocalConfig = () => {
|
|
234
279
|
const store = readConfigStore();
|
|
235
280
|
if (!store)
|
|
@@ -328,14 +373,14 @@ var init_config = () => {};
|
|
|
328
373
|
|
|
329
374
|
// src/lib/server.ts
|
|
330
375
|
import { existsSync as existsSync2, mkdirSync as mkdirSync2, realpathSync } from "fs";
|
|
331
|
-
import { join as
|
|
376
|
+
import { join as join3, dirname } from "path";
|
|
332
377
|
import chalk2 from "chalk";
|
|
333
378
|
var CLI_DIR, PLATFORMS, getPlatformKey = () => {
|
|
334
379
|
return `${process.platform}-${process.arch}`;
|
|
335
380
|
}, findProjectRoot = (startPath = process.cwd()) => {
|
|
336
381
|
let currentPath = startPath;
|
|
337
382
|
while (currentPath !== dirname(currentPath)) {
|
|
338
|
-
if (existsSync2(
|
|
383
|
+
if (existsSync2(join3(currentPath, "package.json"))) {
|
|
339
384
|
return currentPath;
|
|
340
385
|
}
|
|
341
386
|
currentPath = dirname(currentPath);
|
|
@@ -344,7 +389,7 @@ var CLI_DIR, PLATFORMS, getPlatformKey = () => {
|
|
|
344
389
|
}, findMonorepoRoot = (startPath = process.cwd()) => {
|
|
345
390
|
let currentPath = startPath;
|
|
346
391
|
while (currentPath !== dirname(currentPath)) {
|
|
347
|
-
if (existsSync2(
|
|
392
|
+
if (existsSync2(join3(currentPath, "Cargo.toml")) && existsSync2(join3(currentPath, "package.json"))) {
|
|
348
393
|
return currentPath;
|
|
349
394
|
}
|
|
350
395
|
currentPath = dirname(currentPath);
|
|
@@ -357,11 +402,11 @@ var CLI_DIR, PLATFORMS, getPlatformKey = () => {
|
|
|
357
402
|
}
|
|
358
403
|
const monorepoRoot = findMonorepoRoot() || findMonorepoRoot(CLI_DIR);
|
|
359
404
|
if (monorepoRoot) {
|
|
360
|
-
const debugBinary =
|
|
405
|
+
const debugBinary = join3(monorepoRoot, "target", "debug", "server");
|
|
361
406
|
if (existsSync2(debugBinary)) {
|
|
362
407
|
return { path: debugBinary, source: "local-debug" };
|
|
363
408
|
}
|
|
364
|
-
const releaseBinary =
|
|
409
|
+
const releaseBinary = join3(monorepoRoot, "target", "release", "server");
|
|
365
410
|
if (existsSync2(releaseBinary)) {
|
|
366
411
|
return { path: releaseBinary, source: "local-release" };
|
|
367
412
|
}
|
|
@@ -384,7 +429,7 @@ var CLI_DIR, PLATFORMS, getPlatformKey = () => {
|
|
|
384
429
|
args.push("--api-port", "3000");
|
|
385
430
|
args.push("--gateway-port", "3001");
|
|
386
431
|
const projectRoot = findProjectRoot();
|
|
387
|
-
const cloudPath =
|
|
432
|
+
const cloudPath = join3(projectRoot, ".nulljs");
|
|
388
433
|
args.push("--cloud-path", cloudPath);
|
|
389
434
|
if (!existsSync2(cloudPath)) {
|
|
390
435
|
mkdirSync2(cloudPath, { recursive: true });
|
|
@@ -440,69 +485,7 @@ var init_server = __esm(() => {
|
|
|
440
485
|
import { basename, extname } from "path";
|
|
441
486
|
import react from "@vitejs/plugin-react";
|
|
442
487
|
import tailwindcss from "@tailwindcss/vite";
|
|
443
|
-
var
|
|
444
|
-
const entry = basename(filePath, extname(filePath));
|
|
445
|
-
const virtualPrefix = `virtual:config/${entry}.tsx`;
|
|
446
|
-
return {
|
|
447
|
-
name: "nulljs-config-plugin",
|
|
448
|
-
apply: "build",
|
|
449
|
-
config: async (config, { command }) => {
|
|
450
|
-
if (command !== "build") {
|
|
451
|
-
return config;
|
|
452
|
-
}
|
|
453
|
-
return {
|
|
454
|
-
build: {
|
|
455
|
-
ssr: true,
|
|
456
|
-
rollupOptions: {
|
|
457
|
-
input: {
|
|
458
|
-
[entry]: virtualPrefix
|
|
459
|
-
},
|
|
460
|
-
external: ["react", "react/jsx-runtime"],
|
|
461
|
-
output: {
|
|
462
|
-
entryFileNames: "[name].js"
|
|
463
|
-
},
|
|
464
|
-
preserveEntrySignatures: "strict"
|
|
465
|
-
}
|
|
466
|
-
}
|
|
467
|
-
};
|
|
468
|
-
},
|
|
469
|
-
resolveId: (id) => {
|
|
470
|
-
if (id === virtualPrefix) {
|
|
471
|
-
return id;
|
|
472
|
-
}
|
|
473
|
-
return null;
|
|
474
|
-
},
|
|
475
|
-
load: (id) => {
|
|
476
|
-
if (id === virtualPrefix) {
|
|
477
|
-
const script = `
|
|
478
|
-
import { config } from "${filePath.replace(/\\/g, "\\\\")}";
|
|
479
|
-
|
|
480
|
-
export default config;
|
|
481
|
-
`;
|
|
482
|
-
return script;
|
|
483
|
-
}
|
|
484
|
-
return null;
|
|
485
|
-
},
|
|
486
|
-
transform: (code, id) => {
|
|
487
|
-
if (id === filePath) {
|
|
488
|
-
const configMatch = code.match(/export const config = \{[^}]*\};/);
|
|
489
|
-
return configMatch ? { code: configMatch[0], map: null } : null;
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
};
|
|
493
|
-
}, spaConfigConfig = (filePath) => {
|
|
494
|
-
return {
|
|
495
|
-
logLevel: "error",
|
|
496
|
-
plugins: [
|
|
497
|
-
jsConfig({
|
|
498
|
-
filePath
|
|
499
|
-
})
|
|
500
|
-
],
|
|
501
|
-
build: {
|
|
502
|
-
outDir: "/tmp"
|
|
503
|
-
}
|
|
504
|
-
};
|
|
505
|
-
}, jsSpa = ({ filePath }) => {
|
|
488
|
+
var jsSpa = ({ filePath }) => {
|
|
506
489
|
const entry = basename(filePath, extname(filePath));
|
|
507
490
|
const virtualPrefix = `virtual:spa/${entry}.tsx`;
|
|
508
491
|
return {
|
|
@@ -520,7 +503,9 @@ var jsConfig = ({ filePath }) => {
|
|
|
520
503
|
},
|
|
521
504
|
external: ["cloud"],
|
|
522
505
|
output: {
|
|
523
|
-
entryFileNames: "[name].js"
|
|
506
|
+
entryFileNames: "[name].js",
|
|
507
|
+
chunkFileNames: "[name]-[hash].js",
|
|
508
|
+
assetFileNames: "[name]-[hash][extname]"
|
|
524
509
|
}
|
|
525
510
|
}
|
|
526
511
|
},
|
|
@@ -708,7 +693,7 @@ var deployedHashes, clearDeployCache = () => {
|
|
|
708
693
|
deployedHashes.clear();
|
|
709
694
|
}, hashCode = (code) => {
|
|
710
695
|
return Bun.hash(code).toString(16);
|
|
711
|
-
}, deployFunction = async (filePath, privateKey, apiUrl) => {
|
|
696
|
+
}, deployFunction = async (filePath, privateKey, apiUrl, useCurl) => {
|
|
712
697
|
const fileName = basename2(filePath);
|
|
713
698
|
const result = await build(functionConfig(filePath));
|
|
714
699
|
const handler = result.output.find((output) => output.type === "chunk" && output.fileName === "handler.js");
|
|
@@ -720,19 +705,13 @@ var deployedHashes, clearDeployCache = () => {
|
|
|
720
705
|
if (lastHash === hash) {
|
|
721
706
|
return { deployed: false, skipped: true };
|
|
722
707
|
}
|
|
723
|
-
await createFunctionDeployment({ name: fileName, assets: [{ fileName: "handler.js", code: handler.code }] }, { privateKey, url: apiUrl });
|
|
708
|
+
await createFunctionDeployment({ name: fileName, assets: [{ fileName: "handler.js", code: handler.code }] }, { privateKey, url: apiUrl, useCurl });
|
|
724
709
|
deployedHashes.set(filePath, hash);
|
|
725
710
|
return { deployed: true, skipped: false };
|
|
726
|
-
}, deployReact = async (filePath, privateKey, apiUrl) => {
|
|
711
|
+
}, deployReact = async (filePath, privateKey, apiUrl, useCurl) => {
|
|
727
712
|
const fileName = basename2(filePath);
|
|
728
|
-
const configResult = await build(spaConfigConfig(filePath));
|
|
729
|
-
const configOutput = configResult.output.find((output) => output.type === "chunk" && output.fileName === "index.js");
|
|
730
|
-
if (!configOutput) {
|
|
731
|
-
throw new Error("Config build failed: config file not found in output");
|
|
732
|
-
}
|
|
733
713
|
const result = await build(spaClientConfig(filePath));
|
|
734
714
|
const assets3 = [];
|
|
735
|
-
assets3.push({ fileName: "config.js", code: configOutput.code });
|
|
736
715
|
for (const output of result.output) {
|
|
737
716
|
if (output.type === "chunk") {
|
|
738
717
|
assets3.push({ fileName: output.fileName, code: output.code });
|
|
@@ -746,11 +725,11 @@ var deployedHashes, clearDeployCache = () => {
|
|
|
746
725
|
if (lastHash === hash) {
|
|
747
726
|
return { deployed: false, skipped: true };
|
|
748
727
|
}
|
|
749
|
-
await createReactDeployment({ name: fileName, assets: assets3 }, { privateKey, url: apiUrl });
|
|
728
|
+
await createReactDeployment({ name: fileName, assets: assets3 }, { privateKey, url: apiUrl, useCurl });
|
|
750
729
|
deployedHashes.set(filePath, hash);
|
|
751
730
|
return { deployed: true, skipped: false };
|
|
752
731
|
};
|
|
753
|
-
var
|
|
732
|
+
var init_deploy2 = __esm(() => {
|
|
754
733
|
init_api();
|
|
755
734
|
init_bundle();
|
|
756
735
|
deployedHashes = new Map;
|
|
@@ -759,19 +738,19 @@ var init_deploy = __esm(() => {
|
|
|
759
738
|
// src/lib/watcher.ts
|
|
760
739
|
import { watch, existsSync as existsSync3 } from "fs";
|
|
761
740
|
import { readdir } from "fs/promises";
|
|
762
|
-
import { join as
|
|
741
|
+
import { join as join4 } from "path";
|
|
763
742
|
import { build as build2 } from "vite";
|
|
764
743
|
var FUNCTION_DIRS, deployedHashes2, hashCode2 = (code) => {
|
|
765
744
|
return Bun.hash(code).toString(16);
|
|
766
745
|
}, findFunctionEntries = async (functionsPath) => {
|
|
767
746
|
const entries = [];
|
|
768
747
|
for (const dir of FUNCTION_DIRS) {
|
|
769
|
-
const dirPath =
|
|
748
|
+
const dirPath = join4(functionsPath, dir);
|
|
770
749
|
try {
|
|
771
750
|
const items = await readdir(dirPath, { withFileTypes: true });
|
|
772
751
|
for (const item of items) {
|
|
773
752
|
if (item.isFile() && (item.name.endsWith(".ts") || item.name.endsWith(".tsx"))) {
|
|
774
|
-
const fullPath =
|
|
753
|
+
const fullPath = join4(dirPath, item.name);
|
|
775
754
|
const name = `${dir}/${item.name.replace(/\.tsx?$/, "")}`;
|
|
776
755
|
entries.push({
|
|
777
756
|
name,
|
|
@@ -791,7 +770,7 @@ var FUNCTION_DIRS, deployedHashes2, hashCode2 = (code) => {
|
|
|
791
770
|
console.error('No local configuration found. Run "nulljs dev" first.');
|
|
792
771
|
return () => {};
|
|
793
772
|
}
|
|
794
|
-
const functionsPath =
|
|
773
|
+
const functionsPath = join4(srcPath, "function");
|
|
795
774
|
let entries = await findFunctionEntries(functionsPath);
|
|
796
775
|
let viteWatcher = null;
|
|
797
776
|
let isRestarting = false;
|
|
@@ -862,7 +841,7 @@ var FUNCTION_DIRS, deployedHashes2, hashCode2 = (code) => {
|
|
|
862
841
|
viteWatcher = await startViteWatcher();
|
|
863
842
|
const dirWatchers = [];
|
|
864
843
|
for (const dir of FUNCTION_DIRS) {
|
|
865
|
-
const dirPath =
|
|
844
|
+
const dirPath = join4(functionsPath, dir);
|
|
866
845
|
if (!existsSync3(dirPath))
|
|
867
846
|
continue;
|
|
868
847
|
try {
|
|
@@ -871,7 +850,7 @@ var FUNCTION_DIRS, deployedHashes2, hashCode2 = (code) => {
|
|
|
871
850
|
return;
|
|
872
851
|
if (!filename.endsWith(".ts") && !filename.endsWith(".tsx"))
|
|
873
852
|
return;
|
|
874
|
-
const fullPath =
|
|
853
|
+
const fullPath = join4(dirPath, filename);
|
|
875
854
|
const entryName = `${dir}/${filename.replace(/\.tsx?$/, "")}`;
|
|
876
855
|
const existingEntry = entries.find((e) => e.name === entryName);
|
|
877
856
|
const fileExists = existsSync3(fullPath);
|
|
@@ -898,14 +877,14 @@ var FUNCTION_DIRS, deployedHashes2, hashCode2 = (code) => {
|
|
|
898
877
|
}
|
|
899
878
|
const privateKey = await loadPrivateKey(config);
|
|
900
879
|
deployedHashes2.clear();
|
|
901
|
-
const functionsPath =
|
|
880
|
+
const functionsPath = join4(srcPath, "function");
|
|
902
881
|
const entries = await findFunctionEntries(functionsPath);
|
|
903
|
-
const reactEntry =
|
|
882
|
+
const reactEntry = join4(srcPath, "index.tsx");
|
|
904
883
|
const hasReact = existsSync3(reactEntry);
|
|
905
884
|
let total = entries.length + (hasReact ? 1 : 0);
|
|
906
885
|
let successful = 0;
|
|
907
886
|
let failed = 0;
|
|
908
|
-
const { deployFunction: deployFunction2, deployReact: deployReact2, clearDeployCache: clearDeployCache2 } = await Promise.resolve().then(() => (
|
|
887
|
+
const { deployFunction: deployFunction2, deployReact: deployReact2, clearDeployCache: clearDeployCache2 } = await Promise.resolve().then(() => (init_deploy2(), exports_deploy));
|
|
909
888
|
clearDeployCache2();
|
|
910
889
|
for (const entry of entries) {
|
|
911
890
|
try {
|
|
@@ -1751,9 +1730,9 @@ var registerDevCommand = (program) => {
|
|
|
1751
1730
|
// src/commands/deploy.ts
|
|
1752
1731
|
init_config();
|
|
1753
1732
|
init_bundle();
|
|
1754
|
-
|
|
1733
|
+
init_deploy2();
|
|
1755
1734
|
import chalk3 from "chalk";
|
|
1756
|
-
import { basename as basename4, resolve as resolve2, join as
|
|
1735
|
+
import { basename as basename4, resolve as resolve2, join as join5 } from "path";
|
|
1757
1736
|
import { existsSync as existsSync5 } from "fs";
|
|
1758
1737
|
import { readdir as readdir2 } from "fs/promises";
|
|
1759
1738
|
import * as p from "@clack/prompts";
|
|
@@ -1783,24 +1762,24 @@ var selectConfig = async () => {
|
|
|
1783
1762
|
};
|
|
1784
1763
|
var findAllDeployables = async (srcPath) => {
|
|
1785
1764
|
const functions = [];
|
|
1786
|
-
const functionsPath =
|
|
1765
|
+
const functionsPath = join5(srcPath, "function");
|
|
1787
1766
|
for (const dir of FUNCTION_DIRS2) {
|
|
1788
|
-
const dirPath =
|
|
1767
|
+
const dirPath = join5(functionsPath, dir);
|
|
1789
1768
|
try {
|
|
1790
1769
|
const items = await readdir2(dirPath, { withFileTypes: true });
|
|
1791
1770
|
for (const item of items) {
|
|
1792
1771
|
if (item.isFile() && (item.name.endsWith(".ts") || item.name.endsWith(".tsx"))) {
|
|
1793
|
-
functions.push(
|
|
1772
|
+
functions.push(join5(dirPath, item.name));
|
|
1794
1773
|
}
|
|
1795
1774
|
}
|
|
1796
1775
|
} catch {}
|
|
1797
1776
|
}
|
|
1798
|
-
const reactEntry =
|
|
1777
|
+
const reactEntry = join5(srcPath, "index.tsx");
|
|
1799
1778
|
const hasReact = existsSync5(reactEntry);
|
|
1800
1779
|
return { functions, reactEntry: hasReact ? reactEntry : null };
|
|
1801
1780
|
};
|
|
1802
1781
|
var deployAll = async (config, options) => {
|
|
1803
|
-
const srcPath =
|
|
1782
|
+
const srcPath = join5(process.cwd(), "src");
|
|
1804
1783
|
if (!existsSync5(srcPath)) {
|
|
1805
1784
|
console.error(chalk3.red("\u2717 No src directory found"));
|
|
1806
1785
|
process.exit(1);
|
|
@@ -1824,7 +1803,7 @@ var deployAll = async (config, options) => {
|
|
|
1824
1803
|
const fileName = basename4(filePath);
|
|
1825
1804
|
try {
|
|
1826
1805
|
console.log(chalk3.yellow("Bundling ") + chalk3.bold(fileName));
|
|
1827
|
-
const result = await deployFunction(filePath, privateKey, config.api);
|
|
1806
|
+
const result = await deployFunction(filePath, privateKey, config.api, options.curl);
|
|
1828
1807
|
if (result.skipped) {
|
|
1829
1808
|
console.log(chalk3.gray("\u2013 Skipped ") + chalk3.bold(fileName) + chalk3.gray(" (unchanged)"));
|
|
1830
1809
|
skipped++;
|
|
@@ -1841,7 +1820,7 @@ var deployAll = async (config, options) => {
|
|
|
1841
1820
|
const fileName = basename4(reactEntry);
|
|
1842
1821
|
try {
|
|
1843
1822
|
console.log(chalk3.yellow("Bundling React SPA ") + chalk3.bold(fileName));
|
|
1844
|
-
const result = await deployReact(reactEntry, privateKey, config.api);
|
|
1823
|
+
const result = await deployReact(reactEntry, privateKey, config.api, options.curl);
|
|
1845
1824
|
if (result.skipped) {
|
|
1846
1825
|
console.log(chalk3.gray("\u2013 Skipped ") + chalk3.bold(fileName) + chalk3.gray(" (unchanged)"));
|
|
1847
1826
|
skipped++;
|
|
@@ -1863,7 +1842,7 @@ var deployAll = async (config, options) => {
|
|
|
1863
1842
|
}
|
|
1864
1843
|
};
|
|
1865
1844
|
var registerDeployCommand = (program) => {
|
|
1866
|
-
program.command("deploy").description("Bundle and deploy functions and React SPAs").argument("[file]", "Path to a specific file (deploys all if omitted)").option("-e, --env <name>", "Use a specific config environment").option("-f, --force", "Force deploy even if unchanged").action(async (file, options) => {
|
|
1845
|
+
program.command("deploy").description("Bundle and deploy functions and React SPAs").argument("[file]", "Path to a specific file (deploys all if omitted)").option("-e, --env <name>", "Use a specific config environment").option("-f, --force", "Force deploy even if unchanged").option("--curl", "Use curl instead of fetch (workaround for macOS local network issues)").action(async (file, options) => {
|
|
1867
1846
|
let config;
|
|
1868
1847
|
if (options.env) {
|
|
1869
1848
|
config = getConfig(options.env);
|
|
@@ -1897,7 +1876,7 @@ var registerDeployCommand = (program) => {
|
|
|
1897
1876
|
try {
|
|
1898
1877
|
if (isReact(filePath)) {
|
|
1899
1878
|
console.log(chalk3.yellow("Bundling React SPA ") + chalk3.bold(fileName));
|
|
1900
|
-
const result = await deployReact(filePath, privateKey, config.api);
|
|
1879
|
+
const result = await deployReact(filePath, privateKey, config.api, options.curl);
|
|
1901
1880
|
if (result.skipped) {
|
|
1902
1881
|
console.log(chalk3.gray("\u2013 Skipped ") + chalk3.bold(fileName) + chalk3.gray(" (unchanged)"));
|
|
1903
1882
|
} else {
|
|
@@ -1905,7 +1884,7 @@ var registerDeployCommand = (program) => {
|
|
|
1905
1884
|
}
|
|
1906
1885
|
} else {
|
|
1907
1886
|
console.log(chalk3.yellow("Bundling ") + chalk3.bold(fileName));
|
|
1908
|
-
const result = await deployFunction(filePath, privateKey, config.api);
|
|
1887
|
+
const result = await deployFunction(filePath, privateKey, config.api, options.curl);
|
|
1909
1888
|
if (result.skipped) {
|
|
1910
1889
|
console.log(chalk3.gray("\u2013 Skipped ") + chalk3.bold(fileName) + chalk3.gray(" (unchanged)"));
|
|
1911
1890
|
} else {
|
|
@@ -2322,10 +2301,10 @@ Secret keys:`));
|
|
|
2322
2301
|
}));
|
|
2323
2302
|
};
|
|
2324
2303
|
// src/commands/host.ts
|
|
2325
|
-
import { existsSync as existsSync7, writeFileSync as
|
|
2326
|
-
import { join as
|
|
2327
|
-
import { execSync } from "child_process";
|
|
2304
|
+
import { existsSync as existsSync7, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync2 } from "fs";
|
|
2305
|
+
import { join as join6 } from "path";
|
|
2328
2306
|
import { homedir } from "os";
|
|
2307
|
+
var {$: $2 } = globalThis.Bun;
|
|
2329
2308
|
import chalk7 from "chalk";
|
|
2330
2309
|
var PLATFORMS2 = {
|
|
2331
2310
|
"linux-x64": "@tothalex/nulljs-linux-x64",
|
|
@@ -2355,8 +2334,8 @@ var generateProductionKeys = async () => {
|
|
|
2355
2334
|
const publicKey = btoa(String.fromCharCode(...new Uint8Array(publicKeyBuffer)));
|
|
2356
2335
|
return { privateKey, publicKey };
|
|
2357
2336
|
};
|
|
2358
|
-
var saveProductionConfig = (cloudPath, privateKey, publicKey) => {
|
|
2359
|
-
const configPath =
|
|
2337
|
+
var saveProductionConfig = async (cloudPath, privateKey, publicKey) => {
|
|
2338
|
+
const configPath = join6(cloudPath, "config.json");
|
|
2360
2339
|
const config = {
|
|
2361
2340
|
key: {
|
|
2362
2341
|
private: privateKey,
|
|
@@ -2364,8 +2343,8 @@ var saveProductionConfig = (cloudPath, privateKey, publicKey) => {
|
|
|
2364
2343
|
}
|
|
2365
2344
|
};
|
|
2366
2345
|
console.log(chalk7.blue("Saving production configuration..."));
|
|
2367
|
-
|
|
2368
|
-
|
|
2346
|
+
writeFileSync3(configPath, JSON.stringify(config, null, 2));
|
|
2347
|
+
await $2`chmod 600 ${configPath}`;
|
|
2369
2348
|
console.log(chalk7.green("Production configuration saved"));
|
|
2370
2349
|
};
|
|
2371
2350
|
var createSystemdService = (cloudPath, publicKey, serverBinPath, userName) => {
|
|
@@ -2403,14 +2382,15 @@ var ensureCloudDirectory = (cloudPath) => {
|
|
|
2403
2382
|
console.log(chalk7.gray(`Cloud directory already exists: ${cloudPath}`));
|
|
2404
2383
|
}
|
|
2405
2384
|
};
|
|
2406
|
-
var installSystemdService = (serviceContent) => {
|
|
2385
|
+
var installSystemdService = async (serviceContent) => {
|
|
2407
2386
|
const servicePath = "/etc/systemd/system/nulljs.service";
|
|
2408
2387
|
console.log(chalk7.blue("Installing systemd service..."));
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2388
|
+
writeFileSync3("/tmp/nulljs.service", serviceContent);
|
|
2389
|
+
await $2`sudo mv /tmp/nulljs.service ${servicePath}`;
|
|
2390
|
+
await $2`sudo systemctl daemon-reload`;
|
|
2391
|
+
await $2`sudo systemctl enable nulljs.service`;
|
|
2392
|
+
await $2`sudo systemctl start nulljs.service`;
|
|
2393
|
+
console.log(chalk7.green("Systemd service installed, enabled and started"));
|
|
2414
2394
|
};
|
|
2415
2395
|
var validateLinux = () => {
|
|
2416
2396
|
if (process.platform !== "linux") {
|
|
@@ -2426,7 +2406,7 @@ var validateServerBinary = (serverBinPath) => {
|
|
|
2426
2406
|
}
|
|
2427
2407
|
};
|
|
2428
2408
|
var checkExistingProductionConfig = (cloudPath) => {
|
|
2429
|
-
const configPath =
|
|
2409
|
+
const configPath = join6(cloudPath, "config.json");
|
|
2430
2410
|
if (!existsSync7(configPath)) {
|
|
2431
2411
|
return null;
|
|
2432
2412
|
}
|
|
@@ -2446,13 +2426,13 @@ var unhost = async (options = {}) => {
|
|
|
2446
2426
|
try {
|
|
2447
2427
|
console.log(chalk7.blue("Stopping NullJS service..."));
|
|
2448
2428
|
try {
|
|
2449
|
-
|
|
2429
|
+
await $2`sudo systemctl stop nulljs`;
|
|
2450
2430
|
console.log(chalk7.green("Service stopped"));
|
|
2451
2431
|
} catch {
|
|
2452
2432
|
console.log(chalk7.yellow("Service was not running"));
|
|
2453
2433
|
}
|
|
2454
2434
|
try {
|
|
2455
|
-
|
|
2435
|
+
await $2`sudo systemctl disable nulljs`;
|
|
2456
2436
|
console.log(chalk7.green("Service disabled"));
|
|
2457
2437
|
} catch {
|
|
2458
2438
|
console.log(chalk7.yellow("Service was not enabled"));
|
|
@@ -2460,15 +2440,15 @@ var unhost = async (options = {}) => {
|
|
|
2460
2440
|
const servicePath = "/etc/systemd/system/nulljs.service";
|
|
2461
2441
|
if (existsSync7(servicePath)) {
|
|
2462
2442
|
console.log(chalk7.blue("Removing systemd service file..."));
|
|
2463
|
-
|
|
2464
|
-
|
|
2443
|
+
await $2`sudo rm ${servicePath}`;
|
|
2444
|
+
await $2`sudo systemctl daemon-reload`;
|
|
2465
2445
|
console.log(chalk7.green("Systemd service file removed"));
|
|
2466
2446
|
}
|
|
2467
2447
|
if (!options.keepData) {
|
|
2468
|
-
const defaultCloudPath =
|
|
2448
|
+
const defaultCloudPath = join6(homedir(), ".nulljs");
|
|
2469
2449
|
if (existsSync7(defaultCloudPath)) {
|
|
2470
2450
|
console.log(chalk7.blue("Removing cloud directory..."));
|
|
2471
|
-
|
|
2451
|
+
await $2`rm -rf ${defaultCloudPath}`;
|
|
2472
2452
|
console.log(chalk7.green("Cloud directory removed"));
|
|
2473
2453
|
} else {
|
|
2474
2454
|
console.log(chalk7.gray("Cloud directory does not exist"));
|
|
@@ -2488,9 +2468,42 @@ var unhost = async (options = {}) => {
|
|
|
2488
2468
|
process.exit(1);
|
|
2489
2469
|
}
|
|
2490
2470
|
};
|
|
2471
|
+
var update = async () => {
|
|
2472
|
+
validateLinux();
|
|
2473
|
+
const cloudPath = join6(homedir(), ".nulljs");
|
|
2474
|
+
const servicePath = "/etc/systemd/system/nulljs.service";
|
|
2475
|
+
if (!existsSync7(servicePath)) {
|
|
2476
|
+
console.log(chalk7.red("NullJS service is not installed."));
|
|
2477
|
+
console.log(chalk7.gray('Run "nulljs host" to set up hosting first.'));
|
|
2478
|
+
process.exit(1);
|
|
2479
|
+
}
|
|
2480
|
+
const productionConfig = checkExistingProductionConfig(cloudPath);
|
|
2481
|
+
if (!productionConfig) {
|
|
2482
|
+
console.log(chalk7.red("Production config not found."));
|
|
2483
|
+
console.log(chalk7.gray('Run "nulljs host" to set up hosting first.'));
|
|
2484
|
+
process.exit(1);
|
|
2485
|
+
}
|
|
2486
|
+
console.log(chalk7.blue("Updating NullJS service..."));
|
|
2487
|
+
console.log(chalk7.blue("Stopping service..."));
|
|
2488
|
+
try {
|
|
2489
|
+
await $2`sudo systemctl stop nulljs`;
|
|
2490
|
+
} catch {}
|
|
2491
|
+
const serverBinPath = getServerBinPath();
|
|
2492
|
+
validateServerBinary(serverBinPath);
|
|
2493
|
+
console.log(chalk7.gray(` Binary path: ${serverBinPath}`));
|
|
2494
|
+
const currentUser = process.env.USER || process.env.USERNAME || "root";
|
|
2495
|
+
const serviceContent = createSystemdService(cloudPath, productionConfig.key.public, serverBinPath, currentUser);
|
|
2496
|
+
console.log(chalk7.blue("Updating systemd service..."));
|
|
2497
|
+
writeFileSync3("/tmp/nulljs.service", serviceContent);
|
|
2498
|
+
await $2`sudo mv /tmp/nulljs.service ${servicePath}`;
|
|
2499
|
+
await $2`sudo systemctl daemon-reload`;
|
|
2500
|
+
console.log(chalk7.blue("Starting service..."));
|
|
2501
|
+
await $2`sudo systemctl start nulljs.service`;
|
|
2502
|
+
console.log(chalk7.green("NullJS service updated and restarted!"));
|
|
2503
|
+
};
|
|
2491
2504
|
var host = async (cloudPath) => {
|
|
2492
2505
|
validateLinux();
|
|
2493
|
-
const resolvedCloudPath = cloudPath ? cloudPath.startsWith("/") ? cloudPath :
|
|
2506
|
+
const resolvedCloudPath = cloudPath ? cloudPath.startsWith("/") ? cloudPath : join6(process.cwd(), cloudPath) : join6(homedir(), ".nulljs");
|
|
2494
2507
|
console.log(chalk7.blue("Setting up NullJS production hosting..."));
|
|
2495
2508
|
console.log(chalk7.gray(` Cloud path: ${resolvedCloudPath}`));
|
|
2496
2509
|
const serverBinPath = getServerBinPath();
|
|
@@ -2501,7 +2514,7 @@ var host = async (cloudPath) => {
|
|
|
2501
2514
|
let productionConfig = checkExistingProductionConfig(resolvedCloudPath);
|
|
2502
2515
|
if (!productionConfig) {
|
|
2503
2516
|
const { privateKey, publicKey } = await generateProductionKeys();
|
|
2504
|
-
saveProductionConfig(resolvedCloudPath, privateKey, publicKey);
|
|
2517
|
+
await saveProductionConfig(resolvedCloudPath, privateKey, publicKey);
|
|
2505
2518
|
productionConfig = { key: { private: privateKey, public: publicKey } };
|
|
2506
2519
|
console.log(chalk7.green("Production keys generated"));
|
|
2507
2520
|
console.log(chalk7.blue("Production Public Key:"));
|
|
@@ -2512,32 +2525,33 @@ var host = async (cloudPath) => {
|
|
|
2512
2525
|
console.log(chalk7.gray(productionConfig.key.public));
|
|
2513
2526
|
}
|
|
2514
2527
|
const serviceContent = createSystemdService(resolvedCloudPath, productionConfig.key.public, serverBinPath, currentUser);
|
|
2515
|
-
installSystemdService(serviceContent);
|
|
2528
|
+
await installSystemdService(serviceContent);
|
|
2516
2529
|
console.log(chalk7.green("NullJS hosting setup complete!"));
|
|
2517
2530
|
console.log("");
|
|
2518
2531
|
console.log(chalk7.blue("Service management commands:"));
|
|
2519
|
-
console.log(chalk7.gray(" Start: "), chalk7.white("sudo systemctl start nulljs"));
|
|
2520
2532
|
console.log(chalk7.gray(" Stop: "), chalk7.white("sudo systemctl stop nulljs"));
|
|
2533
|
+
console.log(chalk7.gray(" Restart: "), chalk7.white("sudo systemctl restart nulljs"));
|
|
2521
2534
|
console.log(chalk7.gray(" Status: "), chalk7.white("sudo systemctl status nulljs"));
|
|
2522
2535
|
console.log(chalk7.gray(" Logs: "), chalk7.white("sudo journalctl -u nulljs -f"));
|
|
2523
|
-
console.log("");
|
|
2524
|
-
console.log(chalk7.yellow("Remember to start the service: sudo systemctl start nulljs"));
|
|
2525
2536
|
};
|
|
2526
2537
|
var registerHostCommand = (program) => {
|
|
2527
2538
|
const hostCmd = program.command("host").description("Set up NullJS production hosting with systemd").argument("[cloud-path]", "Path for cloud storage (default: ~/.nulljs)").action(async (cloudPath) => {
|
|
2528
2539
|
await host(cloudPath);
|
|
2529
2540
|
});
|
|
2541
|
+
hostCmd.command("update").description("Update NullJS service after package upgrade").action(async () => {
|
|
2542
|
+
await update();
|
|
2543
|
+
});
|
|
2530
2544
|
hostCmd.command("remove").description("Remove NullJS production hosting").option("--keep-data", "Keep cloud data directory").action(async (options) => {
|
|
2531
2545
|
await unhost(options);
|
|
2532
2546
|
});
|
|
2533
|
-
hostCmd.command("logs").description("View NullJS server logs").option("-n, --lines <number>", "Number of lines to show", "100").option("--no-follow", "Do not follow log output").action((options) => {
|
|
2547
|
+
hostCmd.command("logs").description("View NullJS server logs").option("-n, --lines <number>", "Number of lines to show", "100").option("--no-follow", "Do not follow log output").action(async (options) => {
|
|
2534
2548
|
validateLinux();
|
|
2535
|
-
const args = ["-u", "nulljs", "-n", options.lines];
|
|
2536
|
-
if (options.follow) {
|
|
2537
|
-
args.push("-f");
|
|
2538
|
-
}
|
|
2539
2549
|
try {
|
|
2540
|
-
|
|
2550
|
+
if (options.follow) {
|
|
2551
|
+
await $2`journalctl -u nulljs -n ${options.lines} -f`;
|
|
2552
|
+
} else {
|
|
2553
|
+
await $2`journalctl -u nulljs -n ${options.lines}`;
|
|
2554
|
+
}
|
|
2541
2555
|
} catch {}
|
|
2542
2556
|
});
|
|
2543
2557
|
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tothalex/nulljs",
|
|
3
3
|
"module": "dist/index.js",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.59",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"nulljs": "./dist/cli.js"
|
|
@@ -18,9 +18,9 @@
|
|
|
18
18
|
"typescript": "^5"
|
|
19
19
|
},
|
|
20
20
|
"optionalDependencies": {
|
|
21
|
-
"@tothalex/nulljs-darwin-arm64": "^0.0.
|
|
22
|
-
"@tothalex/nulljs-linux-arm64": "^0.0.
|
|
23
|
-
"@tothalex/nulljs-linux-x64": "^0.0.
|
|
21
|
+
"@tothalex/nulljs-darwin-arm64": "^0.0.81",
|
|
22
|
+
"@tothalex/nulljs-linux-arm64": "^0.0.81",
|
|
23
|
+
"@tothalex/nulljs-linux-x64": "^0.0.81"
|
|
24
24
|
},
|
|
25
25
|
"files": [
|
|
26
26
|
"dist"
|