mikser-io 9.0.3 → 9.0.5
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/package.json +83 -83
- package/src/render.js +37 -1
- package/src/server.js +47 -25
package/package.json
CHANGED
|
@@ -1,85 +1,85 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
2
|
+
"name": "mikser-io",
|
|
3
|
+
"version": "9.0.5",
|
|
4
|
+
"description": "<p align=\"center\"> <img src=\"mikser-lockup-stacked.svg\" alt=\"mikser\" width=\"198\" /> </p>",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": "./index.js",
|
|
8
|
+
"./package.json": "./package.json",
|
|
9
|
+
"./*": "./*"
|
|
10
|
+
},
|
|
11
|
+
"scripts": {
|
|
12
|
+
"debug": "node --no-warnings app.js --server --watch --working-folder test/fixture",
|
|
13
|
+
"test:unit": "node --test --test-reporter=spec 'test/unit/**/*.test.js'",
|
|
14
|
+
"test:smoke": "node --no-warnings app.js --working-folder test/fixture",
|
|
15
|
+
"test:scenarios": "node --test --test-reporter=spec --test-timeout=60000 'test/scenarios/**/*.test.js'",
|
|
16
|
+
"test:perf": "node test/perf/generate.js && node --no-warnings app.js --working-folder test/perf",
|
|
17
|
+
"test": "npm run test:unit && npm run test:scenarios && npm run test:smoke"
|
|
18
|
+
},
|
|
19
|
+
"bin": {
|
|
20
|
+
"mikser": "app.js"
|
|
21
|
+
},
|
|
22
|
+
"repository": {
|
|
23
|
+
"type": "git",
|
|
24
|
+
"url": "git+https://github.com/almero-digital-marketing/mikser-io.git"
|
|
25
|
+
},
|
|
26
|
+
"type": "module",
|
|
27
|
+
"author": "",
|
|
28
|
+
"license": "ISC",
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@budibase/handlebars-helpers": "^0.14.3",
|
|
31
|
+
"await-semaphore": "^0.1.3",
|
|
32
|
+
"axios": "^1.17.0",
|
|
33
|
+
"better-sqlite3": "^12.10.0",
|
|
34
|
+
"chokidar": "^5.0.0",
|
|
35
|
+
"commander": "^15.0.0",
|
|
36
|
+
"cors": "^2.8.5",
|
|
37
|
+
"dayjs": "^1.11.21",
|
|
38
|
+
"deepdash": "^5.3.9",
|
|
39
|
+
"escape-string-regexp": "^5.0.0",
|
|
40
|
+
"execa": "^9.6.1",
|
|
41
|
+
"front-matter": "^4.0.2",
|
|
42
|
+
"gauge": "^5.0.2",
|
|
43
|
+
"globby": "^16.2.0",
|
|
44
|
+
"handlebars": "^4.7.9",
|
|
45
|
+
"hasha": "^7.0.0",
|
|
46
|
+
"is-url": "^1.2.4",
|
|
47
|
+
"line-reader": "^0.4.0",
|
|
48
|
+
"lodash": "^4.18.1",
|
|
49
|
+
"minimatch": "^10.2.5",
|
|
50
|
+
"node-cron": "^4.2.1",
|
|
51
|
+
"p-map": "^7.0.4",
|
|
52
|
+
"p-queue": "^9.3.0",
|
|
53
|
+
"pino": "^10.3.1",
|
|
54
|
+
"pino-pretty": "^13.1.3",
|
|
55
|
+
"piscina": "^5.1.4",
|
|
56
|
+
"sift": "^17.1.3",
|
|
57
|
+
"truncate-stream": "^1.0.2",
|
|
58
|
+
"yaml": "^2.9.0"
|
|
59
|
+
},
|
|
60
|
+
"optionalDependencies": {
|
|
61
|
+
"express": "^5.2.1"
|
|
62
|
+
},
|
|
63
|
+
"devDependencies": {
|
|
64
|
+
"@ai-sdk/openai": "^3.0.71",
|
|
65
|
+
"fluent-ffmpeg": "^2.1.3",
|
|
66
|
+
"mikser-io-decap": "file:../mikser-io-decap",
|
|
67
|
+
"mikser-io-post-mjml": "file:../mikser-io-post-mjml",
|
|
68
|
+
"mikser-io-post-pdf": "file:../mikser-io-post-pdf",
|
|
69
|
+
"mikser-io-render-eta": "file:../mikser-io-render-eta",
|
|
70
|
+
"mikser-io-render-liquid": "file:../mikser-io-render-liquid",
|
|
71
|
+
"mikser-io-render-markdown": "file:../mikser-io-render-markdown",
|
|
72
|
+
"mikser-io-schemas": "file:../mikser-io-schemas",
|
|
73
|
+
"mikser-io-vector": "file:../mikser-io-vector",
|
|
74
|
+
"puppeteer": "^25.1.0",
|
|
75
|
+
"sharp": "^0.34.5",
|
|
76
|
+
"zod": "^4.4.3"
|
|
77
|
+
},
|
|
78
|
+
"directories": {
|
|
79
|
+
"test": "test"
|
|
80
|
+
},
|
|
81
|
+
"bugs": {
|
|
82
|
+
"url": "https://github.com/almero-digital-marketing/mikser-io/issues"
|
|
83
|
+
},
|
|
84
|
+
"homepage": "https://github.com/almero-digital-marketing/mikser-io#readme"
|
|
85
85
|
}
|
package/src/render.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { readFileSync, existsSync } from 'node:fs'
|
|
2
|
+
import { readFile, unlink } from 'node:fs/promises'
|
|
2
3
|
import { createRequire } from 'node:module'
|
|
3
4
|
import { randomUUID } from 'node:crypto'
|
|
4
5
|
import path from 'node:path'
|
|
@@ -290,7 +291,42 @@ export function useRenderer(runtime, { defaultTimeout = 30_000 } = {}) {
|
|
|
290
291
|
if (!isFinal) return
|
|
291
292
|
remaining.delete(cid)
|
|
292
293
|
clearTimeout(item.timer)
|
|
293
|
-
|
|
294
|
+
|
|
295
|
+
// The postprocess chain writes the final bytes straight to
|
|
296
|
+
// disk and its dispatcher (src/postprocess.js) returns
|
|
297
|
+
// undefined, so a postprocessed completion arrives with
|
|
298
|
+
// output.result empty — even though the bytes exist on disk.
|
|
299
|
+
// Our contract (see the render() JSDoc below) is to hand back
|
|
300
|
+
// the FINAL pipeline output, so read it from where the chain
|
|
301
|
+
// wrote it: <destination> under outputFolder, or under
|
|
302
|
+
// previewFolder when the caller passed save:false (the engine
|
|
303
|
+
// swaps the postprocess outputFolder to previewFolder in that
|
|
304
|
+
// case — see engine.js onBeforePostprocess). Render-only
|
|
305
|
+
// entities already carry their bytes in output.result and
|
|
306
|
+
// skip this entirely.
|
|
307
|
+
let output = entry.output
|
|
308
|
+
if (hasPostprocessor && output?.result == null && entry.entity?.destination) {
|
|
309
|
+
const saveOff = entry.entity.options?.save === false
|
|
310
|
+
const folder = saveOff
|
|
311
|
+
? runtime.options.previewFolder
|
|
312
|
+
: runtime.options.outputFolder
|
|
313
|
+
const file = path.join(folder, entry.entity.destination.replace(/^\/+/, ''))
|
|
314
|
+
try {
|
|
315
|
+
const bytes = await readFile(file)
|
|
316
|
+
output = { success: true, result: bytes }
|
|
317
|
+
// save:false → that previewFolder file is engine
|
|
318
|
+
// scratch the caller explicitly opted out of
|
|
319
|
+
// persisting. Hand back the bytes, leave nothing on
|
|
320
|
+
// disk.
|
|
321
|
+
if (saveOff) await unlink(file).catch(() => {})
|
|
322
|
+
} catch (err) {
|
|
323
|
+
useLogger()?.warn?.(
|
|
324
|
+
'useRenderer: postprocessed output for %s not found at %s — returning empty result: %s',
|
|
325
|
+
entry.entity.id, file, err.message,
|
|
326
|
+
)
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
item.resolve({ output, entity: entry.entity })
|
|
294
330
|
}
|
|
295
331
|
completedHooks.push(hook)
|
|
296
332
|
|
package/src/server.js
CHANGED
|
@@ -39,6 +39,10 @@ export function attachServerCliOptions(commander) {
|
|
|
39
39
|
// Wire the server lifecycle hooks. Called by engine.js's setup() AFTER
|
|
40
40
|
// engine's own onInitialized/onLoad registrations so the log-line order
|
|
41
41
|
// stays "engine folder logs → server bring-up" rather than the reverse.
|
|
42
|
+
// True when this module created the Express app. The late hook applies config-derived
|
|
43
|
+
// settings only then — a caller-supplied app owns its own configuration.
|
|
44
|
+
let ownsApp = false
|
|
45
|
+
|
|
42
46
|
export function setupServer() {
|
|
43
47
|
onInitialized(async () => {
|
|
44
48
|
const logger = useLogger()
|
|
@@ -53,6 +57,7 @@ export function setupServer() {
|
|
|
53
57
|
throw new Error('Express is required for --server. Run: npm install express')
|
|
54
58
|
})
|
|
55
59
|
runtime.options.app = express()
|
|
60
|
+
ownsApp = true
|
|
56
61
|
runtime.options.port = runtime.options.server === true
|
|
57
62
|
? 3001
|
|
58
63
|
: Number(runtime.options.server) || 3001
|
|
@@ -93,9 +98,8 @@ export function setupServer() {
|
|
|
93
98
|
// DIFFERENT host fails closed (non-loopback peer → XFF ignored →
|
|
94
99
|
// loopback endpoints 403) until the operator sets 'uniquelocal'
|
|
95
100
|
// or the specific subnet.
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
logger.info('Server trust proxy: %s', String(trustProxy))
|
|
101
|
+
// `trust proxy` is applied in the late hook at the bottom of this file: runtime.config
|
|
102
|
+
// is not populated during the initialized phase.
|
|
99
103
|
|
|
100
104
|
// CORS — a server exists to be fetched, and in dev the frontend
|
|
101
105
|
// is almost always on a different origin (a dev server on
|
|
@@ -110,28 +114,30 @@ export function setupServer() {
|
|
|
110
114
|
// Default on (?? true) so programmatic setup({ server }) — which
|
|
111
115
|
// bypasses commander's --no-cors default — matches the CLI.
|
|
112
116
|
// Explicit false (config or --no-cors) still disables.
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
117
|
+
// Registered unconditionally, decided per request. The middleware's position is fixed
|
|
118
|
+
// here — ahead of static and every plugin router — while its setting lives in
|
|
119
|
+
// runtime.config, which is only populated in the load phase. The per-request callback
|
|
120
|
+
// is what lets both hold.
|
|
121
|
+
//
|
|
122
|
+
// Extensible header arrays. Plugins push values into these at factory time to teach
|
|
123
|
+
// CORS about headers they care about — e.g. the mikser-io-mcp plugin adds
|
|
124
|
+
// mcp-session-id, mcp-protocol-version, last-event-id so browser-side MCP clients can
|
|
125
|
+
// complete the Streamable HTTP handshake. Default to the minimum every server needs.
|
|
126
|
+
runtime.options.corsAllowHeaders = ['Content-Type', 'Authorization']
|
|
127
|
+
runtime.options.corsExposeHeaders = []
|
|
128
|
+
const { default: cors } = await import('cors')
|
|
129
|
+
runtime.options.app.use(cors((req, callback) => {
|
|
130
|
+
const configured = runtime.config.server?.cors ?? runtime.options.cors ?? true
|
|
131
|
+
// `origin: false` is how the cors package emits no CORS headers at all, which is
|
|
132
|
+
// what --no-cors / config.server.cors:false asks for.
|
|
133
|
+
if (!configured) return callback(null, { origin: false })
|
|
134
|
+
callback(null, {
|
|
135
|
+
origin: configured === true ? '*' : String(configured),
|
|
136
|
+
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
|
|
137
|
+
allowedHeaders: runtime.options.corsAllowHeaders,
|
|
138
|
+
exposedHeaders: runtime.options.corsExposeHeaders,
|
|
139
|
+
})
|
|
140
|
+
}))
|
|
135
141
|
})
|
|
136
142
|
|
|
137
143
|
// Late-binding static + listen. Registered here (inside another
|
|
@@ -150,6 +156,22 @@ export function setupServer() {
|
|
|
150
156
|
const logger = useLogger()
|
|
151
157
|
const { default: express } = await import('express')
|
|
152
158
|
|
|
159
|
+
// Config-derived settings belong here rather than in the onInitialized block that
|
|
160
|
+
// creates the app. Phases run initialize -> initialized -> load -> loaded, and
|
|
161
|
+
// config.js populates runtime.config from a `load` hook — so anything read during
|
|
162
|
+
// `initialized` sees an empty object and takes its default. `loaded` runs after
|
|
163
|
+
// every `load`, so runtime.config is complete here whatever the import order.
|
|
164
|
+
//
|
|
165
|
+
// Express evaluates `trust proxy` per request, so setting it before listen is
|
|
166
|
+
// equivalent to setting it at creation.
|
|
167
|
+
if (ownsApp) {
|
|
168
|
+
const trustProxy = runtime.config.server?.trustProxy ?? 'loopback'
|
|
169
|
+
runtime.options.app.set('trust proxy', trustProxy)
|
|
170
|
+
logger.info('Server trust proxy: %s', String(trustProxy))
|
|
171
|
+
const corsOrigin = runtime.config.server?.cors ?? runtime.options.cors ?? true
|
|
172
|
+
logger.debug('CORS: %s', corsOrigin === false ? 'disabled' : (corsOrigin === true ? '*' : String(corsOrigin)))
|
|
173
|
+
}
|
|
174
|
+
|
|
153
175
|
runtime.options.app.use(express.static(runtime.options.outputFolder))
|
|
154
176
|
|
|
155
177
|
await new Promise(resolve => {
|