@winstonsayno/mcp-gateway 5.1.0 → 5.3.0

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,121 @@
1
+ /**
2
+ * Managed edge nodes (5.3): the control plane knows its edge fleet, reports config drift and pushes new config.
3
+ *
4
+ * Edges (`@winstonsayno/mcp-gateway/edge` with `sync`) pull snapshots on their own schedule. With `edgeFleet`
5
+ * configured the Node gateway can also *push*: it calls each edge's `POST /api/v1/edge/sync`, which makes the
6
+ * edge pull the current snapshot, replay queued calls and upload usage right away.
7
+ *
8
+ * ```yaml
9
+ * edgeFleet:
10
+ * pushTimeoutMs: 10000
11
+ * nodes:
12
+ * - { id: cf-hkg, url: https://edge-hkg.example.workers.dev, apiKey: ${EDGE_KEY}, labels: { ring: canary } }
13
+ * - { id: deno-fra, url: https://fra.example.deno.dev, apiKey: ${EDGE_KEY}, labels: { ring: stable } }
14
+ * ```
15
+ *
16
+ * - `GET /admin/edge-fleet` — configured + seen edges with drift: `in-sync`, `stale` (applied an older snapshot),
17
+ * `never-synced`, `unmanaged` (syncs but is not in `nodes`), `offline` (not seen for `offlineAfterMs`).
18
+ * - `POST /admin/edge-fleet/push` — `{ nodes?: string[], labels?: Record<string,string>, onlyDrifted?: boolean }`
19
+ * → per-node result. Rolling out by ring: push `labels: { ring: canary }` first, then the rest.
20
+ *
21
+ * @module features/edge-fleet
22
+ */
23
+ import { z } from 'zod';
24
+ import { registerFeature, objectBody, badRequest } from '../gateway/features.js';
25
+ import { buildEdgeSnapshot } from '../gateway/edge-control.js';
26
+ export const EdgeFleetSchema = z
27
+ .object({
28
+ pushTimeoutMs: z.number().int().min(100).default(10_000),
29
+ offlineAfterMs: z.number().int().min(1000).default(15 * 60_000),
30
+ nodes: z
31
+ .array(z.object({ id: z.string().regex(/^[A-Za-z0-9_.:-]{1,128}$/), url: z.string().url(), apiKey: z.string().optional(), labels: z.record(z.string()).default({}) }).strict())
32
+ .default([]),
33
+ })
34
+ .strict();
35
+ /** Merge configured nodes with nodes seen by the control plane and classify drift. */
36
+ export function fleetView(cfg, seen, currentEtags, now = Date.now()) {
37
+ const byId = new Map(seen.map((n) => [n.edgeId, n]));
38
+ const out = [];
39
+ const classify = (n, managed) => {
40
+ if (!n || !n.snapshotEtag)
41
+ return n && !managed ? 'unmanaged' : 'never-synced';
42
+ if (now - Date.parse(n.lastSeen) > cfg.offlineAfterMs)
43
+ return 'offline';
44
+ if (!managed)
45
+ return 'unmanaged';
46
+ return currentEtags.includes(n.snapshotEtag) ? 'in-sync' : 'stale';
47
+ };
48
+ for (const c of cfg.nodes) {
49
+ const n = byId.get(c.id);
50
+ out.push({ id: c.id, url: c.url, labels: c.labels, managed: true, drift: classify(n, true), appliedEtag: n?.snapshotEtag, lastSeen: n?.lastSeen, lastSync: n?.lastSync, queuedCalls: n?.queuedCalls ?? 0, errors: n?.errors ?? 0 });
51
+ byId.delete(c.id);
52
+ }
53
+ for (const n of byId.values()) {
54
+ out.push({ id: n.edgeId, labels: {}, managed: false, drift: classify(n, false), appliedEtag: n.snapshotEtag, lastSeen: n.lastSeen, lastSync: n.lastSync, queuedCalls: n.queuedCalls, errors: n.errors });
55
+ }
56
+ return out;
57
+ }
58
+ /** Which configured nodes a push targets. */
59
+ export function selectNodes(cfg, view, sel) {
60
+ return cfg.nodes.filter((n) => {
61
+ if (sel.nodes?.length && !sel.nodes.includes(n.id))
62
+ return false;
63
+ if (sel.labels && !Object.entries(sel.labels).every(([k, v]) => n.labels[k] === v))
64
+ return false;
65
+ if (sel.onlyDrifted && view.find((v) => v.id === n.id)?.drift === 'in-sync')
66
+ return false;
67
+ return true;
68
+ });
69
+ }
70
+ /** Ask each edge to sync now. */
71
+ export async function pushToNodes(nodes, timeoutMs, f = fetch) {
72
+ return Promise.all(nodes.map(async (n) => {
73
+ const started = Date.now();
74
+ try {
75
+ const res = await f(`${n.url.replace(/\/$/, '')}/api/v1/edge/sync`, {
76
+ method: 'POST',
77
+ headers: { 'content-type': 'application/json', ...(n.apiKey ? { authorization: `Bearer ${n.apiKey}` } : {}) },
78
+ body: '{}',
79
+ signal: AbortSignal.timeout(timeoutMs),
80
+ });
81
+ const body = (await res.json().catch(() => ({})));
82
+ return { id: n.id, ok: res.ok, status: res.status, ...(body.config ? { config: body.config } : {}), ...(!res.ok ? { error: body.message ?? `HTTP ${res.status}` } : {}), durationMs: Date.now() - started };
83
+ }
84
+ catch (e) {
85
+ return { id: n.id, ok: false, error: e.message, durationMs: Date.now() - started };
86
+ }
87
+ }));
88
+ }
89
+ registerFeature({
90
+ id: 'edge-fleet',
91
+ since: '5.3.0',
92
+ summary: 'Managed edge nodes: fleet view with config drift, push config to edges',
93
+ mount: (router, ctx) => {
94
+ const resolve = () => EdgeFleetSchema.parse(ctx.config().edgeFleet ?? {});
95
+ const etags = () => [buildEdgeSnapshot(ctx.config(), ctx.tools(), false).etag, buildEdgeSnapshot(ctx.config(), ctx.tools(), true).etag];
96
+ router.get('/', (_req, res) => {
97
+ const cfg = resolve();
98
+ const [etag] = etags();
99
+ const nodes = fleetView(cfg, ctx.edgeNodes?.() ?? [], etags());
100
+ const counts = nodes.reduce((m, n) => ((m[n.drift] = (m[n.drift] ?? 0) + 1), m), {});
101
+ res.json({ etag, counts, nodes });
102
+ });
103
+ router.post('/push', async (req, res) => {
104
+ const b = objectBody(req, res);
105
+ if (!b)
106
+ return;
107
+ if (b.nodes !== undefined && !(Array.isArray(b.nodes) && b.nodes.every((x) => typeof x === 'string')))
108
+ return badRequest(res, '"nodes" must be an array of ids');
109
+ if (b.labels !== undefined && (typeof b.labels !== 'object' || b.labels === null || Array.isArray(b.labels)))
110
+ return badRequest(res, '"labels" must be an object');
111
+ const cfg = resolve();
112
+ if (!cfg.nodes.length)
113
+ return void res.status(404).json({ error: 'Not Found', message: 'no managed edge nodes (`edgeFleet.nodes`)' });
114
+ const view = fleetView(cfg, ctx.edgeNodes?.() ?? [], etags());
115
+ const targets = selectNodes(cfg, view, { nodes: b.nodes, labels: b.labels, onlyDrifted: b.onlyDrifted === true });
116
+ const results = await pushToNodes(targets, cfg.pushTimeoutMs);
117
+ res.json({ pushed: results.filter((r) => r.ok).length, failed: results.filter((r) => !r.ok).length, results });
118
+ });
119
+ },
120
+ });
121
+ //# sourceMappingURL=edge-fleet.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edge-fleet.js","sourceRoot":"","sources":["../../src/features/edge-fleet.ts"],"names":[],"mappings":"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"}
@@ -5,5 +5,7 @@
5
5
  * @module features
6
6
  */
7
7
  import './conformance.js';
8
+ import './regions.js';
9
+ import './edge-fleet.js';
8
10
  export { listFeatures, registerFeature, createFeatureRouter } from '../gateway/features.js';
9
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/features/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,kBAAkB,CAAC;AAE1B,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/features/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,kBAAkB,CAAC;AAC1B,OAAO,cAAc,CAAC;AACtB,OAAO,iBAAiB,CAAC;AAEzB,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC"}
@@ -5,5 +5,7 @@
5
5
  * @module features
6
6
  */
7
7
  import './conformance.js';
8
+ import './regions.js';
9
+ import './edge-fleet.js';
8
10
  export { listFeatures, registerFeature, createFeatureRouter } from '../gateway/features.js';
9
11
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/features/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,kBAAkB,CAAC;AAE1B,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/features/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,kBAAkB,CAAC;AAC1B,OAAO,cAAc,CAAC;AACtB,OAAO,iBAAiB,CAAC;AAEzB,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC"}
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Multi-region active-active (5.2).
3
+ *
4
+ * Every region runs a full gateway. Regions replicate a small shared key-value state (last-writer-wins per key,
5
+ * ties broken by region id) and gossip which upstream servers are online, so a call whose upstream is down locally
6
+ * can be failed over to a region that has it.
7
+ *
8
+ * ```yaml
9
+ * regions:
10
+ * self: eu-west
11
+ * syncIntervalMs: 5000
12
+ * peers:
13
+ * - { id: us-east, url: https://us.gw.example.com, apiKey: ${US_ADMIN_KEY}, priority: 1 }
14
+ * ```
15
+ *
16
+ * - `GET /admin/regions` — this region, peers (health, last sync, servers online), replicated key count.
17
+ * - `POST /admin/regions/sync` — peer exchange `{ region, since, entries, servers }` → `{ region, entries, servers }`.
18
+ * - `GET|PUT|DELETE /admin/regions/kv/:key` — replicated state (`PUT` body `{ value }`).
19
+ * - `GET /admin/regions/route/:serverId` — where a call to that server should run: `local`, a peer, or none.
20
+ *
21
+ * @module features/regions
22
+ */
23
+ import { z } from 'zod';
24
+ export declare const RegionsSchema: z.ZodObject<{
25
+ self: z.ZodString;
26
+ syncIntervalMs: z.ZodDefault<z.ZodNumber>;
27
+ /** Consecutive failed syncs before a peer is considered down. */
28
+ downAfter: z.ZodDefault<z.ZodNumber>;
29
+ peers: z.ZodDefault<z.ZodArray<z.ZodObject<{
30
+ id: z.ZodString;
31
+ url: z.ZodString;
32
+ apiKey: z.ZodOptional<z.ZodString>;
33
+ priority: z.ZodDefault<z.ZodNumber>;
34
+ }, "strict", z.ZodTypeAny, {
35
+ id: string;
36
+ url: string;
37
+ priority: number;
38
+ apiKey?: string | undefined;
39
+ }, {
40
+ id: string;
41
+ url: string;
42
+ apiKey?: string | undefined;
43
+ priority?: number | undefined;
44
+ }>, "many">>;
45
+ }, "strict", z.ZodTypeAny, {
46
+ self: string;
47
+ syncIntervalMs: number;
48
+ downAfter: number;
49
+ peers: {
50
+ id: string;
51
+ url: string;
52
+ priority: number;
53
+ apiKey?: string | undefined;
54
+ }[];
55
+ }, {
56
+ self: string;
57
+ syncIntervalMs?: number | undefined;
58
+ downAfter?: number | undefined;
59
+ peers?: {
60
+ id: string;
61
+ url: string;
62
+ apiKey?: string | undefined;
63
+ priority?: number | undefined;
64
+ }[] | undefined;
65
+ }>;
66
+ export type RegionsConfig = z.input<typeof RegionsSchema>;
67
+ type Resolved = z.output<typeof RegionsSchema>;
68
+ export interface ReplicatedEntry {
69
+ key: string;
70
+ /** `null` = tombstone (deleted). */
71
+ value: unknown;
72
+ /** Hybrid timestamp: wall-clock ms, bumped to stay monotonic per region. */
73
+ ts: number;
74
+ region: string;
75
+ }
76
+ export interface PeerState {
77
+ id: string;
78
+ url: string;
79
+ priority: number;
80
+ status: 'unknown' | 'up' | 'down';
81
+ failures: number;
82
+ lastSync?: string;
83
+ lastError?: string;
84
+ /** Highest `ts` received from this peer (sync cursor). */
85
+ cursor: number;
86
+ servers: string[];
87
+ }
88
+ /** Last-writer-wins: newer ts wins; equal ts → lexicographically larger region wins. */
89
+ export declare const newer: (a: ReplicatedEntry, b: ReplicatedEntry | undefined) => boolean;
90
+ export declare class RegionMesh {
91
+ private cfg;
92
+ private readonly now;
93
+ private readonly entries;
94
+ private readonly peers;
95
+ private clock;
96
+ private timer?;
97
+ constructor(cfg: Resolved, now?: () => number);
98
+ get self(): string;
99
+ configure(cfg: Resolved): void;
100
+ private tick;
101
+ get(key: string): unknown;
102
+ put(key: string, value: unknown): ReplicatedEntry;
103
+ delete(key: string): boolean;
104
+ size(): number;
105
+ /** Merge remote entries; returns how many changed local state. */
106
+ merge(entries: ReplicatedEntry[]): number;
107
+ /** Entries changed after `since` (any origin, so state relays through intermediate regions). */
108
+ delta(since?: number): ReplicatedEntry[];
109
+ peerList(): PeerState[];
110
+ /** Record a peer's gossip (from an inbound or outbound sync). */
111
+ sawPeer(id: string, servers: unknown, cursor?: number): void;
112
+ peerFailed(id: string, err: string): void;
113
+ /** Where a call to `serverId` should run. */
114
+ route(serverId: string, localOnline: string[]): {
115
+ target: 'local';
116
+ } | {
117
+ target: 'peer';
118
+ peer: string;
119
+ url: string;
120
+ } | {
121
+ target: 'none';
122
+ };
123
+ /** One outbound sync round with every peer. */
124
+ syncOnce(localOnline: string[], f?: typeof fetch): Promise<void>;
125
+ start(localOnline: () => string[], f?: typeof fetch): void;
126
+ stop(): void;
127
+ status(): Record<string, unknown>;
128
+ }
129
+ /** Resolve (validate + defaults) a `regions` section. */
130
+ export declare const resolveRegions: (raw: unknown) => Resolved;
131
+ export {};
132
+ //# sourceMappingURL=regions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"regions.d.ts","sourceRoot":"","sources":["../../src/features/regions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,268 @@
1
+ /**
2
+ * Multi-region active-active (5.2).
3
+ *
4
+ * Every region runs a full gateway. Regions replicate a small shared key-value state (last-writer-wins per key,
5
+ * ties broken by region id) and gossip which upstream servers are online, so a call whose upstream is down locally
6
+ * can be failed over to a region that has it.
7
+ *
8
+ * ```yaml
9
+ * regions:
10
+ * self: eu-west
11
+ * syncIntervalMs: 5000
12
+ * peers:
13
+ * - { id: us-east, url: https://us.gw.example.com, apiKey: ${US_ADMIN_KEY}, priority: 1 }
14
+ * ```
15
+ *
16
+ * - `GET /admin/regions` — this region, peers (health, last sync, servers online), replicated key count.
17
+ * - `POST /admin/regions/sync` — peer exchange `{ region, since, entries, servers }` → `{ region, entries, servers }`.
18
+ * - `GET|PUT|DELETE /admin/regions/kv/:key` — replicated state (`PUT` body `{ value }`).
19
+ * - `GET /admin/regions/route/:serverId` — where a call to that server should run: `local`, a peer, or none.
20
+ *
21
+ * @module features/regions
22
+ */
23
+ import { z } from 'zod';
24
+ import { registerFeature, badRequest, objectBody } from '../gateway/features.js';
25
+ export const RegionsSchema = z
26
+ .object({
27
+ self: z.string().min(1).regex(/^[a-z0-9][a-z0-9-]*$/, 'lowercase letters, digits and dashes'),
28
+ syncIntervalMs: z.number().int().min(250).default(5000),
29
+ /** Consecutive failed syncs before a peer is considered down. */
30
+ downAfter: z.number().int().min(1).default(3),
31
+ peers: z
32
+ .array(z.object({ id: z.string().min(1), url: z.string().url(), apiKey: z.string().optional(), priority: z.number().int().min(0).default(100) }).strict())
33
+ .default([]),
34
+ })
35
+ .strict();
36
+ /** Last-writer-wins: newer ts wins; equal ts → lexicographically larger region wins. */
37
+ export const newer = (a, b) => !b || a.ts > b.ts || (a.ts === b.ts && a.region > b.region);
38
+ export class RegionMesh {
39
+ cfg;
40
+ now;
41
+ entries = new Map();
42
+ peers = new Map();
43
+ clock = 0;
44
+ timer;
45
+ constructor(cfg, now = Date.now) {
46
+ this.cfg = cfg;
47
+ this.now = now;
48
+ this.configure(cfg);
49
+ }
50
+ get self() {
51
+ return this.cfg.self;
52
+ }
53
+ configure(cfg) {
54
+ this.cfg = cfg;
55
+ const keep = new Set(cfg.peers.map((p) => p.id));
56
+ for (const id of [...this.peers.keys()])
57
+ if (!keep.has(id))
58
+ this.peers.delete(id);
59
+ for (const p of cfg.peers) {
60
+ const cur = this.peers.get(p.id);
61
+ this.peers.set(p.id, cur ? { ...cur, url: p.url, priority: p.priority } : { id: p.id, url: p.url, priority: p.priority, status: 'unknown', failures: 0, cursor: 0, servers: [] });
62
+ }
63
+ }
64
+ tick() {
65
+ this.clock = Math.max(this.clock + 1, this.now());
66
+ return this.clock;
67
+ }
68
+ get(key) {
69
+ const e = this.entries.get(key);
70
+ return e && e.value !== null ? e.value : undefined;
71
+ }
72
+ put(key, value) {
73
+ const e = { key, value: value === undefined ? null : value, ts: this.tick(), region: this.cfg.self };
74
+ this.entries.set(key, e);
75
+ return e;
76
+ }
77
+ delete(key) {
78
+ const had = this.get(key) !== undefined;
79
+ this.put(key, null);
80
+ return had;
81
+ }
82
+ size() {
83
+ return [...this.entries.values()].filter((e) => e.value !== null).length;
84
+ }
85
+ /** Merge remote entries; returns how many changed local state. */
86
+ merge(entries) {
87
+ let n = 0;
88
+ for (const e of entries) {
89
+ if (!e || typeof e.key !== 'string' || typeof e.ts !== 'number' || typeof e.region !== 'string')
90
+ continue;
91
+ if (newer(e, this.entries.get(e.key))) {
92
+ this.entries.set(e.key, { key: e.key, value: e.value ?? null, ts: e.ts, region: e.region });
93
+ n++;
94
+ }
95
+ this.clock = Math.max(this.clock, e.ts);
96
+ }
97
+ return n;
98
+ }
99
+ /** Entries changed after `since` (any origin, so state relays through intermediate regions). */
100
+ delta(since = 0) {
101
+ return [...this.entries.values()].filter((e) => e.ts > since).sort((a, b) => a.ts - b.ts);
102
+ }
103
+ peerList() {
104
+ return [...this.peers.values()].sort((a, b) => a.priority - b.priority || a.id.localeCompare(b.id));
105
+ }
106
+ /** Record a peer's gossip (from an inbound or outbound sync). */
107
+ sawPeer(id, servers, cursor) {
108
+ const p = this.peers.get(id);
109
+ if (!p)
110
+ return;
111
+ p.status = 'up';
112
+ p.failures = 0;
113
+ p.lastSync = new Date(this.now()).toISOString();
114
+ delete p.lastError;
115
+ if (Array.isArray(servers))
116
+ p.servers = servers.map(String);
117
+ if (typeof cursor === 'number')
118
+ p.cursor = Math.max(p.cursor, cursor);
119
+ }
120
+ peerFailed(id, err) {
121
+ const p = this.peers.get(id);
122
+ if (!p)
123
+ return;
124
+ p.failures++;
125
+ p.lastError = err;
126
+ if (p.failures >= this.cfg.downAfter)
127
+ p.status = 'down';
128
+ }
129
+ /** Where a call to `serverId` should run. */
130
+ route(serverId, localOnline) {
131
+ if (localOnline.includes(serverId))
132
+ return { target: 'local' };
133
+ const peer = this.peerList().find((p) => p.status === 'up' && p.servers.includes(serverId));
134
+ return peer ? { target: 'peer', peer: peer.id, url: peer.url } : { target: 'none' };
135
+ }
136
+ /** One outbound sync round with every peer. */
137
+ async syncOnce(localOnline, f = fetch) {
138
+ await Promise.all(this.peerList().map(async (p) => {
139
+ const conf = this.cfg.peers.find((x) => x.id === p.id);
140
+ try {
141
+ const res = await f(`${p.url.replace(/\/$/, '')}/api/v1/admin/regions/sync`, {
142
+ method: 'POST',
143
+ headers: { 'content-type': 'application/json', ...(conf?.apiKey ? { authorization: `Bearer ${conf.apiKey}` } : {}) },
144
+ body: JSON.stringify({ region: this.cfg.self, since: p.cursor, entries: this.delta(0), servers: localOnline }),
145
+ signal: AbortSignal.timeout(Math.max(1000, this.cfg.syncIntervalMs)),
146
+ });
147
+ if (!res.ok)
148
+ throw new Error(`HTTP ${res.status}`);
149
+ const body = (await res.json());
150
+ const entries = Array.isArray(body.entries) ? body.entries : [];
151
+ this.merge(entries);
152
+ this.sawPeer(p.id, body.servers, entries.reduce((m, e) => Math.max(m, e.ts ?? 0), 0));
153
+ }
154
+ catch (e) {
155
+ this.peerFailed(p.id, e.message);
156
+ }
157
+ }));
158
+ }
159
+ start(localOnline, f = fetch) {
160
+ this.stop();
161
+ if (!this.cfg.peers.length)
162
+ return;
163
+ this.timer = setInterval(() => void this.syncOnce(localOnline(), f), this.cfg.syncIntervalMs);
164
+ this.timer.unref?.();
165
+ }
166
+ stop() {
167
+ if (this.timer)
168
+ clearInterval(this.timer);
169
+ this.timer = undefined;
170
+ }
171
+ status() {
172
+ return { self: this.cfg.self, syncIntervalMs: this.cfg.syncIntervalMs, keys: this.size(), peers: this.peerList() };
173
+ }
174
+ }
175
+ /** Resolve (validate + defaults) a `regions` section. */
176
+ export const resolveRegions = (raw) => RegionsSchema.parse(raw);
177
+ function meshFor(ctx, state) {
178
+ const raw = ctx.config().regions;
179
+ if (!raw) {
180
+ state.mesh?.stop();
181
+ state.mesh = undefined;
182
+ state.key = undefined;
183
+ return undefined;
184
+ }
185
+ const key = JSON.stringify(raw);
186
+ if (state.key !== key) {
187
+ const cfg = resolveRegions(raw);
188
+ if (state.mesh && state.mesh.self === cfg.self)
189
+ state.mesh.configure(cfg);
190
+ else
191
+ state.mesh = new RegionMesh(cfg);
192
+ state.key = key;
193
+ state.mesh.start(() => ctx.onlineServers?.() ?? []);
194
+ }
195
+ return state.mesh;
196
+ }
197
+ registerFeature({
198
+ id: 'regions',
199
+ since: '5.2.0',
200
+ summary: 'Multi-region active-active: replicated state, peer health, cross-region failover routing',
201
+ mount: (router, ctx) => {
202
+ const state = {};
203
+ ctx.onStop?.(() => state.mesh?.stop());
204
+ // Start syncing as soon as the gateway is up (config may also change later; re-checked on each request).
205
+ setImmediate(() => { try {
206
+ meshFor(ctx, state);
207
+ }
208
+ catch { /* invalid config is reported on request */ } });
209
+ const need = (res) => {
210
+ const m = meshFor(ctx, state);
211
+ if (!m)
212
+ res.status(404).json({ error: 'Not Found', message: 'multi-region is not configured (`regions:`)' });
213
+ return m;
214
+ };
215
+ router.get('/', (_req, res) => {
216
+ const m = need(res);
217
+ if (m)
218
+ res.json(m.status());
219
+ });
220
+ router.post('/sync', (req, res) => {
221
+ const m = need(res);
222
+ if (!m)
223
+ return;
224
+ const b = objectBody(req, res);
225
+ if (!b)
226
+ return;
227
+ if (typeof b.region !== 'string')
228
+ return badRequest(res, '"region" is required');
229
+ if (!m.peerList().some((p) => p.id === b.region))
230
+ return void res.status(403).json({ error: 'Forbidden', message: `unknown region "${b.region}"` });
231
+ const entries = Array.isArray(b.entries) ? b.entries : [];
232
+ m.merge(entries);
233
+ m.sawPeer(b.region, b.servers, entries.reduce((x, e) => Math.max(x, e?.ts ?? 0), 0));
234
+ res.json({ region: m.self, entries: m.delta(typeof b.since === 'number' ? b.since : 0), servers: ctx.onlineServers?.() ?? [] });
235
+ });
236
+ router.get('/kv/:key', (req, res) => {
237
+ const m = need(res);
238
+ if (!m)
239
+ return;
240
+ const v = m.get(req.params.key);
241
+ if (v === undefined)
242
+ return void res.status(404).json({ error: 'Not Found', message: 'no such key' });
243
+ res.json({ key: req.params.key, value: v });
244
+ });
245
+ router.put('/kv/:key', (req, res) => {
246
+ const m = need(res);
247
+ if (!m)
248
+ return;
249
+ const b = objectBody(req, res);
250
+ if (!b)
251
+ return;
252
+ if (!('value' in b) || b.value === null)
253
+ return badRequest(res, '"value" is required (use DELETE to remove)');
254
+ res.json(m.put(req.params.key, b.value));
255
+ });
256
+ router.delete('/kv/:key', (req, res) => {
257
+ const m = need(res);
258
+ if (m)
259
+ res.json({ deleted: m.delete(req.params.key) });
260
+ });
261
+ router.get('/route/:serverId', (req, res) => {
262
+ const m = need(res);
263
+ if (m)
264
+ res.json({ serverId: req.params.serverId, ...m.route(req.params.serverId, ctx.onlineServers?.() ?? []) });
265
+ });
266
+ },
267
+ });
268
+ //# sourceMappingURL=regions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"regions.js","sourceRoot":"","sources":["../../src/features/regions.ts"],"names":[],"mappings":"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"}
@@ -17,6 +17,12 @@ export interface FeatureContext {
17
17
  recent: (limit?: number) => RequestMetric[];
18
18
  /** Base URL of this gateway when listening (for self-tests). */
19
19
  baseUrl: () => string | undefined;
20
+ /** Upstream server ids currently online on this gateway (5.2). */
21
+ onlineServers?: () => string[];
22
+ /** Register a cleanup run when the gateway stops (5.2): timers, sockets. */
23
+ onStop?: (fn: () => void | Promise<void>) => void;
24
+ /** Edge gateways seen by the edge control plane (5.3). */
25
+ edgeNodes?: () => import('./edge-control.js').EdgeNode[];
20
26
  }
21
27
  export interface FeatureModule {
22
28
  /** Mount point: `/api/v1/admin/<id>`. */
@@ -26,6 +32,10 @@ export interface FeatureModule {
26
32
  summary: string;
27
33
  mount: (router: Router, ctx: FeatureContext) => void;
28
34
  }
35
+ /** Top-level config sections owned by feature modules; all hot reload (5.2+). */
36
+ export declare const FEATURE_CONFIG_KEYS: readonly ["regions", "edgeFleet"];
37
+ /** Copy the feature-owned config sections of `next` (for hot reload). */
38
+ export declare function featureSections(next: GatewayConfig): Partial<GatewayConfig>;
29
39
  /** Register a feature module (idempotent by id; later registrations replace earlier ones). */
30
40
  export declare function registerFeature(f: FeatureModule): void;
31
41
  export declare function listFeatures(): ReadonlyArray<Pick<FeatureModule, 'id' | 'since' | 'summary'>>;
@@ -1 +1 @@
1
- {"version":3,"file":"features.d.ts","sourceRoot":"","sources":["../../src/gateway/features.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,OAAO,EAAE,EAAE,KAAK,OAAO,EAAE,KAAK,cAAc,EAAE,KAAK,MAAM,EAAE,MAAM,SAAS,CAAC;AAClF,OAAO,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAG/F,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,aAAa,CAAC;IAC5B,KAAK,EAAE,MAAM,QAAQ,EAAE,CAAC;IACxB,mFAAmF;IACnF,MAAM,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,QAAQ,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,aAAa,CAAC,CAAC;IACrH,iDAAiD;IACjD,MAAM,EAAE,CAAC,KAAK,CAAC,EAAE,MAAM,KAAK,aAAa,EAAE,CAAC;IAC5C,gEAAgE;IAChE,OAAO,EAAE,MAAM,MAAM,GAAG,SAAS,CAAC;CACnC;AAED,MAAM,WAAW,aAAa;IAC5B,yCAAyC;IACzC,EAAE,EAAE,MAAM,CAAC;IACX,kCAAkC;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,cAAc,KAAK,IAAI,CAAC;CACtD;AAID,8FAA8F;AAC9F,wBAAgB,eAAe,CAAC,CAAC,EAAE,aAAa,GAAG,IAAI,CAItD;AAED,wBAAgB,YAAY,IAAI,aAAa,CAAC,IAAI,CAAC,aAAa,EAAE,IAAI,GAAG,OAAO,GAAG,SAAS,CAAC,CAAC,CAE7F;AAED,MAAM,WAAW,iBAAiB;IAChC,YAAY,EAAE,cAAc,CAAC;IAC7B,UAAU,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC;IACtC,OAAO,EAAE,cAAc,CAAC;IACxB,QAAQ,CAAC,EAAE,aAAa,EAAE,CAAC;CAC5B;AAED,wFAAwF;AACxF,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAc3E;AAED,8EAA8E;AAC9E,wBAAgB,UAAU,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,CAAC,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAOnG;AAED,eAAO,MAAM,UAAU,GAAI,KAAK,OAAO,CAAC,QAAQ,EAAE,SAAS,MAAM,cAAiE,CAAC"}
1
+ {"version":3,"file":"features.d.ts","sourceRoot":"","sources":["../../src/gateway/features.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,OAAO,EAAE,EAAE,KAAK,OAAO,EAAE,KAAK,cAAc,EAAE,KAAK,MAAM,EAAE,MAAM,SAAS,CAAC;AAClF,OAAO,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAG/F,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,aAAa,CAAC;IAC5B,KAAK,EAAE,MAAM,QAAQ,EAAE,CAAC;IACxB,mFAAmF;IACnF,MAAM,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,QAAQ,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,aAAa,CAAC,CAAC;IACrH,iDAAiD;IACjD,MAAM,EAAE,CAAC,KAAK,CAAC,EAAE,MAAM,KAAK,aAAa,EAAE,CAAC;IAC5C,gEAAgE;IAChE,OAAO,EAAE,MAAM,MAAM,GAAG,SAAS,CAAC;IAClC,kEAAkE;IAClE,aAAa,CAAC,EAAE,MAAM,MAAM,EAAE,CAAC;IAC/B,4EAA4E;IAC5E,MAAM,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC;IAClD,0DAA0D;IAC1D,SAAS,CAAC,EAAE,MAAM,OAAO,mBAAmB,EAAE,QAAQ,EAAE,CAAC;CAC1D;AAED,MAAM,WAAW,aAAa;IAC5B,yCAAyC;IACzC,EAAE,EAAE,MAAM,CAAC;IACX,kCAAkC;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,cAAc,KAAK,IAAI,CAAC;CACtD;AAID,iFAAiF;AACjF,eAAO,MAAM,mBAAmB,mCAAiF,CAAC;AAElH,yEAAyE;AACzE,wBAAgB,eAAe,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,CAE3E;AAED,8FAA8F;AAC9F,wBAAgB,eAAe,CAAC,CAAC,EAAE,aAAa,GAAG,IAAI,CAItD;AAED,wBAAgB,YAAY,IAAI,aAAa,CAAC,IAAI,CAAC,aAAa,EAAE,IAAI,GAAG,OAAO,GAAG,SAAS,CAAC,CAAC,CAE7F;AAED,MAAM,WAAW,iBAAiB;IAChC,YAAY,EAAE,cAAc,CAAC;IAC7B,UAAU,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC;IACtC,OAAO,EAAE,cAAc,CAAC;IACxB,QAAQ,CAAC,EAAE,aAAa,EAAE,CAAC;CAC5B;AAED,wFAAwF;AACxF,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAc3E;AAED,8EAA8E;AAC9E,wBAAgB,UAAU,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,CAAC,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAOnG;AAED,eAAO,MAAM,UAAU,GAAI,KAAK,OAAO,CAAC,QAAQ,EAAE,SAAS,MAAM,cAAiE,CAAC"}
@@ -9,6 +9,12 @@
9
9
  import express from 'express';
10
10
  import { VERSION } from '../utils/version.js';
11
11
  const registry = [];
12
+ /** Top-level config sections owned by feature modules; all hot reload (5.2+). */
13
+ export const FEATURE_CONFIG_KEYS = ['regions', 'edgeFleet'];
14
+ /** Copy the feature-owned config sections of `next` (for hot reload). */
15
+ export function featureSections(next) {
16
+ return Object.fromEntries(FEATURE_CONFIG_KEYS.map((k) => [k, next[k]]));
17
+ }
12
18
  /** Register a feature module (idempotent by id; later registrations replace earlier ones). */
13
19
  export function registerFeature(f) {
14
20
  const i = registry.findIndex((x) => x.id === f.id);