From 68c34165941c4714e17d2ef42f03b6f8d43a1f33 Mon Sep 17 00:00:00 2001 From: Archicratia Date: Mon, 23 Feb 2026 12:07:01 +0100 Subject: [PATCH] =?UTF-8?q?fix:=20=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ISSUE_TEMPLATE/correction-paragraphe.md | 2 +- .../ISSUE_TEMPLATE/verification-factuelle.md | 2 +- .gitea/workflows/ci.yaml | 19 +- .gitea/workflows/ci.yml | 103 ---- .gitignore | 4 + docs/diagrams/START-HERE.md | 0 package-lock.json | 473 +++++++++++++++++- package.json | 19 +- scripts/check-annotations.mjs | 8 +- scripts/inject-anchor-aliases.mjs | 84 ++-- scripts/purge-dist-dev-whoami.mjs | 31 ++ scripts/verify-anchor-aliases-in-dist.mjs | 2 +- scripts/write-dev-whoami.mjs | 26 + src/annotations/archicrat-ia/prologue.yml | 3 - src/components/SidePanel.astro | 26 +- src/layouts/EditionLayout.astro | 125 +++-- src/pages/annotations-index.json.ts | 197 ++++++++ src/pages/para-index.json.ts | 42 ++ tests/anchors-baseline.json | 4 - 19 files changed, 930 insertions(+), 240 deletions(-) delete mode 100644 .gitea/workflows/ci.yml delete mode 100644 docs/diagrams/START-HERE.md create mode 100644 scripts/purge-dist-dev-whoami.mjs create mode 100644 scripts/write-dev-whoami.mjs create mode 100644 src/pages/annotations-index.json.ts create mode 100644 src/pages/para-index.json.ts diff --git a/.gitea/ISSUE_TEMPLATE/correction-paragraphe.md b/.gitea/ISSUE_TEMPLATE/correction-paragraphe.md index a5e4c22..64807f7 100644 --- a/.gitea/ISSUE_TEMPLATE/correction-paragraphe.md +++ b/.gitea/ISSUE_TEMPLATE/correction-paragraphe.md @@ -3,7 +3,7 @@ name: "Correction paragraphe" about: "Proposer une correction ciblée (un paragraphe) avec justification." --- -## Chemin (ex: /archicratie/prologue/) +## Chemin (ex: /archicrat-ia/prologue/) /... diff --git a/.gitea/ISSUE_TEMPLATE/verification-factuelle.md b/.gitea/ISSUE_TEMPLATE/verification-factuelle.md index f4f231d..8945d43 100644 --- a/.gitea/ISSUE_TEMPLATE/verification-factuelle.md +++ b/.gitea/ISSUE_TEMPLATE/verification-factuelle.md @@ -3,7 +3,7 @@ name: "Vérification factuelle / sources" about: "Signaler une assertion à sourcer ou à corriger (preuves, références)." --- -## Chemin (ex: /archicratie/prologue/) +## Chemin (ex: /archicrat-ia/prologue/) /... diff --git a/.gitea/workflows/ci.yaml b/.gitea/workflows/ci.yaml index 663a4a3..5d4a23e 100644 --- a/.gitea/workflows/ci.yaml +++ b/.gitea/workflows/ci.yaml @@ -79,22 +79,7 @@ jobs: set -euo pipefail npm ci - - name: Inline scripts syntax check + - name: Full test suite (CI=1) run: | set -euo pipefail - node scripts/check-inline-js.mjs - - - name: Build (includes postbuild injection + pagefind) - run: | - set -euo pipefail - npm run build - - - name: Anchors contract - run: | - set -euo pipefail - npm run test:anchors - - - name: Verify anchor aliases injected in dist - run: | - set -euo pipefail - node scripts/verify-anchor-aliases-in-dist.mjs + npm run ci diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml deleted file mode 100644 index 1a9320c..0000000 --- a/.gitea/workflows/ci.yml +++ /dev/null @@ -1,103 +0,0 @@ -name: CI - -on: - push: {} - pull_request: - branches: ["master"] - workflow_dispatch: {} - -env: - NODE_OPTIONS: --dns-result-order=ipv4first - -defaults: - run: - shell: bash - -jobs: - build-and-anchors: - runs-on: ubuntu-latest - container: - image: mcr.microsoft.com/devcontainers/javascript-node:22-bookworm - - steps: - - name: Tools sanity - run: | - set -euo pipefail - git --version - node --version - npm --version - npm ping --registry=https://registry.npmjs.org - - - name: Checkout (from event.json, no external actions) - run: | - set -euo pipefail - EVENT_JSON="/var/run/act/workflow/event.json" - test -f "$EVENT_JSON" || (echo "❌ Missing $EVENT_JSON" && exit 1) - - eval "$(node - <<'NODE' - import fs from "node:fs"; - const ev = JSON.parse(fs.readFileSync("/var/run/act/workflow/event.json","utf8")); - const repo = - ev?.repository?.clone_url || - (ev?.repository?.html_url ? (ev.repository.html_url.replace(/\/$/,'') + ".git") : ""); - const sha = - ev?.after || - ev?.pull_request?.head?.sha || - ev?.head_commit?.id || - ev?.sha || - ""; - if (!repo) { console.error("No repository.clone_url/html_url in event.json"); process.exit(1); } - if (!sha) { console.error("No sha/after/pull_request.head.sha in event.json"); process.exit(1); } - console.log(`REPO_URL=${JSON.stringify(repo)}`); - console.log(`SHA=${JSON.stringify(sha)}`); -NODE - )" - - echo "Repo URL: $REPO_URL" - echo "SHA: $SHA" - - rm -rf .git - git init - git remote add origin "$REPO_URL" - git fetch --depth 1 origin "$SHA" - git checkout -q FETCH_HEAD - git log -1 --oneline - - - name: Anchor aliases schema - run: | - set -euo pipefail - node scripts/check-anchor-aliases.mjs - - - name: NPM harden - run: | - set -euo pipefail - npm config set fetch-retries 5 - npm config set fetch-retry-mintimeout 20000 - npm config set fetch-retry-maxtimeout 120000 - npm config set registry https://registry.npmjs.org - npm config get registry - - - name: Install deps - run: | - set -euo pipefail - npm ci - - - name: Inline scripts syntax check - run: | - set -euo pipefail - node scripts/check-inline-js.mjs - - - name: Build (includes postbuild injection + pagefind) - run: | - set -euo pipefail - npm run build - - - name: Anchors contract - run: | - set -euo pipefail - npm run test:anchors - - - name: Verify anchor aliases injected in dist - run: | - set -euo pipefail - node scripts/verify-anchor-aliases-in-dist.mjs diff --git a/.gitignore b/.gitignore index 1bc8dfd..4d4da79 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,10 @@ .env.* !.env.example +# dev-only +public/_auth/whoami +public/_auth/whoami/* + # --- local backups --- *.bak *.bak.* diff --git a/docs/diagrams/START-HERE.md b/docs/diagrams/START-HERE.md deleted file mode 100644 index e69de29..0000000 diff --git a/package-lock.json b/package-lock.json index fab75c0..e7ec80c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.1", "dependencies": { "@astrojs/mdx": "^4.3.13", - "astro": "^5.16.11" + "astro": "^5.17.3" }, "devDependencies": { "@astrojs/sitemap": "^3.7.0", @@ -1905,9 +1905,9 @@ } }, "node_modules/astro": { - "version": "5.16.11", - "resolved": "https://registry.npmjs.org/astro/-/astro-5.16.11.tgz", - "integrity": "sha512-Z7kvkTTT5n6Hn5lCm6T3WU6pkxx84Hn25dtQ6dR7ATrBGq9eVa8EuB/h1S8xvaoVyCMZnIESu99Z9RJfdLRLDA==", + "version": "5.17.3", + "resolved": "https://registry.npmjs.org/astro/-/astro-5.17.3.tgz", + "integrity": "sha512-69dcfPe8LsHzklwj+hl+vunWUbpMB6pmg35mACjetxbJeUNNys90JaBM8ZiwsPK689SAj/4Zqb1ayaANls9/MA==", "license": "MIT", "dependencies": { "@astrojs/compiler": "^2.13.0", @@ -1933,7 +1933,7 @@ "dlv": "^1.1.3", "dset": "^3.1.4", "es-module-lexer": "^1.7.0", - "esbuild": "^0.25.0", + "esbuild": "^0.27.3", "estree-walker": "^3.0.3", "flattie": "^1.1.1", "fontace": "~0.4.0", @@ -1954,16 +1954,16 @@ "prompts": "^2.4.2", "rehype": "^13.0.2", "semver": "^7.7.3", - "shiki": "^3.20.0", + "shiki": "^3.21.0", "smol-toml": "^1.6.0", "svgo": "^4.0.0", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", "tsconfck": "^3.1.6", "ultrahtml": "^1.6.0", - "unifont": "~0.7.1", + "unifont": "~0.7.3", "unist-util-visit": "^5.0.0", - "unstorage": "^1.17.3", + "unstorage": "^1.17.4", "vfile": "^6.0.3", "vite": "^6.4.1", "vitefu": "^1.1.1", @@ -1990,6 +1990,463 @@ "sharp": "^0.34.0" } }, + "node_modules/astro/node_modules/@esbuild/aix-ppc64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz", + "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/astro/node_modules/@esbuild/android-arm": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz", + "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/astro/node_modules/@esbuild/android-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz", + "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/astro/node_modules/@esbuild/android-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz", + "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/astro/node_modules/@esbuild/darwin-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz", + "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/astro/node_modules/@esbuild/darwin-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz", + "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/astro/node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz", + "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/astro/node_modules/@esbuild/freebsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz", + "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/astro/node_modules/@esbuild/linux-arm": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz", + "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/astro/node_modules/@esbuild/linux-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz", + "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/astro/node_modules/@esbuild/linux-ia32": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz", + "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/astro/node_modules/@esbuild/linux-loong64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz", + "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/astro/node_modules/@esbuild/linux-mips64el": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz", + "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/astro/node_modules/@esbuild/linux-ppc64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz", + "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/astro/node_modules/@esbuild/linux-riscv64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz", + "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/astro/node_modules/@esbuild/linux-s390x": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz", + "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/astro/node_modules/@esbuild/linux-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz", + "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/astro/node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz", + "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/astro/node_modules/@esbuild/netbsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz", + "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/astro/node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz", + "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/astro/node_modules/@esbuild/openbsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz", + "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/astro/node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz", + "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/astro/node_modules/@esbuild/sunos-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz", + "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/astro/node_modules/@esbuild/win32-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz", + "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/astro/node_modules/@esbuild/win32-ia32": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz", + "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/astro/node_modules/@esbuild/win32-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz", + "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/astro/node_modules/esbuild": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz", + "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.3", + "@esbuild/android-arm": "0.27.3", + "@esbuild/android-arm64": "0.27.3", + "@esbuild/android-x64": "0.27.3", + "@esbuild/darwin-arm64": "0.27.3", + "@esbuild/darwin-x64": "0.27.3", + "@esbuild/freebsd-arm64": "0.27.3", + "@esbuild/freebsd-x64": "0.27.3", + "@esbuild/linux-arm": "0.27.3", + "@esbuild/linux-arm64": "0.27.3", + "@esbuild/linux-ia32": "0.27.3", + "@esbuild/linux-loong64": "0.27.3", + "@esbuild/linux-mips64el": "0.27.3", + "@esbuild/linux-ppc64": "0.27.3", + "@esbuild/linux-riscv64": "0.27.3", + "@esbuild/linux-s390x": "0.27.3", + "@esbuild/linux-x64": "0.27.3", + "@esbuild/netbsd-arm64": "0.27.3", + "@esbuild/netbsd-x64": "0.27.3", + "@esbuild/openbsd-arm64": "0.27.3", + "@esbuild/openbsd-x64": "0.27.3", + "@esbuild/openharmony-arm64": "0.27.3", + "@esbuild/sunos-x64": "0.27.3", + "@esbuild/win32-arm64": "0.27.3", + "@esbuild/win32-ia32": "0.27.3", + "@esbuild/win32-x64": "0.27.3" + } + }, "node_modules/axobject-query": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", diff --git a/package.json b/package.json index 4b01f46..d0c16a6 100644 --- a/package.json +++ b/package.json @@ -4,32 +4,29 @@ "version": "0.0.1", "private": true, "scripts": { - "dev": "astro dev", + "dev": "node scripts/write-dev-whoami.mjs && astro dev", "preview": "astro preview", "astro": "astro", - "clean": "rm -rf dist", "build": "astro build", "build:clean": "npm run clean && npm run build", - - "postbuild": "node scripts/inject-anchor-aliases.mjs && node scripts/dedupe-ids-dist.mjs && npx pagefind --site dist", - + "postbuild": "node scripts/inject-anchor-aliases.mjs && node scripts/dedupe-ids-dist.mjs && node scripts/build-para-index.mjs && node scripts/build-annotations-index.mjs && node scripts/purge-dist-dev-whoami.mjs && npx pagefind --site dist", "import": "node scripts/import-docx.mjs", "apply:ticket": "node scripts/apply-ticket.mjs", - "audit:dist": "node scripts/audit-dist.mjs", - + "build:para-index": "node scripts/build-para-index.mjs", + "build:annotations-index": "node scripts/build-annotations-index.mjs", "test:aliases": "node scripts/check-anchor-aliases.mjs", "test:anchors": "node scripts/check-anchors.mjs", "test:anchors:update": "node scripts/check-anchors.mjs --update", - - "test": "npm run test:aliases && npm run build:clean && npm run audit:dist && node scripts/verify-anchor-aliases-in-dist.mjs && npm run test:anchors && node scripts/check-inline-js.mjs", - + "test:annotations": "node scripts/check-annotations.mjs", + "test:annotations:media": "node scripts/check-annotations-media.mjs", + "test": "npm run test:aliases && npm run build:clean && npm run audit:dist && node scripts/verify-anchor-aliases-in-dist.mjs && npm run test:anchors && npm run test:annotations && npm run test:annotations:media && node scripts/check-inline-js.mjs", "ci": "CI=1 npm test" }, "dependencies": { "@astrojs/mdx": "^4.3.13", - "astro": "^5.16.11" + "astro": "^5.17.3" }, "devDependencies": { "@astrojs/sitemap": "^3.7.0", diff --git a/scripts/check-annotations.mjs b/scripts/check-annotations.mjs index 6dd0718..660bb74 100644 --- a/scripts/check-annotations.mjs +++ b/scripts/check-annotations.mjs @@ -60,10 +60,12 @@ function getAlias(aliases, pageKey, oldId) { // supporte: // 1) { "": { "": "" } } // 2) { "": "" } - const a1 = aliases?.[pageKey]?.[oldId]; - if (a1) return a1; + const k1 = String(pageKey || ""); + const k2 = k1 ? ("/" + k1.replace(/^\/+|\/+$/g, "") + "/") : ""; + const a1 = (aliases?.[k1]?.[oldId]) || (k2 ? aliases?.[k2]?.[oldId] : ""); + if (a1) return String(a1); const a2 = aliases?.[oldId]; - if (a2) return a2; + if (a2) return String(a2); return ""; } diff --git a/scripts/inject-anchor-aliases.mjs b/scripts/inject-anchor-aliases.mjs index a283611..200720a 100644 --- a/scripts/inject-anchor-aliases.mjs +++ b/scripts/inject-anchor-aliases.mjs @@ -14,6 +14,24 @@ const STRICT = argv.includes("--strict") || process.env.CI === "1" || process.en function escRe(s) { return String(s).replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); } + +async function exists(p) { + try { + await fs.access(p); + return true; + } catch { + return false; + } +} + +function normalizeRoute(route) { + let r = String(route || "").trim(); + if (!r.startsWith("/")) r = "/" + r; + if (!r.endsWith("/")) r = r + "/"; + r = r.replace(/\/{2,}/g, "/"); + return r; +} + function countIdAttr(html, id) { const re = new RegExp(`\\bid=(["'])${escRe(id)}\\1`, "gi"); let c = 0; @@ -22,7 +40,6 @@ function countIdAttr(html, id) { } function findStartTagWithId(html, id) { - // 1er élément qui porte id="..." const re = new RegExp( `<([a-zA-Z0-9:-]+)\\b[^>]*\\bid=(["'])${escRe(id)}\\2[^>]*>`, "i" @@ -36,34 +53,10 @@ function isInjectedAliasSpan(html, id) { const found = findStartTagWithId(html, id); if (!found) return false; if (found.tagName !== "span") return false; - // class="... para-alias ..." return /\bclass=(["'])(?:(?!\1).)*\bpara-alias\b(?:(?!\1).)*\1/i.test(found.tag); } - -function normalizeRoute(route) { - let r = String(route || "").trim(); - if (!r.startsWith("/")) r = "/" + r; - if (!r.endsWith("/")) r = r + "/"; - r = r.replace(/\/{2,}/g, "/"); - return r; -} - -async function exists(p) { - try { - await fs.access(p); - return true; - } catch { - return false; - } -} - -function hasId(html, id) { - const re = new RegExp(`\\bid=(["'])${escRe(id)}\\1`, "i"); - return re.test(html); -} function injectBeforeId(html, newId, injectHtml) { - // insère juste avant la balise qui porte id="newId" const re = new RegExp( `(<[^>]+\\bid=(["'])${escRe(newId)}\\2[^>]*>)`, "i" @@ -82,6 +75,7 @@ async function main() { } const raw = await fs.readFile(ALIASES_PATH, "utf-8"); + /** @type {Record>} */ let aliases; try { @@ -89,6 +83,7 @@ async function main() { } catch (e) { throw new Error(`JSON invalide: ${ALIASES_PATH} (${e?.message || e})`); } + if (!aliases || typeof aliases !== "object" || Array.isArray(aliases)) { throw new Error(`Format invalide: attendu { route: { oldId: newId } } dans ${ALIASES_PATH}`); } @@ -114,10 +109,10 @@ async function main() { console.log(msg); warnCount++; } - + if (entries.length === 0) continue; - const rel = route.replace(/^\/+|\/+$/g, ""); // sans slash + const rel = route.replace(/^\/+|\/+$/g, ""); const htmlPath = path.join(DIST_ROOT, rel, "index.html"); if (!(await exists(htmlPath))) { @@ -135,24 +130,8 @@ async function main() { if (!oldId || !newId) continue; const oldCount = countIdAttr(html, oldId); - if (oldCount > 0) { - // ✅ déjà injecté (idempotent) - if (isInjectedAliasSpan(html, oldId)) continue; - // ⛔️ oldId existe déjà "en vrai" (ex:

) - // => alias inutile / inversé / obsolète - const found = findStartTagWithId(html, oldId); - const where = found ? `<${found.tagName} … id="${oldId}" …>` : `id="${oldId}"`; - const msg = - `⚠️ alias inutile/inversé: oldId déjà présent dans la page (${where}). ` + - `Supprime l'alias ${oldId} -> ${newId} (ou corrige le sens) pour route=${route}`; - if (STRICT) throw new Error(msg); - console.log(msg); - warnCount++; - continue; - } - - // juste après avoir calculé oldCount + // ✅ déjà injecté => idempotent if (oldCount > 0 && isInjectedAliasSpan(html, oldId)) { if (STRICT && oldCount !== 1) { throw new Error(`oldId dupliqué (${oldCount}) alors qu'il est censé être unique: ${route} id=${oldId}`); @@ -160,18 +139,23 @@ async function main() { continue; } - // avant l'injection, après hasId(newId) - const newCount = countIdAttr(html, newId); - if (newCount !== 1) { - const msg = `⚠️ newId non-unique (${newCount}) : ${route} new=${newId} (injection ambiguë)`; + // ⛔️ oldId existe déjà "en vrai" => alias inutile/inversé + if (oldCount > 0) { + const found = findStartTagWithId(html, oldId); + const where = found ? `<${found.tagName} … id="${oldId}" …>` : `id="${oldId}"`; + const msg = + `⚠️ alias inutile/inversé: oldId déjà présent (${where}). ` + + `Supprime ${oldId} -> ${newId} (ou corrige le sens) pour route=${route}`; if (STRICT) throw new Error(msg); console.log(msg); warnCount++; continue; } - if (!hasId(html, newId)) { - const msg = `⚠️ newId introuvable: ${route} old=${oldId} -> new=${newId}`; + // newId doit exister UNE fois (sinon injection ambiguë) + const newCount = countIdAttr(html, newId); + if (newCount !== 1) { + const msg = `⚠️ newId non-unique (${newCount}) : ${route} new=${newId} (injection ambiguë)`; if (STRICT) throw new Error(msg); console.log(msg); warnCount++; diff --git a/scripts/purge-dist-dev-whoami.mjs b/scripts/purge-dist-dev-whoami.mjs new file mode 100644 index 0000000..2a73b94 --- /dev/null +++ b/scripts/purge-dist-dev-whoami.mjs @@ -0,0 +1,31 @@ +// scripts/purge-dist-dev-whoami.mjs +import fs from "node:fs/promises"; +import path from "node:path"; + +const CWD = process.cwd(); +const targetDir = path.join(CWD, "dist", "_auth", "whoami"); +const targetIndex = path.join(CWD, "dist", "_auth", "whoami", "index.html"); + +// Purge idempotente (force=true => pas d'erreur si absent) +async function rmSafe(p) { + try { + await fs.rm(p, { recursive: true, force: true }); + return true; + } catch { + return false; + } +} + +async function main() { + const removedIndex = await rmSafe(targetIndex); + const removedDir = await rmSafe(targetDir); + + // Optionnel: si dist/_auth devient vide, on laisse tel quel (pas besoin de toucher) + const any = removedIndex || removedDir; + console.log(`✅ purge-dist-dev-whoami: ${any ? "purged" : "nothing to purge"}`); +} + +main().catch((e) => { + console.error("❌ purge-dist-dev-whoami failed:", e); + process.exit(1); +}); \ No newline at end of file diff --git a/scripts/verify-anchor-aliases-in-dist.mjs b/scripts/verify-anchor-aliases-in-dist.mjs index 795a549..4bc6073 100644 --- a/scripts/verify-anchor-aliases-in-dist.mjs +++ b/scripts/verify-anchor-aliases-in-dist.mjs @@ -205,7 +205,7 @@ for (const [route, mapping] of Object.entries(data)) { newId, htmlPath, msg: - `oldId present but is NOT an injected alias span ().).\n` + `Saw: ${seen}`, }); continue; diff --git a/scripts/write-dev-whoami.mjs b/scripts/write-dev-whoami.mjs new file mode 100644 index 0000000..52783a1 --- /dev/null +++ b/scripts/write-dev-whoami.mjs @@ -0,0 +1,26 @@ +import fs from "node:fs/promises"; +import path from "node:path"; + +const OUT = path.join(process.cwd(), "public", "_auth", "whoami"); + +const groupsRaw = process.env.PUBLIC_WHOAMI_GROUPS ?? "editors"; +const user = process.env.PUBLIC_WHOAMI_USER ?? "dev"; +const name = process.env.PUBLIC_WHOAMI_NAME ?? "Dev Local"; +const email = process.env.PUBLIC_WHOAMI_EMAIL ?? "area.technik@proton.me"; + +const groups = groupsRaw + .split(/[;,]/) + .map((s) => s.trim()) + .filter(Boolean) + .join(","); + +const body = + `Remote-User: ${user}\n` + + `Remote-Name: ${name}\n` + + `Remote-Email: ${email}\n` + + `Remote-Groups: ${groups}\n`; + +await fs.mkdir(path.dirname(OUT), { recursive: true }); +await fs.writeFile(OUT, body, "utf8"); + +console.log(`✅ dev whoami written: ${path.relative(process.cwd(), OUT)} (${groups})`); \ No newline at end of file diff --git a/src/annotations/archicrat-ia/prologue.yml b/src/annotations/archicrat-ia/prologue.yml index ec3c301..21ba41f 100644 --- a/src/annotations/archicrat-ia/prologue.yml +++ b/src/annotations/archicrat-ia/prologue.yml @@ -1,8 +1,5 @@ schema: 1 -# optionnel (si présent, doit matcher le chemin du fichier) -page: archicratie/archicrat-ia/prologue - paras: p-0-d7974f88: refs: diff --git a/src/components/SidePanel.astro b/src/components/SidePanel.astro index 0eba9fd..a9f0d3d 100644 --- a/src/components/SidePanel.astro +++ b/src/components/SidePanel.astro @@ -144,15 +144,14 @@ const canReaders = inGroup(groups, "readers"); const canEditors = inGroup(groups, "editors"); - access.canUsers = Boolean((info?.ok && (canReaders || canEditors)) || (isDev() && !info?.ok)); + const whoamiSkipped = Boolean(window.__archiFlags && window.__archiFlags.whoamiSkipped); + access.canUsers = Boolean((info?.ok && (canReaders || canEditors)) || whoamiSkipped); access.ready = true; if (btnMediaSubmit) btnMediaSubmit.disabled = !access.canUsers; if (btnSend) btnSend.disabled = !access.canUsers; - if (btnRefSubmit) btnRefSubmit.disabled = !access.canUsers; - // si pas d'accès, on informe (soft) if (!access.canUsers) { if (msgHead) { @@ -162,12 +161,13 @@ } } }).catch(() => { - // fallback dev + // fallback dev (cohérent: media + ref + comment) access.ready = true; - if (isDev()) { + if (Boolean(window.__archiFlags && window.__archiFlags.whoamiSkipped)) { access.canUsers = true; if (btnMediaSubmit) btnMediaSubmit.disabled = false; if (btnSend) btnSend.disabled = false; + if (btnRefSubmit) btnRefSubmit.disabled = false; } }); @@ -209,8 +209,12 @@ async function loadIndex() { if (_idxP) return _idxP; _idxP = (async () => { - const res = await fetch("/annotations-index.json?_=" + Date.now(), { cache: "no-store" }).catch(() => null); - if (res && res.ok) return await res.json(); + try { + const res = await fetch("/annotations-index.json?_=" + Date.now(), { cache: "no-store" }); + if (res && res.ok) return await res.json(); + } catch {} + // ✅ antifragile: ne pas “cacher” un échec pour toujours (dev/HMR/boot race) + _idxP = null; return null; })(); return _idxP; @@ -564,6 +568,14 @@ hideMsg(msgComment); const idx = await loadIndex(); + + // ✅ message soft si l’index est indisponible (sans écraser le message d’auth) + if (!idx && msgHead && msgHead.hidden) { + msgHead.hidden = false; + msgHead.textContent = "Index annotations indisponible (annotations-index.json)."; + msgHead.dataset.kind = "info"; + } + const data = idx?.pages?.[pageKey]?.paras?.[currentParaId] || null; renderLevel2(data); diff --git a/src/layouts/EditionLayout.astro b/src/layouts/EditionLayout.astro index 3c66d5f..967d829 100644 --- a/src/layouts/EditionLayout.astro +++ b/src/layouts/EditionLayout.astro @@ -30,6 +30,13 @@ const GITEA_REPO = import.meta.env.PUBLIC_GITEA_REPO ?? ""; // ✅ OPTIONNEL : bridge serveur (proxy same-origin) const ISSUE_BRIDGE_PATH = import.meta.env.PUBLIC_ISSUE_BRIDGE_PATH ?? ""; + +// ✅ Auth whoami (same-origin) — configurable, antifragile en dev +const WHOAMI_PATH = import.meta.env.PUBLIC_WHOAMI_PATH ?? "/_auth/whoami"; +// Par défaut: en DEV local on SKIP pour éviter le spam 404. +// Pour tester l’auth en dev: export PUBLIC_WHOAMI_IN_DEV=1 +const WHOAMI_IN_DEV = (import.meta.env.PUBLIC_WHOAMI_IN_DEV ?? "") === "1"; +const WHOAMI_FORCE_LOCALHOST = (import.meta.env.PUBLIC_WHOAMI_FORCE_LOCALHOST ?? "") === "1"; --- @@ -52,54 +59,104 @@ const ISSUE_BRIDGE_PATH = import.meta.env.PUBLIC_ISSUE_BRIDGE_PATH ?? ""; {/* ✅ BOOT EARLY : SidePanel dépend de ces globals. */} - @@ -950,11 +1011,13 @@ const ISSUE_BRIDGE_PATH = import.meta.env.PUBLIC_ISSUE_BRIDGE_PATH ?? ""; safe("propose-gate", () => { if (!giteaReady) return; + const p = window.__archiIsEditorP || Promise.resolve(false); + p.then((ok) => { document.querySelectorAll(".para-propose").forEach((el) => { if (ok) showEl(el); - else el.remove(); + else hideEl(el); // ✅ jamais remove => antifragile }); }).catch((err) => { console.warn("[proposer] gate failed; keeping Proposer hidden", err); diff --git a/src/pages/annotations-index.json.ts b/src/pages/annotations-index.json.ts new file mode 100644 index 0000000..12de9fa --- /dev/null +++ b/src/pages/annotations-index.json.ts @@ -0,0 +1,197 @@ +import type { APIRoute } from "astro"; +import * as fs from "node:fs/promises"; +import * as path from "node:path"; +import { parse as parseYAML } from "yaml"; + +const CWD = process.cwd(); +const ANNO_DIR = path.join(CWD, "src", "annotations"); + +// Strict en CI (ou override explicite) +const STRICT = + process.env.ANNOTATIONS_STRICT === "1" || + process.env.CI === "1" || + process.env.CI === "true"; + +async function exists(p: string): Promise { + try { + await fs.access(p); + return true; + } catch { + return false; + } +} + +async function walk(dir: string): Promise { + const out: string[] = []; + const ents = await fs.readdir(dir, { withFileTypes: true }); + for (const e of ents) { + const p = path.join(dir, e.name); + if (e.isDirectory()) out.push(...(await walk(p))); + else out.push(p); + } + return out; +} + +function isPlainObject(x: unknown): x is Record { + return !!x && typeof x === "object" && !Array.isArray(x); +} + +function normalizePageKey(s: unknown): string { + return String(s ?? "") + .replace(/^\/+/, "") + .replace(/\/+$/, "") + .trim(); +} + +function inferPageKeyFromFile(inDirAbs: string, fileAbs: string): string { + const rel = path.relative(inDirAbs, fileAbs).replace(/\\/g, "/"); + return rel.replace(/\.(ya?ml|json)$/i, ""); +} + +function parseDoc(raw: string, fileAbs: string): unknown { + if (/\.json$/i.test(fileAbs)) return JSON.parse(raw); + return parseYAML(raw); +} + +function hardFailOrCollect(errors: string[], msg: string): void { + if (STRICT) throw new Error(msg); + errors.push(msg); +} + +function sanitizeEntry( + fileRel: string, + paraId: string, + entry: unknown, + errors: string[] +): Record { + if (entry == null) return {}; + + if (!isPlainObject(entry)) { + hardFailOrCollect(errors, `${fileRel}: paras.${paraId} must be an object`); + return {}; + } + + const e: Record = { ...entry }; + + const arrayFields = [ + "refs", + "authors", + "quotes", + "media", + "comments_editorial", + ] as const; + + for (const k of arrayFields) { + if (e[k] == null) continue; + if (!Array.isArray(e[k])) { + errors.push(`${fileRel}: paras.${paraId}.${k} must be an array (coerced to [])`); + e[k] = []; + } + } + + return e; +} + +export const GET: APIRoute = async () => { + if (!(await exists(ANNO_DIR))) { + const out = { + schema: 1, + generatedAt: new Date().toISOString(), + pages: {}, + stats: { pages: 0, paras: 0, errors: 0 }, + errors: [] as string[], + }; + + return new Response(JSON.stringify(out), { + headers: { + "Content-Type": "application/json; charset=utf-8", + "Cache-Control": "no-store", + }, + }); + } + + const files = (await walk(ANNO_DIR)).filter((p) => /\.(ya?ml|json)$/i.test(p)); + + const pages: Record> }> = + Object.create(null); + + const errors: string[] = []; + let paraCount = 0; + + for (const f of files) { + const fileRel = path.relative(CWD, f).replace(/\\/g, "/"); + const pageKey = normalizePageKey(inferPageKeyFromFile(ANNO_DIR, f)); + + if (!pageKey) { + hardFailOrCollect(errors, `${fileRel}: cannot infer page key`); + continue; + } + + let doc: unknown; + try { + const raw = await fs.readFile(f, "utf8"); + doc = parseDoc(raw, f); + } catch (e) { + hardFailOrCollect(errors, `${fileRel}: parse failed: ${String((e as any)?.message ?? e)}`); + continue; + } + + if (!isPlainObject(doc) || (doc as any).schema !== 1) { + hardFailOrCollect(errors, `${fileRel}: schema must be 1`); + continue; + } + + if ((doc as any).page != null) { + const declared = normalizePageKey((doc as any).page); + if (declared !== pageKey) { + hardFailOrCollect( + errors, + `${fileRel}: page mismatch (page="${declared}" vs path="${pageKey}")` + ); + } + } + + const parasAny = (doc as any).paras; + if (!isPlainObject(parasAny)) { + hardFailOrCollect(errors, `${fileRel}: missing object key "paras"`); + continue; + } + + if (pages[pageKey]) { + hardFailOrCollect(errors, `${fileRel}: duplicate page "${pageKey}" (only one file per page)`); + continue; + } + + const parasOut: Record> = Object.create(null); + + for (const [paraId, entry] of Object.entries(parasAny)) { + if (!/^p-\d+-/i.test(paraId)) { + hardFailOrCollect(errors, `${fileRel}: invalid para id "${paraId}"`); + continue; + } + parasOut[paraId] = sanitizeEntry(fileRel, paraId, entry, errors); + } + + pages[pageKey] = { paras: parasOut }; + paraCount += Object.keys(parasOut).length; + } + + const out = { + schema: 1, + generatedAt: new Date().toISOString(), + pages, + stats: { + pages: Object.keys(pages).length, + paras: paraCount, + errors: errors.length, + }, + errors, + }; + + return new Response(JSON.stringify(out), { + headers: { + "Content-Type": "application/json; charset=utf-8", + "Cache-Control": "no-store", + }, + }); +}; diff --git a/src/pages/para-index.json.ts b/src/pages/para-index.json.ts new file mode 100644 index 0000000..f1dea80 --- /dev/null +++ b/src/pages/para-index.json.ts @@ -0,0 +1,42 @@ +import type { APIRoute } from "astro"; +import * as fs from "node:fs/promises"; +import * as path from "node:path"; + +export const prerender = true; + +async function exists(p: string) { + try { await fs.access(p); return true; } catch { return false; } +} + +export const GET: APIRoute = async () => { + const distFile = path.join(process.cwd(), "dist", "para-index.json"); + + // Si dist existe (ex: après un build), on renvoie le vrai fichier. + if (await exists(distFile)) { + const raw = await fs.readFile(distFile, "utf8"); + return new Response(raw, { + status: 200, + headers: { + "content-type": "application/json; charset=utf-8", + "cache-control": "no-store", + }, + }); + } + + // Sinon stub (dev sans build) : pas d’erreur, pas de crash, pas de 404. + const stub = { + schema: 1, + generatedAt: new Date().toISOString(), + items: [], + byId: {}, + note: "para-index not built yet (run: npm run build to generate dist/para-index.json)", + }; + + return new Response(JSON.stringify(stub), { + status: 200, + headers: { + "content-type": "application/json; charset=utf-8", + "cache-control": "no-store", + }, + }); +}; \ No newline at end of file diff --git a/tests/anchors-baseline.json b/tests/anchors-baseline.json index 02e69d3..7c34917 100644 --- a/tests/anchors-baseline.json +++ b/tests/anchors-baseline.json @@ -1,8 +1,4 @@ { - "archicratie/00-demarrage/index.html": [ - "p-0-d64c1c39", - "p-1-3f750540" - ], "archicrat-ia/chapitre-1/index.html": [ "p-0-8d27a7f5", "p-1-8a6c18bf", -- 2.49.1