安装
$
genpm add @core/site你将获得
- 源代码位于 src/lib/site/,共 10 个文件。 (19.3 kB)
- AI 规则位于 src/lib/site/AGENTS.md,另附 IDE 规则文件。
- 添加到 .env.example 的环境变量:SITE_URL, SITE_REDIRECT_HOSTS。
- 自动为你解析 @core/content, @core/contracts, @core/db。
README
此包没有 README。
约 583 个 token→ src/lib/site/AGENTS.md→ .cursor/rules/genpm-core-site.mdc
这正是你的 AI 在 src/lib/site 中工作时读取的内容。不会向其上下文添加其他任何内容。
@core/site — rules for AI agents
Purpose
What every site has: settings (name, logo, contact, social profiles, legal page paths) as a @core/content singleton,
editable menus up to 3 levels as a content collection (slug = location: header, footer), and redirects managed
from the admin with loop detection, safe targets (site paths or https to allowed hosts) and hit counters.
No page content, no SEO tags (@core/seo reads these settings).
Map
index.ts— public API:getSiteSettings,getMenu,upsertRedirect(byfrom),updateRedirect(by id),resolveRedirect,redirectResponse,siteAdminResources.content.ts—siteSettings,menusand their schemas.redirects.ts— redirect logic.links.ts— link validation.adapters/hono.ts—redirectMiddleware.adapters/next.ts—redirectFor(req).
Integration
- Env:
SITE_URL(https://example.com), optionalSITE_REDIRECT_HOSTS(comma-separated external hosts allowed as redirect targets). Migrations as insrc/lib/db/AGENTS.md. - Import this module once at startup (it registers the
site_settingsandmenuscontent definitions). - Seed initial values with
seedEntry(@core/content): settings and theheader/footermenus, using the site's current ones. - Render:
const settings = await getSiteSettings({ locale }),const items = await getMenu('header', { locale }). - Redirects: Hono
app.use(redirectMiddleware); Next.jsmiddleware.tswithruntime: 'nodejs'callingredirectFor(req). - Add
...siteAdminResources()tosrc/genpm/admin.ts. - Verify: create a redirect
/old → /newand request/old(301 to/new).
Conventions
- Menu links are site paths,
https://,mailto:ortel:; open external links withrel="noopener". - Redirect sources are exact paths without query; the incoming query string is preserved.
- Permissions:
site_settings:*,menus:*,redirects:read|create|update|delete.
Don't
- Don't hardcode the site name, logo or menus in components once this module is installed.
- Don't build redirects from user input; only from the admin through
upsertRedirect/updateRedirect. Editing changes that row by id and fails withinvalid_path(422) if the newfrombelongs to another redirect. - Don't put legal text here; only the paths of the legal pages.
# @core/site — rules for AI agents
## Purpose
What every site has: settings (name, logo, contact, social profiles, legal page paths) as a @core/content singleton,
editable menus up to 3 levels as a content collection (slug = location: `header`, `footer`), and redirects managed
from the admin with loop detection, safe targets (site paths or https to allowed hosts) and hit counters.
No page content, no SEO tags (@core/seo reads these settings).
## Map
- `index.ts` — public API: `getSiteSettings`, `getMenu`, `upsertRedirect` (by `from`), `updateRedirect` (by id), `resolveRedirect`, `redirectResponse`, `siteAdminResources`.
- `content.ts` — `siteSettings`, `menus` and their schemas. `redirects.ts` — redirect logic. `links.ts` — link validation.
- `adapters/hono.ts` — `redirectMiddleware`. `adapters/next.ts` — `redirectFor(req)`.
## Integration
1. Env: `SITE_URL` (`https://example.com`), optional `SITE_REDIRECT_HOSTS` (comma-separated external hosts allowed as redirect targets). Migrations as in `src/lib/db/AGENTS.md`.
2. Import this module once at startup (it registers the `site_settings` and `menus` content definitions).
3. Seed initial values with `seedEntry` (@core/content): settings and the `header`/`footer` menus, using the site's current ones.
4. Render: `const settings = await getSiteSettings({ locale })`, `const items = await getMenu('header', { locale })`.
5. Redirects: Hono `app.use(redirectMiddleware)`; Next.js `middleware.ts` with `runtime: 'nodejs'` calling `redirectFor(req)`.
6. Add `...siteAdminResources()` to `src/genpm/admin.ts`.
7. Verify: create a redirect `/old → /new` and request `/old` (301 to `/new`).
## Conventions
- Menu links are site paths, `https://`, `mailto:` or `tel:`; open external links with `rel="noopener"`.
- Redirect sources are exact paths without query; the incoming query string is preserved.
- Permissions: `site_settings:*`, `menus:*`, `redirects:read|create|update|delete`.
## Don't
- Don't hardcode the site name, logo or menus in components once this module is installed.
- Don't build redirects from user input; only from the admin through `upsertRedirect` / `updateRedirect`. Editing changes that row by id and fails with `invalid_path` (422) if the new `from` belongs to another redirect.
- Don't put legal text here; only the paths of the legal pages.
应用 .genpmignore 后将被注入的确切目录树。固定于
// Validación de enlaces y rutas: nada de `javascript:`, `//host` ni dominios no permitidos.
export class SiteError extends Error {
constructor(
readonly code: 'invalid_path' | 'invalid_link' | 'redirect_loop' | 'not_found' | 'forbidden',
message: string = code,
) {
super(message);
this.name = 'SiteError';
}
}
/** Ruta del sitio normalizada: empieza por `/`, sin `//`, sin query/hash, sin barra final (salvo `/`). */
export function normalizePath(path: string): string {
if (!/^\/(?![/\\])[^\s?#\\]*$/.test(path) || path.length > 500 || path.split('/').includes('..'))
throw new SiteError('invalid_path', `invalid path: ${JSON.stringify(path)}`);
return path.length > 1 ? path.replace(/\/+$/, '') : path;
}
/** Hosts permitidos para destinos externos: el del sitio y los de `SITE_REDIRECT_HOSTS` (separados por comas). */
export function allowedHosts(env: Record<string, string | undefined> = process.env): string[] {
const hosts = (env.SITE_REDIRECT_HOSTS ?? '').split(',').map((h) => h.trim().toLowerCase()).filter(Boolean);
if (env.SITE_URL) hosts.push(new URL(env.SITE_URL).hostname.toLowerCase());
return hosts;
}
/** Destino de redirección: ruta relativa o `https://` a un host permitido. */
export function assertRedirectTarget(to: string, hosts = allowedHosts()): string {
if (to.startsWith('/')) return to.includes('?') ? `${normalizePath(to.split('?')[0]!)}?${to.split('?').slice(1).join('?')}` : normalizePath(to);
let url: URL;
try {
url = new URL(to);
} catch {
throw new SiteError('invalid_link', `invalid redirect target: ${to}`);
}
if (url.protocol !== 'https:' || !hosts.includes(url.hostname.toLowerCase()) || url.username || url.password)
throw new SiteError('invalid_link', `redirect target host not allowed: ${url.hostname} (add it to SITE_REDIRECT_HOSTS)`);
return url.toString();
}
/** Enlace de menú: ruta del sitio, `https://`, `mailto:` o `tel:`. */
export function isSafeLink(href: string): boolean {
if (href.startsWith('/')) return /^\/(?![/\\])[^\s\\]*$/.test(href);
if (/^mailto:[^\s]+@[^\s]+$/i.test(href) || /^tel:\+?[0-9 ()-]{3,30}$/i.test(href)) return true;
try {
const u = new URL(href);
return u.protocol === 'https:' || u.protocol === 'http:';
} catch {
return false;
}
}
此包未声明 MCP 服务器。
| 版本 | 提交 | 发布时间 | 扫描 |
|---|---|---|---|
| 1.1.0 | b7c13fa | 5小时前 | 扫描通过 |
- npm
- zod ^4.0.0
- 建议
- GenPM 会给出 npm 命令建议,只有你同意时才会运行。
- 被以下包使用(2)
- @core/kit-cms ^1.0.0@core/seo ^1.0.0
- 扫描
- 扫描通过 · 0 个问题
- 提交
- v1.1.0 → b7c13fa9b316c9880bceb62d510ee9661a76ae08 · 获取后已校验
- 脚本
- 无。GenPM 从不运行包中的代码。
- 许可证
- MIT
- 质量
- 100/100
- 可识别的许可证已满足
- AGENTS.md 说明了用途已满足
- AGENTS.md 包含集成步骤已满足
- AGENTS.md 列出约定或禁止事项已满足
- 包含测试已满足
- 通过安全扫描已满足
- 最近 6 个月内发布已满足
- 已验证的发布者已满足
- 摘要和关键词已满足
- 举报
- 发现问题了吗?