@spfn/core 0.3.0-beta.2 → 0.3.0-beta.4

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.
@@ -0,0 +1,127 @@
1
+ import { a as NamedMiddleware } from './define-middleware-CVKgqo8S.js';
2
+ import { R as RouteDef } from './route-builder-2ani2jEI.js';
3
+
4
+ /**
5
+ * Router Definition
6
+ *
7
+ * Provides router composition and middleware management
8
+ */
9
+
10
+ /**
11
+ * Router definition - holds all routes
12
+ */
13
+ interface Router<TRoutes extends Record<string, RouteDef<any, any, any> | Router<any>>> {
14
+ routes: TRoutes;
15
+ _routes: TRoutes;
16
+ _packageRouters: Router<any>[];
17
+ _globalMiddlewares: NamedMiddleware<string>[];
18
+ /** The contract version these routes publish, or null when uncontracted. */
19
+ _contractVersion: string | null;
20
+ /**
21
+ * Register package routers (type-hidden)
22
+ *
23
+ * Package routes are:
24
+ * - Recognized by RPC proxy and backend
25
+ * - NOT exposed in client types (use package's own API like authApi, cmsApi)
26
+ *
27
+ * @example
28
+ * ```ts
29
+ * import { authRouter } from '@spfn/auth/server';
30
+ * import { cmsAppRouter } from '@spfn/cms/server';
31
+ *
32
+ * export const appRouter = defineRouter({
33
+ * getRoot,
34
+ * getStatus,
35
+ * })
36
+ * .packages([authRouter, cmsAppRouter]);
37
+ *
38
+ * // Client usage:
39
+ * // api.getRoot.call({}) - app routes
40
+ * // authApi.login.call({}) - package API
41
+ * ```
42
+ */
43
+ packages(routers: Router<any>[]): Router<TRoutes>;
44
+ /**
45
+ * Register global middlewares
46
+ *
47
+ * Applied to all routes unless explicitly skipped via .skip()
48
+ *
49
+ * @example
50
+ * ```ts
51
+ * import { authMiddleware, loggingMiddleware } from './middlewares';
52
+ *
53
+ * export const appRouter = defineRouter({
54
+ * getRoot,
55
+ * getStatus,
56
+ * })
57
+ * .packages([authRouter])
58
+ * .use([authMiddleware, loggingMiddleware]);
59
+ * ```
60
+ */
61
+ use(middlewares: NamedMiddleware<string>[]): Router<TRoutes>;
62
+ /**
63
+ * Declare the contract version these routes publish.
64
+ *
65
+ * A client compiled against this server — a mobile app in a store — is
66
+ * generated from one version of the contract and cannot be updated when the
67
+ * server changes. The server announces this version on every response so
68
+ * that client can tell whether the two ends still agree.
69
+ *
70
+ * This is the version's source. A released snapshot is written to
71
+ * `contracts/released/<version>.json` from what is declared here, so the
72
+ * filename follows the code rather than the code having to be told what the
73
+ * filename said.
74
+ *
75
+ * Only a server with contracted routes needs it. Without it the contract
76
+ * generator still writes `current.json` and still runs the compatibility
77
+ * gate; what it cannot do is cut a release or announce a version.
78
+ *
79
+ * @example
80
+ * ```ts
81
+ * export const appRouter = defineRouter({ getRoot, listItems })
82
+ * .contractVersion('1.2.0')
83
+ * .packages([authRouter]);
84
+ * ```
85
+ */
86
+ contractVersion(version: string): Router<TRoutes>;
87
+ }
88
+ /**
89
+ * Define a router with multiple routes (tRPC-style)
90
+ *
91
+ * Supports chainable API for packages and middlewares:
92
+ *
93
+ * @example
94
+ * ```ts
95
+ * // Basic usage
96
+ * export const appRouter = defineRouter({
97
+ * getRoot,
98
+ * getStatus,
99
+ * listExamples,
100
+ * });
101
+ *
102
+ * // With package routers (type-hidden)
103
+ * export const appRouter = defineRouter({
104
+ * getRoot,
105
+ * getStatus,
106
+ * })
107
+ * .packages([authRouter, cmsAppRouter]);
108
+ *
109
+ * // With global middlewares
110
+ * export const appRouter = defineRouter({
111
+ * getRoot,
112
+ * getStatus,
113
+ * })
114
+ * .packages([authRouter])
115
+ * .use([authMiddleware, loggingMiddleware]);
116
+ *
117
+ * export type AppRouter = typeof appRouter;
118
+ * ```
119
+ *
120
+ * Package routes:
121
+ * - Recognized by RPC proxy and backend for routing
122
+ * - NOT included in AppRouter type (use authApi, cmsApi instead)
123
+ * - Prevents confusion between app API and package APIs
124
+ */
125
+ declare function defineRouter<TRoutes extends Record<string, RouteDef<any, any, any> | Router<any>>>(routes: TRoutes): Router<TRoutes>;
126
+
127
+ export { type Router as R, defineRouter as d };
@@ -10,8 +10,13 @@ import { b as EventRouterDef, E as EventDef } from '../token-manager-BT5EnUAR.js
10
10
  import { d as SSEHandlerConfig, e as SSEAuthConfig } from '../types-ZQODsBft.js';
11
11
  import { W as WSRouterDef, f as WSHandlerConfig, e as WSMessageHandlers, g as WSAuthConfig } from '../types-2AbaW4Ie.js';
12
12
  import { DatabaseProvider, MigrationStatus, MigrationStatusDb } from '@spfn/core/db';
13
- import '@sinclair/typebox';
13
+ import * as _sinclair_typebox from '@sinclair/typebox';
14
+ import { Static } from '@sinclair/typebox';
15
+ import { R as RouteDef } from '../route-builder-2ani2jEI.js';
14
16
  import 'pg-boss';
17
+ import '../define-middleware-CVKgqo8S.js';
18
+ import 'hono/utils/http-status';
19
+ import '../route/types.js';
15
20
 
16
21
  /**
17
22
  * @deprecated Use `loadEnv` from '@spfn/core/env/loader' instead.
@@ -22,6 +27,33 @@ import 'pg-boss';
22
27
  */
23
28
  declare function loadEnvFiles(): void;
24
29
 
30
+ /** Stable operation identity used by separately deployed clients. */
31
+ declare const CORE_TIME_OPERATION_ID = "core.time";
32
+ /**
33
+ * Closed response shape for the server-time wire capability.
34
+ *
35
+ * Millisecond timestamps use integers throughout SPFN's external contracts.
36
+ */
37
+ declare const ServerTimeResponseSchema: _sinclair_typebox.TObject<{
38
+ serverTimeMillis: _sinclair_typebox.TInteger;
39
+ }>;
40
+ type ServerTimeResponse = Static<typeof ServerTimeResponseSchema>;
41
+ /** Injectable source of Unix epoch milliseconds. */
42
+ interface ServerClock {
43
+ now(): number;
44
+ }
45
+ /**
46
+ * Build the route from a clock so tests and alternate runtimes can supply the
47
+ * epoch source without replacing global time.
48
+ */
49
+ declare function createCoreTimeRoute(clock?: ServerClock): RouteDef<{}, {}, {
50
+ serverTimeMillis: number;
51
+ }>;
52
+ /** The production route and the public wire-contract source of truth. */
53
+ declare const CORE_TIME_ROUTE: RouteDef<{}, {}, {
54
+ serverTimeMillis: number;
55
+ }>;
56
+
25
57
  /**
26
58
  * Workflow router interface for @spfn/core integration
27
59
  *
@@ -454,7 +486,9 @@ interface ServerConfig {
454
486
  };
455
487
  /**
456
488
  * Health check endpoint configuration
457
- * Provides monitoring endpoints for Kubernetes probes and load balancers
489
+ *
490
+ * The endpoint answers at `/_core/health`, which no app route can take from
491
+ * it. Point Kubernetes probes and load balancers there.
458
492
  */
459
493
  healthCheck?: {
460
494
  /**
@@ -464,9 +498,19 @@ interface ServerConfig {
464
498
  */
465
499
  enabled?: boolean;
466
500
  /**
467
- * Health check endpoint path
468
- * @default '/health'
469
- * @env HEALTH_CHECK_PATH
501
+ * An additional path to answer on, for a deployment whose probe path is
502
+ * fixed somewhere you cannot change it.
503
+ *
504
+ * BREAKING (@spfn/core 0.4): unset, the endpoint answers at
505
+ * `/_core/health` and nowhere else. It used to default to `/health` and
506
+ * swallow any app route declared there. `/health` now belongs to your
507
+ * app; set this to `'/health'` to have the built-in answer there again.
508
+ *
509
+ * The path is registered before app routes, so an app route on the same
510
+ * path will not run and the server says so at boot.
511
+ *
512
+ * @default undefined — `/_core/health` only
513
+ * @example '/health'
470
514
  */
471
515
  path?: string;
472
516
  /**
@@ -477,6 +521,15 @@ interface ServerConfig {
477
521
  */
478
522
  detailed?: boolean;
479
523
  };
524
+ /**
525
+ * Server-time capability dependencies.
526
+ *
527
+ * The endpoint is always enabled at `GET /_core/time`. Supplying a clock is
528
+ * primarily a deterministic test seam; production defaults to `Date.now()`.
529
+ */
530
+ serverTime?: {
531
+ clock?: ServerClock;
532
+ };
480
533
  /**
481
534
  * Migration boot gate
482
535
  *
@@ -501,17 +554,23 @@ interface ServerConfig {
501
554
  /**
502
555
  * Infrastructure initialization control
503
556
  * Controls automatic initialization of database and Redis
504
- * @default Both enabled if credentials exist
557
+ *
558
+ * Both are initialized unless set to `false` — the credentials are not
559
+ * sniffed. A server that needs no database must say so, otherwise boot
560
+ * fails with `No database configuration found`. A component set to `false`
561
+ * is reported as `disabled` by the health endpoint and never degrades it.
562
+ *
563
+ * @default Both enabled
505
564
  */
506
565
  infrastructure?: {
507
566
  /**
508
567
  * Enable/disable automatic database initialization
509
- * @default true if DATABASE_URL exists
568
+ * @default true
510
569
  */
511
570
  database?: boolean;
512
571
  /**
513
572
  * Enable/disable automatic Redis initialization
514
- * @default true if REDIS_URL exists
573
+ * @default true
515
574
  */
516
575
  redis?: boolean;
517
576
  };
@@ -710,6 +769,43 @@ declare module 'hono' {
710
769
  */
711
770
  declare function createServer(config?: ServerConfig): Promise<Hono>;
712
771
 
772
+ /**
773
+ * The paths `@spfn/core` registers for itself.
774
+ *
775
+ * Its own module because both `create-server.ts` (which registers them) and
776
+ * `helpers.ts` (which reports them in the startup banner) need them, and
777
+ * `create-server.ts` already imports `helpers.ts` — putting them in either file
778
+ * would close a cycle, which `pnpm check:circular` fails the build on.
779
+ *
780
+ * @module server/namespace
781
+ */
782
+ /**
783
+ * The path prefix `@spfn/core` registers its own endpoints under.
784
+ *
785
+ * An app declaring a route in here is declaring something it does not own, and
786
+ * the route will not run — core's endpoints are registered before app routes.
787
+ * `@spfn/auth` (`/_auth/`) and ops routes (`/_ops/`) follow the same convention,
788
+ * and neither has ever had a shadowing defect.
789
+ */
790
+ declare const CORE_NAMESPACE = "/_core";
791
+ /**
792
+ * Where the built-in health endpoint always answers.
793
+ *
794
+ * Point a readiness probe, a Dockerfile `HEALTHCHECK` or an uptime monitor here
795
+ * rather than at `/health`: no app route can claim this path, so the answer does
796
+ * not depend on what the app happens to declare. A probe's path is fixed in
797
+ * places this repository cannot change — a GitOps manifest, a Dockerfile, a load
798
+ * balancer console — and a version bump migrates none of them.
799
+ */
800
+ declare const CORE_HEALTH_PATH = "/_core/health";
801
+ /**
802
+ * Where clients obtain the server's current Unix epoch in milliseconds.
803
+ *
804
+ * This endpoint is registered before application routes and application auth
805
+ * middleware so a client can call it before it has a proof or session.
806
+ */
807
+ declare const CORE_TIME_PATH = "/_core/time";
808
+
713
809
  /**
714
810
  * Start SPFN Server
715
811
  *
@@ -1149,6 +1245,12 @@ declare class ServerConfigBuilder {
1149
1245
  * Configure health check endpoint
1150
1246
  */
1151
1247
  healthCheck(healthCheck: ServerConfig['healthCheck']): this;
1248
+ /**
1249
+ * Supply the clock used by the built-in `GET /_core/time` capability.
1250
+ * Production servers normally keep the default `Date.now()` clock; this is
1251
+ * exposed so tests can assert an exact wire value without replacing globals.
1252
+ */
1253
+ serverTime(serverTime: ServerConfig['serverTime']): this;
1152
1254
  /**
1153
1255
  * Configure infrastructure initialization
1154
1256
  */
@@ -1225,4 +1327,4 @@ declare class ServerConfigBuilder {
1225
1327
  */
1226
1328
  declare function defineServerConfig(): ServerConfigBuilder;
1227
1329
 
1228
- export { type AppFactory, type MigrationSnapshot, PendingMigrationsError, type ServerConfig, type ServerInstance, type ShutdownHookOptions, createServer, createServerlessApp, defineServerConfig, getMigrationSnapshot, getShutdownManager, loadEnvFiles, provisionInfrastructure, resetMigrationSnapshot, resetServerlessApp, startServer };
1330
+ export { type AppFactory, CORE_HEALTH_PATH, CORE_NAMESPACE, CORE_TIME_OPERATION_ID, CORE_TIME_PATH, CORE_TIME_ROUTE, type MigrationSnapshot, PendingMigrationsError, type ServerClock, type ServerConfig, type ServerInstance, type ServerTimeResponse, ServerTimeResponseSchema, type ShutdownHookOptions, createCoreTimeRoute, createServer, createServerlessApp, defineServerConfig, getMigrationSnapshot, getShutdownManager, loadEnvFiles, provisionInfrastructure, resetMigrationSnapshot, resetServerlessApp, startServer };