mikser-io 8.3.2 → 8.3.6
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 +1 -1
- package/src/catalog.js +29 -0
- package/src/engine.js +14 -1
- package/src/plugins/files.js +27 -6
package/package.json
CHANGED
package/src/catalog.js
CHANGED
|
@@ -71,9 +71,38 @@ registerSchema('mikser_entities', `
|
|
|
71
71
|
CREATE INDEX IF NOT EXISTS idx_mikser_entities_uri ON mikser_entities(uri);
|
|
72
72
|
`)
|
|
73
73
|
|
|
74
|
+
// Per-process dedupe of the no-filter findEntities/iterateEntities
|
|
75
|
+
// warning. Keyed by the rendering entity id (which is what gets
|
|
76
|
+
// blamed in the recorded refClosure). Once per offending site is
|
|
77
|
+
// enough — the warning is educational, not load-bearing.
|
|
78
|
+
const _warnedNullFilter = new Set()
|
|
79
|
+
|
|
74
80
|
function recordQuery(filter) {
|
|
75
81
|
const ctx = queryContext.getStore()
|
|
76
82
|
if (!ctx?.track) return
|
|
83
|
+
// Null/undefined filter records as `null` in the snapshot's
|
|
84
|
+
// refClosure, which manifest.shouldSkip and manifest.queryAffected
|
|
85
|
+
// treat as "any mutation could have affected this render."
|
|
86
|
+
// Architecturally correct, but it means an aggregate layout whose
|
|
87
|
+
// sidecar calls findEntities() with no args invalidates on every
|
|
88
|
+
// single CREATE/UPDATE/DELETE — including spurious ones (plugins
|
|
89
|
+
// re-emitting unchanged entities, etc.).
|
|
90
|
+
//
|
|
91
|
+
// Warn once per rendering entity so authors can narrow the filter.
|
|
92
|
+
// The fix is almost always to add the collection / type / format
|
|
93
|
+
// dimension the sidecar actually cares about; the JS-side .filter()
|
|
94
|
+
// chain that usually follows findEntities() is the signal that the
|
|
95
|
+
// filter belongs in the SQL.
|
|
96
|
+
if ((filter === undefined || filter === null) && ctx.entityId) {
|
|
97
|
+
if (!_warnedNullFilter.has(ctx.entityId)) {
|
|
98
|
+
_warnedNullFilter.add(ctx.entityId)
|
|
99
|
+
const logger = useLogger()
|
|
100
|
+
logger?.warn(
|
|
101
|
+
'findEntities()/iterateEntities() called with no filter from %s — recorded query dep invalidates on every mutation. For "all renderable entities" use findEntities({"meta.href": {$exists: true}}). For narrower scopes use any indexed column ({collection, type, format, "meta.layout", "meta.lang"}); pushing the filter into SQL keeps invalidation precise.',
|
|
102
|
+
ctx.entityId,
|
|
103
|
+
)
|
|
104
|
+
}
|
|
105
|
+
}
|
|
77
106
|
ctx.track.query(normalizeFilter(filter))
|
|
78
107
|
}
|
|
79
108
|
|
package/src/engine.js
CHANGED
|
@@ -410,7 +410,20 @@ export async function setup(options) {
|
|
|
410
410
|
sidecarQueries: context?.sidecarQueries,
|
|
411
411
|
})
|
|
412
412
|
entry.deps = edges
|
|
413
|
-
|
|
413
|
+
// Pagination produces synthetic pageEntities
|
|
414
|
+
// (index.2.html, index.3.html, ...) whose ids
|
|
415
|
+
// are NOT in mikser_entities — they exist only
|
|
416
|
+
// at render time. Roll their dynamic refs up to
|
|
417
|
+
// entity.parent (set by layouts.onBeforeRender
|
|
418
|
+
// for pages 2+) so the mikser_refs FK to
|
|
419
|
+
// mikser_entities holds. The parent's own
|
|
420
|
+
// render also writes to the same source_id;
|
|
421
|
+
// INSERT OR IGNORE in stmtInsertEdge handles the
|
|
422
|
+
// dedup across pages. Invalidation re-dispatches
|
|
423
|
+
// the parent and the pagination expansion
|
|
424
|
+
// produces the children from there, so granular
|
|
425
|
+
// per-page refs aren't needed.
|
|
426
|
+
runtime.refs?.replaceDynamic(entity.parent ?? entity.id, edges)
|
|
414
427
|
await runtime.complete(entry)
|
|
415
428
|
await updateEntry({ id, output: entry.output, deps: edges })
|
|
416
429
|
}
|
package/src/plugins/files.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import path from 'node:path'
|
|
2
2
|
import { mkdir, symlink, unlink, lstat, realpath } from 'fs/promises'
|
|
3
3
|
import { globby } from 'globby'
|
|
4
|
+
import pMap from 'p-map'
|
|
5
|
+
import { checksumsByCollection } from '../catalog.js'
|
|
4
6
|
|
|
5
7
|
export default ({
|
|
6
8
|
runtime,
|
|
@@ -125,25 +127,44 @@ export default ({
|
|
|
125
127
|
|
|
126
128
|
const paths = await globby('**/*', { cwd: runtime.options.filesFolder })
|
|
127
129
|
trackProgress('Files import', paths.length)
|
|
128
|
-
|
|
130
|
+
// Bulk-prefetch the catalog's existing (id → checksum) map for
|
|
131
|
+
// this collection once at scan start, so the gate below reads
|
|
132
|
+
// from it per-file instead of doing per-file SQL lookups. Same
|
|
133
|
+
// pattern source.js uses for documents/layouts via useSource.
|
|
134
|
+
// Without this gate the plugin re-emitted createEntity on every
|
|
135
|
+
// cycle for every file regardless of changes, inflating the
|
|
136
|
+
// journal with phantom mutations and triggering downstream
|
|
137
|
+
// re-dispatch of aggregate layouts whose recorded query deps
|
|
138
|
+
// matched the collection.
|
|
139
|
+
const priorChecksums = checksumsByCollection(collection)
|
|
140
|
+
await pMap(paths, async relativePath => {
|
|
129
141
|
const { uri, source } = await ensureLink(relativePath)
|
|
130
142
|
let name = relativePath
|
|
131
143
|
if (runtime.config.files?.outputFolder) {
|
|
132
144
|
name = path.join(runtime.config.files.outputFolder, relativePath)
|
|
133
145
|
}
|
|
146
|
+
const id = path.join(`/${collection}`, relativePath)
|
|
147
|
+
const newChecksum = await checksum(source)
|
|
148
|
+
updateProgress()
|
|
149
|
+
// Gate: if the catalog already has this entity with the same
|
|
150
|
+
// checksum, the file hasn't changed since the last cycle.
|
|
151
|
+
// Skip emitting a CREATE — the catalog row stays correct,
|
|
152
|
+
// the journal stays accurate (mutations = actual changes),
|
|
153
|
+
// and downstream aggregate-layout invalidation isn't fired
|
|
154
|
+
// spuriously.
|
|
155
|
+
if (priorChecksums.get(id) === newChecksum) return
|
|
134
156
|
await createEntity({
|
|
135
|
-
id
|
|
157
|
+
id,
|
|
136
158
|
uri,
|
|
137
159
|
collection,
|
|
138
160
|
type,
|
|
139
161
|
format: path.extname(relativePath).substring(1).toLowerCase(),
|
|
140
162
|
name,
|
|
141
163
|
source,
|
|
142
|
-
checksum:
|
|
143
|
-
link: await link(source)
|
|
164
|
+
checksum: newChecksum,
|
|
165
|
+
link: await link(source),
|
|
144
166
|
})
|
|
145
|
-
|
|
146
|
-
}))
|
|
167
|
+
}, { concurrency: 16 })
|
|
147
168
|
})
|
|
148
169
|
|
|
149
170
|
return {
|