mikser-io 9.0.1 → 9.0.3
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/CLAUDE.md +50 -5
- package/README.md +28 -24
- package/docs/api-reference.md +701 -0
- package/docs/architecture.md +357 -0
- package/docs/caching.md +233 -0
- package/docs/configuration.md +423 -0
- package/docs/decisions/0001-content-layer-not-the-app.md +61 -0
- package/docs/decisions/0002-files-as-source-of-truth.md +64 -0
- package/docs/decisions/0003-plugins-independent-engine-stable.md +64 -0
- package/docs/decisions/0004-compose-via-protocols.md +71 -0
- package/docs/decisions/0005-engine-infrastructure-runs-before-plugin-hooks.md +94 -0
- package/docs/decisions/0006-when-to-add-to-core.md +98 -0
- package/docs/decisions/0007-references-declaration-and-expansion.md +349 -0
- package/docs/decisions/0009-database-engine-substrate.md +252 -0
- package/docs/decisions/0010-plugin-bundles-and-inline-options.md +226 -0
- package/docs/decisions/0011-served-entities-expose-deployed-urls.md +306 -0
- package/docs/decisions/README.md +39 -0
- package/docs/entities.md +364 -0
- package/docs/getting-started.md +294 -0
- package/docs/lifecycle.md +417 -0
- package/docs/overview.md +224 -0
- package/docs/plugins.md +922 -0
- package/docs/rendering.md +557 -0
- package/docs/watch-mode.md +266 -0
- package/index.js +1 -0
- package/package.json +1 -1
- package/src/catalog.js +11 -4
- package/src/database/sift-to-sql.js +1 -0
- package/src/plugins/api.js +17 -13
- package/src/plugins/assets.js +29 -0
- package/src/plugins/files.js +7 -0
- package/src/plugins/preview.js +13 -8
- package/src/plugins/resources.js +8 -2
- package/src/render.js +29 -0
- package/src/routes.js +124 -0
- package/src/server.js +24 -10
- package/src/utils.js +40 -0
package/CLAUDE.md
CHANGED
|
@@ -175,6 +175,19 @@ brevity.
|
|
|
175
175
|
Strings produce a v9 migration error pointing at the new shape.
|
|
176
176
|
- `manager.js` — file watching (chokidar) and cron scheduling.
|
|
177
177
|
- `source.js` — `useSource` codifies the folder-of-files pattern.
|
|
178
|
+
- `routes.js` — HTTP route registry. Plugins mount on
|
|
179
|
+
`runtime.options.app` directly; the Express router stack has the
|
|
180
|
+
paths but not the intent (loopback-only? streaming?). So plugins
|
|
181
|
+
declare each mount via `registerRoute({ path, plugin, reachability,
|
|
182
|
+
streaming })` as they make it. `reachability` is `public` | `token`
|
|
183
|
+
| `loopback`; `streaming` flags SSE/WS routes a facade must not
|
|
184
|
+
buffer. Inventory lives at `runtime.routes`; consumers (a Caddy/nginx
|
|
185
|
+
facade generator, healthcheck list, `mikser://routes` resource) read
|
|
186
|
+
it — none baked in. `registerRoute` also folds in the origin/location
|
|
187
|
+
URL building and the standard `"<label> mounted: <loc>
|
|
188
|
+
[<reachability>]"` boot log that api/preview/mcp/vector/forms/decap
|
|
189
|
+
were each copy-pasting. Pure inventory — takes no position on what to
|
|
190
|
+
do with the routes.
|
|
178
191
|
- `constants.js` — `OPERATION` (CREATE/UPDATE/DELETE/RENDER/
|
|
179
192
|
POSTPROCESS), `ACTION` (sync action types), `TASKS` (`INLINE`/
|
|
180
193
|
`SERIAL`/`WORKER` — dispatch modes).
|
|
@@ -199,7 +212,7 @@ substrate.
|
|
|
199
212
|
- **`mikser-io-render-{hbs,liquid,eta,markdown,metatext}`** — renderer
|
|
200
213
|
plugins. `renderHbs` is bundled in core; the rest are siblings.
|
|
201
214
|
- **`mikser-io-post-{mjml,pdf,email}`** — postprocessor plugins,
|
|
202
|
-
composable in chains (see `
|
|
215
|
+
composable in chains (see `docs/rendering.md#postprocess`).
|
|
203
216
|
- **`mikser-io-mcp`** — AI tooling surface (ADR-0006 test #5).
|
|
204
217
|
- **`mikser-io-vector`** — vector index over the catalog.
|
|
205
218
|
- **`mikser-io-forms`** — HTTP form receivers → entities.
|
|
@@ -291,6 +304,18 @@ Test coverage: `test/unit/source-sweep.test.js`.
|
|
|
291
304
|
`runtime.options.port` is the internal listener port (kept for
|
|
292
305
|
loopback/dev URL building); `runtime.options.url` is the external
|
|
293
306
|
origin. When in doubt: external = `url`, internal = `port`.
|
|
307
|
+
- **Trust proxy default is `'loopback'`** (server.js), not Express's
|
|
308
|
+
`false`. The dominant deployment puts a same-host reverse proxy in
|
|
309
|
+
front, where every socket peer is 127.0.0.1; with no trust, mikser
|
|
310
|
+
reads every proxied request as loopback and the loopback-only gate
|
|
311
|
+
(api/mcp/forms/decap enforce `isLoopback(req.ip)`) inverts the
|
|
312
|
+
moment a facade is added. `'loopback'` honors `X-Forwarded-For` only
|
|
313
|
+
from a loopback peer — safe everywhere (a remote attacker's kernel-
|
|
314
|
+
set peer is never 127.0.0.1, so they can't forge a loopback
|
|
315
|
+
`req.ip`). Override via `config.server.trustProxy` — `'uniquelocal'`
|
|
316
|
+
for a sibling-container proxy, a CIDR for a specific subnet, `false`
|
|
317
|
+
to opt out. The loopback-enforcement and this default are a pair; a
|
|
318
|
+
facade that injects `X-Forwarded-For` (Caddy/nginx do) makes it work.
|
|
294
319
|
- **Engine functions** as module-level exports from `mikser-io`:
|
|
295
320
|
`import { queryEntities, subscribe, useRenderer, useCollection,
|
|
296
321
|
readEntityContent, isTextEntity } from 'mikser-io'`.
|
|
@@ -364,6 +389,26 @@ Test coverage: `test/unit/source-sweep.test.js`.
|
|
|
364
389
|
plugin config moved entirely off `runtime.config.<plugin>`; it
|
|
365
390
|
arrives as factory args, gets stashed on the descriptor, and is
|
|
366
391
|
passed as `config` to `load`/`render`/`setup`/`postprocess`.
|
|
392
|
+
- **0011** (implemented; proven against gpoint) — File and resource
|
|
393
|
+
entities expose deployed URLs. References to served files
|
|
394
|
+
(image/video/PDF) are `$`-keyed **served paths** (`/img/X.jpg`,
|
|
395
|
+
`/media/clip.mp4` — the path content authors, = the entity's
|
|
396
|
+
`meta.url`), resolving via a new `refFilter` `{ 'meta.url': … }`
|
|
397
|
+
clause (utils.js) backed by an indexed `meta_url` column
|
|
398
|
+
(catalog.js + the `INDEXED_COLUMNS` map in sift-to-sql.js; schema
|
|
399
|
+
9.0.1 → 9.0.2). No collection-prefixed ids leak into content.
|
|
400
|
+
Id-refs were tried and rejected — gpoint references content by
|
|
401
|
+
served path everywhere, and its `/media/**` `resources()` library
|
|
402
|
+
means the entity only exists *because* content references
|
|
403
|
+
`/media/…`. The `files` plugin stamps `meta.url`; the `resources`
|
|
404
|
+
plugin stamps its library's served location; the `assets` plugin
|
|
405
|
+
stamps `meta.presets` in `onProcessed` via the pure `presetUrl()`
|
|
406
|
+
helper. Expanding a ref then yields the served entity's URL set,
|
|
407
|
+
not a string to reconstruct; `lookupUrl` (render.js) is the sync
|
|
408
|
+
Handlebars helper resolving a ref to `meta.url` or a named preset.
|
|
409
|
+
Base-relative in the live catalog, absolute in static renders
|
|
410
|
+
(from `runtime.options.url`). Umbrella term is *served entity*
|
|
411
|
+
(file/resource/preset).
|
|
367
412
|
|
|
368
413
|
## MCP
|
|
369
414
|
|
|
@@ -528,11 +573,11 @@ the consumer's own project tree — no duplication, no bug.
|
|
|
528
573
|
|
|
529
574
|
## Reference
|
|
530
575
|
|
|
531
|
-
- `
|
|
576
|
+
- `docs/architecture.md` — module map (audit before relying
|
|
532
577
|
on specifics; may have drift)
|
|
533
|
-
- `
|
|
534
|
-
- `
|
|
535
|
-
- `
|
|
578
|
+
- `docs/decisions/` — ADRs
|
|
579
|
+
- `docs/configuration.md` — config reference
|
|
580
|
+
- `docs/api-reference.md` — public API
|
|
536
581
|
- `test/perf/` — render-pipeline perf rig
|
|
537
582
|
- Sibling repos: `mikser-io-layouts` (canonical SSG-flavor render-task
|
|
538
583
|
production policy), `mikser-io-mcp`, `mikser-io-vector`,
|
package/README.md
CHANGED
|
@@ -2,17 +2,21 @@
|
|
|
2
2
|
<img src="mikser-lockup-stacked.svg" alt="mikser" width="198" />
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
|
+
<p align="center"><strong>The AI-native content engine.</strong></p>
|
|
6
|
+
|
|
5
7
|
# Mikser
|
|
6
8
|
|
|
7
|
-
**Mikser is the content
|
|
9
|
+
**Mikser is the AI-native content engine.** It mixes content from anywhere — markdown files, Google Sheets, your ERP, a CMS, any API — into one live catalog, links it together with references, and ships it to any frontend. Edit a price in your ERP or a cell in a spreadsheet, and every page that uses it updates within seconds — in whatever framework you built the site with.
|
|
10
|
+
|
|
11
|
+
**Framework-agnostic on both ends.** Headless CMSes (Sanity, Contentful) free you from the database but lock authoring into their UI. Frameworks like Astro let you bring any frontend but lock you into the framework. Mikser frees both: **any source in, any frontend out.** Provider plugins turn external systems into content sources; the catalog is served over plain HTTP, with idiomatic [Vue](https://github.com/almero-digital-marketing/mikser-io-sdk-vue) / [React](https://github.com/almero-digital-marketing/mikser-io-sdk-react) / [Svelte](https://github.com/almero-digital-marketing/mikser-io-sdk-svelte) SDKs on top (`useDocument`, `useDocuments`, multilingual `useHref`, live SSE). It's also AI-native: agents read and write the same catalog over MCP using the same calls a frontend developer uses — there's no separate "AI API" to keep in sync.
|
|
8
12
|
|
|
9
|
-
**
|
|
13
|
+
**References merge sources into one call.** Mikser tracks how entities reference each other — an article's author, the marketing copy and ERP price behind a product page, the images a landing page uses. Fetch the product and pull its marketing copy, price, and hero image along with it in one round-trip. Change any of them and every page subscribed to a reference touching it re-renders live, without polling — and mikser invalidates *exactly* the pages that depend on it, nothing more.
|
|
10
14
|
|
|
11
|
-
|
|
15
|
+
**Your content stays yours.** Source files live on disk as `.md` / `.yml` — diffable, version-controllable, portable on day one and year ten. No database lock-in, no proprietary export. And it's the content layer, not your whole backend: business logic, accounts, and transactions stay in their own services; mikser handles the part that's actually content — rendered to HTML, PDF, email, and other formats from the same source.
|
|
12
16
|
|
|
13
|
-
|
|
17
|
+
Built for Node.js around a strict lifecycle and a composable plugin system: every document, asset, and template flows through the same pipeline, and plugins hook in at any phase. MIT-licensed, runs on Node, zero hosted dependencies. **The portability promise is the architecture, not a feature.**
|
|
14
18
|
|
|
15
|
-
> **New to mikser?** Read the [Architecture Overview](./
|
|
19
|
+
> **New to mikser?** Read the [Architecture Overview](./docs/overview.md) — one document, end-to-end walkthrough of how a file becomes a deployed page across all twenty lifecycle phases. It's the doc most projects need first.
|
|
16
20
|
|
|
17
21
|
## Where it fits
|
|
18
22
|
|
|
@@ -163,7 +167,7 @@ The engine is what stays stable — the lifecycle, the catalog, the file-based c
|
|
|
163
167
|
| Plugin | What it does |
|
|
164
168
|
|---|---|
|
|
165
169
|
| `data` | JSON snapshots of entities / context / catalog, written to disk for static serving |
|
|
166
|
-
| `api` | REST endpoints with sift-backed queries, per-endpoint tokens, optional render, opt-in [per-query disk cache](./
|
|
170
|
+
| `api` | REST endpoints with sift-backed queries, per-endpoint tokens, optional render, opt-in [per-query disk cache](./docs/caching.md) for reverse-proxy failover |
|
|
167
171
|
| `preview` | In-memory render cache + `GET /preview/:filename` route. Companion to the `mikser_preview_render` MCP tool (in [`mikser-io-mcp`](https://github.com/almero-digital-marketing/mikser-io-mcp)) — transient render bytes served at a clickable URL, no filesystem footprint |
|
|
168
172
|
|
|
169
173
|
**Integrations:**
|
|
@@ -171,7 +175,7 @@ The engine is what stays stable — the lifecycle, the catalog, the file-based c
|
|
|
171
175
|
| Plugin | What it does |
|
|
172
176
|
|---|---|
|
|
173
177
|
| [`mikser-io-vector`](https://github.com/almero-digital-marketing/mikser-io-vector) | OpenAI embeddings + semantic search (sqlite-vec or pgvector) |
|
|
174
|
-
| [`mikser-io-schemas`](https://github.com/almero-digital-marketing/mikser-io-schemas) | Zod-backed entity validation + auto-generated TypeScript declarations for the SDK. Auto-detects `$`-keyed references and warns on broken ones — see [ADR-0007](./
|
|
178
|
+
| [`mikser-io-schemas`](https://github.com/almero-digital-marketing/mikser-io-schemas) | Zod-backed entity validation + auto-generated TypeScript declarations for the SDK. Auto-detects `$`-keyed references and warns on broken ones — see [ADR-0007](./docs/decisions/0007-references-declaration-and-expansion.md) |
|
|
175
179
|
| [`mikser-io-forms`](https://github.com/almero-digital-marketing/mikser-io-forms) | Public form-submission endpoints. POST → captcha + schema validation → write a document file plus uploaded files to disk; the `documents` / `files` plugins pick them up via their normal watch loop. Composes with `mikser-io-schemas` for schema-by-name; built-in captcha providers (Google v2/v3, hCaptcha, Turnstile) plus a custom-verify escape hatch |
|
|
176
180
|
| [`mikser-io-archive`](https://github.com/almero-digital-marketing/mikser-io-archive) | Persist matching entities to YAML — audit trail, versioned content history, downstream export |
|
|
177
181
|
| `mapper` | Run config-supplied transforms over matched entities each cycle (in-core, generic transformation layer) |
|
|
@@ -217,13 +221,13 @@ npx mikser --watch # incremental dev loop
|
|
|
217
221
|
npx mikser --server # build + serve at :3001
|
|
218
222
|
```
|
|
219
223
|
|
|
220
|
-
For a working starter — config with a real plugin set, sample `documents/`, expected output — see [Getting Started](./
|
|
224
|
+
For a working starter — config with a real plugin set, sample `documents/`, expected output — see [Getting Started](./docs/getting-started.md). Or skip straight to "add mikser to this app" via the [Claude Code plugin](#built-for-ai-assisted-development) above.
|
|
221
225
|
|
|
222
226
|
## Core Concepts
|
|
223
227
|
|
|
224
228
|
- **Lifecycle** — Processing runs through fixed phases: initialize → load → import → process → persist → render → finalize. Plugins hook into any phase.
|
|
225
229
|
- **Entities** — Everything is an entity (document, file, layout, asset). Entities flow through the journal and are tracked in the catalog.
|
|
226
|
-
- **References between entities** — A front-matter key starting with `$` (e.g. `$author: /authors/dick`) points at another entity. The engine knows the whole graph: templates can follow the links, the schemas plugin checks they resolve, and a single query can pull referenced entities along inline instead of one round trip per link. See [ADR-0007](./
|
|
230
|
+
- **References between entities** — A front-matter key starting with `$` (e.g. `$author: /authors/dick`) points at another entity. The engine knows the whole graph: templates can follow the links, the schemas plugin checks they resolve, and a single query can pull referenced entities along inline instead of one round trip per link. See [ADR-0007](./docs/decisions/0007-references-declaration-and-expansion.md).
|
|
227
231
|
- **Plugins** — Functionality is delivered via plugins. Built-in plugins handle common sources (documents, files, layouts, assets). Custom plugins can be added to any project.
|
|
228
232
|
- **Runtime Singleton** — A plain module-level object holds all global state and coordinates the lifecycle. The ES module cache guarantees every importer gets the same instance.
|
|
229
233
|
- **Watch Mode** — In watch mode, file changes trigger incremental re-processing without restarting.
|
|
@@ -239,16 +243,16 @@ The shape mikser fits cleanly:
|
|
|
239
243
|
- **Mixed-output publishing** — the same source document renders to HTML, PDF (via `post-pdf`), MJML email (via `post-mjml`), and JSON snapshots. One catalog, many output formats, all concurrent.
|
|
240
244
|
- **Headless backends for static frontends** — pair the `api` plugin with `sdk-api` for SSE-driven live frontends; pair the `data` plugin output with any static host for pre-rendered consumption.
|
|
241
245
|
|
|
242
|
-
The shape mikser **doesn't** fit cleanly: anything with non-technical content authors who can't or won't work with files, anything with non-content business logic at the core, anything needing multi-tenant / per-user auth. Those aren't bugs — they're outside the design envelope. See [`decisions/0001-content-layer-not-the-app.md`](./
|
|
246
|
+
The shape mikser **doesn't** fit cleanly: anything with non-technical content authors who can't or won't work with files, anything with non-content business logic at the core, anything needing multi-tenant / per-user auth. Those aren't bugs — they're outside the design envelope. See [`decisions/0001-content-layer-not-the-app.md`](./docs/decisions/0001-content-layer-not-the-app.md) for the explicit scope decision.
|
|
243
247
|
|
|
244
248
|
## Engineering discipline
|
|
245
249
|
|
|
246
250
|
What you get from how this project is built:
|
|
247
251
|
|
|
248
|
-
- **Every load-bearing decision has an ADR.** The [`decisions/`](./
|
|
252
|
+
- **Every load-bearing decision has an ADR.** The [`decisions/`](./docs/decisions/) folder names which choices are structural — files-as-source, journal+catalog split, plugin-as-factory, when something goes in core vs. ships as a plugin — and explains what protects them. When you push against one, there's a written answer waiting instead of folklore.
|
|
249
253
|
- **Engine stays small; capability ships in plugins.** The 15+ plugin ecosystem adds features without core changes, so your upgrade cost stays low. Probes like `decap` (a full third-party CMS mounted in ~150 lines, zero engine changes) are deliberate evidence the extension model holds where it counts.
|
|
250
254
|
- **Builds are deterministic; no async middleware layer.** The journal is the only synchronization primitive — no event bus, no IoC container, no orchestrator running plugins in surprising order. The lifecycle is a list of named phases; "what ran when?" has an answer you can read off the source.
|
|
251
|
-
- **The whole engine is one read.** The [Architecture Overview](./
|
|
255
|
+
- **The whole engine is one read.** The [Architecture Overview](./docs/overview.md) walks the full pipeline top to bottom. Onboarding a new engineer is an afternoon, not a tour through fifteen reference docs.
|
|
252
256
|
|
|
253
257
|
## Mikser among static site generators
|
|
254
258
|
|
|
@@ -274,19 +278,19 @@ Mikser itself has a previous chapter: the [legacy 7.x line](https://github.com/a
|
|
|
274
278
|
|
|
275
279
|
| Document | Audience | Description |
|
|
276
280
|
| ----------------------------------------------------- | ------------------ | -------------------------------------------------- |
|
|
277
|
-
| [Architecture Overview](./
|
|
278
|
-
| [Getting Started](./
|
|
279
|
-
| [Configuration](./
|
|
280
|
-
| [Lifecycle](./
|
|
281
|
-
| [Plugins](./
|
|
282
|
-
| [Entities](./
|
|
283
|
-
| [Rendering](./
|
|
284
|
-
| [Watch Mode](./
|
|
281
|
+
| [Architecture Overview](./docs/overview.md) | Everyone | **Start here.** End-to-end walkthrough of how a file becomes a deployed page across all lifecycle phases. |
|
|
282
|
+
| [Getting Started](./docs/getting-started.md) | Users | Installation, first project, basic usage |
|
|
283
|
+
| [Configuration](./docs/configuration.md) | Users | All CLI options and config file reference |
|
|
284
|
+
| [Lifecycle](./docs/lifecycle.md) | Users & Developers | Complete lifecycle phases and hook system |
|
|
285
|
+
| [Plugins](./docs/plugins.md) | Users & Developers | Built-in plugins, writing custom plugins, the assets / resources / AI pipeline |
|
|
286
|
+
| [Entities](./docs/entities.md) | Users & Developers | Entity model, operations, journal, catalog |
|
|
287
|
+
| [Rendering](./docs/rendering.md) | Users & Developers | Render pipeline, render plugins, render modes |
|
|
288
|
+
| [Watch Mode](./docs/watch-mode.md) | Users | File watching, scheduled tasks, incremental builds |
|
|
285
289
|
| [MCP](https://github.com/almero-digital-marketing/mikser-io-mcp#readme) | Users | The `mikser-io-mcp` plugin — tool surface, `mikser://` resources, twelve worked AI-driven scenarios |
|
|
286
|
-
| [Caching](./
|
|
287
|
-
| [Architecture](./
|
|
288
|
-
| [API Reference](./
|
|
289
|
-
| [Decisions (ADRs)](./
|
|
290
|
+
| [Caching](./docs/caching.md) | Users (production) | The `cache: true` disk cache + working nginx config for reverse-proxy failover |
|
|
291
|
+
| [Architecture](./docs/architecture.md) | Developers | Module-level reference — what's in each file |
|
|
292
|
+
| [API Reference](./docs/api-reference.md) | Developers | Complete public API reference |
|
|
293
|
+
| [Decisions (ADRs)](./docs/decisions/) | Developers | Load-bearing architectural choices and what protects them |
|
|
290
294
|
|
|
291
295
|
## License
|
|
292
296
|
|