@spfn/core 0.2.0-beta.4 → 0.2.0-beta.42

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.
Files changed (68) hide show
  1. package/README.md +260 -1175
  2. package/dist/{boss-BO8ty33K.d.ts → boss-DI1r4kTS.d.ts} +24 -7
  3. package/dist/cache/index.js +32 -29
  4. package/dist/cache/index.js.map +1 -1
  5. package/dist/codegen/index.d.ts +55 -8
  6. package/dist/codegen/index.js +179 -5
  7. package/dist/codegen/index.js.map +1 -1
  8. package/dist/config/index.d.ts +168 -6
  9. package/dist/config/index.js +29 -5
  10. package/dist/config/index.js.map +1 -1
  11. package/dist/db/index.d.ts +128 -4
  12. package/dist/db/index.js +177 -50
  13. package/dist/db/index.js.map +1 -1
  14. package/dist/env/index.d.ts +55 -1
  15. package/dist/env/index.js +71 -3
  16. package/dist/env/index.js.map +1 -1
  17. package/dist/env/loader.d.ts +27 -19
  18. package/dist/env/loader.js +33 -25
  19. package/dist/env/loader.js.map +1 -1
  20. package/dist/event/index.d.ts +27 -1
  21. package/dist/event/index.js +6 -1
  22. package/dist/event/index.js.map +1 -1
  23. package/dist/event/sse/client.d.ts +77 -2
  24. package/dist/event/sse/client.js +87 -24
  25. package/dist/event/sse/client.js.map +1 -1
  26. package/dist/event/sse/index.d.ts +10 -4
  27. package/dist/event/sse/index.js +158 -12
  28. package/dist/event/sse/index.js.map +1 -1
  29. package/dist/job/index.d.ts +23 -8
  30. package/dist/job/index.js +96 -20
  31. package/dist/job/index.js.map +1 -1
  32. package/dist/logger/index.d.ts +5 -0
  33. package/dist/logger/index.js +14 -0
  34. package/dist/logger/index.js.map +1 -1
  35. package/dist/middleware/index.d.ts +23 -1
  36. package/dist/middleware/index.js +58 -5
  37. package/dist/middleware/index.js.map +1 -1
  38. package/dist/nextjs/index.d.ts +2 -2
  39. package/dist/nextjs/index.js +77 -31
  40. package/dist/nextjs/index.js.map +1 -1
  41. package/dist/nextjs/server.d.ts +44 -23
  42. package/dist/nextjs/server.js +83 -65
  43. package/dist/nextjs/server.js.map +1 -1
  44. package/dist/route/index.d.ts +158 -4
  45. package/dist/route/index.js +253 -17
  46. package/dist/route/index.js.map +1 -1
  47. package/dist/server/index.d.ts +251 -16
  48. package/dist/server/index.js +774 -228
  49. package/dist/server/index.js.map +1 -1
  50. package/dist/{types-D_N_U-Py.d.ts → types-7Mhoxnnt.d.ts} +21 -1
  51. package/dist/types-DKQ90YL7.d.ts +372 -0
  52. package/docs/cache.md +133 -0
  53. package/docs/codegen.md +74 -0
  54. package/docs/database.md +370 -0
  55. package/docs/entity.md +539 -0
  56. package/docs/env.md +499 -0
  57. package/docs/errors.md +319 -0
  58. package/docs/event.md +443 -0
  59. package/docs/file-upload.md +717 -0
  60. package/docs/job.md +131 -0
  61. package/docs/logger.md +108 -0
  62. package/docs/middleware.md +337 -0
  63. package/docs/nextjs.md +247 -0
  64. package/docs/repository.md +496 -0
  65. package/docs/route.md +497 -0
  66. package/docs/server.md +429 -0
  67. package/package.json +3 -2
  68. package/dist/types-B-e_f2dQ.d.ts +0 -121
@@ -1,26 +1,41 @@
1
+ export { loadEnv } from '../env/loader.js';
1
2
  import { MiddlewareHandler, Hono } from 'hono';
2
3
  import { cors } from 'hono/cors';
3
4
  import { serve } from '@hono/node-server';
4
5
  import { NamedMiddleware, Router } from '@spfn/core/route';
5
- import { J as JobRouter, B as BossOptions } from '../boss-BO8ty33K.js';
6
+ import { OnErrorContext } from '@spfn/core/middleware';
7
+ import { J as JobRouter, B as BossOptions } from '../boss-DI1r4kTS.js';
6
8
  import { E as EventRouterDef } from '../router-Di7ENoah.js';
7
- import { S as SSEHandlerConfig } from '../types-B-e_f2dQ.js';
9
+ import { S as SSEHandlerConfig, a as SSEAuthConfig } from '../types-DKQ90YL7.js';
8
10
  import '@sinclair/typebox';
9
11
  import 'pg-boss';
10
12
 
11
13
  /**
12
- * Load environment files for SPFN server
13
- *
14
- * Priority (high → low, later files don't override):
15
- * 1. .env.server.local - Server-only secrets (gitignored)
16
- * 2. .env.server - Server-only defaults
17
- * 3. .env.{NODE_ENV}.local
18
- * 4. .env.local - Local overrides (gitignored)
19
- * 5. .env.{NODE_ENV}
20
- * 6. .env - Defaults
14
+ * @deprecated Use `loadEnv` from '@spfn/core/env/loader' instead.
15
+ * This module will be removed in the next major version.
16
+ */
17
+ /**
18
+ * @deprecated Use `loadEnv()` from '@spfn/core/env/loader' instead.
21
19
  */
22
20
  declare function loadEnvFiles(): void;
23
21
 
22
+ /**
23
+ * Workflow router interface for @spfn/core integration
24
+ *
25
+ * This is a minimal interface that avoids circular dependency with @spfn/workflow.
26
+ * The actual WorkflowRouter from @spfn/workflow implements this interface.
27
+ */
28
+ interface WorkflowRouterLike {
29
+ /**
30
+ * Initialize the workflow engine
31
+ * Called by server during infrastructure initialization
32
+ *
33
+ * @internal
34
+ */
35
+ _init: (db: any, options?: {
36
+ largeOutputThreshold?: number;
37
+ }) => void;
38
+ }
24
39
  /**
25
40
  * CORS configuration options - inferred from hono/cors
26
41
  */
@@ -60,6 +75,21 @@ interface ServerConfig {
60
75
  * Error handler (default: true)
61
76
  */
62
77
  errorHandler?: boolean;
78
+ /**
79
+ * Callback invoked when an error occurs (passed to ErrorHandler)
80
+ *
81
+ * Called asynchronously without blocking the response.
82
+ *
83
+ * @example
84
+ * ```typescript
85
+ * import { createErrorSlackNotifier } from '@spfn/notification/server';
86
+ *
87
+ * middleware: {
88
+ * onError: createErrorSlackNotifier({ minStatusCode: 500 }),
89
+ * }
90
+ * ```
91
+ */
92
+ onError?: (err: Error, context: OnErrorContext) => Promise<void> | void;
63
93
  };
64
94
  /**
65
95
  * Additional custom middleware
@@ -282,6 +312,34 @@ interface ServerConfig {
282
312
  */
283
313
  headers?: number;
284
314
  };
315
+ /**
316
+ * Fetch (outbound HTTP) timeout configuration
317
+ * Controls Node.js undici global dispatcher timeouts for fetch() calls
318
+ * Applies to all outbound HTTP requests made via fetch() in this process
319
+ */
320
+ fetchTimeout?: {
321
+ /**
322
+ * TCP connection timeout in milliseconds
323
+ * Time to establish socket connection to upstream server
324
+ * @default 10000 (10 seconds)
325
+ * @env FETCH_CONNECT_TIMEOUT
326
+ */
327
+ connect?: number;
328
+ /**
329
+ * Response headers timeout in milliseconds
330
+ * Time to receive complete response headers after request sent
331
+ * @default 300000 (5 minutes)
332
+ * @env FETCH_HEADERS_TIMEOUT
333
+ */
334
+ headers?: number;
335
+ /**
336
+ * Body data timeout in milliseconds
337
+ * Maximum time between body data chunks from upstream server
338
+ * @default 300000 (5 minutes)
339
+ * @env FETCH_BODY_TIMEOUT
340
+ */
341
+ body?: number;
342
+ };
285
343
  /**
286
344
  * Graceful shutdown configuration
287
345
  * Controls server shutdown behavior during SIGTERM/SIGINT signals
@@ -289,9 +347,13 @@ interface ServerConfig {
289
347
  shutdown?: {
290
348
  /**
291
349
  * Graceful shutdown timeout in milliseconds
292
- * Maximum time to wait for ongoing requests and resource cleanup
293
- * After timeout, forces process termination
294
- * @default 30000 (30 seconds)
350
+ * Maximum time to wait for in-flight operations to drain and resource cleanup
351
+ * After timeout, forces process.exit() before k8s SIGKILL
352
+ *
353
+ * Formula: terminationGracePeriodSeconds - preStopSleep - safetyMargin
354
+ * Default: 300s - 5s - 15s = 280s
355
+ *
356
+ * @default 280000 (280 seconds)
295
357
  * @env SHUTDOWN_TIMEOUT
296
358
  */
297
359
  timeout?: number;
@@ -338,6 +400,39 @@ interface ServerConfig {
338
400
  */
339
401
  redis?: boolean;
340
402
  };
403
+ /**
404
+ * Workflow router for workflow orchestration
405
+ *
406
+ * Automatically initializes the workflow engine after database is ready.
407
+ * Workflows are defined using @spfn/workflow package.
408
+ *
409
+ * @example
410
+ * ```typescript
411
+ * import { defineWorkflowRouter } from '@spfn/workflow';
412
+ *
413
+ * const workflowRouter = defineWorkflowRouter([
414
+ * provisionTenant,
415
+ * deprovisionTenant,
416
+ * ]);
417
+ *
418
+ * export default defineServerConfig()
419
+ * .workflows(workflowRouter)
420
+ * .build();
421
+ * ```
422
+ */
423
+ workflows?: WorkflowRouterLike;
424
+ /**
425
+ * Workflow engine configuration
426
+ * Only used if workflows router is provided
427
+ */
428
+ workflowsConfig?: {
429
+ /**
430
+ * Large output threshold in bytes
431
+ * Outputs larger than this will be stored in external storage
432
+ * @default 1024 * 1024 (1MB)
433
+ */
434
+ largeOutputThreshold?: number;
435
+ };
341
436
  /**
342
437
  * Server lifecycle hooks for custom infrastructure setup and management
343
438
  * Allows initialization of custom services and resources at different stages
@@ -517,6 +612,124 @@ declare function createServer(config?: ServerConfig): Promise<Hono>;
517
612
  */
518
613
  declare function startServer(config?: ServerConfig): Promise<ServerInstance>;
519
614
 
615
+ /**
616
+ * Shutdown Manager
617
+ *
618
+ * Manages graceful shutdown with drain behavior.
619
+ * All tracked operations must complete before shutdown proceeds.
620
+ *
621
+ * Features:
622
+ * - Hook registry: Multiple modules can register independent cleanup handlers
623
+ * - Operation tracking: Long-running tasks are awaited during shutdown (drain)
624
+ * - State management: isShuttingDown() for rejecting new work
625
+ */
626
+ interface ShutdownHookOptions {
627
+ /**
628
+ * Timeout for this hook in milliseconds
629
+ * If the hook exceeds this time, it is skipped and the next hook runs
630
+ * @default 10000 (10s)
631
+ */
632
+ timeout?: number;
633
+ /**
634
+ * Execution order (lower runs first)
635
+ * @default 100
636
+ */
637
+ order?: number;
638
+ }
639
+ declare class ShutdownManager {
640
+ private state;
641
+ private hooks;
642
+ private operations;
643
+ private operationCounter;
644
+ /**
645
+ * Register a shutdown hook
646
+ *
647
+ * Hooks run in order during shutdown, after all tracked operations drain.
648
+ * Each hook has its own timeout — failure does not block subsequent hooks.
649
+ *
650
+ * @example
651
+ * shutdown.onShutdown('ai-service', async () => {
652
+ * await aiService.cancelPending();
653
+ * }, { timeout: 30000, order: 10 });
654
+ */
655
+ onShutdown(name: string, handler: () => Promise<void>, options?: ShutdownHookOptions): void;
656
+ /**
657
+ * Track a long-running operation
658
+ *
659
+ * During shutdown (drain phase), the process waits for ALL tracked
660
+ * operations to complete before proceeding with cleanup.
661
+ *
662
+ * If shutdown has already started, the operation is rejected immediately.
663
+ *
664
+ * @returns The operation result (pass-through)
665
+ *
666
+ * @example
667
+ * const result = await shutdown.trackOperation(
668
+ * 'ai-generate',
669
+ * aiService.generate(prompt)
670
+ * );
671
+ */
672
+ trackOperation<T>(name: string, operation: Promise<T>): Promise<T>;
673
+ /**
674
+ * Whether the server is shutting down
675
+ *
676
+ * Use this to reject new work early (e.g., return 503 in route handlers).
677
+ */
678
+ isShuttingDown(): boolean;
679
+ /**
680
+ * Number of currently active tracked operations
681
+ */
682
+ getActiveOperationCount(): number;
683
+ /**
684
+ * Mark shutdown as started immediately
685
+ *
686
+ * Call this at the very beginning of the shutdown sequence so that:
687
+ * - Health check returns 503 right away
688
+ * - trackOperation() rejects new work
689
+ * - isShuttingDown() returns true
690
+ */
691
+ beginShutdown(): void;
692
+ /**
693
+ * Execute the full shutdown sequence
694
+ *
695
+ * 1. State → draining (reject new operations)
696
+ * 2. Wait for all tracked operations to complete (drain)
697
+ * 3. Run shutdown hooks in order
698
+ * 4. State → closed
699
+ *
700
+ * @param drainTimeout - Max time to wait for operations to drain (ms)
701
+ */
702
+ execute(drainTimeout: number): Promise<void>;
703
+ /**
704
+ * Wait for all tracked operations to complete, up to drainTimeout
705
+ */
706
+ private drain;
707
+ /**
708
+ * Execute registered shutdown hooks in order
709
+ */
710
+ private executeHooks;
711
+ }
712
+ /**
713
+ * Get the global ShutdownManager instance
714
+ *
715
+ * Available after server starts. Use this to register shutdown hooks
716
+ * or track long-running operations.
717
+ *
718
+ * @example
719
+ * import { getShutdownManager } from '@spfn/core/server';
720
+ *
721
+ * const shutdown = getShutdownManager();
722
+ *
723
+ * // Register cleanup
724
+ * shutdown.onShutdown('my-service', async () => {
725
+ * await myService.close();
726
+ * });
727
+ *
728
+ * // Track long operation
729
+ * await shutdown.trackOperation('ai-task', longRunningPromise);
730
+ */
731
+ declare function getShutdownManager(): ShutdownManager;
732
+
520
733
  /**
521
734
  * Server Config Builder
522
735
  *
@@ -619,8 +832,9 @@ declare class ServerConfigBuilder {
619
832
  * .events(eventRouter, { path: '/sse' })
620
833
  * ```
621
834
  */
622
- events(router: EventRouterDef<any>, config?: SSEHandlerConfig & {
835
+ events<TRouter extends EventRouterDef<any>>(router: TRouter, config?: Omit<SSEHandlerConfig, 'auth'> & {
623
836
  path?: string;
837
+ auth?: SSEAuthConfig<TRouter>;
624
838
  }): this;
625
839
  /**
626
840
  * Enable/disable debug mode
@@ -646,6 +860,27 @@ declare class ServerConfigBuilder {
646
860
  * Configure infrastructure initialization
647
861
  */
648
862
  infrastructure(infrastructure: ServerConfig['infrastructure']): this;
863
+ /**
864
+ * Register workflow router for workflow orchestration
865
+ *
866
+ * Automatically initializes the workflow engine after database is ready.
867
+ *
868
+ * @example
869
+ * ```typescript
870
+ * import { defineWorkflowRouter } from '@spfn/workflow';
871
+ *
872
+ * const workflowRouter = defineWorkflowRouter([
873
+ * provisionTenant,
874
+ * deprovisionTenant,
875
+ * ]);
876
+ *
877
+ * export default defineServerConfig()
878
+ * .routes(appRouter)
879
+ * .workflows(workflowRouter)
880
+ * .build();
881
+ * ```
882
+ */
883
+ workflows(router: ServerConfig['workflows'], config?: ServerConfig['workflowsConfig']): this;
649
884
  /**
650
885
  * Configure lifecycle hooks
651
886
  * Can be called multiple times - hooks will be executed in registration order
@@ -685,4 +920,4 @@ declare class ServerConfigBuilder {
685
920
  */
686
921
  declare function defineServerConfig(): ServerConfigBuilder;
687
922
 
688
- export { type AppFactory, type ServerConfig, type ServerInstance, createServer, defineServerConfig, loadEnvFiles, startServer };
923
+ export { type AppFactory, type ServerConfig, type ServerInstance, type ShutdownHookOptions, createServer, defineServerConfig, getShutdownManager, loadEnvFiles, startServer };