create-wordjs 2.6.0 → 2.8.0
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 +4 -1
- package/index.js +188 -35
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -29,6 +29,9 @@ That single command takes you from nothing to the browser install wizard:
|
|
|
29
29
|
`wordjs-<tag>.zip.sha256` asset published with the release before extracting anything
|
|
30
30
|
(see [Integrity](#integrity)).
|
|
31
31
|
2. Extracts it into `my-site/` and installs the runtime dependencies (`npm run release:install`).
|
|
32
|
+
A failed install (typically a timed-out prebuilt `better-sqlite3` download on a server without a
|
|
33
|
+
compiler) is retried with backoff; if it keeps failing you get a plain explanation, and running
|
|
34
|
+
the same command again resumes the install.
|
|
32
35
|
3. Generates a one-time install token and starts the server (`npm run start:mono`), printing a
|
|
33
36
|
clickable URL:
|
|
34
37
|
|
|
@@ -42,7 +45,7 @@ in CI — plus a pure-JS *SQLite (legacy / WASM)* fallback for hosts where the n
|
|
|
42
45
|
|
|
43
46
|
## Requirements
|
|
44
47
|
|
|
45
|
-
- Node.js **>=
|
|
48
|
+
- Node.js **>= 22** (Node 22 or 24 LTS recommended) with npm on your PATH.
|
|
46
49
|
|
|
47
50
|
## Options
|
|
48
51
|
|
package/index.js
CHANGED
|
@@ -27,14 +27,14 @@
|
|
|
27
27
|
const REPO = 'jaimemartinez/wordjs';
|
|
28
28
|
|
|
29
29
|
// ---------------------------------------------------------------------------------------------
|
|
30
|
-
// Node preflight — same floor as WordJS itself (
|
|
30
|
+
// Node preflight — same floor as WordJS itself (Node >= 22: graphql 17 and the native modules declare it). Failing
|
|
31
31
|
// here with a clear message beats the cryptic EBADENGINE/native-binding crash mid-install.
|
|
32
32
|
// ---------------------------------------------------------------------------------------------
|
|
33
33
|
{
|
|
34
|
-
const
|
|
35
|
-
if (maj <
|
|
36
|
-
console.error(`\n✖ WordJS requires Node.js >=
|
|
37
|
-
console.error(' Install Node
|
|
34
|
+
const maj = Number(process.versions.node.split('.')[0]);
|
|
35
|
+
if (maj < 22) {
|
|
36
|
+
console.error(`\n✖ WordJS requires Node.js >= 22 — you are running ${process.versions.node}.`);
|
|
37
|
+
console.error(' Install Node 22 LTS or 24 LTS from https://nodejs.org and try again.\n');
|
|
38
38
|
process.exit(1);
|
|
39
39
|
}
|
|
40
40
|
}
|
|
@@ -46,6 +46,11 @@ const https = require('https');
|
|
|
46
46
|
const crypto = require('crypto');
|
|
47
47
|
const { spawn, spawnSync } = require('child_process');
|
|
48
48
|
|
|
49
|
+
// Every npm this process starts (release:install and the npm installs inside it, the server scripts)
|
|
50
|
+
// inherits this: npm's "new major version of npm available" notice otherwise lands in the middle of
|
|
51
|
+
// the install output, and it is advice about the operator's npm, not about the site.
|
|
52
|
+
process.env.npm_config_update_notifier = 'false';
|
|
53
|
+
|
|
49
54
|
const HELP = `
|
|
50
55
|
create-wordjs — bootstrap or upgrade a WordJS site with one command
|
|
51
56
|
|
|
@@ -103,10 +108,11 @@ Separate mode (three machines) — run one command per machine:
|
|
|
103
108
|
npx create-wordjs@latest join frontend --gateway 10.0.0.1 --token <t> --ca-hash <fp> --advertise 10.0.0.3
|
|
104
109
|
(join needs 'openssl' on PATH. See documentation/separate-mode.md.)
|
|
105
110
|
|
|
106
|
-
Upgrading preserves your database (backend/data), uploads (backend/uploads),
|
|
107
|
-
(wordjs-config.json + gateway secrets) and any user-installed plugins;
|
|
108
|
-
runs the dependency install. Database schema migrations apply
|
|
109
|
-
starts — then restart WordJS (e.g. 'systemctl restart
|
|
111
|
+
Upgrading preserves your database (backend/data), uploads (backend/uploads), backups
|
|
112
|
+
(backend/backups), config (wordjs-config.json + gateway secrets) and any user-installed plugins;
|
|
113
|
+
it replaces the app code and runs the dependency install. Database schema migrations apply
|
|
114
|
+
automatically the next time the server starts — then restart WordJS (e.g. 'systemctl restart
|
|
115
|
+
wordjs', or stop it and 'npm run start:mono').
|
|
110
116
|
`;
|
|
111
117
|
|
|
112
118
|
function fail(message, hint) {
|
|
@@ -575,17 +581,94 @@ async function obtainBundleZip(opts) {
|
|
|
575
581
|
return { zipPath, tag, cleanup };
|
|
576
582
|
}
|
|
577
583
|
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
584
|
+
// --- dependency install with retries -------------------------------------------------------------
|
|
585
|
+
//
|
|
586
|
+
// `release:install` downloads prebuilt native binaries (better-sqlite3 above all). When that download
|
|
587
|
+
// times out (`prebuild-install warn install Request timed out`), npm falls back to compiling from
|
|
588
|
+
// source, and a server without a C/C++ toolchain ends in a raw `gyp ERR!`. The same command run a
|
|
589
|
+
// minute later usually succeeds, so it is retried with backoff; if it still fails, the operator gets a
|
|
590
|
+
// plain explanation instead of the compiler log.
|
|
591
|
+
|
|
592
|
+
const INSTALL_ATTEMPTS = 3;
|
|
593
|
+
// Waits before the 2nd and 3rd attempts. CREATE_WORDJS_RETRY_BASE_MS scales them (tests use a few ms).
|
|
594
|
+
const INSTALL_RETRY_BASE_MS = /^\d+$/.test(process.env.CREATE_WORDJS_RETRY_BASE_MS || '') ? Number(process.env.CREATE_WORDJS_RETRY_BASE_MS) : 15000;
|
|
595
|
+
// Left in a site whose dependency install did not finish; `create-wordjs <dir>` resumes from it.
|
|
596
|
+
const INCOMPLETE_MARKER = '.create-wordjs-incomplete';
|
|
597
|
+
|
|
598
|
+
/** What a failed install's output says about why it failed. */
|
|
599
|
+
function classifyInstallFailure(output) {
|
|
600
|
+
const text = String(output || '');
|
|
601
|
+
const prebuiltTimeout = /prebuild-install (warn|WARN|ERR!).*(timed out|ETIMEDOUT|ECONNRESET|ESOCKETTIMEDOUT|EAI_AGAIN|ENOTFOUND|socket hang up)/i.test(text);
|
|
602
|
+
const nativeBuild = /gyp ERR!|node-gyp|not found: (make|python)|No (C|C\+\+) compiler|g\+\+: (command )?not found|Could not find any Python/i.test(text);
|
|
603
|
+
const network = prebuiltTimeout || /\b(ETIMEDOUT|ECONNRESET|ESOCKETTIMEDOUT|EAI_AGAIN|ENOTFOUND|ECONNREFUSED)\b|network (timeout|request)|Request timed out/i.test(text);
|
|
604
|
+
return { prebuiltTimeout, nativeBuild, network };
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
/** The operator-facing explanation of an install that failed every attempt. */
|
|
608
|
+
function installFailureAdvice(cwd, why, attempts) {
|
|
609
|
+
const lines = [`The runtime dependencies could not be installed (${attempts} attempt${attempts === 1 ? '' : 's'}).`];
|
|
610
|
+
if (why.prebuiltTimeout || why.nativeBuild) {
|
|
611
|
+
lines.push(
|
|
612
|
+
why.prebuiltTimeout
|
|
613
|
+
? 'Downloading a prebuilt native module (better-sqlite3) timed out, and npm then tried to compile it'
|
|
614
|
+
: 'A native module (better-sqlite3) had to be compiled from source,',
|
|
615
|
+
why.prebuiltTimeout
|
|
616
|
+
? 'from source, which needs a C/C++ toolchain and Python that this machine does not have.'
|
|
617
|
+
: 'which needs a C/C++ toolchain and Python that this machine does not have.',
|
|
618
|
+
);
|
|
619
|
+
} else if (why.network) {
|
|
620
|
+
lines.push('The package downloads kept failing on the network (timeouts or refused connections).');
|
|
621
|
+
}
|
|
622
|
+
lines.push(
|
|
623
|
+
'',
|
|
624
|
+
'Nothing is lost — the site files are in place. Either:',
|
|
625
|
+
` • retry later (the download usually works on a second try): cd ${cwd} && npm run release:install`,
|
|
626
|
+
' (or run the same create-wordjs command again: it resumes this install), or',
|
|
627
|
+
' • install a build toolchain so the module can be compiled locally, then retry:',
|
|
628
|
+
' Debian/Ubuntu: sudo apt-get install -y build-essential python3',
|
|
629
|
+
' Fedora/RHEL: sudo dnf install -y gcc-c++ make python3',
|
|
630
|
+
' Alpine: sudo apk add build-base python3',
|
|
631
|
+
);
|
|
632
|
+
return lines.join('\n');
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
/**
|
|
636
|
+
* `npm run release:install` with up to INSTALL_ATTEMPTS attempts and growing waits between them. The
|
|
637
|
+
* output still streams to the terminal; its tail is kept to explain a failure. Returns
|
|
638
|
+
* { ok, attempts, why } — never exits, so the caller can still write what the next attempt needs.
|
|
639
|
+
*/
|
|
640
|
+
async function installDependencies(cwd, { attempts = INSTALL_ATTEMPTS, baseDelayMs = INSTALL_RETRY_BASE_MS, sleep = (ms) => new Promise((r) => setTimeout(r, ms)) } = {}) {
|
|
641
|
+
let why = { prebuiltTimeout: false, nativeBuild: false, network: false };
|
|
642
|
+
for (let attempt = 1; attempt <= attempts; attempt++) {
|
|
643
|
+
if (attempt > 1) {
|
|
644
|
+
const wait = baseDelayMs * Math.pow(3, attempt - 2); // 15 s, then 45 s
|
|
645
|
+
console.log(`\n ↻ Retrying the dependency install in ${Math.round(wait / 1000)} s (attempt ${attempt} of ${attempts})…\n`);
|
|
646
|
+
await sleep(wait);
|
|
647
|
+
}
|
|
648
|
+
const r = await runReleaseInstallOnce(cwd);
|
|
649
|
+
if (r.ok) return { ok: true, attempts: attempt, why };
|
|
650
|
+
why = classifyInstallFailure(r.output);
|
|
651
|
+
const reason = why.prebuiltTimeout ? 'the prebuilt binary download timed out'
|
|
652
|
+
: why.nativeBuild ? 'a native module could not be compiled'
|
|
653
|
+
: why.network ? 'a network error' : (r.error ? r.error : `exit code ${r.status}`);
|
|
654
|
+
console.error(`\n ⚠️ Dependency install attempt ${attempt} of ${attempts} failed: ${reason}.`);
|
|
655
|
+
if (r.status === 127 || (r.error && /ENOENT/.test(r.error))) return { ok: false, attempts: attempt, why, fatal: 'Is npm on your PATH?' };
|
|
656
|
+
}
|
|
657
|
+
return { ok: false, attempts, why };
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
function runReleaseInstallOnce(cwd) {
|
|
661
|
+
return new Promise((resolve) => {
|
|
662
|
+
let tail = '';
|
|
663
|
+
const keep = (b) => { tail = (tail + b.toString()).slice(-256 * 1024); };
|
|
664
|
+
// A single fixed command string with shell:true resolves npm/npm.cmd on every platform (and
|
|
665
|
+
// avoids Node's DEP0190 warning about args-array + shell). No user input goes into it.
|
|
666
|
+
const child = spawn('npm run release:install', { cwd, shell: true, stdio: ['ignore', 'pipe', 'pipe'], env: process.env });
|
|
667
|
+
child.stdout.on('data', (b) => { process.stdout.write(b); keep(b); });
|
|
668
|
+
child.stderr.on('data', (b) => { process.stderr.write(b); keep(b); });
|
|
669
|
+
child.on('error', (e) => resolve({ ok: false, status: null, error: String(e && e.message || e), output: tail }));
|
|
670
|
+
child.on('close', (code) => resolve({ ok: code === 0, status: code, output: tail }));
|
|
586
671
|
});
|
|
587
|
-
if (r.error) fail(`Could not run "npm run ${script}": ${r.error.message}`, 'Is npm on your PATH?');
|
|
588
|
-
if (r.status !== 0) fail(`"npm run ${script}" exited with code ${r.status}.`, `Fix the error above, then re-run it manually inside ${cwd}.`);
|
|
589
672
|
}
|
|
590
673
|
|
|
591
674
|
// First non-internal IPv4 — a sensible default advertise/host when the user doesn't pass one.
|
|
@@ -999,6 +1082,7 @@ function confirm(question) {
|
|
|
999
1082
|
const PRESERVE_ON_UPGRADE = new Set([
|
|
1000
1083
|
'backend/data', // the database (+ WAL/SHM, ssl/, imports/)
|
|
1001
1084
|
'backend/uploads', // user uploads / media / fonts
|
|
1085
|
+
'backend/backups', // the site's own backups: a bundle never adds to or replaces them
|
|
1002
1086
|
'backend/wordjs-config.json', // site config + secrets
|
|
1003
1087
|
'backend/.env',
|
|
1004
1088
|
'.env',
|
|
@@ -1048,6 +1132,39 @@ async function obtainReleaseToTemp(opts) {
|
|
|
1048
1132
|
}
|
|
1049
1133
|
}
|
|
1050
1134
|
|
|
1135
|
+
/**
|
|
1136
|
+
* The account that owns the site, when this upgrade runs as root and the site is not root's: the files
|
|
1137
|
+
* the upgrade creates are given back to it (restoreInstallOwner). Read from the install directory before
|
|
1138
|
+
* anything is written. Only POSIX root can create files another account would have to own.
|
|
1139
|
+
*/
|
|
1140
|
+
function installDirOwner(installDir) {
|
|
1141
|
+
if (typeof process.getuid !== 'function' || process.getuid() !== 0) return null;
|
|
1142
|
+
try {
|
|
1143
|
+
const st = fs.statSync(installDir);
|
|
1144
|
+
return st.uid === 0 ? null : { uid: st.uid, gid: st.gid };
|
|
1145
|
+
} catch {
|
|
1146
|
+
return null;
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
/**
|
|
1151
|
+
* The documented upgrade is `sudo npx create-wordjs upgrade`, and every file it CREATES — the new
|
|
1152
|
+
* frontend/.next and backend/dist, new modules, node_modules from release:install — then belonged to
|
|
1153
|
+
* root while the service runs as the site's account (--systemd: `wordjs`): Next could no longer create
|
|
1154
|
+
* frontend/.next/cache and every render logged EACCES until someone ran chown by hand.
|
|
1155
|
+
*/
|
|
1156
|
+
function restoreInstallOwner(installDir, owner) {
|
|
1157
|
+
if (!owner) return;
|
|
1158
|
+
const spec = `${owner.uid}:${owner.gid}`;
|
|
1159
|
+
// -h: a symbolic link is re-owned itself, never what it points at.
|
|
1160
|
+
const r = spawnSync('chown', ['-R', '-h', spec, installDir], { stdio: 'inherit' });
|
|
1161
|
+
if (r.status === 0) {
|
|
1162
|
+
console.log(`\n Gave the upgraded files back to the site's owner (${spec}).`);
|
|
1163
|
+
} else {
|
|
1164
|
+
console.warn(`\n ⚠ Could not restore the site's owner. Run: chown -R -h ${spec} ${installDir}`);
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1051
1168
|
async function upgrade(opts) {
|
|
1052
1169
|
const installDir = path.resolve(process.cwd(), opts.dir);
|
|
1053
1170
|
const pkgPath = path.join(installDir, 'package.json');
|
|
@@ -1119,6 +1236,9 @@ async function upgrade(opts) {
|
|
|
1119
1236
|
console.warn(` (could not write config backup: ${e.message} — continuing; your DB/uploads/config are still preserved in place)`);
|
|
1120
1237
|
}
|
|
1121
1238
|
|
|
1239
|
+
// Read before the overlay creates any file (restoreInstallOwner).
|
|
1240
|
+
const siteOwner = installDirOwner(installDir);
|
|
1241
|
+
|
|
1122
1242
|
// Clean-replace the build outputs so no stale chunks linger, then overlay the rest.
|
|
1123
1243
|
for (const rel of CLEAN_REPLACE_ON_UPGRADE) {
|
|
1124
1244
|
const target = path.join(installDir, rel);
|
|
@@ -1135,10 +1255,17 @@ async function upgrade(opts) {
|
|
|
1135
1255
|
// Re-sync dependencies (a new version may add/upgrade packages). Skippable for a code-only swap.
|
|
1136
1256
|
if (opts.install) {
|
|
1137
1257
|
console.log('\n📦 Syncing runtime dependencies (npm run release:install)…\n');
|
|
1138
|
-
|
|
1258
|
+
const installed = await installDependencies(installDir);
|
|
1259
|
+
if (!installed.ok) {
|
|
1260
|
+
restoreInstallOwner(installDir, siteOwner);
|
|
1261
|
+
// The new code is in place and the unit matches it: stage it for when the install is re-run.
|
|
1262
|
+
if (opts.systemd) { try { emitSystemd(installDir, opts); } catch { /* the advice below still applies */ } }
|
|
1263
|
+
fail(installFailureAdvice(opts.dir === '.' ? installDir : opts.dir, installed.why, installed.attempts), installed.fatal);
|
|
1264
|
+
}
|
|
1139
1265
|
} else {
|
|
1140
1266
|
console.log('\n --no-install: skipped dependency sync. Run "npm run release:install" yourself if deps changed.');
|
|
1141
1267
|
}
|
|
1268
|
+
restoreInstallOwner(installDir, siteOwner);
|
|
1142
1269
|
|
|
1143
1270
|
const line = '━'.repeat(64);
|
|
1144
1271
|
console.log(`\n${line}`);
|
|
@@ -1185,7 +1312,8 @@ async function scaffoldBundle(opts, targetDir) {
|
|
|
1185
1312
|
}
|
|
1186
1313
|
|
|
1187
1314
|
console.log('\n📦 Installing runtime dependencies (this downloads prebuilt binaries — a few minutes)…\n');
|
|
1188
|
-
|
|
1315
|
+
const installed = await installDependencies(targetDir);
|
|
1316
|
+
if (!installed.ok) fail(installFailureAdvice(opts.dir, installed.why, installed.attempts), installed.fatal);
|
|
1189
1317
|
}
|
|
1190
1318
|
|
|
1191
1319
|
// `create-wordjs gateway` — set this machine up as the cluster gateway: install, mint the cluster CA +
|
|
@@ -1303,28 +1431,37 @@ async function main() {
|
|
|
1303
1431
|
if (opts.mode === 'join') return join(opts);
|
|
1304
1432
|
const targetDir = path.resolve(process.cwd(), opts.dir);
|
|
1305
1433
|
|
|
1434
|
+
// A site whose dependency install failed earlier is RESUMED: its release files are already extracted
|
|
1435
|
+
// (and were verified then), so only the install and what follows it run again. The marker is only
|
|
1436
|
+
// ever written by this installer, into a directory it extracted a bundle into.
|
|
1437
|
+
const resuming = fs.existsSync(path.join(targetDir, INCOMPLETE_MARKER));
|
|
1438
|
+
|
|
1306
1439
|
// Refuse to scribble over anything that already exists (an existing EMPTY dir is fine).
|
|
1307
|
-
if (fs.existsSync(targetDir)) {
|
|
1440
|
+
if (!resuming && fs.existsSync(targetDir)) {
|
|
1308
1441
|
if (!fs.statSync(targetDir).isDirectory()) fail(`"${opts.dir}" already exists and is not a directory.`);
|
|
1309
1442
|
if (fs.readdirSync(targetDir).length > 0) {
|
|
1310
1443
|
fail(`Directory "${opts.dir}" already exists and is not empty.`, 'Pick a new directory name, or empty it first.');
|
|
1311
1444
|
}
|
|
1312
|
-
} else {
|
|
1445
|
+
} else if (!resuming) {
|
|
1313
1446
|
fs.mkdirSync(targetDir, { recursive: true });
|
|
1314
1447
|
}
|
|
1315
1448
|
|
|
1316
1449
|
console.log('\n🚀 create-wordjs\n');
|
|
1317
1450
|
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1451
|
+
if (resuming) {
|
|
1452
|
+
console.log(` Resuming the unfinished install in ${targetDir} (the release files are already in place).`);
|
|
1453
|
+
} else {
|
|
1454
|
+
// 1) Obtain the release ZIP (local path, https URL, or GitHub latest/tagged release) and verify
|
|
1455
|
+
// its SHA-256 before anything is extracted.
|
|
1456
|
+
const { zipPath, cleanup } = await obtainBundleZip(opts);
|
|
1321
1457
|
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1458
|
+
// 2) Extract + sanity-check that this really is a WordJS release bundle.
|
|
1459
|
+
console.log(` Extracting into ${targetDir}…`);
|
|
1460
|
+
try {
|
|
1461
|
+
extractZip(zipPath, targetDir);
|
|
1462
|
+
} finally {
|
|
1463
|
+
cleanup();
|
|
1464
|
+
}
|
|
1328
1465
|
}
|
|
1329
1466
|
const pkgPath = path.join(targetDir, 'package.json');
|
|
1330
1467
|
let pkg = {};
|
|
@@ -1334,13 +1471,26 @@ async function main() {
|
|
|
1334
1471
|
`Expected a wordjs-*.zip from https://github.com/${REPO}/releases.`);
|
|
1335
1472
|
}
|
|
1336
1473
|
|
|
1337
|
-
// 3) Install runtime dependencies (pre-compiled bundle — no build step).
|
|
1474
|
+
// 3) Install runtime dependencies (pre-compiled bundle — no build step), retried with backoff. The
|
|
1475
|
+
// marker stays until it succeeds, so running this same command again resumes from here.
|
|
1476
|
+
fs.writeFileSync(path.join(targetDir, INCOMPLETE_MARKER), 'The dependency install of this site has not finished. Run "npm run release:install" here, or the same create-wordjs command again.\n');
|
|
1338
1477
|
console.log('\n📦 Installing runtime dependencies (this downloads prebuilt binaries — a few minutes)…\n');
|
|
1339
|
-
|
|
1478
|
+
const installed = await installDependencies(targetDir);
|
|
1340
1479
|
|
|
1341
|
-
// 4) Default to self-signed HTTPS unless the user explicitly asked for HTTP.
|
|
1480
|
+
// 4) Default to self-signed HTTPS unless the user explicitly asked for HTTP. Written whether or not
|
|
1481
|
+
// the install worked: the config belongs to the site, not to its node_modules.
|
|
1342
1482
|
if (!opts.http) ensureHttpsConfig(targetDir);
|
|
1343
1483
|
|
|
1484
|
+
if (!installed.ok) {
|
|
1485
|
+
// Leave everything the next attempt needs: the files, the config and (with --systemd) the unit,
|
|
1486
|
+
// none of which depends on node_modules. Only the install has to run again.
|
|
1487
|
+
if (opts.systemd) {
|
|
1488
|
+
try { emitSystemd(targetDir, opts); } catch (e) { console.error(` (systemd unit not staged: ${e && e.message || e})`); }
|
|
1489
|
+
}
|
|
1490
|
+
fail(installFailureAdvice(opts.dir, installed.why, installed.attempts), installed.fatal);
|
|
1491
|
+
}
|
|
1492
|
+
fs.rmSync(path.join(targetDir, INCOMPLETE_MARKER), { force: true });
|
|
1493
|
+
|
|
1344
1494
|
const proto = opts.http ? 'http' : 'https';
|
|
1345
1495
|
const line = '━'.repeat(64);
|
|
1346
1496
|
|
|
@@ -1424,5 +1574,8 @@ if (require.main === module) {
|
|
|
1424
1574
|
module.exports = {
|
|
1425
1575
|
pickBundleAsset, pickChecksumAsset, extractZip, normalizeSha256, parseChecksumFile, classifyZipSource, sha256File,
|
|
1426
1576
|
parseArgs, buildSystemdFiles, systemdInstallSteps, writeSystemdFiles,
|
|
1577
|
+
copyMerge, PRESERVE_ON_UPGRADE,
|
|
1427
1578
|
sysctlDropIn, serviceUserProblem, systemdPreflight, createSystemdStagingDir, LOW_PORT_ADVICE, HELP, installedFileMode,
|
|
1579
|
+
installDirOwner, restoreInstallOwner,
|
|
1580
|
+
installDependencies, classifyInstallFailure, installFailureAdvice, INCOMPLETE_MARKER,
|
|
1428
1581
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-wordjs",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.8.0",
|
|
4
4
|
"description": "Create a WordJS site with one command — the self-hosted CMS where third-party plugins run in an OS-isolated process with per-capability permission grants. SSR/SEO out of the box, SQLite by default, no PHP.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Jaime Martinez (https://github.com/jaimemartinez)",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"LICENSE"
|
|
15
15
|
],
|
|
16
16
|
"engines": {
|
|
17
|
-
"node": ">=
|
|
17
|
+
"node": ">=22.0.0"
|
|
18
18
|
},
|
|
19
19
|
"repository": {
|
|
20
20
|
"type": "git",
|