@spfn/core 0.2.0-beta.6 → 0.2.0-beta.64
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +181 -1281
- package/dist/authz/index.d.ts +34 -0
- package/dist/authz/index.js +415 -0
- package/dist/authz/index.js.map +1 -0
- package/dist/{boss-DI1r4kTS.d.ts → boss-gXhgctn6.d.ts} +40 -0
- package/dist/cache/index.js +42 -30
- package/dist/cache/index.js.map +1 -1
- package/dist/codegen/index.d.ts +55 -8
- package/dist/codegen/index.js +183 -7
- package/dist/codegen/index.js.map +1 -1
- package/dist/config/index.d.ts +585 -6
- package/dist/config/index.js +116 -5
- package/dist/config/index.js.map +1 -1
- package/dist/db/index.d.ts +270 -4
- package/dist/db/index.js +404 -60
- package/dist/db/index.js.map +1 -1
- package/dist/define-middleware-DuXD8Hvu.d.ts +167 -0
- package/dist/env/index.d.ts +26 -2
- package/dist/env/index.js +15 -5
- package/dist/env/index.js.map +1 -1
- package/dist/env/loader.d.ts +26 -19
- package/dist/env/loader.js +32 -25
- package/dist/env/loader.js.map +1 -1
- package/dist/errors/index.d.ts +10 -0
- package/dist/errors/index.js +20 -2
- package/dist/errors/index.js.map +1 -1
- package/dist/event/index.d.ts +33 -3
- package/dist/event/index.js +24 -3
- package/dist/event/index.js.map +1 -1
- package/dist/event/sse/client.d.ts +42 -3
- package/dist/event/sse/client.js +128 -45
- package/dist/event/sse/client.js.map +1 -1
- package/dist/event/sse/index.d.ts +12 -5
- package/dist/event/sse/index.js +271 -32
- package/dist/event/sse/index.js.map +1 -1
- package/dist/event/ws/client.d.ts +59 -0
- package/dist/event/ws/client.js +273 -0
- package/dist/event/ws/client.js.map +1 -0
- package/dist/event/ws/index.d.ts +94 -0
- package/dist/event/ws/index.js +272 -0
- package/dist/event/ws/index.js.map +1 -0
- package/dist/job/index.d.ts +2 -2
- package/dist/job/index.js +155 -42
- package/dist/job/index.js.map +1 -1
- package/dist/logger/index.d.ts +5 -0
- package/dist/logger/index.js +14 -0
- package/dist/logger/index.js.map +1 -1
- package/dist/middleware/index.d.ts +243 -2
- package/dist/middleware/index.js +1323 -13
- package/dist/middleware/index.js.map +1 -1
- package/dist/nextjs/index.d.ts +2 -2
- package/dist/nextjs/index.js +77 -31
- package/dist/nextjs/index.js.map +1 -1
- package/dist/nextjs/server.d.ts +53 -23
- package/dist/nextjs/server.js +197 -66
- package/dist/nextjs/server.js.map +1 -1
- package/dist/route/index.d.ts +138 -146
- package/dist/route/index.js +238 -22
- package/dist/route/index.js.map +1 -1
- package/dist/security/index.d.ts +83 -0
- package/dist/security/index.js +173 -0
- package/dist/security/index.js.map +1 -0
- package/dist/server/index.d.ts +450 -17
- package/dist/server/index.js +1756 -277
- package/dist/server/index.js.map +1 -1
- package/dist/{router-Di7ENoah.d.ts → token-manager-jKD_EsSE.d.ts} +121 -1
- package/dist/{types-D_N_U-Py.d.ts → types-7Mhoxnnt.d.ts} +21 -1
- package/dist/types-BFB72jbM.d.ts +282 -0
- package/dist/types-DVjf37yO.d.ts +205 -0
- package/docs/file-upload.md +717 -0
- package/package.json +235 -208
- package/dist/types-B-e_f2dQ.d.ts +0 -121
package/dist/config/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { defineEnvSchema,
|
|
1
|
+
import { defineEnvSchema, envBoolean, envNumber, envEnum, envString, envUrl, parseRedisUrl, parsePostgresUrl, createEnvRegistry } from '@spfn/core/env';
|
|
2
2
|
|
|
3
3
|
// src/config/index.ts
|
|
4
4
|
var coreEnvSchema = defineEnvSchema({
|
|
5
5
|
// ========================================================================
|
|
6
6
|
// Core Environment
|
|
7
7
|
// ========================================================================
|
|
8
|
-
NODE_ENV: envEnum(["local", "development", "production", "test"], {
|
|
8
|
+
NODE_ENV: envEnum(["local", "development", "staging", "production", "test"], {
|
|
9
9
|
description: "Node.js runtime environment",
|
|
10
10
|
default: "local",
|
|
11
11
|
nextjs: true
|
|
@@ -42,6 +42,11 @@ var coreEnvSchema = defineEnvSchema({
|
|
|
42
42
|
default: 10,
|
|
43
43
|
examples: [10, 20, 50]
|
|
44
44
|
}),
|
|
45
|
+
DB_POOL_READ_MAX: envNumber({
|
|
46
|
+
description: "Maximum connections for the read-replica pool. Defaults to DB_POOL_MAX. Set lower so write.max + read.max stays under the server max_connections (each process otherwise opens up to 2 \xD7 DB_POOL_MAX when a replica is configured).",
|
|
47
|
+
required: false,
|
|
48
|
+
examples: [5, 10, 20]
|
|
49
|
+
}),
|
|
45
50
|
DB_POOL_IDLE_TIMEOUT: envNumber({
|
|
46
51
|
description: "Database connection idle timeout in seconds",
|
|
47
52
|
default: 30,
|
|
@@ -124,6 +129,19 @@ var coreEnvSchema = defineEnvSchema({
|
|
|
124
129
|
default: 3e4,
|
|
125
130
|
examples: [1e4, 3e4, 6e4]
|
|
126
131
|
}),
|
|
132
|
+
TRANSACTION_IDLE_TIMEOUT: envNumber({
|
|
133
|
+
description: "Max time (ms) a transaction may sit idle (no running query) before Postgres terminates it and reclaims the pooled connection. Guards against external I/O held inside a transaction starving the connection pool. 0 disables.",
|
|
134
|
+
default: 3e4,
|
|
135
|
+
examples: [1e4, 3e4, 0]
|
|
136
|
+
}),
|
|
137
|
+
// ========================================================================
|
|
138
|
+
// Jobs (pg-boss)
|
|
139
|
+
// ========================================================================
|
|
140
|
+
JOB_POLLING_INTERVAL_SECONDS: envNumber({
|
|
141
|
+
description: "How often each pg-boss worker polls the DB for new jobs (seconds). Lower = faster pickup, more idle SELECT load; higher = less DB chatter, slower pickup. Per-job override via job options.",
|
|
142
|
+
default: 2,
|
|
143
|
+
examples: [1, 2, 10]
|
|
144
|
+
}),
|
|
127
145
|
// ========================================================================
|
|
128
146
|
// Database - Development
|
|
129
147
|
// ========================================================================
|
|
@@ -204,6 +222,24 @@ var coreEnvSchema = defineEnvSchema({
|
|
|
204
222
|
default: true,
|
|
205
223
|
examples: [true, false]
|
|
206
224
|
}),
|
|
225
|
+
CACHE_MAX_RETRIES_PER_REQUEST: envNumber({
|
|
226
|
+
description: "Max ioredis retries per command before it rejects (fail fast instead of hanging on a cache outage). ioredis default is 20.",
|
|
227
|
+
default: 3,
|
|
228
|
+
examples: [1, 3, 20]
|
|
229
|
+
}),
|
|
230
|
+
CACHE_ENABLE_OFFLINE_QUEUE: envBoolean({
|
|
231
|
+
description: "Queue commands while the cache is disconnected (true) vs reject immediately for strict fail-fast (false). Default true keeps resilience to brief blips.",
|
|
232
|
+
default: true,
|
|
233
|
+
examples: [true, false]
|
|
234
|
+
}),
|
|
235
|
+
// ========================================================================
|
|
236
|
+
// Database - Query limits
|
|
237
|
+
// ========================================================================
|
|
238
|
+
DB_MAX_ROWS: envNumber({
|
|
239
|
+
description: "Safety ceiling for rows returned by repository findMany (0 = unlimited). When >0, an unbounded query is capped and an explicit limit is clamped, guarding against accidentally loading a whole large table.",
|
|
240
|
+
default: 0,
|
|
241
|
+
examples: [0, 1e3, 1e4]
|
|
242
|
+
}),
|
|
207
243
|
// ========================================================================
|
|
208
244
|
// Server - Core
|
|
209
245
|
// ========================================================================
|
|
@@ -237,9 +273,27 @@ var coreEnvSchema = defineEnvSchema({
|
|
|
237
273
|
examples: [3e4, 6e4, 12e4]
|
|
238
274
|
}),
|
|
239
275
|
SHUTDOWN_TIMEOUT: envNumber({
|
|
240
|
-
description: "Graceful shutdown timeout in milliseconds",
|
|
241
|
-
default:
|
|
242
|
-
examples: [
|
|
276
|
+
description: "Graceful shutdown timeout in milliseconds (must be less than k8s terminationGracePeriodSeconds minus preStop sleep, with safety margin)",
|
|
277
|
+
default: 28e4,
|
|
278
|
+
examples: [3e4, 12e4, 28e4]
|
|
279
|
+
}),
|
|
280
|
+
// ========================================================================
|
|
281
|
+
// Fetch (Node.js undici) - outbound HTTP request timeout
|
|
282
|
+
// ========================================================================
|
|
283
|
+
FETCH_CONNECT_TIMEOUT: envNumber({
|
|
284
|
+
description: "Fetch TCP connection timeout in milliseconds (time to establish socket connection to upstream server)",
|
|
285
|
+
default: 1e4,
|
|
286
|
+
examples: [5e3, 1e4, 3e4]
|
|
287
|
+
}),
|
|
288
|
+
FETCH_HEADERS_TIMEOUT: envNumber({
|
|
289
|
+
description: "Fetch headers timeout in milliseconds (time to receive response headers after request sent)",
|
|
290
|
+
default: 3e5,
|
|
291
|
+
examples: [12e4, 3e5, 6e5]
|
|
292
|
+
}),
|
|
293
|
+
FETCH_BODY_TIMEOUT: envNumber({
|
|
294
|
+
description: "Fetch body timeout in milliseconds (time between body data chunks from upstream server)",
|
|
295
|
+
default: 3e5,
|
|
296
|
+
examples: [12e4, 3e5, 6e5]
|
|
243
297
|
}),
|
|
244
298
|
// ========================================================================
|
|
245
299
|
// Next.js Integration
|
|
@@ -261,6 +315,63 @@ var coreEnvSchema = defineEnvSchema({
|
|
|
261
315
|
required: false,
|
|
262
316
|
nextjs: true,
|
|
263
317
|
examples: ["http://localhost:3790", "https://your-app.com"]
|
|
318
|
+
}),
|
|
319
|
+
RPC_PROXY_TIMEOUT: envNumber({
|
|
320
|
+
description: "RPC proxy request timeout in milliseconds (AbortController timeout for proxied requests to backend, should be shorter than FETCH_HEADERS_TIMEOUT)",
|
|
321
|
+
default: 12e4,
|
|
322
|
+
nextjs: true,
|
|
323
|
+
examples: [6e4, 12e4, 28e4]
|
|
324
|
+
}),
|
|
325
|
+
// ========================================================================
|
|
326
|
+
// Proxy → Backend trust (HMAC signing)
|
|
327
|
+
// ========================================================================
|
|
328
|
+
SPFN_PROXY_SECRET: envString({
|
|
329
|
+
description: "Shared secret for signing proxy\u2192backend requests (HMAC-SHA256). Read by BOTH processes \u2014 the Next.js proxy (to sign) and the SPFN backend (to verify) \u2014 so it belongs in .env.local (loaded by both; the backend reads it via loadEnv, Next.js reads it server-side without exposing it to the browser). Set the SAME value on both. Leave unset to disable proxy-guard signing.",
|
|
330
|
+
required: false,
|
|
331
|
+
sensitive: true,
|
|
332
|
+
nextjs: true,
|
|
333
|
+
examples: ["<32+ byte random hex>", "v2:<32+ byte random hex>"]
|
|
334
|
+
}),
|
|
335
|
+
SPFN_PROXY_SECRET_PREVIOUS: envString({
|
|
336
|
+
description: "Previous (grace) proxy keys still accepted for verification during rotation \u2014 comma-separated <keyId>:<secret>. The proxy never signs with these; they only keep requests signed with the prior key verifying until a rollout settles. Backend-only (verification), so it belongs in .env.server, NOT exposed to the Next.js process.",
|
|
337
|
+
required: false,
|
|
338
|
+
sensitive: true,
|
|
339
|
+
nextjs: false,
|
|
340
|
+
examples: ["v1:<old secret>", "v1:<old>,v0:<older>"]
|
|
341
|
+
}),
|
|
342
|
+
TRUSTED_PROXY_HOPS: envNumber({
|
|
343
|
+
description: "Number of trusted reverse proxies in front of the Next.js proxy (e.g. cloud LB + nginx = 2). Read by the proxy to extract the real client IP from the inbound X-Forwarded-For (counting from the right, which your own infra appends and a client cannot spoof) and forward it to the backend for rate limiting. Set it to your actual hop count; too low trusts a client-spoofable entry, too high collapses users behind a shared proxy IP.",
|
|
344
|
+
default: 1,
|
|
345
|
+
nextjs: true,
|
|
346
|
+
examples: [1, 2, 3]
|
|
347
|
+
}),
|
|
348
|
+
// ========================================================================
|
|
349
|
+
// Rate limiting (global default limiter)
|
|
350
|
+
// ========================================================================
|
|
351
|
+
RATE_LIMIT_MODE: envEnum(["off", "on"], {
|
|
352
|
+
description: `Global default rate limiter. "off": only routes tagged with rateLimitPolicy() are limited. "on": every named-middleware route gets the default limit too (opt out per route with .skip(['rateLimit'])). Health/SSE/WebSocket endpoints are always exempt. Overridden by defineServerConfig().rateLimit({ mode }).`,
|
|
353
|
+
default: "off"
|
|
354
|
+
}),
|
|
355
|
+
RATE_LIMIT_DEFAULT_LIMIT: envNumber({
|
|
356
|
+
description: "Max requests per window for the global default limiter (RATE_LIMIT_MODE=on), counted per route and per client IP.",
|
|
357
|
+
default: 100,
|
|
358
|
+
examples: [60, 100, 300]
|
|
359
|
+
}),
|
|
360
|
+
RATE_LIMIT_DEFAULT_WINDOW_MS: envNumber({
|
|
361
|
+
description: "Window length in milliseconds for the global default limiter.",
|
|
362
|
+
default: 6e4,
|
|
363
|
+
examples: [1e3, 6e4]
|
|
364
|
+
}),
|
|
365
|
+
RATE_LIMIT_FAIL_CLOSED: envBoolean({
|
|
366
|
+
description: "When the cache (Redis/Valkey) backing the limiter is unavailable, reject with 429 instead of allowing requests through. Default false (fail open) so development without a cache still works.",
|
|
367
|
+
default: false
|
|
368
|
+
}),
|
|
369
|
+
// ========================================================================
|
|
370
|
+
// Outbound request safety (SSRF)
|
|
371
|
+
// ========================================================================
|
|
372
|
+
SAFE_FETCH_BLOCK_PRIVATE_IPS: envBoolean({
|
|
373
|
+
description: "Default for safeFetch (@spfn/core/security): block outbound requests that resolve to private/reserved IP ranges, including the cloud metadata address. Keep true in production; set false only for trusted internal-network calls in development. Overridden by defineServerConfig().outboundFetch({ blockPrivateIps }).",
|
|
374
|
+
default: true
|
|
264
375
|
})
|
|
265
376
|
});
|
|
266
377
|
|
package/dist/config/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/config/schema.ts","../../src/config/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Core Package Environment Variable Schema\n *\n * Centralized schema definition for all environment variables used in @spfn/core.\n * This provides type safety, validation, and documentation for environment configuration.\n *\n * @module config/schema\n */\n\nimport {\n defineEnvSchema,\n envEnum,\n envNumber,\n envBoolean,\n envUrl,\n envString,\n parsePostgresUrl,\n parseRedisUrl,\n} from '@spfn/core/env';\n\n/**\n * Core package environment variable schema\n *\n * Defines all environment variables with:\n * - Type information\n * - Default values\n * - Validation rules\n * - Documentation\n *\n * @example\n * ```typescript\n * import { coreEnvSchema } from '@spfn/core/config';\n *\n * // Access schema information\n * console.log(coreEnvSchema.DB_POOL_MAX.description);\n * console.log(coreEnvSchema.DB_POOL_MAX.default);\n * ```\n */\nexport const coreEnvSchema = defineEnvSchema({\n // ========================================================================\n // Core Environment\n // ========================================================================\n\n NODE_ENV: envEnum(['local', 'development', 'production', 'test'] as const, {\n description: 'Node.js runtime environment',\n default: 'local',\n nextjs: true,\n }),\n\n // ========================================================================\n // Database - Connection\n // ========================================================================\n\n DATABASE_URL: envString({\n description: 'Primary database connection URL',\n required: false,\n sensitive: true,\n validator: parsePostgresUrl,\n examples: ['postgresql://user:password@localhost:5432/dbname'],\n }),\n\n DATABASE_WRITE_URL: envString({\n description: 'Write database URL (master-replica pattern)',\n required: false,\n sensitive: true,\n validator: parsePostgresUrl,\n examples: ['postgresql://user:password@master:5432/dbname'],\n }),\n\n DATABASE_READ_URL: envString({\n description: 'Read database URL (master-replica pattern)',\n required: false,\n sensitive: true,\n validator: parsePostgresUrl,\n examples: ['postgresql://user:password@replica:5432/dbname'],\n }),\n\n\n // ========================================================================\n // Database - Connection Pool\n // ========================================================================\n\n DB_POOL_MAX: envNumber({\n description: 'Maximum number of database connections in pool',\n default: 10,\n examples: [10, 20, 50],\n }),\n\n DB_POOL_IDLE_TIMEOUT: envNumber({\n description: 'Database connection idle timeout in seconds',\n default: 30,\n examples: [20, 30, 60],\n }),\n\n // ========================================================================\n // Database - Retry Configuration\n // ========================================================================\n\n DB_RETRY_MAX: envNumber({\n description: 'Maximum number of database connection retry attempts',\n default: 3,\n examples: [3, 5, 10],\n }),\n\n DB_RETRY_INITIAL_DELAY: envNumber({\n description: 'Initial delay between database retry attempts (milliseconds)',\n default: 100,\n examples: [50, 100, 200],\n }),\n\n DB_RETRY_MAX_DELAY: envNumber({\n description: 'Maximum delay cap for database retry attempts (milliseconds)',\n default: 10000,\n examples: [5000, 10000, 30000],\n }),\n\n DB_RETRY_FACTOR: envNumber({\n description: 'Exponential backoff factor for database retry delays',\n default: 2,\n examples: [2, 1.5, 3],\n }),\n\n // ========================================================================\n // Database - Health Check\n // ========================================================================\n\n DB_HEALTH_CHECK_ENABLED: envBoolean({\n description: 'Enable periodic database health checks',\n default: true,\n examples: [true, false],\n }),\n\n DB_HEALTH_CHECK_INTERVAL: envNumber({\n description: 'Database health check interval (milliseconds)',\n default: 60000,\n examples: [30000, 60000, 120000],\n }),\n\n DB_HEALTH_CHECK_RECONNECT: envBoolean({\n description: 'Reconnect to database on health check failure',\n default: true,\n examples: [true, false],\n }),\n\n DB_HEALTH_CHECK_MAX_RETRIES: envNumber({\n description: 'Maximum health check retry attempts before marking as failed',\n default: 3,\n examples: [3, 5, 10],\n }),\n\n DB_HEALTH_CHECK_RETRY_INTERVAL: envNumber({\n description: 'Interval between health check retry attempts (milliseconds)',\n default: 5000,\n examples: [5000, 10000, 15000],\n }),\n\n // ========================================================================\n // Database - Monitoring\n // ========================================================================\n\n DB_MONITORING_ENABLED: envBoolean({\n description: 'Enable database query performance monitoring',\n default: false,\n examples: [true, false],\n }),\n\n DB_MONITORING_SLOW_THRESHOLD: envNumber({\n description: 'Slow query threshold for monitoring (milliseconds)',\n default: 1000,\n examples: [500, 1000, 2000],\n }),\n\n DB_MONITORING_LOG_QUERIES: envBoolean({\n description: 'Log all database queries (not just slow queries)',\n default: false,\n examples: [true, false],\n }),\n\n // ========================================================================\n // Database - Transaction\n // ========================================================================\n\n TRANSACTION_TIMEOUT: envNumber({\n description: 'Transaction timeout in milliseconds',\n default: 30000,\n examples: [10000, 30000, 60000],\n }),\n\n // ========================================================================\n // Database - Development\n // ========================================================================\n\n DB_DEBUG_TRACE: envBoolean({\n description: 'Enable detailed debug tracing for database operations',\n default: false,\n examples: [true, false],\n }),\n\n // ========================================================================\n // Drizzle ORM\n // ========================================================================\n\n DRIZZLE_SCHEMA_PATH: envString({\n description: 'Path to Drizzle schema configuration',\n required: false,\n default: './src/server/entities/config.ts',\n examples: ['./src/db/schema.ts', './src/server/entities/config.ts'],\n }),\n\n DRIZZLE_OUT_DIR: envString({\n description: 'Output directory for Drizzle migrations',\n required: false,\n default: './drizzle',\n examples: ['./drizzle', './migrations'],\n }),\n\n // ========================================================================\n // Logger - Core\n // ========================================================================\n\n SPFN_LOG_LEVEL: envEnum(['debug', 'info', 'warn', 'error', 'fatal'] as const, {\n description: 'Minimum log level to output',\n default: 'info'\n }),\n\n // ========================================================================\n // Cache (Redis/Valkey)\n // ========================================================================\n\n CACHE_URL: envString({\n description: 'Single Redis/Valkey instance URL',\n required: false,\n sensitive: true,\n validator: parseRedisUrl,\n examples: ['redis://localhost:6379', 'rediss://secure.cache.com:6380'],\n }),\n\n CACHE_WRITE_URL: envString({\n description: 'Master Redis/Valkey URL for writes (master-replica pattern)',\n required: false,\n sensitive: true,\n validator: parseRedisUrl,\n examples: ['redis://master:6379'],\n }),\n\n CACHE_READ_URL: envString({\n description: 'Replica Redis/Valkey URL for reads (master-replica pattern)',\n required: false,\n sensitive: true,\n validator: parseRedisUrl,\n examples: ['redis://replica:6379'],\n }),\n\n CACHE_SENTINEL_HOSTS: envString({\n description: 'Comma-separated Redis Sentinel hosts',\n required: false,\n examples: ['sentinel1:26379,sentinel2:26379'],\n }),\n\n CACHE_CLUSTER_NODES: envString({\n description: 'Comma-separated Redis Cluster nodes',\n required: false,\n examples: ['node1:6379,node2:6379,node3:6379'],\n }),\n\n CACHE_MASTER_NAME: envString({\n description: 'Redis Sentinel master name',\n required: false,\n examples: ['mymaster'],\n }),\n\n CACHE_PASSWORD: envString({\n description: 'Redis/Valkey authentication password',\n required: false,\n sensitive: true,\n examples: ['your-redis-password'],\n }),\n\n CACHE_TLS_REJECT_UNAUTHORIZED: envBoolean({\n description: 'Verify TLS certificates for secure Redis connections',\n default: true,\n examples: [true, false],\n }),\n\n // ========================================================================\n // Server - Core\n // ========================================================================\n\n PORT: envNumber({\n description: 'Server port number',\n default: 4000,\n examples: [3000, 4000, 8080],\n }),\n\n HOST: envString({\n description: 'Server hostname',\n default: 'localhost',\n required: false,\n examples: ['localhost', '0.0.0.0', '127.0.0.1'],\n }),\n\n // ========================================================================\n // Server - Timeout\n // ========================================================================\n\n SERVER_TIMEOUT: envNumber({\n description: 'Request timeout in milliseconds',\n default: 120000,\n examples: [60000, 120000, 300000],\n }),\n\n SERVER_KEEPALIVE_TIMEOUT: envNumber({\n description: 'Keep-alive timeout in milliseconds',\n default: 65000,\n examples: [30000, 65000, 120000],\n }),\n\n SERVER_HEADERS_TIMEOUT: envNumber({\n description: 'Headers timeout in milliseconds',\n default: 60000,\n examples: [30000, 60000, 120000],\n }),\n\n SHUTDOWN_TIMEOUT: envNumber({\n description: 'Graceful shutdown timeout in milliseconds',\n default: 30000,\n examples: [10000, 30000, 60000],\n }),\n\n // ========================================================================\n // Next.js Integration\n // ========================================================================\n\n SPFN_API_URL: envUrl({\n description: 'SPFN API URL (used by Next.js to call backend)',\n required: true,\n nextjs: true,\n examples: ['http://localhost:8790', 'https://api.your-app.com'],\n }),\n\n NEXT_PUBLIC_SPFN_API_URL: envUrl({\n description: 'SPFN API URL (used by Next.js to call backend)',\n required: true,\n nextjs: true,\n examples: ['http://localhost:8790', 'https://api.your-app.com'],\n }),\n\n SPFN_APP_URL: envUrl({\n description: 'Next.js application URL (used by SPFN server)',\n required: false,\n nextjs: true,\n examples: ['http://localhost:3790', 'https://your-app.com'],\n }),\n});","/**\n * Core Package Configuration\n *\n * @example\n * ```typescript\n * import { registry } from '@spfn/core/config';\n *\n * const env = registry.validate();\n * console.log(env.DB_POOL_MAX);\n * ```\n *\n * @module config\n */\n\nimport { createEnvRegistry } from '@spfn/core/env';\nimport { coreEnvSchema } from './schema';\n\n/**\n * Core environment schema\n */\nexport { coreEnvSchema as envSchema } from './schema';\n\n/**\n * Environment registry\n *\n * @example\n * ```typescript\n * // Reset for testing\n * registry.reset();\n * ```\n */\nexport const registry = createEnvRegistry(coreEnvSchema);\n\n/**\n * Validated environment configuration\n */\nexport const env = registry.validate();"]}
|
|
1
|
+
{"version":3,"sources":["../../src/config/schema.ts","../../src/config/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Core Package Environment Variable Schema\n *\n * Centralized schema definition for all environment variables used in @spfn/core.\n * This provides type safety, validation, and documentation for environment configuration.\n *\n * @module config/schema\n */\n\nimport {\n defineEnvSchema,\n envEnum,\n envNumber,\n envBoolean,\n envUrl,\n envString,\n parsePostgresUrl,\n parseRedisUrl,\n} from '@spfn/core/env';\n\n/**\n * Core package environment variable schema\n *\n * Defines all environment variables with:\n * - Type information\n * - Default values\n * - Validation rules\n * - Documentation\n *\n * @example\n * ```typescript\n * import { coreEnvSchema } from '@spfn/core/config';\n *\n * // Access schema information\n * console.log(coreEnvSchema.DB_POOL_MAX.description);\n * console.log(coreEnvSchema.DB_POOL_MAX.default);\n * ```\n */\nexport const coreEnvSchema = defineEnvSchema({\n // ========================================================================\n // Core Environment\n // ========================================================================\n\n NODE_ENV: envEnum(['local', 'development', 'staging', 'production', 'test'] as const, {\n description: 'Node.js runtime environment',\n default: 'local',\n nextjs: true,\n }),\n\n // ========================================================================\n // Database - Connection\n // ========================================================================\n\n DATABASE_URL: envString({\n description: 'Primary database connection URL',\n required: false,\n sensitive: true,\n validator: parsePostgresUrl,\n examples: ['postgresql://user:password@localhost:5432/dbname'],\n }),\n\n DATABASE_WRITE_URL: envString({\n description: 'Write database URL (master-replica pattern)',\n required: false,\n sensitive: true,\n validator: parsePostgresUrl,\n examples: ['postgresql://user:password@master:5432/dbname'],\n }),\n\n DATABASE_READ_URL: envString({\n description: 'Read database URL (master-replica pattern)',\n required: false,\n sensitive: true,\n validator: parsePostgresUrl,\n examples: ['postgresql://user:password@replica:5432/dbname'],\n }),\n\n // ========================================================================\n // Database - Connection Pool\n // ========================================================================\n\n DB_POOL_MAX: envNumber({\n description: 'Maximum number of database connections in pool',\n default: 10,\n examples: [10, 20, 50],\n }),\n\n DB_POOL_READ_MAX: envNumber({\n description: 'Maximum connections for the read-replica pool. Defaults to DB_POOL_MAX. Set lower so write.max + read.max stays under the server max_connections (each process otherwise opens up to 2 × DB_POOL_MAX when a replica is configured).',\n required: false,\n examples: [5, 10, 20],\n }),\n\n DB_POOL_IDLE_TIMEOUT: envNumber({\n description: 'Database connection idle timeout in seconds',\n default: 30,\n examples: [20, 30, 60],\n }),\n\n // ========================================================================\n // Database - Retry Configuration\n // ========================================================================\n\n DB_RETRY_MAX: envNumber({\n description: 'Maximum number of database connection retry attempts',\n default: 3,\n examples: [3, 5, 10],\n }),\n\n DB_RETRY_INITIAL_DELAY: envNumber({\n description: 'Initial delay between database retry attempts (milliseconds)',\n default: 100,\n examples: [50, 100, 200],\n }),\n\n DB_RETRY_MAX_DELAY: envNumber({\n description: 'Maximum delay cap for database retry attempts (milliseconds)',\n default: 10000,\n examples: [5000, 10000, 30000],\n }),\n\n DB_RETRY_FACTOR: envNumber({\n description: 'Exponential backoff factor for database retry delays',\n default: 2,\n examples: [2, 1.5, 3],\n }),\n\n // ========================================================================\n // Database - Health Check\n // ========================================================================\n\n DB_HEALTH_CHECK_ENABLED: envBoolean({\n description: 'Enable periodic database health checks',\n default: true,\n examples: [true, false],\n }),\n\n DB_HEALTH_CHECK_INTERVAL: envNumber({\n description: 'Database health check interval (milliseconds)',\n default: 60000,\n examples: [30000, 60000, 120000],\n }),\n\n DB_HEALTH_CHECK_RECONNECT: envBoolean({\n description: 'Reconnect to database on health check failure',\n default: true,\n examples: [true, false],\n }),\n\n DB_HEALTH_CHECK_MAX_RETRIES: envNumber({\n description: 'Maximum health check retry attempts before marking as failed',\n default: 3,\n examples: [3, 5, 10],\n }),\n\n DB_HEALTH_CHECK_RETRY_INTERVAL: envNumber({\n description: 'Interval between health check retry attempts (milliseconds)',\n default: 5000,\n examples: [5000, 10000, 15000],\n }),\n\n // ========================================================================\n // Database - Monitoring\n // ========================================================================\n\n DB_MONITORING_ENABLED: envBoolean({\n description: 'Enable database query performance monitoring',\n default: false,\n examples: [true, false],\n }),\n\n DB_MONITORING_SLOW_THRESHOLD: envNumber({\n description: 'Slow query threshold for monitoring (milliseconds)',\n default: 1000,\n examples: [500, 1000, 2000],\n }),\n\n DB_MONITORING_LOG_QUERIES: envBoolean({\n description: 'Log all database queries (not just slow queries)',\n default: false,\n examples: [true, false],\n }),\n\n // ========================================================================\n // Database - Transaction\n // ========================================================================\n\n TRANSACTION_TIMEOUT: envNumber({\n description: 'Transaction timeout in milliseconds',\n default: 30000,\n examples: [10000, 30000, 60000],\n }),\n\n TRANSACTION_IDLE_TIMEOUT: envNumber({\n description: 'Max time (ms) a transaction may sit idle (no running query) before Postgres terminates it and reclaims the pooled connection. Guards against external I/O held inside a transaction starving the connection pool. 0 disables.',\n default: 30000,\n examples: [10000, 30000, 0],\n }),\n\n // ========================================================================\n // Jobs (pg-boss)\n // ========================================================================\n\n JOB_POLLING_INTERVAL_SECONDS: envNumber({\n description: 'How often each pg-boss worker polls the DB for new jobs (seconds). Lower = faster pickup, more idle SELECT load; higher = less DB chatter, slower pickup. Per-job override via job options.',\n default: 2,\n examples: [1, 2, 10],\n }),\n\n // ========================================================================\n // Database - Development\n // ========================================================================\n\n DB_DEBUG_TRACE: envBoolean({\n description: 'Enable detailed debug tracing for database operations',\n default: false,\n examples: [true, false],\n }),\n\n // ========================================================================\n // Drizzle ORM\n // ========================================================================\n\n DRIZZLE_SCHEMA_PATH: envString({\n description: 'Path to Drizzle schema configuration',\n required: false,\n default: './src/server/entities/config.ts',\n examples: ['./src/db/schema.ts', './src/server/entities/config.ts'],\n }),\n\n DRIZZLE_OUT_DIR: envString({\n description: 'Output directory for Drizzle migrations',\n required: false,\n default: './drizzle',\n examples: ['./drizzle', './migrations'],\n }),\n\n // ========================================================================\n // Logger - Core\n // ========================================================================\n\n SPFN_LOG_LEVEL: envEnum(['debug', 'info', 'warn', 'error', 'fatal'] as const, {\n description: 'Minimum log level to output',\n default: 'info',\n }),\n\n // ========================================================================\n // Cache (Redis/Valkey)\n // ========================================================================\n\n CACHE_URL: envString({\n description: 'Single Redis/Valkey instance URL',\n required: false,\n sensitive: true,\n validator: parseRedisUrl,\n examples: ['redis://localhost:6379', 'rediss://secure.cache.com:6380'],\n }),\n\n CACHE_WRITE_URL: envString({\n description: 'Master Redis/Valkey URL for writes (master-replica pattern)',\n required: false,\n sensitive: true,\n validator: parseRedisUrl,\n examples: ['redis://master:6379'],\n }),\n\n CACHE_READ_URL: envString({\n description: 'Replica Redis/Valkey URL for reads (master-replica pattern)',\n required: false,\n sensitive: true,\n validator: parseRedisUrl,\n examples: ['redis://replica:6379'],\n }),\n\n CACHE_SENTINEL_HOSTS: envString({\n description: 'Comma-separated Redis Sentinel hosts',\n required: false,\n examples: ['sentinel1:26379,sentinel2:26379'],\n }),\n\n CACHE_CLUSTER_NODES: envString({\n description: 'Comma-separated Redis Cluster nodes',\n required: false,\n examples: ['node1:6379,node2:6379,node3:6379'],\n }),\n\n CACHE_MASTER_NAME: envString({\n description: 'Redis Sentinel master name',\n required: false,\n examples: ['mymaster'],\n }),\n\n CACHE_PASSWORD: envString({\n description: 'Redis/Valkey authentication password',\n required: false,\n sensitive: true,\n examples: ['your-redis-password'],\n }),\n\n CACHE_TLS_REJECT_UNAUTHORIZED: envBoolean({\n description: 'Verify TLS certificates for secure Redis connections',\n default: true,\n examples: [true, false],\n }),\n\n CACHE_MAX_RETRIES_PER_REQUEST: envNumber({\n description: 'Max ioredis retries per command before it rejects (fail fast instead of hanging on a cache outage). ioredis default is 20.',\n default: 3,\n examples: [1, 3, 20],\n }),\n\n CACHE_ENABLE_OFFLINE_QUEUE: envBoolean({\n description: 'Queue commands while the cache is disconnected (true) vs reject immediately for strict fail-fast (false). Default true keeps resilience to brief blips.',\n default: true,\n examples: [true, false],\n }),\n\n // ========================================================================\n // Database - Query limits\n // ========================================================================\n\n DB_MAX_ROWS: envNumber({\n description: 'Safety ceiling for rows returned by repository findMany (0 = unlimited). When >0, an unbounded query is capped and an explicit limit is clamped, guarding against accidentally loading a whole large table.',\n default: 0,\n examples: [0, 1000, 10000],\n }),\n\n // ========================================================================\n // Server - Core\n // ========================================================================\n\n PORT: envNumber({\n description: 'Server port number',\n default: 4000,\n examples: [3000, 4000, 8080],\n }),\n\n HOST: envString({\n description: 'Server hostname',\n default: 'localhost',\n required: false,\n examples: ['localhost', '0.0.0.0', '127.0.0.1'],\n }),\n\n // ========================================================================\n // Server - Timeout\n // ========================================================================\n\n SERVER_TIMEOUT: envNumber({\n description: 'Request timeout in milliseconds',\n default: 120000,\n examples: [60000, 120000, 300000],\n }),\n\n SERVER_KEEPALIVE_TIMEOUT: envNumber({\n description: 'Keep-alive timeout in milliseconds',\n default: 65000,\n examples: [30000, 65000, 120000],\n }),\n\n SERVER_HEADERS_TIMEOUT: envNumber({\n description: 'Headers timeout in milliseconds',\n default: 60000,\n examples: [30000, 60000, 120000],\n }),\n\n SHUTDOWN_TIMEOUT: envNumber({\n description: 'Graceful shutdown timeout in milliseconds (must be less than k8s terminationGracePeriodSeconds minus preStop sleep, with safety margin)',\n default: 280000,\n examples: [30000, 120000, 280000],\n }),\n\n // ========================================================================\n // Fetch (Node.js undici) - outbound HTTP request timeout\n // ========================================================================\n\n FETCH_CONNECT_TIMEOUT: envNumber({\n description: 'Fetch TCP connection timeout in milliseconds (time to establish socket connection to upstream server)',\n default: 10000,\n examples: [5000, 10000, 30000],\n }),\n\n FETCH_HEADERS_TIMEOUT: envNumber({\n description: 'Fetch headers timeout in milliseconds (time to receive response headers after request sent)',\n default: 300000,\n examples: [120000, 300000, 600000],\n }),\n\n FETCH_BODY_TIMEOUT: envNumber({\n description: 'Fetch body timeout in milliseconds (time between body data chunks from upstream server)',\n default: 300000,\n examples: [120000, 300000, 600000],\n }),\n\n // ========================================================================\n // Next.js Integration\n // ========================================================================\n\n SPFN_API_URL: envUrl({\n description: 'SPFN API URL (used by Next.js to call backend)',\n required: true,\n nextjs: true,\n examples: ['http://localhost:8790', 'https://api.your-app.com'],\n }),\n\n NEXT_PUBLIC_SPFN_API_URL: envUrl({\n description: 'SPFN API URL (used by Next.js to call backend)',\n required: true,\n nextjs: true,\n examples: ['http://localhost:8790', 'https://api.your-app.com'],\n }),\n\n SPFN_APP_URL: envUrl({\n description: 'Next.js application URL (used by SPFN server)',\n required: false,\n nextjs: true,\n examples: ['http://localhost:3790', 'https://your-app.com'],\n }),\n\n RPC_PROXY_TIMEOUT: envNumber({\n description: 'RPC proxy request timeout in milliseconds (AbortController timeout for proxied requests to backend, should be shorter than FETCH_HEADERS_TIMEOUT)',\n default: 120000,\n nextjs: true,\n examples: [60000, 120000, 280000],\n }),\n\n // ========================================================================\n // Proxy → Backend trust (HMAC signing)\n // ========================================================================\n\n SPFN_PROXY_SECRET: envString({\n description: 'Shared secret for signing proxy→backend requests (HMAC-SHA256). Read by BOTH processes — the Next.js proxy (to sign) and the SPFN backend (to verify) — so it belongs in .env.local (loaded by both; the backend reads it via loadEnv, Next.js reads it server-side without exposing it to the browser). Set the SAME value on both. Leave unset to disable proxy-guard signing.',\n required: false,\n sensitive: true,\n nextjs: true,\n examples: ['<32+ byte random hex>', 'v2:<32+ byte random hex>'],\n }),\n\n SPFN_PROXY_SECRET_PREVIOUS: envString({\n description: 'Previous (grace) proxy keys still accepted for verification during rotation — comma-separated <keyId>:<secret>. The proxy never signs with these; they only keep requests signed with the prior key verifying until a rollout settles. Backend-only (verification), so it belongs in .env.server, NOT exposed to the Next.js process.',\n required: false,\n sensitive: true,\n nextjs: false,\n examples: ['v1:<old secret>', 'v1:<old>,v0:<older>'],\n }),\n\n TRUSTED_PROXY_HOPS: envNumber({\n description: 'Number of trusted reverse proxies in front of the Next.js proxy (e.g. cloud LB + nginx = 2). Read by the proxy to extract the real client IP from the inbound X-Forwarded-For (counting from the right, which your own infra appends and a client cannot spoof) and forward it to the backend for rate limiting. Set it to your actual hop count; too low trusts a client-spoofable entry, too high collapses users behind a shared proxy IP.',\n default: 1,\n nextjs: true,\n examples: [1, 2, 3],\n }),\n\n // ========================================================================\n // Rate limiting (global default limiter)\n // ========================================================================\n\n RATE_LIMIT_MODE: envEnum(['off', 'on'] as const, {\n description: 'Global default rate limiter. \"off\": only routes tagged with rateLimitPolicy() are limited. \"on\": every named-middleware route gets the default limit too (opt out per route with .skip([\\'rateLimit\\'])). Health/SSE/WebSocket endpoints are always exempt. Overridden by defineServerConfig().rateLimit({ mode }).',\n default: 'off',\n }),\n\n RATE_LIMIT_DEFAULT_LIMIT: envNumber({\n description: 'Max requests per window for the global default limiter (RATE_LIMIT_MODE=on), counted per route and per client IP.',\n default: 100,\n examples: [60, 100, 300],\n }),\n\n RATE_LIMIT_DEFAULT_WINDOW_MS: envNumber({\n description: 'Window length in milliseconds for the global default limiter.',\n default: 60000,\n examples: [1000, 60000],\n }),\n\n RATE_LIMIT_FAIL_CLOSED: envBoolean({\n description: 'When the cache (Redis/Valkey) backing the limiter is unavailable, reject with 429 instead of allowing requests through. Default false (fail open) so development without a cache still works.',\n default: false,\n }),\n\n // ========================================================================\n // Outbound request safety (SSRF)\n // ========================================================================\n\n SAFE_FETCH_BLOCK_PRIVATE_IPS: envBoolean({\n description: 'Default for safeFetch (@spfn/core/security): block outbound requests that resolve to private/reserved IP ranges, including the cloud metadata address. Keep true in production; set false only for trusted internal-network calls in development. Overridden by defineServerConfig().outboundFetch({ blockPrivateIps }).',\n default: true,\n }),\n});\n","/**\n * Core Package Configuration\n *\n * @example\n * ```typescript\n * import { registry } from '@spfn/core/config';\n *\n * const env = registry.validate();\n * console.log(env.DB_POOL_MAX);\n * ```\n *\n * @module config\n */\n\nimport { createEnvRegistry } from '@spfn/core/env';\nimport { coreEnvSchema } from './schema';\n\n/**\n * Core environment schema\n */\nexport { coreEnvSchema as envSchema } from './schema';\n\n/**\n * Environment registry\n *\n * @example\n * ```typescript\n * // Reset for testing\n * registry.reset();\n * ```\n */\nexport const registry = createEnvRegistry(coreEnvSchema);\n\n/**\n * Validated environment configuration\n */\nexport const env = registry.validate();\n"]}
|
package/dist/db/index.d.ts
CHANGED
|
@@ -81,6 +81,21 @@ interface PoolConfig {
|
|
|
81
81
|
max: number;
|
|
82
82
|
/** Idle connection timeout in seconds */
|
|
83
83
|
idleTimeout: number;
|
|
84
|
+
/**
|
|
85
|
+
* Maximum connections for the read-replica pool. Defaults to `max`. Set
|
|
86
|
+
* separately (DB_POOL_READ_MAX) so a master-replica app can size the two
|
|
87
|
+
* pools independently and keep `write.max + read.max` under the server's
|
|
88
|
+
* `max_connections` (otherwise each process opens up to 2 × max).
|
|
89
|
+
*/
|
|
90
|
+
readMax?: number;
|
|
91
|
+
/**
|
|
92
|
+
* Use server-side prepared statements (postgres-js `prepare`). When
|
|
93
|
+
* `undefined`, the connection layer auto-detects: it is disabled for
|
|
94
|
+
* transaction-mode poolers (PgBouncer/Supavisor, e.g. Supabase :6543),
|
|
95
|
+
* where cached statements break as the pooler rotates backends, and
|
|
96
|
+
* enabled otherwise. Set explicitly (or via `SPFN_DB_PREPARE`) to override.
|
|
97
|
+
*/
|
|
98
|
+
prepare?: boolean;
|
|
84
99
|
}
|
|
85
100
|
/**
|
|
86
101
|
* Retry configuration for exponential backoff algorithm
|
|
@@ -273,6 +288,36 @@ declare function initDatabase(options?: DatabaseOptions): Promise<{
|
|
|
273
288
|
* ```
|
|
274
289
|
*/
|
|
275
290
|
declare function closeDatabase(): Promise<void>;
|
|
291
|
+
/**
|
|
292
|
+
* Force an immediate database pool rebuild
|
|
293
|
+
*
|
|
294
|
+
* Destroys the current postgres.js pool(s) and rebuilds them with the same
|
|
295
|
+
* configuration passed to the original `initDatabase()` call (or whatever
|
|
296
|
+
* was detected from environment variables). Uses the same atomic-swap
|
|
297
|
+
* strategy as the periodic health check: new connections are created and
|
|
298
|
+
* tested BEFORE the old ones are torn down, so `getDatabase()` callers never
|
|
299
|
+
* observe a missing instance.
|
|
300
|
+
*
|
|
301
|
+
* Use this when application code detects that the pool is stuck and does not
|
|
302
|
+
* want to wait for the next periodic health check tick. Concurrent calls are
|
|
303
|
+
* coalesced — if a reconnect is already in progress, this resolves to `false`
|
|
304
|
+
* without starting a second one.
|
|
305
|
+
*
|
|
306
|
+
* @param reason - Short label describing why the rebuild was requested (for logs)
|
|
307
|
+
* @returns `true` if a reconnection ran, `false` if one was already in-flight.
|
|
308
|
+
* Resolves after the rebuild completes (success or max retries exhausted).
|
|
309
|
+
*
|
|
310
|
+
* @example
|
|
311
|
+
* ```typescript
|
|
312
|
+
* import { forceReconnectDatabase } from '@spfn/core/db';
|
|
313
|
+
*
|
|
314
|
+
* app.post('/admin/db/reconnect', async (c) => {
|
|
315
|
+
* const ran = await forceReconnectDatabase('admin_request');
|
|
316
|
+
* return c.json({ reconnected: ran });
|
|
317
|
+
* });
|
|
318
|
+
* ```
|
|
319
|
+
*/
|
|
320
|
+
declare function forceReconnectDatabase(reason?: string): Promise<boolean>;
|
|
276
321
|
/**
|
|
277
322
|
* Get database connection info (for debugging)
|
|
278
323
|
*
|
|
@@ -308,6 +353,60 @@ declare function getDatabaseInfo(): {
|
|
|
308
353
|
isReplica: boolean;
|
|
309
354
|
};
|
|
310
355
|
|
|
356
|
+
/**
|
|
357
|
+
* Reconnect Trigger — Query-error driven pool rebuild
|
|
358
|
+
*
|
|
359
|
+
* Complements the periodic health check with a fast-path: when application
|
|
360
|
+
* queries start failing with connection-level errors, we do not wait up to
|
|
361
|
+
* DB_HEALTH_CHECK_INTERVAL (default 60s) to notice. A sliding-window counter
|
|
362
|
+
* trips a force-reconnect as soon as the failure rate crosses a threshold.
|
|
363
|
+
*
|
|
364
|
+
* Why this exists:
|
|
365
|
+
* - postgres.js transparently drops dead sockets and opens new ones on the
|
|
366
|
+
* next query. A single `SELECT 1` on the periodic interval can therefore
|
|
367
|
+
* false-pass while user-facing queries keep hitting the remaining dead
|
|
368
|
+
* sockets in the pool.
|
|
369
|
+
* - This module observes real query errors and, when it sees a burst of
|
|
370
|
+
* connection-level failures, calls triggerForceReconnect() which performs
|
|
371
|
+
* the same atomic-swap rebuild as the health check.
|
|
372
|
+
*
|
|
373
|
+
* Configuration (env vars, hardcoded defaults):
|
|
374
|
+
* - DB_RECONNECT_ERROR_THRESHOLD (default 3): errors needed in window
|
|
375
|
+
* - DB_RECONNECT_ERROR_WINDOW_MS (default 10000): sliding window size
|
|
376
|
+
*/
|
|
377
|
+
/**
|
|
378
|
+
* Determine whether an error looks like a pool/connection failure
|
|
379
|
+
*
|
|
380
|
+
* Returns true when any layer in the error chain exposes a connection-level
|
|
381
|
+
* code (postgres.js driver code, Node network errno, PG SQLSTATE class 08 etc.)
|
|
382
|
+
* or is an instance of our own ConnectionError wrapper.
|
|
383
|
+
*
|
|
384
|
+
* Returns false for query errors (syntax, constraint violations, etc.) — those
|
|
385
|
+
* should NOT trigger a pool rebuild.
|
|
386
|
+
*/
|
|
387
|
+
declare function isConnectionLevelError(error: unknown): boolean;
|
|
388
|
+
/**
|
|
389
|
+
* Reset the internal error counter
|
|
390
|
+
*
|
|
391
|
+
* Exposed for tests that need a clean slate between cases. Does not clear
|
|
392
|
+
* the WeakSet (which is GC-backed and self-cleans with error lifetimes).
|
|
393
|
+
*/
|
|
394
|
+
declare function resetConnectionErrorCounter(): void;
|
|
395
|
+
/**
|
|
396
|
+
* Report a database error to the reconnect trigger
|
|
397
|
+
*
|
|
398
|
+
* Call this from any site that catches a query error before rethrowing.
|
|
399
|
+
* It is a no-op for non-connection-level errors. When the threshold is
|
|
400
|
+
* crossed it calls triggerForceReconnect() in the background — callers
|
|
401
|
+
* should NOT await it.
|
|
402
|
+
*
|
|
403
|
+
* Safe to call from any context: catches its own errors so it cannot
|
|
404
|
+
* disrupt the calling catch block. Deduplicates across error-chain
|
|
405
|
+
* re-wrapping so one failure counts exactly once regardless of how many
|
|
406
|
+
* catch layers it passes through.
|
|
407
|
+
*/
|
|
408
|
+
declare function reportDatabaseError(error: unknown): void;
|
|
409
|
+
|
|
311
410
|
/**
|
|
312
411
|
* Create database connection with exponential backoff retry strategy
|
|
313
412
|
*
|
|
@@ -380,6 +479,8 @@ interface DrizzleConfigOptions {
|
|
|
380
479
|
schemaFilter?: string[];
|
|
381
480
|
/** Auto-detect PostgreSQL schemas from entity files (requires expandGlobs: true) */
|
|
382
481
|
autoDetectSchemas?: boolean;
|
|
482
|
+
/** Migration prefix strategy (default: 'timestamp') */
|
|
483
|
+
migrationPrefix?: 'index' | 'timestamp' | 'unix' | 'none';
|
|
383
484
|
}
|
|
384
485
|
/**
|
|
385
486
|
* Detect database dialect from connection URL
|
|
@@ -411,6 +512,9 @@ declare function getDrizzleConfig(options?: DrizzleConfigOptions): {
|
|
|
411
512
|
dbCredentials: {
|
|
412
513
|
url: string;
|
|
413
514
|
};
|
|
515
|
+
migrations: {
|
|
516
|
+
prefix: "timestamp" | "none" | "index" | "unix";
|
|
517
|
+
};
|
|
414
518
|
schemaFilter?: undefined;
|
|
415
519
|
} | {
|
|
416
520
|
schema: string | string[];
|
|
@@ -420,6 +524,9 @@ declare function getDrizzleConfig(options?: DrizzleConfigOptions): {
|
|
|
420
524
|
url: string;
|
|
421
525
|
};
|
|
422
526
|
schemaFilter: string[] | undefined;
|
|
527
|
+
migrations: {
|
|
528
|
+
prefix: "timestamp" | "none" | "index" | "unix";
|
|
529
|
+
};
|
|
423
530
|
};
|
|
424
531
|
/**
|
|
425
532
|
* Generate drizzle.config.ts file content
|
|
@@ -475,7 +582,7 @@ declare function timestamps(): {
|
|
|
475
582
|
/**
|
|
476
583
|
* Foreign key reference to another table
|
|
477
584
|
*
|
|
478
|
-
* Creates a
|
|
585
|
+
* Creates a bigint column with cascade delete.
|
|
479
586
|
* Type-safe: ensures the reference points to a valid PostgreSQL column.
|
|
480
587
|
*
|
|
481
588
|
* @param name - Column name (e.g., 'author' creates 'author_id')
|
|
@@ -495,7 +602,7 @@ declare function timestamps(): {
|
|
|
495
602
|
*/
|
|
496
603
|
declare function foreignKey<T extends PgColumn>(name: string, reference: () => T, options?: {
|
|
497
604
|
onDelete?: 'cascade' | 'set null' | 'restrict' | 'no action';
|
|
498
|
-
}): drizzle_orm.NotNull<drizzle_orm_pg_core.
|
|
605
|
+
}): drizzle_orm.NotNull<drizzle_orm_pg_core.PgBigInt53BuilderInitial<`${string}_id`>>;
|
|
499
606
|
/**
|
|
500
607
|
* Optional foreign key reference (nullable)
|
|
501
608
|
*
|
|
@@ -515,7 +622,7 @@ declare function foreignKey<T extends PgColumn>(name: string, reference: () => T
|
|
|
515
622
|
*/
|
|
516
623
|
declare function optionalForeignKey<T extends PgColumn>(name: string, reference: () => T, options?: {
|
|
517
624
|
onDelete?: 'cascade' | 'set null' | 'restrict' | 'no action';
|
|
518
|
-
}): drizzle_orm_pg_core.
|
|
625
|
+
}): drizzle_orm_pg_core.PgBigInt53BuilderInitial<`${string}_id`>;
|
|
519
626
|
/**
|
|
520
627
|
* UUID primary key
|
|
521
628
|
*
|
|
@@ -846,6 +953,10 @@ declare function getSchemaInfo(packageName: string): {
|
|
|
846
953
|
* Uses Record<string, unknown> to accept any schema shape
|
|
847
954
|
*/
|
|
848
955
|
type TransactionDB = PostgresJsDatabase<Record<string, unknown>>;
|
|
956
|
+
/**
|
|
957
|
+
* afterCommit callback type
|
|
958
|
+
*/
|
|
959
|
+
type AfterCommitCallback = () => void | Promise<void>;
|
|
849
960
|
/**
|
|
850
961
|
* Transaction context stored in AsyncLocalStorage
|
|
851
962
|
*/
|
|
@@ -855,7 +966,15 @@ type TransactionContext = {
|
|
|
855
966
|
/** Unique transaction ID for logging and tracing */
|
|
856
967
|
txId: string;
|
|
857
968
|
level: number;
|
|
969
|
+
/** Callbacks to execute after root transaction commits */
|
|
970
|
+
afterCommitCallbacks: AfterCommitCallback[];
|
|
858
971
|
};
|
|
972
|
+
/**
|
|
973
|
+
* Get current transaction object and metadata from AsyncLocalStorage
|
|
974
|
+
*
|
|
975
|
+
* @returns TransactionContext if available, null otherwise
|
|
976
|
+
*/
|
|
977
|
+
declare function getTransactionContext(): TransactionContext | null;
|
|
859
978
|
/**
|
|
860
979
|
* Get current transaction from AsyncLocalStorage
|
|
861
980
|
*
|
|
@@ -875,6 +994,31 @@ declare function getTransaction(): TransactionDB | null;
|
|
|
875
994
|
*/
|
|
876
995
|
declare function runWithTransaction<T>(tx: TransactionDB, txId: string, // Add txId parameter
|
|
877
996
|
callback: () => Promise<T>): Promise<T>;
|
|
997
|
+
/**
|
|
998
|
+
* Register a callback to run after the current transaction commits
|
|
999
|
+
*
|
|
1000
|
+
* - Inside a transaction: queued and executed after root transaction commits
|
|
1001
|
+
* - Outside a transaction: executed immediately (already "committed")
|
|
1002
|
+
* - Nested transactions: callbacks bubble up to root transaction
|
|
1003
|
+
* - Callbacks run outside transaction context (new connection for DB access)
|
|
1004
|
+
* - Errors are logged but never thrown (commit already succeeded)
|
|
1005
|
+
*
|
|
1006
|
+
* @example
|
|
1007
|
+
* ```typescript
|
|
1008
|
+
* import { onAfterCommit } from '@spfn/core/db/transaction';
|
|
1009
|
+
*
|
|
1010
|
+
* async function submit(spaceId: string, chatId: string)
|
|
1011
|
+
* {
|
|
1012
|
+
* const publication = await publicationRepo.create({...});
|
|
1013
|
+
* await requestRepo.updateStatusAtomically(...);
|
|
1014
|
+
*
|
|
1015
|
+
* onAfterCommit(() => generateArticle(spaceId, chatId, publication.id));
|
|
1016
|
+
*
|
|
1017
|
+
* return publication;
|
|
1018
|
+
* }
|
|
1019
|
+
* ```
|
|
1020
|
+
*/
|
|
1021
|
+
declare function onAfterCommit(callback: AfterCommitCallback): void;
|
|
878
1022
|
|
|
879
1023
|
/**
|
|
880
1024
|
* Transaction middleware options
|
|
@@ -910,6 +1054,15 @@ interface TransactionalOptions {
|
|
|
910
1054
|
* ```
|
|
911
1055
|
*/
|
|
912
1056
|
timeout?: number;
|
|
1057
|
+
/**
|
|
1058
|
+
* Idle-in-transaction timeout in milliseconds — Postgres reclaims the pooled
|
|
1059
|
+
* connection if the transaction sits idle (e.g. the handler awaits external
|
|
1060
|
+
* I/O) longer than this. A backstop against pool starvation, not a license
|
|
1061
|
+
* to do non-DB work inside a transaction. `0` disables it.
|
|
1062
|
+
*
|
|
1063
|
+
* @default 30000 (30s) or TRANSACTION_IDLE_TIMEOUT environment variable
|
|
1064
|
+
*/
|
|
1065
|
+
idleTimeout?: number;
|
|
913
1066
|
}
|
|
914
1067
|
/**
|
|
915
1068
|
* Transaction middleware for Hono routes
|
|
@@ -955,6 +1108,93 @@ interface TransactionalOptions {
|
|
|
955
1108
|
*/
|
|
956
1109
|
declare function Transactional(options?: TransactionalOptions): hono_types.MiddlewareHandler<any, string, {}, Response>;
|
|
957
1110
|
|
|
1111
|
+
/**
|
|
1112
|
+
* Transaction runner options
|
|
1113
|
+
*/
|
|
1114
|
+
interface RunInTransactionOptions {
|
|
1115
|
+
/**
|
|
1116
|
+
* Slow transaction warning threshold in milliseconds
|
|
1117
|
+
* @default 1000 (1 second)
|
|
1118
|
+
*/
|
|
1119
|
+
slowThreshold?: number;
|
|
1120
|
+
/**
|
|
1121
|
+
* Enable transaction logging
|
|
1122
|
+
* @default true
|
|
1123
|
+
*/
|
|
1124
|
+
enableLogging?: boolean;
|
|
1125
|
+
/**
|
|
1126
|
+
* Transaction timeout in milliseconds
|
|
1127
|
+
*
|
|
1128
|
+
* Sets PostgreSQL `statement_timeout` to enforce database-level timeout.
|
|
1129
|
+
* If transaction exceeds this duration, PostgreSQL will automatically cancel
|
|
1130
|
+
* the query and rollback the transaction, ensuring data consistency.
|
|
1131
|
+
*
|
|
1132
|
+
* Behavior:
|
|
1133
|
+
* - `timeout: 0` - Disables timeout (unlimited execution time)
|
|
1134
|
+
* - `timeout: null` - Uses default (30s or TRANSACTION_TIMEOUT env var)
|
|
1135
|
+
* - `timeout: undefined` - Uses default (30s or TRANSACTION_TIMEOUT env var)
|
|
1136
|
+
* - `timeout: N` - Sets timeout to N milliseconds (1 to 2147483647)
|
|
1137
|
+
*
|
|
1138
|
+
* Note: Timeout is only applied to root transactions. Nested transactions
|
|
1139
|
+
* (SAVEPOINTs) inherit the timeout from the outer transaction.
|
|
1140
|
+
*
|
|
1141
|
+
* @default 30000 (30 seconds) or TRANSACTION_TIMEOUT environment variable
|
|
1142
|
+
*
|
|
1143
|
+
* @example
|
|
1144
|
+
* ```typescript
|
|
1145
|
+
* // Use default timeout (30s)
|
|
1146
|
+
* await runInTransaction(callback);
|
|
1147
|
+
*
|
|
1148
|
+
* // Disable timeout for long-running operations
|
|
1149
|
+
* await runInTransaction(callback, { timeout: 0 });
|
|
1150
|
+
*
|
|
1151
|
+
* // Set custom timeout (60s)
|
|
1152
|
+
* await runInTransaction(callback, { timeout: 60000 });
|
|
1153
|
+
* ```
|
|
1154
|
+
*/
|
|
1155
|
+
timeout?: number;
|
|
1156
|
+
/**
|
|
1157
|
+
* Idle-in-transaction timeout in milliseconds (root transactions only).
|
|
1158
|
+
*
|
|
1159
|
+
* Sets PostgreSQL `idle_in_transaction_session_timeout`: if the transaction
|
|
1160
|
+
* sits open without running a query for longer than this — e.g. while the
|
|
1161
|
+
* handler awaits external I/O inside the transaction — Postgres terminates
|
|
1162
|
+
* the session and rolls back, reclaiming the pooled connection instead of
|
|
1163
|
+
* letting one stuck request hold it (and its row locks) indefinitely.
|
|
1164
|
+
*
|
|
1165
|
+
* Do not put external I/O inside a transaction; this is a backstop, not a
|
|
1166
|
+
* license. `0` disables it.
|
|
1167
|
+
*
|
|
1168
|
+
* @default 30000 (30s) or TRANSACTION_IDLE_TIMEOUT environment variable
|
|
1169
|
+
*/
|
|
1170
|
+
idleTimeout?: number;
|
|
1171
|
+
/**
|
|
1172
|
+
* Context string for logging (e.g., 'migration:add-user', 'script:cleanup')
|
|
1173
|
+
* @default 'transaction'
|
|
1174
|
+
*/
|
|
1175
|
+
context?: string;
|
|
1176
|
+
}
|
|
1177
|
+
/**
|
|
1178
|
+
* Run a callback function within a database transaction
|
|
1179
|
+
*
|
|
1180
|
+
* Automatically manages transaction lifecycle:
|
|
1181
|
+
* - Commits on success
|
|
1182
|
+
* - Rolls back on error
|
|
1183
|
+
* - Tracks execution time
|
|
1184
|
+
* - Warns about slow transactions
|
|
1185
|
+
* - Enforces timeout if configured
|
|
1186
|
+
*
|
|
1187
|
+
* Errors are propagated to the caller without modification.
|
|
1188
|
+
* Caller is responsible for error handling and conversion.
|
|
1189
|
+
*
|
|
1190
|
+
* @param callback - Function to execute within transaction
|
|
1191
|
+
* @param options - Transaction options
|
|
1192
|
+
* @returns Result of callback function
|
|
1193
|
+
* @throws TransactionError if database not initialized or timeout exceeded
|
|
1194
|
+
* @throws Any error thrown by callback function
|
|
1195
|
+
*/
|
|
1196
|
+
declare function runInTransaction<T>(callback: (tx: TransactionDB) => Promise<T>, options?: RunInTransactionOptions): Promise<T>;
|
|
1197
|
+
|
|
958
1198
|
/**
|
|
959
1199
|
* PostgreSQL Error Conversion Utilities
|
|
960
1200
|
*
|
|
@@ -1432,6 +1672,32 @@ declare abstract class BaseRepository<TSchema extends Record<string, unknown> =
|
|
|
1432
1672
|
limit?: number;
|
|
1433
1673
|
offset?: number;
|
|
1434
1674
|
}): Promise<T['$inferSelect'][]>;
|
|
1675
|
+
/**
|
|
1676
|
+
* Keyset (cursor) pagination — O(limit) instead of OFFSET's O(offset).
|
|
1677
|
+
*
|
|
1678
|
+
* Pages by a strictly-ordered, unique column instead of a numeric offset, so a
|
|
1679
|
+
* deep page doesn't scan and discard everything before it. Pass the cursor
|
|
1680
|
+
* column's value from the last row of the previous page as `after`; omit it for
|
|
1681
|
+
* the first page. The column must be unique and the sole sort key (e.g. an
|
|
1682
|
+
* auto-increment id or a ULID).
|
|
1683
|
+
*
|
|
1684
|
+
* @example
|
|
1685
|
+
* ```typescript
|
|
1686
|
+
* const page1 = await this._findManyKeyset(users, { cursorColumn: users.id, limit: 20 });
|
|
1687
|
+
* const page2 = await this._findManyKeyset(users, {
|
|
1688
|
+
* cursorColumn: users.id,
|
|
1689
|
+
* after: page1.at(-1)?.id,
|
|
1690
|
+
* limit: 20,
|
|
1691
|
+
* });
|
|
1692
|
+
* ```
|
|
1693
|
+
*/
|
|
1694
|
+
protected _findManyKeyset<T extends PgTable>(table: T, options: {
|
|
1695
|
+
cursorColumn: PgColumn;
|
|
1696
|
+
limit: number;
|
|
1697
|
+
after?: string | number | bigint | Date;
|
|
1698
|
+
order?: 'asc' | 'desc';
|
|
1699
|
+
where?: Record<string, any> | SQL | undefined;
|
|
1700
|
+
}): Promise<T['$inferSelect'][]>;
|
|
1435
1701
|
/**
|
|
1436
1702
|
* Create a new record
|
|
1437
1703
|
*
|
|
@@ -1563,4 +1829,4 @@ declare abstract class BaseRepository<TSchema extends Record<string, unknown> =
|
|
|
1563
1829
|
protected _count<T extends PgTable>(table: T, where?: Record<string, any> | SQL | undefined): Promise<number>;
|
|
1564
1830
|
}
|
|
1565
1831
|
|
|
1566
|
-
export { BaseRepository, type DatabaseClients, type DrizzleConfigOptions, type PoolConfig, RepositoryError, type RetryConfig, type TransactionContext, type TransactionDB, Transactional, type TransactionalOptions, auditFields, checkConnection, closeDatabase, count, create, createDatabaseConnection, createDatabaseFromEnv, createMany, createSchema, deleteMany, deleteOne, detectDialect, enumText, findMany, findOne, foreignKey, fromPostgresError, generateDrizzleConfigFile, getDatabase, getDatabaseInfo, getDrizzleConfig, getSchemaInfo, getTransaction, id, initDatabase, optionalForeignKey, packageNameToSchema, publishingFields, runWithTransaction, setDatabase, softDelete, timestamps, typedJsonb, updateMany, updateOne, upsert, utcTimestamp, uuid, verificationTimestamp };
|
|
1832
|
+
export { type AfterCommitCallback, BaseRepository, type DatabaseClients, type DrizzleConfigOptions, type PoolConfig, RepositoryError, type RetryConfig, type RunInTransactionOptions, type TransactionContext, type TransactionDB, Transactional, type TransactionalOptions, auditFields, checkConnection, closeDatabase, count, create, createDatabaseConnection, createDatabaseFromEnv, createMany, createSchema, deleteMany, deleteOne, detectDialect, enumText, findMany, findOne, forceReconnectDatabase, foreignKey, fromPostgresError, generateDrizzleConfigFile, getDatabase, getDatabaseInfo, getDrizzleConfig, getSchemaInfo, getTransaction, getTransactionContext, id, initDatabase, isConnectionLevelError, onAfterCommit, optionalForeignKey, packageNameToSchema, publishingFields, reportDatabaseError, resetConnectionErrorCounter, runInTransaction, runWithTransaction, setDatabase, softDelete, timestamps, typedJsonb, updateMany, updateOne, upsert, utcTimestamp, uuid, verificationTimestamp };
|