From 27009245e3d8ecb480fa3696dfad7c0a5500f1e9 Mon Sep 17 00:00:00 2001 From: kang-heewon Date: Mon, 15 Jun 2026 09:43:11 +0900 Subject: [PATCH 1/4] fix: publish extension compatibility matrix --- README.md | 64 ++++ docs/package-catalog.json | 294 +++++++++++++++ docs/package-docs-report.md | 12 + .../docs/en/reference/extension-matrix.md | 70 ++++ scripts/package-docs-check.mts | 341 ++++++++++++++++++ scripts/tests/package-docs-check.spec.ts | 70 +++- 6 files changed, 849 insertions(+), 2 deletions(-) create mode 100644 packages/docs/src/content/docs/en/reference/extension-matrix.md diff --git a/README.md b/README.md index fd176b8e3..4e3a609ad 100644 --- a/README.md +++ b/README.md @@ -360,6 +360,70 @@ Croco가 **완전한 SaaS 프레임워크**가 되기 위해 계획 중인 기 | 🔴 alpha/WIP | 개발 중, 사용 시 주의 필요 | 31 | | ⚠️ deprecated | 대체 패키지 존재, 마이그레이션 권장 | 0 | +### Extension & Adapter Matrix + +> 이 섹션은 `docs/package-catalog.json`의 `extensionMatrix` metadata에서 생성됩니다. 성숙도와 CI smoke 상태는 별도 열로 표시합니다. + +Runtime columns: Node는 장기 실행 서버/CLI, Lambda는 서버리스 함수, Workers는 Cloudflare Workers, Frontend는 browser/SSR frontend integration을 의미합니다. + +#### Provider + +| Package | Domain | Adapter | Node | Lambda | Workers | Frontend | Required env/config | Peer deps | Features | Maturity | CI smoke | +| -------------------------------- | --------------- | ------------------------------------ | ---- | ------ | ------- | -------- | ----------------------------------------------------------------------------------- | -------------- | ----------------------------------------------------------------- | ------------ | -------------------------------- | +| `@croco/access-drizzle` | Access control | Drizzle repository | yes | yes | - | - | database connection supplied by app | drizzle-orm | permission checks
policy storage | 🔴 alpha/WIP | entrypoint smoke + package tests | +| `@croco/audit-drizzle` | Audit | Drizzle repository | yes | yes | - | - | database connection supplied by app | - | audit event persistence
tenant audit lookup | 🔴 alpha/WIP | entrypoint smoke + package tests | +| `@croco/auth-better-auth` | Auth | Better Auth + Drizzle provider | yes | yes | - | - | BETTER_AUTH_URL
BETTER_AUTH_SECRET
BETTER_AUTH_WEBHOOK_SECRET optional | - | session auth
webhooks
Drizzle schema | 🔴 alpha/WIP | entrypoint smoke + package tests | +| `@croco/auth-clerk` | Auth | Clerk Backend provider | yes | yes | - | - | CLERK_SECRET_KEY
CLERK_PUBLISHABLE_KEY optional
CLERK_WEBHOOK_SECRET optional | - | token auth
session management
organizations
webhooks | 🔴 alpha/WIP | entrypoint smoke + package tests | +| `@croco/auth-drizzle` | Auth | Drizzle store | yes | yes | - | - | database connection supplied by app | drizzle-orm | API key store
role registry | 🔴 alpha/WIP | entrypoint smoke + package tests | +| `@croco/batch-qstash` | Batch | QStash chunk executor | yes | yes | yes | - | QSTASH_TOKEN
public webhook URL | - | chunk scheduling
checkpoint resume
idempotent publish | 🔴 alpha/WIP | entrypoint smoke + package tests | +| `@croco/billing-polar` | Billing | Polar billing gateway | yes | yes | - | - | POLAR_ACCESS_TOKEN
POLAR_WEBHOOK_SECRET
POLAR_ORGANIZATION_ID optional | - | checkout
webhooks
subscription lifecycle
customer portal | 🟡 beta | entrypoint smoke + package tests | +| `@croco/customer-health-drizzle` | Customer health | Drizzle repository | yes | yes | - | - | database connection supplied by app | drizzle-orm | health score persistence
customer health lookup | 🔴 alpha/WIP | entrypoint smoke + package tests | +| `@croco/entitlements-drizzle` | Entitlements | Drizzle repository | yes | yes | - | - | database connection supplied by app | drizzle-orm | entitlement persistence
billing entitlement lookup | 🔴 alpha/WIP | entrypoint smoke + package tests | +| `@croco/execution-drizzle` | Execution | Drizzle execution store | yes | yes | - | - | database connection supplied by app | drizzle-orm | execution state persistence
retryable failure records | 🔴 alpha/WIP | entrypoint smoke + package tests | +| `@croco/invitation-drizzle` | Invitation | Drizzle repository | yes | yes | - | - | database connection supplied by app | - | invitation persistence
transaction-aware repository | 🔴 alpha/WIP | entrypoint smoke + package tests | +| `@croco/membership-drizzle` | Membership | Drizzle repository | yes | yes | - | - | database connection supplied by app | - | membership persistence
transaction-aware repository | 🔴 alpha/WIP | entrypoint smoke + package tests | +| `@croco/metering-drizzle` | Metering | Drizzle usage store | yes | yes | - | - | database connection supplied by app | - | usage persistence
quota lookup
migration scripts | 🔴 alpha/WIP | entrypoint smoke + package tests | +| `@croco/metering-upstash` | Metering | Upstash Redis client adapter | yes | yes | yes | - | UPSTASH_REDIS_REST_URL
UPSTASH_REDIS_REST_TOKEN | - | Redis command adapter
serverless usage storage | 🔴 alpha/WIP | entrypoint smoke + package tests | +| `@croco/metrics-billing` | Metrics | Billing metrics bridge | yes | yes | yes | - | none | - | billing event metrics
usage aggregation bridge | 🔴 alpha/WIP | entrypoint smoke + package tests | +| `@croco/notifications-resend` | Notifications | Resend email provider | yes | yes | - | - | RESEND_API_KEY
default from address | - | email send
template send
retry
idempotency key | 🔴 alpha/WIP | entrypoint smoke + package tests | +| `@croco/onboarding-drizzle` | Onboarding | Drizzle repository | yes | yes | - | - | database connection supplied by app | - | onboarding state persistence
step completion storage | 🔴 alpha/WIP | entrypoint smoke + package tests | +| `@croco/ratelimit-upstash` | Rate limiting | Upstash Redis rate-limit store | yes | yes | yes | - | UPSTASH_REDIS_REST_URL
UPSTASH_REDIS_REST_TOKEN | @upstash/redis | sliding window
token bucket
fixed window
Lua atomicity | 🔴 alpha/WIP | entrypoint smoke + package tests | +| `@croco/search-drizzle` | Search | Drizzle search index | yes | yes | - | - | database connection supplied by app | drizzle-orm | search document persistence
tenant-aware lookup | 🔴 alpha/WIP | entrypoint smoke + package tests | +| `@croco/search-meilisearch` | Search | Meilisearch engine | yes | yes | - | - | MEILISEARCH_HOST
MEILISEARCH_API_KEY | - | indexing
search
tenant tokens | 🔴 alpha/WIP | entrypoint smoke + package tests | +| `@croco/storage-cloudflare` | Storage | Cloudflare Images provider | yes | yes | - | - | CLOUDFLARE_ACCOUNT_ID
CLOUDFLARE_API_TOKEN
CLOUDFLARE_ACCOUNT_HASH | - | image upload
transform URLs
upload intents
signed URLs | 🔴 alpha/WIP | entrypoint smoke + package tests | +| `@croco/storage-cloudinary` | Storage | Cloudinary provider | yes | yes | - | - | CLOUDINARY_CLOUD_NAME
CLOUDINARY_API_KEY
CLOUDINARY_API_SECRET | - | file upload
transform URLs
upload intents
retry | 🔴 alpha/WIP | entrypoint smoke + package tests | +| `@croco/storage-r2` | Storage | Cloudflare R2 S3-compatible provider | yes | yes | - | - | R2_ACCOUNT_ID
R2_ACCESS_KEY_ID
R2_SECRET_ACCESS_KEY
R2_BUCKET | - | put/get/delete
signed URLs
stream reads
retry | 🟡 beta | entrypoint smoke + package tests | +| `@croco/tasks-qstash` | Tasks | QStash task runner | yes | yes | yes | - | QSTASH_TOKEN
destination webhook URL | - | task publish
delay override
custom headers | 🔴 alpha/WIP | entrypoint smoke + package tests | +| `@croco/triggers-qstash` | Triggers | QStash scheduler and webhook handler | yes | yes | yes | - | QSTASH_TOKEN
public webhook URL | - | schedule publish
webhook verification
trigger dispatch | 🔴 alpha/WIP | entrypoint smoke + package tests | + +#### Integration + +| Package | Domain | Adapter | Node | Lambda | Workers | Frontend | Required env/config | Peer deps | Features | Maturity | CI smoke | +| ----------------------------- | ------------- | ------------------------------ | ---- | ------ | ------- | -------- | ----------------------------------------------------------------------------------------------- | --------- | ------------------------------------------------------------------ | ------------------- | -------------------------------- | +| `@croco/analytics-posthog` | Analytics | PostHog analytics provider | yes | yes | - | - | POSTHOG_API_KEY
POSTHOG_HOST optional | - | event capture
user/group analytics | 🔴 alpha/WIP | entrypoint smoke + package tests | +| `@croco/features-posthog` | Feature flags | PostHog feature provider | yes | yes | - | - | POSTHOG_API_KEY
POSTHOG_HOST optional | - | feature flag lookup
PostHog client reuse | 🟡 beta | entrypoint smoke + package tests | +| `@croco/integrations-posthog` | PostHog | Shared PostHog client | yes | yes | - | - | POSTHOG_API_KEY
POSTHOG_HOST optional | - | client lifecycle
capture flush
diagnostics | 🟡 beta | entrypoint smoke + package tests | +| `@croco/telemetry-api` | Telemetry | OpenTelemetry application API | yes | yes | yes | - | none | - | Trace decorator
withSpan
recordError
trace context lookup | 🟢 production-ready | entrypoint smoke + package tests | +| `@croco/telemetry-sdk-node` | Telemetry | OpenTelemetry Node SDK runtime | yes | yes | - | - | OTEL_EXPORTER_OTLP_TRACES_ENDPOINT or OTEL_EXPORTER_OTLP_ENDPOINT
TELEMETRY_ENABLED optional | - | SDK init
Lambda preset
OTLP export
forceFlush | 🟢 production-ready | entrypoint smoke + package tests | + +#### Transport + +| Package | Domain | Adapter | Node | Lambda | Workers | Frontend | Required env/config | Peer deps | Features | Maturity | CI smoke | +| -------------------------------------- | ----------------- | -------------------------- | ---- | ------ | ------- | -------- | --------------------------------------------------------------------------- | --------- | ------------------------------------------------------------------------------ | ------------------- | -------------------------------- | +| `@croco/transports-graphql` | GraphQL transport | GraphQL Yoga transport | yes | yes | - | - | none | - | GraphQL server
resolver execution
Problem mapping | 🟡 beta | entrypoint smoke + package tests | +| `@croco/transports-cloudflare-workers` | HTTP transport | Cloudflare Workers adapter | - | - | yes | - | Cloudflare Worker env object supplied by platform | - | Worker fetch adapter
request context bridge | 🔴 alpha/WIP | entrypoint smoke + package tests | +| `@croco/transports-http` | HTTP transport | Hono HTTP/Lambda transport | yes | yes | - | - | CROCO_DIAGNOSTICS_TOKEN optional
CROCO_HTTP_SECURITY_VALIDATION optional | - | REST route execution
Lambda adapter
operational endpoints
diagnostics | 🟢 production-ready | entrypoint smoke + package tests | + +#### Presentation + +| Package | Domain | Adapter | Node | Lambda | Workers | Frontend | Required env/config | Peer deps | Features | Maturity | CI smoke | +| ---------------------------- | ------------------- | ----------------------------------- | ---- | ------ | ------- | -------- | ------------------------------------------------------ | -------------------------------------- | --------------------------------------------------------------------- | ------------ | -------------------------------- | +| `@croco/frontend-react` | Frontend React | React integration helpers | yes | - | - | yes | none | @croco/meta-vite
react
react-dom | React bindings
meta-vite integration | 🔴 alpha/WIP | entrypoint smoke + package tests | +| `@croco/meta-vite` | Frontend routing | Meta Vite runtime | yes | yes | yes | yes | optional Redis-compatible ISR adapter config | react
react-dom
vite
zod | route registry
server actions
SSR/RSC streaming
ISR adapters | 🟡 beta | entrypoint smoke + package tests | +| `@croco/frontend-cloudflare` | Frontend SSR | Cloudflare SSR handler | - | - | yes | - | API_WORKER binding optional
ASSETS binding optional | - | SSR fetch handler
service binding routing
asset fallback | 🔴 alpha/WIP | entrypoint smoke + package tests | +| `@croco/frontend-vite` | Frontend Vite | Vite integration helpers | yes | - | yes | yes | none | @cloudflare/vite-plugin
vite | Vite config helpers
Cloudflare Vite compatibility | 🔴 alpha/WIP | entrypoint smoke + package tests | +| `@croco/presentation-preset` | Presentation preset | Backend/frontend preset composition | yes | yes | yes | yes | none | - | preset composition
contract wiring
generated app support | 🟡 beta | entrypoint smoke + package tests | + ### 🟢 production-ready | 패키지 | 그룹 | 디렉터리 | 문서 | diff --git a/docs/package-catalog.json b/docs/package-catalog.json index 2d23aa733..cc93a98d3 100644 --- a/docs/package-catalog.json +++ b/docs/package-catalog.json @@ -257,5 +257,299 @@ "label": "⚠️ deprecated", "packages": [] } + }, + "extensionMatrix": { + "groups": ["Provider", "Integration", "Transport", "Presentation"], + "packages": { + "access-drizzle": { + "domain": "Access control", + "adapter": "Drizzle repository", + "runtimes": ["node", "lambda"], + "requiredEnv": ["database connection supplied by app"], + "features": ["permission checks", "policy storage"] + }, + "analytics-posthog": { + "domain": "Analytics", + "adapter": "PostHog analytics provider", + "runtimes": ["node", "lambda"], + "requiredEnv": ["POSTHOG_API_KEY", "POSTHOG_HOST optional"], + "features": ["event capture", "user/group analytics"] + }, + "audit-drizzle": { + "domain": "Audit", + "adapter": "Drizzle repository", + "runtimes": ["node", "lambda"], + "requiredEnv": ["database connection supplied by app"], + "features": ["audit event persistence", "tenant audit lookup"] + }, + "auth-better-auth": { + "domain": "Auth", + "adapter": "Better Auth + Drizzle provider", + "runtimes": ["node", "lambda"], + "requiredEnv": [ + "BETTER_AUTH_URL", + "BETTER_AUTH_SECRET", + "BETTER_AUTH_WEBHOOK_SECRET optional" + ], + "features": ["session auth", "webhooks", "Drizzle schema"] + }, + "auth-clerk": { + "domain": "Auth", + "adapter": "Clerk Backend provider", + "runtimes": ["node", "lambda"], + "requiredEnv": [ + "CLERK_SECRET_KEY", + "CLERK_PUBLISHABLE_KEY optional", + "CLERK_WEBHOOK_SECRET optional" + ], + "features": ["token auth", "session management", "organizations", "webhooks"] + }, + "auth-drizzle": { + "domain": "Auth", + "adapter": "Drizzle store", + "runtimes": ["node", "lambda"], + "requiredEnv": ["database connection supplied by app"], + "features": ["API key store", "role registry"] + }, + "batch-qstash": { + "domain": "Batch", + "adapter": "QStash chunk executor", + "runtimes": ["node", "lambda", "cloudflare-workers"], + "requiredEnv": ["QSTASH_TOKEN", "public webhook URL"], + "features": ["chunk scheduling", "checkpoint resume", "idempotent publish"] + }, + "billing-polar": { + "domain": "Billing", + "adapter": "Polar billing gateway", + "runtimes": ["node", "lambda"], + "requiredEnv": [ + "POLAR_ACCESS_TOKEN", + "POLAR_WEBHOOK_SECRET", + "POLAR_ORGANIZATION_ID optional" + ], + "features": ["checkout", "webhooks", "subscription lifecycle", "customer portal"] + }, + "customer-health-drizzle": { + "domain": "Customer health", + "adapter": "Drizzle repository", + "runtimes": ["node", "lambda"], + "requiredEnv": ["database connection supplied by app"], + "features": ["health score persistence", "customer health lookup"] + }, + "entitlements-drizzle": { + "domain": "Entitlements", + "adapter": "Drizzle repository", + "runtimes": ["node", "lambda"], + "requiredEnv": ["database connection supplied by app"], + "features": ["entitlement persistence", "billing entitlement lookup"] + }, + "execution-drizzle": { + "domain": "Execution", + "adapter": "Drizzle execution store", + "runtimes": ["node", "lambda"], + "requiredEnv": ["database connection supplied by app"], + "features": ["execution state persistence", "retryable failure records"] + }, + "features-posthog": { + "domain": "Feature flags", + "adapter": "PostHog feature provider", + "runtimes": ["node", "lambda"], + "requiredEnv": ["POSTHOG_API_KEY", "POSTHOG_HOST optional"], + "features": ["feature flag lookup", "PostHog client reuse"] + }, + "frontend-cloudflare": { + "domain": "Frontend SSR", + "adapter": "Cloudflare SSR handler", + "runtimes": ["cloudflare-workers"], + "requiredEnv": ["API_WORKER binding optional", "ASSETS binding optional"], + "features": ["SSR fetch handler", "service binding routing", "asset fallback"] + }, + "frontend-react": { + "domain": "Frontend React", + "adapter": "React integration helpers", + "runtimes": ["browser", "node"], + "requiredEnv": ["none"], + "features": ["React bindings", "meta-vite integration"] + }, + "frontend-vite": { + "domain": "Frontend Vite", + "adapter": "Vite integration helpers", + "runtimes": ["node", "browser", "cloudflare-workers"], + "requiredEnv": ["none"], + "features": ["Vite config helpers", "Cloudflare Vite compatibility"] + }, + "integrations-posthog": { + "domain": "PostHog", + "adapter": "Shared PostHog client", + "runtimes": ["node", "lambda"], + "requiredEnv": ["POSTHOG_API_KEY", "POSTHOG_HOST optional"], + "features": ["client lifecycle", "capture flush", "diagnostics"] + }, + "invitation-drizzle": { + "domain": "Invitation", + "adapter": "Drizzle repository", + "runtimes": ["node", "lambda"], + "requiredEnv": ["database connection supplied by app"], + "features": ["invitation persistence", "transaction-aware repository"] + }, + "membership-drizzle": { + "domain": "Membership", + "adapter": "Drizzle repository", + "runtimes": ["node", "lambda"], + "requiredEnv": ["database connection supplied by app"], + "features": ["membership persistence", "transaction-aware repository"] + }, + "meta-vite": { + "domain": "Frontend routing", + "adapter": "Meta Vite runtime", + "runtimes": ["node", "lambda", "cloudflare-workers", "browser"], + "requiredEnv": ["optional Redis-compatible ISR adapter config"], + "features": ["route registry", "server actions", "SSR/RSC streaming", "ISR adapters"] + }, + "metering-drizzle": { + "domain": "Metering", + "adapter": "Drizzle usage store", + "runtimes": ["node", "lambda"], + "requiredEnv": ["database connection supplied by app"], + "features": ["usage persistence", "quota lookup", "migration scripts"] + }, + "metering-upstash": { + "domain": "Metering", + "adapter": "Upstash Redis client adapter", + "runtimes": ["node", "lambda", "cloudflare-workers"], + "requiredEnv": ["UPSTASH_REDIS_REST_URL", "UPSTASH_REDIS_REST_TOKEN"], + "features": ["Redis command adapter", "serverless usage storage"] + }, + "metrics-billing": { + "domain": "Metrics", + "adapter": "Billing metrics bridge", + "runtimes": ["node", "lambda", "cloudflare-workers"], + "requiredEnv": ["none"], + "features": ["billing event metrics", "usage aggregation bridge"] + }, + "notifications-resend": { + "domain": "Notifications", + "adapter": "Resend email provider", + "runtimes": ["node", "lambda"], + "requiredEnv": ["RESEND_API_KEY", "default from address"], + "features": ["email send", "template send", "retry", "idempotency key"] + }, + "onboarding-drizzle": { + "domain": "Onboarding", + "adapter": "Drizzle repository", + "runtimes": ["node", "lambda"], + "requiredEnv": ["database connection supplied by app"], + "features": ["onboarding state persistence", "step completion storage"] + }, + "presentation-preset": { + "domain": "Presentation preset", + "adapter": "Backend/frontend preset composition", + "runtimes": ["node", "lambda", "cloudflare-workers", "browser"], + "requiredEnv": ["none"], + "features": ["preset composition", "contract wiring", "generated app support"] + }, + "ratelimit-upstash": { + "domain": "Rate limiting", + "adapter": "Upstash Redis rate-limit store", + "runtimes": ["node", "lambda", "cloudflare-workers"], + "requiredEnv": ["UPSTASH_REDIS_REST_URL", "UPSTASH_REDIS_REST_TOKEN"], + "features": ["sliding window", "token bucket", "fixed window", "Lua atomicity"] + }, + "search-drizzle": { + "domain": "Search", + "adapter": "Drizzle search index", + "runtimes": ["node", "lambda"], + "requiredEnv": ["database connection supplied by app"], + "features": ["search document persistence", "tenant-aware lookup"] + }, + "search-meilisearch": { + "domain": "Search", + "adapter": "Meilisearch engine", + "runtimes": ["node", "lambda"], + "requiredEnv": ["MEILISEARCH_HOST", "MEILISEARCH_API_KEY"], + "features": ["indexing", "search", "tenant tokens"] + }, + "storage-cloudflare": { + "domain": "Storage", + "adapter": "Cloudflare Images provider", + "runtimes": ["node", "lambda"], + "requiredEnv": ["CLOUDFLARE_ACCOUNT_ID", "CLOUDFLARE_API_TOKEN", "CLOUDFLARE_ACCOUNT_HASH"], + "features": ["image upload", "transform URLs", "upload intents", "signed URLs"] + }, + "storage-cloudinary": { + "domain": "Storage", + "adapter": "Cloudinary provider", + "runtimes": ["node", "lambda"], + "requiredEnv": ["CLOUDINARY_CLOUD_NAME", "CLOUDINARY_API_KEY", "CLOUDINARY_API_SECRET"], + "features": ["file upload", "transform URLs", "upload intents", "retry"] + }, + "storage-r2": { + "domain": "Storage", + "adapter": "Cloudflare R2 S3-compatible provider", + "runtimes": ["node", "lambda"], + "requiredEnv": ["R2_ACCOUNT_ID", "R2_ACCESS_KEY_ID", "R2_SECRET_ACCESS_KEY", "R2_BUCKET"], + "features": ["put/get/delete", "signed URLs", "stream reads", "retry"] + }, + "tasks-qstash": { + "domain": "Tasks", + "adapter": "QStash task runner", + "runtimes": ["node", "lambda", "cloudflare-workers"], + "requiredEnv": ["QSTASH_TOKEN", "destination webhook URL"], + "features": ["task publish", "delay override", "custom headers"] + }, + "telemetry-api": { + "domain": "Telemetry", + "adapter": "OpenTelemetry application API", + "runtimes": ["node", "lambda", "cloudflare-workers"], + "requiredEnv": ["none"], + "features": ["Trace decorator", "withSpan", "recordError", "trace context lookup"] + }, + "telemetry-sdk-node": { + "domain": "Telemetry", + "adapter": "OpenTelemetry Node SDK runtime", + "runtimes": ["node", "lambda"], + "requiredEnv": [ + "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT or OTEL_EXPORTER_OTLP_ENDPOINT", + "TELEMETRY_ENABLED optional" + ], + "features": ["SDK init", "Lambda preset", "OTLP export", "forceFlush"] + }, + "transports-cloudflare-workers": { + "domain": "HTTP transport", + "adapter": "Cloudflare Workers adapter", + "runtimes": ["cloudflare-workers"], + "requiredEnv": ["Cloudflare Worker env object supplied by platform"], + "features": ["Worker fetch adapter", "request context bridge"] + }, + "transports-graphql": { + "domain": "GraphQL transport", + "adapter": "GraphQL Yoga transport", + "runtimes": ["node", "lambda"], + "requiredEnv": ["none"], + "features": ["GraphQL server", "resolver execution", "Problem mapping"] + }, + "transports-http": { + "domain": "HTTP transport", + "adapter": "Hono HTTP/Lambda transport", + "runtimes": ["node", "lambda"], + "requiredEnv": [ + "CROCO_DIAGNOSTICS_TOKEN optional", + "CROCO_HTTP_SECURITY_VALIDATION optional" + ], + "features": [ + "REST route execution", + "Lambda adapter", + "operational endpoints", + "diagnostics" + ] + }, + "triggers-qstash": { + "domain": "Triggers", + "adapter": "QStash scheduler and webhook handler", + "runtimes": ["node", "lambda", "cloudflare-workers"], + "requiredEnv": ["QSTASH_TOKEN", "public webhook URL"], + "features": ["schedule publish", "webhook verification", "trigger dispatch"] + } + } } } diff --git a/docs/package-docs-report.md b/docs/package-docs-report.md index 293f430dc..7da7b6b74 100644 --- a/docs/package-docs-report.md +++ b/docs/package-docs-report.md @@ -11,6 +11,7 @@ | Missing package README | 23 | | Missing generated API docs | 85 | | Missing package test directory | 1 | +| Extension matrix packages | 38 | New public packages must not add missing README, API docs, or test coverage unless the gap is explicitly listed in `docs/package-docs-baseline.json`. @@ -152,3 +153,14 @@ New public packages must not add missing README, API docs, or test coverage unle | 🟡 beta | 44 | | 🔴 alpha/WIP | 31 | | ⚠️ deprecated | 0 | + +## Extension Matrix + +Extension matrix metadata is maintained in `docs/package-catalog.json` and rendered to the root README plus the docs reference page. + +| Group | Packages | Without package tests | +| ------------ | -------: | --------------------: | +| Provider | 25 | 0 | +| Integration | 5 | 0 | +| Transport | 3 | 0 | +| Presentation | 5 | 0 | diff --git a/packages/docs/src/content/docs/en/reference/extension-matrix.md b/packages/docs/src/content/docs/en/reference/extension-matrix.md new file mode 100644 index 000000000..6dc6deab2 --- /dev/null +++ b/packages/docs/src/content/docs/en/reference/extension-matrix.md @@ -0,0 +1,70 @@ +--- +title: Extension Matrix +description: Official Croco provider and adapter compatibility matrix. +--- + +# Extension Matrix + +> Generated by `pnpm docs:catalog:write`. Do not edit this file by hand. + +This page lists Croco provider, integration, transport, and presentation adapter compatibility from `docs/package-catalog.json`. Required configuration, runtime support, package peer dependencies, maturity, and CI smoke status are intentionally separate so users can evaluate production readiness without treating a passing unit test as a maturity claim. + +Runtime columns: Node covers long-running server and CLI use, Lambda covers serverless functions, Workers covers Cloudflare Workers, and Frontend covers browser or SSR frontend integration. + +## Provider + +| Package | Domain | Adapter | Node | Lambda | Workers | Frontend | Required env/config | Peer deps | Features | Maturity | CI smoke | +| -------------------------------- | --------------- | ------------------------------------ | ---- | ------ | ------- | -------- | ----------------------------------------------------------------------------------- | -------------- | ----------------------------------------------------------------- | ------------ | -------------------------------- | +| `@croco/access-drizzle` | Access control | Drizzle repository | yes | yes | - | - | database connection supplied by app | drizzle-orm | permission checks
policy storage | 🔴 alpha/WIP | entrypoint smoke + package tests | +| `@croco/audit-drizzle` | Audit | Drizzle repository | yes | yes | - | - | database connection supplied by app | - | audit event persistence
tenant audit lookup | 🔴 alpha/WIP | entrypoint smoke + package tests | +| `@croco/auth-better-auth` | Auth | Better Auth + Drizzle provider | yes | yes | - | - | BETTER_AUTH_URL
BETTER_AUTH_SECRET
BETTER_AUTH_WEBHOOK_SECRET optional | - | session auth
webhooks
Drizzle schema | 🔴 alpha/WIP | entrypoint smoke + package tests | +| `@croco/auth-clerk` | Auth | Clerk Backend provider | yes | yes | - | - | CLERK_SECRET_KEY
CLERK_PUBLISHABLE_KEY optional
CLERK_WEBHOOK_SECRET optional | - | token auth
session management
organizations
webhooks | 🔴 alpha/WIP | entrypoint smoke + package tests | +| `@croco/auth-drizzle` | Auth | Drizzle store | yes | yes | - | - | database connection supplied by app | drizzle-orm | API key store
role registry | 🔴 alpha/WIP | entrypoint smoke + package tests | +| `@croco/batch-qstash` | Batch | QStash chunk executor | yes | yes | yes | - | QSTASH_TOKEN
public webhook URL | - | chunk scheduling
checkpoint resume
idempotent publish | 🔴 alpha/WIP | entrypoint smoke + package tests | +| `@croco/billing-polar` | Billing | Polar billing gateway | yes | yes | - | - | POLAR_ACCESS_TOKEN
POLAR_WEBHOOK_SECRET
POLAR_ORGANIZATION_ID optional | - | checkout
webhooks
subscription lifecycle
customer portal | 🟡 beta | entrypoint smoke + package tests | +| `@croco/customer-health-drizzle` | Customer health | Drizzle repository | yes | yes | - | - | database connection supplied by app | drizzle-orm | health score persistence
customer health lookup | 🔴 alpha/WIP | entrypoint smoke + package tests | +| `@croco/entitlements-drizzle` | Entitlements | Drizzle repository | yes | yes | - | - | database connection supplied by app | drizzle-orm | entitlement persistence
billing entitlement lookup | 🔴 alpha/WIP | entrypoint smoke + package tests | +| `@croco/execution-drizzle` | Execution | Drizzle execution store | yes | yes | - | - | database connection supplied by app | drizzle-orm | execution state persistence
retryable failure records | 🔴 alpha/WIP | entrypoint smoke + package tests | +| `@croco/invitation-drizzle` | Invitation | Drizzle repository | yes | yes | - | - | database connection supplied by app | - | invitation persistence
transaction-aware repository | 🔴 alpha/WIP | entrypoint smoke + package tests | +| `@croco/membership-drizzle` | Membership | Drizzle repository | yes | yes | - | - | database connection supplied by app | - | membership persistence
transaction-aware repository | 🔴 alpha/WIP | entrypoint smoke + package tests | +| `@croco/metering-drizzle` | Metering | Drizzle usage store | yes | yes | - | - | database connection supplied by app | - | usage persistence
quota lookup
migration scripts | 🔴 alpha/WIP | entrypoint smoke + package tests | +| `@croco/metering-upstash` | Metering | Upstash Redis client adapter | yes | yes | yes | - | UPSTASH_REDIS_REST_URL
UPSTASH_REDIS_REST_TOKEN | - | Redis command adapter
serverless usage storage | 🔴 alpha/WIP | entrypoint smoke + package tests | +| `@croco/metrics-billing` | Metrics | Billing metrics bridge | yes | yes | yes | - | none | - | billing event metrics
usage aggregation bridge | 🔴 alpha/WIP | entrypoint smoke + package tests | +| `@croco/notifications-resend` | Notifications | Resend email provider | yes | yes | - | - | RESEND_API_KEY
default from address | - | email send
template send
retry
idempotency key | 🔴 alpha/WIP | entrypoint smoke + package tests | +| `@croco/onboarding-drizzle` | Onboarding | Drizzle repository | yes | yes | - | - | database connection supplied by app | - | onboarding state persistence
step completion storage | 🔴 alpha/WIP | entrypoint smoke + package tests | +| `@croco/ratelimit-upstash` | Rate limiting | Upstash Redis rate-limit store | yes | yes | yes | - | UPSTASH_REDIS_REST_URL
UPSTASH_REDIS_REST_TOKEN | @upstash/redis | sliding window
token bucket
fixed window
Lua atomicity | 🔴 alpha/WIP | entrypoint smoke + package tests | +| `@croco/search-drizzle` | Search | Drizzle search index | yes | yes | - | - | database connection supplied by app | drizzle-orm | search document persistence
tenant-aware lookup | 🔴 alpha/WIP | entrypoint smoke + package tests | +| `@croco/search-meilisearch` | Search | Meilisearch engine | yes | yes | - | - | MEILISEARCH_HOST
MEILISEARCH_API_KEY | - | indexing
search
tenant tokens | 🔴 alpha/WIP | entrypoint smoke + package tests | +| `@croco/storage-cloudflare` | Storage | Cloudflare Images provider | yes | yes | - | - | CLOUDFLARE_ACCOUNT_ID
CLOUDFLARE_API_TOKEN
CLOUDFLARE_ACCOUNT_HASH | - | image upload
transform URLs
upload intents
signed URLs | 🔴 alpha/WIP | entrypoint smoke + package tests | +| `@croco/storage-cloudinary` | Storage | Cloudinary provider | yes | yes | - | - | CLOUDINARY_CLOUD_NAME
CLOUDINARY_API_KEY
CLOUDINARY_API_SECRET | - | file upload
transform URLs
upload intents
retry | 🔴 alpha/WIP | entrypoint smoke + package tests | +| `@croco/storage-r2` | Storage | Cloudflare R2 S3-compatible provider | yes | yes | - | - | R2_ACCOUNT_ID
R2_ACCESS_KEY_ID
R2_SECRET_ACCESS_KEY
R2_BUCKET | - | put/get/delete
signed URLs
stream reads
retry | 🟡 beta | entrypoint smoke + package tests | +| `@croco/tasks-qstash` | Tasks | QStash task runner | yes | yes | yes | - | QSTASH_TOKEN
destination webhook URL | - | task publish
delay override
custom headers | 🔴 alpha/WIP | entrypoint smoke + package tests | +| `@croco/triggers-qstash` | Triggers | QStash scheduler and webhook handler | yes | yes | yes | - | QSTASH_TOKEN
public webhook URL | - | schedule publish
webhook verification
trigger dispatch | 🔴 alpha/WIP | entrypoint smoke + package tests | + +## Integration + +| Package | Domain | Adapter | Node | Lambda | Workers | Frontend | Required env/config | Peer deps | Features | Maturity | CI smoke | +| ----------------------------- | ------------- | ------------------------------ | ---- | ------ | ------- | -------- | ----------------------------------------------------------------------------------------------- | --------- | ------------------------------------------------------------------ | ------------------- | -------------------------------- | +| `@croco/analytics-posthog` | Analytics | PostHog analytics provider | yes | yes | - | - | POSTHOG_API_KEY
POSTHOG_HOST optional | - | event capture
user/group analytics | 🔴 alpha/WIP | entrypoint smoke + package tests | +| `@croco/features-posthog` | Feature flags | PostHog feature provider | yes | yes | - | - | POSTHOG_API_KEY
POSTHOG_HOST optional | - | feature flag lookup
PostHog client reuse | 🟡 beta | entrypoint smoke + package tests | +| `@croco/integrations-posthog` | PostHog | Shared PostHog client | yes | yes | - | - | POSTHOG_API_KEY
POSTHOG_HOST optional | - | client lifecycle
capture flush
diagnostics | 🟡 beta | entrypoint smoke + package tests | +| `@croco/telemetry-api` | Telemetry | OpenTelemetry application API | yes | yes | yes | - | none | - | Trace decorator
withSpan
recordError
trace context lookup | 🟢 production-ready | entrypoint smoke + package tests | +| `@croco/telemetry-sdk-node` | Telemetry | OpenTelemetry Node SDK runtime | yes | yes | - | - | OTEL_EXPORTER_OTLP_TRACES_ENDPOINT or OTEL_EXPORTER_OTLP_ENDPOINT
TELEMETRY_ENABLED optional | - | SDK init
Lambda preset
OTLP export
forceFlush | 🟢 production-ready | entrypoint smoke + package tests | + +## Transport + +| Package | Domain | Adapter | Node | Lambda | Workers | Frontend | Required env/config | Peer deps | Features | Maturity | CI smoke | +| -------------------------------------- | ----------------- | -------------------------- | ---- | ------ | ------- | -------- | --------------------------------------------------------------------------- | --------- | ------------------------------------------------------------------------------ | ------------------- | -------------------------------- | +| `@croco/transports-graphql` | GraphQL transport | GraphQL Yoga transport | yes | yes | - | - | none | - | GraphQL server
resolver execution
Problem mapping | 🟡 beta | entrypoint smoke + package tests | +| `@croco/transports-cloudflare-workers` | HTTP transport | Cloudflare Workers adapter | - | - | yes | - | Cloudflare Worker env object supplied by platform | - | Worker fetch adapter
request context bridge | 🔴 alpha/WIP | entrypoint smoke + package tests | +| `@croco/transports-http` | HTTP transport | Hono HTTP/Lambda transport | yes | yes | - | - | CROCO_DIAGNOSTICS_TOKEN optional
CROCO_HTTP_SECURITY_VALIDATION optional | - | REST route execution
Lambda adapter
operational endpoints
diagnostics | 🟢 production-ready | entrypoint smoke + package tests | + +## Presentation + +| Package | Domain | Adapter | Node | Lambda | Workers | Frontend | Required env/config | Peer deps | Features | Maturity | CI smoke | +| ---------------------------- | ------------------- | ----------------------------------- | ---- | ------ | ------- | -------- | ------------------------------------------------------ | -------------------------------------- | --------------------------------------------------------------------- | ------------ | -------------------------------- | +| `@croco/frontend-react` | Frontend React | React integration helpers | yes | - | - | yes | none | @croco/meta-vite
react
react-dom | React bindings
meta-vite integration | 🔴 alpha/WIP | entrypoint smoke + package tests | +| `@croco/meta-vite` | Frontend routing | Meta Vite runtime | yes | yes | yes | yes | optional Redis-compatible ISR adapter config | react
react-dom
vite
zod | route registry
server actions
SSR/RSC streaming
ISR adapters | 🟡 beta | entrypoint smoke + package tests | +| `@croco/frontend-cloudflare` | Frontend SSR | Cloudflare SSR handler | - | - | yes | - | API_WORKER binding optional
ASSETS binding optional | - | SSR fetch handler
service binding routing
asset fallback | 🔴 alpha/WIP | entrypoint smoke + package tests | +| `@croco/frontend-vite` | Frontend Vite | Vite integration helpers | yes | - | yes | yes | none | @cloudflare/vite-plugin
vite | Vite config helpers
Cloudflare Vite compatibility | 🔴 alpha/WIP | entrypoint smoke + package tests | +| `@croco/presentation-preset` | Presentation preset | Backend/frontend preset composition | yes | yes | yes | yes | none | - | preset composition
contract wiring
generated app support | 🟡 beta | entrypoint smoke + package tests | diff --git a/scripts/package-docs-check.mts b/scripts/package-docs-check.mts index f2b520d1d..bff179f33 100644 --- a/scripts/package-docs-check.mts +++ b/scripts/package-docs-check.mts @@ -19,6 +19,7 @@ type Options = { }; type PackageJson = { + readonly peerDependencies?: unknown; readonly name?: unknown; readonly private?: unknown; }; @@ -29,6 +30,7 @@ type PackageInfo = { readonly hasReadme: boolean; readonly hasTests: boolean; readonly name: string; + readonly peerDependencies: readonly string[]; readonly private: boolean; readonly shortName: string; }; @@ -39,6 +41,7 @@ type PackageRecord = PackageInfo & { }; type CatalogMetadata = { + readonly extensionMatrix?: unknown; readonly schemaVersion?: unknown; readonly groups?: unknown; readonly maturity?: unknown; @@ -54,6 +57,25 @@ type MaturityConfig = { readonly packages: readonly string[]; }; +type RuntimeKey = (typeof runtimeOrder)[number]; + +type ExtensionMetadata = { + readonly adapter: string; + readonly domain: string; + readonly features: readonly string[]; + readonly requiredEnv: readonly string[]; + readonly runtimes: readonly RuntimeKey[]; +}; + +type ExtensionRecord = PackageRecord & { + readonly extension: ExtensionMetadata; +}; + +type ExtensionMatrixState = { + readonly groups: readonly string[]; + readonly packages: readonly ExtensionRecord[]; +}; + type DocsBaseline = { readonly schemaVersion?: unknown; readonly allowedMissingApiDocs?: unknown; @@ -74,6 +96,7 @@ type CoverageSet = { }; type CatalogState = { + readonly extensionMatrix: ExtensionMatrixState; readonly groups: ReadonlyMap; readonly maturity: ReadonlyMap; readonly packages: readonly PackageRecord[]; @@ -88,8 +111,19 @@ const docsDirName = "docs"; const catalogMetadataPath = join(docsDirName, "package-catalog.json"); const docsBaselinePath = join(docsDirName, "package-docs-baseline.json"); const docsReportPath = join(docsDirName, "package-docs-report.md"); +const extensionMatrixDocsPath = join( + "packages", + "docs", + "src", + "content", + "docs", + "en", + "reference", + "extension-matrix.md", +); const readmePath = "README.md"; const maturityOrder = ["production", "beta", "alpha", "deprecated"] as const; +const runtimeOrder = ["node", "lambda", "cloudflare-workers", "browser"] as const; const scriptRootDir = dirname(dirname(fileURLToPath(import.meta.url))); type MaturityKey = (typeof maturityOrder)[number]; @@ -125,6 +159,10 @@ function run(options: Options): string[] { validateCoverageBaseline(coverage, baseline, violations); const generatedCatalog = formatMarkdown(readmePath, generateReadmeCatalog(state)); + const generatedExtensionMatrixDocs = formatMarkdown( + extensionMatrixDocsPath, + generateExtensionMatrixDocs(state), + ); const generatedReport = formatMarkdown( docsReportPath, generateDocsReport(state, coverage, baseline), @@ -132,6 +170,10 @@ function run(options: Options): string[] { if (options.mode === "write") { writeReadmeCatalog(options.rootDir, generatedCatalog); + writeGeneratedFile( + join(options.rootDir, extensionMatrixDocsPath), + generatedExtensionMatrixDocs, + ); writeGeneratedFile(join(options.rootDir, docsReportPath), generatedReport); return violations; } @@ -142,6 +184,14 @@ function run(options: Options): string[] { violations.push(`README.md package catalog drift detected; run pnpm docs:catalog:write`); } + const extensionMatrixPath = join(options.rootDir, extensionMatrixDocsPath); + const currentExtensionMatrix = existsSync(extensionMatrixPath) + ? readFileSync(extensionMatrixPath, "utf-8") + : ""; + if (currentExtensionMatrix !== generatedExtensionMatrixDocs) { + violations.push(`${extensionMatrixDocsPath} drift detected; run pnpm docs:catalog:write`); + } + const reportPath = join(options.rootDir, docsReportPath); const currentReport = existsSync(reportPath) ? readFileSync(reportPath, "utf-8") : ""; if (currentReport !== generatedReport) { @@ -205,8 +255,15 @@ function loadCatalogState(rootDir: string, violations: string[]): CatalogState { maturity: maturityKey, }; }); + const extensionMatrix = parseExtensionMatrix( + metadata.extensionMatrix, + groups, + records, + violations, + ); return { + extensionMatrix, groups, maturity, packages: records, @@ -245,6 +302,7 @@ function readPackages(rootDir: string): PackageInfo[] { existsSync(join(packageDir, "src", "tests")) || existsSync(join(packageDir, "src", "__tests__")), name: pkg.name, + peerDependencies: readDependencyKeys(pkg.peerDependencies), private: pkg.private === true, shortName: toShortPackageName(pkg.name), }); @@ -324,6 +382,192 @@ function parseMaturity( return maturity; } +function parseExtensionMatrix( + value: unknown, + groups: ReadonlyMap, + packages: readonly PackageRecord[], + violations: string[], +): ExtensionMatrixState { + if (!value || typeof value !== "object" || Array.isArray(value)) { + violations.push(`${catalogMetadataPath}: extensionMatrix must be an object`); + return { + groups: [], + packages: [], + }; + } + + const groupValue = (value as { readonly groups?: unknown }).groups; + const packageValue = (value as { readonly packages?: unknown }).packages; + if (!isStringArray(groupValue)) { + violations.push(`${catalogMetadataPath}: extensionMatrix.groups must be a string array`); + } + if (!packageValue || typeof packageValue !== "object" || Array.isArray(packageValue)) { + violations.push(`${catalogMetadataPath}: extensionMatrix.packages must be an object`); + return { + groups: isStringArray(groupValue) ? groupValue : [], + packages: [], + }; + } + + const extensionGroups = isStringArray(groupValue) ? groupValue : []; + for (const group of extensionGroups) { + if (!groups.has(group)) { + violations.push( + `${catalogMetadataPath}: extensionMatrix.groups references missing group ${group}`, + ); + } + } + + const packageByName = new Map(packages.map((pkg) => [pkg.shortName, pkg])); + const extensionGroupSet = new Set(extensionGroups); + const targetPackages = packages.filter((pkg) => extensionGroupSet.has(pkg.group)); + const records: ExtensionRecord[] = []; + + for (const [packageName, metadataValue] of Object.entries(packageValue)) { + const pkg = packageByName.get(packageName); + if (!pkg) { + violations.push( + `${catalogMetadataPath}: extensionMatrix.packages references missing package ${packageName}`, + ); + continue; + } + if (!extensionGroupSet.has(pkg.group)) { + violations.push( + `${catalogMetadataPath}: extensionMatrix.packages.${packageName} is not in an extension group`, + ); + continue; + } + + const metadata = parseExtensionMetadata(packageName, metadataValue, violations); + if (!metadata) { + continue; + } + + records.push({ + ...pkg, + extension: metadata, + }); + } + + const metadataPackageNames = new Set(records.map((pkg) => pkg.shortName)); + for (const pkg of targetPackages) { + if (!metadataPackageNames.has(pkg.shortName)) { + violations.push( + `${catalogMetadataPath}: extensionMatrix is missing metadata for ${pkg.group} package ${pkg.shortName}`, + ); + } + } + + return { + groups: extensionGroups, + packages: records.sort( + (left, right) => + extensionGroups.indexOf(left.group) - extensionGroups.indexOf(right.group) || + left.extension.domain.localeCompare(right.extension.domain) || + left.shortName.localeCompare(right.shortName), + ), + }; +} + +function parseExtensionMetadata( + packageName: string, + value: unknown, + violations: string[], +): ExtensionMetadata | null { + if (!value || typeof value !== "object" || Array.isArray(value)) { + violations.push( + `${catalogMetadataPath}: extensionMatrix.packages.${packageName} must be an object`, + ); + return null; + } + + const metadata = value as Record; + const adapter = readRequiredString( + metadata.adapter, + `extensionMatrix.packages.${packageName}.adapter`, + violations, + ); + const domain = readRequiredString( + metadata.domain, + `extensionMatrix.packages.${packageName}.domain`, + violations, + ); + const features = readRequiredStringArray( + metadata.features, + `extensionMatrix.packages.${packageName}.features`, + violations, + ); + const requiredEnv = readRequiredStringArray( + metadata.requiredEnv, + `extensionMatrix.packages.${packageName}.requiredEnv`, + violations, + ); + const runtimes = readRuntimeArray( + metadata.runtimes, + `extensionMatrix.packages.${packageName}.runtimes`, + violations, + ); + + if ( + !adapter || + !domain || + features.length === 0 || + requiredEnv.length === 0 || + runtimes.length === 0 + ) { + return null; + } + + return { + adapter, + domain, + features, + requiredEnv, + runtimes, + }; +} + +function readRequiredString(value: unknown, key: string, violations: string[]): string { + if (typeof value !== "string" || value.length === 0) { + violations.push(`${catalogMetadataPath}: ${key} must be a non-empty string`); + return ""; + } + + return value; +} + +function readRequiredStringArray( + value: unknown, + key: string, + violations: string[], +): readonly string[] { + if (!isStringArray(value) || value.length === 0) { + violations.push(`${catalogMetadataPath}: ${key} must be a non-empty string array`); + return []; + } + + return value; +} + +function readRuntimeArray( + value: unknown, + key: string, + violations: string[], +): readonly RuntimeKey[] { + const runtimes = readRequiredStringArray(value, key, violations); + const validRuntimes = new Set(runtimeOrder); + + for (const runtime of runtimes) { + if (!validRuntimes.has(runtime)) { + violations.push( + `${catalogMetadataPath}: ${key} contains unsupported runtime ${runtime}; expected one of ${runtimeOrder.join(", ")}`, + ); + } + } + + return runtimes.filter((runtime): runtime is RuntimeKey => validRuntimes.has(runtime)); +} + function validateAssignments( label: string, assignments: ReadonlyMap, @@ -506,6 +750,16 @@ function generateReadmeCatalog(state: CatalogState): string { lines.push(`| ${config.label} | ${maturityDescription(maturity)} | ${count} |`); } + lines.push( + "", + "### Extension & Adapter Matrix", + "", + "> 이 섹션은 `docs/package-catalog.json`의 `extensionMatrix` metadata에서 생성됩니다. 성숙도와 CI smoke 상태는 별도 열로 표시합니다.", + "", + "Runtime columns: Node는 장기 실행 서버/CLI, Lambda는 서버리스 함수, Workers는 Cloudflare Workers, Frontend는 browser/SSR frontend integration을 의미합니다.", + ); + appendExtensionMatrixTables(lines, state, "####"); + for (const maturity of maturityOrder) { const config = state.maturity.get(maturity); if (!config) { @@ -570,6 +824,7 @@ function generateDocsReport( `| Missing package README | ${coverage.missingReadme.length} |`, `| Missing generated API docs | ${coverage.missingApiDocs.length} |`, `| Missing package test directory | ${coverage.missingTests.length} |`, + `| Extension matrix packages | ${state.extensionMatrix.packages.length} |`, "", "New public packages must not add missing README, API docs, or test coverage unless the gap is explicitly listed in `docs/package-docs-baseline.json`.", "", @@ -606,10 +861,76 @@ function generateDocsReport( ); } + lines.push( + "", + "## Extension Matrix", + "", + "Extension matrix metadata is maintained in `docs/package-catalog.json` and rendered to the root README plus the docs reference page.", + "", + "| Group | Packages | Without package tests |", + "| --- | ---: | ---: |", + ); + for (const group of state.extensionMatrix.groups) { + const packages = state.extensionMatrix.packages.filter((pkg) => pkg.group === group); + lines.push( + `| ${group} | ${packages.length} | ${packages.filter((pkg) => !pkg.hasTests).length} |`, + ); + } + + lines.push(""); + return lines.join("\n"); +} + +function generateExtensionMatrixDocs(state: CatalogState): string { + const lines: string[] = [ + "---", + "title: Extension Matrix", + "description: Official Croco provider and adapter compatibility matrix.", + "---", + "", + "# Extension Matrix", + "", + "> Generated by `pnpm docs:catalog:write`. Do not edit this file by hand.", + "", + "This page lists Croco provider, integration, transport, and presentation adapter compatibility from `docs/package-catalog.json`. Required configuration, runtime support, package peer dependencies, maturity, and CI smoke status are intentionally separate so users can evaluate production readiness without treating a passing unit test as a maturity claim.", + "", + "Runtime columns: Node covers long-running server and CLI use, Lambda covers serverless functions, Workers covers Cloudflare Workers, and Frontend covers browser or SSR frontend integration.", + ]; + + appendExtensionMatrixTables(lines, state, "##"); + lines.push(""); return lines.join("\n"); } +function appendExtensionMatrixTables( + lines: string[], + state: CatalogState, + headingPrefix: "##" | "####", +): void { + for (const group of state.extensionMatrix.groups) { + const packages = state.extensionMatrix.packages.filter((pkg) => pkg.group === group); + if (packages.length === 0) { + continue; + } + + lines.push( + "", + `${headingPrefix} ${group}`, + "", + "| Package | Domain | Adapter | Node | Lambda | Workers | Frontend | Required env/config | Peer deps | Features | Maturity | CI smoke |", + "| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |", + ); + + for (const pkg of packages) { + const maturity = state.maturity.get(pkg.maturity)?.label ?? pkg.maturity; + lines.push( + `| \`${pkg.name}\` | ${pkg.extension.domain} | ${pkg.extension.adapter} | ${formatRuntimeSupport(pkg, "node")} | ${formatRuntimeSupport(pkg, "lambda")} | ${formatRuntimeSupport(pkg, "cloudflare-workers")} | ${formatRuntimeSupport(pkg, "browser")} | ${formatList(pkg.extension.requiredEnv)} | ${formatList(pkg.peerDependencies)} | ${formatList(pkg.extension.features)} | ${maturity} | ${formatSmokeStatus(pkg)} |`, + ); + } + } +} + function formatMissingPackages( packages: readonly PackageRecord[], allowedMissingPackages: ReadonlySet, @@ -624,6 +945,18 @@ function formatMissingPackages( }); } +function formatRuntimeSupport(pkg: ExtensionRecord, runtime: RuntimeKey): string { + return pkg.extension.runtimes.includes(runtime) ? "yes" : "-"; +} + +function formatList(values: readonly string[]): string { + return values.length > 0 ? values.join("
") : "-"; +} + +function formatSmokeStatus(pkg: PackageRecord): string { + return pkg.hasTests ? "entrypoint smoke + package tests" : "entrypoint smoke only"; +} + function writeReadmeCatalog(rootDir: string, generatedCatalog: string): void { const readmeFilePath = join(rootDir, readmePath); const readme = readRequiredFile(readmeFilePath); @@ -702,6 +1035,14 @@ function toShortPackageName(packageName: string): string { return packageName.replace(/^@croco\//, ""); } +function readDependencyKeys(value: unknown): readonly string[] { + if (!value || typeof value !== "object" || Array.isArray(value)) { + return []; + } + + return Object.keys(value).sort((left, right) => left.localeCompare(right)); +} + function readJsonFile(filePath: string): T { return JSON.parse(readRequiredFile(filePath)) as T; } diff --git a/scripts/tests/package-docs-check.spec.ts b/scripts/tests/package-docs-check.spec.ts index 875e0b1db..43d592782 100644 --- a/scripts/tests/package-docs-check.spec.ts +++ b/scripts/tests/package-docs-check.spec.ts @@ -34,12 +34,30 @@ describe("package-docs-check.mts", () => { const result = runScript(root, "--write"); const readme = readFileSync(join(root, "README.md"), "utf-8"); const report = readFileSync(join(root, "docs", "package-docs-report.md"), "utf-8"); + const matrix = readFileSync( + join( + root, + "packages", + "docs", + "src", + "content", + "docs", + "en", + "reference", + "extension-matrix.md", + ), + "utf-8", + ); expect(result.status).toBe(0); expect(readme).toContain(""); expect(readme).toContain("현재 카탈로그는 **2개 public package**"); + expect(readme).toContain("Extension & Adapter Matrix"); expect(readme).toContain("`@croco/alpha`"); expect(report).toContain("Missing generated API docs"); + expect(report).toContain("Extension Matrix"); + expect(matrix).toContain("title: Extension Matrix"); + expect(matrix).toContain("`@croco/alpha`"); }); it("fails check mode when the README catalog was not regenerated", () => { @@ -56,6 +74,7 @@ describe("package-docs-check.mts", () => { expect(result.status).toBe(1); expect(result.stdout).toContain("README.md package catalog drift detected"); + expect(result.stdout).toContain("reference/extension-matrix.md drift detected"); expect(result.stdout).toContain("docs/package-docs-report.md drift detected"); }); @@ -91,6 +110,28 @@ describe("package-docs-check.mts", () => { expect(result.stdout).toContain("new public packages missing README"); expect(result.stdout).toContain("new public packages missing API docs"); }); + + it("fails when an extension group package is missing matrix metadata", () => { + const root = createTempRoot(); + writePackage(root, "provider", { name: "@croco/provider" }); + writeCatalogMetadata(root, ["provider"], { + extensionGroups: ["Provider"], + extensionPackages: [], + groupName: "Provider", + }); + writeDocsBaseline(root, { + allowedMissingApiDocs: ["provider"], + allowedMissingReadme: [], + allowedMissingTests: [], + }); + + const result = runScript(root, "--write"); + + expect(result.status).toBe(1); + expect(result.stdout).toContain( + "extensionMatrix is missing metadata for Provider package provider", + ); + }); }); function createTempRoot(): string { @@ -140,11 +181,21 @@ function writePackage( writeFileSync(join(packageDir, "package.json"), `${JSON.stringify(pkg, null, 2)}\n`); } -function writeCatalogMetadata(root: string, packageNames: readonly string[]): void { +function writeCatalogMetadata( + root: string, + packageNames: readonly string[], + options: { + readonly extensionGroups?: readonly string[]; + readonly extensionPackages?: readonly string[]; + readonly groupName?: string; + } = {}, +): void { + const groupName = options.groupName ?? "Core"; + const extensionPackages = options.extensionPackages ?? packageNames; writeJson(join(root, "docs", "package-catalog.json"), { schemaVersion: 1, groups: { - Core: { + [groupName]: { description: "Fixture core packages", packages: packageNames, }, @@ -167,6 +218,21 @@ function writeCatalogMetadata(root: string, packageNames: readonly string[]): vo packages: [], }, }, + extensionMatrix: { + groups: options.extensionGroups ?? [groupName], + packages: Object.fromEntries( + extensionPackages.map((packageName) => [ + packageName, + { + adapter: "Fixture adapter", + domain: "Fixture", + features: ["Fixture feature"], + requiredEnv: ["none"], + runtimes: ["node"], + }, + ]), + ), + }, }); } From 61d60963b7ff2af22234497d81f92156a6431a88 Mon Sep 17 00:00:00 2001 From: kang-heewon Date: Mon, 15 Jun 2026 09:43:27 +0900 Subject: [PATCH 2/4] chore: add changeset [skip ci] --- .changeset/52b493b7.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/52b493b7.md diff --git a/.changeset/52b493b7.md b/.changeset/52b493b7.md new file mode 100644 index 000000000..411218944 --- /dev/null +++ b/.changeset/52b493b7.md @@ -0,0 +1,5 @@ +--- +'@croco/docs': patch +--- + +- fix: publish extension compatibility matrix From 645509bdf28e7fa3efac3c58c4e13b4d45b50333 Mon Sep 17 00:00:00 2001 From: kang-heewon Date: Mon, 15 Jun 2026 10:29:51 +0900 Subject: [PATCH 3/4] fix: resolve testing helpers without built problems-core output --- packages/testing/vitest.config.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 packages/testing/vitest.config.ts diff --git a/packages/testing/vitest.config.ts b/packages/testing/vitest.config.ts new file mode 100644 index 000000000..b1c5922dd --- /dev/null +++ b/packages/testing/vitest.config.ts @@ -0,0 +1,19 @@ +import { dirname, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; +import { defineConfig } from "vitest/config"; + +const currentDir = dirname(fileURLToPath(import.meta.url)); + +export default defineConfig({ + resolve: { + alias: { + "@croco/problems-core": resolve(currentDir, "../problems-core/src/index.ts"), + }, + }, + test: { + globals: true, + environment: "node", + include: ["src/**/*.test.ts", "src/**/*.spec.ts"], + exclude: ["**/node_modules/**", "**/dist/**"], + }, +}); From d3a02a27e662618a786daab88f1fff822a8ef03c Mon Sep 17 00:00:00 2001 From: kang-heewon Date: Mon, 15 Jun 2026 10:50:54 +0900 Subject: [PATCH 4/4] fix: clarify extension matrix validation signals --- README.md | 96 +++++++++---------- .../docs/en/reference/extension-matrix.md | 94 +++++++++--------- scripts/package-docs-check.mts | 14 +-- 3 files changed, 102 insertions(+), 102 deletions(-) diff --git a/README.md b/README.md index bf5d72855..2846089cc 100644 --- a/README.md +++ b/README.md @@ -361,67 +361,67 @@ Croco가 **완전한 SaaS 프레임워크**가 되기 위해 계획 중인 기 ### Extension & Adapter Matrix -> 이 섹션은 `docs/package-catalog.json`의 `extensionMatrix` metadata에서 생성됩니다. 성숙도와 CI smoke 상태는 별도 열로 표시합니다. +> 이 섹션은 `docs/package-catalog.json`의 `extensionMatrix` metadata에서 생성됩니다. 성숙도와 package test 존재 여부는 별도 열로 표시합니다. Runtime columns: Node는 장기 실행 서버/CLI, Lambda는 서버리스 함수, Workers는 Cloudflare Workers, Frontend는 browser/SSR frontend integration을 의미합니다. #### Provider -| Package | Domain | Adapter | Node | Lambda | Workers | Frontend | Required env/config | Peer deps | Features | Maturity | CI smoke | -| -------------------------------- | --------------- | ------------------------------------ | ---- | ------ | ------- | -------- | ----------------------------------------------------------------------------------- | -------------- | ----------------------------------------------------------------- | ------------ | -------------------------------- | -| `@croco/access-drizzle` | Access control | Drizzle repository | yes | yes | - | - | database connection supplied by app | drizzle-orm | permission checks
policy storage | 🔴 alpha/WIP | entrypoint smoke + package tests | -| `@croco/audit-drizzle` | Audit | Drizzle repository | yes | yes | - | - | database connection supplied by app | - | audit event persistence
tenant audit lookup | 🔴 alpha/WIP | entrypoint smoke + package tests | -| `@croco/auth-better-auth` | Auth | Better Auth + Drizzle provider | yes | yes | - | - | BETTER_AUTH_URL
BETTER_AUTH_SECRET
BETTER_AUTH_WEBHOOK_SECRET optional | - | session auth
webhooks
Drizzle schema | 🔴 alpha/WIP | entrypoint smoke + package tests | -| `@croco/auth-clerk` | Auth | Clerk Backend provider | yes | yes | - | - | CLERK_SECRET_KEY
CLERK_PUBLISHABLE_KEY optional
CLERK_WEBHOOK_SECRET optional | - | token auth
session management
organizations
webhooks | 🔴 alpha/WIP | entrypoint smoke + package tests | -| `@croco/auth-drizzle` | Auth | Drizzle store | yes | yes | - | - | database connection supplied by app | drizzle-orm | API key store
role registry | 🔴 alpha/WIP | entrypoint smoke + package tests | -| `@croco/batch-qstash` | Batch | QStash chunk executor | yes | yes | yes | - | QSTASH_TOKEN
public webhook URL | - | chunk scheduling
checkpoint resume
idempotent publish | 🔴 alpha/WIP | entrypoint smoke + package tests | -| `@croco/billing-polar` | Billing | Polar billing gateway | yes | yes | - | - | POLAR_ACCESS_TOKEN
POLAR_WEBHOOK_SECRET
POLAR_ORGANIZATION_ID optional | - | checkout
webhooks
subscription lifecycle
customer portal | 🟡 beta | entrypoint smoke + package tests | -| `@croco/customer-health-drizzle` | Customer health | Drizzle repository | yes | yes | - | - | database connection supplied by app | drizzle-orm | health score persistence
customer health lookup | 🔴 alpha/WIP | entrypoint smoke + package tests | -| `@croco/entitlements-drizzle` | Entitlements | Drizzle repository | yes | yes | - | - | database connection supplied by app | drizzle-orm | entitlement persistence
billing entitlement lookup | 🔴 alpha/WIP | entrypoint smoke + package tests | -| `@croco/execution-drizzle` | Execution | Drizzle execution store | yes | yes | - | - | database connection supplied by app | drizzle-orm | execution state persistence
retryable failure records | 🔴 alpha/WIP | entrypoint smoke + package tests | -| `@croco/invitation-drizzle` | Invitation | Drizzle repository | yes | yes | - | - | database connection supplied by app | - | invitation persistence
transaction-aware repository | 🔴 alpha/WIP | entrypoint smoke + package tests | -| `@croco/membership-drizzle` | Membership | Drizzle repository | yes | yes | - | - | database connection supplied by app | - | membership persistence
transaction-aware repository | 🔴 alpha/WIP | entrypoint smoke + package tests | -| `@croco/metering-drizzle` | Metering | Drizzle usage store | yes | yes | - | - | database connection supplied by app | - | usage persistence
quota lookup
migration scripts | 🔴 alpha/WIP | entrypoint smoke + package tests | -| `@croco/metering-upstash` | Metering | Upstash Redis client adapter | yes | yes | yes | - | UPSTASH_REDIS_REST_URL
UPSTASH_REDIS_REST_TOKEN | - | Redis command adapter
serverless usage storage | 🔴 alpha/WIP | entrypoint smoke + package tests | -| `@croco/metrics-billing` | Metrics | Billing metrics bridge | yes | yes | yes | - | none | - | billing event metrics
usage aggregation bridge | 🔴 alpha/WIP | entrypoint smoke + package tests | -| `@croco/notifications-resend` | Notifications | Resend email provider | yes | yes | - | - | RESEND_API_KEY
default from address | - | email send
template send
retry
idempotency key | 🔴 alpha/WIP | entrypoint smoke + package tests | -| `@croco/onboarding-drizzle` | Onboarding | Drizzle repository | yes | yes | - | - | database connection supplied by app | - | onboarding state persistence
step completion storage | 🔴 alpha/WIP | entrypoint smoke + package tests | -| `@croco/ratelimit-upstash` | Rate limiting | Upstash Redis rate-limit store | yes | yes | yes | - | UPSTASH_REDIS_REST_URL
UPSTASH_REDIS_REST_TOKEN | @upstash/redis | sliding window
token bucket
fixed window
Lua atomicity | 🔴 alpha/WIP | entrypoint smoke + package tests | -| `@croco/search-drizzle` | Search | Drizzle search index | yes | yes | - | - | database connection supplied by app | drizzle-orm | search document persistence
tenant-aware lookup | 🔴 alpha/WIP | entrypoint smoke + package tests | -| `@croco/search-meilisearch` | Search | Meilisearch engine | yes | yes | - | - | MEILISEARCH_HOST
MEILISEARCH_API_KEY | - | indexing
search
tenant tokens | 🔴 alpha/WIP | entrypoint smoke + package tests | -| `@croco/storage-cloudflare` | Storage | Cloudflare Images provider | yes | yes | - | - | CLOUDFLARE_ACCOUNT_ID
CLOUDFLARE_API_TOKEN
CLOUDFLARE_ACCOUNT_HASH | - | image upload
transform URLs
upload intents
signed URLs | 🔴 alpha/WIP | entrypoint smoke + package tests | -| `@croco/storage-cloudinary` | Storage | Cloudinary provider | yes | yes | - | - | CLOUDINARY_CLOUD_NAME
CLOUDINARY_API_KEY
CLOUDINARY_API_SECRET | - | file upload
transform URLs
upload intents
retry | 🔴 alpha/WIP | entrypoint smoke + package tests | -| `@croco/storage-r2` | Storage | Cloudflare R2 S3-compatible provider | yes | yes | - | - | R2_ACCOUNT_ID
R2_ACCESS_KEY_ID
R2_SECRET_ACCESS_KEY
R2_BUCKET | - | put/get/delete
signed URLs
stream reads
retry | 🟡 beta | entrypoint smoke + package tests | -| `@croco/tasks-qstash` | Tasks | QStash task runner | yes | yes | yes | - | QSTASH_TOKEN
destination webhook URL | - | task publish
delay override
custom headers | 🔴 alpha/WIP | entrypoint smoke + package tests | -| `@croco/triggers-qstash` | Triggers | QStash scheduler and webhook handler | yes | yes | yes | - | QSTASH_TOKEN
public webhook URL | - | schedule publish
webhook verification
trigger dispatch | 🔴 alpha/WIP | entrypoint smoke + package tests | +| Package | Domain | Adapter | Node | Lambda | Workers | Frontend | Required env/config | Peer deps | Features | Maturity | Package tests | +| -------------------------------- | --------------- | ------------------------------------ | ---- | ------ | ------- | -------- | ----------------------------------------------------------------------------------- | -------------- | ----------------------------------------------------------------- | ------------ | ----------------- | +| `@croco/access-drizzle` | Access control | Drizzle repository | yes | yes | - | - | database connection supplied by app | drizzle-orm | permission checks
policy storage | 🔴 alpha/WIP | has package tests | +| `@croco/audit-drizzle` | Audit | Drizzle repository | yes | yes | - | - | database connection supplied by app | - | audit event persistence
tenant audit lookup | 🔴 alpha/WIP | has package tests | +| `@croco/auth-better-auth` | Auth | Better Auth + Drizzle provider | yes | yes | - | - | BETTER_AUTH_URL
BETTER_AUTH_SECRET
BETTER_AUTH_WEBHOOK_SECRET optional | - | session auth
webhooks
Drizzle schema | 🔴 alpha/WIP | has package tests | +| `@croco/auth-clerk` | Auth | Clerk Backend provider | yes | yes | - | - | CLERK_SECRET_KEY
CLERK_PUBLISHABLE_KEY optional
CLERK_WEBHOOK_SECRET optional | - | token auth
session management
organizations
webhooks | 🔴 alpha/WIP | has package tests | +| `@croco/auth-drizzle` | Auth | Drizzle store | yes | yes | - | - | database connection supplied by app | drizzle-orm | API key store
role registry | 🔴 alpha/WIP | has package tests | +| `@croco/batch-qstash` | Batch | QStash chunk executor | yes | yes | yes | - | QSTASH_TOKEN
public webhook URL | - | chunk scheduling
checkpoint resume
idempotent publish | 🔴 alpha/WIP | has package tests | +| `@croco/billing-polar` | Billing | Polar billing gateway | yes | yes | - | - | POLAR_ACCESS_TOKEN
POLAR_WEBHOOK_SECRET
POLAR_ORGANIZATION_ID optional | - | checkout
webhooks
subscription lifecycle
customer portal | 🟡 beta | has package tests | +| `@croco/customer-health-drizzle` | Customer health | Drizzle repository | yes | yes | - | - | database connection supplied by app | drizzle-orm | health score persistence
customer health lookup | 🔴 alpha/WIP | has package tests | +| `@croco/entitlements-drizzle` | Entitlements | Drizzle repository | yes | yes | - | - | database connection supplied by app | drizzle-orm | entitlement persistence
billing entitlement lookup | 🔴 alpha/WIP | has package tests | +| `@croco/execution-drizzle` | Execution | Drizzle execution store | yes | yes | - | - | database connection supplied by app | drizzle-orm | execution state persistence
retryable failure records | 🔴 alpha/WIP | has package tests | +| `@croco/invitation-drizzle` | Invitation | Drizzle repository | yes | yes | - | - | database connection supplied by app | - | invitation persistence
transaction-aware repository | 🔴 alpha/WIP | has package tests | +| `@croco/membership-drizzle` | Membership | Drizzle repository | yes | yes | - | - | database connection supplied by app | - | membership persistence
transaction-aware repository | 🔴 alpha/WIP | has package tests | +| `@croco/metering-drizzle` | Metering | Drizzle usage store | yes | yes | - | - | database connection supplied by app | - | usage persistence
quota lookup
migration scripts | 🔴 alpha/WIP | has package tests | +| `@croco/metering-upstash` | Metering | Upstash Redis client adapter | yes | yes | yes | - | UPSTASH_REDIS_REST_URL
UPSTASH_REDIS_REST_TOKEN | - | Redis command adapter
serverless usage storage | 🔴 alpha/WIP | has package tests | +| `@croco/metrics-billing` | Metrics | Billing metrics bridge | yes | yes | yes | - | none | - | billing event metrics
usage aggregation bridge | 🔴 alpha/WIP | has package tests | +| `@croco/notifications-resend` | Notifications | Resend email provider | yes | yes | - | - | RESEND_API_KEY
default from address | - | email send
template send
retry
idempotency key | 🔴 alpha/WIP | has package tests | +| `@croco/onboarding-drizzle` | Onboarding | Drizzle repository | yes | yes | - | - | database connection supplied by app | - | onboarding state persistence
step completion storage | 🔴 alpha/WIP | has package tests | +| `@croco/ratelimit-upstash` | Rate limiting | Upstash Redis rate-limit store | yes | yes | yes | - | UPSTASH_REDIS_REST_URL
UPSTASH_REDIS_REST_TOKEN | @upstash/redis | sliding window
token bucket
fixed window
Lua atomicity | 🔴 alpha/WIP | has package tests | +| `@croco/search-drizzle` | Search | Drizzle search index | yes | yes | - | - | database connection supplied by app | drizzle-orm | search document persistence
tenant-aware lookup | 🔴 alpha/WIP | has package tests | +| `@croco/search-meilisearch` | Search | Meilisearch engine | yes | yes | - | - | MEILISEARCH_HOST
MEILISEARCH_API_KEY | - | indexing
search
tenant tokens | 🔴 alpha/WIP | has package tests | +| `@croco/storage-cloudflare` | Storage | Cloudflare Images provider | yes | yes | - | - | CLOUDFLARE_ACCOUNT_ID
CLOUDFLARE_API_TOKEN
CLOUDFLARE_ACCOUNT_HASH | - | image upload
transform URLs
upload intents
signed URLs | 🔴 alpha/WIP | has package tests | +| `@croco/storage-cloudinary` | Storage | Cloudinary provider | yes | yes | - | - | CLOUDINARY_CLOUD_NAME
CLOUDINARY_API_KEY
CLOUDINARY_API_SECRET | - | file upload
transform URLs
upload intents
retry | 🔴 alpha/WIP | has package tests | +| `@croco/storage-r2` | Storage | Cloudflare R2 S3-compatible provider | yes | yes | - | - | R2_ACCOUNT_ID
R2_ACCESS_KEY_ID
R2_SECRET_ACCESS_KEY
R2_BUCKET | - | put/get/delete
signed URLs
stream reads
retry | 🟡 beta | has package tests | +| `@croco/tasks-qstash` | Tasks | QStash task runner | yes | yes | yes | - | QSTASH_TOKEN
destination webhook URL | - | task publish
delay override
custom headers | 🔴 alpha/WIP | has package tests | +| `@croco/triggers-qstash` | Triggers | QStash scheduler and webhook handler | yes | yes | yes | - | QSTASH_TOKEN
public webhook URL | - | schedule publish
webhook verification
trigger dispatch | 🔴 alpha/WIP | has package tests | #### Integration -| Package | Domain | Adapter | Node | Lambda | Workers | Frontend | Required env/config | Peer deps | Features | Maturity | CI smoke | -| ----------------------------- | ------------- | ------------------------------ | ---- | ------ | ------- | -------- | ----------------------------------------------------------------------------------------------- | --------- | ------------------------------------------------------------------ | ------------------- | -------------------------------- | -| `@croco/analytics-posthog` | Analytics | PostHog analytics provider | yes | yes | - | - | POSTHOG_API_KEY
POSTHOG_HOST optional | - | event capture
user/group analytics | 🔴 alpha/WIP | entrypoint smoke + package tests | -| `@croco/features-posthog` | Feature flags | PostHog feature provider | yes | yes | - | - | POSTHOG_API_KEY
POSTHOG_HOST optional | - | feature flag lookup
PostHog client reuse | 🟡 beta | entrypoint smoke + package tests | -| `@croco/integrations-posthog` | PostHog | Shared PostHog client | yes | yes | - | - | POSTHOG_API_KEY
POSTHOG_HOST optional | - | client lifecycle
capture flush
diagnostics | 🟡 beta | entrypoint smoke + package tests | -| `@croco/telemetry-api` | Telemetry | OpenTelemetry application API | yes | yes | yes | - | none | - | Trace decorator
withSpan
recordError
trace context lookup | 🟢 production-ready | entrypoint smoke + package tests | -| `@croco/telemetry-sdk-node` | Telemetry | OpenTelemetry Node SDK runtime | yes | yes | - | - | OTEL_EXPORTER_OTLP_TRACES_ENDPOINT or OTEL_EXPORTER_OTLP_ENDPOINT
TELEMETRY_ENABLED optional | - | SDK init
Lambda preset
OTLP export
forceFlush | 🟢 production-ready | entrypoint smoke + package tests | +| Package | Domain | Adapter | Node | Lambda | Workers | Frontend | Required env/config | Peer deps | Features | Maturity | Package tests | +| ----------------------------- | ------------- | ------------------------------ | ---- | ------ | ------- | -------- | ----------------------------------------------------------------------------------------------- | --------- | ------------------------------------------------------------------ | ------------------- | ----------------- | +| `@croco/analytics-posthog` | Analytics | PostHog analytics provider | yes | yes | - | - | POSTHOG_API_KEY
POSTHOG_HOST optional | - | event capture
user/group analytics | 🔴 alpha/WIP | has package tests | +| `@croco/features-posthog` | Feature flags | PostHog feature provider | yes | yes | - | - | POSTHOG_API_KEY
POSTHOG_HOST optional | - | feature flag lookup
PostHog client reuse | 🟡 beta | has package tests | +| `@croco/integrations-posthog` | PostHog | Shared PostHog client | yes | yes | - | - | POSTHOG_API_KEY
POSTHOG_HOST optional | - | client lifecycle
capture flush
diagnostics | 🟡 beta | has package tests | +| `@croco/telemetry-api` | Telemetry | OpenTelemetry application API | yes | yes | yes | - | none | - | Trace decorator
withSpan
recordError
trace context lookup | 🟢 production-ready | has package tests | +| `@croco/telemetry-sdk-node` | Telemetry | OpenTelemetry Node SDK runtime | yes | yes | - | - | OTEL_EXPORTER_OTLP_TRACES_ENDPOINT or OTEL_EXPORTER_OTLP_ENDPOINT
TELEMETRY_ENABLED optional | - | SDK init
Lambda preset
OTLP export
forceFlush | 🟢 production-ready | has package tests | #### Transport -| Package | Domain | Adapter | Node | Lambda | Workers | Frontend | Required env/config | Peer deps | Features | Maturity | CI smoke | -| -------------------------------------- | ----------------- | -------------------------- | ---- | ------ | ------- | -------- | --------------------------------------------------------------------------- | --------- | ------------------------------------------------------------------------------ | ------------------- | -------------------------------- | -| `@croco/transports-graphql` | GraphQL transport | GraphQL Yoga transport | yes | yes | - | - | none | - | GraphQL server
resolver execution
Problem mapping | 🟡 beta | entrypoint smoke + package tests | -| `@croco/transports-cloudflare-workers` | HTTP transport | Cloudflare Workers adapter | - | - | yes | - | Cloudflare Worker env object supplied by platform | - | Worker fetch adapter
request context bridge | 🔴 alpha/WIP | entrypoint smoke + package tests | -| `@croco/transports-http` | HTTP transport | Hono HTTP/Lambda transport | yes | yes | - | - | CROCO_DIAGNOSTICS_TOKEN optional
CROCO_HTTP_SECURITY_VALIDATION optional | - | REST route execution
Lambda adapter
operational endpoints
diagnostics | 🟢 production-ready | entrypoint smoke + package tests | +| Package | Domain | Adapter | Node | Lambda | Workers | Frontend | Required env/config | Peer deps | Features | Maturity | Package tests | +| -------------------------------------- | ----------------- | -------------------------- | ---- | ------ | ------- | -------- | --------------------------------------------------------------------------- | --------- | ------------------------------------------------------------------------------ | ------------------- | ----------------- | +| `@croco/transports-graphql` | GraphQL transport | GraphQL Yoga transport | yes | yes | - | - | none | - | GraphQL server
resolver execution
Problem mapping | 🟡 beta | has package tests | +| `@croco/transports-cloudflare-workers` | HTTP transport | Cloudflare Workers adapter | - | - | yes | - | Cloudflare Worker env object supplied by platform | - | Worker fetch adapter
request context bridge | 🔴 alpha/WIP | has package tests | +| `@croco/transports-http` | HTTP transport | Hono HTTP/Lambda transport | yes | yes | - | - | CROCO_DIAGNOSTICS_TOKEN optional
CROCO_HTTP_SECURITY_VALIDATION optional | - | REST route execution
Lambda adapter
operational endpoints
diagnostics | 🟢 production-ready | has package tests | #### Presentation -| Package | Domain | Adapter | Node | Lambda | Workers | Frontend | Required env/config | Peer deps | Features | Maturity | CI smoke | -| ---------------------------- | ------------------- | ----------------------------------- | ---- | ------ | ------- | -------- | ------------------------------------------------------ | -------------------------------------------- | --------------------------------------------------------------------- | ------------ | -------------------------------- | -| `@croco/frontend-react` | Frontend React | React integration helpers | yes | - | - | yes | none | @croco/meta-vite
react
react-dom | React bindings
meta-vite integration | 🔴 alpha/WIP | entrypoint smoke + package tests | -| `@croco/meta-vite` | Frontend routing | Meta Vite runtime | yes | yes | yes | yes | optional Redis-compatible ISR adapter config | ioredis
react
react-dom
vite
zod | route registry
server actions
SSR/RSC streaming
ISR adapters | 🟡 beta | entrypoint smoke + package tests | -| `@croco/frontend-cloudflare` | Frontend SSR | Cloudflare SSR handler | - | - | yes | - | API_WORKER binding optional
ASSETS binding optional | - | SSR fetch handler
service binding routing
asset fallback | 🔴 alpha/WIP | entrypoint smoke + package tests | -| `@croco/frontend-vite` | Frontend Vite | Vite integration helpers | yes | - | yes | yes | none | @cloudflare/vite-plugin
vite | Vite config helpers
Cloudflare Vite compatibility | 🔴 alpha/WIP | entrypoint smoke + package tests | -| `@croco/presentation-preset` | Presentation preset | Backend/frontend preset composition | yes | yes | yes | yes | none | - | preset composition
contract wiring
generated app support | 🟡 beta | entrypoint smoke + package tests | +| Package | Domain | Adapter | Node | Lambda | Workers | Frontend | Required env/config | Peer deps | Features | Maturity | Package tests | +| ---------------------------- | ------------------- | ----------------------------------- | ---- | ------ | ------- | -------- | ------------------------------------------------------ | -------------------------------------------- | --------------------------------------------------------------------- | ------------ | ----------------- | +| `@croco/frontend-react` | Frontend React | React integration helpers | yes | - | - | yes | none | @croco/meta-vite
react
react-dom | React bindings
meta-vite integration | 🔴 alpha/WIP | has package tests | +| `@croco/meta-vite` | Frontend routing | Meta Vite runtime | yes | yes | yes | yes | optional Redis-compatible ISR adapter config | ioredis
react
react-dom
vite
zod | route registry
server actions
SSR/RSC streaming
ISR adapters | 🟡 beta | has package tests | +| `@croco/frontend-cloudflare` | Frontend SSR | Cloudflare SSR handler | - | - | yes | - | API_WORKER binding optional
ASSETS binding optional | - | SSR fetch handler
service binding routing
asset fallback | 🔴 alpha/WIP | has package tests | +| `@croco/frontend-vite` | Frontend Vite | Vite integration helpers | yes | - | yes | yes | none | @cloudflare/vite-plugin
vite | Vite config helpers
Cloudflare Vite compatibility | 🔴 alpha/WIP | has package tests | +| `@croco/presentation-preset` | Presentation preset | Backend/frontend preset composition | yes | yes | yes | yes | none | - | preset composition
contract wiring
generated app support | 🟡 beta | has package tests | ### 🟢 production-ready @@ -537,7 +537,7 @@ Runtime columns: Node는 장기 실행 서버/CLI, Lambda는 서버리스 함수 ### Documentation Gate -- `pnpm docs:catalog:check`는 README 카탈로그와 문서 커버리지 리포트 drift를 검증합니다. +- `pnpm docs:catalog:check`는 README 카탈로그, extension matrix reference 문서, 문서 커버리지 리포트 drift를 검증합니다. - 신규 public package는 `docs/package-catalog.json`에 그룹/성숙도 metadata가 있어야 합니다. - 신규 public package의 README, API docs, tests 누락은 `docs/package-docs-baseline.json`에 없는 한 실패합니다. diff --git a/packages/docs/src/content/docs/en/reference/extension-matrix.md b/packages/docs/src/content/docs/en/reference/extension-matrix.md index f17379f03..96ea119dd 100644 --- a/packages/docs/src/content/docs/en/reference/extension-matrix.md +++ b/packages/docs/src/content/docs/en/reference/extension-matrix.md @@ -7,64 +7,64 @@ description: Official Croco provider and adapter compatibility matrix. > Generated by `pnpm docs:catalog:write`. Do not edit this file by hand. -This page lists Croco provider, integration, transport, and presentation adapter compatibility from `docs/package-catalog.json`. Required configuration, runtime support, package peer dependencies, maturity, and CI smoke status are intentionally separate so users can evaluate production readiness without treating a passing unit test as a maturity claim. +This page lists Croco provider, integration, transport, and presentation adapter compatibility from `docs/package-catalog.json`. Required configuration, runtime support, package peer dependencies, maturity, and package test presence are intentionally separate so users can evaluate production readiness without treating a passing unit test as a maturity claim. Runtime columns: Node covers long-running server and CLI use, Lambda covers serverless functions, Workers covers Cloudflare Workers, and Frontend covers browser or SSR frontend integration. ## Provider -| Package | Domain | Adapter | Node | Lambda | Workers | Frontend | Required env/config | Peer deps | Features | Maturity | CI smoke | -| -------------------------------- | --------------- | ------------------------------------ | ---- | ------ | ------- | -------- | ----------------------------------------------------------------------------------- | -------------- | ----------------------------------------------------------------- | ------------ | -------------------------------- | -| `@croco/access-drizzle` | Access control | Drizzle repository | yes | yes | - | - | database connection supplied by app | drizzle-orm | permission checks
policy storage | 🔴 alpha/WIP | entrypoint smoke + package tests | -| `@croco/audit-drizzle` | Audit | Drizzle repository | yes | yes | - | - | database connection supplied by app | - | audit event persistence
tenant audit lookup | 🔴 alpha/WIP | entrypoint smoke + package tests | -| `@croco/auth-better-auth` | Auth | Better Auth + Drizzle provider | yes | yes | - | - | BETTER_AUTH_URL
BETTER_AUTH_SECRET
BETTER_AUTH_WEBHOOK_SECRET optional | - | session auth
webhooks
Drizzle schema | 🔴 alpha/WIP | entrypoint smoke + package tests | -| `@croco/auth-clerk` | Auth | Clerk Backend provider | yes | yes | - | - | CLERK_SECRET_KEY
CLERK_PUBLISHABLE_KEY optional
CLERK_WEBHOOK_SECRET optional | - | token auth
session management
organizations
webhooks | 🔴 alpha/WIP | entrypoint smoke + package tests | -| `@croco/auth-drizzle` | Auth | Drizzle store | yes | yes | - | - | database connection supplied by app | drizzle-orm | API key store
role registry | 🔴 alpha/WIP | entrypoint smoke + package tests | -| `@croco/batch-qstash` | Batch | QStash chunk executor | yes | yes | yes | - | QSTASH_TOKEN
public webhook URL | - | chunk scheduling
checkpoint resume
idempotent publish | 🔴 alpha/WIP | entrypoint smoke + package tests | -| `@croco/billing-polar` | Billing | Polar billing gateway | yes | yes | - | - | POLAR_ACCESS_TOKEN
POLAR_WEBHOOK_SECRET
POLAR_ORGANIZATION_ID optional | - | checkout
webhooks
subscription lifecycle
customer portal | 🟡 beta | entrypoint smoke + package tests | -| `@croco/customer-health-drizzle` | Customer health | Drizzle repository | yes | yes | - | - | database connection supplied by app | drizzle-orm | health score persistence
customer health lookup | 🔴 alpha/WIP | entrypoint smoke + package tests | -| `@croco/entitlements-drizzle` | Entitlements | Drizzle repository | yes | yes | - | - | database connection supplied by app | drizzle-orm | entitlement persistence
billing entitlement lookup | 🔴 alpha/WIP | entrypoint smoke + package tests | -| `@croco/execution-drizzle` | Execution | Drizzle execution store | yes | yes | - | - | database connection supplied by app | drizzle-orm | execution state persistence
retryable failure records | 🔴 alpha/WIP | entrypoint smoke + package tests | -| `@croco/invitation-drizzle` | Invitation | Drizzle repository | yes | yes | - | - | database connection supplied by app | - | invitation persistence
transaction-aware repository | 🔴 alpha/WIP | entrypoint smoke + package tests | -| `@croco/membership-drizzle` | Membership | Drizzle repository | yes | yes | - | - | database connection supplied by app | - | membership persistence
transaction-aware repository | 🔴 alpha/WIP | entrypoint smoke + package tests | -| `@croco/metering-drizzle` | Metering | Drizzle usage store | yes | yes | - | - | database connection supplied by app | - | usage persistence
quota lookup
migration scripts | 🔴 alpha/WIP | entrypoint smoke + package tests | -| `@croco/metering-upstash` | Metering | Upstash Redis client adapter | yes | yes | yes | - | UPSTASH_REDIS_REST_URL
UPSTASH_REDIS_REST_TOKEN | - | Redis command adapter
serverless usage storage | 🔴 alpha/WIP | entrypoint smoke + package tests | -| `@croco/metrics-billing` | Metrics | Billing metrics bridge | yes | yes | yes | - | none | - | billing event metrics
usage aggregation bridge | 🔴 alpha/WIP | entrypoint smoke + package tests | -| `@croco/notifications-resend` | Notifications | Resend email provider | yes | yes | - | - | RESEND_API_KEY
default from address | - | email send
template send
retry
idempotency key | 🔴 alpha/WIP | entrypoint smoke + package tests | -| `@croco/onboarding-drizzle` | Onboarding | Drizzle repository | yes | yes | - | - | database connection supplied by app | - | onboarding state persistence
step completion storage | 🔴 alpha/WIP | entrypoint smoke + package tests | -| `@croco/ratelimit-upstash` | Rate limiting | Upstash Redis rate-limit store | yes | yes | yes | - | UPSTASH_REDIS_REST_URL
UPSTASH_REDIS_REST_TOKEN | @upstash/redis | sliding window
token bucket
fixed window
Lua atomicity | 🔴 alpha/WIP | entrypoint smoke + package tests | -| `@croco/search-drizzle` | Search | Drizzle search index | yes | yes | - | - | database connection supplied by app | drizzle-orm | search document persistence
tenant-aware lookup | 🔴 alpha/WIP | entrypoint smoke + package tests | -| `@croco/search-meilisearch` | Search | Meilisearch engine | yes | yes | - | - | MEILISEARCH_HOST
MEILISEARCH_API_KEY | - | indexing
search
tenant tokens | 🔴 alpha/WIP | entrypoint smoke + package tests | -| `@croco/storage-cloudflare` | Storage | Cloudflare Images provider | yes | yes | - | - | CLOUDFLARE_ACCOUNT_ID
CLOUDFLARE_API_TOKEN
CLOUDFLARE_ACCOUNT_HASH | - | image upload
transform URLs
upload intents
signed URLs | 🔴 alpha/WIP | entrypoint smoke + package tests | -| `@croco/storage-cloudinary` | Storage | Cloudinary provider | yes | yes | - | - | CLOUDINARY_CLOUD_NAME
CLOUDINARY_API_KEY
CLOUDINARY_API_SECRET | - | file upload
transform URLs
upload intents
retry | 🔴 alpha/WIP | entrypoint smoke + package tests | -| `@croco/storage-r2` | Storage | Cloudflare R2 S3-compatible provider | yes | yes | - | - | R2_ACCOUNT_ID
R2_ACCESS_KEY_ID
R2_SECRET_ACCESS_KEY
R2_BUCKET | - | put/get/delete
signed URLs
stream reads
retry | 🟡 beta | entrypoint smoke + package tests | -| `@croco/tasks-qstash` | Tasks | QStash task runner | yes | yes | yes | - | QSTASH_TOKEN
destination webhook URL | - | task publish
delay override
custom headers | 🔴 alpha/WIP | entrypoint smoke + package tests | -| `@croco/triggers-qstash` | Triggers | QStash scheduler and webhook handler | yes | yes | yes | - | QSTASH_TOKEN
public webhook URL | - | schedule publish
webhook verification
trigger dispatch | 🔴 alpha/WIP | entrypoint smoke + package tests | +| Package | Domain | Adapter | Node | Lambda | Workers | Frontend | Required env/config | Peer deps | Features | Maturity | Package tests | +| -------------------------------- | --------------- | ------------------------------------ | ---- | ------ | ------- | -------- | ----------------------------------------------------------------------------------- | -------------- | ----------------------------------------------------------------- | ------------ | ----------------- | +| `@croco/access-drizzle` | Access control | Drizzle repository | yes | yes | - | - | database connection supplied by app | drizzle-orm | permission checks
policy storage | 🔴 alpha/WIP | has package tests | +| `@croco/audit-drizzle` | Audit | Drizzle repository | yes | yes | - | - | database connection supplied by app | - | audit event persistence
tenant audit lookup | 🔴 alpha/WIP | has package tests | +| `@croco/auth-better-auth` | Auth | Better Auth + Drizzle provider | yes | yes | - | - | BETTER_AUTH_URL
BETTER_AUTH_SECRET
BETTER_AUTH_WEBHOOK_SECRET optional | - | session auth
webhooks
Drizzle schema | 🔴 alpha/WIP | has package tests | +| `@croco/auth-clerk` | Auth | Clerk Backend provider | yes | yes | - | - | CLERK_SECRET_KEY
CLERK_PUBLISHABLE_KEY optional
CLERK_WEBHOOK_SECRET optional | - | token auth
session management
organizations
webhooks | 🔴 alpha/WIP | has package tests | +| `@croco/auth-drizzle` | Auth | Drizzle store | yes | yes | - | - | database connection supplied by app | drizzle-orm | API key store
role registry | 🔴 alpha/WIP | has package tests | +| `@croco/batch-qstash` | Batch | QStash chunk executor | yes | yes | yes | - | QSTASH_TOKEN
public webhook URL | - | chunk scheduling
checkpoint resume
idempotent publish | 🔴 alpha/WIP | has package tests | +| `@croco/billing-polar` | Billing | Polar billing gateway | yes | yes | - | - | POLAR_ACCESS_TOKEN
POLAR_WEBHOOK_SECRET
POLAR_ORGANIZATION_ID optional | - | checkout
webhooks
subscription lifecycle
customer portal | 🟡 beta | has package tests | +| `@croco/customer-health-drizzle` | Customer health | Drizzle repository | yes | yes | - | - | database connection supplied by app | drizzle-orm | health score persistence
customer health lookup | 🔴 alpha/WIP | has package tests | +| `@croco/entitlements-drizzle` | Entitlements | Drizzle repository | yes | yes | - | - | database connection supplied by app | drizzle-orm | entitlement persistence
billing entitlement lookup | 🔴 alpha/WIP | has package tests | +| `@croco/execution-drizzle` | Execution | Drizzle execution store | yes | yes | - | - | database connection supplied by app | drizzle-orm | execution state persistence
retryable failure records | 🔴 alpha/WIP | has package tests | +| `@croco/invitation-drizzle` | Invitation | Drizzle repository | yes | yes | - | - | database connection supplied by app | - | invitation persistence
transaction-aware repository | 🔴 alpha/WIP | has package tests | +| `@croco/membership-drizzle` | Membership | Drizzle repository | yes | yes | - | - | database connection supplied by app | - | membership persistence
transaction-aware repository | 🔴 alpha/WIP | has package tests | +| `@croco/metering-drizzle` | Metering | Drizzle usage store | yes | yes | - | - | database connection supplied by app | - | usage persistence
quota lookup
migration scripts | 🔴 alpha/WIP | has package tests | +| `@croco/metering-upstash` | Metering | Upstash Redis client adapter | yes | yes | yes | - | UPSTASH_REDIS_REST_URL
UPSTASH_REDIS_REST_TOKEN | - | Redis command adapter
serverless usage storage | 🔴 alpha/WIP | has package tests | +| `@croco/metrics-billing` | Metrics | Billing metrics bridge | yes | yes | yes | - | none | - | billing event metrics
usage aggregation bridge | 🔴 alpha/WIP | has package tests | +| `@croco/notifications-resend` | Notifications | Resend email provider | yes | yes | - | - | RESEND_API_KEY
default from address | - | email send
template send
retry
idempotency key | 🔴 alpha/WIP | has package tests | +| `@croco/onboarding-drizzle` | Onboarding | Drizzle repository | yes | yes | - | - | database connection supplied by app | - | onboarding state persistence
step completion storage | 🔴 alpha/WIP | has package tests | +| `@croco/ratelimit-upstash` | Rate limiting | Upstash Redis rate-limit store | yes | yes | yes | - | UPSTASH_REDIS_REST_URL
UPSTASH_REDIS_REST_TOKEN | @upstash/redis | sliding window
token bucket
fixed window
Lua atomicity | 🔴 alpha/WIP | has package tests | +| `@croco/search-drizzle` | Search | Drizzle search index | yes | yes | - | - | database connection supplied by app | drizzle-orm | search document persistence
tenant-aware lookup | 🔴 alpha/WIP | has package tests | +| `@croco/search-meilisearch` | Search | Meilisearch engine | yes | yes | - | - | MEILISEARCH_HOST
MEILISEARCH_API_KEY | - | indexing
search
tenant tokens | 🔴 alpha/WIP | has package tests | +| `@croco/storage-cloudflare` | Storage | Cloudflare Images provider | yes | yes | - | - | CLOUDFLARE_ACCOUNT_ID
CLOUDFLARE_API_TOKEN
CLOUDFLARE_ACCOUNT_HASH | - | image upload
transform URLs
upload intents
signed URLs | 🔴 alpha/WIP | has package tests | +| `@croco/storage-cloudinary` | Storage | Cloudinary provider | yes | yes | - | - | CLOUDINARY_CLOUD_NAME
CLOUDINARY_API_KEY
CLOUDINARY_API_SECRET | - | file upload
transform URLs
upload intents
retry | 🔴 alpha/WIP | has package tests | +| `@croco/storage-r2` | Storage | Cloudflare R2 S3-compatible provider | yes | yes | - | - | R2_ACCOUNT_ID
R2_ACCESS_KEY_ID
R2_SECRET_ACCESS_KEY
R2_BUCKET | - | put/get/delete
signed URLs
stream reads
retry | 🟡 beta | has package tests | +| `@croco/tasks-qstash` | Tasks | QStash task runner | yes | yes | yes | - | QSTASH_TOKEN
destination webhook URL | - | task publish
delay override
custom headers | 🔴 alpha/WIP | has package tests | +| `@croco/triggers-qstash` | Triggers | QStash scheduler and webhook handler | yes | yes | yes | - | QSTASH_TOKEN
public webhook URL | - | schedule publish
webhook verification
trigger dispatch | 🔴 alpha/WIP | has package tests | ## Integration -| Package | Domain | Adapter | Node | Lambda | Workers | Frontend | Required env/config | Peer deps | Features | Maturity | CI smoke | -| ----------------------------- | ------------- | ------------------------------ | ---- | ------ | ------- | -------- | ----------------------------------------------------------------------------------------------- | --------- | ------------------------------------------------------------------ | ------------------- | -------------------------------- | -| `@croco/analytics-posthog` | Analytics | PostHog analytics provider | yes | yes | - | - | POSTHOG_API_KEY
POSTHOG_HOST optional | - | event capture
user/group analytics | 🔴 alpha/WIP | entrypoint smoke + package tests | -| `@croco/features-posthog` | Feature flags | PostHog feature provider | yes | yes | - | - | POSTHOG_API_KEY
POSTHOG_HOST optional | - | feature flag lookup
PostHog client reuse | 🟡 beta | entrypoint smoke + package tests | -| `@croco/integrations-posthog` | PostHog | Shared PostHog client | yes | yes | - | - | POSTHOG_API_KEY
POSTHOG_HOST optional | - | client lifecycle
capture flush
diagnostics | 🟡 beta | entrypoint smoke + package tests | -| `@croco/telemetry-api` | Telemetry | OpenTelemetry application API | yes | yes | yes | - | none | - | Trace decorator
withSpan
recordError
trace context lookup | 🟢 production-ready | entrypoint smoke + package tests | -| `@croco/telemetry-sdk-node` | Telemetry | OpenTelemetry Node SDK runtime | yes | yes | - | - | OTEL_EXPORTER_OTLP_TRACES_ENDPOINT or OTEL_EXPORTER_OTLP_ENDPOINT
TELEMETRY_ENABLED optional | - | SDK init
Lambda preset
OTLP export
forceFlush | 🟢 production-ready | entrypoint smoke + package tests | +| Package | Domain | Adapter | Node | Lambda | Workers | Frontend | Required env/config | Peer deps | Features | Maturity | Package tests | +| ----------------------------- | ------------- | ------------------------------ | ---- | ------ | ------- | -------- | ----------------------------------------------------------------------------------------------- | --------- | ------------------------------------------------------------------ | ------------------- | ----------------- | +| `@croco/analytics-posthog` | Analytics | PostHog analytics provider | yes | yes | - | - | POSTHOG_API_KEY
POSTHOG_HOST optional | - | event capture
user/group analytics | 🔴 alpha/WIP | has package tests | +| `@croco/features-posthog` | Feature flags | PostHog feature provider | yes | yes | - | - | POSTHOG_API_KEY
POSTHOG_HOST optional | - | feature flag lookup
PostHog client reuse | 🟡 beta | has package tests | +| `@croco/integrations-posthog` | PostHog | Shared PostHog client | yes | yes | - | - | POSTHOG_API_KEY
POSTHOG_HOST optional | - | client lifecycle
capture flush
diagnostics | 🟡 beta | has package tests | +| `@croco/telemetry-api` | Telemetry | OpenTelemetry application API | yes | yes | yes | - | none | - | Trace decorator
withSpan
recordError
trace context lookup | 🟢 production-ready | has package tests | +| `@croco/telemetry-sdk-node` | Telemetry | OpenTelemetry Node SDK runtime | yes | yes | - | - | OTEL_EXPORTER_OTLP_TRACES_ENDPOINT or OTEL_EXPORTER_OTLP_ENDPOINT
TELEMETRY_ENABLED optional | - | SDK init
Lambda preset
OTLP export
forceFlush | 🟢 production-ready | has package tests | ## Transport -| Package | Domain | Adapter | Node | Lambda | Workers | Frontend | Required env/config | Peer deps | Features | Maturity | CI smoke | -| -------------------------------------- | ----------------- | -------------------------- | ---- | ------ | ------- | -------- | --------------------------------------------------------------------------- | --------- | ------------------------------------------------------------------------------ | ------------------- | -------------------------------- | -| `@croco/transports-graphql` | GraphQL transport | GraphQL Yoga transport | yes | yes | - | - | none | - | GraphQL server
resolver execution
Problem mapping | 🟡 beta | entrypoint smoke + package tests | -| `@croco/transports-cloudflare-workers` | HTTP transport | Cloudflare Workers adapter | - | - | yes | - | Cloudflare Worker env object supplied by platform | - | Worker fetch adapter
request context bridge | 🔴 alpha/WIP | entrypoint smoke + package tests | -| `@croco/transports-http` | HTTP transport | Hono HTTP/Lambda transport | yes | yes | - | - | CROCO_DIAGNOSTICS_TOKEN optional
CROCO_HTTP_SECURITY_VALIDATION optional | - | REST route execution
Lambda adapter
operational endpoints
diagnostics | 🟢 production-ready | entrypoint smoke + package tests | +| Package | Domain | Adapter | Node | Lambda | Workers | Frontend | Required env/config | Peer deps | Features | Maturity | Package tests | +| -------------------------------------- | ----------------- | -------------------------- | ---- | ------ | ------- | -------- | --------------------------------------------------------------------------- | --------- | ------------------------------------------------------------------------------ | ------------------- | ----------------- | +| `@croco/transports-graphql` | GraphQL transport | GraphQL Yoga transport | yes | yes | - | - | none | - | GraphQL server
resolver execution
Problem mapping | 🟡 beta | has package tests | +| `@croco/transports-cloudflare-workers` | HTTP transport | Cloudflare Workers adapter | - | - | yes | - | Cloudflare Worker env object supplied by platform | - | Worker fetch adapter
request context bridge | 🔴 alpha/WIP | has package tests | +| `@croco/transports-http` | HTTP transport | Hono HTTP/Lambda transport | yes | yes | - | - | CROCO_DIAGNOSTICS_TOKEN optional
CROCO_HTTP_SECURITY_VALIDATION optional | - | REST route execution
Lambda adapter
operational endpoints
diagnostics | 🟢 production-ready | has package tests | ## Presentation -| Package | Domain | Adapter | Node | Lambda | Workers | Frontend | Required env/config | Peer deps | Features | Maturity | CI smoke | -| ---------------------------- | ------------------- | ----------------------------------- | ---- | ------ | ------- | -------- | ------------------------------------------------------ | -------------------------------------------- | --------------------------------------------------------------------- | ------------ | -------------------------------- | -| `@croco/frontend-react` | Frontend React | React integration helpers | yes | - | - | yes | none | @croco/meta-vite
react
react-dom | React bindings
meta-vite integration | 🔴 alpha/WIP | entrypoint smoke + package tests | -| `@croco/meta-vite` | Frontend routing | Meta Vite runtime | yes | yes | yes | yes | optional Redis-compatible ISR adapter config | ioredis
react
react-dom
vite
zod | route registry
server actions
SSR/RSC streaming
ISR adapters | 🟡 beta | entrypoint smoke + package tests | -| `@croco/frontend-cloudflare` | Frontend SSR | Cloudflare SSR handler | - | - | yes | - | API_WORKER binding optional
ASSETS binding optional | - | SSR fetch handler
service binding routing
asset fallback | 🔴 alpha/WIP | entrypoint smoke + package tests | -| `@croco/frontend-vite` | Frontend Vite | Vite integration helpers | yes | - | yes | yes | none | @cloudflare/vite-plugin
vite | Vite config helpers
Cloudflare Vite compatibility | 🔴 alpha/WIP | entrypoint smoke + package tests | -| `@croco/presentation-preset` | Presentation preset | Backend/frontend preset composition | yes | yes | yes | yes | none | - | preset composition
contract wiring
generated app support | 🟡 beta | entrypoint smoke + package tests | +| Package | Domain | Adapter | Node | Lambda | Workers | Frontend | Required env/config | Peer deps | Features | Maturity | Package tests | +| ---------------------------- | ------------------- | ----------------------------------- | ---- | ------ | ------- | -------- | ------------------------------------------------------ | -------------------------------------------- | --------------------------------------------------------------------- | ------------ | ----------------- | +| `@croco/frontend-react` | Frontend React | React integration helpers | yes | - | - | yes | none | @croco/meta-vite
react
react-dom | React bindings
meta-vite integration | 🔴 alpha/WIP | has package tests | +| `@croco/meta-vite` | Frontend routing | Meta Vite runtime | yes | yes | yes | yes | optional Redis-compatible ISR adapter config | ioredis
react
react-dom
vite
zod | route registry
server actions
SSR/RSC streaming
ISR adapters | 🟡 beta | has package tests | +| `@croco/frontend-cloudflare` | Frontend SSR | Cloudflare SSR handler | - | - | yes | - | API_WORKER binding optional
ASSETS binding optional | - | SSR fetch handler
service binding routing
asset fallback | 🔴 alpha/WIP | has package tests | +| `@croco/frontend-vite` | Frontend Vite | Vite integration helpers | yes | - | yes | yes | none | @cloudflare/vite-plugin
vite | Vite config helpers
Cloudflare Vite compatibility | 🔴 alpha/WIP | has package tests | +| `@croco/presentation-preset` | Presentation preset | Backend/frontend preset composition | yes | yes | yes | yes | none | - | preset composition
contract wiring
generated app support | 🟡 beta | has package tests | diff --git a/scripts/package-docs-check.mts b/scripts/package-docs-check.mts index bff179f33..e77317884 100644 --- a/scripts/package-docs-check.mts +++ b/scripts/package-docs-check.mts @@ -754,7 +754,7 @@ function generateReadmeCatalog(state: CatalogState): string { "", "### Extension & Adapter Matrix", "", - "> 이 섹션은 `docs/package-catalog.json`의 `extensionMatrix` metadata에서 생성됩니다. 성숙도와 CI smoke 상태는 별도 열로 표시합니다.", + "> 이 섹션은 `docs/package-catalog.json`의 `extensionMatrix` metadata에서 생성됩니다. 성숙도와 package test 존재 여부는 별도 열로 표시합니다.", "", "Runtime columns: Node는 장기 실행 서버/CLI, Lambda는 서버리스 함수, Workers는 Cloudflare Workers, Frontend는 browser/SSR frontend integration을 의미합니다.", ); @@ -794,7 +794,7 @@ function generateReadmeCatalog(state: CatalogState): string { "", "### Documentation Gate", "", - "- `pnpm docs:catalog:check`는 README 카탈로그와 문서 커버리지 리포트 drift를 검증합니다.", + "- `pnpm docs:catalog:check`는 README 카탈로그, extension matrix reference 문서, 문서 커버리지 리포트 drift를 검증합니다.", "- 신규 public package는 `docs/package-catalog.json`에 그룹/성숙도 metadata가 있어야 합니다.", "- 신규 public package의 README, API docs, tests 누락은 `docs/package-docs-baseline.json`에 없는 한 실패합니다.", "", @@ -892,7 +892,7 @@ function generateExtensionMatrixDocs(state: CatalogState): string { "", "> Generated by `pnpm docs:catalog:write`. Do not edit this file by hand.", "", - "This page lists Croco provider, integration, transport, and presentation adapter compatibility from `docs/package-catalog.json`. Required configuration, runtime support, package peer dependencies, maturity, and CI smoke status are intentionally separate so users can evaluate production readiness without treating a passing unit test as a maturity claim.", + "This page lists Croco provider, integration, transport, and presentation adapter compatibility from `docs/package-catalog.json`. Required configuration, runtime support, package peer dependencies, maturity, and package test presence are intentionally separate so users can evaluate production readiness without treating a passing unit test as a maturity claim.", "", "Runtime columns: Node covers long-running server and CLI use, Lambda covers serverless functions, Workers covers Cloudflare Workers, and Frontend covers browser or SSR frontend integration.", ]; @@ -918,14 +918,14 @@ function appendExtensionMatrixTables( "", `${headingPrefix} ${group}`, "", - "| Package | Domain | Adapter | Node | Lambda | Workers | Frontend | Required env/config | Peer deps | Features | Maturity | CI smoke |", + "| Package | Domain | Adapter | Node | Lambda | Workers | Frontend | Required env/config | Peer deps | Features | Maturity | Package tests |", "| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |", ); for (const pkg of packages) { const maturity = state.maturity.get(pkg.maturity)?.label ?? pkg.maturity; lines.push( - `| \`${pkg.name}\` | ${pkg.extension.domain} | ${pkg.extension.adapter} | ${formatRuntimeSupport(pkg, "node")} | ${formatRuntimeSupport(pkg, "lambda")} | ${formatRuntimeSupport(pkg, "cloudflare-workers")} | ${formatRuntimeSupport(pkg, "browser")} | ${formatList(pkg.extension.requiredEnv)} | ${formatList(pkg.peerDependencies)} | ${formatList(pkg.extension.features)} | ${maturity} | ${formatSmokeStatus(pkg)} |`, + `| \`${pkg.name}\` | ${pkg.extension.domain} | ${pkg.extension.adapter} | ${formatRuntimeSupport(pkg, "node")} | ${formatRuntimeSupport(pkg, "lambda")} | ${formatRuntimeSupport(pkg, "cloudflare-workers")} | ${formatRuntimeSupport(pkg, "browser")} | ${formatList(pkg.extension.requiredEnv)} | ${formatList(pkg.peerDependencies)} | ${formatList(pkg.extension.features)} | ${maturity} | ${formatPackageTestStatus(pkg)} |`, ); } } @@ -953,8 +953,8 @@ function formatList(values: readonly string[]): string { return values.length > 0 ? values.join("
") : "-"; } -function formatSmokeStatus(pkg: PackageRecord): string { - return pkg.hasTests ? "entrypoint smoke + package tests" : "entrypoint smoke only"; +function formatPackageTestStatus(pkg: PackageRecord): string { + return pkg.hasTests ? "has package tests" : "no package tests"; } function writeReadmeCatalog(rootDir: string, generatedCatalog: string): void {