@routerxjs/core 0.0.0-dev-20260318021459

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,91 @@
1
+ // src/router/Router.ts
2
+ function modelName(entry) {
3
+ return typeof entry === "string" ? entry : entry.name;
4
+ }
5
+ function upstreamModelName(entry) {
6
+ return typeof entry === "string" ? entry : entry.upstreamModel;
7
+ }
8
+ var Router = class {
9
+ providers;
10
+ defaultProviderId;
11
+ constructor(config) {
12
+ this.providers = config.providers;
13
+ this.defaultProviderId = config.defaultProviderId;
14
+ }
15
+ /**
16
+ * Find the best provider for a given model
17
+ */
18
+ route(model) {
19
+ const candidates = [];
20
+ for (const provider of this.providers) {
21
+ if (provider.enabled === false) continue;
22
+ const entry = provider.models.find((m) => modelName(m) === model);
23
+ if (entry) {
24
+ candidates.push({ provider, entry });
25
+ }
26
+ }
27
+ candidates.sort((a, b) => (a.provider.priority ?? 100) - (b.provider.priority ?? 100));
28
+ if (candidates.length > 0) {
29
+ const { provider, entry } = candidates[0];
30
+ return {
31
+ provider,
32
+ model,
33
+ upstreamModel: upstreamModelName(entry)
34
+ };
35
+ }
36
+ if (this.defaultProviderId) {
37
+ const defaultProvider = this.providers.find(
38
+ (p) => p.id === this.defaultProviderId && p.enabled !== false
39
+ );
40
+ if (defaultProvider) {
41
+ return { provider: defaultProvider, model, upstreamModel: model };
42
+ }
43
+ }
44
+ return null;
45
+ }
46
+ /**
47
+ * List all available models across all providers
48
+ */
49
+ listModels() {
50
+ const models = [];
51
+ for (const provider of this.providers) {
52
+ if (provider.enabled === false) continue;
53
+ for (const entry of provider.models) {
54
+ models.push({
55
+ model: modelName(entry),
56
+ upstreamModel: upstreamModelName(entry),
57
+ providerId: provider.id,
58
+ protocol: provider.protocol
59
+ });
60
+ }
61
+ }
62
+ return models;
63
+ }
64
+ /**
65
+ * Add a provider at runtime
66
+ */
67
+ addProvider(provider) {
68
+ const existing = this.providers.findIndex((p) => p.id === provider.id);
69
+ if (existing >= 0) {
70
+ this.providers[existing] = provider;
71
+ } else {
72
+ this.providers.push(provider);
73
+ }
74
+ }
75
+ /**
76
+ * Remove a provider
77
+ */
78
+ removeProvider(id) {
79
+ const idx = this.providers.findIndex((p) => p.id === id);
80
+ if (idx >= 0) {
81
+ this.providers.splice(idx, 1);
82
+ return true;
83
+ }
84
+ return false;
85
+ }
86
+ };
87
+
88
+ export {
89
+ Router
90
+ };
91
+ //# sourceMappingURL=chunk-VPPWHNGZ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/router/Router.ts"],"sourcesContent":["/**\n * Router — model matching and provider selection\n *\n * Given a model name, find the best provider to serve it.\n * Supports model name mapping (client name → upstream provider name).\n */\n\nimport type { ModelEntry, RegisteredProvider, RouteResult, RouterConfig } from \"./types\";\n\n/** Resolve a ModelEntry to its client-facing name */\nfunction modelName(entry: ModelEntry): string {\n return typeof entry === \"string\" ? entry : entry.name;\n}\n\n/** Resolve a ModelEntry to its upstream name */\nfunction upstreamModelName(entry: ModelEntry): string {\n return typeof entry === \"string\" ? entry : entry.upstreamModel;\n}\n\nexport class Router {\n private providers: RegisteredProvider[];\n private defaultProviderId?: string;\n\n constructor(config: RouterConfig) {\n this.providers = config.providers;\n this.defaultProviderId = config.defaultProviderId;\n }\n\n /**\n * Find the best provider for a given model\n */\n route(model: string): RouteResult | null {\n // Find providers that have this model (by client-facing name)\n const candidates: Array<{ provider: RegisteredProvider; entry: ModelEntry }> = [];\n\n for (const provider of this.providers) {\n if (provider.enabled === false) continue;\n const entry = provider.models.find((m) => modelName(m) === model);\n if (entry) {\n candidates.push({ provider, entry });\n }\n }\n\n // Sort by priority\n candidates.sort((a, b) => (a.provider.priority ?? 100) - (b.provider.priority ?? 100));\n\n if (candidates.length > 0) {\n const { provider, entry } = candidates[0];\n return {\n provider,\n model,\n upstreamModel: upstreamModelName(entry),\n };\n }\n\n // Fallback to default provider\n if (this.defaultProviderId) {\n const defaultProvider = this.providers.find(\n (p) => p.id === this.defaultProviderId && p.enabled !== false\n );\n if (defaultProvider) {\n return { provider: defaultProvider, model, upstreamModel: model };\n }\n }\n\n return null;\n }\n\n /**\n * List all available models across all providers\n */\n listModels(): Array<{\n model: string;\n upstreamModel: string;\n providerId: string;\n protocol: string;\n }> {\n const models: Array<{\n model: string;\n upstreamModel: string;\n providerId: string;\n protocol: string;\n }> = [];\n for (const provider of this.providers) {\n if (provider.enabled === false) continue;\n for (const entry of provider.models) {\n models.push({\n model: modelName(entry),\n upstreamModel: upstreamModelName(entry),\n providerId: provider.id,\n protocol: provider.protocol,\n });\n }\n }\n return models;\n }\n\n /**\n * Add a provider at runtime\n */\n addProvider(provider: RegisteredProvider): void {\n const existing = this.providers.findIndex((p) => p.id === provider.id);\n if (existing >= 0) {\n this.providers[existing] = provider;\n } else {\n this.providers.push(provider);\n }\n }\n\n /**\n * Remove a provider\n */\n removeProvider(id: string): boolean {\n const idx = this.providers.findIndex((p) => p.id === id);\n if (idx >= 0) {\n this.providers.splice(idx, 1);\n return true;\n }\n return false;\n }\n}\n"],"mappings":";AAUA,SAAS,UAAU,OAA2B;AAC5C,SAAO,OAAO,UAAU,WAAW,QAAQ,MAAM;AACnD;AAGA,SAAS,kBAAkB,OAA2B;AACpD,SAAO,OAAO,UAAU,WAAW,QAAQ,MAAM;AACnD;AAEO,IAAM,SAAN,MAAa;AAAA,EACV;AAAA,EACA;AAAA,EAER,YAAY,QAAsB;AAChC,SAAK,YAAY,OAAO;AACxB,SAAK,oBAAoB,OAAO;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,OAAmC;AAEvC,UAAM,aAAyE,CAAC;AAEhF,eAAW,YAAY,KAAK,WAAW;AACrC,UAAI,SAAS,YAAY,MAAO;AAChC,YAAM,QAAQ,SAAS,OAAO,KAAK,CAAC,MAAM,UAAU,CAAC,MAAM,KAAK;AAChE,UAAI,OAAO;AACT,mBAAW,KAAK,EAAE,UAAU,MAAM,CAAC;AAAA,MACrC;AAAA,IACF;AAGA,eAAW,KAAK,CAAC,GAAG,OAAO,EAAE,SAAS,YAAY,QAAQ,EAAE,SAAS,YAAY,IAAI;AAErF,QAAI,WAAW,SAAS,GAAG;AACzB,YAAM,EAAE,UAAU,MAAM,IAAI,WAAW,CAAC;AACxC,aAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA,eAAe,kBAAkB,KAAK;AAAA,MACxC;AAAA,IACF;AAGA,QAAI,KAAK,mBAAmB;AAC1B,YAAM,kBAAkB,KAAK,UAAU;AAAA,QACrC,CAAC,MAAM,EAAE,OAAO,KAAK,qBAAqB,EAAE,YAAY;AAAA,MAC1D;AACA,UAAI,iBAAiB;AACnB,eAAO,EAAE,UAAU,iBAAiB,OAAO,eAAe,MAAM;AAAA,MAClE;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,aAKG;AACD,UAAM,SAKD,CAAC;AACN,eAAW,YAAY,KAAK,WAAW;AACrC,UAAI,SAAS,YAAY,MAAO;AAChC,iBAAW,SAAS,SAAS,QAAQ;AACnC,eAAO,KAAK;AAAA,UACV,OAAO,UAAU,KAAK;AAAA,UACtB,eAAe,kBAAkB,KAAK;AAAA,UACtC,YAAY,SAAS;AAAA,UACrB,UAAU,SAAS;AAAA,QACrB,CAAC;AAAA,MACH;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,YAAY,UAAoC;AAC9C,UAAM,WAAW,KAAK,UAAU,UAAU,CAAC,MAAM,EAAE,OAAO,SAAS,EAAE;AACrE,QAAI,YAAY,GAAG;AACjB,WAAK,UAAU,QAAQ,IAAI;AAAA,IAC7B,OAAO;AACL,WAAK,UAAU,KAAK,QAAQ;AAAA,IAC9B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,eAAe,IAAqB;AAClC,UAAM,MAAM,KAAK,UAAU,UAAU,CAAC,MAAM,EAAE,OAAO,EAAE;AACvD,QAAI,OAAO,GAAG;AACZ,WAAK,UAAU,OAAO,KAAK,CAAC;AAC5B,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT;AACF;","names":[]}
@@ -0,0 +1 @@
1
+ export { ModelEntry, ProviderProtocol, RegisteredProvider, RouteResult, Router, RouterConfig } from './router/index.js';
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ import {
2
+ Router
3
+ } from "./chunk-VPPWHNGZ.js";
4
+ export {
5
+ Router
6
+ };
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Router Types — routing configuration and model matching
3
+ */
4
+ /**
5
+ * Supported upstream provider protocols
6
+ */
7
+ type ProviderProtocol = "openai-compatible" | "anthropic";
8
+ /**
9
+ * Model entry — either a plain string or an object with upstream mapping
10
+ *
11
+ * Plain string: model name is the same on both sides
12
+ * Object: name is what clients request, upstreamModel is what the provider expects
13
+ *
14
+ * @example
15
+ * // Simple — same name both sides
16
+ * "gpt-4o"
17
+ *
18
+ * // Mapped — "deepseek-v3" externally, "ep-xxx" at the provider
19
+ * { name: "deepseek-v3", upstreamModel: "ep-20250101-xxx" }
20
+ */
21
+ type ModelEntry = string | {
22
+ name: string;
23
+ upstreamModel: string;
24
+ };
25
+ /**
26
+ * A registered upstream provider with its capabilities
27
+ */
28
+ interface RegisteredProvider {
29
+ /** Unique provider ID */
30
+ id: string;
31
+ /** Display name */
32
+ name: string;
33
+ /** Protocol this provider speaks */
34
+ protocol: ProviderProtocol;
35
+ /** API key */
36
+ apiKey: string;
37
+ /** Custom base URL */
38
+ baseUrl?: string;
39
+ /** Models this provider can serve */
40
+ models: ModelEntry[];
41
+ /** Priority for model matching (lower = higher priority) */
42
+ priority?: number;
43
+ /** Whether this provider is enabled */
44
+ enabled?: boolean;
45
+ }
46
+ /**
47
+ * The result of routing a request to a provider
48
+ */
49
+ interface RouteResult {
50
+ /** The matched provider */
51
+ provider: RegisteredProvider;
52
+ /** The model name as requested by the client */
53
+ model: string;
54
+ /** The model name to send to the upstream provider (may differ from model) */
55
+ upstreamModel: string;
56
+ }
57
+ /**
58
+ * Router configuration
59
+ */
60
+ interface RouterConfig {
61
+ /** Registered providers */
62
+ providers: RegisteredProvider[];
63
+ /** Default provider ID (fallback when no model match) */
64
+ defaultProviderId?: string;
65
+ }
66
+
67
+ /**
68
+ * Router — model matching and provider selection
69
+ *
70
+ * Given a model name, find the best provider to serve it.
71
+ * Supports model name mapping (client name → upstream provider name).
72
+ */
73
+
74
+ declare class Router {
75
+ private providers;
76
+ private defaultProviderId?;
77
+ constructor(config: RouterConfig);
78
+ /**
79
+ * Find the best provider for a given model
80
+ */
81
+ route(model: string): RouteResult | null;
82
+ /**
83
+ * List all available models across all providers
84
+ */
85
+ listModels(): Array<{
86
+ model: string;
87
+ upstreamModel: string;
88
+ providerId: string;
89
+ protocol: string;
90
+ }>;
91
+ /**
92
+ * Add a provider at runtime
93
+ */
94
+ addProvider(provider: RegisteredProvider): void;
95
+ /**
96
+ * Remove a provider
97
+ */
98
+ removeProvider(id: string): boolean;
99
+ }
100
+
101
+ export { type ModelEntry, type ProviderProtocol, type RegisteredProvider, type RouteResult, Router, type RouterConfig };
@@ -0,0 +1,7 @@
1
+ import {
2
+ Router
3
+ } from "../chunk-VPPWHNGZ.js";
4
+ export {
5
+ Router
6
+ };
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@routerxjs/core",
3
+ "version": "0.0.0-dev-20260318021459",
4
+ "type": "module",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "import": "./dist/index.js"
11
+ },
12
+ "./router": {
13
+ "types": "./dist/router/index.d.ts",
14
+ "import": "./dist/router/index.js"
15
+ }
16
+ },
17
+ "files": [
18
+ "dist",
19
+ "src"
20
+ ],
21
+ "scripts": {
22
+ "build": "tsup",
23
+ "typecheck": "tsc --noEmit",
24
+ "test": "bun test"
25
+ },
26
+ "devDependencies": {
27
+ "typescript": "^5.9.3"
28
+ }
29
+ }
package/src/index.ts ADDED
@@ -0,0 +1,10 @@
1
+ // Router
2
+
3
+ export type {
4
+ ModelEntry,
5
+ ProviderProtocol,
6
+ RegisteredProvider,
7
+ RouteResult,
8
+ RouterConfig,
9
+ } from "./router/index";
10
+ export { Router } from "./router/index";
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Router — model matching and provider selection
3
+ *
4
+ * Given a model name, find the best provider to serve it.
5
+ * Supports model name mapping (client name → upstream provider name).
6
+ */
7
+
8
+ import type { ModelEntry, RegisteredProvider, RouteResult, RouterConfig } from "./types";
9
+
10
+ /** Resolve a ModelEntry to its client-facing name */
11
+ function modelName(entry: ModelEntry): string {
12
+ return typeof entry === "string" ? entry : entry.name;
13
+ }
14
+
15
+ /** Resolve a ModelEntry to its upstream name */
16
+ function upstreamModelName(entry: ModelEntry): string {
17
+ return typeof entry === "string" ? entry : entry.upstreamModel;
18
+ }
19
+
20
+ export class Router {
21
+ private providers: RegisteredProvider[];
22
+ private defaultProviderId?: string;
23
+
24
+ constructor(config: RouterConfig) {
25
+ this.providers = config.providers;
26
+ this.defaultProviderId = config.defaultProviderId;
27
+ }
28
+
29
+ /**
30
+ * Find the best provider for a given model
31
+ */
32
+ route(model: string): RouteResult | null {
33
+ // Find providers that have this model (by client-facing name)
34
+ const candidates: Array<{ provider: RegisteredProvider; entry: ModelEntry }> = [];
35
+
36
+ for (const provider of this.providers) {
37
+ if (provider.enabled === false) continue;
38
+ const entry = provider.models.find((m) => modelName(m) === model);
39
+ if (entry) {
40
+ candidates.push({ provider, entry });
41
+ }
42
+ }
43
+
44
+ // Sort by priority
45
+ candidates.sort((a, b) => (a.provider.priority ?? 100) - (b.provider.priority ?? 100));
46
+
47
+ if (candidates.length > 0) {
48
+ const { provider, entry } = candidates[0];
49
+ return {
50
+ provider,
51
+ model,
52
+ upstreamModel: upstreamModelName(entry),
53
+ };
54
+ }
55
+
56
+ // Fallback to default provider
57
+ if (this.defaultProviderId) {
58
+ const defaultProvider = this.providers.find(
59
+ (p) => p.id === this.defaultProviderId && p.enabled !== false
60
+ );
61
+ if (defaultProvider) {
62
+ return { provider: defaultProvider, model, upstreamModel: model };
63
+ }
64
+ }
65
+
66
+ return null;
67
+ }
68
+
69
+ /**
70
+ * List all available models across all providers
71
+ */
72
+ listModels(): Array<{
73
+ model: string;
74
+ upstreamModel: string;
75
+ providerId: string;
76
+ protocol: string;
77
+ }> {
78
+ const models: Array<{
79
+ model: string;
80
+ upstreamModel: string;
81
+ providerId: string;
82
+ protocol: string;
83
+ }> = [];
84
+ for (const provider of this.providers) {
85
+ if (provider.enabled === false) continue;
86
+ for (const entry of provider.models) {
87
+ models.push({
88
+ model: modelName(entry),
89
+ upstreamModel: upstreamModelName(entry),
90
+ providerId: provider.id,
91
+ protocol: provider.protocol,
92
+ });
93
+ }
94
+ }
95
+ return models;
96
+ }
97
+
98
+ /**
99
+ * Add a provider at runtime
100
+ */
101
+ addProvider(provider: RegisteredProvider): void {
102
+ const existing = this.providers.findIndex((p) => p.id === provider.id);
103
+ if (existing >= 0) {
104
+ this.providers[existing] = provider;
105
+ } else {
106
+ this.providers.push(provider);
107
+ }
108
+ }
109
+
110
+ /**
111
+ * Remove a provider
112
+ */
113
+ removeProvider(id: string): boolean {
114
+ const idx = this.providers.findIndex((p) => p.id === id);
115
+ if (idx >= 0) {
116
+ this.providers.splice(idx, 1);
117
+ return true;
118
+ }
119
+ return false;
120
+ }
121
+ }
@@ -0,0 +1,8 @@
1
+ export { Router } from "./Router";
2
+ export type {
3
+ ModelEntry,
4
+ ProviderProtocol,
5
+ RegisteredProvider,
6
+ RouteResult,
7
+ RouterConfig,
8
+ } from "./types";
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Router Types — routing configuration and model matching
3
+ */
4
+
5
+ /**
6
+ * Supported upstream provider protocols
7
+ */
8
+ export type ProviderProtocol = "openai-compatible" | "anthropic";
9
+
10
+ /**
11
+ * Model entry — either a plain string or an object with upstream mapping
12
+ *
13
+ * Plain string: model name is the same on both sides
14
+ * Object: name is what clients request, upstreamModel is what the provider expects
15
+ *
16
+ * @example
17
+ * // Simple — same name both sides
18
+ * "gpt-4o"
19
+ *
20
+ * // Mapped — "deepseek-v3" externally, "ep-xxx" at the provider
21
+ * { name: "deepseek-v3", upstreamModel: "ep-20250101-xxx" }
22
+ */
23
+ export type ModelEntry = string | { name: string; upstreamModel: string };
24
+
25
+ /**
26
+ * A registered upstream provider with its capabilities
27
+ */
28
+ export interface RegisteredProvider {
29
+ /** Unique provider ID */
30
+ id: string;
31
+
32
+ /** Display name */
33
+ name: string;
34
+
35
+ /** Protocol this provider speaks */
36
+ protocol: ProviderProtocol;
37
+
38
+ /** API key */
39
+ apiKey: string;
40
+
41
+ /** Custom base URL */
42
+ baseUrl?: string;
43
+
44
+ /** Models this provider can serve */
45
+ models: ModelEntry[];
46
+
47
+ /** Priority for model matching (lower = higher priority) */
48
+ priority?: number;
49
+
50
+ /** Whether this provider is enabled */
51
+ enabled?: boolean;
52
+ }
53
+
54
+ /**
55
+ * The result of routing a request to a provider
56
+ */
57
+ export interface RouteResult {
58
+ /** The matched provider */
59
+ provider: RegisteredProvider;
60
+
61
+ /** The model name as requested by the client */
62
+ model: string;
63
+
64
+ /** The model name to send to the upstream provider (may differ from model) */
65
+ upstreamModel: string;
66
+ }
67
+
68
+ /**
69
+ * Router configuration
70
+ */
71
+ export interface RouterConfig {
72
+ /** Registered providers */
73
+ providers: RegisteredProvider[];
74
+
75
+ /** Default provider ID (fallback when no model match) */
76
+ defaultProviderId?: string;
77
+ }