fix(server): make analytics best-effort

Remove server-side PostHog wiring so request handlers no longer wait on third-party analytics delivery.

Keep auth activity and product event writes best-effort so degraded analytics do not fail session creation.

Signed-off-by: RainbowBird <git@luoling.moe>

Commit-Message-Assisted-by: Codex (via Codex)
This commit is contained in:
RainbowBird 2026-06-05 16:25:33 +08:00
parent f6b53a37fa
commit cf5ed0766b
No known key found for this signature in database
15 changed files with 59 additions and 601 deletions

View file

@ -1,6 +1,6 @@
# Metrics Ownership
这份文档定义 AIRI 团队的指标分层规则:什么指标该走 Grafana / PrometheusOTel server-side什么该走 PostHog前后端混合 product analytics同名指标怎么处理。落地这份是为了避免后期"同一个 KPI 三处不同数"的漂移。
这份文档定义 AIRI 团队的指标分层规则:什么指标该走 Grafana / PrometheusOTel server-side什么该走 PostHogfrontend/external product analytics同名指标怎么处理。落地这份是为了避免后期"同一个 KPI 三处不同数"的漂移。
## 总原则
@ -9,7 +9,7 @@
| 层 | 工具 | 关键属性 |
|---|---|---|
| **System / API observability** | Grafana Cloud + Prometheus + OTel | 系统健康、延迟、错误率、SRE on-call 告警 |
| **Product analytics** | PostHog Cloud | 用户行为、漏斗、retention、cohort、A/B、feature adoption |
| **Product analytics** | PostHog Cloud | 前端用户行为、漏斗、retention、cohort、A/B、feature adoption |
| **Financial truth source** | Postgres (`flux_transaction` / Stripe webhook 持久化) | 收入与扣费 ledger任何展示都视作近似 |
| **LLM-native observability** | Langfuse Cloud已接入chat completion + TTS speech | 逐条 prompt/completion trace、TTS text trace、token/字符用量、按 user/session 成本归因、eval。真实 staging HTTP E2E 与 model 定价匹配待做 |
@ -45,11 +45,11 @@
|---|---|---|
| 计费 ledger每一分钱可审计 | **Postgres** | Grafana / PostHog 都视作近似展示,争议查 SQL |
| HTTP / WS / DB / Stripe webhook **计数** | **Grafana**OTel counter | 系统事件PostHog 看不到 |
| 用户去重 DAU / WAU / retention | **PostHog** | 需要 distinctId 去重session table 计数不准 |
| 用户去重 DAU / WAU / retention | **Postgres → Grafana** for server truth; **PostHog** for frontend journey | Server 不直接发 PostHog后端事实查 `product_events` / `user.last_seen_at` |
| 收入展示MRR / ARR / churn revenue | **Postgres → 两边展示** | 真相在 PostgresGrafana 取系统侧切片panel-30PostHog 取用户维度切片 |
| LLM token / cost聚合速率、按模型 | **Grafana**OTel counter | 系统侧聚合SRE 视角 |
| LLM 逐条 prompt / completion / TTS text / eval / 按 user-session 成本 | **Langfuse** | 已接入 chat completion + TTS speech正文级 trace + eval |
| 用户行为漏斗各步骤 | **PostHog**(必须) | 第一步通常是前端事件Grafana 拿不到 |
| 用户行为漏斗各步骤 | **PostHog** for frontend steps; **Postgres/Grafana** for server steps | Server-side facts 不在请求路径发 PostHog |
### Better Auth session table 与活跃用户
@ -80,7 +80,7 @@
| properties 用 `snake_case` | `{ plan_id, price_usd, checkout_session_id }` |
| 跟外部系统串联的 ID 用原平台命名 | `stripe_customer_id``stripe_subscription_id``checkout_session_id` |
`distinctId` 在登录后必须调 `posthog.identify(userId)`userId 用 Better Auth 的 user id跟 server 里的 `c.get('user').id` 一致)。后端 `posthog-node` 上报支付事件时用 fallback 链 `userId` (`session.metadata.userId`) > `email` (`session.customer_email`) > `session.id`——第一项跟前端 `identify` 一致PostHog person merge 在这里完成。前端 wiring 由 `useSharedAnalyticsStore.initialize()` 自动处理,不需要每个 caller 手动 identify。
`distinctId` 在登录后必须调 `posthog.identify(userId)`userId 用 Better Auth 的 user id跟 server 里的 `c.get('user').id` 一致)。AIRI server 不直接接入 `posthog-node`,后端事实事件写入 Postgres `product_events` 并通过 `airi_product_events_total` 暴露到 Grafana。前端 wiring 由 `useSharedAnalyticsStore.initialize()` 自动处理,不需要每个 caller 手动 identify。
参考来源:[PostHog: 5 events all teams should track](https://posthog.com/blog/events-you-should-track-with-posthog)。
@ -109,23 +109,23 @@
| Runtime | `v8js_memory_*` / `nodejs_eventloop_delay_*` | Grafana | per `service_instance_id` |
| Rate-limit | `airi_rate_limit_blocked_total` | Grafana | in-memory per replica |
### PostHog后端混合,产品侧)
### PostHog端 / 外部数据源,产品侧)
已接入:
- 前端 `posthog-js` 通过 `packages/stage-ui/src/stores/analytics/posthog.ts` 初始化,三个 appweb / desktop / pocket`isStageTamagotchi()` 等选 project key
- 后端 `posthog-node` 通过 `apps/server/src/services/adapters/posthog.ts` + injeca provider `services:posthog`
- 前端↔后端 identity merge`useSharedAnalyticsStore.initialize()` watch `authStore.isAuthenticated` 自动调 `posthog.identify(user.id)` / `reset()`
- Server 不接入 `posthog-node`。后端产品事件写 `product_events`Grafana 展示低基数聚合;需要 PostHog revenue/person analytics 时优先用 PostHog Stripe source connector 或离线导入,不允许在 API 请求路径同步发 PostHog。
- 前端 identity`useSharedAnalyticsStore.initialize()` watch `authStore.isAuthenticated` 自动调 `posthog.identify(user.id)` / `reset()`
已埋点:
| 域 | 事件 | 来源 | 落点 | Truth |
|---|---|---|---|---|
| 付费漏斗 | `pricing_page_viewed` / `plan_selected` / `checkout_started` | 前端 | `packages/stage-pages/src/pages/settings/flux.vue` | PostHog |
| 付费漏斗终点 | `payment_completed` | 后端 webhook | `apps/server/src/routes/stripe/index.ts` | PostHog |
| 付费漏斗终点 | `payment_completed` | 后端 webhook | `product_events` + GrafanaPostHog 走 Stripe source connector/离线导入 | Postgres |
| Activation / Retention | `first_model_selected` / `model_switched` | 前端consciousness store watcher | `packages/stage-ui/src/stores/analytics/index.ts` | PostHog |
| Retention | `character_created` | 前端 | `apps/stage-web/src/pages/settings/characters/components/CharacterDialog.vue` | PostHog |
| Retention | `chat_session_started` | 前端 | `packages/stage-ui/src/components/scenarios/chat/components/sessions-drawer.vue` | PostHog |
| Churn | `subscription_cancelled`(带 cancellation_reason | 后端 webhook | `apps/server/src/routes/stripe/index.ts` | PostHog |
| Churn | `subscription_cancelled`(带 cancellation_reason | 外部 Stripe 数据源 | PostHog Stripe source connector | Stripe/Postgres |
| 老事件 | `provider_card_clicked` / `first_message_sent` | 前端 | `packages/stage-ui/src/composables/use-analytics.ts` | PostHog |
待埋点API 已在 `use-analytics.ts` 暴露但调用点未接入):
@ -140,34 +140,16 @@
| 指标 | Grafana | PostHog | Truth | 语义差异 |
|---|---|---|---|---|
| 活跃用户数 | `user_active_sessions`Postgres session 计数) | DAU = 去重 distinctId | **PostHog** | Grafana 是 active **sessions**PostHog 是 active **users** |
| Checkout 完成数 | `stripe_checkout_completed_total` | `payment_completed` event | **Postgres** | 两边都展示Grafana 是 webhook 计数PostHog 是漏斗终点 |
| 活跃用户数 | `user_active_rolling` / `user_distinct_active` | DAU = 前端 journey 去重 distinctId | **Postgres/Grafana** for server truth | Grafana 是服务端可验证活跃PostHog 是前端产品旅程 |
| Checkout 完成数 | `stripe_checkout_completed_total` + `product_events.payment_completed` | Stripe source connector / offline import | **Postgres** | Grafana 是 webhook 计数PostHog 是产品漏斗展示 |
| LLM 请求 | `gen_ai_client_operation_count_total` | `chat_session_started` 等 | **Grafana**(系统计数) | PostHog 是用户维度切片,会少于 GrafanaPostHog 只覆盖 logged-in user |
## PostHog 接入路线图
落地分两步,**不要一次性埋全部事件**,否则 schema 漂移会很快出现。
落地分两步,**不要一次性埋全部事件**,否则 schema 漂移会很快出现。PostHog 采集只发生在前端或外部 source connectorserver 请求路径只写 Postgres/Grafana。
### 阶段 1P0 — 付费漏斗 + activation
`apps/server`
```ts
// services/adapters/posthog.ts新增
import { PostHog } from 'posthog-node'
export function createPostHog(env: ServerEnv) {
return new PostHog(env.POSTHOG_KEY, { host: 'https://us.i.posthog.com' })
}
// 在 Stripe webhook handler 里
posthog.capture({
distinctId: stripeCustomerEmail,
event: 'payment_completed',
properties: { plan_id, amount_usd, stripe_customer_id, stripe_subscription_id }
})
```
`apps/stage-web`
```ts
@ -199,12 +181,12 @@ posthog.init(import.meta.env.VITE_POSTHOG_KEY, {
埋点事件清单P0
- 前端:`pricing_page_viewed``plan_selected``checkout_started``user_signed_up``first_message_sent``first_model_selected`
- 后端:`payment_completed`
- 后端:不直接发 PostHog`payment_completed` 写入 `product_events`PostHog 侧通过 Stripe source connector 或离线导入展示
PostHog UI 配两个 funnel
- **付费漏斗** (7d 窗口)`pricing_page_viewed → plan_selected → checkout_started → payment_completed`
- **激活漏斗** (14d 窗口)`user_signed_up → first_message_sent → first_model_selected → payment_completed`
- **付费漏斗** (7d 窗口)`pricing_page_viewed → plan_selected → checkout_started → payment_completed`(最后一步来自 Stripe source connector / 离线导入)
- **激活漏斗** (14d 窗口)`user_signed_up → first_message_sent → first_model_selected → payment_completed`(最后一步同上)
### 阶段 2P1 — retention / feature adoption / churn
@ -217,14 +199,14 @@ PostHog UI 配 cohort
### Stripe → PostHog 集成路径
**两条路径都接**
**不在 server webhook 里手动 capture**
| 路径 | 用途 |
|---|---|
| PostHog Stripe **source connector** | MRR / ARR / churn revenue dashboardPostHog 原生 Revenue analytics |
| **手动 capture** `payment_completed`(后端 webhook | 漏斗终点 event跟前端 `checkout_started` 串联 |
| 离线导入 `product_events.payment_completed`(可选 | 漏斗终点 event跟前端 `checkout_started` 串联 |
能只用 source connector它是 data warehouse 层,**不生成 person event做不了漏斗**
要在 API 请求路径同步发 PostHogPostHog 网络尾延迟会污染 auth / billing / chat route latency。需要 person-level funnel endpoint 时,用后台导入或 connector不阻塞用户请求
## 5xx Triage 路径

View file

@ -61,7 +61,6 @@
"nanoid": "catalog:",
"ofetch": "catalog:",
"pg": "catalog:",
"posthog-node": "catalog:",
"resend": "catalog:",
"stripe": "catalog:",
"unspeech": "catalog:xsai",

View file

@ -81,7 +81,6 @@ function createTestDeps() {
encryptKey: vi.fn(),
decryptKey: vi.fn(),
} as any,
posthog: null,
}
return {

View file

@ -1,5 +1,4 @@
import type Redis from 'ioredis'
import type { PostHog } from 'posthog-node'
import type { AuthInstance } from './libs/auth'
import type { Database } from './libs/db'
@ -67,7 +66,6 @@ import { createProviderRoutes } from './routes/providers'
import { createStripeRoutes } from './routes/stripe'
import { createConfigKVService } from './services/adapters/config-kv'
import { createEmailService } from './services/adapters/email'
import { createPostHogClient } from './services/adapters/posthog'
import { createAdminFluxGrantsService } from './services/domain/admin/flux-grants'
import { createAdminRouterConfigService } from './services/domain/admin/router-config'
import { createAdminUsersService } from './services/domain/admin/users'
@ -111,7 +109,6 @@ interface AppDeps {
otel: OtelInstance | null
userDeletionService: UserDeletionService
llmRouter: LlmRouterService
posthog: PostHog | null
}
export async function buildApp(deps: AppDeps) {
@ -233,7 +230,6 @@ export async function buildApp(deps: AppDeps) {
genAi: deps.otel?.genAi,
revenue: deps.otel?.revenue,
rateLimitMetrics: deps.otel?.rateLimit,
posthog: deps.posthog,
})
const builtApp = app
@ -365,7 +361,7 @@ export async function buildApp(deps: AppDeps) {
/**
* Stripe routes.
*/
.route('/api/v1/stripe', createStripeRoutes(deps.fluxService, deps.stripeService, deps.billingService, deps.configKV, deps.env, deps.redis, deps.otel?.revenue, deps.otel?.rateLimit, deps.posthog, deps.productEventService))
.route('/api/v1/stripe', createStripeRoutes(deps.fluxService, deps.stripeService, deps.billingService, deps.configKV, deps.env, deps.redis, deps.otel?.revenue, deps.otel?.rateLimit, deps.productEventService))
/**
* Admin routes guarded by the `adminGuard` role check (`role === 'admin'`,
@ -505,44 +501,6 @@ export async function createApp() {
}, undefined, dependsOn.otel?.email),
})
// Webhook capture path goes through `captureSafe` → `captureImmediate`,
// which awaits the HTTP send inline, so individual events never sit in
// the background queue. `flush()` + `_shutdown()` on SIGTERM is the belt-
// and-suspenders drain for any future call site that uses the regular
// `capture()` (which only enqueues).
//
// NOTICE:
// We use the underscore-prefixed `_shutdown` despite its "internal" naming
// because the public `shutdown(timeoutMs)` returns void (`types.d.ts:580`)
// — there is no way to await its completion. `_shutdown(timeoutMs)` returns
// `Promise<void>` (`client.d.ts:934`) and is the only way to ensure the
// process doesn't exit while PostHog cleanup is still running. Posthog's
// own examples show `await client._shutdown()` as the recommended pattern.
const posthog = injeca.provide('services:posthog', {
dependsOn: { env: parsedEnv, lifecycle },
build: ({ dependsOn }) => {
const client = createPostHogClient(dependsOn.env)
if (client) {
dependsOn.lifecycle.appHooks.onStop(async () => {
try {
await client.flush()
}
catch {
// Flush failures on shutdown are non-fatal; we lose at most a
// few queued events. Fall through to shutdown anyway.
}
try {
await client._shutdown(5000)
}
catch {
// Shutdown errors are also non-fatal during process exit.
}
})
}
return client
},
})
const productEventService = injeca.provide('services:productEvents', {
dependsOn: { db, otel },
build: ({ dependsOn }) => createProductEventService(dependsOn.db, dependsOn.otel?.product),
@ -608,7 +566,7 @@ export async function createApp() {
})
const auth = injeca.provide('services:auth', {
dependsOn: { db, env: parsedEnv, otel, email: emailService, userDeletionService, posthog, productEventService },
dependsOn: { db, env: parsedEnv, otel, email: emailService, userDeletionService, productEventService },
build: async ({ dependsOn }) => {
// Seed trusted OIDC clients into DB so FK constraints on oauth_access_token are satisfied
await seedTrustedClients(dependsOn.db, dependsOn.env)
@ -621,7 +579,7 @@ export async function createApp() {
redirectUris: client.redirectUris.join(', '),
}).log('OIDC trusted client ready')
}
return createAuth(dependsOn.db, dependsOn.env, dependsOn.email, dependsOn.otel?.auth, dependsOn.userDeletionService, dependsOn.posthog, dependsOn.productEventService)
return createAuth(dependsOn.db, dependsOn.env, dependsOn.email, dependsOn.otel?.auth, dependsOn.userDeletionService, dependsOn.productEventService)
},
})
@ -731,7 +689,6 @@ export async function createApp() {
otel,
userDeletionService,
llmRouter,
posthog,
})
// Register the cluster-wide ObservableGauges for sessions / users. Each
// replica polls the same DB (cached inside each gauge, in-flight coalesced);
@ -773,7 +730,6 @@ export async function createApp() {
otel: resolved.otel,
userDeletionService: resolved.userDeletionService,
llmRouter: resolved.llmRouter,
posthog: resolved.posthog,
})
logger.withFields({ hostname: resolved.env.HOST, port: resolved.env.PORT }).log('Server started')

View file

@ -1,5 +1,3 @@
import type { PostHog } from 'posthog-node'
import type { AuthMetrics } from '../otel'
import type { EmailService } from '../services/adapters/email'
import type { ProductEventService } from '../services/domain/product-events'
@ -10,6 +8,7 @@ import type { Env } from './env'
import { Buffer } from 'node:buffer'
import { oauthProvider } from '@better-auth/oauth-provider'
import { useLogger } from '@guiiai/logg'
import { betterAuth } from 'better-auth'
import { drizzleAdapter } from 'better-auth/adapters/drizzle'
import { createAuthMiddleware } from 'better-auth/api'
@ -17,13 +16,14 @@ import { deleteSessionCookie } from 'better-auth/cookies'
import { admin, bearer, jwt, magicLink } from 'better-auth/plugins'
import { eq } from 'drizzle-orm'
import { captureSafe } from '../services/adapters/posthog'
import { ApiError } from '../utils/error'
import { getAuthTrustedOrigins, getTrustedOrigin } from '../utils/origin'
import { oidcJwtBearer } from './auth-plugins/oidc-jwt-bearer'
import * as authSchema from '../schemas/accounts'
const logger = useLogger('auth').useGlobalConfig()
interface TrustedClientSeed {
clientId: string
/** Omit for public clients — only confidential clients need a secret. */
@ -365,7 +365,6 @@ export function createAuth(
email?: EmailService,
metrics?: AuthMetrics | null,
userDeletionService?: UserDeletionService,
posthog?: PostHog | null,
productEventService?: ProductEventService,
) {
return betterAuth({
@ -665,17 +664,13 @@ export function createAuth(
create: {
after: async (user) => {
metrics?.userRegistered.add(1)
await productEventService?.track({
void productEventService?.track({
userId: user.id,
feature: 'auth',
action: 'user_signed_up',
status: 'succeeded',
source: 'better-auth.user.create',
})
await captureSafe(posthog ?? null, {
event: 'user_signed_up',
distinctId: user.id,
})
},
},
update: {
@ -706,21 +701,20 @@ export function createAuth(
// `after` hook for last-seen / analytics.
after: async (session) => {
metrics?.userLogin.add(1)
await db
// Best-effort analytics: session creation must not fail because
// active-user reporting is degraded.
void db
.update(authSchema.user)
.set({ lastSeenAt: new Date() })
.where(eq(authSchema.user.id, session.userId))
await productEventService?.track({
.catch(err => logger.withError(err).withFields({ userId: session.userId }).warn('Failed to update user lastSeenAt; continuing session create'))
void productEventService?.track({
userId: session.userId,
feature: 'auth',
action: 'session_started',
status: 'succeeded',
source: 'better-auth.session.create',
})
await captureSafe(posthog ?? null, {
event: 'session_started',
distinctId: session.userId,
})
},
},
},

View file

@ -131,11 +131,6 @@ const EnvSchema = object({
STRIPE_SECRET_KEY: optional(string()),
STRIPE_WEBHOOK_SECRET: optional(string()),
// PostHog server-side analytics. Optional — when unset the client is null
// and `captureSafe(...)` is a no-op so webhooks still complete.
POSTHOG_API_KEY: optional(string(), ''),
POSTHOG_HOST: optional(string(), 'https://us.i.posthog.com'),
// LLM/TTS gateway is fully internalised by the in-process router; provider
// baseURLs live per-upstream inside LLM_ROUTER_CONFIG, and the default chat /
// tts model aliases moved to configKV (DEFAULT_CHAT_MODEL / DEFAULT_TTS_MODEL)

View file

@ -4,7 +4,6 @@ import type { V1RouteDeps } from '../../types'
import { useLogger } from '@guiiai/logg'
import { captureSafe } from '../../../../../services/adapters/posthog'
import { extractUsageFromBody } from '../../../../../services/domain/billing/billing'
import { nanoid } from '../../../../../utils/id'
import { buildSafeResponseHeaders } from '../../http/response'
@ -156,19 +155,6 @@ export function chatCompletions(deps: V1RouteDeps): GatewayCallback<'chat.comple
stream,
},
})
// Emit server-side so funnels see real HTTP status — the client only
// ever observes "stream closed" and cannot tell 401 / 429 / 5xx apart.
void captureSafe(deps.posthog ?? null, {
distinctId: input.userId,
event: 'llm_request_failed',
properties: {
model: requestModel,
http_status: response.status,
duration_ms: durationMs,
stream: !!body.stream,
},
})
logger.withFields({ requestId, userId: input.userId, model: requestModel, status: response.status, durationMs })
.warn('chat completion delivered with upstream error status')
@ -400,21 +386,6 @@ function streamChatCompletion(input: {
},
})
void captureSafe(input.deps.posthog ?? null, {
distinctId: input.userId,
event: 'llm_request_succeeded',
properties: {
model: input.requestModel,
http_status: input.response.status,
duration_ms: input.durationMs,
prompt_tokens: usage.promptTokens ?? 0,
completion_tokens: usage.completionTokens ?? 0,
flux_consumed: actualCharged,
stream: true,
stream_interrupted: streamInterrupted,
},
})
input.logger.withFields({
requestId: input.requestId,
userId: input.userId,
@ -534,20 +505,6 @@ async function completeNonStreamingChat(input: {
},
})
void captureSafe(input.deps.posthog ?? null, {
distinctId: input.userId,
event: 'llm_request_succeeded',
properties: {
model: input.requestModel,
http_status: input.response.status,
duration_ms: input.durationMs,
prompt_tokens: usage.promptTokens ?? 0,
completion_tokens: usage.completionTokens ?? 0,
flux_consumed: actualCharged,
stream: false,
},
})
input.logger.withFields({
requestId: input.requestId,
userId: input.userId,

View file

@ -151,7 +151,6 @@ function createTestApp(
genAi: null,
revenue: null,
rateLimitMetrics: null,
posthog: null,
llmTracing,
})
const app = new Hono<HonoEnv>()

View file

@ -1,5 +1,3 @@
import type { PostHog } from 'posthog-node'
import type { GenAiMetrics, RateLimitMetrics, RevenueMetrics } from '../../../otel'
import type { ConfigKVService } from '../../../services/adapters/config-kv'
import type { BillingService } from '../../../services/domain/billing/billing-service'
@ -28,7 +26,6 @@ export interface V1RouteDeps {
genAi?: GenAiMetrics | null
revenue?: RevenueMetrics | null
rateLimitMetrics?: RateLimitMetrics | null
posthog?: PostHog | null
llmTracing: LlmTracingDeps
}

View file

@ -1,5 +1,4 @@
import type Redis from 'ioredis'
import type { PostHog } from 'posthog-node'
import type { Env } from '../../libs/env'
import type { RateLimitMetrics, RevenueMetrics } from '../../otel'
@ -47,7 +46,6 @@ export function createStripeRoutes(
redis: Redis,
metrics?: RevenueMetrics | null,
rateLimitMetrics?: RateLimitMetrics | null,
posthog?: PostHog | null,
productEventService?: ProductEventService,
) {
const stripe = env.STRIPE_SECRET_KEY ? new Stripe(env.STRIPE_SECRET_KEY) : null
@ -60,7 +58,6 @@ export function createStripeRoutes(
stripeService,
billingService,
metrics,
posthog,
productEventService,
})

View file

@ -1,4 +1,3 @@
import type { PostHog } from 'posthog-node'
import type Stripe from 'stripe'
import type { RevenueMetrics } from '../../../otel'
@ -9,7 +8,6 @@ import type { StripeService } from '../../../services/domain/stripe'
import { useLogger } from '@guiiai/logg'
import { captureSafe } from '../../../services/adapters/posthog'
import { createBadRequestError, createServiceUnavailableError } from '../../../utils/error'
import { errorMessageFromUnknown } from '../../../utils/error-message'
@ -22,7 +20,6 @@ export interface WebhookOperationDeps {
stripeService: StripeService
billingService: BillingService
metrics?: RevenueMetrics | null
posthog?: PostHog | null
productEventService?: ProductEventService
}
@ -75,13 +72,9 @@ export function createWebhookOperation(deps: WebhookOperationDeps) {
source: 'checkout',
})
}
// PostHog: funnel terminator. Only fire when the handler actually
// processed the checkout — malformed sessions (missing userId,
// invalid fluxAmount) take the early-return path above and would
// otherwise poison the funnel with phantom conversions. distinctId
// is the Better Auth user id so it merges with the browser's
// `posthog.identify(userId)` and the prior `checkout_started`
// event lines up. See docs/ai-context/metrics-ownership.md.
// Record the product conversion only after the handler actually
// processed the checkout. Malformed sessions (missing userId,
// invalid fluxAmount) take the early-return path above.
if (result.processed) {
const userId = event.data.object.metadata?.userId
if (userId) {
@ -99,7 +92,6 @@ export function createWebhookOperation(deps: WebhookOperationDeps) {
},
})
}
await capturePaymentCompleted(deps.posthog, event.data.object)
}
break
}
@ -113,8 +105,6 @@ export function createWebhookOperation(deps: WebhookOperationDeps) {
case 'customer.subscription.deleted': {
await handleSubscriptionEvent(event.data.object, deps.stripeService)
deps.metrics?.stripeSubscriptionEvent.add(1, { event_type: event.type.replace('customer.subscription.', '') })
if (event.type === 'customer.subscription.deleted')
await captureSubscriptionCancelled(deps.posthog, deps.stripeService, event.data.object)
break
}
case 'invoice.created':
@ -226,73 +216,6 @@ async function handleCheckoutSessionCompleted(
return { processed: true }
}
async function capturePaymentCompleted(
posthog: PostHog | null | undefined,
session: Stripe.Checkout.Session,
): Promise<void> {
if (!posthog)
return
const userId = session.metadata?.userId
const email = session.customer_email
|| (typeof session.customer_details?.email === 'string' ? session.customer_details.email : null)
|| null
// distinctId fallback chain: userId (Better Auth, matches browser identify())
// > email (PostHog will merge on identify later) > stripe session id (last
// resort — orphan event but at least we count it).
const distinctId = userId || email || session.id
const fluxAmount = Number(session.metadata?.fluxAmount)
const stripeCustomerId = typeof session.customer === 'string' ? session.customer : session.customer?.id
await captureSafe(posthog, {
distinctId,
event: 'payment_completed',
properties: {
amount_total: session.amount_total,
currency: session.currency,
flux_amount: Number.isFinite(fluxAmount) ? fluxAmount : null,
mode: session.mode,
stripe_session_id: session.id,
stripe_customer_id: stripeCustomerId,
stripe_subscription_id: typeof session.subscription === 'string' ? session.subscription : session.subscription?.id,
...(userId ? { user_id: userId } : {}),
// $set populates the PostHog person profile so funnel joins work even
// when a user pays via direct checkout link before ever loading the SPA.
...(email ? { $set: { email } } : {}),
},
})
}
async function captureSubscriptionCancelled(
posthog: PostHog | null | undefined,
stripeService: StripeService,
subscription: Stripe.Subscription,
): Promise<void> {
if (!posthog)
return
const stripeCustomerId = typeof subscription.customer === 'string' ? subscription.customer : subscription.customer.id
const customer = await stripeService.getCustomerByStripeId(stripeCustomerId)
const distinctId = customer?.userId || stripeCustomerId
await captureSafe(posthog, {
distinctId,
event: 'subscription_cancelled',
properties: {
stripe_subscription_id: subscription.id,
stripe_customer_id: stripeCustomerId,
cancel_at_period_end: subscription.cancel_at_period_end,
cancellation_reason: subscription.cancellation_details?.reason ?? null,
cancellation_comment: subscription.cancellation_details?.comment ?? null,
canceled_at: subscription.canceled_at,
ended_at: subscription.ended_at,
...(customer?.userId ? { user_id: customer.userId } : {}),
},
})
}
async function handleCustomerEvent(
customer: Stripe.Customer | Stripe.DeletedCustomer,
stripeService: StripeService,

View file

@ -1,95 +0,0 @@
import type { Env } from '../../libs/env'
import { useLogger } from '@guiiai/logg'
import { PostHog } from 'posthog-node'
const logger = useLogger('posthog')
/**
* Server-side PostHog client. Used to capture authoritative business events
* the browser cannot see (Stripe webhooks, subscription state changes,
* admin actions). Pairs with the browser-side PostHog already wired up in
* `packages/stage-ui/src/stores/analytics/posthog.ts`.
*
* Use when:
* - You're inside a server-side handler (Stripe webhook, admin route) and
* need to emit an event that will be analysed in PostHog funnels or
* cohorts (e.g. `payment_completed`, `subscription_cancelled`).
*
* Expects:
* - `POSTHOG_API_KEY` env var. When unset (dev/CI) this returns `null` so
* callers degrade gracefully with `posthog?.capture(...)`.
* - `distinctId` must match the browser's `posthog.identify(userId)` we
* use the Better Auth `user.id` for that everywhere. Stripe webhooks
* that only have an email use the email as a fallback `distinctId` and
* include `userId` in the event properties so PostHog's merge resolves
* the person.
*
* Returns:
* - A `PostHog` client configured for low-latency immediate sends, or
* `null` when key is unset. Callers must use `captureSafe()` (which
* wraps `captureImmediate`) the regular `capture()` only enqueues
* and would let webhook responses race ahead of the HTTP send.
*/
export function createPostHogClient(env: Env): PostHog | null {
if (!env.POSTHOG_API_KEY) {
logger.warn('POSTHOG_API_KEY is unset — server-side analytics disabled')
return null
}
// NOTICE:
// `flushAt: 1` keeps the background-batch threshold low so any stray
// `posthog.capture()` (non-immediate path) flushes promptly. Real send-
// path for webhook events goes through `captureImmediate()` in
// `captureSafe`, which bypasses the queue entirely and resolves only
// after the HTTP round-trip. We also rely on `shutdown(timeoutMs)` from
// app.ts to drain any residual queue on SIGTERM.
return new PostHog(env.POSTHOG_API_KEY, {
host: env.POSTHOG_HOST || 'https://us.i.posthog.com',
flushAt: 1,
flushInterval: 0,
})
}
/**
* Safe capture wrapper. PostHog must never block or fail a webhook /
* billing path any error here is logged and swallowed.
*
* Use when:
* - Inside a server-side handler that has business work to finish even
* if PostHog is down. The handler already wrote to Postgres and
* updated metrics; PostHog is the optional last step.
*
* Expects:
* - Caller awaits the returned promise. We use `captureImmediate` (not
* `capture`) because PostHog Node SDK's regular `capture` only enqueues
* `flushAt: 1` triggers a *background* flush, which means the webhook
* handler can return before the event reaches PostHog and SIGTERM may
* strand the queued event. `captureImmediate` does the HTTP send inline
* and resolves only after the network round-trip.
*
* Consent boundary:
* - Browser-side capture is gated by `useAnalytics().canCapture()` (user
* toggle in Settings Analytics).
* - Server-side events here are operational telemetry tied to business
* facts that already occurred (auth records the user, Stripe records
* the payment, the LLM router records the request). They are emitted
* regardless of the browser toggle on legitimate-interest grounds
* the user cannot opt out of the server knowing they signed up or
* paid, only out of additional product analytics on top. Surface this
* distinction in the privacy policy.
*/
export async function captureSafe(
posthog: PostHog | null,
event: { distinctId: string, event: string, properties?: Record<string, unknown> },
): Promise<void> {
if (!posthog)
return
try {
await posthog.captureImmediate(event)
}
catch (err) {
logger.withError(err).withFields({ event: event.event, distinctId: event.distinctId }).warn('PostHog captureImmediate failed; swallowing to protect caller')
}
}

View file

@ -199,7 +199,6 @@ export async function startVerificationContext() {
route: vi.fn(async () => new Response('{}', { status: 200 })),
invalidateConfig: vi.fn(),
} as any,
posthog: null,
})
return {

293
pnpm-lock.yaml generated
View file

@ -880,9 +880,6 @@ catalogs:
posthog-js:
specifier: 1.306.1
version: 1.306.1
posthog-node:
specifier: ^5.34.1
version: 5.34.1
postprocessing:
specifier: ^6.39.1
version: 6.39.1
@ -1608,9 +1605,6 @@ importers:
pg:
specifier: 'catalog:'
version: 8.20.0
posthog-node:
specifier: 'catalog:'
version: 5.34.1(rxjs@7.8.2)
resend:
specifier: 'catalog:'
version: 6.12.2
@ -2345,7 +2339,7 @@ importers:
version: 3.0.2(electron@41.2.1)
'@electron-toolkit/tsconfig':
specifier: 'catalog:'
version: 2.0.0(@types/node@24.12.2)
version: 2.0.0(@types/node@25.6.0)
'@electron-toolkit/utils':
specifier: 'catalog:'
version: 4.0.0(electron@41.2.1)
@ -2384,7 +2378,7 @@ importers:
version: 3.1.0
'@intlify/unplugin-vue-i18n':
specifier: 'catalog:'
version: 11.0.7(@vue/compiler-dom@3.5.32)(eslint@10.2.1(jiti@2.6.1))(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-i18n@11.3.2(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))
version: 11.0.7(@vue/compiler-dom@3.5.32)(eslint@10.2.1(jiti@2.6.1))(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-i18n@11.3.2(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))
'@modelcontextprotocol/sdk':
specifier: 'catalog:'
version: 1.29.0(@cfworker/json-schema@4.1.1)(zod@4.3.6)
@ -2420,10 +2414,10 @@ importers:
version: link:../../packages/ui-transitions
'@proj-airi/unplugin-fetch':
specifier: 'catalog:'
version: 0.2.3(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
version: 0.2.3(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
'@proj-airi/unplugin-live2d-sdk':
specifier: 'catalog:'
version: 0.1.7(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
version: 0.1.7(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
'@types/audioworklet':
specifier: 'catalog:'
version: 0.0.97
@ -2450,7 +2444,7 @@ importers:
version: 2.10.3
'@vitejs/plugin-vue':
specifier: 'catalog:'
version: 6.0.6(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue@3.5.32(typescript@5.9.3))
version: 6.0.6(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue@3.5.32(typescript@5.9.3))
'@vue-macros/volar':
specifier: 'catalog:'
version: 3.1.2(typescript@5.9.3)(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.32(typescript@5.9.3))
@ -2483,7 +2477,7 @@ importers:
version: 6.8.3
electron-vite:
specifier: 'catalog:'
version: 5.0.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
version: 5.0.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
get-port-please:
specifier: 'catalog:'
version: 3.2.0
@ -2504,31 +2498,31 @@ importers:
version: 2.2.6
unocss-preset-scrollbar:
specifier: 'catalog:'
version: 4.0.0(unocss@66.6.8(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)))
version: 4.0.0(unocss@66.6.8(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)))
unplugin-info:
specifier: 'catalog:'
version: 1.3.2(esbuild@0.27.2)(rollup@4.60.1)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
version: 1.3.2(esbuild@0.27.2)(rollup@4.60.1)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
unplugin-yaml:
specifier: 'catalog:'
version: 4.1.0(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
version: 4.1.0(@nuxt/kit@3.20.2(magicast@0.5.2))(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
vite:
specifier: 'catalog:'
version: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
version: 8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
vite-bundle-visualizer:
specifier: 'catalog:'
version: 1.2.1(rolldown@1.0.0-rc.16)(rollup@4.60.1)
vite-plugin-mkcert:
specifier: 'catalog:'
version: 2.0.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
version: 2.0.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
vite-plugin-vue-devtools:
specifier: 'catalog:'
version: 8.1.1(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue@3.5.32(typescript@5.9.3))
version: 8.1.1(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue@3.5.32(typescript@5.9.3))
vite-plugin-vue-layouts:
specifier: 'catalog:'
version: 0.11.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-router@5.0.4(@pinia/colada@1.2.1(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(@vue/compiler-sfc@3.5.32)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))
version: 0.11.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-router@5.0.4(@pinia/colada@1.2.1(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(@vue/compiler-sfc@3.5.32)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))
vue-macros:
specifier: 'catalog:'
version: 3.1.2(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@vueuse/core@14.2.1(vue@3.5.32(typescript@5.9.3)))(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.32(typescript@5.9.3))
version: 3.1.2(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@vueuse/core@14.2.1(vue@3.5.32(typescript@5.9.3)))(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.32(typescript@5.9.3))
vue-tsc:
specifier: 'catalog:'
version: 3.2.6(typescript@5.9.3)
@ -9654,15 +9648,9 @@ packages:
'@polka/url@1.0.0-next.29':
resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==}
'@posthog/core@1.29.1':
resolution: {integrity: sha512-q+/t/DZALr50YTE0dFgfGSS9EgwcyAlqsn+JS61wLkwdcDM5yu/YTDM8oMKmJupsyjSZlVkDuHZAMd4ab7AxzQ==}
'@posthog/core@1.7.1':
resolution: {integrity: sha512-kjK0eFMIpKo9GXIbts8VtAknsoZ18oZorANdtuTj1CbgS28t4ZVq//HAWhnxEuXRTrtkd+SUJ6Ux3j2Af8NCuA==}
'@posthog/types@1.373.4':
resolution: {integrity: sha512-n+0AbGRYYsbi+CQXQi2rF1lwTSyASlaogcw4YSkzB5KeMa4Y6nhNb7+TTnu9aVor+BycsQYCa2OsBrMMbaTekw==}
'@prisma/client@5.22.0':
resolution: {integrity: sha512-M0SVXfyHnQREBKxCgyo7sffrKttwE6R8PMq330MIUF0pTwjUhLbW84pFDlf06B27XyCR++VtjugEnIHdr07SVA==}
engines: {node: '>=16.13'}
@ -16684,15 +16672,6 @@ packages:
posthog-js@1.306.1:
resolution: {integrity: sha512-wO7bliv/5tlAlfoKCUzwkGXZVNexk0dHigMf9tNp0q1rzs62wThogREY7Tz7h/iWKYiuXy1RumtVlTmHuBXa1w==}
posthog-node@5.34.1:
resolution: {integrity: sha512-kGl0kSfh2+Ey3KL5Sji3yv9W5xwPK9sTkINRoFqCh9fbYXWWY6Zwi5Psv2QmRcbYiMJBk/iecnoOKVDRRga6PA==}
engines: {node: ^20.20.0 || >=22.22.0}
peerDependencies:
rxjs: ^7.0.0
peerDependenciesMeta:
rxjs:
optional: true
postject@1.0.0-alpha.6:
resolution: {integrity: sha512-b9Eb8h2eVqNE8edvKdwqkrY6O7kAwmI8kcnBv1NScolYJbo59XUF0noFq+lxbC1yN20bmC0WBEbDC5H/7ASb0A==}
engines: {node: '>=14.0.0'}
@ -20909,9 +20888,9 @@ snapshots:
dependencies:
electron: 41.2.1
'@electron-toolkit/tsconfig@2.0.0(@types/node@24.12.2)':
'@electron-toolkit/tsconfig@2.0.0(@types/node@25.6.0)':
dependencies:
'@types/node': 24.12.2
'@types/node': 25.6.0
'@electron-toolkit/utils@4.0.0(electron@41.2.1)':
dependencies:
@ -21823,31 +21802,6 @@ snapshots:
- supports-color
- typescript
'@intlify/unplugin-vue-i18n@11.0.7(@vue/compiler-dom@3.5.32)(eslint@10.2.1(jiti@2.6.1))(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-i18n@11.3.2(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))':
dependencies:
'@eslint-community/eslint-utils': 4.9.1(eslint@10.2.1(jiti@2.6.1))
'@intlify/bundle-utils': 11.0.7(vue-i18n@11.3.2(vue@3.5.32(typescript@5.9.3)))
'@intlify/shared': 11.3.2
'@intlify/vue-i18n-extensions': 8.0.0(@intlify/shared@11.3.2)(@vue/compiler-dom@3.5.32)(vue-i18n@11.3.2(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))
'@rollup/pluginutils': 5.3.0(rollup@4.60.1)
'@typescript-eslint/scope-manager': 8.58.1
'@typescript-eslint/typescript-estree': 8.58.1(typescript@5.9.3)
debug: 4.4.3(supports-color@10.2.2)
fast-glob: 3.3.3
pathe: 2.0.3
picocolors: 1.1.1
unplugin: 2.3.11
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
vue: 3.5.32(typescript@5.9.3)
optionalDependencies:
vue-i18n: 11.3.2(vue@3.5.32(typescript@5.9.3))
transitivePeerDependencies:
- '@vue/compiler-dom'
- eslint
- rollup
- supports-color
- typescript
'@intlify/unplugin-vue-i18n@11.0.7(@vue/compiler-dom@3.5.32)(eslint@10.2.1(jiti@2.6.1))(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-i18n@11.3.2(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))':
dependencies:
'@eslint-community/eslint-utils': 4.9.1(eslint@10.2.1(jiti@2.6.1))
@ -23789,16 +23743,10 @@ snapshots:
'@polka/url@1.0.0-next.29': {}
'@posthog/core@1.29.1':
dependencies:
'@posthog/types': 1.373.4
'@posthog/core@1.7.1':
dependencies:
cross-spawn: 7.0.6
'@posthog/types@1.373.4': {}
'@prisma/client@5.22.0': {}
'@proj-airi/chromatic@1.1.1':
@ -23856,35 +23804,11 @@ snapshots:
transitivePeerDependencies:
- magicast
'@proj-airi/unplugin-fetch@0.2.3(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))':
dependencies:
ofetch: 1.5.1
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
'@proj-airi/unplugin-fetch@0.2.3(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))':
dependencies:
ofetch: 1.5.1
vite: 8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
'@proj-airi/unplugin-live2d-sdk@0.1.7(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)':
dependencies:
ofetch: 1.5.1
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
yauzl: 3.3.0
transitivePeerDependencies:
- '@types/node'
- '@vitejs/devtools'
- esbuild
- jiti
- less
- sass
- sass-embedded
- stylus
- sugarss
- terser
- tsx
- yaml
'@proj-airi/unplugin-live2d-sdk@0.1.7(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)':
dependencies:
ofetch: 1.5.1
@ -25450,12 +25374,6 @@ snapshots:
vite: 6.4.2(@types/node@25.6.0)(jiti@2.6.1)(less@4.6.4)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
vue: 3.5.32(typescript@5.9.3)
'@vitejs/plugin-vue@6.0.6(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue@3.5.32(typescript@5.9.3))':
dependencies:
'@rolldown/pluginutils': 1.0.0-rc.13
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
vue: 3.5.32(typescript@5.9.3)
'@vitejs/plugin-vue@6.0.6(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue@3.5.32(typescript@5.9.3))':
dependencies:
'@rolldown/pluginutils': 1.0.0-rc.13
@ -25536,9 +25454,9 @@ snapshots:
obug: 2.1.1
std-env: 4.1.0
tinyrainbow: 3.1.0
vitest: 4.1.4(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/browser-playwright@4.1.4)(@vitest/coverage-v8@4.1.4)(jsdom@29.1.1(@noble/hashes@2.0.1)(canvas@3.2.3))(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
vitest: 4.1.4(@opentelemetry/api@1.9.1)(@types/node@25.6.0)(@vitest/browser-playwright@4.1.4)(@vitest/coverage-v8@4.1.4)(jsdom@29.1.1(@noble/hashes@2.0.1)(canvas@3.2.3))(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
optionalDependencies:
'@vitest/browser': 4.1.4(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@4.1.4)
'@vitest/browser': 4.1.4(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@4.1.4)
'@vitest/eslint-plugin@1.6.15(@typescript-eslint/eslint-plugin@8.58.1(@typescript-eslint/parser@8.58.1(eslint@10.2.1(jiti@2.6.1))(typescript@5.9.3))(eslint@10.2.1(jiti@2.6.1))(typescript@5.9.3))(eslint@10.2.1(jiti@2.6.1))(typescript@5.9.3)(vitest@4.1.4)':
dependencies:
@ -25758,15 +25676,6 @@ snapshots:
transitivePeerDependencies:
- vue
'@vue-macros/devtools@3.1.2(typescript@5.9.3)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))':
dependencies:
sirv: 3.0.2
vue: 3.5.32(typescript@5.9.3)
optionalDependencies:
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
transitivePeerDependencies:
- typescript
'@vue-macros/devtools@3.1.2(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))':
dependencies:
sirv: 3.0.2
@ -27908,7 +27817,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
electron-vite@5.0.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
electron-vite@5.0.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
dependencies:
'@babel/core': 7.29.0
'@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.29.0)
@ -27916,7 +27825,7 @@ snapshots:
esbuild: 0.25.12
magic-string: 0.30.21
picocolors: 1.1.1
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
vite: 8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
transitivePeerDependencies:
- supports-color
@ -31983,12 +31892,6 @@ snapshots:
preact: 10.28.1
web-vitals: 4.2.4
posthog-node@5.34.1(rxjs@7.8.2):
dependencies:
'@posthog/core': 1.29.1
optionalDependencies:
rxjs: 7.8.2
postject@1.0.0-alpha.6:
dependencies:
commander: 9.5.0
@ -33944,6 +33847,11 @@ snapshots:
'@unocss/preset-mini': 66.6.8
unocss: 66.6.8(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
unocss-preset-scrollbar@4.0.0(unocss@66.6.8(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))):
dependencies:
'@unocss/preset-mini': 66.6.8
unocss: 66.6.8(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
unocss@66.6.8(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(vite@6.4.2(@types/node@25.6.0)(jiti@2.6.1)(less@4.6.4)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
dependencies:
'@unocss/cli': 66.6.8
@ -34040,14 +33948,6 @@ snapshots:
unplugin: 2.3.11
vite: 8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
unplugin-combine@2.3.0(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(unplugin@2.3.11)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
optionalDependencies:
esbuild: 0.27.2
rolldown: 1.0.0-rc.16
rollup: 4.60.1
unplugin: 2.3.11
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
unplugin-combine@2.3.0(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(unplugin@2.3.11)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
optionalDependencies:
esbuild: 0.27.2
@ -34082,19 +33982,6 @@ snapshots:
transitivePeerDependencies:
- supports-color
unplugin-info@1.3.2(esbuild@0.27.2)(rollup@4.60.1)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
dependencies:
ci-info: 4.4.0
git-url-parse: 16.1.0
simple-git: 3.36.0
unplugin: 2.3.11
optionalDependencies:
esbuild: 0.27.2
rollup: 4.60.1
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
transitivePeerDependencies:
- supports-color
unplugin-info@1.3.2(esbuild@0.27.2)(rollup@4.60.1)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
dependencies:
ci-info: 4.4.0
@ -34204,17 +34091,6 @@ snapshots:
rollup: 4.60.1
vite: 6.4.2(@types/node@25.6.0)(jiti@2.6.1)(less@4.6.4)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
unplugin-yaml@4.1.0(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
dependencies:
'@rollup/pluginutils': 5.3.0(rollup@4.60.1)
unplugin: 3.0.0
yaml: 2.8.3
optionalDependencies:
esbuild: 0.27.2
rolldown: 1.0.0-rc.16
rollup: 4.60.1
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
unplugin@2.3.11:
dependencies:
'@jridgewell/remapping': 2.3.5
@ -34446,22 +34322,12 @@ snapshots:
- rollup
- supports-color
vite-dev-rpc@1.1.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
dependencies:
birpc: 2.9.0
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
vite-hot-client: 2.1.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
vite-dev-rpc@1.1.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
dependencies:
birpc: 2.9.0
vite: 8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
vite-hot-client: 2.1.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
vite-hot-client@2.1.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
dependencies:
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
vite-hot-client@2.1.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
dependencies:
vite: 8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
@ -34507,21 +34373,6 @@ snapshots:
- tsx
- yaml
vite-plugin-inspect@11.3.3(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
dependencies:
ansis: 4.2.0
debug: 4.4.3(supports-color@10.2.2)
error-stack-parser-es: 1.0.5
ohash: 2.0.11
open: 10.2.0
perfect-debounce: 2.1.0
sirv: 3.0.2
unplugin-utils: 0.3.1
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
vite-dev-rpc: 1.1.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
transitivePeerDependencies:
- supports-color
vite-plugin-inspect@11.3.3(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
dependencies:
ansis: 4.2.0
@ -34553,13 +34404,6 @@ snapshots:
- typescript
- ws
vite-plugin-mkcert@2.0.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
dependencies:
debug: 4.4.3(supports-color@10.2.2)
supports-color: 10.2.2
undici: 8.1.0
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
vite-plugin-mkcert@2.0.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
dependencies:
debug: 4.4.3(supports-color@10.2.2)
@ -34578,20 +34422,6 @@ snapshots:
transitivePeerDependencies:
- supports-color
vite-plugin-vue-devtools@8.1.1(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue@3.5.32(typescript@5.9.3)):
dependencies:
'@vue/devtools-core': 8.1.1(vue@3.5.32(typescript@5.9.3))
'@vue/devtools-kit': 8.1.1
'@vue/devtools-shared': 8.1.1
sirv: 3.0.2
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
vite-plugin-inspect: 11.3.3(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
vite-plugin-vue-inspector: 5.3.2(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
transitivePeerDependencies:
- '@nuxt/kit'
- supports-color
- vue
vite-plugin-vue-devtools@8.1.1(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue@3.5.32(typescript@5.9.3)):
dependencies:
'@vue/devtools-core': 8.1.1(vue@3.5.32(typescript@5.9.3))
@ -34606,21 +34436,6 @@ snapshots:
- supports-color
- vue
vite-plugin-vue-inspector@5.3.2(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
dependencies:
'@babel/core': 7.29.0
'@babel/plugin-proposal-decorators': 7.28.0(@babel/core@7.29.0)
'@babel/plugin-syntax-import-attributes': 7.28.6(@babel/core@7.29.0)
'@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.29.0)
'@babel/plugin-transform-typescript': 7.28.5(@babel/core@7.29.0)
'@vue/babel-plugin-jsx': 1.5.0(@babel/core@7.29.0)
'@vue/compiler-dom': 3.5.32
kolorist: 1.8.0
magic-string: 0.30.21
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
transitivePeerDependencies:
- supports-color
vite-plugin-vue-inspector@5.3.2(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
dependencies:
'@babel/core': 7.29.0
@ -34636,16 +34451,6 @@ snapshots:
transitivePeerDependencies:
- supports-color
vite-plugin-vue-layouts@0.11.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-router@5.0.4(@pinia/colada@1.2.1(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(@vue/compiler-sfc@3.5.32)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)):
dependencies:
debug: 4.4.3(supports-color@10.2.2)
fast-glob: 3.3.3
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
vue: 3.5.32(typescript@5.9.3)
vue-router: 5.0.4(@pinia/colada@1.2.1(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(@vue/compiler-sfc@3.5.32)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))
transitivePeerDependencies:
- supports-color
vite-plugin-vue-layouts@0.11.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-router@5.0.4(@pinia/colada@1.2.1(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(@vue/compiler-sfc@3.5.32)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)):
dependencies:
debug: 4.4.3(supports-color@10.2.2)
@ -34912,54 +34717,6 @@ snapshots:
- vue-tsc
- webpack
vue-macros@3.1.2(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@vueuse/core@14.2.1(vue@3.5.32(typescript@5.9.3)))(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.32(typescript@5.9.3)):
dependencies:
'@vue-macros/better-define': 3.1.2(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(vue@3.5.32(typescript@5.9.3))
'@vue-macros/boolean-prop': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/chain-call': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/common': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/config': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/define-emit': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/define-models': 3.1.2(@vueuse/core@14.2.1(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))
'@vue-macros/define-prop': 3.1.2(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(vue@3.5.32(typescript@5.9.3))
'@vue-macros/define-props': 3.1.2(@vue-macros/reactivity-transform@3.1.2(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))
'@vue-macros/define-props-refs': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/define-render': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/define-slots': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/define-stylex': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/devtools': 3.1.2(typescript@5.9.3)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
'@vue-macros/export-expose': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/export-props': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/export-render': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/hoist-static': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/jsx-directive': 3.1.2(typescript@5.9.3)
'@vue-macros/named-template': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/reactivity-transform': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/script-lang': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/setup-block': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/setup-component': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/setup-sfc': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/short-bind': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/short-emits': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/short-vmodel': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/volar': 3.1.2(typescript@5.9.3)(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.32(typescript@5.9.3))
unplugin: 2.3.11
unplugin-combine: 2.3.0(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(unplugin@2.3.11)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
unplugin-vue-define-options: 3.1.2(vue@3.5.32(typescript@5.9.3))
vue: 3.5.32(typescript@5.9.3)
transitivePeerDependencies:
- '@emnapi/core'
- '@emnapi/runtime'
- '@rspack/core'
- '@vueuse/core'
- esbuild
- rolldown
- rollup
- typescript
- vite
- vue-tsc
- webpack
vue-macros@3.1.2(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@vueuse/core@14.2.1(vue@3.5.32(typescript@5.9.3)))(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.32(typescript@5.9.3)):
dependencies:
'@vue-macros/better-define': 3.1.2(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(vue@3.5.32(typescript@5.9.3))

View file

@ -321,7 +321,6 @@ catalog:
postcss: ^8.5.10
postgres: ^3.4.9
posthog-js: 1.306.1
posthog-node: ^5.34.1
postprocessing: ^6.39.1
prismarine-block: ^1.23.0
prismarine-entity: ^2.6.0