mikser-io 8.3.9 → 9.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.
- package/CLAUDE.md +59 -18
- package/README.md +4 -2
- package/index.js +32 -1
- package/package.json +6 -1
- package/src/config.js +20 -29
- package/src/engine.js +48 -36
- package/src/plugins/api.js +16 -14
- package/src/plugins/assets.js +37 -35
- package/src/plugins/commands.js +61 -59
- package/src/plugins/data.js +21 -19
- package/src/plugins/documents.js +13 -12
- package/src/plugins/files.js +145 -143
- package/src/plugins/front-matter.js +20 -18
- package/src/plugins/json.js +18 -16
- package/src/plugins/layouts.js +46 -31
- package/src/plugins/mapper.js +22 -23
- package/src/plugins/observer.js +26 -24
- package/src/plugins/preview.js +9 -9
- package/src/plugins/render/asset.js +4 -0
- package/src/plugins/render/file.js +4 -0
- package/src/plugins/render/hbs.js +14 -0
- package/src/plugins/render/href.js +4 -0
- package/src/plugins/render/preset.js +4 -0
- package/src/plugins/render/resource.js +4 -0
- package/src/plugins/resources.js +28 -26
- package/src/plugins/shares.js +34 -32
- package/src/plugins/validator.js +18 -19
- package/src/plugins/yaml.js +21 -19
- package/src/plugins.js +92 -53
- package/src/postprocess.js +46 -18
- package/src/render.js +29 -3
package/CLAUDE.md
CHANGED
|
@@ -81,7 +81,10 @@ brevity.
|
|
|
81
81
|
(`minThreads: 0` + `idleTimeout: 30_000`) so INLINE-only workloads
|
|
82
82
|
pay no worker overhead. `workerSafeOptions(runtime.options)`
|
|
83
83
|
strips plugin-surface functions before TASKS.WORKER dispatch so
|
|
84
|
-
Piscina's structured clone doesn't choke.
|
|
84
|
+
Piscina's structured clone doesn't choke. Renderer / postprocessor
|
|
85
|
+
descriptors in `runtime.options.plugins` are projected to their
|
|
86
|
+
`render-${name}` / `post-${name}` identifier so workers can
|
|
87
|
+
resolve them via dynamic import.
|
|
85
88
|
- `database/` — `createSqliteDatabase()`, `registerSchema()`,
|
|
86
89
|
`useDatabase()` (the `mikser_meta` table stamps schema_version).
|
|
87
90
|
`sift-to-sql.js` translates sift filters to SQL WHERE clauses
|
|
@@ -111,10 +114,27 @@ brevity.
|
|
|
111
114
|
the engine's logger. Each worker opens its own read-only sqlite
|
|
112
115
|
handle on first task (`ensureWorkerDb` in render.js) so template
|
|
113
116
|
helpers like `runtime.lookupHref` stay sync. Never touch the journal
|
|
114
|
-
directly.
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
117
|
+
directly. Plugin loading: main-thread INLINE dispatch reads
|
|
118
|
+
`runtime.renderers` / `runtime.postprocessors` first (populated by
|
|
119
|
+
plugins.js from descriptor returns); workers see empty registries
|
|
120
|
+
in their separate-process runtime and fall through to dynamic-import
|
|
121
|
+
by `mikser-io-${name}` package name. Per-plugin options flow through
|
|
122
|
+
`descriptor.options` and arrive as the `config` arg to
|
|
123
|
+
`load`/`render`/`setup`/`postprocess`/`teardown`.
|
|
124
|
+
- `config.js` — loads `mikser.config.js` at `onLoad` into
|
|
125
|
+
`runtime.config`. v9 holds only engine-level keys (`server`,
|
|
126
|
+
`logging`, `catalog` if tuned) plus the `plugins` array — all
|
|
127
|
+
plugin options moved to the factory call site (ADR-0010).
|
|
128
|
+
- `plugins.js` — dispatches v9 plugin entries at `onLoad`. Each
|
|
129
|
+
entry in `plugins: []` is a factory call return; the dispatcher
|
|
130
|
+
duck-types on shape:
|
|
131
|
+
- function → lifecycle plugin; called with `core` so it can
|
|
132
|
+
register hooks.
|
|
133
|
+
- `{ name, options, load?, render? }` → renderer descriptor;
|
|
134
|
+
stored in `runtime.renderers`.
|
|
135
|
+
- `{ name, options, postprocess, output?, setup?, teardown? }` →
|
|
136
|
+
postprocessor descriptor; stored in `runtime.postprocessors`.
|
|
137
|
+
Strings produce a v9 migration error pointing at the new shape.
|
|
118
138
|
- `manager.js` — file watching (chokidar) and cron scheduling.
|
|
119
139
|
- `source.js` — `useSource` codifies the folder-of-files pattern.
|
|
120
140
|
- `constants.js` — `OPERATION` (CREATE/UPDATE/DELETE/RENDER/
|
|
@@ -149,7 +169,10 @@ brevity.
|
|
|
149
169
|
`import { queryEntities, subscribe, useRenderer, useCollection,
|
|
150
170
|
readEntityContent, isTextEntity } from 'mikser-io'`.
|
|
151
171
|
- **Plugin packages**: `mikser-io-<name>` (mikser-io-mcp, mikser-io-vector,
|
|
152
|
-
mikser-io-schemas, etc.).
|
|
172
|
+
mikser-io-schemas, etc.). Each exports a v9 named factory in
|
|
173
|
+
camelCase: `import { vector } from 'mikser-io-vector'`,
|
|
174
|
+
`import { renderHbs } from 'mikser-io'`. Consumer uses
|
|
175
|
+
`plugins: [vector({...})]` — never the bare string.
|
|
153
176
|
- **MCP tools**: `mikser_<verb>` or `mikser_<subsystem>_<verb>`:
|
|
154
177
|
`mikser_query_entities`, `mikser_read_entity`, `mikser_update_entity`,
|
|
155
178
|
`mikser_delete_entity`, `mikser_render`, `mikser_refs_inbound`,
|
|
@@ -206,25 +229,39 @@ brevity.
|
|
|
206
229
|
persistence pattern. Journal-on-sqlite (Phase 7) enables `--resume`;
|
|
207
230
|
auto-persist (Phase 9) means plugins mutate the yielded entity and
|
|
208
231
|
the journal writes back without an explicit `updateEntry` call.
|
|
232
|
+
- **0010** — Plugin bundles + factory-call form + inline options.
|
|
233
|
+
Plugins are imported by name and called as factories;
|
|
234
|
+
`plugins: []` carries factory returns, never strings.
|
|
235
|
+
Lifecycle plugins are `(options) => (core) => void`; renderers
|
|
236
|
+
return `{name, options, load?, render?}`; postprocessors return
|
|
237
|
+
`{name, options, output?, setup?, postprocess, teardown?}`. Per-
|
|
238
|
+
plugin config moved entirely off `runtime.config.<plugin>`; it
|
|
239
|
+
arrives as factory args, gets stashed on the descriptor, and is
|
|
240
|
+
passed as `config` to `load`/`render`/`setup`/`postprocess`.
|
|
209
241
|
|
|
210
242
|
## MCP
|
|
211
243
|
|
|
212
|
-
Lives in `mikser-io-mcp` plugin (separate repo). Activate by
|
|
213
|
-
`
|
|
244
|
+
Lives in `mikser-io-mcp` plugin (separate repo). Activate by calling
|
|
245
|
+
`mcp()` **first** in your `mikser.config.js` plugins array:
|
|
214
246
|
|
|
215
247
|
```js
|
|
248
|
+
import { mcp } from 'mikser-io-mcp'
|
|
249
|
+
|
|
216
250
|
export default {
|
|
217
|
-
plugins: [
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
251
|
+
plugins: [
|
|
252
|
+
mcp({
|
|
253
|
+
// path: '/mcp', // default '/mcp'
|
|
254
|
+
// endpoints: { ... }, // per-endpoint token + scope
|
|
255
|
+
}),
|
|
256
|
+
/* ...other plugins */
|
|
257
|
+
],
|
|
222
258
|
}
|
|
223
259
|
```
|
|
224
260
|
|
|
225
261
|
Must be first because its factory creates `runtime.options.mcp`
|
|
226
|
-
synchronously, and other plugins gate their
|
|
227
|
-
`if (runtime.options.mcp)` in their own
|
|
262
|
+
synchronously when its closure runs, and other plugins gate their
|
|
263
|
+
MCP tool registration on `if (runtime.options.mcp)` in their own
|
|
264
|
+
`onLoaded`.
|
|
228
265
|
|
|
229
266
|
There is **no `--mcp` CLI flag**. Activation is plugin-presence only.
|
|
230
267
|
|
|
@@ -276,9 +313,13 @@ There is **no `--mcp` CLI flag**. Activation is plugin-presence only.
|
|
|
276
313
|
|
|
277
314
|
- **New engine capability?** Run through ADR-0006's five tests. Bar
|
|
278
315
|
is high. Express is the only earned addition.
|
|
279
|
-
- **New plugin?** Own repo, named `mikser-io-<name>`.
|
|
280
|
-
|
|
281
|
-
|
|
316
|
+
- **New plugin?** Own repo, named `mikser-io-<name>`. Exports a
|
|
317
|
+
named v9 factory (e.g. `export function vector(options = {}) {
|
|
318
|
+
return (core) => { ... } }`). Composes against
|
|
319
|
+
`runtime.options.app` / `runtime.options.mcp` / lifecycle hooks.
|
|
320
|
+
Never imports another plugin's source — and the engine never
|
|
321
|
+
reads `runtime.config.<plugin>` for plugin options; everything
|
|
322
|
+
flows through the factory arg (ADR-0010).
|
|
282
323
|
- **New MCP tool?** Add to `mikser-io-mcp/index.js` via
|
|
283
324
|
`mcp.simpleTool(name, description, zodSchema, handler)`. Tool name
|
|
284
325
|
follows `mikser_*` convention.
|
package/README.md
CHANGED
|
@@ -92,9 +92,11 @@ Plugins extend the tool surface the same way they mount HTTP routes; install the
|
|
|
92
92
|
|
|
93
93
|
```js
|
|
94
94
|
// mikser.config.js
|
|
95
|
+
import { mcp } from 'mikser-io-mcp'
|
|
96
|
+
|
|
95
97
|
export default {
|
|
96
|
-
plugins: [
|
|
97
|
-
//
|
|
98
|
+
plugins: [mcp(), /* … */],
|
|
99
|
+
// mcp({ path: '/mcp', endpoints: { … } }) when options are needed
|
|
98
100
|
}
|
|
99
101
|
```
|
|
100
102
|
|
package/index.js
CHANGED
|
@@ -15,4 +15,35 @@ export * from './src/manager.js'
|
|
|
15
15
|
export * from './src/logger.js'
|
|
16
16
|
export * from './src/engine.js'
|
|
17
17
|
export * from './src/render.js'
|
|
18
|
-
export * from './src/source.js'
|
|
18
|
+
export * from './src/source.js'
|
|
19
|
+
|
|
20
|
+
// Built-in plugin factories. Each takes options and returns the
|
|
21
|
+
// (core) => void closure the engine calls at onLoad time. See ADR-0010
|
|
22
|
+
// for the v9 plugin shape.
|
|
23
|
+
export { api } from './src/plugins/api.js'
|
|
24
|
+
export { assets } from './src/plugins/assets.js'
|
|
25
|
+
export { commands } from './src/plugins/commands.js'
|
|
26
|
+
export { data } from './src/plugins/data.js'
|
|
27
|
+
export { documents } from './src/plugins/documents.js'
|
|
28
|
+
export { files } from './src/plugins/files.js'
|
|
29
|
+
export { frontMatter } from './src/plugins/front-matter.js'
|
|
30
|
+
export { json } from './src/plugins/json.js'
|
|
31
|
+
export { layouts } from './src/plugins/layouts.js'
|
|
32
|
+
export { mapper } from './src/plugins/mapper.js'
|
|
33
|
+
export { observer } from './src/plugins/observer.js'
|
|
34
|
+
export { preview } from './src/plugins/preview.js'
|
|
35
|
+
export { resources } from './src/plugins/resources.js'
|
|
36
|
+
export { shares } from './src/plugins/shares.js'
|
|
37
|
+
export { validator } from './src/plugins/validator.js'
|
|
38
|
+
export { yaml } from './src/plugins/yaml.js'
|
|
39
|
+
|
|
40
|
+
// Built-in renderers. v9 factory shape returns the descriptor that the
|
|
41
|
+
// loader stores in `runtime.renderers`; the same module also still
|
|
42
|
+
// exports `load`/`render` at the top level so Piscina worker dispatch
|
|
43
|
+
// can resolve via dynamic import. ADR-0010.
|
|
44
|
+
export { renderAsset } from './src/plugins/render/asset.js'
|
|
45
|
+
export { renderFile } from './src/plugins/render/file.js'
|
|
46
|
+
export { renderHbs } from './src/plugins/render/hbs.js'
|
|
47
|
+
export { renderHref } from './src/plugins/render/href.js'
|
|
48
|
+
export { renderPreset } from './src/plugins/render/preset.js'
|
|
49
|
+
export { renderResource } from './src/plugins/render/resource.js'
|
package/package.json
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mikser-io",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "9.0.0",
|
|
4
4
|
"description": "<p align=\"center\"> <img src=\"mikser-lockup-stacked.svg\" alt=\"mikser\" width=\"198\" /> </p>",
|
|
5
5
|
"main": "index.js",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": "./index.js",
|
|
8
|
+
"./package.json": "./package.json",
|
|
9
|
+
"./*": "./*"
|
|
10
|
+
},
|
|
6
11
|
"scripts": {
|
|
7
12
|
"debug": "node --no-warnings app.js --server --watch --working-folder test/fixture",
|
|
8
13
|
"test:unit": "node --test --test-reporter=spec 'test/unit/**/*.test.js'",
|
package/src/config.js
CHANGED
|
@@ -4,34 +4,25 @@ import { onLoad } from './lifecycle.js'
|
|
|
4
4
|
import path from 'node:path'
|
|
5
5
|
|
|
6
6
|
onLoad(async () => {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
7
|
+
const logger = useLogger()
|
|
8
|
+
const configFile = path.resolve(runtime.options.config)
|
|
9
|
+
logger.info('Config: %s', configFile)
|
|
10
|
+
try {
|
|
11
|
+
const config = await import(configFile)
|
|
12
|
+
if (typeof config.default == 'function') {
|
|
13
|
+
runtime.config = await config.default(runtime)
|
|
14
|
+
} else if (typeof config.default == 'object') {
|
|
15
|
+
runtime.config = config.default
|
|
16
|
+
}
|
|
17
|
+
} catch (err) {
|
|
18
|
+
if (err.code != 'ERR_MODULE_NOT_FOUND') throw err
|
|
19
|
+
}
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
runtime.config[plugin] = await config.default(runtime)
|
|
29
|
-
} else if (typeof config.default == 'object') {
|
|
30
|
-
runtime.config[plugin] = config.default
|
|
31
|
-
}
|
|
32
|
-
} catch (err) {
|
|
33
|
-
if (err.code != 'ERR_MODULE_NOT_FOUND') throw err
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
}
|
|
21
|
+
// v8 used to walk `runtime.config.plugins` looking for matching
|
|
22
|
+
// `config/<plugin>.config.js` files to merge into `runtime.config`,
|
|
23
|
+
// because plugin entries were strings (names). v9 entries are factory
|
|
24
|
+
// call results (closures or descriptors), and plugin options arrive
|
|
25
|
+
// as factory args — see ADR-0010 — so per-plugin auxiliary config
|
|
26
|
+
// files have nothing to bind to. The loader was removed when the
|
|
27
|
+
// plugins list stopped carrying names.
|
|
37
28
|
})
|
package/src/engine.js
CHANGED
|
@@ -38,6 +38,24 @@ import { queryContext } from './database/query-context.js'
|
|
|
38
38
|
function workerSafeOptions(opts) {
|
|
39
39
|
const result = {}
|
|
40
40
|
for (const [k, v] of Object.entries(opts)) {
|
|
41
|
+
// `plugins` is a mixed array of factory-return values — functions
|
|
42
|
+
// (lifecycle plugins; workers don't need them) and descriptor
|
|
43
|
+
// objects (renderers / postprocessors) carrying closures that
|
|
44
|
+
// can't survive structuredClone. Project descriptors to their
|
|
45
|
+
// `render-${name}` / `post-${name}` identifiers so the worker
|
|
46
|
+
// can resolve them via dynamic import.
|
|
47
|
+
if (k === 'plugins' && Array.isArray(v)) {
|
|
48
|
+
result[k] = v
|
|
49
|
+
.map(p => {
|
|
50
|
+
if (p && typeof p === 'object' && typeof p.name === 'string'
|
|
51
|
+
&& (typeof p.load === 'function' || typeof p.render === 'function')) return `render-${p.name}`
|
|
52
|
+
if (p && typeof p === 'object' && typeof p.name === 'string'
|
|
53
|
+
&& typeof p.postprocess === 'function') return `post-${p.name}`
|
|
54
|
+
return null
|
|
55
|
+
})
|
|
56
|
+
.filter(Boolean)
|
|
57
|
+
continue
|
|
58
|
+
}
|
|
41
59
|
try {
|
|
42
60
|
structuredClone(v)
|
|
43
61
|
result[k] = v
|
|
@@ -89,7 +107,6 @@ export async function setup(options) {
|
|
|
89
107
|
onInitialize(async () => {
|
|
90
108
|
runtime.engine.commander?.version(packageInfo.version)
|
|
91
109
|
.option('-i --working-folder <folder>', 'set mikser working folder', './')
|
|
92
|
-
.option('-p --plugins [plugins...]', 'list of mikser plugins to load', [])
|
|
93
110
|
.option('-c --config <file>', 'set mikser mikser.config.js location', './mikser.config.js')
|
|
94
111
|
.option('-m --mode <mode>', 'set mikser runtime mode', 'development')
|
|
95
112
|
.option('-r --clear', 'clear current state before execution', false)
|
|
@@ -203,16 +220,6 @@ export async function setup(options) {
|
|
|
203
220
|
onRender(async (signal) => {
|
|
204
221
|
const logger = useLogger()
|
|
205
222
|
const renderJobs = new Set()
|
|
206
|
-
// Computed once per cycle — runtime.config doesn't mutate
|
|
207
|
-
// mid-cycle, so every render gets the same filtered slice.
|
|
208
|
-
// Native key iteration + String#startsWith; was a per-render
|
|
209
|
-
// `_.pickBy(runtime.config, (v,k) => _.startsWith(k,'render-'))`
|
|
210
|
-
// which charged ~20 predicate calls + 1 object allocation per
|
|
211
|
-
// entry. Symmetric with onPostprocess's `config` below.
|
|
212
|
-
const renderConfig = {}
|
|
213
|
-
for (const key in runtime.config) {
|
|
214
|
-
if (key.startsWith('render-')) renderConfig[key] = runtime.config[key]
|
|
215
|
-
}
|
|
216
223
|
|
|
217
224
|
// Collect this cycle's mutated entity ids/hrefs/entities so the
|
|
218
225
|
// manifest skip check can re-render anything whose dependencies
|
|
@@ -326,7 +333,11 @@ export async function setup(options) {
|
|
|
326
333
|
...runtime.options,
|
|
327
334
|
...options,
|
|
328
335
|
},
|
|
329
|
-
|
|
336
|
+
// Per-renderer options live on each renderer
|
|
337
|
+
// descriptor (.options) and are picked up inside
|
|
338
|
+
// render.js at dispatch time — no top-level config
|
|
339
|
+
// channel anymore (ADR-0010).
|
|
340
|
+
config: {},
|
|
330
341
|
context,
|
|
331
342
|
state: runtime.state,
|
|
332
343
|
track,
|
|
@@ -470,25 +481,13 @@ export async function setup(options) {
|
|
|
470
481
|
for await (const { entity, options, context, output } of useJournal('Queuing postprocess', [OPERATION.RENDER], signal)) {
|
|
471
482
|
if (output?.success && options.postprocessor) {
|
|
472
483
|
const ext = await resolveOutputExt(options.postprocessor)
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
//
|
|
476
|
-
//
|
|
477
|
-
//
|
|
478
|
-
//
|
|
479
|
-
|
|
480
|
-
// `foo/index.pdf`. Collapse the `/index` segment when the
|
|
481
|
-
// produced extension differs from the rendered page's.
|
|
482
|
-
// Genuine index documents (name ends in `index`) keep
|
|
483
|
-
// their path.
|
|
484
|
-
const originExt = path.extname(entity.destination).slice(1)
|
|
485
|
-
const isCleanUrlPage =
|
|
486
|
-
runtime.config.layouts?.cleanUrls &&
|
|
487
|
-
!_.endsWith(entity.name, 'index') &&
|
|
488
|
-
path.basename(entity.destination, path.extname(entity.destination)) === 'index'
|
|
489
|
-
if (ext !== originExt && isCleanUrlPage) {
|
|
490
|
-
destination = `${path.dirname(entity.destination)}.${ext}`
|
|
491
|
-
}
|
|
484
|
+
// Engine swaps the extension on whatever destination the
|
|
485
|
+
// entity carries. Routing semantics (cleanUrls folder
|
|
486
|
+
// structure, alternate placements, etc.) are whoever-set-
|
|
487
|
+
// entity.destination's responsibility — typically the
|
|
488
|
+
// layouts plugin during layout-match. The postprocess
|
|
489
|
+
// subsystem has no opinion about routing.
|
|
490
|
+
const destination = changeExtension(entity.destination, ext)
|
|
492
491
|
|
|
493
492
|
tasks.push({
|
|
494
493
|
entity: {
|
|
@@ -521,14 +520,23 @@ export async function setup(options) {
|
|
|
521
520
|
|
|
522
521
|
onPostprocess(async (signal) => {
|
|
523
522
|
const logger = useLogger()
|
|
524
|
-
const config = _.pickBy(runtime.config, (value, key) => _.startsWith(key, 'post-'))
|
|
525
523
|
|
|
524
|
+
// Collect every postprocessor descriptor in the plugins list
|
|
525
|
+
// and project to the `post-${name}` identifier the loader uses.
|
|
526
|
+
// Per-postprocessor options now live on each descriptor (.options)
|
|
527
|
+
// — no top-level `config['post-*']` channel anymore (ADR-0010).
|
|
528
|
+
const postPluginNames = []
|
|
529
|
+
for (const entry of runtime.options.plugins) {
|
|
530
|
+
if (entry && typeof entry === 'object' && typeof entry.postprocess === 'function' && typeof entry.name === 'string') {
|
|
531
|
+
postPluginNames.push(`post-${entry.name}`)
|
|
532
|
+
}
|
|
533
|
+
}
|
|
526
534
|
const postPlugins = {}
|
|
527
|
-
for (const pluginName of
|
|
535
|
+
for (const pluginName of postPluginNames) {
|
|
528
536
|
const plugin = await loadPostPlugin(pluginName, runtime.options.workingFolder)
|
|
529
537
|
if (plugin) {
|
|
530
538
|
postPlugins[pluginName] = plugin
|
|
531
|
-
if (plugin.setup) await plugin.setup({ options: runtime.options, config:
|
|
539
|
+
if (plugin.setup) await plugin.setup({ options: runtime.options, config: plugin.options, state: runtime.state, logger })
|
|
532
540
|
}
|
|
533
541
|
}
|
|
534
542
|
|
|
@@ -546,7 +554,11 @@ export async function setup(options) {
|
|
|
546
554
|
...runtime.options,
|
|
547
555
|
...options,
|
|
548
556
|
},
|
|
549
|
-
|
|
557
|
+
// Per-postprocessor options live on the descriptor
|
|
558
|
+
// and are read inside postprocess.js at dispatch
|
|
559
|
+
// time; the empty object here keeps the worker
|
|
560
|
+
// arg shape stable.
|
|
561
|
+
config: {},
|
|
550
562
|
context,
|
|
551
563
|
state: runtime.state
|
|
552
564
|
}
|
|
@@ -632,7 +644,7 @@ export async function setup(options) {
|
|
|
632
644
|
postprocessJobs.size && logger.info('Postprocessed: %d', postprocessJobs.size)
|
|
633
645
|
} finally {
|
|
634
646
|
for (const [pluginName, plugin] of Object.entries(postPlugins)) {
|
|
635
|
-
if (plugin.teardown) await plugin.teardown({ options: runtime.options, config:
|
|
647
|
+
if (plugin.teardown) await plugin.teardown({ options: runtime.options, config: plugin.options, state: runtime.state, logger })
|
|
636
648
|
}
|
|
637
649
|
}
|
|
638
650
|
})
|
package/src/plugins/api.js
CHANGED
|
@@ -312,18 +312,19 @@ function sseSend(res, eventName, payload) {
|
|
|
312
312
|
} catch { /* connection dropped — cleanup runs via 'close' */ }
|
|
313
313
|
}
|
|
314
314
|
|
|
315
|
-
export
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
315
|
+
export function api(options = {}) {
|
|
316
|
+
return ({
|
|
317
|
+
runtime,
|
|
318
|
+
onLoaded,
|
|
319
|
+
onFinalize,
|
|
320
|
+
useLogger,
|
|
321
|
+
useJournal,
|
|
322
|
+
constants: { OPERATION },
|
|
323
|
+
}) => {
|
|
323
324
|
// Shared between onLoaded (populates) and onFinalize (consumes).
|
|
324
325
|
// Hoisted so the lifecycle hooks see the same registry; the body
|
|
325
326
|
// inside onLoaded is what actually fills it in based on
|
|
326
|
-
//
|
|
327
|
+
// `options.endpoints`.
|
|
327
328
|
let apiBase = '/api'
|
|
328
329
|
const cachedEndpoints = []
|
|
329
330
|
|
|
@@ -343,9 +344,9 @@ export default ({
|
|
|
343
344
|
)
|
|
344
345
|
}
|
|
345
346
|
|
|
346
|
-
const endpoints =
|
|
347
|
+
const endpoints = options.endpoints
|
|
347
348
|
if (!endpoints || !Object.keys(endpoints).length) {
|
|
348
|
-
logger.warn('Api plugin loaded but no endpoints configured (api
|
|
349
|
+
logger.warn('Api plugin loaded but no endpoints configured (pass { endpoints: {...} } to api()) — nothing to mount')
|
|
349
350
|
return
|
|
350
351
|
}
|
|
351
352
|
|
|
@@ -353,10 +354,10 @@ export default ({
|
|
|
353
354
|
throw new Error('Express is required for the api plugin — run: npm install express')
|
|
354
355
|
})
|
|
355
356
|
|
|
356
|
-
apiBase =
|
|
357
|
+
apiBase = options.base ?? '/api'
|
|
357
358
|
const base = apiBase // alias so existing local references still work
|
|
358
|
-
const globalPageSize =
|
|
359
|
-
const globalRenderTimeout =
|
|
359
|
+
const globalPageSize = options.pageSize ?? 10
|
|
360
|
+
const globalRenderTimeout = options.renderTimeout ?? 30_000
|
|
360
361
|
|
|
361
362
|
// Preview workflow (render → cache → URL) lives in its own
|
|
362
363
|
// plugin (src/plugins/preview.js) as of v7.3.0. The api plugin
|
|
@@ -916,4 +917,5 @@ export default ({
|
|
|
916
917
|
}
|
|
917
918
|
}
|
|
918
919
|
})
|
|
920
|
+
}
|
|
919
921
|
}
|
package/src/plugins/assets.js
CHANGED
|
@@ -6,7 +6,7 @@ import { globby } from 'globby'
|
|
|
6
6
|
import _ from 'lodash'
|
|
7
7
|
import map from 'p-map'
|
|
8
8
|
|
|
9
|
-
// Normalize a `
|
|
9
|
+
// Normalize a `options.presets[name]` value to a consistent
|
|
10
10
|
// { matches, options } shape so callers don't have to inspect which form
|
|
11
11
|
// the config used.
|
|
12
12
|
//
|
|
@@ -48,35 +48,36 @@ export function normalizePresetConfig(value) {
|
|
|
48
48
|
return { matches: [value], options: {} }
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
export
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
51
|
+
export function assets(options = {}) {
|
|
52
|
+
return ({
|
|
53
|
+
runtime,
|
|
54
|
+
onLoaded,
|
|
55
|
+
useLogger,
|
|
56
|
+
onImport,
|
|
57
|
+
watch,
|
|
58
|
+
onProcessed,
|
|
59
|
+
onBeforeRender,
|
|
60
|
+
useJournal,
|
|
61
|
+
createEntity,
|
|
62
|
+
updateEntity,
|
|
63
|
+
deleteEntity,
|
|
64
|
+
renderEntities,
|
|
65
|
+
onComplete,
|
|
66
|
+
onSync,
|
|
67
|
+
onFinalize,
|
|
68
|
+
findEntity,
|
|
69
|
+
matchEntity,
|
|
70
|
+
changeExtension,
|
|
71
|
+
constants: { ACTION, OPERATION },
|
|
72
|
+
}) => {
|
|
72
73
|
const collection = 'presets'
|
|
73
74
|
const type = 'preset'
|
|
74
75
|
const checksumMap = new Set()
|
|
75
76
|
|
|
76
77
|
async function getEntityPresets(entity) {
|
|
77
78
|
const entityPresets = []
|
|
78
|
-
for (let preset in (
|
|
79
|
-
const { matches } = normalizePresetConfig(
|
|
79
|
+
for (let preset in (options.presets || {})) {
|
|
80
|
+
const { matches } = normalizePresetConfig(options.presets[preset])
|
|
80
81
|
for (let match of matches) {
|
|
81
82
|
if (matchEntity(entity, match)) {
|
|
82
83
|
entityPresets.push(preset)
|
|
@@ -178,7 +179,7 @@ export default ({
|
|
|
178
179
|
// picked up on the next cycle without rebuilding the
|
|
179
180
|
// preset entity from its module.
|
|
180
181
|
const { options: configOptions } = normalizePresetConfig(
|
|
181
|
-
|
|
182
|
+
options.presets?.[entityPreset]
|
|
182
183
|
)
|
|
183
184
|
let destination = entity.name
|
|
184
185
|
if (entity.preset.format) {
|
|
@@ -204,27 +205,27 @@ export default ({
|
|
|
204
205
|
onLoaded(async () => {
|
|
205
206
|
const logger = useLogger()
|
|
206
207
|
|
|
207
|
-
const assetsName =
|
|
208
|
+
const assetsName = options.assetsFolder || 'assets'
|
|
208
209
|
runtime.state.assets = {
|
|
209
210
|
presets: {},
|
|
210
211
|
assetsMap: {},
|
|
211
|
-
assetsFolder:
|
|
212
|
-
? path.join(
|
|
212
|
+
assetsFolder: options.outputFolder
|
|
213
|
+
? path.join(options.outputFolder, assetsName)
|
|
213
214
|
: assetsName,
|
|
214
215
|
}
|
|
215
216
|
|
|
216
|
-
runtime.options.presets =
|
|
217
|
+
runtime.options.presets = options.presetsFolder || collection
|
|
217
218
|
runtime.options.presetsFolder = path.join(runtime.options.workingFolder, runtime.options.presets)
|
|
218
219
|
logger.debug('Presets folder: %s', runtime.options.presetsFolder)
|
|
219
220
|
await mkdir(runtime.options.presetsFolder, { recursive: true })
|
|
220
221
|
|
|
221
|
-
runtime.options.assets =
|
|
222
|
+
runtime.options.assets = options.assetsFolder || 'assets'
|
|
222
223
|
runtime.options.assetsFolder = path.join(runtime.options.workingFolder, runtime.options.assets)
|
|
223
224
|
logger.debug('Assets folder: %s', runtime.options.assetsFolder)
|
|
224
225
|
await mkdir(runtime.options.assetsFolder, { recursive: true })
|
|
225
226
|
|
|
226
227
|
let link = path.join(runtime.options.outputFolder, runtime.options.assets)
|
|
227
|
-
if (
|
|
228
|
+
if (options.outputFolder) link = path.join(runtime.options.outputFolder, options.outputFolder, runtime.options.assets)
|
|
228
229
|
try {
|
|
229
230
|
await mkdir(path.dirname(link), { recursive: true })
|
|
230
231
|
await symlink(path.resolve(runtime.options.assetsFolder), link, 'dir')
|
|
@@ -317,7 +318,7 @@ export default ({
|
|
|
317
318
|
// `mikser-io-preset-<name>`. Config is the source of truth for
|
|
318
319
|
// which presets a project uses; this fills the names a folder
|
|
319
320
|
// scan can't (the code lives in node_modules, not presets/).
|
|
320
|
-
for (const name of Object.keys(
|
|
321
|
+
for (const name of Object.keys(options.presets || {})) {
|
|
321
322
|
if (presets[name]) continue // a local file already loaded it
|
|
322
323
|
const resolved = resolvePreset(name)
|
|
323
324
|
if (!resolved) {
|
|
@@ -430,8 +431,9 @@ export default ({
|
|
|
430
431
|
}
|
|
431
432
|
})
|
|
432
433
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
434
|
+
return {
|
|
435
|
+
collection,
|
|
436
|
+
type,
|
|
437
|
+
}
|
|
436
438
|
}
|
|
437
439
|
}
|