feat(web): payload cms v3 + blog + changelog data model
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
5
.gitignore
vendored
5
.gitignore
vendored
@@ -67,3 +67,8 @@ dist/
|
||||
|
||||
# Auto Claude data directory
|
||||
.auto-claude/
|
||||
|
||||
# Payload CMS
|
||||
apps/web/payload.db
|
||||
apps/web/public/media/*
|
||||
!apps/web/public/media/.gitkeep
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { NextConfig } from "next";
|
||||
import { withPayload } from "@payloadcms/next/withPayload";
|
||||
|
||||
import env from "./env.config";
|
||||
|
||||
@@ -124,4 +125,4 @@ const withBundleAnalyzer = require("@next/bundle-analyzer")({
|
||||
enabled: env.ANALYZE,
|
||||
});
|
||||
|
||||
export default withBundleAnalyzer(config);
|
||||
export default withPayload(withBundleAnalyzer(config));
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
"@hookform/resolvers": "5.2.2",
|
||||
"@next/bundle-analyzer": "16.0.10",
|
||||
"@number-flow/react": "0.5.10",
|
||||
"@payloadcms/db-sqlite": "^3.81.0",
|
||||
"@payloadcms/next": "^3.81.0",
|
||||
"@payloadcms/richtext-lexical": "^3.81.0",
|
||||
"@tanstack/react-query": "catalog:",
|
||||
"@tanstack/react-query-devtools": "catalog:",
|
||||
"@tanstack/react-table": "catalog:",
|
||||
@@ -44,6 +47,7 @@
|
||||
"next-i18n-router": "5.5.5",
|
||||
"next-themes": "0.4.6",
|
||||
"nuqs": "2.7.2",
|
||||
"payload": "^3.81.0",
|
||||
"pdfjs-dist": "5.4.530",
|
||||
"qrcode": "1.5.4",
|
||||
"react": "catalog:react19",
|
||||
@@ -57,6 +61,7 @@
|
||||
"rehype-raw": "7.0.0",
|
||||
"remark-gfm": "4.0.1",
|
||||
"remark-math": "6.0.0",
|
||||
"sharp": "0.34.5",
|
||||
"sonner": "2.0.7",
|
||||
"zod": "catalog:",
|
||||
"zustand": "5.0.8"
|
||||
|
||||
199
apps/web/payload.config.ts
Normal file
199
apps/web/payload.config.ts
Normal file
@@ -0,0 +1,199 @@
|
||||
import { buildConfig } from "payload";
|
||||
import { sqliteAdapter } from "@payloadcms/db-sqlite";
|
||||
import { lexicalEditor } from "@payloadcms/richtext-lexical";
|
||||
import path from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
import sharp from "sharp";
|
||||
|
||||
const filename = fileURLToPath(import.meta.url);
|
||||
const dirname = path.dirname(filename);
|
||||
|
||||
export default buildConfig({
|
||||
secret: process.env.PAYLOAD_SECRET || "claudemesh-dev-secret-change-in-production",
|
||||
|
||||
admin: {
|
||||
user: "users",
|
||||
meta: {
|
||||
titleSuffix: "— claudemesh",
|
||||
},
|
||||
},
|
||||
|
||||
editor: lexicalEditor(),
|
||||
|
||||
db: sqliteAdapter({
|
||||
client: {
|
||||
url: process.env.PAYLOAD_DATABASE_URI || path.resolve(dirname, "payload.db"),
|
||||
},
|
||||
}),
|
||||
|
||||
sharp,
|
||||
|
||||
collections: [
|
||||
// --- Users (admin panel) ---
|
||||
{
|
||||
slug: "users",
|
||||
auth: true,
|
||||
admin: { useAsTitle: "email" },
|
||||
fields: [
|
||||
{ name: "name", type: "text" },
|
||||
{ name: "role", type: "select", options: ["admin", "editor"], defaultValue: "editor" },
|
||||
],
|
||||
},
|
||||
|
||||
// --- Media ---
|
||||
{
|
||||
slug: "media",
|
||||
upload: {
|
||||
staticDir: path.resolve(dirname, "public/media"),
|
||||
mimeTypes: ["image/*"],
|
||||
},
|
||||
admin: { useAsTitle: "alt" },
|
||||
fields: [
|
||||
{ name: "alt", type: "text", required: true },
|
||||
],
|
||||
},
|
||||
|
||||
// --- Authors ---
|
||||
{
|
||||
slug: "authors",
|
||||
admin: { useAsTitle: "name" },
|
||||
fields: [
|
||||
{ name: "name", type: "text", required: true },
|
||||
{ name: "slug", type: "text", required: true, unique: true },
|
||||
{ name: "bio", type: "textarea" },
|
||||
{ name: "role", type: "text" },
|
||||
{
|
||||
name: "avatar",
|
||||
type: "upload",
|
||||
relationTo: "media",
|
||||
},
|
||||
{
|
||||
name: "links",
|
||||
type: "group",
|
||||
fields: [
|
||||
{ name: "github", type: "text" },
|
||||
{ name: "twitter", type: "text" },
|
||||
{ name: "website", type: "text" },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
// --- Categories ---
|
||||
{
|
||||
slug: "categories",
|
||||
admin: { useAsTitle: "name" },
|
||||
fields: [
|
||||
{ name: "name", type: "text", required: true },
|
||||
{ name: "slug", type: "text", required: true, unique: true },
|
||||
{ name: "description", type: "textarea" },
|
||||
],
|
||||
},
|
||||
|
||||
// --- Blog Posts ---
|
||||
{
|
||||
slug: "posts",
|
||||
admin: {
|
||||
useAsTitle: "title",
|
||||
defaultColumns: ["title", "status", "publishedAt", "author"],
|
||||
},
|
||||
versions: { drafts: true },
|
||||
fields: [
|
||||
{ name: "title", type: "text", required: true },
|
||||
{
|
||||
name: "slug",
|
||||
type: "text",
|
||||
required: true,
|
||||
unique: true,
|
||||
admin: {
|
||||
position: "sidebar",
|
||||
description: "URL-friendly identifier. Auto-generated from title if left blank.",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "excerpt",
|
||||
type: "textarea",
|
||||
admin: { description: "1-2 sentence summary for cards and meta descriptions." },
|
||||
},
|
||||
{
|
||||
name: "content",
|
||||
type: "richText",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: "coverImage",
|
||||
type: "upload",
|
||||
relationTo: "media",
|
||||
},
|
||||
{
|
||||
name: "author",
|
||||
type: "relationship",
|
||||
relationTo: "authors",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: "categories",
|
||||
type: "relationship",
|
||||
relationTo: "categories",
|
||||
hasMany: true,
|
||||
},
|
||||
{
|
||||
name: "publishedAt",
|
||||
type: "date",
|
||||
admin: { position: "sidebar", date: { pickerAppearance: "dayOnly" } },
|
||||
},
|
||||
{
|
||||
name: "status",
|
||||
type: "select",
|
||||
options: [
|
||||
{ label: "Draft", value: "draft" },
|
||||
{ label: "Published", value: "published" },
|
||||
],
|
||||
defaultValue: "draft",
|
||||
admin: { position: "sidebar" },
|
||||
},
|
||||
{
|
||||
name: "seo",
|
||||
type: "group",
|
||||
fields: [
|
||||
{ name: "metaTitle", type: "text" },
|
||||
{ name: "metaDescription", type: "textarea" },
|
||||
{ name: "ogImage", type: "upload", relationTo: "media" },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
// --- Changelog ---
|
||||
{
|
||||
slug: "changelog",
|
||||
admin: {
|
||||
useAsTitle: "version",
|
||||
defaultColumns: ["version", "date", "type"],
|
||||
},
|
||||
fields: [
|
||||
{ name: "version", type: "text", required: true },
|
||||
{ name: "date", type: "date", required: true },
|
||||
{
|
||||
name: "type",
|
||||
type: "select",
|
||||
options: [
|
||||
{ label: "Feature", value: "feat" },
|
||||
{ label: "Fix", value: "fix" },
|
||||
{ label: "Docs", value: "docs" },
|
||||
{ label: "Breaking", value: "breaking" },
|
||||
],
|
||||
required: true,
|
||||
},
|
||||
{ name: "summary", type: "text", required: true },
|
||||
{ name: "body", type: "richText" },
|
||||
{ name: "npmUrl", type: "text" },
|
||||
{ name: "githubUrl", type: "text" },
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
typescript: {
|
||||
outputFile: path.resolve(dirname, "src/payload-types.ts"),
|
||||
},
|
||||
});
|
||||
0
apps/web/public/media/.gitkeep
Normal file
0
apps/web/public/media/.gitkeep
Normal file
14
apps/web/src/app/(payload)/admin/[[...segments]]/page.tsx
Normal file
14
apps/web/src/app/(payload)/admin/[[...segments]]/page.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck — Payload generates these types at build time
|
||||
import { RootPage, generatePageMetadata } from "@payloadcms/next/views";
|
||||
import { importMap } from "../importMap";
|
||||
import config from "@payload-config";
|
||||
|
||||
type Args = { params: Promise<{ segments: string[] }> };
|
||||
|
||||
export const generateMetadata = ({ params }: Args) =>
|
||||
generatePageMetadata({ config, params });
|
||||
|
||||
export default function Page({ params }: Args) {
|
||||
return <RootPage config={config} params={params} importMap={importMap} />;
|
||||
}
|
||||
2
apps/web/src/app/(payload)/admin/importMap.js
Normal file
2
apps/web/src/app/(payload)/admin/importMap.js
Normal file
@@ -0,0 +1,2 @@
|
||||
// Auto-generated by Payload — placeholder until first build
|
||||
export const importMap = {};
|
||||
11
apps/web/src/app/(payload)/api/[...slug]/route.ts
Normal file
11
apps/web/src/app/(payload)/api/[...slug]/route.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
import { REST_DELETE, REST_GET, REST_OPTIONS, REST_PATCH, REST_POST, REST_PUT } from "@payloadcms/next/routes";
|
||||
import config from "@payload-config";
|
||||
|
||||
export const GET = REST_GET(config);
|
||||
export const POST = REST_POST(config);
|
||||
export const DELETE = REST_DELETE(config);
|
||||
export const PATCH = REST_PATCH(config);
|
||||
export const PUT = REST_PUT(config);
|
||||
export const OPTIONS = REST_OPTIONS(config);
|
||||
14
apps/web/src/app/(payload)/layout.tsx
Normal file
14
apps/web/src/app/(payload)/layout.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import "@payloadcms/next/css";
|
||||
import type { ReactNode } from "react";
|
||||
|
||||
export const metadata = {
|
||||
title: "Admin — claudemesh",
|
||||
};
|
||||
|
||||
export default function PayloadLayout({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body>{children}</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
80
apps/web/src/app/[locale]/(marketing)/blog/[slug]/page.tsx
Normal file
80
apps/web/src/app/[locale]/(marketing)/blog/[slug]/page.tsx
Normal file
@@ -0,0 +1,80 @@
|
||||
import { notFound } from "next/navigation";
|
||||
import { getPayload } from "payload";
|
||||
import config from "@payload-config";
|
||||
import { RichText } from "@payloadcms/richtext-lexical/react";
|
||||
|
||||
type Props = { params: Promise<{ slug: string }> };
|
||||
|
||||
export async function generateMetadata({ params }: Props) {
|
||||
const { slug } = await params;
|
||||
const payload = await getPayload({ config });
|
||||
const { docs } = await payload.find({
|
||||
collection: "posts",
|
||||
where: { slug: { equals: slug }, status: { equals: "published" } },
|
||||
limit: 1,
|
||||
depth: 1,
|
||||
});
|
||||
const post = docs[0];
|
||||
if (!post) return { title: "Not found — claudemesh" };
|
||||
return {
|
||||
title: `${post.title} — claudemesh`,
|
||||
description: post.excerpt || post.seo?.metaDescription || undefined,
|
||||
};
|
||||
}
|
||||
|
||||
export default async function BlogPost({ params }: Props) {
|
||||
const { slug } = await params;
|
||||
const payload = await getPayload({ config });
|
||||
const { docs } = await payload.find({
|
||||
collection: "posts",
|
||||
where: { slug: { equals: slug }, status: { equals: "published" } },
|
||||
limit: 1,
|
||||
depth: 2,
|
||||
});
|
||||
|
||||
const post = docs[0] as any;
|
||||
if (!post) notFound();
|
||||
|
||||
const author = typeof post.author === "object" ? post.author : null;
|
||||
|
||||
return (
|
||||
<article className="mx-auto max-w-3xl px-6 py-24 md:py-32">
|
||||
<header className="mb-12">
|
||||
<time
|
||||
dateTime={post.publishedAt}
|
||||
className="text-[11px] uppercase tracking-wider text-[var(--cm-fg-tertiary)]"
|
||||
style={{ fontFamily: "var(--cm-font-mono)" }}
|
||||
>
|
||||
{post.publishedAt
|
||||
? new Date(post.publishedAt).toLocaleDateString("en-US", {
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
})
|
||||
: "Draft"}
|
||||
</time>
|
||||
<h1
|
||||
className="mt-3 text-[clamp(2rem,4.5vw,3rem)] font-medium leading-[1.1] text-[var(--cm-fg)]"
|
||||
style={{ fontFamily: "var(--cm-font-serif)" }}
|
||||
>
|
||||
{post.title}
|
||||
</h1>
|
||||
{author && (
|
||||
<p
|
||||
className="mt-4 text-sm text-[var(--cm-fg-secondary)]"
|
||||
style={{ fontFamily: "var(--cm-font-sans)" }}
|
||||
>
|
||||
by {author.name}{author.role ? ` · ${author.role}` : ""}
|
||||
</p>
|
||||
)}
|
||||
</header>
|
||||
|
||||
<div
|
||||
className="prose prose-invert max-w-none prose-headings:font-medium prose-a:text-[var(--cm-clay)] prose-a:no-underline hover:prose-a:underline prose-code:text-[var(--cm-fg-secondary)]"
|
||||
style={{ fontFamily: "var(--cm-font-serif)" }}
|
||||
>
|
||||
{post.content && <RichText data={post.content} />}
|
||||
</div>
|
||||
</article>
|
||||
);
|
||||
}
|
||||
78
apps/web/src/app/[locale]/(marketing)/blog/page.tsx
Normal file
78
apps/web/src/app/[locale]/(marketing)/blog/page.tsx
Normal file
@@ -0,0 +1,78 @@
|
||||
import Link from "next/link";
|
||||
import { getPayload } from "payload";
|
||||
import config from "@payload-config";
|
||||
|
||||
export const metadata = {
|
||||
title: "Blog — claudemesh",
|
||||
description: "Engineering notes on peer messaging, protocol design, and multi-agent security.",
|
||||
};
|
||||
|
||||
export default async function BlogIndex() {
|
||||
const payload = await getPayload({ config });
|
||||
const { docs: posts } = await payload.find({
|
||||
collection: "posts",
|
||||
where: { status: { equals: "published" } },
|
||||
sort: "-publishedAt",
|
||||
limit: 20,
|
||||
depth: 1,
|
||||
});
|
||||
|
||||
return (
|
||||
<section className="mx-auto max-w-3xl px-6 py-24 md:py-32">
|
||||
<h1
|
||||
className="text-[clamp(2rem,4.5vw,3rem)] font-medium leading-[1.1] text-[var(--cm-fg)]"
|
||||
style={{ fontFamily: "var(--cm-font-serif)" }}
|
||||
>
|
||||
Blog
|
||||
</h1>
|
||||
<p
|
||||
className="mt-4 text-[15px] text-[var(--cm-fg-secondary)]"
|
||||
style={{ fontFamily: "var(--cm-font-sans)" }}
|
||||
>
|
||||
Engineering notes on protocol design, security, and multi-agent UX.
|
||||
</p>
|
||||
|
||||
<div className="mt-12 space-y-10">
|
||||
{posts.length === 0 && (
|
||||
<p className="text-sm text-[var(--cm-fg-tertiary)]" style={{ fontFamily: "var(--cm-font-mono)" }}>
|
||||
No posts yet. First one ships soon.
|
||||
</p>
|
||||
)}
|
||||
{posts.map((post: any) => (
|
||||
<article key={post.id} className="border-b border-[var(--cm-border)] pb-8">
|
||||
<time
|
||||
dateTime={post.publishedAt}
|
||||
className="text-[11px] uppercase tracking-wider text-[var(--cm-fg-tertiary)]"
|
||||
style={{ fontFamily: "var(--cm-font-mono)" }}
|
||||
>
|
||||
{post.publishedAt
|
||||
? new Date(post.publishedAt).toLocaleDateString("en-US", {
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
})
|
||||
: "Draft"}
|
||||
</time>
|
||||
<h2 className="mt-2">
|
||||
<Link
|
||||
href={`/blog/${post.slug}`}
|
||||
className="text-[22px] font-medium leading-tight text-[var(--cm-fg)] transition-colors hover:text-[var(--cm-clay)]"
|
||||
style={{ fontFamily: "var(--cm-font-serif)" }}
|
||||
>
|
||||
{post.title}
|
||||
</Link>
|
||||
</h2>
|
||||
{post.excerpt && (
|
||||
<p
|
||||
className="mt-3 text-[14px] leading-[1.6] text-[var(--cm-fg-secondary)]"
|
||||
style={{ fontFamily: "var(--cm-font-sans)" }}
|
||||
>
|
||||
{post.excerpt}
|
||||
</p>
|
||||
)}
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
107
apps/web/src/app/[locale]/(marketing)/changelog/page.tsx
Normal file
107
apps/web/src/app/[locale]/(marketing)/changelog/page.tsx
Normal file
@@ -0,0 +1,107 @@
|
||||
import { getPayload } from "payload";
|
||||
import config from "@payload-config";
|
||||
|
||||
export const metadata = {
|
||||
title: "Changelog — claudemesh",
|
||||
description: "Release history for claudemesh-cli.",
|
||||
};
|
||||
|
||||
const TYPE_LABELS: Record<string, string> = {
|
||||
feat: "Feature",
|
||||
fix: "Fix",
|
||||
docs: "Docs",
|
||||
breaking: "Breaking",
|
||||
};
|
||||
|
||||
const TYPE_COLORS: Record<string, string> = {
|
||||
feat: "bg-[var(--cm-clay)]",
|
||||
fix: "bg-[var(--cm-cactus)]",
|
||||
docs: "bg-[var(--cm-oat)]",
|
||||
breaking: "bg-red-500",
|
||||
};
|
||||
|
||||
export default async function ChangelogPage() {
|
||||
const payload = await getPayload({ config });
|
||||
const { docs: entries } = await payload.find({
|
||||
collection: "changelog",
|
||||
sort: "-date",
|
||||
limit: 50,
|
||||
});
|
||||
|
||||
return (
|
||||
<section className="mx-auto max-w-3xl px-6 py-24 md:py-32">
|
||||
<h1
|
||||
className="text-[clamp(2rem,4.5vw,3rem)] font-medium leading-[1.1] text-[var(--cm-fg)]"
|
||||
style={{ fontFamily: "var(--cm-font-serif)" }}
|
||||
>
|
||||
Changelog
|
||||
</h1>
|
||||
<p
|
||||
className="mt-4 text-[15px] text-[var(--cm-fg-secondary)]"
|
||||
style={{ fontFamily: "var(--cm-font-sans)" }}
|
||||
>
|
||||
Every shipped version of claudemesh-cli.
|
||||
</p>
|
||||
|
||||
<div className="mt-12 space-y-8">
|
||||
{entries.length === 0 && (
|
||||
<p className="text-sm text-[var(--cm-fg-tertiary)]" style={{ fontFamily: "var(--cm-font-mono)" }}>
|
||||
No entries yet.
|
||||
</p>
|
||||
)}
|
||||
{entries.map((entry: any) => (
|
||||
<article
|
||||
key={entry.id}
|
||||
className="border-b border-[var(--cm-border)] pb-6"
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<span
|
||||
className={`rounded-[4px] px-2 py-0.5 text-[10px] font-medium uppercase tracking-wider text-[var(--cm-bg)] ${TYPE_COLORS[entry.type] || "bg-[var(--cm-fg-tertiary)]"}`}
|
||||
style={{ fontFamily: "var(--cm-font-mono)" }}
|
||||
>
|
||||
{TYPE_LABELS[entry.type] || entry.type}
|
||||
</span>
|
||||
<span
|
||||
className="text-[18px] font-medium text-[var(--cm-fg)]"
|
||||
style={{ fontFamily: "var(--cm-font-serif)" }}
|
||||
>
|
||||
v{entry.version}
|
||||
</span>
|
||||
<time
|
||||
dateTime={entry.date}
|
||||
className="text-[11px] text-[var(--cm-fg-tertiary)]"
|
||||
style={{ fontFamily: "var(--cm-font-mono)" }}
|
||||
>
|
||||
{new Date(entry.date).toLocaleDateString("en-US", {
|
||||
year: "numeric",
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
})}
|
||||
</time>
|
||||
</div>
|
||||
<p
|
||||
className="mt-2 text-[14px] leading-[1.6] text-[var(--cm-fg-secondary)]"
|
||||
style={{ fontFamily: "var(--cm-font-sans)" }}
|
||||
>
|
||||
{entry.summary}
|
||||
</p>
|
||||
{(entry.npmUrl || entry.githubUrl) && (
|
||||
<div className="mt-3 flex gap-4 text-[12px]" style={{ fontFamily: "var(--cm-font-mono)" }}>
|
||||
{entry.npmUrl && (
|
||||
<a href={entry.npmUrl} className="text-[var(--cm-clay)] hover:underline">
|
||||
npm →
|
||||
</a>
|
||||
)}
|
||||
{entry.githubUrl && (
|
||||
<a href={entry.githubUrl} className="text-[var(--cm-clay)] hover:underline">
|
||||
github →
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -19,6 +19,6 @@ export const proxy = (request: NextRequest) =>
|
||||
});
|
||||
|
||||
export const config = {
|
||||
matcher: "/((?!api|static|install|.*\\..*|_next).*)",
|
||||
matcher: "/((?!api|static|install|admin|.*\\..*|_next).*)",
|
||||
unstable_allowDynamic: ["**/node_modules/lodash*/**/*.js"],
|
||||
};
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
"jsx": "preserve",
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"~/*": ["./src/*"]
|
||||
"~/*": ["./src/*"],
|
||||
"@payload-config": ["./payload.config.ts"]
|
||||
},
|
||||
"plugins": [{ "name": "next" }],
|
||||
"module": "esnext"
|
||||
|
||||
@@ -44,7 +44,9 @@
|
||||
"pnpm": {
|
||||
"onlyBuiltDependencies": [
|
||||
"esbuild",
|
||||
"duckdb"
|
||||
"duckdb",
|
||||
"better-sqlite3",
|
||||
"sharp"
|
||||
],
|
||||
"overrides": {
|
||||
"csstype": "3.1.3",
|
||||
|
||||
2635
pnpm-lock.yaml
generated
2635
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,3 +1,5 @@
|
||||
esbuild@0.23.1
|
||||
esbuild@0.25.10
|
||||
esbuild@0.27.2
|
||||
better-sqlite3@12.4.1
|
||||
sharp@0.34.5
|
||||
|
||||
Reference in New Issue
Block a user