create-wordjs 1.14.1 → 2.0.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.
Files changed (3) hide show
  1. package/README.md +18 -8
  2. package/index.js +52 -3
  3. package/package.json +5 -2
package/README.md CHANGED
@@ -34,8 +34,9 @@ That single command takes you from nothing to the browser install wizard:
34
34
  → https://localhost:3000/install?token=…
35
35
  ```
36
36
 
37
- Open the URL, pick your database (SQLite zero config or PostgreSQL), create your admin
38
- account, and you're in.
37
+ Open the URL, pick your database, create your admin account, and you're in. The wizard offers
38
+ **SQLite** (zero config, the default), **PostgreSQL** and **MySQL/MariaDB** — all three are certified
39
+ in CI — plus a pure-JS *SQLite (legacy / WASM)* fallback for hosts where the native binary can't load.
39
40
 
40
41
  ## Requirements
41
42
 
@@ -49,6 +50,9 @@ account, and you're in.
49
50
  | `--version <tag>` | Install a specific release (e.g. `--version v1.0.0`) instead of the latest. |
50
51
  | `--http` | Serve plain HTTP instead of self-signed HTTPS (sets `WORDJS_HTTP=1`). |
51
52
  | `--no-start` | Scaffold and install dependencies only — start the server yourself later. |
53
+ | `--yes`, `-y` | Skip the confirmation prompt (required when `upgrade` runs non-interactively). |
54
+ | `--force` | (`upgrade`) Re-apply even if the site is already on the target version. |
55
+ | `--no-install` | (`upgrade`) Swap the code only; skip `npm run release:install`. |
52
56
  | `-h`, `--help` | Show usage. |
53
57
 
54
58
  Separate-mode options:
@@ -68,8 +72,10 @@ Separate-mode options:
68
72
  cd .. && npx create-wordjs@latest upgrade my-site # or run it from inside: npx create-wordjs@latest upgrade .
69
73
  ```
70
74
 
71
- Downloads the newest release and replaces the app code while **preserving your data**: the SQLite
72
- database, `uploads/`, `wordjs-config.json`, gateway secrets and any user-installed plugins survive.
75
+ Downloads the newest release and replaces the app code while **preserving your data**: the database
76
+ directory (`backend/data`), `backend/uploads/`, `wordjs-config.json`, `.env`, gateway secrets
77
+ (`gateway/gateway-config.json`) and any user-installed plugins survive. It asks for confirmation
78
+ before touching an existing install — on a non-interactive shell it refuses unless you pass `--yes`.
73
79
  Restart the server afterwards (schema migrations run automatically on the next start).
74
80
 
75
81
  ## Separate mode (multi-machine)
@@ -95,9 +101,11 @@ npx create-wordjs@latest join frontend --gateway 10.0.0.1 --token <t> --ca-hash
95
101
  ```
96
102
 
97
103
  Each `join` downloads the release, enrolls against the gateway (the token authorizes exactly one
98
- certificate signing; it is burned afterwards), then starts the service, which registers with the
99
- gateway over mTLS. Browse `https://<gateway>:3000` when all three are up. `join` machines need
100
- `openssl` on the PATH. Full details, port matrix and the manual (source-checkout) procedure:
104
+ certificate signing; it is burned afterwards, and the ones `gateway` printed also expire after 120
105
+ minutes mint more on the gateway with `node scripts/cluster.js token <backend|frontend>`, which
106
+ defaults to a 60-minute TTL and takes `--ttl <minutes>`), then starts the service, which registers
107
+ with the gateway over mTLS. Browse `https://<gateway>:3000` when all three are up. `join` machines
108
+ need `openssl` on the PATH. Full details, port matrix and the manual (source-checkout) procedure:
101
109
  [documentation/separate-mode.md](https://github.com/jaimemartinez/wordjs/blob/main/documentation/separate-mode.md).
102
110
 
103
111
  ## Good to know
@@ -118,7 +126,9 @@ gateway over mTLS. Browse `https://<gateway>:3000` when all three are up. `join`
118
126
  ## What gets created
119
127
 
120
128
  A ready-to-run WordJS bundle: backend (pre-compiled to `dist/`), frontend (pre-built `.next`),
121
- gateway, bundled plugins and themes. Secrets (JWT, DB password, install token) are generated
129
+ gateway, the bundled plugins and the four bundled themes (`circuito`, `default`, `gaceta`,
130
+ `vergel`). Marketplace plugins are **not** in the bundle — they ship as separate release assets and
131
+ are installed from the admin. Secrets (JWT, DB password, install token) are generated
122
132
  locally during install — nothing sensitive ships in the bundle. See `INSTALL.md` inside the
123
133
  scaffolded directory for the manual steps and `documentation/deployment.md` for production
124
134
  deployment.
package/index.js CHANGED
@@ -181,6 +181,41 @@ async function githubJson(url) {
181
181
  try { return JSON.parse(body); } catch { fail('GitHub returned an unparsable response.', 'Try again, or use --zip <path-to-zip>.'); }
182
182
  }
183
183
 
184
+ // NAME THE ASSET WE WANT; DO NOT TAKE THE FIRST ONE THAT LOOKS RIGHT.
185
+ //
186
+ // The core bundle is not alone on the release: the same release carries all 31 marketplace plugin
187
+ // zips, and `wordjs-*.zip` is a shape, not an identity. A plugin slug beginning with `wordjs-` would
188
+ // sort ahead of the bundle in the assets array and this installer would download a plugin and try to
189
+ // boot it as a site. Nothing today collides, which is exactly when it is cheap to fix.
190
+ //
191
+ // release.yml names the bundle after the tag (`wordjs-v2.0.0.zip`), so ask for that by name. The
192
+ // loose match survives only as a fallback — for older releases, and so a rename in the workflow
193
+ // degrades gracefully instead of failing hard.
194
+ //
195
+ // BUT THE FALLBACK IS THE OLD RULE, so it cannot be allowed to guess. Taking the first loose match
196
+ // would reinstate exactly the bug the exact match was added to fix, on every path where the
197
+ // tag-named asset is absent (a workflow_dispatch build, a rename, any earlier release). The loose
198
+ // shape is therefore used ONLY when it is unambiguous: exactly one candidate. Two or more means we
199
+ // would be choosing which file is the site, and choosing wrong installs a plugin as a site — so we
200
+ // refuse and say so, and `--zip` is right there. Fail closed, never guess.
201
+ //
202
+ // Exported (below) so it can be exercised directly: it is the one piece of release resolution that is
203
+ // pure, and testing it through the network call would mean testing a copy of it instead.
204
+ function pickBundleAsset(assets, tagName) {
205
+ const list = Array.isArray(assets) ? assets : [];
206
+ const wanted = `wordjs-${tagName}.zip`.toLowerCase();
207
+ const exact = list.find((a) => String(a && a.name || '').toLowerCase() === wanted);
208
+ if (exact) return exact;
209
+ const loose = looseBundleCandidates(list);
210
+ return loose.length === 1 ? loose[0] : null;
211
+ }
212
+
213
+ /** Every asset matching the loose `wordjs-*.zip` shape — used to explain an ambiguous refusal. */
214
+ function looseBundleCandidates(assets) {
215
+ const list = Array.isArray(assets) ? assets : [];
216
+ return list.filter((a) => /^wordjs-.*\.zip$/i.test(a && a.name || ''));
217
+ }
218
+
184
219
  async function resolveReleaseAsset(tag) {
185
220
  const url = tag
186
221
  ? `https://api.github.com/repos/${REPO}/releases/tags/${encodeURIComponent(tag)}`
@@ -190,8 +225,17 @@ async function resolveReleaseAsset(tag) {
190
225
  fail(tag ? `No release found for tag "${tag}".` : `No releases found for ${REPO}.`,
191
226
  `See https://github.com/${REPO}/releases for available versions, or pass --zip <path-or-url>.`);
192
227
  }
193
- const asset = (release.assets || []).find((a) => /^wordjs-.*\.zip$/i.test(a.name || ''));
194
- if (!asset) fail(`Release ${release.tag_name} has no wordjs-*.zip asset.`, 'Pass --zip <path-or-url> instead.');
228
+ const asset = pickBundleAsset(release.assets, release.tag_name);
229
+ if (!asset) {
230
+ // Say WHICH of the two refusals this is: "there is no bundle" and "there are several and I
231
+ // will not guess" need different answers from whoever is reading.
232
+ const candidates = looseBundleCandidates(release.assets).map((a) => a.name);
233
+ if (candidates.length > 1) {
234
+ fail(`Release ${release.tag_name} has no asset named wordjs-${release.tag_name}.zip, and ${candidates.length} others match wordjs-*.zip: ${candidates.join(', ')}.`,
235
+ 'Refusing to guess which one is the site bundle — pass --zip <path-or-url> with the one you want.');
236
+ }
237
+ fail(`Release ${release.tag_name} has no wordjs-*.zip asset.`, 'Pass --zip <path-or-url> instead.');
238
+ }
195
239
  return { name: asset.name, url: asset.browser_download_url, tag: release.tag_name };
196
240
  }
197
241
 
@@ -730,4 +774,9 @@ async function main() {
730
774
  child.on('exit', (code) => process.exit(code == null ? 0 : code));
731
775
  }
732
776
 
733
- main().catch((e) => fail(e && e.message ? e.message : String(e)));
777
+ // Run only when invoked as the CLI, so the pure helpers above can be required and exercised.
778
+ if (require.main === module) {
779
+ main().catch((e) => fail(e && e.message ? e.message : String(e)));
780
+ }
781
+
782
+ module.exports = { pickBundleAsset };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-wordjs",
3
- "version": "1.14.1",
3
+ "version": "2.0.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)",
@@ -46,6 +46,9 @@
46
46
  "access": "public"
47
47
  },
48
48
  "dependencies": {
49
- "adm-zip": "^0.5.16"
49
+ "adm-zip": "^0.6.0"
50
+ },
51
+ "scripts": {
52
+ "test": "node --test test/*.test.js"
50
53
  }
51
54
  }