@eve-online-tools/eve-resfile 0.0.1

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 ADDED
@@ -0,0 +1,109 @@
1
+ # @eve-online-tools/eve-resfile
2
+
3
+ Load EVE Online client assets via `res:/` imports in Vite and Rollup projects.
4
+
5
+ The EVE client stores game assets behind a resfile index: a mapping from in-game resource paths (like `res:/icons/64/icon64.png`) to CDN paths. This package downloads and caches that index, then provides bundler plugins that resolve `res:/` imports to either:
6
+
7
+ - **Dev:** a local proxy URL (`/__eve_res__/...`) that fetches assets on demand
8
+ - **Production:** emitted static assets fetched from the EVE CDN at build time
9
+
10
+ ## Installation
11
+
12
+ ```bash
13
+ pnpm add -D @eve-online-tools/eve-resfile
14
+ ```
15
+
16
+ ## Vite
17
+
18
+ ```ts
19
+ // vite.config.ts
20
+ import { defineConfig } from "vite";
21
+ import { eveResfile } from "@eve-online-tools/eve-resfile/vite";
22
+
23
+ export default defineConfig({
24
+ plugins: [eveResfile()],
25
+ });
26
+ ```
27
+
28
+ Add client types so TypeScript understands `res:/` imports:
29
+
30
+ ```ts
31
+ // src/vite-env.d.ts
32
+ /// <reference types="@eve-online-tools/eve-resfile/client" />
33
+ ```
34
+
35
+ Use resfile imports in application code:
36
+
37
+ ```ts
38
+ import iconUrl from "res:/icons/64/icon64.png";
39
+
40
+ export function Icon() {
41
+ return <img src={iconUrl} alt="" />;
42
+ }
43
+ ```
44
+
45
+ In dev, `iconUrl` is a proxy path served by the plugin. In production builds, the asset is downloaded from the CDN and emitted into your bundle output.
46
+
47
+ ## Rollup
48
+
49
+ ```ts
50
+ // rollup.config.ts
51
+ import { eveResfile } from "@eve-online-tools/eve-resfile/rollup";
52
+
53
+ export default {
54
+ input: "src/main.ts",
55
+ plugins: [eveResfile()],
56
+ };
57
+ ```
58
+
59
+ Rollup resolves `cacheDir` relative to `root` (default: `process.cwd()`). Pass `root` explicitly if your config runs from another directory.
60
+
61
+ ## Options
62
+
63
+ ```ts
64
+ eveResfile({
65
+ /** Pin to a specific client build; omit to use the latest TQ build */
66
+ buildNumber: 1234567,
67
+ /** Cache directory (default: .cache/eve-resfile) */
68
+ cacheDir: ".cache/eve-resfile",
69
+ /** Project root for resolving cacheDir (Rollup only) */
70
+ root: process.cwd(),
71
+ });
72
+ ```
73
+
74
+ ## Core API
75
+
76
+ The main entry exports helpers for working with the resfile index outside of a bundler:
77
+
78
+ ```ts
79
+ import { loadResfileIndexData, lookupCdnPath, normalizeResPath } from "@eve-online-tools/eve-resfile";
80
+
81
+ const index = await loadResfileIndexData({
82
+ cacheDir: "/tmp/eve-resfile",
83
+ });
84
+
85
+ const cdnPath = lookupCdnPath(index.resPathToCdnPath, normalizeResPath("res:/icons/64/icon64.png"));
86
+ ```
87
+
88
+ ## Caching
89
+
90
+ On first run the plugin:
91
+
92
+ 1. Fetches the current client build number, unless `buildNumber` is set
93
+ 2. Downloads the build index and locates the resfile index
94
+ 3. Downloads the resfile index and writes a parsed JSON map to cache
95
+
96
+ Subsequent runs reuse cached files under `.cache/eve-resfile/<buildNumber>/`. Add this directory to `.gitignore`.
97
+
98
+ ## Exports
99
+
100
+ | Import | Description |
101
+ | --- | --- |
102
+ | `@eve-online-tools/eve-resfile` | Core index loader and parsing utilities |
103
+ | `@eve-online-tools/eve-resfile/vite` | Vite plugin |
104
+ | `@eve-online-tools/eve-resfile/rollup` | Rollup plugin |
105
+ | `@eve-online-tools/eve-resfile/client` | TypeScript types for `res:/` imports |
106
+
107
+ ## License
108
+
109
+ MIT
@@ -0,0 +1,63 @@
1
+ import {
2
+ DEFAULT_ASSET_ORIGIN,
3
+ DEFAULT_CACHE_DIR,
4
+ DEFAULT_INDEX_ORIGIN,
5
+ RES_IMPORT_PREFIX,
6
+ VIRTUAL_PREFIX,
7
+ __async,
8
+ devProxyUrl,
9
+ fetchBuffer,
10
+ lookupCdnPath,
11
+ normalizeResPath
12
+ } from "./chunk-YCJ3SYSF.js";
13
+
14
+ // src/plugin-core.ts
15
+ import { basename, isAbsolute, resolve } from "path";
16
+ var resolveEveResfileOptions = (options = {}, root) => {
17
+ var _a, _b, _c;
18
+ const cacheDir = (_a = options.cacheDir) != null ? _a : DEFAULT_CACHE_DIR;
19
+ return {
20
+ buildNumber: options.buildNumber,
21
+ indexOrigin: (_b = options.indexOrigin) != null ? _b : DEFAULT_INDEX_ORIGIN,
22
+ assetOrigin: (_c = options.assetOrigin) != null ? _c : DEFAULT_ASSET_ORIGIN,
23
+ cacheDir: isAbsolute(cacheDir) ? cacheDir : resolve(root, cacheDir)
24
+ };
25
+ };
26
+ var isVirtualResId = (id) => id.startsWith(VIRTUAL_PREFIX);
27
+ var virtualIdForResPath = (resPath) => `${VIRTUAL_PREFIX}${resPath}`;
28
+ var resPathFromVirtualId = (id) => id.slice(VIRTUAL_PREFIX.length);
29
+ var lookupOrThrow = (index, resPath) => {
30
+ const cdnPath = lookupCdnPath(index.resPathToCdnPath, resPath);
31
+ if (!cdnPath) {
32
+ throw new Error(`${resPath} not found in resfileindex (build ${index.buildNumber}).`);
33
+ }
34
+ return cdnPath;
35
+ };
36
+ var resolveResfileId = (source) => {
37
+ if (!source.startsWith(RES_IMPORT_PREFIX)) return null;
38
+ return virtualIdForResPath(normalizeResPath(source));
39
+ };
40
+ var loadResfileAssetModule = (_0) => __async(null, [_0], function* ({
41
+ watchMode,
42
+ assetOrigin,
43
+ index,
44
+ resPath,
45
+ emitAsset
46
+ }) {
47
+ const cdnPath = lookupOrThrow(index, resPath);
48
+ if (watchMode) {
49
+ return `export default ${JSON.stringify(devProxyUrl(resPath))}`;
50
+ }
51
+ const buffer = yield fetchBuffer(`${assetOrigin}/${cdnPath}`);
52
+ const referenceId = emitAsset(basename(resPath), buffer);
53
+ return `export default import.meta.ROLLUP_FILE_URL_${referenceId}`;
54
+ });
55
+
56
+ export {
57
+ resolveEveResfileOptions,
58
+ isVirtualResId,
59
+ resPathFromVirtualId,
60
+ lookupOrThrow,
61
+ resolveResfileId,
62
+ loadResfileAssetModule
63
+ };
@@ -0,0 +1,65 @@
1
+ import {
2
+ DEFAULT_ASSET_ORIGIN,
3
+ DEFAULT_CACHE_DIR,
4
+ DEFAULT_INDEX_ORIGIN,
5
+ RES_IMPORT_PREFIX,
6
+ VIRTUAL_PREFIX,
7
+ __async,
8
+ devProxyUrl,
9
+ fetchBuffer,
10
+ lookupCdnPath,
11
+ normalizeResPath
12
+ } from "./chunk-RP7V25VC.js";
13
+
14
+ // src/plugin-core.ts
15
+ import { basename, isAbsolute, resolve } from "path";
16
+ var resolveEveResfileOptions = (options = {}, root) => {
17
+ var _a, _b, _c;
18
+ const cacheDir = (_a = options.cacheDir) != null ? _a : DEFAULT_CACHE_DIR;
19
+ return {
20
+ buildNumber: options.buildNumber,
21
+ indexOrigin: (_b = options.indexOrigin) != null ? _b : DEFAULT_INDEX_ORIGIN,
22
+ assetOrigin: (_c = options.assetOrigin) != null ? _c : DEFAULT_ASSET_ORIGIN,
23
+ cacheDir: isAbsolute(cacheDir) ? cacheDir : resolve(root, cacheDir)
24
+ };
25
+ };
26
+ var isVirtualResId = (id) => id.startsWith(VIRTUAL_PREFIX);
27
+ var virtualIdForResPath = (resPath) => `${VIRTUAL_PREFIX}${resPath}`;
28
+ var resPathFromVirtualId = (id) => id.slice(VIRTUAL_PREFIX.length);
29
+ var lookupOrThrow = (index, resPath) => {
30
+ const cdnPath = lookupCdnPath(index.resPathToCdnPath, resPath);
31
+ if (!cdnPath) {
32
+ throw new Error(`${resPath} not found in resfileindex (build ${index.buildNumber}).`);
33
+ }
34
+ return cdnPath;
35
+ };
36
+ var resolveResfileId = (source) => {
37
+ if (!source.startsWith(RES_IMPORT_PREFIX)) {
38
+ return null;
39
+ }
40
+ return virtualIdForResPath(normalizeResPath(source));
41
+ };
42
+ var loadResfileAssetModule = (_0) => __async(null, [_0], function* ({
43
+ watchMode,
44
+ assetOrigin,
45
+ index,
46
+ resPath,
47
+ emitAsset
48
+ }) {
49
+ const cdnPath = lookupOrThrow(index, resPath);
50
+ if (watchMode) {
51
+ return `export default ${JSON.stringify(devProxyUrl(resPath))}`;
52
+ }
53
+ const buffer = yield fetchBuffer(`${assetOrigin}/${cdnPath}`);
54
+ const referenceId = emitAsset(basename(resPath), buffer);
55
+ return `export default import.meta.ROLLUP_FILE_URL_${referenceId}`;
56
+ });
57
+
58
+ export {
59
+ resolveEveResfileOptions,
60
+ isVirtualResId,
61
+ resPathFromVirtualId,
62
+ lookupOrThrow,
63
+ resolveResfileId,
64
+ loadResfileAssetModule
65
+ };
@@ -0,0 +1,229 @@
1
+ var __async = (__this, __arguments, generator) => {
2
+ return new Promise((resolve, reject) => {
3
+ var fulfilled = (value) => {
4
+ try {
5
+ step(generator.next(value));
6
+ } catch (e) {
7
+ reject(e);
8
+ }
9
+ };
10
+ var rejected = (value) => {
11
+ try {
12
+ step(generator.throw(value));
13
+ } catch (e) {
14
+ reject(e);
15
+ }
16
+ };
17
+ var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
18
+ step((generator = generator.apply(__this, __arguments)).next());
19
+ });
20
+ };
21
+
22
+ // src/constants.ts
23
+ var DEFAULT_INDEX_ORIGIN = "https://binaries.eveonline.com";
24
+ var DEFAULT_ASSET_ORIGIN = "https://resources.eveonline.com";
25
+ var DEFAULT_CACHE_DIR = ".cache/eve-resfile";
26
+ var EVE_CLIENT_MANIFEST = "eveclient_TQ.json";
27
+ var RESFILE_INDEX_PATH = "app:/resfileindex.txt";
28
+ var VIRTUAL_PREFIX = "\0res:";
29
+ var DEV_PROXY_PREFIX = "/__eve_res__/";
30
+ var RES_IMPORT_PREFIX = "res:/";
31
+
32
+ // src/parse.ts
33
+ var parseFirstTwoColumns = (line) => {
34
+ const commaIndex = line.indexOf(",");
35
+ if (commaIndex === -1) {
36
+ return null;
37
+ }
38
+ const path = line.slice(0, commaIndex);
39
+ const remainder = line.slice(commaIndex + 1);
40
+ const secondComma = remainder.indexOf(",");
41
+ const cdnPath = secondComma === -1 ? remainder : remainder.slice(0, secondComma);
42
+ if (!path || !cdnPath) {
43
+ return null;
44
+ }
45
+ return [path, cdnPath];
46
+ };
47
+ var parseResfileIndex = (content) => {
48
+ const map = /* @__PURE__ */ new Map();
49
+ for (const line of content.split("\n")) {
50
+ const trimmed = line.trim();
51
+ if (!trimmed) {
52
+ continue;
53
+ }
54
+ const parsed = parseFirstTwoColumns(trimmed);
55
+ if (!parsed) {
56
+ continue;
57
+ }
58
+ const [resPath, cdnPath] = parsed;
59
+ map.set(resPath, cdnPath);
60
+ }
61
+ return map;
62
+ };
63
+ var findResfileIndexCdnPath = (buildIndex) => {
64
+ for (const line of buildIndex.split("\n")) {
65
+ const trimmed = line.trim();
66
+ if (!trimmed) {
67
+ continue;
68
+ }
69
+ const parsed = parseFirstTwoColumns(trimmed);
70
+ if (!parsed) {
71
+ continue;
72
+ }
73
+ const [path, cdnPath] = parsed;
74
+ if (path === RESFILE_INDEX_PATH) {
75
+ return cdnPath;
76
+ }
77
+ }
78
+ throw new Error(`${RESFILE_INDEX_PATH} not found in build index.`);
79
+ };
80
+
81
+ // src/index-loader.ts
82
+ import { mkdir as mkdir2 } from "fs/promises";
83
+
84
+ // src/cache.ts
85
+ import { existsSync } from "fs";
86
+ import { mkdir, readFile, writeFile } from "fs/promises";
87
+ import { join } from "path";
88
+ var buildCacheDir = (cacheDir, buildNumber) => join(cacheDir, buildNumber);
89
+ var buildIndexPath = (cacheDir, buildNumber) => join(buildCacheDir(cacheDir, buildNumber), "build-index.txt");
90
+ var resfileIndexPath = (cacheDir, buildNumber) => join(buildCacheDir(cacheDir, buildNumber), "resfileindex.txt");
91
+ var resfileMapPath = (cacheDir, buildNumber) => join(buildCacheDir(cacheDir, buildNumber), "resfile-map.json");
92
+ var readCachedText = (path) => __async(null, null, function* () {
93
+ if (!existsSync(path)) {
94
+ return null;
95
+ }
96
+ return readFile(path, "utf8");
97
+ });
98
+ var writeCachedText = (path, content) => __async(null, null, function* () {
99
+ yield mkdir(join(path, ".."), { recursive: true });
100
+ yield writeFile(path, content);
101
+ });
102
+ var readCachedMap = (path) => __async(null, null, function* () {
103
+ if (!existsSync(path)) {
104
+ return null;
105
+ }
106
+ const parsed = JSON.parse(yield readFile(path, "utf8"));
107
+ return new Map(Object.entries(parsed));
108
+ });
109
+ var writeCachedMap = (path, map) => __async(null, null, function* () {
110
+ yield mkdir(join(path, ".."), { recursive: true });
111
+ yield writeFile(path, JSON.stringify(Object.fromEntries(map)));
112
+ });
113
+
114
+ // src/fetch.ts
115
+ var fetchText = (url) => __async(null, null, function* () {
116
+ const response = yield fetch(url);
117
+ if (!response.ok) {
118
+ throw new Error(`Failed to fetch ${url} (${response.status}).`);
119
+ }
120
+ return response.text();
121
+ });
122
+ var fetchBuffer = (url) => __async(null, null, function* () {
123
+ const response = yield fetch(url);
124
+ if (!response.ok) {
125
+ throw new Error(`Failed to fetch ${url} (${response.status}).`);
126
+ }
127
+ const arrayBuffer = yield response.arrayBuffer();
128
+ return Buffer.from(arrayBuffer);
129
+ });
130
+
131
+ // src/index-loader.ts
132
+ var resolveBuildNumber = (options) => __async(null, null, function* () {
133
+ if (options.buildNumber !== void 0) {
134
+ return String(options.buildNumber);
135
+ }
136
+ const manifestUrl = `${options.indexOrigin}/${EVE_CLIENT_MANIFEST}`;
137
+ const manifest = JSON.parse(yield fetchText(manifestUrl));
138
+ if (!manifest.buildNumber) {
139
+ throw new Error(`Missing buildNumber in ${manifestUrl}.`);
140
+ }
141
+ return manifest.buildNumber;
142
+ });
143
+ var loadBuildIndex = (options, buildNumber) => __async(null, null, function* () {
144
+ const cachedPath = buildIndexPath(options.cacheDir, buildNumber);
145
+ const cached = yield readCachedText(cachedPath);
146
+ if (cached) {
147
+ return cached;
148
+ }
149
+ const buildIndexUrl = `${options.indexOrigin}/eveonline_${buildNumber}.txt`;
150
+ const content = yield fetchText(buildIndexUrl);
151
+ yield writeCachedText(cachedPath, content);
152
+ return content;
153
+ });
154
+ var loadResfileIndex = (options, buildNumber, cdnPath) => __async(null, null, function* () {
155
+ const cachedPath = resfileIndexPath(options.cacheDir, buildNumber);
156
+ const cached = yield readCachedText(cachedPath);
157
+ if (cached) {
158
+ return cached;
159
+ }
160
+ const resfileIndexUrl = `${options.indexOrigin}/${cdnPath}`;
161
+ const content = yield fetchText(resfileIndexUrl);
162
+ yield writeCachedText(cachedPath, content);
163
+ return content;
164
+ });
165
+ var loadResfileIndexData = (options) => __async(null, null, function* () {
166
+ const { cacheDir } = options;
167
+ const buildNumber = yield resolveBuildNumber(options);
168
+ yield mkdir2(buildCacheDir(cacheDir, buildNumber), { recursive: true });
169
+ const cachedMap = yield readCachedMap(resfileMapPath(cacheDir, buildNumber));
170
+ if (cachedMap) {
171
+ return { buildNumber, resPathToCdnPath: cachedMap };
172
+ }
173
+ const buildIndex = yield loadBuildIndex(options, buildNumber);
174
+ const resfileIndexCdnPath = findResfileIndexCdnPath(buildIndex);
175
+ const resfileIndex = yield loadResfileIndex(options, buildNumber, resfileIndexCdnPath);
176
+ const resPathToCdnPath = parseResfileIndex(resfileIndex);
177
+ yield writeCachedMap(resfileMapPath(cacheDir, buildNumber), resPathToCdnPath);
178
+ return { buildNumber, resPathToCdnPath };
179
+ });
180
+
181
+ // src/lookup.ts
182
+ var normalizeResPath = (source) => {
183
+ var _a;
184
+ const withoutQuery = (_a = source.split(/[?#]/)[0]) != null ? _a : source;
185
+ if (withoutQuery.startsWith(RES_IMPORT_PREFIX)) {
186
+ return withoutQuery;
187
+ }
188
+ if (withoutQuery.startsWith("res:")) {
189
+ const path = withoutQuery.slice("res:".length);
190
+ return path.startsWith("/") ? `res:${path}` : `res:/${path}`;
191
+ }
192
+ throw new Error(`Invalid res import "${source}". Expected a path starting with "res:/".`);
193
+ };
194
+ var lookupCdnPath = (index, resPath) => index.get(resPath);
195
+ var devProxyUrl = (resPath) => {
196
+ const encoded = encodeURIComponent(resPath.slice(RES_IMPORT_PREFIX.length));
197
+ return `/__eve_res__/${encoded}`;
198
+ };
199
+ var resPathFromDevProxyUrl = (pathname) => {
200
+ if (!pathname.startsWith("/__eve_res__/")) {
201
+ return null;
202
+ }
203
+ const encoded = pathname.slice("/__eve_res__/".length);
204
+ if (!encoded) {
205
+ return null;
206
+ }
207
+ return `${RES_IMPORT_PREFIX}${decodeURIComponent(encoded)}`;
208
+ };
209
+
210
+ export {
211
+ __async,
212
+ DEFAULT_INDEX_ORIGIN,
213
+ DEFAULT_ASSET_ORIGIN,
214
+ DEFAULT_CACHE_DIR,
215
+ EVE_CLIENT_MANIFEST,
216
+ RESFILE_INDEX_PATH,
217
+ VIRTUAL_PREFIX,
218
+ DEV_PROXY_PREFIX,
219
+ RES_IMPORT_PREFIX,
220
+ fetchBuffer,
221
+ parseFirstTwoColumns,
222
+ parseResfileIndex,
223
+ findResfileIndexCdnPath,
224
+ loadResfileIndexData,
225
+ normalizeResPath,
226
+ lookupCdnPath,
227
+ devProxyUrl,
228
+ resPathFromDevProxyUrl
229
+ };
@@ -0,0 +1,203 @@
1
+ var __async = (__this, __arguments, generator) => {
2
+ return new Promise((resolve, reject) => {
3
+ var fulfilled = (value) => {
4
+ try {
5
+ step(generator.next(value));
6
+ } catch (e) {
7
+ reject(e);
8
+ }
9
+ };
10
+ var rejected = (value) => {
11
+ try {
12
+ step(generator.throw(value));
13
+ } catch (e) {
14
+ reject(e);
15
+ }
16
+ };
17
+ var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
18
+ step((generator = generator.apply(__this, __arguments)).next());
19
+ });
20
+ };
21
+
22
+ // src/constants.ts
23
+ var DEFAULT_INDEX_ORIGIN = "https://binaries.eveonline.com";
24
+ var DEFAULT_ASSET_ORIGIN = "https://resources.eveonline.com";
25
+ var DEFAULT_CACHE_DIR = ".cache/eve-resfile";
26
+ var EVE_CLIENT_MANIFEST = "eveclient_TQ.json";
27
+ var RESFILE_INDEX_PATH = "app:/resfileindex.txt";
28
+ var VIRTUAL_PREFIX = "\0res:";
29
+ var DEV_PROXY_PREFIX = "/__eve_res__/";
30
+ var RES_IMPORT_PREFIX = "res:/";
31
+
32
+ // src/parse.ts
33
+ var parseFirstTwoColumns = (line) => {
34
+ const commaIndex = line.indexOf(",");
35
+ if (commaIndex === -1) return null;
36
+ const path = line.slice(0, commaIndex);
37
+ const remainder = line.slice(commaIndex + 1);
38
+ const secondComma = remainder.indexOf(",");
39
+ const cdnPath = secondComma === -1 ? remainder : remainder.slice(0, secondComma);
40
+ if (!path || !cdnPath) return null;
41
+ return [path, cdnPath];
42
+ };
43
+ var parseResfileIndex = (content) => {
44
+ const map = /* @__PURE__ */ new Map();
45
+ for (const line of content.split("\n")) {
46
+ const trimmed = line.trim();
47
+ if (!trimmed) continue;
48
+ const parsed = parseFirstTwoColumns(trimmed);
49
+ if (!parsed) continue;
50
+ const [resPath, cdnPath] = parsed;
51
+ map.set(resPath, cdnPath);
52
+ }
53
+ return map;
54
+ };
55
+ var findResfileIndexCdnPath = (buildIndex) => {
56
+ for (const line of buildIndex.split("\n")) {
57
+ const trimmed = line.trim();
58
+ if (!trimmed) continue;
59
+ const parsed = parseFirstTwoColumns(trimmed);
60
+ if (!parsed) continue;
61
+ const [path, cdnPath] = parsed;
62
+ if (path === RESFILE_INDEX_PATH) return cdnPath;
63
+ }
64
+ throw new Error(`${RESFILE_INDEX_PATH} not found in build index.`);
65
+ };
66
+
67
+ // src/index-loader.ts
68
+ import { mkdir as mkdir2 } from "fs/promises";
69
+
70
+ // src/cache.ts
71
+ import { existsSync } from "fs";
72
+ import { mkdir, readFile, writeFile } from "fs/promises";
73
+ import { join } from "path";
74
+ var buildCacheDir = (cacheDir, buildNumber) => join(cacheDir, buildNumber);
75
+ var buildIndexPath = (cacheDir, buildNumber) => join(buildCacheDir(cacheDir, buildNumber), "build-index.txt");
76
+ var resfileIndexPath = (cacheDir, buildNumber) => join(buildCacheDir(cacheDir, buildNumber), "resfileindex.txt");
77
+ var resfileMapPath = (cacheDir, buildNumber) => join(buildCacheDir(cacheDir, buildNumber), "resfile-map.json");
78
+ var readCachedText = (path) => __async(null, null, function* () {
79
+ if (!existsSync(path)) return null;
80
+ return readFile(path, "utf8");
81
+ });
82
+ var writeCachedText = (path, content) => __async(null, null, function* () {
83
+ yield mkdir(join(path, ".."), { recursive: true });
84
+ yield writeFile(path, content);
85
+ });
86
+ var readCachedMap = (path) => __async(null, null, function* () {
87
+ if (!existsSync(path)) return null;
88
+ const parsed = JSON.parse(yield readFile(path, "utf8"));
89
+ return new Map(Object.entries(parsed));
90
+ });
91
+ var writeCachedMap = (path, map) => __async(null, null, function* () {
92
+ yield mkdir(join(path, ".."), { recursive: true });
93
+ yield writeFile(path, JSON.stringify(Object.fromEntries(map)));
94
+ });
95
+
96
+ // src/fetch.ts
97
+ var fetchText = (url) => __async(null, null, function* () {
98
+ const response = yield fetch(url);
99
+ if (!response.ok) {
100
+ throw new Error(`Failed to fetch ${url} (${response.status}).`);
101
+ }
102
+ return response.text();
103
+ });
104
+ var fetchBuffer = (url) => __async(null, null, function* () {
105
+ const response = yield fetch(url);
106
+ if (!response.ok) {
107
+ throw new Error(`Failed to fetch ${url} (${response.status}).`);
108
+ }
109
+ const arrayBuffer = yield response.arrayBuffer();
110
+ return Buffer.from(arrayBuffer);
111
+ });
112
+
113
+ // src/index-loader.ts
114
+ var resolveBuildNumber = (options) => __async(null, null, function* () {
115
+ if (options.buildNumber !== void 0) {
116
+ return String(options.buildNumber);
117
+ }
118
+ const manifestUrl = `${options.indexOrigin}/${EVE_CLIENT_MANIFEST}`;
119
+ const manifest = JSON.parse(yield fetchText(manifestUrl));
120
+ if (!manifest.buildNumber) {
121
+ throw new Error(`Missing buildNumber in ${manifestUrl}.`);
122
+ }
123
+ return manifest.buildNumber;
124
+ });
125
+ var loadBuildIndex = (options, buildNumber) => __async(null, null, function* () {
126
+ const cachedPath = buildIndexPath(options.cacheDir, buildNumber);
127
+ const cached = yield readCachedText(cachedPath);
128
+ if (cached) return cached;
129
+ const buildIndexUrl = `${options.indexOrigin}/eveonline_${buildNumber}.txt`;
130
+ const content = yield fetchText(buildIndexUrl);
131
+ yield writeCachedText(cachedPath, content);
132
+ return content;
133
+ });
134
+ var loadResfileIndex = (options, buildNumber, cdnPath) => __async(null, null, function* () {
135
+ const cachedPath = resfileIndexPath(options.cacheDir, buildNumber);
136
+ const cached = yield readCachedText(cachedPath);
137
+ if (cached) return cached;
138
+ const resfileIndexUrl = `${options.indexOrigin}/${cdnPath}`;
139
+ const content = yield fetchText(resfileIndexUrl);
140
+ yield writeCachedText(cachedPath, content);
141
+ return content;
142
+ });
143
+ var loadResfileIndexData = (options) => __async(null, null, function* () {
144
+ const { cacheDir } = options;
145
+ const buildNumber = yield resolveBuildNumber(options);
146
+ yield mkdir2(buildCacheDir(cacheDir, buildNumber), { recursive: true });
147
+ const cachedMap = yield readCachedMap(resfileMapPath(cacheDir, buildNumber));
148
+ if (cachedMap) {
149
+ return { buildNumber, resPathToCdnPath: cachedMap };
150
+ }
151
+ const buildIndex = yield loadBuildIndex(options, buildNumber);
152
+ const resfileIndexCdnPath = findResfileIndexCdnPath(buildIndex);
153
+ const resfileIndex = yield loadResfileIndex(options, buildNumber, resfileIndexCdnPath);
154
+ const resPathToCdnPath = parseResfileIndex(resfileIndex);
155
+ yield writeCachedMap(resfileMapPath(cacheDir, buildNumber), resPathToCdnPath);
156
+ return { buildNumber, resPathToCdnPath };
157
+ });
158
+
159
+ // src/lookup.ts
160
+ var normalizeResPath = (source) => {
161
+ var _a;
162
+ const withoutQuery = (_a = source.split(/[?#]/)[0]) != null ? _a : source;
163
+ if (withoutQuery.startsWith(RES_IMPORT_PREFIX)) {
164
+ return withoutQuery;
165
+ }
166
+ if (withoutQuery.startsWith("res:")) {
167
+ const path = withoutQuery.slice("res:".length);
168
+ return path.startsWith("/") ? `res:${path}` : `res:/${path}`;
169
+ }
170
+ throw new Error(`Invalid res import "${source}". Expected a path starting with "res:/".`);
171
+ };
172
+ var lookupCdnPath = (index, resPath) => index.get(resPath);
173
+ var devProxyUrl = (resPath) => {
174
+ const encoded = encodeURIComponent(resPath.slice(RES_IMPORT_PREFIX.length));
175
+ return `/__eve_res__/${encoded}`;
176
+ };
177
+ var resPathFromDevProxyUrl = (pathname) => {
178
+ if (!pathname.startsWith("/__eve_res__/")) return null;
179
+ const encoded = pathname.slice("/__eve_res__/".length);
180
+ if (!encoded) return null;
181
+ return `${RES_IMPORT_PREFIX}${decodeURIComponent(encoded)}`;
182
+ };
183
+
184
+ export {
185
+ __async,
186
+ DEFAULT_INDEX_ORIGIN,
187
+ DEFAULT_ASSET_ORIGIN,
188
+ DEFAULT_CACHE_DIR,
189
+ EVE_CLIENT_MANIFEST,
190
+ RESFILE_INDEX_PATH,
191
+ VIRTUAL_PREFIX,
192
+ DEV_PROXY_PREFIX,
193
+ RES_IMPORT_PREFIX,
194
+ fetchBuffer,
195
+ parseFirstTwoColumns,
196
+ parseResfileIndex,
197
+ findResfileIndexCdnPath,
198
+ loadResfileIndexData,
199
+ normalizeResPath,
200
+ lookupCdnPath,
201
+ devProxyUrl,
202
+ resPathFromDevProxyUrl
203
+ };