chore(deps): bump dependencies

This commit is contained in:
Neko Ayaka
2026-08-26 19:01:04 +08:00
parent 4803f1c16f
commit 6a24582696
42 changed files with 10997 additions and 12681 deletions
+12 -10
View File
@@ -119,9 +119,9 @@ else {
const resource = resourceFromAttributes({
[ATTR_SERVICE_NAME]: serviceName,
[ATTR_SERVICE_VERSION]: env.npm_package_version || '0.0.0',
'service.namespace': serviceNamespace,
'service.instance.id': instanceId,
'deployment.environment': env.NODE_ENV || 'development',
'service.instance.id': instanceId,
'service.namespace': serviceNamespace,
})
// Traces fan out to independent processors. OTLP (Grafana Tempo) sees every
@@ -131,8 +131,8 @@ else {
const spanProcessors = []
if (otlpEndpoint) {
spanProcessors.push(new BatchSpanProcessor(new OTLPTraceExporter({
url: `${otlpEndpoint}/v1/traces`,
headers,
url: `${otlpEndpoint}/v1/traces`,
})))
}
// Langfuse runs on its OWN TracerProvider, isolated from the global NodeSDK
@@ -160,12 +160,12 @@ else {
resource,
sampler: new AlwaysOnSampler(),
spanProcessors: [new LangfuseSpanProcessor({
publicKey: env.LANGFUSE_PUBLIC_KEY,
secretKey: env.LANGFUSE_SECRET_KEY,
baseUrl: env.LANGFUSE_BASE_URL,
// Railway is long-running → batched. Flushed via
// langfuseProvider.shutdown() in the SIGTERM handler below.
exportMode: 'batched',
publicKey: env.LANGFUSE_PUBLIC_KEY,
secretKey: env.LANGFUSE_SECRET_KEY,
// Full override of Langfuse's default filter. Export ONLY spans the
// @langfuse/tracing SDK created (they carry `langfuse.*` attributes
// such as `langfuse.observation.type`). This provider should only ever
@@ -196,18 +196,20 @@ else {
// pointed at an empty URL.
...(otlpEndpoint
? {
logRecordProcessors: [new BatchLogRecordProcessor({
exporter: new OTLPLogExporter({
headers,
url: `${otlpEndpoint}/v1/logs`,
}),
})],
metricReaders: [new PeriodicExportingMetricReader({
exporter: new OTLPMetricExporter({
url: `${otlpEndpoint}/v1/metrics`,
headers,
url: `${otlpEndpoint}/v1/metrics`,
}),
exportIntervalMillis: 15_000,
exportTimeoutMillis: 10_000,
})],
logRecordProcessors: [new BatchLogRecordProcessor(new OTLPLogExporter({
url: `${otlpEndpoint}/v1/logs`,
headers,
}))],
}
: {}),
instrumentations: [
+1
View File
@@ -18,6 +18,7 @@
"dependencies": {
"@dotenvx/dotenvx": "catalog:",
"@electric-sql/pglite": "catalog:",
"@electric-sql/pglite-pgvector": "catalog:",
"@guiiai/logg": "catalog:",
"@hono/node-server": "catalog:",
"@hono/node-ws": "catalog:",
+47 -47
View File
@@ -45,47 +45,27 @@ function optionalNumberFromString(defaultValue: number, envKey: string, minimum:
}
const EnvSchema = object({
HOST: optional(string(), '0.0.0.0'),
PORT: optionalIntegerFromString(3000, 'PORT', 1),
API_SERVER_URL: optional(string(), 'http://localhost:3000'),
AUTH_SERVER_URL: optional(string(), 'http://localhost:3000'),
AUTH_SERVER_INTERNAL_URL: optional(string()),
// Canonical user-facing web app origin. Used as the Stripe redirect base
// (success_url / cancel_url / portal return_url) when a request has no trusted
// browser origin — notably the Electron desktop renderer, which loads from
// file:// and sends no usable web origin. Web/mobile requests keep returning to
// their own origin; only origin-less clients fall back to this.
WEB_APP_URL: optional(string(), 'https://airi.moeru.ai'),
// Comma-separated exact origins (e.g. Capacitor dev server `https://10.x:5273`).
// Prefer this over broad private-IP regex heuristics in production-like configs.
ADDITIONAL_TRUSTED_ORIGINS: optional(
AdditionalTrustedOriginsSchema,
'',
),
API_SERVER_URL: optional(string(), 'http://localhost:3000'),
AUTH_SERVER_INTERNAL_URL: optional(string()),
AUTH_SERVER_URL: optional(string(), 'http://localhost:3000'),
DATABASE_URL: pipe(string(), nonEmpty('DATABASE_URL is required')),
REDIS_URL: pipe(string(), nonEmpty('REDIS_URL is required')),
// Testing-only bearer token bypass. Keep unset in production. When set,
// Authorization: Bearer $TEST_AUTH_TOKEN resolves to the virtual user below
// through resolveRequestAuth without creating an Auth session row.
TEST_AUTH_TOKEN: optional(string(), ''),
TEST_AUTH_USER_ID: optional(pipe(string(), nonEmpty('TEST_AUTH_USER_ID must not be empty when set')), 'test-user'),
TEST_AUTH_USER_EMAIL: optional(pipe(string(), nonEmpty('TEST_AUTH_USER_EMAIL must not be empty when set')), 'test@example.com'),
TEST_AUTH_USER_NAME: optional(pipe(string(), nonEmpty('TEST_AUTH_USER_NAME must not be empty when set')), 'Test User'),
DB_POOL_CONNECTION_TIMEOUT_MS: optionalIntegerFromString(5000, 'DB_POOL_CONNECTION_TIMEOUT_MS', 1),
STRIPE_SECRET_KEY: optional(string()),
STRIPE_WEBHOOK_SECRET: optional(string()),
DB_POOL_IDLE_TIMEOUT_MS: optionalIntegerFromString(30000, 'DB_POOL_IDLE_TIMEOUT_MS', 1),
// 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)
// so they're hot-swappable via Pub/Sub invalidation. No env entries needed
// here.
DB_POOL_KEEPALIVE_INITIAL_DELAY_MS: optionalIntegerFromString(10000, 'DB_POOL_KEEPALIVE_INITIAL_DELAY_MS', 1),
// Database pool
DB_POOL_MAX: optionalIntegerFromString(20, 'DB_POOL_MAX', 1),
HOST: optional(string(), '0.0.0.0'),
// Envelope-encryption master key for in-process LLM/TTS router (KTD-5).
// Stored as base64-encoded 32 random bytes. Validator decodes + asserts the
// 32-byte length at parse time so a misconfigured key fails the deploy
@@ -107,27 +87,47 @@ const EnvSchema = object({
transform(b64 => Buffer.from(b64, 'base64')),
check(buf => buf.length === 32, 'LLM_ROUTER_MASTER_KEY_PREVIOUS must decode to exactly 32 bytes when set'),
)),
OTEL_DEBUG: optional(string()),
// Database pool
DB_POOL_MAX: optionalIntegerFromString(20, 'DB_POOL_MAX', 1),
DB_POOL_IDLE_TIMEOUT_MS: optionalIntegerFromString(30000, 'DB_POOL_IDLE_TIMEOUT_MS', 1),
DB_POOL_CONNECTION_TIMEOUT_MS: optionalIntegerFromString(5000, 'DB_POOL_CONNECTION_TIMEOUT_MS', 1),
DB_POOL_KEEPALIVE_INITIAL_DELAY_MS: optionalIntegerFromString(10000, 'DB_POOL_KEEPALIVE_INITIAL_DELAY_MS', 1),
// PostHog product-event forwarding for server-confirmed funnel facts.
// Defaults to the shared AIRI project key (same browser-safe phc_* key the
// client surfaces embed in posthog.config.ts), so forwarding is on out of
// the box. Set to an empty string to disable server-side product analytics.
POSTHOG_PROJECT_KEY: optional(string(), 'phc_pzjziJjrVZpa9SqnQqq0QEKvkmuCPH7GDTA6TbRTEf9'), // cspell:disable-line
POSTHOG_API_HOST: optional(string(), 'https://t.airi.build'),
// OpenTelemetry
OTEL_SERVICE_NAMESPACE: optional(string(), 'airi'),
OTEL_SERVICE_NAME: optional(string(), 'server'),
OTEL_TRACES_SAMPLING_RATIO: optionalNumberFromString(1, 'OTEL_TRACES_SAMPLING_RATIO', 0, 1),
OTEL_EXPORTER_OTLP_ENDPOINT: optional(string()),
OTEL_EXPORTER_OTLP_HEADERS: optional(string()),
OTEL_DEBUG: optional(string()),
// 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)
// so they're hot-swappable via Pub/Sub invalidation. No env entries needed
// here.
OTEL_SERVICE_NAME: optional(string(), 'server'),
// OpenTelemetry
OTEL_SERVICE_NAMESPACE: optional(string(), 'airi'),
OTEL_TRACES_SAMPLING_RATIO: optionalNumberFromString(1, 'OTEL_TRACES_SAMPLING_RATIO', 0, 1),
PORT: optionalIntegerFromString(3000, 'PORT', 1),
POSTHOG_API_HOST: optional(string(), 'https://t.airi.build'),
// PostHog product-event forwarding for server-confirmed funnel facts.
// Defaults to the shared AIRI project key (same browser-safe phc_* key the
// client surfaces embed in stage-shared/analytics/posthog), so forwarding is on out of
// the box. Set to an empty string to disable server-side product analytics.
POSTHOG_PROJECT_KEY: optional(string(), 'phc_pzjziJjrVZpa9SqnQqq0QEKvkmuCPH7GDTA6TbRTEf9'), // cspell:disable-line
REDIS_URL: pipe(string(), nonEmpty('REDIS_URL is required')),
STRIPE_SECRET_KEY: optional(string()),
STRIPE_WEBHOOK_SECRET: optional(string()),
// Testing-only bearer token bypass. Keep unset in production. When set,
// Authorization: Bearer $TEST_AUTH_TOKEN resolves to the virtual user below
// through resolveRequestAuth without creating an Auth session row.
TEST_AUTH_TOKEN: optional(string(), ''),
TEST_AUTH_USER_EMAIL: optional(pipe(string(), nonEmpty('TEST_AUTH_USER_EMAIL must not be empty when set')), 'test@example.com'),
TEST_AUTH_USER_ID: optional(pipe(string(), nonEmpty('TEST_AUTH_USER_ID must not be empty when set')), 'test-user'),
TEST_AUTH_USER_NAME: optional(pipe(string(), nonEmpty('TEST_AUTH_USER_NAME must not be empty when set')), 'Test User'),
// Canonical user-facing web app origin. Used as the Stripe redirect base
// (success_url / cancel_url / portal return_url) when a request has no trusted
// browser origin — notably the Electron desktop renderer, which loads from
// file:// and sends no usable web origin. Web/mobile requests keep returning to
// their own origin; only origin-less clients fall back to this.
WEB_APP_URL: optional(string(), 'https://airi.moeru.ai'),
})
export type Env = InferOutput<typeof EnvSchema>
+22
View File
@@ -0,0 +1,22 @@
import { sql } from 'drizzle-orm'
import { describe, expect, it } from 'vitest'
import { mockDB } from './mock-db'
describe('mockDB', () => {
it('registers the pgvector extension', async () => {
const db = await mockDB({})
// ROOT CAUSE:
//
// PGlite 0.5 moved pgvector from the main package to a separate package.
// The old subpath import prevents this test database from loading.
//
// The helper now registers the separate extension before it creates the schema.
const result = await db.execute<{ distance: number }>(sql`
SELECT '[1, 2, 3]'::vector <-> '[1, 2, 4]'::vector AS distance
`)
expect(result.rows).toEqual([{ distance: 1 }])
})
})
+1 -1
View File
@@ -1,7 +1,7 @@
import type { Database } from './db'
import { PGlite } from '@electric-sql/pglite'
import { vector } from '@electric-sql/pglite/vector'
import { vector } from '@electric-sql/pglite-pgvector'
import { sql } from 'drizzle-orm'
import { drizzle } from 'drizzle-orm/pglite'
import { vi } from 'vitest'
+19 -17
View File
@@ -51,27 +51,12 @@ else {
const resource = resourceFromAttributes({
[ATTR_SERVICE_NAME]: env.OTEL_SERVICE_NAME || 'auth-server',
[ATTR_SERVICE_VERSION]: env.npm_package_version || '0.0.0',
'service.namespace': env.OTEL_SERVICE_NAMESPACE || 'airi',
'service.instance.id': instanceId,
'deployment.environment': env.NODE_ENV || 'development',
'service.instance.id': instanceId,
'service.namespace': env.OTEL_SERVICE_NAMESPACE || 'airi',
})
const sdk = new NodeSDK({
resource,
sampler: new ParentBasedSampler({ root: new TraceIdRatioBasedSampler(samplingRatio) }),
spanProcessors: [new BatchSpanProcessor(new OTLPTraceExporter({
url: `${otlpEndpoint}/v1/traces`,
headers,
}))],
metricReaders: [new PeriodicExportingMetricReader({
exporter: new OTLPMetricExporter({ url: `${otlpEndpoint}/v1/metrics`, headers }),
exportIntervalMillis: 15_000,
exportTimeoutMillis: 10_000,
})],
logRecordProcessors: [new BatchLogRecordProcessor(new OTLPLogExporter({
url: `${otlpEndpoint}/v1/logs`,
headers,
}))],
instrumentations: [
new HttpInstrumentation({ disableIncomingRequestInstrumentation: true }),
new PgInstrumentation({ enhancedDatabaseReporting: true }),
@@ -79,6 +64,23 @@ else {
new RuntimeNodeInstrumentation(),
new UndiciInstrumentation(),
],
logRecordProcessors: [new BatchLogRecordProcessor({
exporter: new OTLPLogExporter({
headers,
url: `${otlpEndpoint}/v1/logs`,
}),
})],
metricReaders: [new PeriodicExportingMetricReader({
exporter: new OTLPMetricExporter({ headers, url: `${otlpEndpoint}/v1/metrics` }),
exportIntervalMillis: 15_000,
exportTimeoutMillis: 10_000,
})],
resource,
sampler: new ParentBasedSampler({ root: new TraceIdRatioBasedSampler(samplingRatio) }),
spanProcessors: [new BatchSpanProcessor(new OTLPTraceExporter({
headers,
url: `${otlpEndpoint}/v1/traces`,
}))],
})
sdk.start()
+1
View File
@@ -54,6 +54,7 @@
"@electric-sql/pglite": "catalog:",
"@types/pg": "catalog:",
"drizzle-kit": "catalog:",
"kysely": "catalog:",
"typescript": "catalog:"
}
}