203 lines
11 KiB
Markdown
203 lines
11 KiB
Markdown
## 3. CLI commands + required installs (per deployment method)
|
|
|
|
Everything runs through `bun` and the scripts in `package.json`. The build loop
|
|
iterates every folder under **`plugin-designs/`** (each folder = one bundle), so once a
|
|
map exists there, `bun run build` picks it up automatically — no per-map wiring.
|
|
|
|
> **Install once (all methods):** [Bun](https://bun.sh) (`curl -fsSL https://bun.sh/install | bash`) — Node ≥ 18 also works (`npm run <script>`; the scripts are identical). Then:
|
|
>
|
|
> ```bash
|
|
> bun install
|
|
> ```
|
|
|
|
The scripts these commands are grounded in (from this branch's `package.json`):
|
|
|
|
```jsonc
|
|
// TRANSCRIBED FROM package.json 2026-09-20. The previous copy had DRIFTED: it listed
|
|
// `check:drop` twice, omitted `check:template-literal` and `check:sri`, and still showed
|
|
// the old inline `build` pipeline — which has since moved behind a LOCK WRAPPER.
|
|
// ★ `build` is now `bash internal/build-lock.sh`; the real pipeline is `build:unlocked`.
|
|
// The lock SERIALIZES concurrent builds (a `clean` rm -rf's the shared output tree at the
|
|
// START of every build, so two builds racing give opposite answers seconds apart). Use
|
|
// BUILD_LOCK_WAIT=600 to QUEUE rather than collide. Note the lock serializes but does NOT
|
|
// dedupe: N queued builds = N full rebuilds.
|
|
// If you edit package.json, re-transcribe this block — a doc that QUOTES a config is
|
|
// claiming to be ground truth, and this one had silently stopped being it.
|
|
"dev": "bun x vite",
|
|
"build": "bash internal/build-lock.sh",
|
|
"build:unlocked": "bun run check:template-literal && bun run clean && bun run token && for d in plugin-designs/*/; do BUNDLE=$(basename \"$d\") bun x vite build || exit 1; done && bun run tree && bun run check:drop && bun run check:parse && bun run verify && bun run check:sri && bun run gallery",
|
|
"preview": "bun x vite preview",
|
|
"serve": "python3 -m http.server 4173 --bind 0.0.0.0 --directory public",
|
|
"cap:sync": "bunx cap sync android",
|
|
"cap:verify": "test -f capacitor/android/app/src/main/assets/public/index.html && echo \"OK webDir copied\" || (echo \"FAIL: empty assets/public -> blank white screen. Check webDir.\" && exit 1)",
|
|
"apk:debug": "bun run build && bun run cap:sync && bun run cap:verify && cd capacitor/android && ./gradlew assembleDebug",
|
|
"apk:install": "adb install -r capacitor/android/app/build/outputs/apk/debug/app-debug.apk",
|
|
"phone": "bun run apk:debug && bun run apk:install"
|
|
```
|
|
|
|
`bun run build` writes the whole deployable tree to `public/doxa-maps/`, including
|
|
`manifest.json` — the machine-readable index that carries the ready-to-paste
|
|
**iframe** and **`<script src>` + SRI** embed snippets for every map.
|
|
|
|
### Summary table
|
|
|
|
| # | Deploy method | Install first (prereqs) | Command(s) |
|
|
|---|---------------|-------------------------|------------|
|
|
| 1 | **iframe embed** | Bun | `bun install` → `bun run build` → `bun run serve` (or drop `public/doxa-maps/` on any host) |
|
|
| 2 | **Mobile app (Android)** | Bun ALONE for `check:mobile`; + Android SDK · `adb` on PATH · JDK 17 (Gradle) for the APK | `bun run check:mobile` (sync + webDir assertion, NO toolchain needed) · `bun run phone` (= `apk:debug` + `apk:install` to a device/emulator) |
|
|
| 3 | **Direct-JS / dashboard** | Bun | `bun run build`, then paste the `<script src> + integrity` (SRI) snippet from `manifest.json`. *Proven same-root AND foreign-host (verified 2026-09-20: gl + attribution, 0 errors, 0 404s). Fill the `YOUR_*` placeholders the snippet ships with* |
|
|
| 4 | **WordPress** | Bun (+ `php` CLI to run the contract gate) | `bun run build`, then either paste the iframe/script snippet into a **Custom HTML** block, **or** install `wordpress/doxa-maps-embed/` (shortcode + Gutenberg block). `bun run check:wp-embed` verifies the emitted markup. *(Contract-proven; not yet shot on a live WP install.)* |
|
|
| 5 | **Desktop (Tauri)** | Rust toolchain + Tauri CLI *(not yet scaffolded)* | **Deferred POC** — no command exists on this branch yet. |
|
|
|
|
---
|
|
|
|
### 1. iframe embed *(required)*
|
|
|
|
**Install:** Bun.
|
|
|
|
```bash
|
|
bun install
|
|
bun run build # builds every plugin-designs/* bundle -> public/doxa-maps/
|
|
bun run serve # optional: serves public/ at http://localhost:4173
|
|
```
|
|
|
|
The build writes `public/doxa-maps/manifest.json`. Each profile carries an
|
|
`embed.iframe` snippet, e.g.:
|
|
|
|
```html
|
|
<iframe src="./all-people-groups/all-people-groups/index.html"
|
|
width="100%" height="600"
|
|
style="border:none;border-radius:12px;" allowfullscreen></iframe>
|
|
```
|
|
|
|
URLs are root-relative, so the whole `doxa-maps/` folder is position-independent —
|
|
drop it on Cloudflare/Vercel/any static host and every iframe resolves. (For an
|
|
external absolute base, set `embedBaseUrl` in the build config before building.)
|
|
|
|
### 2. Mobile app — Android / Capacitor *(required)*
|
|
|
|
**Install:** Bun · **Android SDK** (`sdkmanager`, `adb` on your PATH) · **JDK 17**
|
|
(the Gradle wrapper needs it) · Capacitor (`@capacitor/*` are already devDependencies,
|
|
so `bun install` covers them). A connected device or running emulator for install.
|
|
|
|
```bash
|
|
bun run check:mobile # CONTRACT check — needs NO JDK/SDK/device
|
|
bun run phone # full path — needs the toolchain above
|
|
```
|
|
|
|
**`check:mobile` is runnable anywhere.** `cap sync` is a file copy plus plugin wiring, so it
|
|
needs no Android toolchain at all; `cap:verify` then asserts
|
|
`capacitor/android/app/src/main/assets/public/index.html` exists. That guards the soft
|
|
failure `capacitor.config.json` warns about — *a wrong `webDir` yields an EMPTY
|
|
`assets/public` and a BLANK WHITE SCREEN at launch, not an error*. Verified 2026-09-20 with
|
|
`java`, `gradle`, `adb` and `sdkmanager` all absent: sync EXIT=0, 21 bundles staged,
|
|
`cap:verify` OK. Negative-tested by removing `index.html` — EXIT=1.
|
|
|
|
**Status: built + contract-proven; not shot.** The APK itself (`gradlew assembleDebug`) and
|
|
its launch on a device still need JDK 17 + the Android SDK + `adb`.
|
|
|
|
`phone` chains the whole path: `apk:debug` (which runs `bun run build`, then
|
|
`cap:sync` to copy the web output into the Android project, then `cap:verify`, then
|
|
`./gradlew assembleDebug`) and finally `apk:install` (`adb install -r ...`) onto the
|
|
device. Run the halves separately if you want to inspect between steps:
|
|
|
|
```bash
|
|
bun run apk:debug # build the APK only
|
|
bun run apk:install # push the built APK to a connected device
|
|
```
|
|
|
|
Only Android is scaffolded on this branch (`capacitor/android`, `capacitor.config.json`);
|
|
iOS is not set up.
|
|
|
|
### 3. Direct-JS / dashboard *(required)*
|
|
|
|
**Install:** Bun.
|
|
|
|
```bash
|
|
bun run build
|
|
```
|
|
|
|
Then paste the profile's `embed.script` snippet from `manifest.json` — a `<script src>`
|
|
with a Subresource-Integrity (SRI) hash plus the custom-element tag:
|
|
|
|
```html
|
|
<script src="./all-people-groups/all-people-groups.js"
|
|
integrity="sha384-j5Ypx+EI9DGlTClzgLs1R8wAnmTnkQ7HgAhHrxyGoVoHDMTlFVY7slU6JNicn77T"
|
|
crossorigin="anonymous"></script>
|
|
<all-people-groups
|
|
profile-config='{"profile":"all-people-groups","tk":"YOUR_MAPBOX_TOKEN"}'>
|
|
</all-people-groups>
|
|
```
|
|
|
|
The element accepts props / emits events, so it can wire into a surrounding page as a
|
|
unified research dashboard (deploy-3 proof). The SRI hash regenerates on every build.
|
|
|
|
#### Embedding on a FOREIGN host (a partner site) — the two things that differ
|
|
|
|
Both embed methods were run to their first failure on a foreign host page (2026-09-20).
|
|
**deploy-1 needed nothing**; **deploy-3 needed two values**, and the snippet now ships both.
|
|
|
|
*Why they differ:* an **iframe carries its own document base**, so the leaf page's relative
|
|
asset and API paths resolve correctly. A **direct-JS embed executes in the host's document**,
|
|
so those same relative paths resolve against the *host* — and 404. Same bundle, same data,
|
|
different containing document.
|
|
|
|
| | iframe (deploy-1) | direct-JS (deploy-3) |
|
|
|---|---|---|
|
|
| `assetsBase` | not needed | **required** — in `profile-config` |
|
|
| API base | not needed | **required** — `window.MAP_APP_API_URL` (a host global, *not* a profile-config key) |
|
|
| Mapbox token | inherited by the leaf | replace `YOUR_MAPBOX_TOKEN` |
|
|
|
|
Both are emitted into `embed.script` already: the API base uses the configured value, and
|
|
`assetsBase` falls back to a `YOUR_TREE_URL/assets` placeholder when `assetsBaseUrl` is unset.
|
|
|
|
**One rewrite either way.** Snippet URLs are **tree-relative** (`./bundle/...`) because
|
|
`embedBaseUrl` is empty by default — that is what keeps a dropped folder position-independent
|
|
and the `check:drop` gate green. A partner whose page is not *at* the tree root must either
|
|
prefix the path (`./doxa-maps/...`) or set **`embedBaseUrl`** in the build config to emit
|
|
absolute URLs. That is a deployment choice, not a defect.
|
|
|
|
### 4. WordPress *(built + contract-proven; not yet shot on a live WP install)*
|
|
|
|
> **Corrected 2026-09-20.** This section previously read *"deferred POC — no dedicated
|
|
> WordPress build step exists on this branch"*. That is no longer true: a first-class
|
|
> plugin exists at `wordpress/doxa-maps-embed/` (a `[doxa_map]` shortcode + a Gutenberg
|
|
> block), and its emitted markup is now verified by a gate.
|
|
|
|
**The simple path** — no plugin needed, same artifacts as methods 1 and 3:
|
|
|
|
```bash
|
|
bun run build
|
|
```
|
|
|
|
Then paste either the iframe snippet **or** the `<script src> + SRI` snippet from
|
|
`manifest.json` into a WordPress **Custom HTML** block.
|
|
|
|
**The plugin path** — `wordpress/doxa-maps-embed/` registers the bundle through WordPress's
|
|
own dependency graph, so the SRI hash and `crossorigin` survive and the script loads once
|
|
per page rather than once per embed.
|
|
|
|
```bash
|
|
bun run check:wp-embed # renders the REAL shortcode with no WordPress installed
|
|
```
|
|
|
|
That gate stubs the ~20 WordPress functions the plugin actually calls (`wordpress/tests/`),
|
|
renders the shortcode against the REAL built `manifest.json`, and asserts the contract
|
|
`HOW-TO-TEST.md` defines: the element tag is emitted, `profile-config` decodes to valid
|
|
JSON, the script `src` equals the catalog path, the SRI `integrity` is injected by the
|
|
`script_loader_tag` filter, `crossorigin="anonymous"` is present — plus a negative test
|
|
that a **malformed** integrity is rejected.
|
|
|
|
**Status honesty — say "contract-proven", not "proven".** What is proven: the plugin emits
|
|
the correct markup, repeatably and in CI. What is *not*: that WordPress renders the page,
|
|
that the block editor registers it, or that a browser executes the result. Those still need
|
|
a real WP install (`wp-env start` — see `wordpress/doxa-maps-embed/HOW-TO-TEST.md`).
|
|
|
|
### 5. Desktop — Tauri *(deferred POC)*
|
|
|
|
**Intended prereqs:** the Rust toolchain (`rustup`) + the Tauri CLI, wrapping the same
|
|
`bun run build` web output as a native desktop shell.
|
|
|
|
**Status: deferred POC.** No Tauri scaffolding or build script exists on this branch
|
|
yet, so there is no copy-paste command to run today. Documented here for completeness
|
|
of the 5-method model.
|