[E00-S01] Workspace bootstrap #367
@@ -0,0 +1,33 @@
|
|||||||
|
name: CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
workspace:
|
||||||
|
name: Workspace bootstrap checks
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install Node.js 24
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '24'
|
||||||
|
|
||||||
|
- name: Enable pnpm (corepack, pinned to 11.23.0 via packageManager)
|
||||||
|
run: corepack enable
|
||||||
|
|
||||||
|
- name: Install dependencies (frozen lockfile)
|
||||||
|
run: pnpm install --frozen-lockfile
|
||||||
|
|
||||||
|
- name: Build all workspaces
|
||||||
|
run: pnpm build
|
||||||
|
|
||||||
|
- name: Run test suite (includes FIT-001 architecture guard)
|
||||||
|
run: pnpm test
|
||||||
|
|
||||||
|
- name: Typecheck all workspaces
|
||||||
|
run: pnpm typecheck
|
||||||
+17
@@ -0,0 +1,17 @@
|
|||||||
|
# Dependencies
|
||||||
|
node_modules/
|
||||||
|
.pnpm-store/
|
||||||
|
|
||||||
|
# Build output
|
||||||
|
dist/
|
||||||
|
coverage/
|
||||||
|
|
||||||
|
# Local environment files (a committed .env.example lands in E00-S04)
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# OS / editor
|
||||||
|
.DS_Store
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
# PersonalBlog (EPPP)
|
||||||
|
|
||||||
|
A modular personal blogging platform. This is the **workspace bootstrap**
|
||||||
|
(E00-S01): a pnpm monorepo skeleton with strict TypeScript, a committed frozen
|
||||||
|
lockfile, and an enforced dependency boundary between core packages and
|
||||||
|
concrete extensions.
|
||||||
|
|
||||||
|
## Toolchain (pinned)
|
||||||
|
|
||||||
|
| Tool | Version | Where pinned |
|
||||||
|
| ------------ | ------------ | --------------------------------------------- |
|
||||||
|
| pnpm | 11.23.0 | `packageManager` in root `package.json` |
|
||||||
|
| Node.js | 24.x line | `engines.node` in root `package.json` |
|
||||||
|
| TypeScript | 6.0.3 (exact)| root `devDependencies` |
|
||||||
|
| Vitest | 4.1.10 (exact)| root `devDependencies` |
|
||||||
|
|
||||||
|
Direct toolchain dependencies are exact-pinned; the lockfile is committed and
|
||||||
|
installs always use `--frozen-lockfile`.
|
||||||
|
|
||||||
|
## Workspace layout
|
||||||
|
|
||||||
|
```
|
||||||
|
apps/ application shells (e.g. server, admin)
|
||||||
|
packages/ core / shared packages (contracts, core, extension-sdk, ...)
|
||||||
|
extensions/ concrete extensions (themes, features, integrations)
|
||||||
|
tests/ root-level architecture guard tests
|
||||||
|
```
|
||||||
|
|
||||||
|
Each group is a separate workspace glob in `pnpm-workspace.yaml`, so packages
|
||||||
|
cannot accidentally collapse into one another.
|
||||||
|
|
||||||
|
## Dependency boundary (FIT-001)
|
||||||
|
|
||||||
|
Core packages (`packages/*`) must **never** import a concrete extension
|
||||||
|
(`extensions/*`) — neither by package name nor by relative path. The rule is
|
||||||
|
enforced by `tests/architecture.test.ts`, which discovers extension packages
|
||||||
|
from the filesystem, so new extensions are guarded automatically. It runs as
|
||||||
|
part of `pnpm test` and CI.
|
||||||
|
|
||||||
|
## Local (non-container) developer path
|
||||||
|
|
||||||
|
No Docker required for workspace work; containers arrive in E00-S02.
|
||||||
|
|
||||||
|
1. Install Node.js 24.x (e.g. via nvm: `nvm install 24`).
|
||||||
|
2. Enable the pinned package manager:
|
||||||
|
`corepack enable` (reads `packageManager: pnpm@11.23.0`), or
|
||||||
|
`npm install -g pnpm@11.23.0`.
|
||||||
|
3. Install dependencies (frozen lockfile):
|
||||||
|
`pnpm install --frozen-lockfile`
|
||||||
|
4. Verify the workspace:
|
||||||
|
|
||||||
|
| Command | Runs |
|
||||||
|
| --------------- | --------------------------------------------------- |
|
||||||
|
| `pnpm build` | compiles every workspace package |
|
||||||
|
| `pnpm test` | runs the test suite (Vitest, incl. FIT-001) |
|
||||||
|
| `pnpm typecheck`| typechecks root tests and every workspace package |
|
||||||
|
|
||||||
|
## Out of scope for this story
|
||||||
|
|
||||||
|
- Docker Compose baseline and container smoke tests — E00-S02
|
||||||
|
- PostgreSQL adapter and migration runner — E00-S03
|
||||||
|
- CI pipeline stages beyond the root scripts — E00-S05
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"name": "@personal-blog/server",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"description": "EPPP public server application. Bootstrap placeholder — the Fastify 5 application shell lands in a later story.",
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc -p tsconfig.json",
|
||||||
|
"typecheck": "tsc -p tsconfig.json --noEmit"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
/**
|
||||||
|
* @personal-blog/server — EPPP public server application.
|
||||||
|
*
|
||||||
|
* Bootstrap placeholder so apps/ is a real workspace and root build/typecheck
|
||||||
|
* cover it. The Fastify 5 application shell lands in a later story.
|
||||||
|
*/
|
||||||
|
export const APP_NAME = 'eppp-server';
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../tsconfig.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"rootDir": "src",
|
||||||
|
"outDir": "dist"
|
||||||
|
},
|
||||||
|
"include": ["src"]
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"name": "@personal-blog/example-extension",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"description": "Placeholder extension that exercises the extensions/ workspace group and the FIT-001 dependency boundary. Real extensions (e.g. theme-amber) replace it in later stories.",
|
||||||
|
"main": "./dist/index.js",
|
||||||
|
"types": "./dist/index.d.ts",
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"types": "./dist/index.d.ts",
|
||||||
|
"default": "./dist/index.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"dist"
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc -p tsconfig.json",
|
||||||
|
"typecheck": "tsc -p tsconfig.json --noEmit"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
/**
|
||||||
|
* @personal-blog/example-extension — bootstrap placeholder extension.
|
||||||
|
*
|
||||||
|
* FIT-001: core packages (packages/*) must never import a concrete extension
|
||||||
|
* like this one. Enforced by tests/architecture.test.ts in CI.
|
||||||
|
*/
|
||||||
|
export const EXAMPLE_EXTENSION_ID = 'example';
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../tsconfig.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"rootDir": "src",
|
||||||
|
"outDir": "dist"
|
||||||
|
},
|
||||||
|
"include": ["src"]
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"name": "personal-blog",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"description": "EPPP — a modular personal blogging platform. Workspace bootstrap (E00-S01).",
|
||||||
|
"packageManager": "pnpm@11.23.0",
|
||||||
|
"engines": {
|
||||||
|
"node": "24.x"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "pnpm -r --if-present run build",
|
||||||
|
"test": "vitest run",
|
||||||
|
"typecheck": "tsc -p tsconfig.json && pnpm -r --if-present run typecheck"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "24.13.3",
|
||||||
|
"typescript": "6.0.3",
|
||||||
|
"vitest": "4.1.10"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"name": "@personal-blog/core",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"description": "EPPP application core. Bootstrap placeholder — owns application lifecycle, site identity and content primitives in later stories.",
|
||||||
|
"main": "./dist/index.js",
|
||||||
|
"types": "./dist/index.d.ts",
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"types": "./dist/index.d.ts",
|
||||||
|
"default": "./dist/index.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"dist"
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc -p tsconfig.json",
|
||||||
|
"typecheck": "tsc -p tsconfig.json --noEmit"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
/**
|
||||||
|
* @personal-blog/core — EPPP application core.
|
||||||
|
*
|
||||||
|
* Bootstrap placeholder. Later EPPP stories replace this module with the real
|
||||||
|
* application core (site identity, content primitives, extension runtime).
|
||||||
|
*
|
||||||
|
* FIT-001: code in this package must never import a concrete extension.
|
||||||
|
* Enforced by tests/architecture.test.ts in CI.
|
||||||
|
*/
|
||||||
|
export const CORE_VERSION = '0.0.0';
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../tsconfig.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"rootDir": "src",
|
||||||
|
"outDir": "dist"
|
||||||
|
},
|
||||||
|
"include": ["src"]
|
||||||
|
}
|
||||||
Generated
+733
@@ -0,0 +1,733 @@
|
|||||||
|
lockfileVersion: '9.0'
|
||||||
|
|
||||||
|
settings:
|
||||||
|
autoInstallPeers: true
|
||||||
|
excludeLinksFromLockfile: false
|
||||||
|
|
||||||
|
importers:
|
||||||
|
|
||||||
|
.:
|
||||||
|
devDependencies:
|
||||||
|
'@types/node':
|
||||||
|
specifier: 24.13.3
|
||||||
|
version: 24.13.3
|
||||||
|
typescript:
|
||||||
|
specifier: 6.0.3
|
||||||
|
version: 6.0.3
|
||||||
|
vitest:
|
||||||
|
specifier: 4.1.10
|
||||||
|
version: 4.1.10(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3))
|
||||||
|
|
||||||
|
apps/server: {}
|
||||||
|
|
||||||
|
extensions/example: {}
|
||||||
|
|
||||||
|
packages/core: {}
|
||||||
|
|
||||||
|
packages:
|
||||||
|
|
||||||
|
'@jridgewell/sourcemap-codec@1.5.5':
|
||||||
|
resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
|
||||||
|
|
||||||
|
'@oxc-project/types@0.147.0':
|
||||||
|
resolution: {integrity: sha512-IJ3s6ltHLp45S0bh7phkX+gJO7A1Wuz2EaqpAhb8WjqDwbzMiWKHhyyT42tskaWjEYXtHtVCPpnBJVT9+dcRLg==}
|
||||||
|
|
||||||
|
'@rolldown/binding-android-arm-eabi@1.2.6':
|
||||||
|
resolution: {integrity: sha512-b+jTcARdTiFLI6jB4a5XjTm0RWd6KcRfQj/I2356fxUZemiho9zQLxo0RtCuMDAyKcLo6cEltkgbQp6d1+sjjQ==}
|
||||||
|
engines: {node: ^20.19.0 || >=22.12.0}
|
||||||
|
cpu: [arm]
|
||||||
|
os: [android]
|
||||||
|
|
||||||
|
'@rolldown/binding-android-arm64@1.2.6':
|
||||||
|
resolution: {integrity: sha512-lkWU8ZJaRk9q3CIEY1Tc7vIFALp3Xw5NfGJo2hQg5oIqNgxWi1zI+IiDEK3r70BF5Dzol1tcXsnzsRc8NLhG+Q==}
|
||||||
|
engines: {node: ^20.19.0 || >=22.12.0}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [android]
|
||||||
|
|
||||||
|
'@rolldown/binding-darwin-arm64@1.2.6':
|
||||||
|
resolution: {integrity: sha512-dgR56NYnvAszm7Ob1B2/Vn0e8bUQYZH2UjVaMMtMVOCKFSfjhfLmuA/9+O+F+ajUdG6B/bSssrKW6JJYASa8jA==}
|
||||||
|
engines: {node: ^20.19.0 || >=22.12.0}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [darwin]
|
||||||
|
|
||||||
|
'@rolldown/binding-darwin-x64@1.2.6':
|
||||||
|
resolution: {integrity: sha512-vpVxFvUCFioJqug7OTvqptkc4yb8UX0AwfDmJpaR/0sWz+BUmqSVAf7c8JkUgnN8YLspb4a/N6NhTyMAmdyQ7Q==}
|
||||||
|
engines: {node: ^20.19.0 || >=22.12.0}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [darwin]
|
||||||
|
|
||||||
|
'@rolldown/binding-freebsd-x64@1.2.6':
|
||||||
|
resolution: {integrity: sha512-h1wG6Y6K3JlRswxsI64qQJqBAy4vrLuHgRbc8CZMGSWTOFRY6ghMApM1NKzB2I0n5xV1fjkE18SuVl2QpLeNpA==}
|
||||||
|
engines: {node: ^20.19.0 || >=22.12.0}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [freebsd]
|
||||||
|
|
||||||
|
'@rolldown/binding-linux-arm-gnueabihf@1.2.6':
|
||||||
|
resolution: {integrity: sha512-tbCiqub0q2MVWJKgF5PoAlNWCtQydiOYSLIkd8sByqK/6MMYLJRcSXSYodqYtd0O+Fw7QaVmKKlS4oL94YRZ0w==}
|
||||||
|
engines: {node: ^20.19.0 || >=22.12.0}
|
||||||
|
cpu: [arm]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@rolldown/binding-linux-arm64-gnu@1.2.6':
|
||||||
|
resolution: {integrity: sha512-oxK9+baEBPhZG5HB4URY+uU04zJWeZlH6Tb9rB5DK4DF9XR1uXNLXt5Q5ZsugTKayNCNLhkcwz/ye74hRI98dg==}
|
||||||
|
engines: {node: ^20.19.0 || >=22.12.0}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
|
'@rolldown/binding-linux-arm64-musl@1.2.6':
|
||||||
|
resolution: {integrity: sha512-muWCk27FVBEZtv0MsK8gnfSmgczA8KQ0uRVJbTABKhkRfQc38aUrcb7fhi3BNiyseFmgcRsoMfQsSNJ+DbZdSw==}
|
||||||
|
engines: {node: ^20.19.0 || >=22.12.0}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [linux]
|
||||||
|
libc: [musl]
|
||||||
|
|
||||||
|
'@rolldown/binding-linux-ppc64-gnu@1.2.6':
|
||||||
|
resolution: {integrity: sha512-eWDoSfU7Co2qj3vgB3Dt4lj1mG6CoWbcJQkRMP3XJplyCMtuaq3LHvPFjS9QIPvMGWVadJC04Xiy0IdcVPtnwQ==}
|
||||||
|
engines: {node: ^20.19.0 || >=22.12.0}
|
||||||
|
cpu: [ppc64]
|
||||||
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
|
'@rolldown/binding-linux-s390x-gnu@1.2.6':
|
||||||
|
resolution: {integrity: sha512-2bWNjRSIayvupRKxXUY2tWG9fYdoUlTqWywHRvE8Eq3GvuQ+f2HeIkve697fIt+IQs/PV8yFsdWuhp1aJ1PdnA==}
|
||||||
|
engines: {node: ^20.19.0 || >=22.12.0}
|
||||||
|
cpu: [s390x]
|
||||||
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
|
'@rolldown/binding-linux-x64-gnu@1.2.6':
|
||||||
|
resolution: {integrity: sha512-KekI0gS0wLxe1UBSQSjenBVwou/JkcQPDzBPICGZjxUv9k3RteHDPBQaiOicZUFKRIH2wKEimGwVpnJsbPzu7w==}
|
||||||
|
engines: {node: ^20.19.0 || >=22.12.0}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
|
'@rolldown/binding-linux-x64-musl@1.2.6':
|
||||||
|
resolution: {integrity: sha512-TvtPnfVr+HtyGiDmPK4VWmlNm7QhNNAcK5Q9A7aOXsI8545yCyaoMaicXrFZ72JzeYjaUVk7yT243zT0jzjFKQ==}
|
||||||
|
engines: {node: ^20.19.0 || >=22.12.0}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [linux]
|
||||||
|
libc: [musl]
|
||||||
|
|
||||||
|
'@rolldown/binding-openharmony-arm64@1.2.6':
|
||||||
|
resolution: {integrity: sha512-iOo0VEay2XFhaCcH0sps5XIimkSuOnNaZrf6+ZkoSOQBJPKNU48RkmJv0/lSpipexu5P+ouFgafe5IGr/DiQfg==}
|
||||||
|
engines: {node: ^20.19.0 || >=22.12.0}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [openharmony]
|
||||||
|
|
||||||
|
'@rolldown/binding-win32-arm64-msvc@1.2.6':
|
||||||
|
resolution: {integrity: sha512-y5NTmmasMS455JlOCO4ZM9krIchv3Mvm1crL1iUPGOPgEzSkves9n0SdC5Sjz6+qWDFhd8/JpfWMH8NSWNHe+A==}
|
||||||
|
engines: {node: ^20.19.0 || >=22.12.0}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [win32]
|
||||||
|
|
||||||
|
'@rolldown/binding-win32-x64-msvc@1.2.6':
|
||||||
|
resolution: {integrity: sha512-np8iZSLfXlAD4kWhiyq/u0Yt8oZDtRQ8lGhQaCXo2rl37KNjeU0GjJuwr4P3oeZ++ROfofsKNBqR5LTO8aXyWQ==}
|
||||||
|
engines: {node: ^20.19.0 || >=22.12.0}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [win32]
|
||||||
|
|
||||||
|
'@rolldown/pluginutils@1.0.1':
|
||||||
|
resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==}
|
||||||
|
|
||||||
|
'@standard-schema/spec@1.1.0':
|
||||||
|
resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==}
|
||||||
|
|
||||||
|
'@types/chai@5.2.3':
|
||||||
|
resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==}
|
||||||
|
|
||||||
|
'@types/deep-eql@4.0.2':
|
||||||
|
resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==}
|
||||||
|
|
||||||
|
'@types/estree@1.0.9':
|
||||||
|
resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==}
|
||||||
|
|
||||||
|
'@types/node@24.13.3':
|
||||||
|
resolution: {integrity: sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==}
|
||||||
|
|
||||||
|
'@vitest/expect@4.1.10':
|
||||||
|
resolution: {integrity: sha512-YsCn+qAk1GWjQOWFEsEcL2gNQ0zmVmQu3T03qP6UyjhtmdtwtbuI+DASn/7iQB3HGTXkdBwGddzxPlmiql5vlA==}
|
||||||
|
|
||||||
|
'@vitest/mocker@4.1.10':
|
||||||
|
resolution: {integrity: sha512-v0xaezt+DKEmKfaxg133ldzADrwLGd7Ze1MfQQTYfvs8OqZIwbxyxaYURivwV7sWy5fqn3rH5uOrSp07bp44Ow==}
|
||||||
|
peerDependencies:
|
||||||
|
msw: ^2.4.9
|
||||||
|
vite: ^6.0.0 || ^7.0.0 || ^8.0.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
msw:
|
||||||
|
optional: true
|
||||||
|
vite:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@vitest/pretty-format@4.1.10':
|
||||||
|
resolution: {integrity: sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q==}
|
||||||
|
|
||||||
|
'@vitest/runner@4.1.10':
|
||||||
|
resolution: {integrity: sha512-IKI6kpIH+LmpROplyLwBBaCfMgOZOMsygVa6BARD6ahA04VRuJSa6OaVG7kRvSEMD870Vd91rSSw0eegtWyLGg==}
|
||||||
|
|
||||||
|
'@vitest/snapshot@4.1.10':
|
||||||
|
resolution: {integrity: sha512-xRkfOT1qpTAi/Ti4Y1LtfRc3kEuqxGw59eN2jN9pRWMtS/XDevekhcFSqvQqjUNGksfjMJu3Y+oJ+4Ypn2OaJw==}
|
||||||
|
|
||||||
|
'@vitest/spy@4.1.10':
|
||||||
|
resolution: {integrity: sha512-PLf/Ugvoq5wO/b4rwYCR1h2PSIdXz7wnkQFMiUpLdtM7l6pqVFcQIBEHyT1+l+cj7mNwAfZHzqXqDyjvOuwbDw==}
|
||||||
|
|
||||||
|
'@vitest/utils@4.1.10':
|
||||||
|
resolution: {integrity: sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==}
|
||||||
|
|
||||||
|
assertion-error@2.0.1:
|
||||||
|
resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
|
chai@6.2.2:
|
||||||
|
resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
convert-source-map@2.0.0:
|
||||||
|
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
|
||||||
|
|
||||||
|
detect-libc@2.1.2:
|
||||||
|
resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
es-module-lexer@2.3.2:
|
||||||
|
resolution: {integrity: sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==}
|
||||||
|
|
||||||
|
estree-walker@3.0.3:
|
||||||
|
resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
|
||||||
|
|
||||||
|
expect-type@1.4.0:
|
||||||
|
resolution: {integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==}
|
||||||
|
engines: {node: '>=12.0.0'}
|
||||||
|
|
||||||
|
fdir@6.5.0:
|
||||||
|
resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
|
||||||
|
engines: {node: '>=12.0.0'}
|
||||||
|
peerDependencies:
|
||||||
|
picomatch: ^3 || ^4
|
||||||
|
peerDependenciesMeta:
|
||||||
|
picomatch:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
fsevents@2.3.3:
|
||||||
|
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
|
||||||
|
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
||||||
|
os: [darwin]
|
||||||
|
|
||||||
|
lightningcss-android-arm64@1.33.0:
|
||||||
|
resolution: {integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==}
|
||||||
|
engines: {node: '>= 12.0.0'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [android]
|
||||||
|
|
||||||
|
lightningcss-darwin-arm64@1.33.0:
|
||||||
|
resolution: {integrity: sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==}
|
||||||
|
engines: {node: '>= 12.0.0'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [darwin]
|
||||||
|
|
||||||
|
lightningcss-darwin-x64@1.33.0:
|
||||||
|
resolution: {integrity: sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==}
|
||||||
|
engines: {node: '>= 12.0.0'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [darwin]
|
||||||
|
|
||||||
|
lightningcss-freebsd-x64@1.33.0:
|
||||||
|
resolution: {integrity: sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==}
|
||||||
|
engines: {node: '>= 12.0.0'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [freebsd]
|
||||||
|
|
||||||
|
lightningcss-linux-arm-gnueabihf@1.33.0:
|
||||||
|
resolution: {integrity: sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==}
|
||||||
|
engines: {node: '>= 12.0.0'}
|
||||||
|
cpu: [arm]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
lightningcss-linux-arm64-gnu@1.33.0:
|
||||||
|
resolution: {integrity: sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==}
|
||||||
|
engines: {node: '>= 12.0.0'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
|
lightningcss-linux-arm64-musl@1.33.0:
|
||||||
|
resolution: {integrity: sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==}
|
||||||
|
engines: {node: '>= 12.0.0'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [linux]
|
||||||
|
libc: [musl]
|
||||||
|
|
||||||
|
lightningcss-linux-x64-gnu@1.33.0:
|
||||||
|
resolution: {integrity: sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==}
|
||||||
|
engines: {node: '>= 12.0.0'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
|
lightningcss-linux-x64-musl@1.33.0:
|
||||||
|
resolution: {integrity: sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==}
|
||||||
|
engines: {node: '>= 12.0.0'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [linux]
|
||||||
|
libc: [musl]
|
||||||
|
|
||||||
|
lightningcss-win32-arm64-msvc@1.33.0:
|
||||||
|
resolution: {integrity: sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==}
|
||||||
|
engines: {node: '>= 12.0.0'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [win32]
|
||||||
|
|
||||||
|
lightningcss-win32-x64-msvc@1.33.0:
|
||||||
|
resolution: {integrity: sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==}
|
||||||
|
engines: {node: '>= 12.0.0'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [win32]
|
||||||
|
|
||||||
|
lightningcss@1.33.0:
|
||||||
|
resolution: {integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==}
|
||||||
|
engines: {node: '>= 12.0.0'}
|
||||||
|
|
||||||
|
magic-string@0.30.21:
|
||||||
|
resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
|
||||||
|
|
||||||
|
nanoid@3.3.18:
|
||||||
|
resolution: {integrity: sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==}
|
||||||
|
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
obug@2.1.4:
|
||||||
|
resolution: {integrity: sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==}
|
||||||
|
engines: {node: '>=12.20.0'}
|
||||||
|
|
||||||
|
pathe@2.0.3:
|
||||||
|
resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
|
||||||
|
|
||||||
|
picocolors@1.1.1:
|
||||||
|
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
|
||||||
|
|
||||||
|
picomatch@4.0.7:
|
||||||
|
resolution: {integrity: sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
|
postcss@8.5.26:
|
||||||
|
resolution: {integrity: sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==}
|
||||||
|
engines: {node: ^10 || ^12 || >=14}
|
||||||
|
|
||||||
|
rolldown@1.2.6:
|
||||||
|
resolution: {integrity: sha512-vMM4q3aixf46GiF1Kok8jDPFsEpXgFWGjUHXNkNHNm+Y2adXAG2dbX91jkti3i0ZRsOlcmbuzAz1poObSHCmUA==}
|
||||||
|
engines: {node: ^20.19.0 || >=22.12.0}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
siginfo@2.0.0:
|
||||||
|
resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==}
|
||||||
|
|
||||||
|
source-map-js@1.2.1:
|
||||||
|
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
stackback@0.0.2:
|
||||||
|
resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==}
|
||||||
|
|
||||||
|
std-env@4.2.0:
|
||||||
|
resolution: {integrity: sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==}
|
||||||
|
|
||||||
|
tinybench@2.9.0:
|
||||||
|
resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==}
|
||||||
|
|
||||||
|
tinyexec@1.3.0:
|
||||||
|
resolution: {integrity: sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
tinyglobby@0.2.17:
|
||||||
|
resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==}
|
||||||
|
engines: {node: '>=12.0.0'}
|
||||||
|
|
||||||
|
tinyrainbow@3.1.1:
|
||||||
|
resolution: {integrity: sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw==}
|
||||||
|
engines: {node: '>=14.0.0'}
|
||||||
|
|
||||||
|
typescript@6.0.3:
|
||||||
|
resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==}
|
||||||
|
engines: {node: '>=14.17'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
undici-types@7.18.2:
|
||||||
|
resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==}
|
||||||
|
|
||||||
|
vite@8.2.2:
|
||||||
|
resolution: {integrity: sha512-cFKLV/PRgAUlIRm5WjMjJ86jrftzpqcgH+Us+DS8mI3CDNiH30Whrz8uHL3+MOLPAgqbMBAqWdAHAphOAM+z/Q==}
|
||||||
|
engines: {node: ^20.19.0 || >=22.12.0}
|
||||||
|
hasBin: true
|
||||||
|
peerDependencies:
|
||||||
|
'@types/node': ^20.19.0 || >=22.12.0
|
||||||
|
'@vitejs/devtools': ^0.4.0 || ^0.5.0
|
||||||
|
esbuild: ^0.27.0 || ^0.28.0
|
||||||
|
jiti: '>=1.21.0'
|
||||||
|
less: ^4.0.0
|
||||||
|
sass: ^1.70.0
|
||||||
|
sass-embedded: ^1.70.0
|
||||||
|
stylus: '>=0.54.8'
|
||||||
|
sugarss: ^5.0.0
|
||||||
|
terser: ^5.16.0
|
||||||
|
tsx: ^4.8.1
|
||||||
|
yaml: ^2.4.2
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/node':
|
||||||
|
optional: true
|
||||||
|
'@vitejs/devtools':
|
||||||
|
optional: true
|
||||||
|
esbuild:
|
||||||
|
optional: true
|
||||||
|
jiti:
|
||||||
|
optional: true
|
||||||
|
less:
|
||||||
|
optional: true
|
||||||
|
sass:
|
||||||
|
optional: true
|
||||||
|
sass-embedded:
|
||||||
|
optional: true
|
||||||
|
stylus:
|
||||||
|
optional: true
|
||||||
|
sugarss:
|
||||||
|
optional: true
|
||||||
|
terser:
|
||||||
|
optional: true
|
||||||
|
tsx:
|
||||||
|
optional: true
|
||||||
|
yaml:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
vitest@4.1.10:
|
||||||
|
resolution: {integrity: sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw==}
|
||||||
|
engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0}
|
||||||
|
hasBin: true
|
||||||
|
peerDependencies:
|
||||||
|
'@edge-runtime/vm': '*'
|
||||||
|
'@opentelemetry/api': ^1.9.0
|
||||||
|
'@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0
|
||||||
|
'@vitest/browser-playwright': 4.1.10
|
||||||
|
'@vitest/browser-preview': 4.1.10
|
||||||
|
'@vitest/browser-webdriverio': 4.1.10
|
||||||
|
'@vitest/coverage-istanbul': 4.1.10
|
||||||
|
'@vitest/coverage-v8': 4.1.10
|
||||||
|
'@vitest/ui': 4.1.10
|
||||||
|
happy-dom: '*'
|
||||||
|
jsdom: '*'
|
||||||
|
vite: ^6.0.0 || ^7.0.0 || ^8.0.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@edge-runtime/vm':
|
||||||
|
optional: true
|
||||||
|
'@opentelemetry/api':
|
||||||
|
optional: true
|
||||||
|
'@types/node':
|
||||||
|
optional: true
|
||||||
|
'@vitest/browser-playwright':
|
||||||
|
optional: true
|
||||||
|
'@vitest/browser-preview':
|
||||||
|
optional: true
|
||||||
|
'@vitest/browser-webdriverio':
|
||||||
|
optional: true
|
||||||
|
'@vitest/coverage-istanbul':
|
||||||
|
optional: true
|
||||||
|
'@vitest/coverage-v8':
|
||||||
|
optional: true
|
||||||
|
'@vitest/ui':
|
||||||
|
optional: true
|
||||||
|
happy-dom:
|
||||||
|
optional: true
|
||||||
|
jsdom:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
why-is-node-running@2.3.0:
|
||||||
|
resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
snapshots:
|
||||||
|
|
||||||
|
'@jridgewell/sourcemap-codec@1.5.5': {}
|
||||||
|
|
||||||
|
'@oxc-project/types@0.147.0': {}
|
||||||
|
|
||||||
|
'@rolldown/binding-android-arm-eabi@1.2.6':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rolldown/binding-android-arm64@1.2.6':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rolldown/binding-darwin-arm64@1.2.6':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rolldown/binding-darwin-x64@1.2.6':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rolldown/binding-freebsd-x64@1.2.6':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rolldown/binding-linux-arm-gnueabihf@1.2.6':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rolldown/binding-linux-arm64-gnu@1.2.6':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rolldown/binding-linux-arm64-musl@1.2.6':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rolldown/binding-linux-ppc64-gnu@1.2.6':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rolldown/binding-linux-s390x-gnu@1.2.6':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rolldown/binding-linux-x64-gnu@1.2.6':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rolldown/binding-linux-x64-musl@1.2.6':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rolldown/binding-openharmony-arm64@1.2.6':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rolldown/binding-win32-arm64-msvc@1.2.6':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rolldown/binding-win32-x64-msvc@1.2.6':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rolldown/pluginutils@1.0.1': {}
|
||||||
|
|
||||||
|
'@standard-schema/spec@1.1.0': {}
|
||||||
|
|
||||||
|
'@types/chai@5.2.3':
|
||||||
|
dependencies:
|
||||||
|
'@types/deep-eql': 4.0.2
|
||||||
|
assertion-error: 2.0.1
|
||||||
|
|
||||||
|
'@types/deep-eql@4.0.2': {}
|
||||||
|
|
||||||
|
'@types/estree@1.0.9': {}
|
||||||
|
|
||||||
|
'@types/node@24.13.3':
|
||||||
|
dependencies:
|
||||||
|
undici-types: 7.18.2
|
||||||
|
|
||||||
|
'@vitest/expect@4.1.10':
|
||||||
|
dependencies:
|
||||||
|
'@standard-schema/spec': 1.1.0
|
||||||
|
'@types/chai': 5.2.3
|
||||||
|
'@vitest/spy': 4.1.10
|
||||||
|
'@vitest/utils': 4.1.10
|
||||||
|
chai: 6.2.2
|
||||||
|
tinyrainbow: 3.1.1
|
||||||
|
|
||||||
|
'@vitest/mocker@4.1.10(vite@8.2.2(@types/node@24.13.3))':
|
||||||
|
dependencies:
|
||||||
|
'@vitest/spy': 4.1.10
|
||||||
|
estree-walker: 3.0.3
|
||||||
|
magic-string: 0.30.21
|
||||||
|
optionalDependencies:
|
||||||
|
vite: 8.2.2(@types/node@24.13.3)
|
||||||
|
|
||||||
|
'@vitest/pretty-format@4.1.10':
|
||||||
|
dependencies:
|
||||||
|
tinyrainbow: 3.1.1
|
||||||
|
|
||||||
|
'@vitest/runner@4.1.10':
|
||||||
|
dependencies:
|
||||||
|
'@vitest/utils': 4.1.10
|
||||||
|
pathe: 2.0.3
|
||||||
|
|
||||||
|
'@vitest/snapshot@4.1.10':
|
||||||
|
dependencies:
|
||||||
|
'@vitest/pretty-format': 4.1.10
|
||||||
|
'@vitest/utils': 4.1.10
|
||||||
|
magic-string: 0.30.21
|
||||||
|
pathe: 2.0.3
|
||||||
|
|
||||||
|
'@vitest/spy@4.1.10': {}
|
||||||
|
|
||||||
|
'@vitest/utils@4.1.10':
|
||||||
|
dependencies:
|
||||||
|
'@vitest/pretty-format': 4.1.10
|
||||||
|
convert-source-map: 2.0.0
|
||||||
|
tinyrainbow: 3.1.1
|
||||||
|
|
||||||
|
assertion-error@2.0.1: {}
|
||||||
|
|
||||||
|
chai@6.2.2: {}
|
||||||
|
|
||||||
|
convert-source-map@2.0.0: {}
|
||||||
|
|
||||||
|
detect-libc@2.1.2: {}
|
||||||
|
|
||||||
|
es-module-lexer@2.3.2: {}
|
||||||
|
|
||||||
|
estree-walker@3.0.3:
|
||||||
|
dependencies:
|
||||||
|
'@types/estree': 1.0.9
|
||||||
|
|
||||||
|
expect-type@1.4.0: {}
|
||||||
|
|
||||||
|
fdir@6.5.0(picomatch@4.0.7):
|
||||||
|
optionalDependencies:
|
||||||
|
picomatch: 4.0.7
|
||||||
|
|
||||||
|
fsevents@2.3.3:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
lightningcss-android-arm64@1.33.0:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
lightningcss-darwin-arm64@1.33.0:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
lightningcss-darwin-x64@1.33.0:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
lightningcss-freebsd-x64@1.33.0:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
lightningcss-linux-arm-gnueabihf@1.33.0:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
lightningcss-linux-arm64-gnu@1.33.0:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
lightningcss-linux-arm64-musl@1.33.0:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
lightningcss-linux-x64-gnu@1.33.0:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
lightningcss-linux-x64-musl@1.33.0:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
lightningcss-win32-arm64-msvc@1.33.0:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
lightningcss-win32-x64-msvc@1.33.0:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
lightningcss@1.33.0:
|
||||||
|
dependencies:
|
||||||
|
detect-libc: 2.1.2
|
||||||
|
optionalDependencies:
|
||||||
|
lightningcss-android-arm64: 1.33.0
|
||||||
|
lightningcss-darwin-arm64: 1.33.0
|
||||||
|
lightningcss-darwin-x64: 1.33.0
|
||||||
|
lightningcss-freebsd-x64: 1.33.0
|
||||||
|
lightningcss-linux-arm-gnueabihf: 1.33.0
|
||||||
|
lightningcss-linux-arm64-gnu: 1.33.0
|
||||||
|
lightningcss-linux-arm64-musl: 1.33.0
|
||||||
|
lightningcss-linux-x64-gnu: 1.33.0
|
||||||
|
lightningcss-linux-x64-musl: 1.33.0
|
||||||
|
lightningcss-win32-arm64-msvc: 1.33.0
|
||||||
|
lightningcss-win32-x64-msvc: 1.33.0
|
||||||
|
|
||||||
|
magic-string@0.30.21:
|
||||||
|
dependencies:
|
||||||
|
'@jridgewell/sourcemap-codec': 1.5.5
|
||||||
|
|
||||||
|
nanoid@3.3.18: {}
|
||||||
|
|
||||||
|
obug@2.1.4: {}
|
||||||
|
|
||||||
|
pathe@2.0.3: {}
|
||||||
|
|
||||||
|
picocolors@1.1.1: {}
|
||||||
|
|
||||||
|
picomatch@4.0.7: {}
|
||||||
|
|
||||||
|
postcss@8.5.26:
|
||||||
|
dependencies:
|
||||||
|
nanoid: 3.3.18
|
||||||
|
picocolors: 1.1.1
|
||||||
|
source-map-js: 1.2.1
|
||||||
|
|
||||||
|
rolldown@1.2.6:
|
||||||
|
dependencies:
|
||||||
|
'@oxc-project/types': 0.147.0
|
||||||
|
'@rolldown/pluginutils': 1.0.1
|
||||||
|
optionalDependencies:
|
||||||
|
'@rolldown/binding-android-arm-eabi': 1.2.6
|
||||||
|
'@rolldown/binding-android-arm64': 1.2.6
|
||||||
|
'@rolldown/binding-darwin-arm64': 1.2.6
|
||||||
|
'@rolldown/binding-darwin-x64': 1.2.6
|
||||||
|
'@rolldown/binding-freebsd-x64': 1.2.6
|
||||||
|
'@rolldown/binding-linux-arm-gnueabihf': 1.2.6
|
||||||
|
'@rolldown/binding-linux-arm64-gnu': 1.2.6
|
||||||
|
'@rolldown/binding-linux-arm64-musl': 1.2.6
|
||||||
|
'@rolldown/binding-linux-ppc64-gnu': 1.2.6
|
||||||
|
'@rolldown/binding-linux-s390x-gnu': 1.2.6
|
||||||
|
'@rolldown/binding-linux-x64-gnu': 1.2.6
|
||||||
|
'@rolldown/binding-linux-x64-musl': 1.2.6
|
||||||
|
'@rolldown/binding-openharmony-arm64': 1.2.6
|
||||||
|
'@rolldown/binding-win32-arm64-msvc': 1.2.6
|
||||||
|
'@rolldown/binding-win32-x64-msvc': 1.2.6
|
||||||
|
|
||||||
|
siginfo@2.0.0: {}
|
||||||
|
|
||||||
|
source-map-js@1.2.1: {}
|
||||||
|
|
||||||
|
stackback@0.0.2: {}
|
||||||
|
|
||||||
|
std-env@4.2.0: {}
|
||||||
|
|
||||||
|
tinybench@2.9.0: {}
|
||||||
|
|
||||||
|
tinyexec@1.3.0: {}
|
||||||
|
|
||||||
|
tinyglobby@0.2.17:
|
||||||
|
dependencies:
|
||||||
|
fdir: 6.5.0(picomatch@4.0.7)
|
||||||
|
picomatch: 4.0.7
|
||||||
|
|
||||||
|
tinyrainbow@3.1.1: {}
|
||||||
|
|
||||||
|
typescript@6.0.3: {}
|
||||||
|
|
||||||
|
undici-types@7.18.2: {}
|
||||||
|
|
||||||
|
vite@8.2.2(@types/node@24.13.3):
|
||||||
|
dependencies:
|
||||||
|
lightningcss: 1.33.0
|
||||||
|
picomatch: 4.0.7
|
||||||
|
postcss: 8.5.26
|
||||||
|
rolldown: 1.2.6
|
||||||
|
tinyglobby: 0.2.17
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/node': 24.13.3
|
||||||
|
fsevents: 2.3.3
|
||||||
|
|
||||||
|
vitest@4.1.10(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)):
|
||||||
|
dependencies:
|
||||||
|
'@vitest/expect': 4.1.10
|
||||||
|
'@vitest/mocker': 4.1.10(vite@8.2.2(@types/node@24.13.3))
|
||||||
|
'@vitest/pretty-format': 4.1.10
|
||||||
|
'@vitest/runner': 4.1.10
|
||||||
|
'@vitest/snapshot': 4.1.10
|
||||||
|
'@vitest/spy': 4.1.10
|
||||||
|
'@vitest/utils': 4.1.10
|
||||||
|
es-module-lexer: 2.3.2
|
||||||
|
expect-type: 1.4.0
|
||||||
|
magic-string: 0.30.21
|
||||||
|
obug: 2.1.4
|
||||||
|
pathe: 2.0.3
|
||||||
|
picomatch: 4.0.7
|
||||||
|
std-env: 4.2.0
|
||||||
|
tinybench: 2.9.0
|
||||||
|
tinyexec: 1.3.0
|
||||||
|
tinyglobby: 0.2.17
|
||||||
|
tinyrainbow: 3.1.1
|
||||||
|
vite: 8.2.2(@types/node@24.13.3)
|
||||||
|
why-is-node-running: 2.3.0
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/node': 24.13.3
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- msw
|
||||||
|
|
||||||
|
why-is-node-running@2.3.0:
|
||||||
|
dependencies:
|
||||||
|
siginfo: 2.0.0
|
||||||
|
stackback: 0.0.2
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
packages:
|
||||||
|
- apps/*
|
||||||
|
- packages/*
|
||||||
|
- extensions/*
|
||||||
@@ -0,0 +1,145 @@
|
|||||||
|
/**
|
||||||
|
* FIT-001: no core package imports a concrete extension.
|
||||||
|
*
|
||||||
|
* Architecture guard (CI-enforced, see .gitea/workflows/ci.yml):
|
||||||
|
* - a "concrete extension" is any workspace package under extensions/
|
||||||
|
* - code under packages/ (the core packages) must never import one, neither
|
||||||
|
* by package name nor by a relative path that resolves into extensions/
|
||||||
|
*
|
||||||
|
* The rule is discovered from the filesystem, so adding an extension package
|
||||||
|
* under extensions/ automatically extends the guard; no manifest edits needed.
|
||||||
|
*/
|
||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
import { readdirSync, readFileSync, existsSync } from 'node:fs';
|
||||||
|
import { dirname, isAbsolute, join, relative, resolve } from 'node:path';
|
||||||
|
import { fileURLToPath } from 'node:url';
|
||||||
|
|
||||||
|
const repoRoot = resolve(fileURLToPath(new URL('..', import.meta.url)));
|
||||||
|
const packagesRoot = join(repoRoot, 'packages');
|
||||||
|
const extensionsRoot = join(repoRoot, 'extensions');
|
||||||
|
|
||||||
|
interface ExtensionInfo {
|
||||||
|
/** Absolute path of the extension package directory. */
|
||||||
|
dir: string;
|
||||||
|
/** Package name from its manifest (e.g. "@personal-blog/example-extension"). */
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** True when `candidate` is `parent` itself or lies inside it. */
|
||||||
|
function isWithin(parent: string, candidate: string): boolean {
|
||||||
|
const rel = relative(parent, candidate);
|
||||||
|
return rel === '' || (!rel.startsWith('..') && !isAbsolute(rel));
|
||||||
|
}
|
||||||
|
|
||||||
|
function findExtensionPackages(): ExtensionInfo[] {
|
||||||
|
if (!existsSync(extensionsRoot)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return readdirSync(extensionsRoot, { withFileTypes: true })
|
||||||
|
.filter((entry) => entry.isDirectory())
|
||||||
|
.map((entry): ExtensionInfo | null => {
|
||||||
|
const manifestPath = join(extensionsRoot, entry.name, 'package.json');
|
||||||
|
if (!existsSync(manifestPath)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const manifest = JSON.parse(readFileSync(manifestPath, 'utf8')) as {
|
||||||
|
name?: string;
|
||||||
|
};
|
||||||
|
return manifest.name ? { dir: join(extensionsRoot, entry.name), name: manifest.name } : null;
|
||||||
|
})
|
||||||
|
.filter((info): info is ExtensionInfo => info !== null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Matches static imports, side-effect imports, dynamic imports and require. */
|
||||||
|
const IMPORT_SPECIFIER_RE = /(?:from\s*|import\s*\(\s*|require\s*\(\s*|import\s+)(['"])([^'"]+)\1/g;
|
||||||
|
|
||||||
|
function extractImportSpecifiers(source: string): string[] {
|
||||||
|
return [...source.matchAll(IMPORT_SPECIFIER_RE)]
|
||||||
|
.map((match) => match[2] ?? '')
|
||||||
|
.filter((specifier) => specifier.length > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** True when `specifier` targets a concrete extension from `importerFile`. */
|
||||||
|
function isExtensionImport(
|
||||||
|
specifier: string,
|
||||||
|
importerFile: string,
|
||||||
|
extensions: ExtensionInfo[],
|
||||||
|
): boolean {
|
||||||
|
for (const ext of extensions) {
|
||||||
|
if (specifier === ext.name || specifier.startsWith(`${ext.name}/`)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (specifier.startsWith('.')) {
|
||||||
|
const resolved = resolve(dirname(importerFile), specifier);
|
||||||
|
if (isWithin(extensionsRoot, resolved)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function listSourceFiles(dir: string): string[] {
|
||||||
|
if (!existsSync(dir)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const files: string[] = [];
|
||||||
|
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
||||||
|
const path = join(dir, entry.name);
|
||||||
|
if (entry.isDirectory()) {
|
||||||
|
files.push(...listSourceFiles(path));
|
||||||
|
} else if (entry.isFile() && /\.(?:[cm]?ts|tsx)$/.test(entry.name) && !entry.name.endsWith('.d.ts')) {
|
||||||
|
files.push(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('FIT-001: no core package imports a concrete extension', () => {
|
||||||
|
const extensions = findExtensionPackages();
|
||||||
|
|
||||||
|
it('finds at least one concrete extension package to check against', () => {
|
||||||
|
// Guards against a vacuous pass when extensions/ is empty.
|
||||||
|
expect(extensions.length).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('packages/* sources import no concrete extension', () => {
|
||||||
|
const violations: string[] = [];
|
||||||
|
for (const file of listSourceFiles(packagesRoot)) {
|
||||||
|
const source = readFileSync(file, 'utf8');
|
||||||
|
for (const specifier of extractImportSpecifiers(source)) {
|
||||||
|
if (isExtensionImport(specifier, file, extensions)) {
|
||||||
|
violations.push(
|
||||||
|
`${relative(repoRoot, file)}: import "${specifier}" targets concrete extension "${extensions
|
||||||
|
.filter((ext) => isExtensionImport(specifier, file, [ext]))
|
||||||
|
.map((ext) => ext.name)
|
||||||
|
.join(', ')}"`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect(violations).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('detector rejects extension imports and accepts benign imports (sanity)', () => {
|
||||||
|
const ext = extensions[0];
|
||||||
|
expect(ext, 'expected at least one extension package').toBeDefined();
|
||||||
|
if (!ext) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const importer = join(packagesRoot, 'core', 'src', 'index.ts');
|
||||||
|
|
||||||
|
// By package name (bare and subpath).
|
||||||
|
expect(isExtensionImport(ext.name, importer, extensions)).toBe(true);
|
||||||
|
expect(isExtensionImport(`${ext.name}/server`, importer, extensions)).toBe(true);
|
||||||
|
// By relative path resolving into extensions/.
|
||||||
|
expect(
|
||||||
|
isExtensionImport(relative(dirname(importer), join(ext.dir, 'src', 'index.js')), importer, extensions),
|
||||||
|
).toBe(true);
|
||||||
|
// Benign imports must pass.
|
||||||
|
expect(isExtensionImport('node:test', importer, extensions)).toBe(false);
|
||||||
|
expect(isExtensionImport('vitest', importer, extensions)).toBe(false);
|
||||||
|
expect(isExtensionImport('@personal-blog/core', importer, extensions)).toBe(false);
|
||||||
|
expect(isExtensionImport('./site.js', importer, extensions)).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2023",
|
||||||
|
"lib": ["ES2023"],
|
||||||
|
"module": "NodeNext",
|
||||||
|
"moduleResolution": "NodeNext",
|
||||||
|
"strict": true,
|
||||||
|
"noUncheckedIndexedAccess": true,
|
||||||
|
"exactOptionalPropertyTypes": true,
|
||||||
|
"noImplicitOverride": true,
|
||||||
|
"useUnknownInCatchVariables": true,
|
||||||
|
"verbatimModuleSyntax": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"declaration": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
"skipLibCheck": true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"noEmit": true,
|
||||||
|
"types": ["node"]
|
||||||
|
},
|
||||||
|
"include": ["tests/**/*.ts", "vitest.config.ts"]
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import { defineConfig } from 'vitest/config';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
test: {
|
||||||
|
environment: 'node',
|
||||||
|
},
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user