fundus 0.1.0 → 0.2.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/README.md +31 -0
- package/build/client/_app/immutable/assets/0.inkPq3ni.css +1 -0
- package/build/client/_app/immutable/assets/0.inkPq3ni.css.br +0 -0
- package/build/client/_app/immutable/assets/0.inkPq3ni.css.gz +0 -0
- package/build/client/_app/immutable/assets/3.ChSpCG-T.css +1 -0
- package/build/client/_app/immutable/assets/3.ChSpCG-T.css.br +0 -0
- package/build/client/_app/immutable/assets/3.ChSpCG-T.css.gz +0 -0
- package/build/client/_app/immutable/assets/{LibraryContextMenu.CMefGNE2.css → LibraryContextMenu.BYvU4SY7.css} +1 -1
- package/build/client/_app/immutable/assets/LibraryContextMenu.BYvU4SY7.css.br +0 -0
- package/build/client/_app/immutable/assets/LibraryContextMenu.BYvU4SY7.css.gz +0 -0
- package/build/client/_app/immutable/assets/ManifestSelector.Il8p28r8.css +1 -0
- package/build/client/_app/immutable/assets/ManifestSelector.Il8p28r8.css.br +0 -0
- package/build/client/_app/immutable/assets/ManifestSelector.Il8p28r8.css.gz +0 -0
- package/build/client/_app/immutable/chunks/BAEZPv6c.js +1 -0
- package/build/client/_app/immutable/chunks/BAEZPv6c.js.br +0 -0
- package/build/client/_app/immutable/chunks/BAEZPv6c.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{D9-155C9.js → ByVSyIj7.js} +1 -1
- package/build/client/_app/immutable/chunks/ByVSyIj7.js.br +1 -0
- package/build/client/_app/immutable/chunks/ByVSyIj7.js.gz +0 -0
- package/build/client/_app/immutable/chunks/C8W3KwDq.js +1 -0
- package/build/client/_app/immutable/chunks/C8W3KwDq.js.br +0 -0
- package/build/client/_app/immutable/chunks/C8W3KwDq.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CP-bnOxU.js +1 -0
- package/build/client/_app/immutable/chunks/CP-bnOxU.js.br +0 -0
- package/build/client/_app/immutable/chunks/CP-bnOxU.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{CNRz3sSw.js → D65cSBhe.js} +2 -2
- package/build/client/_app/immutable/chunks/D65cSBhe.js.br +0 -0
- package/build/client/_app/immutable/chunks/D65cSBhe.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Db8LRH6X.js +108 -0
- package/build/client/_app/immutable/chunks/Db8LRH6X.js.br +0 -0
- package/build/client/_app/immutable/chunks/Db8LRH6X.js.gz +0 -0
- package/build/client/_app/immutable/chunks/TZFh8bT7.js +1 -0
- package/build/client/_app/immutable/chunks/TZFh8bT7.js.br +0 -0
- package/build/client/_app/immutable/chunks/TZFh8bT7.js.gz +0 -0
- package/build/client/_app/immutable/entry/app.nM6SgHKJ.js +2 -0
- package/build/client/_app/immutable/entry/app.nM6SgHKJ.js.br +0 -0
- package/build/client/_app/immutable/entry/app.nM6SgHKJ.js.gz +0 -0
- package/build/client/_app/immutable/entry/start.DNXvkhtP.js +1 -0
- package/build/client/_app/immutable/entry/start.DNXvkhtP.js.br +0 -0
- package/build/client/_app/immutable/entry/start.DNXvkhtP.js.gz +0 -0
- package/build/client/_app/immutable/nodes/0.QeXQNCp2.js +2 -0
- package/build/client/_app/immutable/nodes/0.QeXQNCp2.js.br +0 -0
- package/build/client/_app/immutable/nodes/0.QeXQNCp2.js.gz +0 -0
- package/build/client/_app/immutable/nodes/1.dMklHTc2.js +1 -0
- package/build/client/_app/immutable/nodes/1.dMklHTc2.js.br +0 -0
- package/build/client/_app/immutable/nodes/1.dMklHTc2.js.gz +0 -0
- package/build/client/_app/immutable/nodes/2.CW4VMcIP.js +3 -0
- package/build/client/_app/immutable/nodes/2.CW4VMcIP.js.br +0 -0
- package/build/client/_app/immutable/nodes/2.CW4VMcIP.js.gz +0 -0
- package/build/client/_app/immutable/nodes/3.CTiY_pbK.js +1 -0
- package/build/client/_app/immutable/nodes/3.CTiY_pbK.js.br +0 -0
- package/build/client/_app/immutable/nodes/3.CTiY_pbK.js.gz +0 -0
- package/build/client/_app/immutable/nodes/4.Dgxx52FJ.js +2 -0
- package/build/client/_app/immutable/nodes/4.Dgxx52FJ.js.br +0 -0
- package/build/client/_app/immutable/nodes/4.Dgxx52FJ.js.gz +0 -0
- package/build/client/_app/version.json +1 -1
- package/build/client/_app/version.json.br +0 -0
- package/build/client/_app/version.json.gz +0 -0
- package/build/client/favicon.svg +4 -0
- package/build/client/favicon.svg.br +0 -0
- package/build/client/favicon.svg.gz +0 -0
- package/build/handler.js +7 -7
- package/build/index.js +7 -7
- package/build/server/chunks/chunks/{context.js-CGD-b_vR.js → context.js-B77ZAVLi.js} +401 -31
- package/build/server/chunks/chunks/context.js-B77ZAVLi.js.map +1 -0
- package/build/server/chunks/chunks/{core.js-xfrkaQpS.js → core.js-EA_ysb1J.js} +130 -3
- package/build/server/chunks/chunks/core.js-EA_ysb1J.js.map +1 -0
- package/build/server/chunks/chunks/{exports.js-B4E0NJIt.js → exports.js-DvhE7Ffd.js} +2 -2
- package/build/server/chunks/chunks/{exports.js-B4E0NJIt.js.map → exports.js-DvhE7Ffd.js.map} +1 -1
- package/build/server/chunks/chunks/{http.js-Bu-Ao0Vk.js → http.js-DyoR9XRI.js} +5 -3
- package/build/server/chunks/chunks/http.js-DyoR9XRI.js.map +1 -0
- package/build/server/chunks/chunks/{internal.js-ClSxY3uX.js → internal.js-Dn37LrAW.js} +4 -4
- package/build/server/chunks/chunks/{internal.js-ClSxY3uX.js.map → internal.js-Dn37LrAW.js.map} +1 -1
- package/build/server/chunks/chunks/internal2.js-BlXt2rfE.js.map +1 -1
- package/build/server/chunks/chunks/{library-store.js-CZCDpai0.js → library-store.js-DJ-JRE-g.js} +337 -20
- package/build/server/chunks/chunks/library-store.js-DJ-JRE-g.js.map +1 -0
- package/build/server/chunks/chunks/{shared.js-CHQOW_GN.js → shared.js-Dob0TlTu.js} +2 -2
- package/build/server/chunks/chunks/{shared.js-CHQOW_GN.js.map → shared.js-Dob0TlTu.js.map} +1 -1
- package/build/server/chunks/chunks/{utils.js-B9L6I-_K.js → utils.js-B4YimMxv.js} +3 -3
- package/build/server/chunks/chunks/{utils.js-B9L6I-_K.js.map → utils.js-B4YimMxv.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/_...path_/{_server.ts.js-PO5RpNzP.js → _server.ts.js-H4Pq1KAE.js} +4 -5
- package/build/server/chunks/entries/endpoints/api/_...path_/{_server.ts.js-PO5RpNzP.js.map → _server.ts.js-H4Pq1KAE.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/assets/_id_/{_server.ts.js-BmgqGEXs.js → _server.ts.js-BI3KHSfE.js} +5 -6
- package/build/server/chunks/entries/endpoints/api/assets/_id_/{_server.ts.js-BmgqGEXs.js.map → _server.ts.js-BI3KHSfE.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/assets/_id_/raw/{_server.ts.js-DnTOlfHs.js → _server.ts.js-BlNYUbXX.js} +5 -6
- package/build/server/chunks/entries/endpoints/api/assets/_id_/raw/{_server.ts.js-DnTOlfHs.js.map → _server.ts.js-BlNYUbXX.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/assets/{_server.ts.js-DzdC6OJE.js → _server.ts.js-j-_N540j.js} +9 -7
- package/build/server/chunks/entries/endpoints/api/assets/_server.ts.js-j-_N540j.js.map +1 -0
- package/build/server/chunks/entries/endpoints/api/assets/delete/{_server.ts.js-DXJNoYAs.js → _server.ts.js-BHKobzAg.js} +5 -6
- package/build/server/chunks/entries/endpoints/api/assets/delete/{_server.ts.js-DXJNoYAs.js.map → _server.ts.js-BHKobzAg.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/assets/move/{_server.ts.js-CqdKXoZL.js → _server.ts.js-BFKjIOCv.js} +5 -6
- package/build/server/chunks/entries/endpoints/api/assets/move/{_server.ts.js-CqdKXoZL.js.map → _server.ts.js-BFKjIOCv.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/folders/_path_/{_server.ts.js-DIuY66jG.js → _server.ts.js-BxZeVw8y.js} +5 -6
- package/build/server/chunks/entries/endpoints/api/folders/_path_/{_server.ts.js-DIuY66jG.js.map → _server.ts.js-BxZeVw8y.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/folders/{_server.ts.js-D9p-cdJQ.js → _server.ts.js-CO36U_CT.js} +5 -6
- package/build/server/chunks/entries/endpoints/api/folders/{_server.ts.js-D9p-cdJQ.js.map → _server.ts.js-CO36U_CT.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/manifests/_name_/{_server.ts.js-DP1x-xAW.js → _server.ts.js-CBlg1Bkl.js} +5 -6
- package/build/server/chunks/entries/endpoints/api/manifests/_name_/{_server.ts.js-DP1x-xAW.js.map → _server.ts.js-CBlg1Bkl.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/manifests/{_server.ts.js-r1eNMvPd.js → _server.ts.js-DcGLCjIs.js} +5 -6
- package/build/server/chunks/entries/endpoints/api/manifests/{_server.ts.js-r1eNMvPd.js.map → _server.ts.js-DcGLCjIs.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/api/raw/adopt/_server.ts.js-AqhtjOiL.js +32 -0
- package/build/server/chunks/entries/endpoints/api/raw/adopt/_server.ts.js-AqhtjOiL.js.map +1 -0
- package/build/server/chunks/entries/endpoints/api/raw/edited/accept/_server.ts.js-CSa6-DcU.js +30 -0
- package/build/server/chunks/entries/endpoints/api/raw/edited/accept/_server.ts.js-CSa6-DcU.js.map +1 -0
- package/build/server/chunks/entries/endpoints/api/raw/edited/dismiss/_server.ts.js-DnzZUoAo.js +30 -0
- package/build/server/chunks/entries/endpoints/api/raw/edited/dismiss/_server.ts.js-DnzZUoAo.js.map +1 -0
- package/build/server/chunks/entries/endpoints/api/reconcile/_server.ts.js-B0KMprKq.js +33 -0
- package/build/server/chunks/entries/endpoints/api/reconcile/_server.ts.js-B0KMprKq.js.map +1 -0
- package/build/server/chunks/entries/endpoints/api/state/{_server.ts.js-D-m3BwRj.js → _server.ts.js-B1N9o1Ne.js} +5 -6
- package/build/server/chunks/entries/endpoints/api/state/{_server.ts.js-D-m3BwRj.js.map → _server.ts.js-B1N9o1Ne.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/files/_...path_/{_server.ts.js-3nyAQ3f0.js → _server.ts.js-B6HbnRqq.js} +4 -5
- package/build/server/chunks/entries/endpoints/files/_...path_/{_server.ts.js-3nyAQ3f0.js.map → _server.ts.js-B6HbnRqq.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/files/proxy/_...path_/{_server.ts.js-Ba285FiJ.js → _server.ts.js-7wzPyG9v.js} +5 -6
- package/build/server/chunks/entries/endpoints/files/proxy/_...path_/{_server.ts.js-Ba285FiJ.js.map → _server.ts.js-7wzPyG9v.js.map} +1 -1
- package/build/server/chunks/entries/endpoints/files/raw/_...path_/{_server.ts.js-X354l2NP.js → _server.ts.js-BWztxYge.js} +5 -6
- package/build/server/chunks/entries/endpoints/files/raw/_...path_/{_server.ts.js-X354l2NP.js.map → _server.ts.js-BWztxYge.js.map} +1 -1
- package/build/server/chunks/entries/{hooks.server.js-eZ7H27L4.js → hooks.server.js-CVKLksJ1.js} +4 -5
- package/build/server/chunks/entries/{hooks.server.js-eZ7H27L4.js.map → hooks.server.js-CVKLksJ1.js.map} +1 -1
- package/build/server/chunks/{handler-Dcc_-rVh.js → handler-DZ2Sps4a.js} +5 -5
- package/build/server/chunks/{handler-Dcc_-rVh.js.map → handler-DZ2Sps4a.js.map} +1 -1
- package/build/server/chunks/{index.js-DoVHd4mf.js → index.js-CdmTWQz8.js} +5 -5
- package/build/server/chunks/{index.js-DoVHd4mf.js.map → index.js-CdmTWQz8.js.map} +1 -1
- package/build/server/chunks/{manifest.js-C_MsDhaa.js → manifest.js-GhfNr35x.js} +51 -23
- package/build/server/chunks/manifest.js-GhfNr35x.js.map +1 -0
- package/build/server/chunks/nodes/0.js-DGT86_i-.js +11 -0
- package/build/server/chunks/nodes/0.js-DGT86_i-.js.map +1 -0
- package/build/server/chunks/nodes/1.js-DQgYreU0.js +7 -0
- package/build/server/chunks/nodes/{1.js-DKlcrAQP.js.map → 1.js-DQgYreU0.js.map} +1 -1
- package/build/server/chunks/nodes/2.js-DItreSrX.js +7 -0
- package/build/server/chunks/nodes/2.js-DItreSrX.js.map +1 -0
- package/build/server/chunks/nodes/3.js-bE1dJ_6v.js +7 -0
- package/build/server/chunks/nodes/3.js-bE1dJ_6v.js.map +1 -0
- package/build/server/chunks/nodes/4.js-BebbMVXy.js +7 -0
- package/build/server/chunks/nodes/{4.js-JQQZnHWZ.js.map → 4.js-BebbMVXy.js.map} +1 -1
- package/dist/core/api-types.d.ts +38 -0
- package/dist/core/index.d.ts +2 -1
- package/dist/core/index.js +1 -0
- package/dist/core/raw-path.d.ts +8 -0
- package/dist/core/raw-path.js +63 -0
- package/dist/server/build.js +11 -2
- package/dist/server/check.d.ts +5 -1
- package/dist/server/check.js +80 -28
- package/dist/server/cli-commands.d.ts +1 -1
- package/dist/server/cli-commands.js +5 -3
- package/dist/server/cli-init.js +16 -1
- package/dist/server/http.js +5 -1
- package/dist/server/library-store.d.ts +12 -0
- package/dist/server/library-store.js +25 -14
- package/dist/server/operations.d.ts +9 -1
- package/dist/server/operations.js +387 -31
- package/dist/server/processing/processor.js +3 -0
- package/dist/server/raw-reconciliation.d.ts +53 -0
- package/dist/server/raw-reconciliation.js +338 -0
- package/dist/server/raw-safety.d.ts +10 -0
- package/dist/server/raw-safety.js +73 -0
- package/package.json +1 -1
- package/build/client/_app/immutable/assets/0.DLhIsYA_.css +0 -1
- package/build/client/_app/immutable/assets/0.DLhIsYA_.css.br +0 -0
- package/build/client/_app/immutable/assets/0.DLhIsYA_.css.gz +0 -0
- package/build/client/_app/immutable/assets/3.C-toFWI4.css +0 -1
- package/build/client/_app/immutable/assets/3.C-toFWI4.css.br +0 -0
- package/build/client/_app/immutable/assets/3.C-toFWI4.css.gz +0 -0
- package/build/client/_app/immutable/assets/LibraryContextMenu.CMefGNE2.css.br +0 -0
- package/build/client/_app/immutable/assets/LibraryContextMenu.CMefGNE2.css.gz +0 -0
- package/build/client/_app/immutable/chunks/BA62n9FX.js +0 -108
- package/build/client/_app/immutable/chunks/BA62n9FX.js.br +0 -0
- package/build/client/_app/immutable/chunks/BA62n9FX.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CNRz3sSw.js.br +0 -0
- package/build/client/_app/immutable/chunks/CNRz3sSw.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CQsKwm8E.js +0 -1
- package/build/client/_app/immutable/chunks/CQsKwm8E.js.br +0 -0
- package/build/client/_app/immutable/chunks/CQsKwm8E.js.gz +0 -0
- package/build/client/_app/immutable/chunks/D9-155C9.js.br +0 -0
- package/build/client/_app/immutable/chunks/D9-155C9.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DVEDrSOn.js +0 -1
- package/build/client/_app/immutable/chunks/DVEDrSOn.js.br +0 -0
- package/build/client/_app/immutable/chunks/DVEDrSOn.js.gz +0 -0
- package/build/client/_app/immutable/chunks/cznBUb-E.js +0 -1
- package/build/client/_app/immutable/chunks/cznBUb-E.js.br +0 -0
- package/build/client/_app/immutable/chunks/cznBUb-E.js.gz +0 -0
- package/build/client/_app/immutable/entry/app.DwQWZs1F.js +0 -2
- package/build/client/_app/immutable/entry/app.DwQWZs1F.js.br +0 -0
- package/build/client/_app/immutable/entry/app.DwQWZs1F.js.gz +0 -0
- package/build/client/_app/immutable/entry/start.DEpAL_8Q.js +0 -1
- package/build/client/_app/immutable/entry/start.DEpAL_8Q.js.br +0 -0
- package/build/client/_app/immutable/entry/start.DEpAL_8Q.js.gz +0 -0
- package/build/client/_app/immutable/nodes/0.Sr1WDgiY.js +0 -2
- package/build/client/_app/immutable/nodes/0.Sr1WDgiY.js.br +0 -0
- package/build/client/_app/immutable/nodes/0.Sr1WDgiY.js.gz +0 -0
- package/build/client/_app/immutable/nodes/1.BwHqv728.js +0 -1
- package/build/client/_app/immutable/nodes/1.BwHqv728.js.br +0 -2
- package/build/client/_app/immutable/nodes/1.BwHqv728.js.gz +0 -0
- package/build/client/_app/immutable/nodes/2.DidxYIU-.js +0 -3
- package/build/client/_app/immutable/nodes/2.DidxYIU-.js.br +0 -0
- package/build/client/_app/immutable/nodes/2.DidxYIU-.js.gz +0 -0
- package/build/client/_app/immutable/nodes/3.C1AavoTf.js +0 -1
- package/build/client/_app/immutable/nodes/3.C1AavoTf.js.br +0 -0
- package/build/client/_app/immutable/nodes/3.C1AavoTf.js.gz +0 -0
- package/build/client/_app/immutable/nodes/4.D8UoOONM.js +0 -2
- package/build/client/_app/immutable/nodes/4.D8UoOONM.js.br +0 -0
- package/build/client/_app/immutable/nodes/4.D8UoOONM.js.gz +0 -0
- package/build/server/chunks/chunks/context.js-CGD-b_vR.js.map +0 -1
- package/build/server/chunks/chunks/core.js-xfrkaQpS.js.map +0 -1
- package/build/server/chunks/chunks/folder-path.js-ytx9ucgg.js +0 -64
- package/build/server/chunks/chunks/folder-path.js-ytx9ucgg.js.map +0 -1
- package/build/server/chunks/chunks/http.js-Bu-Ao0Vk.js.map +0 -1
- package/build/server/chunks/chunks/library-store.js-CZCDpai0.js.map +0 -1
- package/build/server/chunks/entries/endpoints/api/assets/_server.ts.js-DzdC6OJE.js.map +0 -1
- package/build/server/chunks/manifest.js-C_MsDhaa.js.map +0 -1
- package/build/server/chunks/nodes/0.js-BP6hHAK9.js +0 -11
- package/build/server/chunks/nodes/0.js-BP6hHAK9.js.map +0 -1
- package/build/server/chunks/nodes/1.js-DKlcrAQP.js +0 -7
- package/build/server/chunks/nodes/2.js-B-8TTZH_.js +0 -7
- package/build/server/chunks/nodes/2.js-B-8TTZH_.js.map +0 -1
- package/build/server/chunks/nodes/3.js-Cf7H6K5p.js +0 -7
- package/build/server/chunks/nodes/3.js-Cf7H6K5p.js.map +0 -1
- package/build/server/chunks/nodes/4.js-JQQZnHWZ.js +0 -7
package/dist/server/check.js
CHANGED
|
@@ -2,16 +2,17 @@
|
|
|
2
2
|
* `fundus check` — the verify-only CI gate: no writes, ever. It
|
|
3
3
|
* fails when the library does not validate, when a committed proxy is stale,
|
|
4
4
|
* missing, or orphaned, or when a generated manifest module differs from an
|
|
5
|
-
* in-memory regeneration. Warnings (unreachable
|
|
5
|
+
* in-memory regeneration. Warnings (unreachable assets) are
|
|
6
6
|
* treated as errors unless `--allow-warnings` downgrades them — errors are
|
|
7
7
|
* never downgradable.
|
|
8
8
|
*/
|
|
9
9
|
import { existsSync } from 'node:fs';
|
|
10
10
|
import { readFile } from 'node:fs/promises';
|
|
11
11
|
import { join } from 'node:path';
|
|
12
|
-
import { collectAllReferences, compareCodeUnits, computeLibraryMembership, rawFileNameOfPath } from '../core/index.js';
|
|
12
|
+
import { collectAllReferences, compareCodeUnits, computeLibraryMembership, rawFileNameOfPath, typesForFileName } from '../core/index.js';
|
|
13
13
|
import { loadConfig } from './config-loader.js';
|
|
14
|
-
import { LibraryStore
|
|
14
|
+
import { LibraryStore } from './library-store.js';
|
|
15
|
+
import { classifyRawDirectory } from './raw-reconciliation.js';
|
|
15
16
|
import { buildManifestEntries, manifestModuleFileName, renderManifestModule, strayGeneratedModuleNames } from './codegen.js';
|
|
16
17
|
import { descriptorsByType, serverPluginsByType } from './plugins.js';
|
|
17
18
|
import { deliveredAssetIds, expectedProxyRelativePath, isProxyFileName, readCurrentProxy } from './processing/processor.js';
|
|
@@ -32,13 +33,21 @@ export async function runCheck(projectRoot, registry = serverPluginsByType) {
|
|
|
32
33
|
};
|
|
33
34
|
// 1. Library validation (structure, parameters, raw pointers, references).
|
|
34
35
|
const issues = await store.validate(library);
|
|
36
|
+
const validationKeys = new Set();
|
|
35
37
|
for (const issue of issues) {
|
|
38
|
+
const key = `${issue.assetId ?? ''}|${issue.message}`;
|
|
39
|
+
if (validationKeys.has(key))
|
|
40
|
+
continue;
|
|
41
|
+
validationKeys.add(key);
|
|
36
42
|
report.errors.push({
|
|
37
43
|
kind: 'validation',
|
|
38
44
|
message: issue.message,
|
|
39
45
|
...(issue.assetId !== undefined ? { assetId: issue.assetId } : {})
|
|
40
46
|
});
|
|
41
47
|
}
|
|
48
|
+
const descriptors = Object.values(registry).map((plugin) => plugin.descriptor);
|
|
49
|
+
const raw = await classifyRawDirectory(config.rawDir, library, (path) => typesForFileName(path, descriptors).length > 0);
|
|
50
|
+
report.errors.push(...rawCheckFindings(library, raw, registry, issues));
|
|
42
51
|
// 2. Proxies: every delivered asset's expected proxy exists (staleness is a
|
|
43
52
|
// name check), and the proxy directory holds no orphans.
|
|
44
53
|
const delivered = deliveredAssetIds(library, registry);
|
|
@@ -93,15 +102,80 @@ export async function runCheck(projectRoot, registry = serverPluginsByType) {
|
|
|
93
102
|
// 3. Generated manifest modules must be byte-identical to an in-memory
|
|
94
103
|
// regeneration. Only meaningful when validation passed and every proxy
|
|
95
104
|
// exists — otherwise regeneration is impossible and already reported.
|
|
96
|
-
if (
|
|
105
|
+
if (report.errors.length === 0 && proxiesComplete) {
|
|
97
106
|
await checkGeneratedModules(report, library, config, proxies, registry);
|
|
98
107
|
await checkStrayModules(report, library, config);
|
|
99
108
|
}
|
|
100
|
-
// 4. Warnings: unreachable assets
|
|
109
|
+
// 4. Warnings: unreachable assets only. Raw drift is always a hard CI failure.
|
|
101
110
|
collectUnreachableAssets(report, library, delivered);
|
|
102
|
-
|
|
111
|
+
report.errors.sort((left, right) => compareCodeUnits(`${left.fileName ?? ''}|${left.assetId ?? ''}|${left.kind}|${left.message}`, `${right.fileName ?? ''}|${right.assetId ?? ''}|${right.kind}|${right.message}`));
|
|
112
|
+
report.warnings.sort((left, right) => compareCodeUnits(`${left.assetId ?? ''}|${left.kind}|${left.message}`, `${right.assetId ?? ''}|${right.kind}|${right.message}`));
|
|
103
113
|
return report;
|
|
104
114
|
}
|
|
115
|
+
/** Pure expansion of a raw classification into canonical hard check findings. */
|
|
116
|
+
export function rawCheckFindings(library, raw, registry, validationIssues) {
|
|
117
|
+
const findings = [];
|
|
118
|
+
const descriptors = Object.values(registry).map((plugin) => plugin.descriptor);
|
|
119
|
+
const compatibleTypes = (path) => typesForFileName(path, descriptors);
|
|
120
|
+
for (const diagnostic of raw.diagnostics) {
|
|
121
|
+
findings.push({
|
|
122
|
+
kind: 'raw-scan-incomplete',
|
|
123
|
+
message: diagnostic.reason,
|
|
124
|
+
...(diagnostic.path ? { fileName: diagnostic.path } : {})
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
const edited = new Map(raw.edited.map((entry) => [entry.assetId, entry]));
|
|
128
|
+
for (const assetId of raw.unchangedAssetIds) {
|
|
129
|
+
const record = library.assets[assetId];
|
|
130
|
+
if (!compatibleTypes(record.raw).includes(record.type)) {
|
|
131
|
+
findings.push({
|
|
132
|
+
kind: 'raw-type-incompatible',
|
|
133
|
+
message: `Raw file "${record.raw}" is not compatible with its current ${record.type} type — ` +
|
|
134
|
+
'open or refocus the editor and choose a compatible type.',
|
|
135
|
+
assetId,
|
|
136
|
+
fileName: record.raw
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
for (const entry of edited.values()) {
|
|
141
|
+
findings.push({
|
|
142
|
+
kind: 'raw-hash-drift',
|
|
143
|
+
message: `Raw file "${entry.path}" changed outside Fundus — open or refocus the editor ` +
|
|
144
|
+
'to process or dismiss the edited ingestion item.',
|
|
145
|
+
assetId: entry.assetId,
|
|
146
|
+
fileName: entry.path
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
const missingAssetIds = new Set(validationIssues
|
|
150
|
+
.filter((issue) => issue.message.includes('is missing from the raw directory'))
|
|
151
|
+
.map((issue) => issue.assetId));
|
|
152
|
+
for (const move of raw.moves) {
|
|
153
|
+
if (!missingAssetIds.has(move.assetId)) {
|
|
154
|
+
findings.push({
|
|
155
|
+
kind: 'validation',
|
|
156
|
+
message: `Raw file "${move.from}" is missing from the raw directory.`,
|
|
157
|
+
assetId: move.assetId
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
for (const file of [...raw.unmanaged, ...raw.moves]) {
|
|
162
|
+
findings.push({
|
|
163
|
+
kind: 'raw-unmanaged',
|
|
164
|
+
message: `Raw file "${file.path}" is not managed by Fundus — open or refocus the editor and ` +
|
|
165
|
+
'resolve its ingestion queue item.',
|
|
166
|
+
fileName: file.path
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
for (const blocker of raw.blockers) {
|
|
170
|
+
findings.push({
|
|
171
|
+
kind: 'raw-unmanaged',
|
|
172
|
+
message: `Unsupported raw entry "${blocker.path}": ${blocker.reason} Rename or remove it externally.`,
|
|
173
|
+
fileName: blocker.path,
|
|
174
|
+
...(blocker.assetId ? { assetId: blocker.assetId } : {})
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
return findings;
|
|
178
|
+
}
|
|
105
179
|
async function checkGeneratedModules(report, library, config, proxies, registry) {
|
|
106
180
|
const referencesByAsset = collectAllReferences(library, descriptorsByType(registry));
|
|
107
181
|
const membershipByManifest = computeLibraryMembership(library, referencesByAsset);
|
|
@@ -175,25 +249,3 @@ function collectUnreachableAssets(report, library, delivered) {
|
|
|
175
249
|
}
|
|
176
250
|
}
|
|
177
251
|
}
|
|
178
|
-
/**
|
|
179
|
-
* Raw-hash drift: a raw file replaced on disk while the library still pins the
|
|
180
|
-
* old content hash. The proxy name derives from the *recorded* hash, so the
|
|
181
|
-
* name check alone would keep passing while the delivered pixels are outdated.
|
|
182
|
-
*/
|
|
183
|
-
async function collectRawHashDrift(report, library, config) {
|
|
184
|
-
for (const [assetId, record] of Object.entries(library.assets)) {
|
|
185
|
-
const rawPath = join(config.rawDir, record.raw);
|
|
186
|
-
if (!existsSync(rawPath))
|
|
187
|
-
continue; // A missing raw file is a validation error already.
|
|
188
|
-
const currentHash = sha256Hex(new Uint8Array(await readFile(rawPath)));
|
|
189
|
-
if (currentHash !== record.rawHash) {
|
|
190
|
-
report.warnings.push({
|
|
191
|
-
kind: 'raw-hash-drift',
|
|
192
|
-
message: `Raw file "${record.raw}" changed since it was ingested (hash drift) — ` +
|
|
193
|
-
`re-ingest or update it through the editor, then run \`fundus build\`.`,
|
|
194
|
-
assetId,
|
|
195
|
-
fileName: record.raw
|
|
196
|
-
});
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
}
|
|
@@ -22,5 +22,5 @@ export declare const MANIFEST_PALETTE_HELP = "fundus manifest palette \u2014 lis
|
|
|
22
22
|
export declare function runManifestCommand(argumentList: string[]): Promise<void>;
|
|
23
23
|
export declare const BUILD_HELP = "fundus build \u2014 run the headless pipeline\n\nUsage: fundus build [--json]\n\nValidates the library, processes every stale or missing proxy, prunes\norphaned proxies, and regenerates all manifest modules. Deterministic\nand idempotent: a second run on an unchanged project processes nothing\nand rewrites byte-identical modules. Run it in CI, postinstall, or\nbefore the host build; commit the result.\n\nFlags:\n --json print {\"ok\": true, \"processed\": [\u2026], \"upToDate\": [\u2026],\n \"prunedProxies\": [\u2026], \"modulesWritten\": [\u2026],\n \"modulesRemoved\": [\u2026]} \u2014 asset ids for processing,\n file names for pruning/codegen\n --help this help\n\nA validation failure processes nothing and exits 1 (with --json:\n{\"error\": {\"message\": \u2026}}).\n\nExample:\n fundus build --json | jq '.processed'\n";
|
|
24
24
|
export declare function runBuildCommand(argumentList: string[]): Promise<void>;
|
|
25
|
-
export declare const CHECK_HELP = "fundus check \u2014 verify the committed state (the CI gate)\n\nUsage: fundus check [--allow-warnings] [--json]\n\nVerify-only, no writes: the library must validate, every delivered\nasset's committed proxy must exist under its expected name (staleness\nis a name check), the proxy directory must hold no orphans, and every\ngenerated manifest module must be byte-identical to an in-memory\nregeneration. Warnings (
|
|
25
|
+
export declare const CHECK_HELP = "fundus check \u2014 verify the committed state (the CI gate)\n\nUsage: fundus check [--allow-warnings] [--json]\n\nVerify-only, no writes: the library must validate, every delivered\nasset's committed proxy must exist under its expected name (staleness\nis a name check), the proxy directory must hold no orphans, and every\ngenerated manifest module must be byte-identical to an in-memory\nregeneration. Warnings (such as unreachable assets) fail the check\nunless --allow-warnings downgrades them. New, missing, edited, or\ninvalid entries in the raw directory are hard errors:\nopen or refocus the Fundus editor and resolve its ingestion queue.\nRaw-file errors never downgrade.\n\nFlags:\n --allow-warnings exit 0 despite warnings (errors still fail)\n --json print one report document:\n {\"ok\": <pass/fail>, \"assetCount\": \u2026, \"manifestCount\": \u2026,\n \"errors\": [<finding>, \u2026], \"warnings\": [<finding>, \u2026]}\n where a finding is {\"kind\", \"message\"} plus\n \"assetId\" / \"manifest\" / \"fileName\" where applicable\n --help this help\n\nExit codes: 0 = clean (or warnings allowed), 1 = failed. A failure to\neven run the check (e.g. no fundus.config.ts) exits 1 with the usual\n{\"error\": {\"message\": \u2026}} envelope under --json.\n\nExample:\n fundus check --json | jq '.errors[].kind'\n";
|
|
26
26
|
export declare function runCheckCommand(argumentList: string[]): Promise<void>;
|
|
@@ -1157,9 +1157,11 @@ Verify-only, no writes: the library must validate, every delivered
|
|
|
1157
1157
|
asset's committed proxy must exist under its expected name (staleness
|
|
1158
1158
|
is a name check), the proxy directory must hold no orphans, and every
|
|
1159
1159
|
generated manifest module must be byte-identical to an in-memory
|
|
1160
|
-
regeneration. Warnings (
|
|
1161
|
-
|
|
1162
|
-
|
|
1160
|
+
regeneration. Warnings (such as unreachable assets) fail the check
|
|
1161
|
+
unless --allow-warnings downgrades them. New, missing, edited, or
|
|
1162
|
+
invalid entries in the raw directory are hard errors:
|
|
1163
|
+
open or refocus the Fundus editor and resolve its ingestion queue.
|
|
1164
|
+
Raw-file errors never downgrade.
|
|
1163
1165
|
|
|
1164
1166
|
Flags:
|
|
1165
1167
|
--allow-warnings exit 0 despite warnings (errors still fail)
|
package/dist/server/cli-init.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { mkdir, unlink, writeFile } from 'node:fs/promises';
|
|
1
|
+
import { lstat, mkdir, unlink, writeFile } from 'node:fs/promises';
|
|
2
2
|
import { dirname, resolve } from 'node:path';
|
|
3
3
|
import { createEmptyLibrary, serializeLibrary } from '../core/index.js';
|
|
4
4
|
import { parseArguments, printJsonDocument, requirePositionals } from './cli-io.js';
|
|
@@ -48,6 +48,7 @@ export async function runInitCommand(argumentList) {
|
|
|
48
48
|
throw new Error(`Cannot create fundus.config.ts: ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
49
49
|
}
|
|
50
50
|
const libraryPath = resolve(process.cwd(), 'assets/fundus.library.json');
|
|
51
|
+
const rawPath = resolve(process.cwd(), 'assets/raw');
|
|
51
52
|
const library = createEmptyLibrary();
|
|
52
53
|
library.manifests.main = {};
|
|
53
54
|
try {
|
|
@@ -63,6 +64,20 @@ export async function runInitCommand(argumentList) {
|
|
|
63
64
|
}
|
|
64
65
|
throw new Error(`Cannot create assets/fundus.library.json: ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
65
66
|
}
|
|
67
|
+
try {
|
|
68
|
+
await mkdir(rawPath, { recursive: true });
|
|
69
|
+
const rawStat = await lstat(rawPath);
|
|
70
|
+
if (!rawStat.isDirectory() || rawStat.isSymbolicLink()) {
|
|
71
|
+
throw new Error('the path exists but is not a regular directory');
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
catch (cause) {
|
|
75
|
+
// Only the two files written exclusively by this invocation are rolled
|
|
76
|
+
// back. Existing paths and directories are never removed recursively.
|
|
77
|
+
await unlink(libraryPath).catch(() => { });
|
|
78
|
+
await unlink(configPath).catch(() => { });
|
|
79
|
+
throw new Error(`Cannot create assets/raw directory: ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
80
|
+
}
|
|
66
81
|
if (parsed.flags.json) {
|
|
67
82
|
printJsonDocument({ ok: true, path: 'fundus.config.ts' });
|
|
68
83
|
return;
|
package/dist/server/http.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
|
-
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { lstat, readFile } from 'node:fs/promises';
|
|
3
3
|
import { extname, join } from 'node:path';
|
|
4
4
|
import { assertSafeRelativeFilePath } from './library-store.js';
|
|
5
5
|
const CONTENT_TYPES = {
|
|
@@ -71,6 +71,10 @@ export async function serveFundusFile(config, kind, fileName, request) {
|
|
|
71
71
|
if (!existsSync(filePath)) {
|
|
72
72
|
return errorResponse(new Error(`File "${fileName}" not found.`), 404);
|
|
73
73
|
}
|
|
74
|
+
const fileStat = await lstat(filePath);
|
|
75
|
+
if (!fileStat.isFile() || fileStat.isSymbolicLink()) {
|
|
76
|
+
return errorResponse(new Error(`File "${fileName}" is not a regular file.`), 400);
|
|
77
|
+
}
|
|
74
78
|
const bytes = new Uint8Array(await readFile(filePath));
|
|
75
79
|
const size = bytes.byteLength;
|
|
76
80
|
const headers = {
|
|
@@ -36,6 +36,18 @@ export declare class LibraryStore {
|
|
|
36
36
|
read(): Promise<Library>;
|
|
37
37
|
/** Deterministically serialize + write the library file. */
|
|
38
38
|
write(library: Library): Promise<void>;
|
|
39
|
+
/**
|
|
40
|
+
* Candidate-first source transaction used for externally-owned raw files.
|
|
41
|
+
* The callback runs after entering the store queue and may scan the raw tree;
|
|
42
|
+
* Fundus writes only the library candidate and never touches external bytes.
|
|
43
|
+
*/
|
|
44
|
+
transactCandidate<T>(change: (candidate: Library, current: Library) => Promise<T>): Promise<{
|
|
45
|
+
library: Library;
|
|
46
|
+
value: T;
|
|
47
|
+
changed: boolean;
|
|
48
|
+
}>;
|
|
49
|
+
/** Remove derived proxies after a source record was externally deleted/moved. */
|
|
50
|
+
deleteProxiesOf(assetId: string): Promise<void>;
|
|
39
51
|
/**
|
|
40
52
|
* Ingest a new asset: write the raw file into `rawDir` (under a sanitized,
|
|
41
53
|
* non-colliding name), hash its content, and create the library record with
|
|
@@ -8,7 +8,7 @@ import { existsSync } from 'node:fs';
|
|
|
8
8
|
import { mkdir, readFile, rename, rm, unlink, writeFile } from 'node:fs/promises';
|
|
9
9
|
import { dirname, extname, join } from 'node:path';
|
|
10
10
|
import { createHash } from 'node:crypto';
|
|
11
|
-
import { assetIdError, collectAllReferences, createEmptyLibrary, folderNameError, folderNameOf, folderOfRawPath, folderPathError, isFolderOrDescendant, joinFolderPath, manifestColorError, manifestEmojiError, parseLibrary, referenceErrors, normalizeFolderPath, parentFolderPath, portableFolderPathKey, rawFileNameOfPath, replaceFolderPrefix, manifestNameError, serializeLibrary } from '../core/index.js';
|
|
11
|
+
import { assetIdError, collectAllReferences, createEmptyLibrary, folderNameError, folderNameOf, folderOfRawPath, folderPathError, isFolderOrDescendant, joinFolderPath, manifestColorError, manifestEmojiError, parseLibrary, referenceErrors, normalizeFolderPath, parentFolderPath, portableFolderPathKey, rawFilePathError, rawFileNameOfPath, replaceFolderPrefix, manifestNameError, serializeLibrary } from '../core/index.js';
|
|
12
12
|
import { manifestModuleFileName } from './codegen.js';
|
|
13
13
|
import { descriptorsByType, serverPluginForType, serverPluginsByType } from './plugins.js';
|
|
14
14
|
import { isProxyFileNameFor } from './processing/processor.js';
|
|
@@ -22,19 +22,9 @@ export function assertSafeFileName(fileName) {
|
|
|
22
22
|
}
|
|
23
23
|
/** Validate a persisted/served file path without allowing traversal outside its root. */
|
|
24
24
|
export function assertSafeRelativeFilePath(filePath) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
const segments = filePath.split('/');
|
|
29
|
-
if (segments.length === 0)
|
|
30
|
-
throw new Error(`Unsafe file path "${filePath}".`);
|
|
31
|
-
assertSafeFileName(segments.at(-1) ?? '');
|
|
32
|
-
const folder = segments.slice(0, -1).join('/');
|
|
33
|
-
if (folder !== '') {
|
|
34
|
-
const problem = folderPathError(folder);
|
|
35
|
-
if (problem)
|
|
36
|
-
throw new Error(problem);
|
|
37
|
-
}
|
|
25
|
+
const problem = rawFilePathError(filePath);
|
|
26
|
+
if (problem)
|
|
27
|
+
throw new Error(problem);
|
|
38
28
|
}
|
|
39
29
|
/**
|
|
40
30
|
* Turn an arbitrary uploaded file name (spaces, umlauts, parentheses, nested
|
|
@@ -97,6 +87,27 @@ export class LibraryStore {
|
|
|
97
87
|
await writeFile(temporaryPath, serializeLibrary(library));
|
|
98
88
|
await rename(temporaryPath, this.#config.library);
|
|
99
89
|
}
|
|
90
|
+
/**
|
|
91
|
+
* Candidate-first source transaction used for externally-owned raw files.
|
|
92
|
+
* The callback runs after entering the store queue and may scan the raw tree;
|
|
93
|
+
* Fundus writes only the library candidate and never touches external bytes.
|
|
94
|
+
*/
|
|
95
|
+
async transactCandidate(change) {
|
|
96
|
+
return this.#enqueue(async () => {
|
|
97
|
+
const current = await this.read();
|
|
98
|
+
const before = serializeLibrary(current);
|
|
99
|
+
const candidate = structuredClone(current);
|
|
100
|
+
const value = await change(candidate, current);
|
|
101
|
+
const changed = serializeLibrary(candidate) !== before;
|
|
102
|
+
if (changed)
|
|
103
|
+
await this.write(candidate);
|
|
104
|
+
return { library: candidate, value, changed };
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
/** Remove derived proxies after a source record was externally deleted/moved. */
|
|
108
|
+
async deleteProxiesOf(assetId) {
|
|
109
|
+
await this.#deleteProxiesOf(assetId);
|
|
110
|
+
}
|
|
100
111
|
/**
|
|
101
112
|
* Ingest a new asset: write the raw file into `rawDir` (under a sanitized,
|
|
102
113
|
* non-colliding name), hash its content, and create the library record with
|
|
@@ -22,8 +22,16 @@ export declare class FundusOperations {
|
|
|
22
22
|
constructor(config: ResolvedFundusConfig, registry?: ServerPluginRegistry);
|
|
23
23
|
get config(): ResolvedFundusConfig;
|
|
24
24
|
get store(): LibraryStore;
|
|
25
|
-
/** Read-only state snapshot
|
|
25
|
+
/** Read-only state snapshot, serialized with mutations but never processing or writing. */
|
|
26
26
|
state(): Promise<FundusStateDto>;
|
|
27
|
+
/** Reconcile only changes that are safe without a user decision: unique moves and deletions. */
|
|
28
|
+
reconcileRawState(): Promise<FundusStateDto>;
|
|
29
|
+
/** Adopt an already-existing raw file without copying, renaming, or deleting it. */
|
|
30
|
+
adoptRawFile(token: string, id: string, type: string, manifests?: readonly string[]): Promise<FundusStateDto>;
|
|
31
|
+
/** Reset/reseed edited bytes, optionally changing to another compatible type. */
|
|
32
|
+
acceptEditedRawFile(token: string, type: string): Promise<FundusStateDto>;
|
|
33
|
+
/** Accept edited bytes while preserving type, parameters, path and memberships. */
|
|
34
|
+
dismissEditedRawFile(token: string): Promise<FundusStateDto>;
|
|
27
35
|
/**
|
|
28
36
|
* Ingest an upload: store the raw file + record, then let the owning
|
|
29
37
|
* plugin's optional `seedIngestParameters` derive better starting
|