lopata 0.11.0 → 0.13.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lopata",
3
- "version": "0.11.0",
3
+ "version": "0.13.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -21,6 +21,15 @@ export function hasFlag(args: string[], name: string): boolean {
21
21
  return args.includes(name)
22
22
  }
23
23
 
24
+ /** Exit with an error if --remote is passed, suggesting the equivalent wrangler command. */
25
+ export function rejectRemoteFlag(args: string[]): void {
26
+ if (args.includes('--remote')) {
27
+ const wranglerCmd = `wrangler ${args.join(' ')}`
28
+ console.error(`Error: --remote is not supported by lopata. Lopata is a local-only runtime.\nDid you mean: ${wranglerCmd}`)
29
+ process.exit(1)
30
+ }
31
+ }
32
+
24
33
  /** Get positional args (everything that's not a flag or flag value). */
25
34
  export function positionalArgs(args: string[], flags: string[]): string[] {
26
35
  const result: string[] = []
package/src/cli/dev.ts CHANGED
@@ -140,6 +140,12 @@ export async function run(ctx: CliContext) {
140
140
 
141
141
  // Build route dispatcher (aux workers only — main is the fallback)
142
142
  routeDispatcher = new RouteDispatcher(mainManager)
143
+
144
+ // Register main worker host patterns so they take priority over wildcard aux hosts
145
+ if (lopataConfig.hosts?.length) {
146
+ routeDispatcher.addHostWorker(mainManager, mainConfig.name, lopataConfig.hosts)
147
+ }
148
+
143
149
  for (const workerDef of lopataConfig.workers ?? []) {
144
150
  const auxMgr = registry.getManager(workerDef.name)
145
151
  if (!auxMgr) continue
@@ -159,6 +165,11 @@ export async function run(ctx: CliContext) {
159
165
 
160
166
  // Expose host routes to the dashboard API
161
167
  const hostRoutes: Array<{ pattern: string; workerName: string }> = []
168
+ if (lopataConfig.hosts?.length) {
169
+ for (const host of lopataConfig.hosts) {
170
+ hostRoutes.push({ pattern: host, workerName: mainConfig.name })
171
+ }
172
+ }
162
173
  for (const workerDef of lopataConfig.workers ?? []) {
163
174
  if (!workerDef.hosts) continue
164
175
  for (const host of workerDef.hosts) {
package/src/cli.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env bun
2
2
 
3
- import { createContext, hasFlag } from './cli/context'
3
+ import { createContext, hasFlag, rejectRemoteFlag } from './cli/context'
4
4
 
5
5
  const ctx = createContext(process.argv)
6
6
  const args = ctx.args
@@ -19,6 +19,8 @@ for (let i = 0; i < args.length; i++) {
19
19
  const command = commandArgs[0]
20
20
  const subcommand = commandArgs[1]
21
21
 
22
+ rejectRemoteFlag(args)
23
+
22
24
  if (!command || hasFlag(args, '--help') || hasFlag(args, '-h')) {
23
25
  printHelp()
24
26
  process.exit(0)
package/src/d1-migrate.ts CHANGED
@@ -9,17 +9,15 @@
9
9
  */
10
10
 
11
11
  import { join, resolve } from 'node:path'
12
+ import { parseFlag, rejectRemoteFlag } from './cli/context'
12
13
  import { applyMigrations } from './cli/d1'
13
14
  import { autoLoadConfig, loadConfig } from './config'
14
15
 
15
- // Parse CLI flags
16
- function parseFlag(name: string): string | undefined {
17
- const idx = process.argv.indexOf(name)
18
- return idx !== -1 ? process.argv[idx + 1] : undefined
19
- }
16
+ const args = process.argv.slice(2)
17
+ rejectRemoteFlag(args)
20
18
 
21
- const configPath = parseFlag('--config') ?? parseFlag('-c')
22
- const envName = parseFlag('--env') ?? parseFlag('-e')
19
+ const configPath = parseFlag(args, '--config') ?? parseFlag(args, '-c')
20
+ const envName = parseFlag(args, '--env') ?? parseFlag(args, '-e')
23
21
  const baseDir = process.cwd()
24
22
 
25
23
  const config = configPath
@@ -4,6 +4,8 @@ import { dirname, join, resolve } from 'node:path'
4
4
  export interface LopataConfig {
5
5
  /** Path to the main worker's wrangler config (HTTP entrypoint) */
6
6
  main: string
7
+ /** Host patterns that route to the main worker (takes priority over wildcard auxiliary hosts). */
8
+ hosts?: string[]
7
9
  /** Auxiliary workers, each with a service name and wrangler config path */
8
10
  workers?: Array<{
9
11
  name: string
@@ -77,6 +77,12 @@ export function matchHost(hostname: string, pattern: string): boolean {
77
77
  return false
78
78
  }
79
79
 
80
+ /** Check if host patterns contain any wildcard entries (e.g. `*.localhost`). */
81
+ function hasWildcardHost(patterns?: string[]): boolean {
82
+ if (!patterns) return false
83
+ return patterns.some(p => p.includes('*'))
84
+ }
85
+
80
86
  /** Count the number of path segments in a pattern (ignoring trailing wildcard). */
81
87
  function segmentCount(pattern: string): number {
82
88
  const clean = pattern.replace(/\/?\*$/, '')
@@ -90,6 +96,8 @@ interface RouteEntry {
90
96
  manager: RoutableManager
91
97
  /** When set, this route only matches requests whose hostname matches one of these patterns. */
92
98
  hostPatterns?: string[]
99
+ /** Pre-computed: true if any hostPattern contains a wildcard. Used for sort ordering. */
100
+ hasWildcardHost: boolean
93
101
  }
94
102
 
95
103
  /**
@@ -143,7 +151,7 @@ export class RouteDispatcher {
143
151
  continue
144
152
  }
145
153
 
146
- this.routes.push({ pattern, workerName, manager, hostPatterns })
154
+ this.routes.push({ pattern, workerName, manager, hostPatterns, hasWildcardHost: hasWildcardHost(hostPatterns) })
147
155
  this.sorted = false
148
156
  }
149
157
  }
@@ -152,7 +160,7 @@ export class RouteDispatcher {
152
160
  addHostWorker(manager: RoutableManager, workerName: string, hostPatterns: string[]): void {
153
161
  // Clear existing routes for this worker to support re-registration
154
162
  this.routes = this.routes.filter(r => r.workerName !== workerName)
155
- this.routes.push({ pattern: '/*', workerName, manager, hostPatterns })
163
+ this.routes.push({ pattern: '/*', workerName, manager, hostPatterns, hasWildcardHost: hasWildcardHost(hostPatterns) })
156
164
  this.sorted = false
157
165
  }
158
166
 
@@ -175,6 +183,8 @@ export class RouteDispatcher {
175
183
  const aSlashStar = a.pattern.endsWith('/*')
176
184
  const bSlashStar = b.pattern.endsWith('/*')
177
185
  if (aSlashStar !== bSlashStar) return aSlashStar ? -1 : 1
186
+ // Exact host patterns beat wildcard host patterns (e.g. admin.localhost before *.localhost)
187
+ if (a.hasWildcardHost !== b.hasWildcardHost) return a.hasWildcardHost ? 1 : -1
178
188
  // Longer pattern string as tiebreaker
179
189
  return b.pattern.length - a.pattern.length
180
190
  })
@@ -8,6 +8,7 @@ import { extractHostname, RouteDispatcher } from '../route-matcher.ts'
8
8
  interface DevServerPluginOptions {
9
9
  configPath?: string
10
10
  envName: string
11
+ hosts?: string[]
11
12
  auxiliaryWorkers?: { configPath: string; name?: string; hosts?: string[] }[]
12
13
  }
13
14
 
@@ -425,6 +426,12 @@ export function devServerPlugin(options: DevServerPluginOptions): Plugin {
425
426
 
426
427
  // Build route dispatcher (aux workers only — main is the fallback)
427
428
  routeDispatcher = new RouteDispatcher(mainAdapter)
429
+
430
+ // Register main worker host patterns so they take priority over wildcard aux hosts
431
+ if (options.hosts?.length) {
432
+ routeDispatcher.addHostWorker(mainAdapter, config.name, options.hosts)
433
+ }
434
+
428
435
  for (const workerDef of options.auxiliaryWorkers) {
429
436
  const cached = auxConfigs.get(workerDef.configPath)
430
437
  if (!cached) continue
@@ -446,6 +453,11 @@ export function devServerPlugin(options: DevServerPluginOptions): Plugin {
446
453
 
447
454
  // Expose host routes to the dashboard API
448
455
  const hostRoutes: Array<{ pattern: string; workerName: string }> = []
456
+ if (options.hosts?.length) {
457
+ for (const host of options.hosts) {
458
+ hostRoutes.push({ pattern: host, workerName: config.name })
459
+ }
460
+ }
449
461
  for (const workerDef of options.auxiliaryWorkers) {
450
462
  if (!workerDef.hosts) continue
451
463
  const cached = auxConfigs.get(workerDef.configPath)
@@ -10,6 +10,8 @@ export interface LopataPluginConfig {
10
10
  configPath?: string
11
11
  /** Vite environment name for SSR. Default: "ssr" */
12
12
  viteEnvironment?: { name?: string }
13
+ /** Host patterns that route to the main worker (takes priority over wildcard auxiliary hosts). */
14
+ hosts?: string[]
13
15
  /** Auxiliary workers loaded via native Bun import (not through Vite). */
14
16
  auxiliaryWorkers?: { configPath: string; name?: string; hosts?: string[] }[]
15
17
  }
@@ -36,6 +38,7 @@ export function lopata(config?: LopataPluginConfig): Plugin[] {
36
38
  devServerPlugin({
37
39
  configPath: config?.configPath,
38
40
  envName,
41
+ hosts: config?.hosts,
39
42
  auxiliaryWorkers: config?.auxiliaryWorkers,
40
43
  }),
41
44
  reactRouterPlugin(),