lopata 0.10.0 → 0.10.2
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 +37 -0
- package/dist/dashboard/{chunk-hnsny9g7.js → chunk-jpd3vqkt.js} +17 -1
- package/dist/dashboard/index.html +1 -1
- package/package.json +1 -1
- package/src/api/handlers/routes.ts +1 -0
- package/src/cli/dev.ts +19 -29
- package/src/route-matcher.ts +26 -7
- package/src/vite-plugin/dev-server-plugin.ts +21 -31
package/README.md
CHANGED
|
@@ -198,6 +198,43 @@ export default {
|
|
|
198
198
|
|
|
199
199
|
Workers can call each other via service bindings configured in their respective `wrangler.jsonc`. Both HTTP (`binding.fetch()`) and RPC (`binding.myMethod()`) modes are supported, including promise pipelining.
|
|
200
200
|
|
|
201
|
+
### Route patterns
|
|
202
|
+
|
|
203
|
+
In multi-worker setups, auxiliary workers can use Cloudflare-style `routes` in their `wrangler.jsonc` to handle specific URL patterns. The main worker acts as a fallback for unmatched requests.
|
|
204
|
+
|
|
205
|
+
```jsonc
|
|
206
|
+
// workers/api/wrangler.jsonc
|
|
207
|
+
{
|
|
208
|
+
"name": "api-worker",
|
|
209
|
+
"main": "src/index.ts",
|
|
210
|
+
"routes": [
|
|
211
|
+
"example.com/api/*",
|
|
212
|
+
{ "pattern": "example.com/webhooks/*" }
|
|
213
|
+
]
|
|
214
|
+
}
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
Routes support trailing `*` wildcards (`/api/*` matches `/api/foo` and `/api/foo/bar`). When multiple routes match, the most specific one wins (more path segments > fewer, exact match > wildcard). The domain portion is stripped — only the path is matched locally.
|
|
218
|
+
|
|
219
|
+
### Host-based routing
|
|
220
|
+
|
|
221
|
+
For routing by hostname (e.g. subdomains), use `hosts` in `lopata.config.ts`:
|
|
222
|
+
|
|
223
|
+
```ts
|
|
224
|
+
export default {
|
|
225
|
+
main: './wrangler.jsonc',
|
|
226
|
+
workers: [
|
|
227
|
+
{
|
|
228
|
+
name: 'api-worker',
|
|
229
|
+
config: './workers/api/wrangler.jsonc',
|
|
230
|
+
hosts: ['api.localhost', '*.api.localhost'],
|
|
231
|
+
},
|
|
232
|
+
],
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
Wildcard patterns like `*.localhost` match any subdomain. Requests matching a host pattern are routed to that worker regardless of path-based routes.
|
|
237
|
+
|
|
201
238
|
## Vite plugin
|
|
202
239
|
|
|
203
240
|
The Vite plugin is a drop-in replacement for `@cloudflare/vite-plugin`. It provides:
|
|
@@ -1168,8 +1168,24 @@ function CopyMarkdownButton({ getMarkdown, title }) {
|
|
|
1168
1168
|
children: copied ? "Copied!" : "Copy MD"
|
|
1169
1169
|
}, undefined, false, undefined, this);
|
|
1170
1170
|
}
|
|
1171
|
+
function extractText(v3) {
|
|
1172
|
+
if (v3 == null || v3 === false || v3 === true)
|
|
1173
|
+
return "";
|
|
1174
|
+
if (typeof v3 === "string" || typeof v3 === "number")
|
|
1175
|
+
return String(v3);
|
|
1176
|
+
if (Array.isArray(v3))
|
|
1177
|
+
return v3.map(extractText).join("");
|
|
1178
|
+
if (typeof v3 === "object" && "props" in v3) {
|
|
1179
|
+
const vnode = v3;
|
|
1180
|
+
if (typeof vnode.type === "function") {
|
|
1181
|
+
return extractText(vnode.type(vnode.props));
|
|
1182
|
+
}
|
|
1183
|
+
return extractText(vnode.props.children);
|
|
1184
|
+
}
|
|
1185
|
+
return String(v3);
|
|
1186
|
+
}
|
|
1171
1187
|
function tableToMarkdown(headers, rows) {
|
|
1172
|
-
const escape = (v3) =>
|
|
1188
|
+
const escape = (v3) => extractText(v3).replace(/\|/g, "\\|").replace(/\n/g, " ");
|
|
1173
1189
|
const headerRow = `| ${headers.map(escape).join(" | ")} |`;
|
|
1174
1190
|
const separator = `| ${headers.map(() => "---").join(" | ")} |`;
|
|
1175
1191
|
const dataRows = rows.map((row) => `| ${row.map(escape).join(" | ")} |`);
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
9
9
|
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet" />
|
|
10
10
|
|
|
11
|
-
<link rel="stylesheet" crossorigin href="/__dashboard/assets/chunk-jzyhpjad.css"><script type="module" crossorigin src="/__dashboard/assets/chunk-
|
|
11
|
+
<link rel="stylesheet" crossorigin href="/__dashboard/assets/chunk-jzyhpjad.css"><script type="module" crossorigin src="/__dashboard/assets/chunk-jpd3vqkt.js"></script></head>
|
|
12
12
|
<body class="h-full bg-surface text-ink" style="font-family: system-ui, -apple-system, sans-serif;">
|
|
13
13
|
<script>
|
|
14
14
|
// Apply saved theme before first paint to prevent flash
|
package/package.json
CHANGED
|
@@ -12,6 +12,7 @@ export const handlers = {
|
|
|
12
12
|
|
|
13
13
|
if (ctx.routeDispatcher) {
|
|
14
14
|
for (const r of ctx.routeDispatcher.getRegisteredRoutes()) {
|
|
15
|
+
if (r.hostPatterns) continue // already shown as host routes
|
|
15
16
|
routes.push({ pattern: r.pattern, workerName: r.workerName, isFallback: false })
|
|
16
17
|
}
|
|
17
18
|
}
|
package/src/cli/dev.ts
CHANGED
|
@@ -22,7 +22,7 @@ import { FileWatcher } from '../file-watcher'
|
|
|
22
22
|
import { GenerationManager } from '../generation-manager'
|
|
23
23
|
import { loadLopataConfig } from '../lopata-config'
|
|
24
24
|
import { addCfProperty } from '../request-cf'
|
|
25
|
-
import {
|
|
25
|
+
import { extractHostname, RouteDispatcher } from '../route-matcher'
|
|
26
26
|
import { getTraceStore } from '../tracing/store'
|
|
27
27
|
import type { TraceEvent } from '../tracing/types'
|
|
28
28
|
import { WorkerRegistry } from '../worker-registry'
|
|
@@ -43,7 +43,6 @@ export async function run(ctx: CliContext) {
|
|
|
43
43
|
let manager: GenerationManager
|
|
44
44
|
let routeDispatcher: RouteDispatcher | undefined
|
|
45
45
|
let registry: WorkerRegistry | undefined
|
|
46
|
-
const hostRoutes: Array<{ pattern: string; manager: GenerationManager; workerName: string }> = []
|
|
47
46
|
|
|
48
47
|
if (lopataConfig) {
|
|
49
48
|
// ─── Multi-worker mode ─────────────────────────────────────────
|
|
@@ -114,7 +113,7 @@ export async function run(ctx: CliContext) {
|
|
|
114
113
|
if (routeDispatcher) {
|
|
115
114
|
try {
|
|
116
115
|
const freshConfig = await loadConfig(workerDef.config, envFlag)
|
|
117
|
-
routeDispatcher.addRoutes(freshConfig, auxManager, workerDef.name)
|
|
116
|
+
routeDispatcher.addRoutes(freshConfig, auxManager, workerDef.name, workerDef.hosts)
|
|
118
117
|
} catch (err) {
|
|
119
118
|
console.warn(`[lopata] Failed to re-read config for "${workerDef.name}" routes:`, err)
|
|
120
119
|
}
|
|
@@ -139,32 +138,35 @@ export async function run(ctx: CliContext) {
|
|
|
139
138
|
)
|
|
140
139
|
}
|
|
141
140
|
|
|
142
|
-
// Build route dispatcher
|
|
141
|
+
// Build route dispatcher (aux workers only — main is the fallback)
|
|
143
142
|
routeDispatcher = new RouteDispatcher(mainManager)
|
|
144
143
|
for (const workerDef of lopataConfig.workers ?? []) {
|
|
145
|
-
const auxConfig = auxConfigs.get(workerDef.name)
|
|
146
144
|
const auxMgr = registry.getManager(workerDef.name)
|
|
147
|
-
if (
|
|
145
|
+
if (!auxMgr) continue
|
|
146
|
+
const auxConfig = auxConfigs.get(workerDef.name)
|
|
147
|
+
if (auxConfig) routeDispatcher.addRoutes(auxConfig, auxMgr, workerDef.name, workerDef.hosts)
|
|
148
|
+
// Workers with hosts but no wrangler routes still need a catch-all entry
|
|
149
|
+
if (workerDef.hosts && (!auxConfig?.routes || auxConfig.routes.length === 0)) {
|
|
150
|
+
routeDispatcher.addHostWorker(auxMgr, workerDef.name, workerDef.hosts)
|
|
151
|
+
}
|
|
148
152
|
}
|
|
149
153
|
if (routeDispatcher.hasRoutes()) {
|
|
150
154
|
for (const r of routeDispatcher.getRegisteredRoutes()) {
|
|
151
|
-
|
|
155
|
+
const hostInfo = r.hostPatterns ? ` (hosts: ${r.hostPatterns.join(', ')})` : ''
|
|
156
|
+
console.log(`[lopata] Route: ${r.pattern} → ${r.workerName}${hostInfo}`)
|
|
152
157
|
}
|
|
153
158
|
}
|
|
154
159
|
|
|
155
|
-
//
|
|
160
|
+
// Expose host routes to the dashboard API
|
|
161
|
+
const hostRoutes: Array<{ pattern: string; workerName: string }> = []
|
|
156
162
|
for (const workerDef of lopataConfig.workers ?? []) {
|
|
157
163
|
if (!workerDef.hosts) continue
|
|
158
|
-
const auxMgr = registry.getManager(workerDef.name)
|
|
159
|
-
if (!auxMgr) continue
|
|
160
164
|
for (const host of workerDef.hosts) {
|
|
161
|
-
hostRoutes.push({ pattern: host,
|
|
162
|
-
console.log(`[lopata] Host route: ${host} → ${workerDef.name}`)
|
|
165
|
+
hostRoutes.push({ pattern: host, workerName: workerDef.name })
|
|
163
166
|
}
|
|
164
167
|
}
|
|
165
|
-
|
|
166
168
|
if (hostRoutes.length > 0) {
|
|
167
|
-
setHostRoutes(hostRoutes
|
|
169
|
+
setHostRoutes(hostRoutes)
|
|
168
170
|
}
|
|
169
171
|
|
|
170
172
|
manager = mainManager
|
|
@@ -280,21 +282,9 @@ export async function run(ctx: CliContext) {
|
|
|
280
282
|
return gen.callScheduled(cronExpr)
|
|
281
283
|
}
|
|
282
284
|
|
|
283
|
-
//
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
const hostname = hostHeader.split(':')[0] ?? ''
|
|
287
|
-
for (const hr of hostRoutes) {
|
|
288
|
-
if (matchHost(hostname, hr.pattern)) {
|
|
289
|
-
const gen = hr.manager.active
|
|
290
|
-
if (!gen) return new Response('No active generation', { status: 503 })
|
|
291
|
-
return (await gen.callFetch(request, server)) as Response
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
// Delegate to active generation (route-based dispatch in multi-worker mode)
|
|
297
|
-
const targetManager = routeDispatcher ? routeDispatcher.resolve(url.pathname) : manager
|
|
285
|
+
// Delegate to active generation (host + route dispatch in multi-worker mode)
|
|
286
|
+
const reqHostname = extractHostname(request.headers.get('host') ?? '')
|
|
287
|
+
const targetManager = routeDispatcher ? routeDispatcher.resolve(url.pathname, reqHostname) : manager
|
|
298
288
|
const gen = targetManager.active
|
|
299
289
|
if (!gen) {
|
|
300
290
|
return new Response('No active generation', { status: 503 })
|
package/src/route-matcher.ts
CHANGED
|
@@ -61,6 +61,11 @@ export function matchRoute(pathname: string, pattern: string): boolean {
|
|
|
61
61
|
return pathname === pattern
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
+
/** Extract hostname from a Host header value, stripping the port if present. */
|
|
65
|
+
export function extractHostname(hostHeader: string): string {
|
|
66
|
+
return hostHeader.split(':')[0] ?? ''
|
|
67
|
+
}
|
|
68
|
+
|
|
64
69
|
/** Match a hostname against a host pattern. Supports exact match and `*.domain` wildcards. */
|
|
65
70
|
export function matchHost(hostname: string, pattern: string): boolean {
|
|
66
71
|
if (pattern === hostname) return true
|
|
@@ -83,6 +88,8 @@ interface RouteEntry {
|
|
|
83
88
|
pattern: string
|
|
84
89
|
workerName: string
|
|
85
90
|
manager: RoutableManager
|
|
91
|
+
/** When set, this route only matches requests whose hostname matches one of these patterns. */
|
|
92
|
+
hostPatterns?: string[]
|
|
86
93
|
}
|
|
87
94
|
|
|
88
95
|
/**
|
|
@@ -101,7 +108,7 @@ export class RouteDispatcher {
|
|
|
101
108
|
this.fallback = fallback
|
|
102
109
|
}
|
|
103
110
|
|
|
104
|
-
addRoutes(config: WranglerConfig, manager: RoutableManager, workerName: string): void {
|
|
111
|
+
addRoutes(config: WranglerConfig, manager: RoutableManager, workerName: string, hostPatterns?: string[]): void {
|
|
105
112
|
if (!config.routes) return
|
|
106
113
|
|
|
107
114
|
// Clear existing routes for this worker to support re-registration (e.g. config reload)
|
|
@@ -127,8 +134,8 @@ export class RouteDispatcher {
|
|
|
127
134
|
console.warn(`[lopata] Warning: route pattern "${pattern}" has a wildcard not at the end — Cloudflare only supports trailing wildcards`)
|
|
128
135
|
}
|
|
129
136
|
|
|
130
|
-
// Skip duplicate patterns from different workers (first registered wins)
|
|
131
|
-
const existing = this.routes.find(r => r.pattern === pattern)
|
|
137
|
+
// Skip duplicate patterns from different workers when they share the same host scope (first registered wins)
|
|
138
|
+
const existing = this.routes.find(r => r.pattern === pattern && !r.hostPatterns && !hostPatterns)
|
|
132
139
|
if (existing) {
|
|
133
140
|
console.warn(
|
|
134
141
|
`[lopata] Warning: route pattern "${pattern}" is already registered by "${existing.workerName}" — skipping duplicate from "${workerName}"`,
|
|
@@ -136,11 +143,19 @@ export class RouteDispatcher {
|
|
|
136
143
|
continue
|
|
137
144
|
}
|
|
138
145
|
|
|
139
|
-
this.routes.push({ pattern, workerName, manager })
|
|
146
|
+
this.routes.push({ pattern, workerName, manager, hostPatterns })
|
|
140
147
|
this.sorted = false
|
|
141
148
|
}
|
|
142
149
|
}
|
|
143
150
|
|
|
151
|
+
/** Register a worker that handles all paths for the given host patterns (no wrangler routes needed). */
|
|
152
|
+
addHostWorker(manager: RoutableManager, workerName: string, hostPatterns: string[]): void {
|
|
153
|
+
// Clear existing routes for this worker to support re-registration
|
|
154
|
+
this.routes = this.routes.filter(r => r.workerName !== workerName)
|
|
155
|
+
this.routes.push({ pattern: '/*', workerName, manager, hostPatterns })
|
|
156
|
+
this.sorted = false
|
|
157
|
+
}
|
|
158
|
+
|
|
144
159
|
removeWorkerRoutes(workerName: string): void {
|
|
145
160
|
this.routes = this.routes.filter(r => r.workerName !== workerName)
|
|
146
161
|
}
|
|
@@ -166,9 +181,13 @@ export class RouteDispatcher {
|
|
|
166
181
|
this.sorted = true
|
|
167
182
|
}
|
|
168
183
|
|
|
169
|
-
resolve(pathname: string): RoutableManager {
|
|
184
|
+
resolve(pathname: string, hostname?: string): RoutableManager {
|
|
170
185
|
this.ensureSorted()
|
|
171
186
|
for (const entry of this.routes) {
|
|
187
|
+
// If route has host constraints, skip unless hostname matches one of them
|
|
188
|
+
if (entry.hostPatterns) {
|
|
189
|
+
if (hostname === undefined || !entry.hostPatterns.some(hp => matchHost(hostname, hp))) continue
|
|
190
|
+
}
|
|
172
191
|
if (matchRoute(pathname, entry.pattern)) {
|
|
173
192
|
return entry.manager
|
|
174
193
|
}
|
|
@@ -185,8 +204,8 @@ export class RouteDispatcher {
|
|
|
185
204
|
return this.routes.length > 0
|
|
186
205
|
}
|
|
187
206
|
|
|
188
|
-
getRegisteredRoutes(): Array<{ pattern: string; workerName: string }> {
|
|
207
|
+
getRegisteredRoutes(): Array<{ pattern: string; workerName: string; hostPatterns?: string[] }> {
|
|
189
208
|
this.ensureSorted()
|
|
190
|
-
return this.routes.map(r => ({ pattern: r.pattern, workerName: r.workerName }))
|
|
209
|
+
return this.routes.map(r => ({ pattern: r.pattern, workerName: r.workerName, hostPatterns: r.hostPatterns }))
|
|
191
210
|
}
|
|
192
211
|
}
|
|
@@ -3,7 +3,7 @@ import { dirname, resolve } from 'node:path'
|
|
|
3
3
|
import type { Plugin, ViteDevServer } from 'vite'
|
|
4
4
|
import { FileWatcher } from '../file-watcher.ts'
|
|
5
5
|
import type { RoutableManager } from '../route-matcher.ts'
|
|
6
|
-
import {
|
|
6
|
+
import { extractHostname, RouteDispatcher } from '../route-matcher.ts'
|
|
7
7
|
|
|
8
8
|
interface DevServerPluginOptions {
|
|
9
9
|
configPath?: string
|
|
@@ -53,8 +53,6 @@ export function devServerPlugin(options: DevServerPluginOptions): Plugin {
|
|
|
53
53
|
|
|
54
54
|
// Route dispatcher for multi-worker route-based dispatching
|
|
55
55
|
let routeDispatcher: RouteDispatcher | undefined
|
|
56
|
-
// Host-based routing: map host patterns to managers
|
|
57
|
-
const hostRoutes: Array<{ pattern: string; manager: RoutableManager; workerName: string }> = []
|
|
58
56
|
|
|
59
57
|
// Track current module to detect when Vite HMR invalidates it
|
|
60
58
|
let currentModule: Record<string, unknown> | null = null
|
|
@@ -208,27 +206,15 @@ export function devServerPlugin(options: DevServerPluginOptions): Plugin {
|
|
|
208
206
|
|
|
209
207
|
/**
|
|
210
208
|
* Resolve an auxiliary worker for the given request.
|
|
211
|
-
* Checks host-based routes first, then path-based route dispatcher.
|
|
212
209
|
* Returns null if the request should be handled by the main worker.
|
|
213
210
|
*/
|
|
214
211
|
function resolveAuxWorker(req: IncomingMessage, url: string): { manager: RoutableManager; workerName: string } | null {
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
return hr
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
// Path-based dispatch
|
|
226
|
-
if (routeDispatcher) {
|
|
227
|
-
const parsedUrl = new URL(url, 'http://localhost')
|
|
228
|
-
const targetManager = routeDispatcher.resolve(parsedUrl.pathname)
|
|
229
|
-
if (!routeDispatcher.isFallback(targetManager)) {
|
|
230
|
-
return { manager: targetManager, workerName: (targetManager as any).config?.name ?? 'aux' }
|
|
231
|
-
}
|
|
212
|
+
if (!routeDispatcher) return null
|
|
213
|
+
const parsedUrl = new URL(url, 'http://localhost')
|
|
214
|
+
const hostname = extractHostname(req.headers.host ?? '')
|
|
215
|
+
const targetManager = routeDispatcher.resolve(parsedUrl.pathname, hostname)
|
|
216
|
+
if (!routeDispatcher.isFallback(targetManager)) {
|
|
217
|
+
return { manager: targetManager, workerName: (targetManager as any).config?.name ?? 'aux' }
|
|
232
218
|
}
|
|
233
219
|
return null
|
|
234
220
|
}
|
|
@@ -415,7 +401,7 @@ export function devServerPlugin(options: DevServerPluginOptions): Plugin {
|
|
|
415
401
|
if (routeDispatcher) {
|
|
416
402
|
try {
|
|
417
403
|
const freshConfig = await configMod.loadConfig(auxConfigPath)
|
|
418
|
-
routeDispatcher.addRoutes(freshConfig, auxManager, workerName)
|
|
404
|
+
routeDispatcher.addRoutes(freshConfig, auxManager, workerName, workerDef.hosts)
|
|
419
405
|
} catch (err) {
|
|
420
406
|
console.warn(`[lopata:vite] Failed to re-read config for "${workerName}" routes:`, err)
|
|
421
407
|
}
|
|
@@ -437,35 +423,39 @@ export function devServerPlugin(options: DevServerPluginOptions): Plugin {
|
|
|
437
423
|
)
|
|
438
424
|
}
|
|
439
425
|
|
|
440
|
-
// Build route dispatcher
|
|
426
|
+
// Build route dispatcher (aux workers only — main is the fallback)
|
|
441
427
|
routeDispatcher = new RouteDispatcher(mainAdapter)
|
|
442
428
|
for (const workerDef of options.auxiliaryWorkers) {
|
|
443
429
|
const cached = auxConfigs.get(workerDef.configPath)
|
|
444
430
|
if (!cached) continue
|
|
445
431
|
const auxMgr = workerRegistry.getManager(cached.name)
|
|
446
|
-
if (auxMgr)
|
|
432
|
+
if (!auxMgr) continue
|
|
433
|
+
routeDispatcher.addRoutes(cached.config, auxMgr, cached.name, workerDef.hosts)
|
|
434
|
+
// Workers with hosts but no wrangler routes still need a catch-all entry
|
|
435
|
+
if (workerDef.hosts && (!cached.config.routes || cached.config.routes.length === 0)) {
|
|
436
|
+
routeDispatcher.addHostWorker(auxMgr, cached.name, workerDef.hosts)
|
|
437
|
+
}
|
|
447
438
|
}
|
|
448
439
|
if (routeDispatcher.hasRoutes()) {
|
|
449
440
|
for (const r of routeDispatcher.getRegisteredRoutes()) {
|
|
450
|
-
|
|
441
|
+
const hostInfo = r.hostPatterns ? ` (hosts: ${r.hostPatterns.join(', ')})` : ''
|
|
442
|
+
console.log(`[lopata:vite] Route: ${r.pattern} → ${r.workerName}${hostInfo}`)
|
|
451
443
|
}
|
|
452
444
|
}
|
|
453
445
|
apiMod.setRouteDispatcher(routeDispatcher)
|
|
454
446
|
|
|
455
|
-
//
|
|
447
|
+
// Expose host routes to the dashboard API
|
|
448
|
+
const hostRoutes: Array<{ pattern: string; workerName: string }> = []
|
|
456
449
|
for (const workerDef of options.auxiliaryWorkers) {
|
|
457
450
|
if (!workerDef.hosts) continue
|
|
458
451
|
const cached = auxConfigs.get(workerDef.configPath)
|
|
459
452
|
if (!cached) continue
|
|
460
|
-
const auxMgr = workerRegistry.getManager(cached.name)
|
|
461
|
-
if (!auxMgr) continue
|
|
462
453
|
for (const host of workerDef.hosts) {
|
|
463
|
-
hostRoutes.push({ pattern: host,
|
|
464
|
-
console.log(`[lopata:vite] Host route: ${host} → ${cached.name}`)
|
|
454
|
+
hostRoutes.push({ pattern: host, workerName: cached.name })
|
|
465
455
|
}
|
|
466
456
|
}
|
|
467
457
|
if (hostRoutes.length > 0) {
|
|
468
|
-
apiMod.setHostRoutes(hostRoutes
|
|
458
|
+
apiMod.setHostRoutes(hostRoutes)
|
|
469
459
|
}
|
|
470
460
|
}
|
|
471
461
|
|