Files
OmniRoute/open-sse/config/dynamicImageModelSources.ts
Diego Rodrigues de Sa e Souza 22b89a273b fix(config): keep the SQLite driver out of the client bundle (#10692) (#10695)
* fix(sse): import localDb through its real .ts extension (#10674)

`open-sse/services/combo.ts` imported "../../src/lib/localDb.js" — a .js suffix
on a module that only exists as .ts. Turbopack resolved it by accident until the
dependency-tree change in #10647; after that the instrumentation hook died at boot
with MODULE_NOT_FOUND, breaking `npm run dev` and the production build (60
consecutive red `Build App` runs on release/v3.8.50).

Fixes the same latent pattern in src/lib/usage/usageLedger.ts, which survived only
because it is an `import type` and is erased before resolution.

Adds a guard rejecting relative .js specifiers across open-sse/ and src/. Package
specifiers are untouched: publishing ESM as .js is legitimate there (e.g.
@modelcontextprotocol/sdk), and only first-party relative imports are first-party
TypeScript.

Closes #10674

* fix(config): keep the SQLite driver out of the client bundle (#10692)

The `aihorde` entry in IMAGE_PROVIDERS imported its live-catalog service directly.
IMAGE_PROVIDERS is reachable from "use client" dashboard pages — they read its KEYS
to derive which providers support which media kind — so that import dragged
aihordeImageCatalog → safeOutboundFetch → proxyFetch → featureFlags → db/core →
sqljsAdapter into the browser graph. The build then tried to bundle fs/net/tls for
the browser and failed with 28 Module not found errors, leaving `Build App` red for
60 consecutive runs and no artifact buildable from the branch.

A dynamic import() does not fix this: the bundler still has to make the module
browser-loadable. The dependency is inverted instead — the registry entry knows only
a pure registration module, and the server-only service registers itself on import,
which every server path needing live models already does. With nothing registered the
getter yields [], exactly what the live catalog returned before its first poll.

Validated by a full `npm run build:release`: 0 Module not found, artifact produced.

Closes #10692

---------

Co-authored-by: Xiangzhe <bakryun0718@proton.me>
2026-08-18 19:23:46 -03:00

50 lines
2.0 KiB
TypeScript

/**
* Registry indirection for image providers whose model list is discovered at runtime (#10692).
*
* `IMAGE_PROVIDERS` is reachable from `"use client"` dashboard pages — they read its KEYS to
* decide which providers support which media kind (see `mediaServiceKinds.ts`). A provider entry
* that imports its live-catalog service directly therefore drags that service, and everything it
* imports, into the browser graph. For AI Horde that meant
* `aihordeImageCatalog → safeOutboundFetch → proxyFetch → featureFlags → db/core → sqljsAdapter`,
* so the build tried to bundle `fs`/`net`/`tls` for the browser and failed.
*
* A dynamic `import()` does not help: the bundler still has to make the module browser-loadable.
* The dependency has to be inverted instead — the registry entry knows only this pure module, and
* the server-only service registers itself when it is imported (which every server path that needs
* live models already does).
*
* With no source registered the getter yields `[]`, which is exactly what the live catalog
* returned before it had polled — so the client keeps seeing the provider without its models,
* unchanged.
*/
export interface DynamicImageModelEntry {
id: string;
name: string;
inputModalities: string[];
}
type DynamicImageModelSource = () => DynamicImageModelEntry[];
const sources = new Map<string, DynamicImageModelSource>();
/** Called by a server-only catalog service at import time. Last registration wins. */
export function registerDynamicImageModelSource(
providerId: string,
source: DynamicImageModelSource
): void {
sources.set(providerId, source);
}
/** Models discovered for `providerId`, or `[]` when no server-side source is loaded. */
export function getDynamicImageModels(providerId: string): DynamicImageModelEntry[] {
const source = sources.get(providerId);
if (!source) return [];
return source();
}
/** Test seam — drops every registration. */
export function resetDynamicImageModelSources(): void {
sources.clear();
}