@vymalo/opencode-models-info 0.12.0 → 0.14.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/dist/cache.d.ts +23 -23
- package/dist/cache.js +89 -91
- package/dist/cache.js.map +1 -1
- package/dist/config.d.ts +23 -23
- package/dist/config.js +109 -109
- package/dist/config.js.map +1 -1
- package/dist/fetcher.d.ts +9 -9
- package/dist/fetcher.js +76 -70
- package/dist/fetcher.js.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/lib.js +1 -0
- package/dist/lib.js.map +1 -1
- package/dist/logging.d.ts +6 -6
- package/dist/logging.js +58 -62
- package/dist/logging.js.map +1 -1
- package/dist/mapping.d.ts +39 -39
- package/dist/mapping.js +144 -130
- package/dist/mapping.js.map +1 -1
- package/dist/opencode.d.ts +4 -4
- package/dist/opencode.js +70 -69
- package/dist/opencode.js.map +1 -1
- package/dist/plugin.d.ts +25 -25
- package/dist/plugin.js +342 -315
- package/dist/plugin.js.map +1 -1
- package/dist/scheduler.d.ts +16 -16
- package/dist/scheduler.js +55 -57
- package/dist/scheduler.js.map +1 -1
- package/dist/types.d.ts +67 -67
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -1
- package/package.json +3 -3
package/dist/cache.d.ts
CHANGED
|
@@ -1,33 +1,33 @@
|
|
|
1
1
|
import type { CachedModelsRecord } from "./types.js";
|
|
2
2
|
export declare function resolveCacheDir(namespace?: string): string;
|
|
3
3
|
/**
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
4
|
+
* Cache key = sha256(providerId :: url :: stableJSON(headers)).
|
|
5
|
+
*
|
|
6
|
+
* Only the **caller-specified** headers (i.e. `meta.modelsInfoHeaders`) go
|
|
7
|
+
* into the key — NOT the provider's other request headers. Rationale: if a
|
|
8
|
+
* rotating bearer (e.g. from `@vymalo/opencode-oauth2`) were keyed in, the
|
|
9
|
+
* cache would thrash on every token refresh. Headers the user explicitly
|
|
10
|
+
* configures for the metadata fetch (tenant selectors, static auth, etc.)
|
|
11
|
+
* are exactly the ones that should bust the cache when they change.
|
|
12
|
+
*/
|
|
13
13
|
export declare function cacheKey(providerId: string, url: string, headers?: Record<string, string>): string;
|
|
14
14
|
export interface CacheStore {
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
get(key: string): Promise<CachedModelsRecord | undefined>;
|
|
16
|
+
put(key: string, record: CachedModelsRecord): Promise<void>;
|
|
17
17
|
}
|
|
18
18
|
/**
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
* Two-layer cache: an in-memory map for the process lifetime, backed by JSON
|
|
20
|
+
* files on disk so cold starts reuse the last good snapshot. Disk writes are
|
|
21
|
+
* atomic via rename-after-write so a crashed process can't leave a torn file.
|
|
22
|
+
*/
|
|
23
23
|
export declare class FileCacheStore implements CacheStore {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
24
|
+
private readonly baseDir;
|
|
25
|
+
private readonly memory;
|
|
26
|
+
private ready;
|
|
27
|
+
constructor(baseDir?: string);
|
|
28
|
+
private ensureReady;
|
|
29
|
+
private filePath;
|
|
30
|
+
get(key: string): Promise<CachedModelsRecord | undefined>;
|
|
31
|
+
put(key: string, record: CachedModelsRecord): Promise<void>;
|
|
32
32
|
}
|
|
33
33
|
export declare function isExpired(record: CachedModelsRecord, now?: number): boolean;
|
package/dist/cache.js
CHANGED
|
@@ -3,112 +3,110 @@ import { mkdir, readFile, rename, unlink, writeFile } from "node:fs/promises";
|
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
function resolveDefaultCacheRoot() {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
6
|
+
if (process.platform === "win32") {
|
|
7
|
+
return process.env.LOCALAPPDATA ?? join(homedir(), "AppData", "Local");
|
|
8
|
+
}
|
|
9
|
+
if (process.platform === "darwin") {
|
|
10
|
+
return join(homedir(), "Library", "Caches");
|
|
11
|
+
}
|
|
12
|
+
return process.env.XDG_CACHE_HOME ?? join(homedir(), ".cache");
|
|
13
13
|
}
|
|
14
14
|
export function resolveCacheDir(namespace = "opencode-models-info") {
|
|
15
|
-
|
|
15
|
+
return join(resolveDefaultCacheRoot(), namespace);
|
|
16
16
|
}
|
|
17
17
|
/**
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
18
|
+
* Cache key = sha256(providerId :: url :: stableJSON(headers)).
|
|
19
|
+
*
|
|
20
|
+
* Only the **caller-specified** headers (i.e. `meta.modelsInfoHeaders`) go
|
|
21
|
+
* into the key — NOT the provider's other request headers. Rationale: if a
|
|
22
|
+
* rotating bearer (e.g. from `@vymalo/opencode-oauth2`) were keyed in, the
|
|
23
|
+
* cache would thrash on every token refresh. Headers the user explicitly
|
|
24
|
+
* configures for the metadata fetch (tenant selectors, static auth, etc.)
|
|
25
|
+
* are exactly the ones that should bust the cache when they change.
|
|
26
|
+
*/
|
|
27
27
|
export function cacheKey(providerId, url, headers) {
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
const headerPart = headers ? stableStringify(headers) : "";
|
|
29
|
+
return createHash("sha256").update(`${providerId}::${url}::${headerPart}`).digest("hex");
|
|
30
30
|
}
|
|
31
31
|
function stableStringify(headers) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
.map((k) => [k.toLowerCase(), headers[k]]);
|
|
35
|
-
return JSON.stringify(sorted);
|
|
32
|
+
const sorted = Object.keys(headers).sort().map((k) => [k.toLowerCase(), headers[k]]);
|
|
33
|
+
return JSON.stringify(sorted);
|
|
36
34
|
}
|
|
37
35
|
/**
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
36
|
+
* Two-layer cache: an in-memory map for the process lifetime, backed by JSON
|
|
37
|
+
* files on disk so cold starts reuse the last good snapshot. Disk writes are
|
|
38
|
+
* atomic via rename-after-write so a crashed process can't leave a torn file.
|
|
39
|
+
*/
|
|
42
40
|
export class FileCacheStore {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
41
|
+
baseDir;
|
|
42
|
+
memory = new Map();
|
|
43
|
+
ready;
|
|
44
|
+
constructor(baseDir = resolveCacheDir()) {
|
|
45
|
+
this.baseDir = baseDir;
|
|
46
|
+
}
|
|
47
|
+
async ensureReady() {
|
|
48
|
+
if (!this.ready) {
|
|
49
|
+
this.ready = mkdir(this.baseDir, {
|
|
50
|
+
recursive: true,
|
|
51
|
+
mode: 448
|
|
52
|
+
}).then(() => undefined);
|
|
53
|
+
}
|
|
54
|
+
await this.ready;
|
|
55
|
+
}
|
|
56
|
+
filePath(key) {
|
|
57
|
+
return join(this.baseDir, `${key}.json`);
|
|
58
|
+
}
|
|
59
|
+
async get(key) {
|
|
60
|
+
const memHit = this.memory.get(key);
|
|
61
|
+
if (memHit) {
|
|
62
|
+
return memHit;
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
await this.ensureReady();
|
|
66
|
+
const raw = await readFile(this.filePath(key), "utf8");
|
|
67
|
+
const parsed = JSON.parse(raw);
|
|
68
|
+
if (!isValidRecord(parsed)) {
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
this.memory.set(key, parsed);
|
|
72
|
+
return parsed;
|
|
73
|
+
} catch (error) {
|
|
74
|
+
if (isFileNotFound(error)) {
|
|
75
|
+
return undefined;
|
|
76
|
+
}
|
|
77
|
+
return undefined;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
async put(key, record) {
|
|
81
|
+
this.memory.set(key, record);
|
|
82
|
+
await this.ensureReady();
|
|
83
|
+
const target = this.filePath(key);
|
|
84
|
+
// Unique per-write temp name (pid + uuid) so concurrent opencode instances
|
|
85
|
+
// — or two enrich passes in one process racing the same key — never collide
|
|
86
|
+
// on the temp file and trip an ENOENT on rename. See opencode-oauth2's
|
|
87
|
+
// saveServerState for the full rationale.
|
|
88
|
+
const tmp = `${target}.${process.pid}.${randomUUID()}.tmp`;
|
|
89
|
+
try {
|
|
90
|
+
await writeFile(tmp, JSON.stringify(record), { mode: 384 });
|
|
91
|
+
await rename(tmp, target);
|
|
92
|
+
} catch (error) {
|
|
93
|
+
await unlink(tmp).catch(() => {});
|
|
94
|
+
throw error;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
98
97
|
}
|
|
99
98
|
export function isExpired(record, now = Date.now()) {
|
|
100
|
-
|
|
99
|
+
return now - record.fetchedAt > record.ttlSeconds * 1e3;
|
|
101
100
|
}
|
|
102
101
|
function isValidRecord(value) {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
typeof record.ttlSeconds === "number" &&
|
|
109
|
-
Array.isArray(record.models));
|
|
102
|
+
if (!value || typeof value !== "object") {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
const record = value;
|
|
106
|
+
return typeof record.fetchedAt === "number" && typeof record.ttlSeconds === "number" && Array.isArray(record.models);
|
|
110
107
|
}
|
|
111
108
|
function isFileNotFound(error) {
|
|
112
|
-
|
|
109
|
+
return Boolean(error && typeof error === "object" && error.code === "ENOENT");
|
|
113
110
|
}
|
|
111
|
+
|
|
114
112
|
//# sourceMappingURL=cache.js.map
|
package/dist/cache.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"
|
|
1
|
+
{"mappings":"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","names":[],"sources":["../src/cache.ts"],"version":3,"file":"cache.js","sourceRoot":""}
|
package/dist/config.d.ts
CHANGED
|
@@ -1,27 +1,27 @@
|
|
|
1
1
|
import type { MetaProviderOptions } from "./types.js";
|
|
2
2
|
export declare const DEFAULT_TTL_SECONDS = 86400;
|
|
3
|
-
export declare const DEFAULT_TIMEOUT_MS =
|
|
3
|
+
export declare const DEFAULT_TIMEOUT_MS = 5e3;
|
|
4
4
|
/**
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
5
|
+
* Parse a provider's `options.meta` for opt-in model-info fields. Returns
|
|
6
|
+
* `null` if the provider has not opted in (no `meta.modelsInfoUrl`).
|
|
7
|
+
*
|
|
8
|
+
* URL resolution follows the WHATWG URL spec when `modelsInfoUrl` is not
|
|
9
|
+
* absolute:
|
|
10
|
+
* - Absolute URL (`https://…`) → used as-is.
|
|
11
|
+
* - Path starting with `/` → resolves from the **origin**
|
|
12
|
+
* of `baseURL`. So with
|
|
13
|
+
* `baseURL: "https://x.test/v1"`
|
|
14
|
+
* and `modelsInfoUrl: "/models"`,
|
|
15
|
+
* you get `https://x.test/models`.
|
|
16
|
+
* Useful when your metadata
|
|
17
|
+
* endpoint sits at a different
|
|
18
|
+
* path than the inference API.
|
|
19
|
+
* - Path without leading `/` → resolves **relative to**
|
|
20
|
+
* `baseURL`. So with
|
|
21
|
+
* `baseURL: "https://x.test/v1"`
|
|
22
|
+
* and `modelsInfoUrl: "models"`,
|
|
23
|
+
* you get `https://x.test/v1/models`.
|
|
24
|
+
* Useful when metadata sits under
|
|
25
|
+
* the same path as inference.
|
|
26
|
+
*/
|
|
27
27
|
export declare function parseMetaOptions(providerOptions: Record<string, unknown> | undefined): MetaProviderOptions | null;
|
package/dist/config.js
CHANGED
|
@@ -1,131 +1,131 @@
|
|
|
1
|
-
export const DEFAULT_TTL_SECONDS =
|
|
2
|
-
export const DEFAULT_TIMEOUT_MS =
|
|
1
|
+
export const DEFAULT_TTL_SECONDS = 86400;
|
|
2
|
+
export const DEFAULT_TIMEOUT_MS = 5e3;
|
|
3
3
|
const META_KEY = "meta";
|
|
4
4
|
/**
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
* Fields a user may opt out of upstream-wins via `meta.modelsInfoOverwrite`.
|
|
6
|
+
* Mirrors the keys of `ModelMetadata` — anything outside this set is ignored
|
|
7
|
+
* so a typo never silently clobbers an unrelated field.
|
|
8
|
+
*/
|
|
9
9
|
const OVERWRITABLE_FIELDS = new Set([
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
10
|
+
"name",
|
|
11
|
+
"attachment",
|
|
12
|
+
"reasoning",
|
|
13
|
+
"temperature",
|
|
14
|
+
"tool_call",
|
|
15
|
+
"cost",
|
|
16
|
+
"limit",
|
|
17
|
+
"modalities"
|
|
18
18
|
]);
|
|
19
19
|
function asRecord(value) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
return value;
|
|
24
24
|
}
|
|
25
25
|
function asString(value) {
|
|
26
|
-
|
|
26
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
|
|
27
27
|
}
|
|
28
28
|
function asStringMap(value) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
29
|
+
const record = asRecord(value);
|
|
30
|
+
if (!record) {
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
const out = {};
|
|
34
|
+
for (const [key, raw] of Object.entries(record)) {
|
|
35
|
+
if (typeof raw === "string" && raw.length > 0) {
|
|
36
|
+
out[key] = raw;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return Object.keys(out).length > 0 ? out : undefined;
|
|
40
40
|
}
|
|
41
41
|
function asOverwriteList(value) {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
42
|
+
if (!Array.isArray(value)) {
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
const out = [];
|
|
46
|
+
for (const raw of value) {
|
|
47
|
+
if (typeof raw === "string" && OVERWRITABLE_FIELDS.has(raw) && !out.includes(raw)) {
|
|
48
|
+
out.push(raw);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return out.length > 0 ? out : undefined;
|
|
52
52
|
}
|
|
53
53
|
function asPositiveInt(value, fallback) {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
54
|
+
if (typeof value === "number" && Number.isFinite(value) && value > 0) {
|
|
55
|
+
return Math.floor(value);
|
|
56
|
+
}
|
|
57
|
+
return fallback;
|
|
58
58
|
}
|
|
59
59
|
function asBoolean(value) {
|
|
60
|
-
|
|
60
|
+
return value === true;
|
|
61
61
|
}
|
|
62
62
|
/**
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
63
|
+
* Parse a provider's `options.meta` for opt-in model-info fields. Returns
|
|
64
|
+
* `null` if the provider has not opted in (no `meta.modelsInfoUrl`).
|
|
65
|
+
*
|
|
66
|
+
* URL resolution follows the WHATWG URL spec when `modelsInfoUrl` is not
|
|
67
|
+
* absolute:
|
|
68
|
+
* - Absolute URL (`https://…`) → used as-is.
|
|
69
|
+
* - Path starting with `/` → resolves from the **origin**
|
|
70
|
+
* of `baseURL`. So with
|
|
71
|
+
* `baseURL: "https://x.test/v1"`
|
|
72
|
+
* and `modelsInfoUrl: "/models"`,
|
|
73
|
+
* you get `https://x.test/models`.
|
|
74
|
+
* Useful when your metadata
|
|
75
|
+
* endpoint sits at a different
|
|
76
|
+
* path than the inference API.
|
|
77
|
+
* - Path without leading `/` → resolves **relative to**
|
|
78
|
+
* `baseURL`. So with
|
|
79
|
+
* `baseURL: "https://x.test/v1"`
|
|
80
|
+
* and `modelsInfoUrl: "models"`,
|
|
81
|
+
* you get `https://x.test/v1/models`.
|
|
82
|
+
* Useful when metadata sits under
|
|
83
|
+
* the same path as inference.
|
|
84
|
+
*/
|
|
85
85
|
export function parseMetaOptions(providerOptions) {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
86
|
+
if (!providerOptions) {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
const meta = asRecord(providerOptions[META_KEY]);
|
|
90
|
+
if (!meta) {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
const rawUrl = asString(meta.modelsInfoUrl);
|
|
94
|
+
if (!rawUrl) {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
const baseURL = asString(providerOptions.baseURL);
|
|
98
|
+
const modelsInfoUrl = resolveUrl(rawUrl, baseURL);
|
|
99
|
+
return {
|
|
100
|
+
modelsInfoUrl,
|
|
101
|
+
modelsInfoTtlSeconds: asPositiveInt(meta.modelsInfoTtlSeconds, DEFAULT_TTL_SECONDS),
|
|
102
|
+
modelsInfoTimeoutMs: asPositiveInt(meta.modelsInfoTimeoutMs, DEFAULT_TIMEOUT_MS),
|
|
103
|
+
modelsInfoHeaders: asStringMap(meta.modelsInfoHeaders),
|
|
104
|
+
modelsInfoOverwrite: asOverwriteList(meta.modelsInfoOverwrite),
|
|
105
|
+
modelsInfoHideTextOnly: asBoolean(meta.modelsInfoHideTextOnly),
|
|
106
|
+
modelsInfoHideInternal: asBoolean(meta.modelsInfoHideInternal),
|
|
107
|
+
modelsInfoHideUnmatched: asBoolean(meta.modelsInfoHideUnmatched),
|
|
108
|
+
modelsInfoFormat: "openrouter"
|
|
109
|
+
};
|
|
110
110
|
}
|
|
111
111
|
function resolveUrl(candidate, baseURL) {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
}
|
|
112
|
+
if (/^https?:\/\//i.test(candidate)) {
|
|
113
|
+
return candidate;
|
|
114
|
+
}
|
|
115
|
+
if (!baseURL) {
|
|
116
|
+
return candidate;
|
|
117
|
+
}
|
|
118
|
+
// Always treat the baseURL as a directory by appending a trailing slash if
|
|
119
|
+
// it's missing. This way a path-relative `modelsInfoUrl` ("models/info")
|
|
120
|
+
// resolves under the baseURL's path instead of replacing its last segment
|
|
121
|
+
// (the WHATWG default). A leading-slash candidate ("/models/info") still
|
|
122
|
+
// resolves from the origin per spec.
|
|
123
|
+
const base = baseURL.endsWith("/") ? baseURL : `${baseURL}/`;
|
|
124
|
+
try {
|
|
125
|
+
return new URL(candidate, base).toString();
|
|
126
|
+
} catch {
|
|
127
|
+
return candidate;
|
|
128
|
+
}
|
|
130
129
|
}
|
|
130
|
+
|
|
131
131
|
//# sourceMappingURL=config.js.map
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"
|
|
1
|
+
{"mappings":"AAEA,OAAO,MAAM,sBAAsB;AACnC,OAAO,MAAM,qBAAqB;AAElC,MAAM,WAAW;;;;;;AAOjB,MAAM,sBAAsB,IAAI,IAAI;CAClC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;AAED,SAAS,SAAS,OAAqD;CACrE,IAAI,CAAC,SAAS,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK,GAAG;EAC/D,OAAO;CACT;CACA,OAAO;AACT;AAEA,SAAS,SAAS,OAAoC;CACpD,OAAO,OAAO,UAAU,YAAY,MAAM,KAAK,CAAC,CAAC,SAAS,IAAI,MAAM,KAAK,IAAI;AAC/E;AAEA,SAAS,YAAY,OAAoD;CACvE,MAAM,SAAS,SAAS,KAAK;CAC7B,IAAI,CAAC,QAAQ;EACX,OAAO;CACT;CACA,MAAM,MAA8B,CAAC;CACrC,KAAK,MAAM,CAAC,KAAK,QAAQ,OAAO,QAAQ,MAAM,GAAG;EAC/C,IAAI,OAAO,QAAQ,YAAY,IAAI,SAAS,GAAG;GAC7C,IAAI,OAAO;EACb;CACF;CACA,OAAO,OAAO,KAAK,GAAG,CAAC,CAAC,SAAS,IAAI,MAAM;AAC7C;AAEA,SAAS,gBAAgB,OAAsC;CAC7D,IAAI,CAAC,MAAM,QAAQ,KAAK,GAAG;EACzB,OAAO;CACT;CACA,MAAM,MAAgB,CAAC;CACvB,KAAK,MAAM,OAAO,OAAO;EACvB,IAAI,OAAO,QAAQ,YAAY,oBAAoB,IAAI,GAAG,KAAK,CAAC,IAAI,SAAS,GAAG,GAAG;GACjF,IAAI,KAAK,GAAG;EACd;CACF;CACA,OAAO,IAAI,SAAS,IAAI,MAAM;AAChC;AAEA,SAAS,cAAc,OAAgB,UAA0B;CAC/D,IAAI,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,KAAK,QAAQ,GAAG;EACpE,OAAO,KAAK,MAAM,KAAK;CACzB;CACA,OAAO;AACT;AAEA,SAAS,UAAU,OAAyB;CAC1C,OAAO,UAAU;AACnB;;;;;;;;;;;;;;;;;;;;;;;;AAyBA,OAAO,SAAS,iBACd,iBAC4B;CAC5B,IAAI,CAAC,iBAAiB;EACpB,OAAO;CACT;CAEA,MAAM,OAAO,SAAS,gBAAgB,SAAS;CAC/C,IAAI,CAAC,MAAM;EACT,OAAO;CACT;CAEA,MAAM,SAAS,SAAS,KAAK,aAAa;CAC1C,IAAI,CAAC,QAAQ;EACX,OAAO;CACT;CAEA,MAAM,UAAU,SAAS,gBAAgB,OAAO;CAChD,MAAM,gBAAgB,WAAW,QAAQ,OAAO;CAEhD,OAAO;EACL;EACA,sBAAsB,cAAc,KAAK,sBAAsB,mBAAmB;EAClF,qBAAqB,cAAc,KAAK,qBAAqB,kBAAkB;EAC/E,mBAAmB,YAAY,KAAK,iBAAiB;EACrD,qBAAqB,gBAAgB,KAAK,mBAAmB;EAC7D,wBAAwB,UAAU,KAAK,sBAAsB;EAC7D,wBAAwB,UAAU,KAAK,sBAAsB;EAC7D,yBAAyB,UAAU,KAAK,uBAAuB;EAC/D,kBAAkB;CACpB;AACF;AAEA,SAAS,WAAW,WAAmB,SAAqC;CAC1E,IAAI,gBAAgB,KAAK,SAAS,GAAG;EACnC,OAAO;CACT;CACA,IAAI,CAAC,SAAS;EACZ,OAAO;CACT;;;;;;CAMA,MAAM,OAAO,QAAQ,SAAS,GAAG,IAAI,UAAU,GAAG,QAAQ;CAC1D,IAAI;EACF,OAAO,IAAI,IAAI,WAAW,IAAI,CAAC,CAAC,SAAS;CAC3C,QAAQ;EACN,OAAO;CACT;AACF","names":[],"sources":["../src/config.ts"],"version":3,"file":"config.js","sourceRoot":""}
|