@spfn/core 0.3.0-beta.3 → 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.
- package/README.md +33 -0
- package/dist/contract/index.d.ts +4 -3
- package/dist/ops/index.d.ts +3 -2
- package/dist/route/index.d.ts +4 -2
- package/dist/{router-Cy7rAfmj.d.ts → route-builder-2ani2jEI.d.ts} +2 -125
- package/dist/router-DJdpwuB6.d.ts +127 -0
- package/dist/server/index.d.ts +56 -2
- package/dist/server/index.js +390 -2
- package/dist/server/index.js.map +1 -1
- package/dist/{types-CfYVhIQ9.d.ts → types-ClQVomgV.d.ts} +1 -1
- package/package.json +1 -1
package/dist/server/index.js
CHANGED
|
@@ -13,6 +13,7 @@ import { randomBytes, createHash } from 'crypto';
|
|
|
13
13
|
import { Agent, setGlobalDispatcher } from 'undici';
|
|
14
14
|
import { initDatabase, getDatabase, hasMigrationTargets, collectMigrationStatus, countPendingMigrations, pendingMigrationTargets, formatPendingMigrations, pendingMigrationsSummary, RUN_MIGRATIONS_HINT, closeDatabase, migrationTargets } from '@spfn/core/db';
|
|
15
15
|
import { initCache, getCache, closeCache } from '@spfn/core/cache';
|
|
16
|
+
import { FormatRegistry, Type } from '@sinclair/typebox';
|
|
16
17
|
import { serve } from '@hono/node-server';
|
|
17
18
|
import PgBoss from 'pg-boss';
|
|
18
19
|
import { networkInterfaces } from 'os';
|
|
@@ -1080,6 +1081,7 @@ async function runMigrationBootGate(config, cwd = process.cwd()) {
|
|
|
1080
1081
|
// src/server/namespace.ts
|
|
1081
1082
|
var CORE_NAMESPACE = "/_core";
|
|
1082
1083
|
var CORE_HEALTH_PATH = `${CORE_NAMESPACE}/health`;
|
|
1084
|
+
var CORE_TIME_PATH = `${CORE_NAMESPACE}/time`;
|
|
1083
1085
|
var LEGACY_HEALTH_PATH = "/health";
|
|
1084
1086
|
|
|
1085
1087
|
// src/server/shutdown-manager.ts
|
|
@@ -1480,6 +1482,378 @@ function buildStartupConfig(config, timeouts) {
|
|
|
1480
1482
|
};
|
|
1481
1483
|
}
|
|
1482
1484
|
|
|
1485
|
+
// src/route/route-builder.ts
|
|
1486
|
+
var RouteBuilder = class _RouteBuilder {
|
|
1487
|
+
_method;
|
|
1488
|
+
_path;
|
|
1489
|
+
_input;
|
|
1490
|
+
_interceptor;
|
|
1491
|
+
_middlewares;
|
|
1492
|
+
_skipMiddlewares;
|
|
1493
|
+
_contract;
|
|
1494
|
+
/**
|
|
1495
|
+
* Create a new RouteBuilder with copied properties and optional overrides
|
|
1496
|
+
*/
|
|
1497
|
+
clone(overrides) {
|
|
1498
|
+
const builder = new _RouteBuilder();
|
|
1499
|
+
builder._method = this._method;
|
|
1500
|
+
builder._path = this._path;
|
|
1501
|
+
builder._input = overrides?.input ?? this._input;
|
|
1502
|
+
builder._interceptor = overrides?.interceptor ?? this._interceptor;
|
|
1503
|
+
builder._middlewares = overrides?.middlewares ?? this._middlewares;
|
|
1504
|
+
builder._skipMiddlewares = overrides?.skipMiddlewares ?? this._skipMiddlewares;
|
|
1505
|
+
builder._contract = overrides?.contract ?? this._contract;
|
|
1506
|
+
return builder;
|
|
1507
|
+
}
|
|
1508
|
+
/**
|
|
1509
|
+
* Define input schemas
|
|
1510
|
+
*
|
|
1511
|
+
* @example
|
|
1512
|
+
* ```ts
|
|
1513
|
+
* route.get('/users/:id')
|
|
1514
|
+
* .input({
|
|
1515
|
+
* params: Type.Object({ id: Type.String() }),
|
|
1516
|
+
* query: Type.Object({ page: Type.Number() }),
|
|
1517
|
+
* headers: Type.Object({ authorization: Type.String() })
|
|
1518
|
+
* })
|
|
1519
|
+
* .handler(async (c) => {
|
|
1520
|
+
* const { params, query, headers } = await c.data();
|
|
1521
|
+
* // params = { id: string }
|
|
1522
|
+
* // query = { page: number }
|
|
1523
|
+
* // headers = { authorization: string }
|
|
1524
|
+
* })
|
|
1525
|
+
* ```
|
|
1526
|
+
*/
|
|
1527
|
+
input(input) {
|
|
1528
|
+
return this.clone({ input });
|
|
1529
|
+
}
|
|
1530
|
+
/**
|
|
1531
|
+
* Define fields injected by interceptors
|
|
1532
|
+
*
|
|
1533
|
+
* These fields are:
|
|
1534
|
+
* - Available in the handler (merged with input)
|
|
1535
|
+
* - Excluded from client types (codegen uses only input)
|
|
1536
|
+
* - Not validated by route input schema (injected by middleware)
|
|
1537
|
+
*
|
|
1538
|
+
* Use this when middleware/interceptors add fields to the request
|
|
1539
|
+
* before it reaches the handler.
|
|
1540
|
+
*
|
|
1541
|
+
* @example
|
|
1542
|
+
* ```ts
|
|
1543
|
+
* // Auth interceptor injects crypto key fields
|
|
1544
|
+
* route.post('/_auth/login')
|
|
1545
|
+
* .input({
|
|
1546
|
+
* body: Type.Object({
|
|
1547
|
+
* email: Type.String(),
|
|
1548
|
+
* password: Type.String()
|
|
1549
|
+
* })
|
|
1550
|
+
* })
|
|
1551
|
+
* .interceptor({
|
|
1552
|
+
* body: Type.Object({
|
|
1553
|
+
* publicKey: Type.String(),
|
|
1554
|
+
* keyId: Type.String(),
|
|
1555
|
+
* fingerprint: Type.String()
|
|
1556
|
+
* })
|
|
1557
|
+
* })
|
|
1558
|
+
* .handler(async (c) => {
|
|
1559
|
+
* const { body } = await c.data();
|
|
1560
|
+
* // body type: { email, password, publicKey, keyId, fingerprint }
|
|
1561
|
+
* // Client only sees: { email, password }
|
|
1562
|
+
* return loginService(body);
|
|
1563
|
+
* });
|
|
1564
|
+
* ```
|
|
1565
|
+
*/
|
|
1566
|
+
interceptor(interceptor) {
|
|
1567
|
+
return this.clone({ interceptor });
|
|
1568
|
+
}
|
|
1569
|
+
/**
|
|
1570
|
+
* Add middlewares to the route
|
|
1571
|
+
*
|
|
1572
|
+
* Accepts both regular middleware handlers and named middlewares (NamedMiddleware).
|
|
1573
|
+
* Named middlewares that are already registered globally will be automatically
|
|
1574
|
+
* deduplicated to prevent double execution.
|
|
1575
|
+
*
|
|
1576
|
+
* @example
|
|
1577
|
+
* ```ts
|
|
1578
|
+
* import { authenticate } from '@spfn/auth/server/middleware';
|
|
1579
|
+
*
|
|
1580
|
+
* // With NamedMiddleware (auto-deduped if registered globally)
|
|
1581
|
+
* route.get('/users')
|
|
1582
|
+
* .use([authenticate, RateLimitMiddleware()])
|
|
1583
|
+
*
|
|
1584
|
+
* // With regular middleware handlers
|
|
1585
|
+
* route.get('/users')
|
|
1586
|
+
* .use([AuthMiddleware(), RateLimitMiddleware()])
|
|
1587
|
+
* ```
|
|
1588
|
+
*/
|
|
1589
|
+
middleware(middlewares) {
|
|
1590
|
+
return this.clone({ middlewares });
|
|
1591
|
+
}
|
|
1592
|
+
/**
|
|
1593
|
+
* Add middlewares to the route (alias for `.middleware()`)
|
|
1594
|
+
*
|
|
1595
|
+
* Accepts both regular middleware handlers and named middlewares (NamedMiddleware).
|
|
1596
|
+
* Named middlewares that are already registered globally will be automatically
|
|
1597
|
+
* deduplicated to prevent double execution.
|
|
1598
|
+
*
|
|
1599
|
+
* @example
|
|
1600
|
+
* ```ts
|
|
1601
|
+
* import { authenticate } from '@spfn/auth/server/middleware';
|
|
1602
|
+
*
|
|
1603
|
+
* // With NamedMiddleware (auto-deduped if registered globally)
|
|
1604
|
+
* route.get('/users')
|
|
1605
|
+
* .use([authenticate, RateLimitMiddleware()])
|
|
1606
|
+
*
|
|
1607
|
+
* // With regular middleware handlers
|
|
1608
|
+
* route.get('/users')
|
|
1609
|
+
* .use([AuthMiddleware(), RateLimitMiddleware()])
|
|
1610
|
+
* ```
|
|
1611
|
+
*/
|
|
1612
|
+
use(middlewares) {
|
|
1613
|
+
return this.middleware(middlewares);
|
|
1614
|
+
}
|
|
1615
|
+
/**
|
|
1616
|
+
* Skip server-level named middlewares
|
|
1617
|
+
*
|
|
1618
|
+
* Useful for public endpoints that should bypass auth or rate limiting
|
|
1619
|
+
*
|
|
1620
|
+
* @param middlewareNames - Array of middleware names to skip, or '*' to skip all
|
|
1621
|
+
*
|
|
1622
|
+
* @example
|
|
1623
|
+
* ```ts
|
|
1624
|
+
* // Skip specific middlewares
|
|
1625
|
+
* route.get('/status')
|
|
1626
|
+
* .skip(['auth', 'rateLimit'])
|
|
1627
|
+
* .handler(async (c) => c.json({ status: 'ok' }));
|
|
1628
|
+
*
|
|
1629
|
+
* // Skip only auth (still apply rate limiting)
|
|
1630
|
+
* route.get('/public-data')
|
|
1631
|
+
* .skip(['auth'])
|
|
1632
|
+
* .handler(async (c) => { ... });
|
|
1633
|
+
*
|
|
1634
|
+
* // Skip all middlewares
|
|
1635
|
+
* route.get('/public-health')
|
|
1636
|
+
* .skip('*')
|
|
1637
|
+
* .handler(async (c) => c.json({ status: 'ok' }));
|
|
1638
|
+
* ```
|
|
1639
|
+
*/
|
|
1640
|
+
skip(middlewareNames) {
|
|
1641
|
+
return this.clone({ skipMiddlewares: middlewareNames });
|
|
1642
|
+
}
|
|
1643
|
+
/**
|
|
1644
|
+
* Publish this route as a versioned contract operation
|
|
1645
|
+
*
|
|
1646
|
+
* Marks the route as a promise to clients that are compiled and deployed
|
|
1647
|
+
* separately from the server — a mobile app, an external API consumer.
|
|
1648
|
+
* The `@spfn/core:contract` generator writes every contracted route into
|
|
1649
|
+
* `contracts/current.json`, and the build refuses a change that would break
|
|
1650
|
+
* an already-released client.
|
|
1651
|
+
*
|
|
1652
|
+
* Routes without `.contract()` are unaffected: they simply do not appear in
|
|
1653
|
+
* the contract. A web client needs nothing here — it derives its types from
|
|
1654
|
+
* the router in the same build.
|
|
1655
|
+
*
|
|
1656
|
+
* @example
|
|
1657
|
+
* ```ts
|
|
1658
|
+
* export const getUser = route.get('/users/:id')
|
|
1659
|
+
* .input({ params: Type.Object({ id: Type.String() }) })
|
|
1660
|
+
* .contract({
|
|
1661
|
+
* since: '1.2.0',
|
|
1662
|
+
* auth: 'clientProofV1',
|
|
1663
|
+
* requiresSession: true,
|
|
1664
|
+
* response: Type.Object({
|
|
1665
|
+
* id: Type.String(),
|
|
1666
|
+
* name: Type.String(),
|
|
1667
|
+
* email: Type.Optional(Type.String()),
|
|
1668
|
+
* }),
|
|
1669
|
+
* })
|
|
1670
|
+
* .handler(async (c) => { ... });
|
|
1671
|
+
* ```
|
|
1672
|
+
*/
|
|
1673
|
+
contract(contract) {
|
|
1674
|
+
return this.clone({ contract });
|
|
1675
|
+
}
|
|
1676
|
+
/**
|
|
1677
|
+
* Define handler function
|
|
1678
|
+
*
|
|
1679
|
+
* Response type is automatically inferred from the return value.
|
|
1680
|
+
* Use helper methods like `c.created()`, `c.paginated()` for proper type inference.
|
|
1681
|
+
*
|
|
1682
|
+
* @example
|
|
1683
|
+
* ```ts
|
|
1684
|
+
* // Direct return - type inferred from data
|
|
1685
|
+
* route.get('/users/:id')
|
|
1686
|
+
* .input({ params: Type.Object({ id: Type.String() }) })
|
|
1687
|
+
* .handler(async (c) => {
|
|
1688
|
+
* const { params } = await c.data();
|
|
1689
|
+
* return await getUser(params.id); // Type: User
|
|
1690
|
+
* })
|
|
1691
|
+
*
|
|
1692
|
+
* // Using c.created() - returns data with 201 status, type preserved
|
|
1693
|
+
* route.post('/users')
|
|
1694
|
+
* .input({ body: Type.Object({ name: Type.String() }) })
|
|
1695
|
+
* .handler(async (c) => {
|
|
1696
|
+
* const { body } = await c.data();
|
|
1697
|
+
* return c.created(await createUser(body)); // Type: User
|
|
1698
|
+
* })
|
|
1699
|
+
*
|
|
1700
|
+
* // Using c.paginated() - returns PaginatedResult<T>
|
|
1701
|
+
* route.get('/users')
|
|
1702
|
+
* .handler(async (c) => {
|
|
1703
|
+
* const users = await getUsers();
|
|
1704
|
+
* return c.paginated(users, 1, 20, 100); // Type: PaginatedResult<User>
|
|
1705
|
+
* })
|
|
1706
|
+
*
|
|
1707
|
+
* // Using c.noContent() - returns void
|
|
1708
|
+
* route.delete('/users/:id')
|
|
1709
|
+
* .handler(async (c) => {
|
|
1710
|
+
* await deleteUser(params.id);
|
|
1711
|
+
* return c.noContent(); // Type: void
|
|
1712
|
+
* })
|
|
1713
|
+
*
|
|
1714
|
+
* // Using c.json() - returns Response (type inference lost)
|
|
1715
|
+
* // Use only when you need custom status codes not covered by helpers
|
|
1716
|
+
* route.get('/custom')
|
|
1717
|
+
* .handler(async (c) => {
|
|
1718
|
+
* return c.json({ data }, 418); // Type: Response
|
|
1719
|
+
* })
|
|
1720
|
+
* ```
|
|
1721
|
+
*/
|
|
1722
|
+
handler(fn) {
|
|
1723
|
+
return {
|
|
1724
|
+
method: this._method,
|
|
1725
|
+
path: this._path,
|
|
1726
|
+
input: this._input,
|
|
1727
|
+
interceptor: this._interceptor,
|
|
1728
|
+
middlewares: this._middlewares,
|
|
1729
|
+
skipMiddlewares: this._skipMiddlewares,
|
|
1730
|
+
contract: this._contract,
|
|
1731
|
+
handler: fn,
|
|
1732
|
+
_input: {},
|
|
1733
|
+
_interceptor: {},
|
|
1734
|
+
_response: {}
|
|
1735
|
+
};
|
|
1736
|
+
}
|
|
1737
|
+
};
|
|
1738
|
+
function createMethodRoute(method) {
|
|
1739
|
+
return (path) => {
|
|
1740
|
+
const builder = new RouteBuilder();
|
|
1741
|
+
builder._method = method;
|
|
1742
|
+
builder._path = path;
|
|
1743
|
+
return builder;
|
|
1744
|
+
};
|
|
1745
|
+
}
|
|
1746
|
+
var route = {
|
|
1747
|
+
get: createMethodRoute("GET"),
|
|
1748
|
+
post: createMethodRoute("POST"),
|
|
1749
|
+
put: createMethodRoute("PUT"),
|
|
1750
|
+
patch: createMethodRoute("PATCH"),
|
|
1751
|
+
delete: createMethodRoute("DELETE")
|
|
1752
|
+
};
|
|
1753
|
+
|
|
1754
|
+
// src/route/router.ts
|
|
1755
|
+
function createRouterInstance(routes, packageRouters = [], globalMiddlewares = [], contractVersion = null) {
|
|
1756
|
+
return {
|
|
1757
|
+
routes,
|
|
1758
|
+
_routes: routes,
|
|
1759
|
+
_packageRouters: packageRouters,
|
|
1760
|
+
_globalMiddlewares: globalMiddlewares,
|
|
1761
|
+
_contractVersion: contractVersion,
|
|
1762
|
+
packages(routers) {
|
|
1763
|
+
const newPackageRouters = [...this._packageRouters, ...routers];
|
|
1764
|
+
for (const pkgRouter of routers) {
|
|
1765
|
+
if (pkgRouter._packageRouters?.length > 0) {
|
|
1766
|
+
newPackageRouters.push(...pkgRouter._packageRouters);
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
return createRouterInstance(
|
|
1770
|
+
this.routes,
|
|
1771
|
+
newPackageRouters,
|
|
1772
|
+
this._globalMiddlewares,
|
|
1773
|
+
this._contractVersion
|
|
1774
|
+
);
|
|
1775
|
+
},
|
|
1776
|
+
use(middlewares) {
|
|
1777
|
+
return createRouterInstance(
|
|
1778
|
+
this.routes,
|
|
1779
|
+
this._packageRouters,
|
|
1780
|
+
[...this._globalMiddlewares, ...middlewares],
|
|
1781
|
+
this._contractVersion
|
|
1782
|
+
);
|
|
1783
|
+
},
|
|
1784
|
+
contractVersion(version) {
|
|
1785
|
+
assertContractVersion(version);
|
|
1786
|
+
return createRouterInstance(
|
|
1787
|
+
this.routes,
|
|
1788
|
+
this._packageRouters,
|
|
1789
|
+
this._globalMiddlewares,
|
|
1790
|
+
version
|
|
1791
|
+
);
|
|
1792
|
+
}
|
|
1793
|
+
};
|
|
1794
|
+
}
|
|
1795
|
+
function assertContractVersion(version) {
|
|
1796
|
+
if (!/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)*$/.test(version)) {
|
|
1797
|
+
throw new Error(
|
|
1798
|
+
`contractVersion("${version}") is not a version of the form major.minor.patch. The released snapshot is named from this value and releases are compared by it.`
|
|
1799
|
+
);
|
|
1800
|
+
}
|
|
1801
|
+
}
|
|
1802
|
+
function defineRouter(routes) {
|
|
1803
|
+
return createRouterInstance(routes);
|
|
1804
|
+
}
|
|
1805
|
+
|
|
1806
|
+
// src/route/validation.ts
|
|
1807
|
+
FormatRegistry.Set(
|
|
1808
|
+
"email",
|
|
1809
|
+
(value) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)
|
|
1810
|
+
);
|
|
1811
|
+
FormatRegistry.Set(
|
|
1812
|
+
"uri",
|
|
1813
|
+
(value) => /^https?:\/\/.+/.test(value)
|
|
1814
|
+
);
|
|
1815
|
+
FormatRegistry.Set(
|
|
1816
|
+
"uuid",
|
|
1817
|
+
(value) => /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value)
|
|
1818
|
+
);
|
|
1819
|
+
FormatRegistry.Set(
|
|
1820
|
+
"date",
|
|
1821
|
+
(value) => /^\d{4}-\d{2}-\d{2}$/.test(value)
|
|
1822
|
+
);
|
|
1823
|
+
FormatRegistry.Set(
|
|
1824
|
+
"date-time",
|
|
1825
|
+
(value) => !isNaN(Date.parse(value))
|
|
1826
|
+
);
|
|
1827
|
+
|
|
1828
|
+
// src/server/server-time.ts
|
|
1829
|
+
var CORE_TIME_OPERATION_ID = "core.time";
|
|
1830
|
+
var ServerTimeResponseSchema = Type.Object({
|
|
1831
|
+
serverTimeMillis: Type.Integer()
|
|
1832
|
+
}, { additionalProperties: false });
|
|
1833
|
+
var systemServerClock = {
|
|
1834
|
+
now: () => Date.now()
|
|
1835
|
+
};
|
|
1836
|
+
function createCoreTimeRoute(clock = systemServerClock) {
|
|
1837
|
+
return route.get(CORE_TIME_PATH).skip("*").contract({
|
|
1838
|
+
since: "0.3.0",
|
|
1839
|
+
auth: "none",
|
|
1840
|
+
requiresSession: false,
|
|
1841
|
+
response: ServerTimeResponseSchema
|
|
1842
|
+
}).handler(async (c) => {
|
|
1843
|
+
const response = {
|
|
1844
|
+
serverTimeMillis: clock.now()
|
|
1845
|
+
};
|
|
1846
|
+
c.raw.header("Cache-Control", "no-store");
|
|
1847
|
+
return response;
|
|
1848
|
+
});
|
|
1849
|
+
}
|
|
1850
|
+
var CORE_TIME_ROUTE = createCoreTimeRoute();
|
|
1851
|
+
function createCoreTimeRouter(clock) {
|
|
1852
|
+
return defineRouter({
|
|
1853
|
+
[CORE_TIME_OPERATION_ID]: createCoreTimeRoute(clock)
|
|
1854
|
+
});
|
|
1855
|
+
}
|
|
1856
|
+
|
|
1483
1857
|
// src/server/create-server.ts
|
|
1484
1858
|
var rateLimitApplied = /* @__PURE__ */ new WeakSet();
|
|
1485
1859
|
async function createServer(config) {
|
|
@@ -1522,6 +1896,7 @@ async function createAutoConfiguredApp(config) {
|
|
|
1522
1896
|
applyDefaultMiddleware(app, config, enableLogger, enableCors);
|
|
1523
1897
|
await applyProxyGuard(app, config);
|
|
1524
1898
|
applyRateLimit(config);
|
|
1899
|
+
registerCoreTimeEndpoint(app, config);
|
|
1525
1900
|
if (Array.isArray(config?.use)) {
|
|
1526
1901
|
config.use.forEach((mw) => app.use("*", mw));
|
|
1527
1902
|
}
|
|
@@ -1571,7 +1946,7 @@ async function applyProxyGuard(app, config) {
|
|
|
1571
1946
|
serverLogger.warn("Proxy-guard nonce: cache module unavailable \u2014 using in-memory store (single instance only)");
|
|
1572
1947
|
}
|
|
1573
1948
|
}
|
|
1574
|
-
const autoSkip = [CORE_HEALTH_PATH, LEGACY_HEALTH_PATH];
|
|
1949
|
+
const autoSkip = [CORE_HEALTH_PATH, CORE_TIME_PATH, LEGACY_HEALTH_PATH];
|
|
1575
1950
|
if (config?.healthCheck?.path) {
|
|
1576
1951
|
autoSkip.push(config.healthCheck.path);
|
|
1577
1952
|
}
|
|
@@ -1628,6 +2003,10 @@ function resolveHealthCheck(config) {
|
|
|
1628
2003
|
detailed: healthCheckConfig.detailed ?? process.env.NODE_ENV === "development"
|
|
1629
2004
|
};
|
|
1630
2005
|
}
|
|
2006
|
+
function registerCoreTimeEndpoint(app, config) {
|
|
2007
|
+
registerRoutes(app, createCoreTimeRouter(config?.serverTime?.clock));
|
|
2008
|
+
serverLogger.debug(`Server time endpoint enabled at ${CORE_TIME_PATH}`);
|
|
2009
|
+
}
|
|
1631
2010
|
function registerCoreHealthEndpoint(app, config) {
|
|
1632
2011
|
const { enabled, path, detailed } = resolveHealthCheck(config);
|
|
1633
2012
|
if (!enabled) {
|
|
@@ -3231,6 +3610,15 @@ var ServerConfigBuilder = class {
|
|
|
3231
3610
|
this.config.healthCheck = healthCheck;
|
|
3232
3611
|
return this;
|
|
3233
3612
|
}
|
|
3613
|
+
/**
|
|
3614
|
+
* Supply the clock used by the built-in `GET /_core/time` capability.
|
|
3615
|
+
* Production servers normally keep the default `Date.now()` clock; this is
|
|
3616
|
+
* exposed so tests can assert an exact wire value without replacing globals.
|
|
3617
|
+
*/
|
|
3618
|
+
serverTime(serverTime) {
|
|
3619
|
+
this.config.serverTime = serverTime;
|
|
3620
|
+
return this;
|
|
3621
|
+
}
|
|
3234
3622
|
/**
|
|
3235
3623
|
* Configure infrastructure initialization
|
|
3236
3624
|
*/
|
|
@@ -3327,6 +3715,6 @@ function defineServerConfig() {
|
|
|
3327
3715
|
return new ServerConfigBuilder();
|
|
3328
3716
|
}
|
|
3329
3717
|
|
|
3330
|
-
export { CORE_HEALTH_PATH, CORE_NAMESPACE, PendingMigrationsError, createServer, createServerlessApp, defineServerConfig, getMigrationSnapshot, getShutdownManager, loadEnv, loadEnvFiles, provisionInfrastructure, resetMigrationSnapshot, resetServerlessApp, startServer };
|
|
3718
|
+
export { CORE_HEALTH_PATH, CORE_NAMESPACE, CORE_TIME_OPERATION_ID, CORE_TIME_PATH, CORE_TIME_ROUTE, PendingMigrationsError, ServerTimeResponseSchema, createCoreTimeRoute, createServer, createServerlessApp, defineServerConfig, getMigrationSnapshot, getShutdownManager, loadEnv, loadEnvFiles, provisionInfrastructure, resetMigrationSnapshot, resetServerlessApp, startServer };
|
|
3331
3719
|
//# sourceMappingURL=index.js.map
|
|
3332
3720
|
//# sourceMappingURL=index.js.map
|