mikser-io 9.61.1 → 9.63.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 +8 -1
- package/package.json +1 -1
- package/src/manager.js +27 -0
- package/src/server.js +36 -0
package/CLAUDE.md
CHANGED
|
@@ -233,7 +233,14 @@ brevity.
|
|
|
233
233
|
- `{ name, options, postprocess, output?, setup?, teardown? }` →
|
|
234
234
|
postprocessor descriptor; stored in `runtime.postprocessors`.
|
|
235
235
|
Strings produce a v9 migration error pointing at the new shape.
|
|
236
|
-
- `manager.js` — file watching (chokidar) and cron scheduling.
|
|
236
|
+
- `manager.js` — file watching (chokidar) and cron scheduling. `watch()`
|
|
237
|
+
turns file events into SYNC events — it is how a source folder becomes
|
|
238
|
+
entities, so pointing it at the output folder feeds output back in as
|
|
239
|
+
input. `watchFolder(folder, handler, options)` is the primitive without
|
|
240
|
+
that meaning, for a plugin that only needs to know bytes changed; it
|
|
241
|
+
carries the shared junk filter and `followSymlinks: true`, which is
|
|
242
|
+
load-bearing because the files plugin serves by symlinking into the
|
|
243
|
+
output folder.
|
|
237
244
|
- `source.js` — `useSource` codifies the folder-of-files pattern.
|
|
238
245
|
- `tools.js` — tool registry. `registerTool(name, {description,
|
|
239
246
|
inputSchema}, handler)` / `toolNames()` / `toolSchema()` / `invokeTool()`,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mikser-io",
|
|
3
|
-
"version": "9.
|
|
3
|
+
"version": "9.63.0",
|
|
4
4
|
"description": "A mixer for content: entities in, configurable render pipelines, outputs of any kind. Static sites are the canonical recipe, not the definition — the same engine renders PDFs, emails and whatever a renderer plugin produces. Files are the source of truth, every lifecycle phase is observable, and the build graph is queryable by an agent.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"exports": {
|
package/src/manager.js
CHANGED
|
@@ -90,6 +90,33 @@ export async function deletedHook(name, context) {
|
|
|
90
90
|
// `ignored` and a function is the one form that has stayed stable.
|
|
91
91
|
const ignoreJunk = (filePath) => /[/\\]\./.test(filePath) || junkFilter()(filePath)
|
|
92
92
|
|
|
93
|
+
// Watch a folder, with mikser's own settings and none of its lifecycle.
|
|
94
|
+
//
|
|
95
|
+
// `watch()` below turns file events into SYNC events — it is how a source
|
|
96
|
+
// folder becomes entities, and pointing it at anything else feeds output back
|
|
97
|
+
// in as input. A plugin that only wants to know when bytes changed needs the
|
|
98
|
+
// watching without the meaning, and was otherwise reaching for chokidar
|
|
99
|
+
// directly: a second copy of a dependency the engine already has, and a second
|
|
100
|
+
// junk filter that would drift from this one.
|
|
101
|
+
//
|
|
102
|
+
// followSymlinks matters more than it looks. The files plugin serves a file by
|
|
103
|
+
// symlinking it from the source folder into the output folder, so a watcher on
|
|
104
|
+
// the output folder that did not follow links would see the link created once
|
|
105
|
+
// and never hear about the file again — every stylesheet edit silently
|
|
106
|
+
// invisible to anything watching what is served.
|
|
107
|
+
export function watchFolder(folder, handler, options = {}) {
|
|
108
|
+
return chokidar
|
|
109
|
+
.watch(folder, {
|
|
110
|
+
interval: 1000,
|
|
111
|
+
binaryInterval: 3000,
|
|
112
|
+
ignored: ignoreJunk,
|
|
113
|
+
ignoreInitial: true,
|
|
114
|
+
followSymlinks: true,
|
|
115
|
+
...options,
|
|
116
|
+
})
|
|
117
|
+
.on('all', (event, fullPath) => handler(event, fullPath))
|
|
118
|
+
}
|
|
119
|
+
|
|
93
120
|
export function watch(name, folder, options = { interval: 1000, binaryInterval: 3000, ignored: ignoreJunk, ignoreInitial: true }) {
|
|
94
121
|
if (runtime.options.watch !== true) return
|
|
95
122
|
|
package/src/server.js
CHANGED
|
@@ -22,11 +22,25 @@
|
|
|
22
22
|
|
|
23
23
|
import path from 'node:path'
|
|
24
24
|
import { fileURLToPath } from 'node:url'
|
|
25
|
+
import { networkInterfaces } from 'node:os'
|
|
25
26
|
|
|
26
27
|
import runtime from './runtime.js'
|
|
27
28
|
import { useLogger } from './engine.js'
|
|
28
29
|
import { onInitialized, onLoad, onLoaded } from './lifecycle.js'
|
|
29
30
|
|
|
31
|
+
// Every non-loopback IPv4 address this machine answers on.
|
|
32
|
+
//
|
|
33
|
+
// IPv4 only, deliberately. The non-internal IPv6 addresses on a typical
|
|
34
|
+
// machine are link-local (fe80::), which need a zone index to be usable and
|
|
35
|
+
// are not what anyone is going to type into a phone — listing them would make
|
|
36
|
+
// the useful lines harder to find, which is the opposite of the point.
|
|
37
|
+
export function localAddresses() {
|
|
38
|
+
return Object.values(networkInterfaces())
|
|
39
|
+
.flat()
|
|
40
|
+
.filter(iface => iface && iface.family === 'IPv4' && !iface.internal)
|
|
41
|
+
.map(iface => iface.address)
|
|
42
|
+
}
|
|
43
|
+
|
|
30
44
|
// Extend the engine's commander with server-related flags. Called by
|
|
31
45
|
// engine.js's onInitialize callback AFTER engine's own options chain
|
|
32
46
|
// and BEFORE the .parse() call — preserves the order of options in
|
|
@@ -214,6 +228,28 @@ export function setupServer() {
|
|
|
214
228
|
// back to the bind URL when no public origin is set.
|
|
215
229
|
const externalUrl = runtime.options.url ?? `http://localhost:${runtime.options.port}`
|
|
216
230
|
logger.info('Server listening: %s', externalUrl)
|
|
231
|
+
|
|
232
|
+
// The addresses that are NOT localhost.
|
|
233
|
+
//
|
|
234
|
+
// listen(port) binds every interface, so the server is
|
|
235
|
+
// already reachable from a phone or a second machine —
|
|
236
|
+
// and the one address printed above is the single one
|
|
237
|
+
// that will not work from either. Anyone testing a
|
|
238
|
+
// layout on a real device has to go and find their own
|
|
239
|
+
// IP, every time, on a machine that already knows it.
|
|
240
|
+
//
|
|
241
|
+
// Only when no public URL is configured. Behind a proxy
|
|
242
|
+
// or a tunnel the LAN address is not how the site is
|
|
243
|
+
// reached, and offering it would be an invitation to
|
|
244
|
+
// debug the wrong origin.
|
|
245
|
+
if (!runtime.options.url) {
|
|
246
|
+
const lan = localAddresses()
|
|
247
|
+
if (lan.length) {
|
|
248
|
+
logger.info('Also on: %s', lan
|
|
249
|
+
.map(address => `http://${address}:${runtime.options.port}`)
|
|
250
|
+
.join(' '))
|
|
251
|
+
}
|
|
252
|
+
}
|
|
217
253
|
resolve()
|
|
218
254
|
})
|
|
219
255
|
|