@emdzej/bimmerz-vfs 0.1.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.
@@ -0,0 +1,170 @@
1
+ /**
2
+ * End-to-end tests — HttpDirectory against a real in-process HTTP server.
3
+ * The fixture is defined as a nested JS object; index.json files are
4
+ * generated automatically for every level, matching the format written
5
+ * by `bimmerz data index`.
6
+ */
7
+ import { createServer } from 'node:http';
8
+ import { afterAll, beforeAll, describe, expect, it } from 'vitest';
9
+ import { HttpDirectory } from './http.js';
10
+ import { drillPath, listFiles } from './utils.js';
11
+ const MS43_IPO = new Uint8Array([0x49, 0x50, 0x4f, 0x01, 0x02]);
12
+ const RADIO_IPO = new Uint8Array([0x52, 0x41, 0x44, 0x03]);
13
+ const MS43_PRG = new Uint8Array([0x50, 0x52, 0x47, 0x04]);
14
+ const INPA_INI = '[INPA]\nPath=C:\\EC-APPS\\INPA\n';
15
+ const EDIABAS_INI = '[EDIABAS]\nInterface=STD:OBD\n';
16
+ const FIXTURE = {
17
+ 'INPA.INI': INPA_INI,
18
+ 'EC-APPS': {
19
+ INPA: {
20
+ CFGDAT: {
21
+ 'MS43.IPO': MS43_IPO,
22
+ 'INPA.INI': INPA_INI,
23
+ 'E46.ENG': 'E46\nMOTOR\n',
24
+ },
25
+ SGDAT: {
26
+ 'RADIO.IPO': RADIO_IPO,
27
+ },
28
+ },
29
+ },
30
+ EDIABAS: {
31
+ Ecu: {
32
+ 'MS43.prg': MS43_PRG,
33
+ },
34
+ Bin: {
35
+ 'EDIABAS.INI': EDIABAS_INI,
36
+ },
37
+ },
38
+ };
39
+ function buildRoutes(tree, prefix = '') {
40
+ const routes = new Map();
41
+ const indexEntries = [];
42
+ for (const [name, value] of Object.entries(tree)) {
43
+ const path = prefix ? `${prefix}/${name}` : name;
44
+ if (value instanceof Uint8Array || typeof value === 'string') {
45
+ const buf = typeof value === 'string' ? Buffer.from(value, 'utf-8') : Buffer.from(value);
46
+ routes.set(path, buf);
47
+ const dotIdx = name.lastIndexOf('.');
48
+ const baseName = dotIdx > 0 ? name.slice(0, dotIdx) : name;
49
+ indexEntries.push({
50
+ type: 'file',
51
+ name: baseName.toLowerCase(),
52
+ fullName: name.toLowerCase(),
53
+ originalName: baseName,
54
+ originalFullName: name,
55
+ size: buf.length,
56
+ });
57
+ }
58
+ else {
59
+ for (const [sub, subBuf] of buildRoutes(value, path)) {
60
+ routes.set(sub, subBuf);
61
+ }
62
+ indexEntries.push({
63
+ type: 'dir',
64
+ name: name.toLowerCase(),
65
+ fullName: name.toLowerCase(),
66
+ originalName: name,
67
+ originalFullName: name,
68
+ size: 0,
69
+ });
70
+ }
71
+ }
72
+ const indexPath = prefix ? `${prefix}/index.json` : 'index.json';
73
+ routes.set(indexPath, Buffer.from(JSON.stringify(indexEntries, null, 2), 'utf-8'));
74
+ return routes;
75
+ }
76
+ // ---------------------------------------------------------------------------
77
+ // HTTP server lifecycle
78
+ // ---------------------------------------------------------------------------
79
+ let server;
80
+ let baseUrl;
81
+ beforeAll(async () => {
82
+ const routes = buildRoutes(FIXTURE);
83
+ await new Promise((resolve) => {
84
+ server = createServer((req, res) => {
85
+ const rawPath = decodeURIComponent((req.url ?? '/').replace(/^\//, ''));
86
+ const buf = routes.get(rawPath);
87
+ if (!buf) {
88
+ res.writeHead(404, { 'Content-Type': 'text/plain' });
89
+ res.end('Not Found');
90
+ return;
91
+ }
92
+ res.writeHead(200, {
93
+ 'Content-Type': rawPath.endsWith('.json') ? 'application/json' : 'application/octet-stream',
94
+ 'Content-Length': buf.length,
95
+ });
96
+ res.end(buf);
97
+ });
98
+ server.listen(0, '127.0.0.1', () => {
99
+ const { port } = server.address();
100
+ baseUrl = `http://127.0.0.1:${port}`;
101
+ resolve();
102
+ });
103
+ });
104
+ });
105
+ afterAll(() => {
106
+ server?.close();
107
+ });
108
+ // ---------------------------------------------------------------------------
109
+ // Tests
110
+ // ---------------------------------------------------------------------------
111
+ function root() {
112
+ return new HttpDirectory(baseUrl);
113
+ }
114
+ describe('HttpDirectory E2E', () => {
115
+ it('reads root-level entries from index.json', async () => {
116
+ const entries = await root().entries();
117
+ const names = entries.map((e) => e.name).sort();
118
+ expect(names).toEqual(['EDIABAS', 'EC-APPS', 'INPA.INI'].sort());
119
+ });
120
+ it('navigates a multi-level path', async () => {
121
+ const cfgdat = await drillPath(root(), 'EC-APPS', 'INPA', 'CFGDAT');
122
+ expect(cfgdat?.name).toBe('CFGDAT');
123
+ });
124
+ it('is case-insensitive at every path segment', async () => {
125
+ const cfgdat = await drillPath(root(), 'ec-apps', 'inpa', 'cfgdat');
126
+ expect(cfgdat?.name).toBe('CFGDAT');
127
+ });
128
+ it('reads a text file', async () => {
129
+ const f = await root().file('INPA.INI');
130
+ expect(f).not.toBeNull();
131
+ const buf = await f.arrayBuffer();
132
+ const text = new TextDecoder().decode(buf);
133
+ expect(text).toBe(INPA_INI);
134
+ });
135
+ it('reads a binary file', async () => {
136
+ const ecu = await drillPath(root(), 'EDIABAS', 'Ecu');
137
+ const f = await ecu.file('MS43.prg');
138
+ expect(f).not.toBeNull();
139
+ const buf = await f.arrayBuffer();
140
+ expect(new Uint8Array(buf)).toEqual(MS43_PRG);
141
+ });
142
+ it('resolves a file case-insensitively', async () => {
143
+ const cfgdat = await drillPath(root(), 'EC-APPS', 'INPA', 'CFGDAT');
144
+ const f = await cfgdat.file('ms43.ipo');
145
+ expect(f?.name).toBe('MS43.IPO');
146
+ const buf = await f.arrayBuffer();
147
+ expect(new Uint8Array(buf)).toEqual(MS43_IPO);
148
+ });
149
+ it('returns null for a missing file', async () => {
150
+ expect(await root().file('ghost.ipo')).toBeNull();
151
+ });
152
+ it('returns null for a missing directory', async () => {
153
+ expect(await root().dir('missing')).toBeNull();
154
+ });
155
+ it('lists only IPO files in CFGDAT', async () => {
156
+ const cfgdat = await drillPath(root(), 'EC-APPS', 'INPA', 'CFGDAT');
157
+ const ipos = await listFiles(cfgdat, '.ipo');
158
+ expect(ipos.map((f) => f.name)).toEqual(['MS43.IPO']);
159
+ });
160
+ it('reports correct file sizes', async () => {
161
+ const cfgdat = await drillPath(root(), 'EC-APPS', 'INPA', 'CFGDAT');
162
+ const f = await cfgdat.file('MS43.IPO');
163
+ expect(f?.size).toBe(MS43_IPO.length);
164
+ });
165
+ it('index.json is not listed as an entry', async () => {
166
+ const entries = await root().entries();
167
+ expect(entries.find((e) => e.name === 'index.json')).toBeUndefined();
168
+ });
169
+ });
170
+ //# sourceMappingURL=http.e2e.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.e2e.test.js","sourceRoot":"","sources":["../src/http.e2e.test.ts"],"names":[],"mappings":"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"}
package/dist/http.js ADDED
@@ -0,0 +1,76 @@
1
+ export class HttpFile {
2
+ name;
3
+ size;
4
+ #url;
5
+ #fetch;
6
+ constructor(name, size, url, fetchFn) {
7
+ this.name = name;
8
+ this.size = size;
9
+ this.#url = url;
10
+ this.#fetch = fetchFn;
11
+ }
12
+ async arrayBuffer() {
13
+ const res = await this.#fetch(this.#url);
14
+ if (!res.ok)
15
+ throw new Error(`HTTP ${res.status} ${res.statusText}: ${this.#url}`);
16
+ return res.arrayBuffer();
17
+ }
18
+ }
19
+ export class HttpDirectory {
20
+ name;
21
+ #baseUrl;
22
+ #indexFile;
23
+ #fetch;
24
+ // Lazily populated on first access; one fetch per directory instance.
25
+ #index = null;
26
+ constructor(baseUrl, options = {}) {
27
+ // Normalise: no trailing slash so URL joins are predictable.
28
+ this.#baseUrl = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl;
29
+ this.#indexFile = options.indexFile ?? 'index.json';
30
+ this.#fetch = options.fetch ?? globalThis.fetch.bind(globalThis);
31
+ // Use the last non-empty URL segment as the directory name.
32
+ this.name = this.#baseUrl.split('/').filter(Boolean).at(-1) ?? '';
33
+ }
34
+ async #loadIndex() {
35
+ if (this.#index !== null)
36
+ return this.#index;
37
+ const url = `${this.#baseUrl}/${this.#indexFile}`;
38
+ const res = await this.#fetch(url);
39
+ if (!res.ok)
40
+ throw new Error(`HTTP ${res.status} ${res.statusText}: ${url}`);
41
+ this.#index = (await res.json());
42
+ return this.#index;
43
+ }
44
+ async file(name) {
45
+ const index = await this.#loadIndex();
46
+ const target = name.toLowerCase();
47
+ // Both 'file' and 'link' entries are readable resources.
48
+ const entry = index.find((e) => (e.type === 'file' || e.type === 'link') && e.fullName === target);
49
+ if (!entry)
50
+ return null;
51
+ const url = `${this.#baseUrl}/${entry.originalFullName}`;
52
+ return new HttpFile(entry.originalFullName, entry.size, url, this.#fetch);
53
+ }
54
+ async dir(name) {
55
+ const index = await this.#loadIndex();
56
+ const target = name.toLowerCase();
57
+ const entry = index.find((e) => e.type === 'dir' && e.fullName === target);
58
+ if (!entry)
59
+ return null;
60
+ return new HttpDirectory(`${this.#baseUrl}/${entry.originalFullName}`, { indexFile: this.#indexFile, fetch: this.#fetch });
61
+ }
62
+ async entries() {
63
+ const index = await this.#loadIndex();
64
+ const result = [];
65
+ for (const entry of index) {
66
+ if (entry.type === 'file' || entry.type === 'link') {
67
+ result.push({ kind: 'file', name: entry.originalFullName, size: entry.size });
68
+ }
69
+ else if (entry.type === 'dir') {
70
+ result.push({ kind: 'dir', name: entry.originalFullName });
71
+ }
72
+ }
73
+ return result;
74
+ }
75
+ }
76
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=http.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.test.d.ts","sourceRoot":"","sources":["../src/http.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,220 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { HttpDirectory, HttpFile } from './http.js';
3
+ function makeFetch(routes, spy) {
4
+ return async (input) => {
5
+ const url = input.toString();
6
+ if (spy)
7
+ spy.calls.push(url);
8
+ const value = routes[url];
9
+ if (value === undefined) {
10
+ return new Response(null, { status: 404, statusText: 'Not Found' });
11
+ }
12
+ return new Response(value, { status: 200 });
13
+ };
14
+ }
15
+ // Minimal index.json fixture used by most HttpDirectory tests.
16
+ const BASE = 'http://test.local/install';
17
+ const INDEX = {
18
+ [`${BASE}/index.json`]: JSON.stringify([
19
+ {
20
+ type: 'file',
21
+ name: 'ms43',
22
+ fullName: 'ms43.ipo',
23
+ originalName: 'MS43',
24
+ originalFullName: 'MS43.IPO',
25
+ size: 512,
26
+ },
27
+ {
28
+ type: 'link',
29
+ name: 'readme',
30
+ fullName: 'readme.txt',
31
+ originalName: 'README',
32
+ originalFullName: 'README.TXT',
33
+ size: 64,
34
+ },
35
+ {
36
+ type: 'dir',
37
+ name: 'ediabas',
38
+ fullName: 'ediabas',
39
+ originalName: 'EDIABAS',
40
+ originalFullName: 'EDIABAS',
41
+ size: 0,
42
+ },
43
+ {
44
+ type: 'dir',
45
+ name: 'ec-apps',
46
+ fullName: 'ec-apps',
47
+ originalName: 'EC-APPS',
48
+ originalFullName: 'EC-APPS',
49
+ size: 0,
50
+ },
51
+ ]),
52
+ [`${BASE}/MS43.IPO`]: new Uint8Array([0x49, 0x50, 0x4f]),
53
+ [`${BASE}/README.TXT`]: 'hello',
54
+ };
55
+ // ---------------------------------------------------------------------------
56
+ // HttpFile
57
+ // ---------------------------------------------------------------------------
58
+ describe('HttpFile', () => {
59
+ it('exposes name and size', () => {
60
+ const f = new HttpFile('MS43.IPO', 512, `${BASE}/MS43.IPO`, makeFetch(INDEX));
61
+ expect(f.name).toBe('MS43.IPO');
62
+ expect(f.size).toBe(512);
63
+ });
64
+ it('arrayBuffer() fetches the URL and returns the body', async () => {
65
+ const f = new HttpFile('MS43.IPO', 512, `${BASE}/MS43.IPO`, makeFetch(INDEX));
66
+ const buf = await f.arrayBuffer();
67
+ expect(new Uint8Array(buf)).toEqual(new Uint8Array([0x49, 0x50, 0x4f]));
68
+ });
69
+ it('arrayBuffer() throws on a non-200 response', async () => {
70
+ const f = new HttpFile('ghost.ipo', 0, `${BASE}/ghost.ipo`, makeFetch(INDEX));
71
+ await expect(f.arrayBuffer()).rejects.toThrow('404');
72
+ });
73
+ });
74
+ // ---------------------------------------------------------------------------
75
+ // HttpDirectory — entries()
76
+ // ---------------------------------------------------------------------------
77
+ describe('HttpDirectory.entries()', () => {
78
+ it('returns file and dir entries from index.json', async () => {
79
+ const dir = new HttpDirectory(BASE, { fetch: makeFetch(INDEX) });
80
+ const entries = await dir.entries();
81
+ expect(entries).toHaveLength(4);
82
+ });
83
+ it('maps file entries with kind "file" and original-cased name + size', async () => {
84
+ const dir = new HttpDirectory(BASE, { fetch: makeFetch(INDEX) });
85
+ const entries = await dir.entries();
86
+ const file = entries.find((e) => e.name === 'MS43.IPO');
87
+ expect(file).toEqual({ kind: 'file', name: 'MS43.IPO', size: 512 });
88
+ });
89
+ it('maps dir entries with kind "dir" and original-cased name', async () => {
90
+ const dir = new HttpDirectory(BASE, { fetch: makeFetch(INDEX) });
91
+ const entries = await dir.entries();
92
+ const d = entries.find((e) => e.name === 'EDIABAS');
93
+ expect(d).toEqual({ kind: 'dir', name: 'EDIABAS' });
94
+ });
95
+ it('treats "link" type entries as "file" kind', async () => {
96
+ const dir = new HttpDirectory(BASE, { fetch: makeFetch(INDEX) });
97
+ const entries = await dir.entries();
98
+ const link = entries.find((e) => e.name === 'README.TXT');
99
+ expect(link?.kind).toBe('file');
100
+ });
101
+ it('fetches index.json only once across multiple accesses', async () => {
102
+ const spy = { calls: [] };
103
+ const dir = new HttpDirectory(BASE, { fetch: makeFetch(INDEX, spy) });
104
+ await dir.entries();
105
+ await dir.entries();
106
+ await dir.file('MS43.IPO');
107
+ const indexFetches = spy.calls.filter((u) => u.endsWith('index.json'));
108
+ expect(indexFetches).toHaveLength(1);
109
+ });
110
+ });
111
+ // ---------------------------------------------------------------------------
112
+ // HttpDirectory — file()
113
+ // ---------------------------------------------------------------------------
114
+ describe('HttpDirectory.file()', () => {
115
+ it('returns null when name is not in the index', async () => {
116
+ const dir = new HttpDirectory(BASE, { fetch: makeFetch(INDEX) });
117
+ expect(await dir.file('ghost.ipo')).toBeNull();
118
+ });
119
+ it('finds a file by exact lowercased name', async () => {
120
+ const dir = new HttpDirectory(BASE, { fetch: makeFetch(INDEX) });
121
+ const f = await dir.file('ms43.ipo');
122
+ expect(f?.name).toBe('MS43.IPO');
123
+ });
124
+ it('finds a file case-insensitively', async () => {
125
+ const dir = new HttpDirectory(BASE, { fetch: makeFetch(INDEX) });
126
+ const f = await dir.file('MS43.IPO');
127
+ expect(f?.name).toBe('MS43.IPO');
128
+ });
129
+ it('finds a link entry as a file', async () => {
130
+ const dir = new HttpDirectory(BASE, { fetch: makeFetch(INDEX) });
131
+ const f = await dir.file('readme.txt');
132
+ expect(f?.name).toBe('README.TXT');
133
+ });
134
+ it('returned file fetches from original-cased URL', async () => {
135
+ const spy = { calls: [] };
136
+ const dir = new HttpDirectory(BASE, { fetch: makeFetch(INDEX, spy) });
137
+ const f = await dir.file('ms43.ipo');
138
+ await f.arrayBuffer();
139
+ expect(spy.calls).toContain(`${BASE}/MS43.IPO`);
140
+ });
141
+ it('returned file has the correct size', async () => {
142
+ const dir = new HttpDirectory(BASE, { fetch: makeFetch(INDEX) });
143
+ const f = await dir.file('MS43.IPO');
144
+ expect(f?.size).toBe(512);
145
+ });
146
+ });
147
+ // ---------------------------------------------------------------------------
148
+ // HttpDirectory — dir()
149
+ // ---------------------------------------------------------------------------
150
+ describe('HttpDirectory.dir()', () => {
151
+ it('returns null when name is not in the index', async () => {
152
+ const dir = new HttpDirectory(BASE, { fetch: makeFetch(INDEX) });
153
+ expect(await dir.dir('missing')).toBeNull();
154
+ });
155
+ it('finds a directory case-insensitively', async () => {
156
+ const dir = new HttpDirectory(BASE, { fetch: makeFetch(INDEX) });
157
+ const child = await dir.dir('ediabas');
158
+ expect(child?.name).toBe('EDIABAS');
159
+ });
160
+ it('child directory base URL uses original-cased name', async () => {
161
+ const spy = { calls: [] };
162
+ const routes = {
163
+ ...INDEX,
164
+ [`${BASE}/EDIABAS/index.json`]: JSON.stringify([]),
165
+ };
166
+ const dir = new HttpDirectory(BASE, { fetch: makeFetch(routes, spy) });
167
+ const child = await dir.dir('ediabas');
168
+ await child.entries();
169
+ expect(spy.calls).toContain(`${BASE}/EDIABAS/index.json`);
170
+ });
171
+ it('child inherits the custom indexFile option', async () => {
172
+ const spy = { calls: [] };
173
+ const custom = 'dir.json';
174
+ const routes = {
175
+ [`${BASE}/${custom}`]: JSON.stringify([
176
+ {
177
+ type: 'dir',
178
+ name: 'ediabas',
179
+ fullName: 'ediabas',
180
+ originalName: 'EDIABAS',
181
+ originalFullName: 'EDIABAS',
182
+ size: 0,
183
+ },
184
+ ]),
185
+ [`${BASE}/EDIABAS/${custom}`]: JSON.stringify([]),
186
+ };
187
+ const dir = new HttpDirectory(BASE, { indexFile: custom, fetch: makeFetch(routes, spy) });
188
+ const child = await dir.dir('EDIABAS');
189
+ await child.entries();
190
+ expect(spy.calls).toContain(`${BASE}/EDIABAS/${custom}`);
191
+ });
192
+ it('child inherits the custom fetch function', async () => {
193
+ const spy = { calls: [] };
194
+ const routes = {
195
+ ...INDEX,
196
+ [`${BASE}/EDIABAS/index.json`]: JSON.stringify([]),
197
+ };
198
+ const dir = new HttpDirectory(BASE, { fetch: makeFetch(routes, spy) });
199
+ const child = await dir.dir('EDIABAS');
200
+ await child.entries();
201
+ // Both the parent index and child index went through the same fetch spy
202
+ expect(spy.calls.filter((u) => u.includes('EDIABAS'))).not.toHaveLength(0);
203
+ });
204
+ });
205
+ // ---------------------------------------------------------------------------
206
+ // HttpDirectory — constructor
207
+ // ---------------------------------------------------------------------------
208
+ describe('HttpDirectory constructor', () => {
209
+ it('name is the last URL segment', () => {
210
+ expect(new HttpDirectory('http://x.com/a/b/c').name).toBe('c');
211
+ });
212
+ it('strips a trailing slash from the base URL', async () => {
213
+ const spy = { calls: [] };
214
+ const routes = { [`${BASE}/index.json`]: JSON.stringify([]) };
215
+ const dir = new HttpDirectory(BASE + '/', { fetch: makeFetch(routes, spy) });
216
+ await dir.entries();
217
+ expect(spy.calls[0]).toBe(`${BASE}/index.json`);
218
+ });
219
+ });
220
+ //# sourceMappingURL=http.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.test.js","sourceRoot":"","sources":["../src/http.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export type { VirtualFile, VirtualDirectory, VirtualEntry } from './types.js';
2
+ export { FsaFile, FsaDirectory } from './fsa.js';
3
+ export { HttpFile, HttpDirectory, type HttpDirectoryOptions } from './http.js';
4
+ export { drillPath, listFiles } from './utils.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,WAAW,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC9E,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,KAAK,oBAAoB,EAAE,MAAM,WAAW,CAAC;AAC/E,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export { FsaFile, FsaDirectory } from './fsa.js';
2
+ export { HttpFile, HttpDirectory } from './http.js';
3
+ export { drillPath, listFiles } from './utils.js';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,aAAa,EAA6B,MAAM,WAAW,CAAC;AAC/E,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,37 @@
1
+ /** A readable file in the virtual FS. */
2
+ export interface VirtualFile {
3
+ /** Original-cased full basename, e.g. `"MS43.IPO"`. */
4
+ readonly name: string;
5
+ /** Size in bytes. */
6
+ readonly size: number;
7
+ arrayBuffer(): Promise<ArrayBuffer>;
8
+ }
9
+ /** A read-only directory in the virtual FS. */
10
+ export interface VirtualDirectory {
11
+ /** Original-cased directory name. */
12
+ readonly name: string;
13
+ /**
14
+ * Case-insensitive file lookup.
15
+ * Returns `null` when no file with that name exists.
16
+ */
17
+ file(name: string): Promise<VirtualFile | null>;
18
+ /**
19
+ * Case-insensitive subdirectory lookup.
20
+ * Returns `null` when no directory with that name exists.
21
+ */
22
+ dir(name: string): Promise<VirtualDirectory | null>;
23
+ /**
24
+ * Flat listing of direct children. For FSA-backed directories, file
25
+ * sizes are 0 — call `file(name)` when you need the actual size.
26
+ */
27
+ entries(): Promise<VirtualEntry[]>;
28
+ }
29
+ export type VirtualEntry = {
30
+ kind: 'file';
31
+ name: string;
32
+ size: number;
33
+ } | {
34
+ kind: 'dir';
35
+ name: string;
36
+ };
37
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,MAAM,WAAW,WAAW;IAC1B,uDAAuD;IACvD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,qBAAqB;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,WAAW,IAAI,OAAO,CAAC,WAAW,CAAC,CAAC;CACrC;AAED,+CAA+C;AAC/C,MAAM,WAAW,gBAAgB;IAC/B,qCAAqC;IACrC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB;;;OAGG;IACH,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IAEhD;;;OAGG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;IAEpD;;;OAGG;IACH,OAAO,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC;CACpC;AAED,MAAM,MAAM,YAAY,GACpB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC5C;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,19 @@
1
+ import type { VirtualDirectory, VirtualEntry } from './types.js';
2
+ /**
3
+ * Walk down path segments case-insensitively.
4
+ * Returns `null` as soon as any segment is not found.
5
+ *
6
+ * @example
7
+ * const cfgdat = await drillPath(root, 'EC-APPS', 'INPA', 'CFGDAT');
8
+ */
9
+ export declare function drillPath(root: VirtualDirectory, ...segments: string[]): Promise<VirtualDirectory | null>;
10
+ /**
11
+ * List all files directly inside `dir`, optionally filtered by extension
12
+ * (matched case-insensitively, leading dot required, e.g. `".ipo"`).
13
+ *
14
+ * Returns lightweight entry objects — call `dir.file(name)` to open one.
15
+ */
16
+ export declare function listFiles(dir: VirtualDirectory, ext?: string): Promise<Array<VirtualEntry & {
17
+ kind: 'file';
18
+ }>>;
19
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAEjE;;;;;;GAMG;AACH,wBAAsB,SAAS,CAC7B,IAAI,EAAE,gBAAgB,EACtB,GAAG,QAAQ,EAAE,MAAM,EAAE,GACpB,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAQlC;AAED;;;;;GAKG;AACH,wBAAsB,SAAS,CAC7B,GAAG,EAAE,gBAAgB,EACrB,GAAG,CAAC,EAAE,MAAM,GACX,OAAO,CAAC,KAAK,CAAC,YAAY,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC,CAMjD"}
package/dist/utils.js ADDED
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Walk down path segments case-insensitively.
3
+ * Returns `null` as soon as any segment is not found.
4
+ *
5
+ * @example
6
+ * const cfgdat = await drillPath(root, 'EC-APPS', 'INPA', 'CFGDAT');
7
+ */
8
+ export async function drillPath(root, ...segments) {
9
+ let current = root;
10
+ for (const segment of segments) {
11
+ const next = await current.dir(segment);
12
+ if (!next)
13
+ return null;
14
+ current = next;
15
+ }
16
+ return current;
17
+ }
18
+ /**
19
+ * List all files directly inside `dir`, optionally filtered by extension
20
+ * (matched case-insensitively, leading dot required, e.g. `".ipo"`).
21
+ *
22
+ * Returns lightweight entry objects — call `dir.file(name)` to open one.
23
+ */
24
+ export async function listFiles(dir, ext) {
25
+ const all = await dir.entries();
26
+ const files = all.filter((e) => e.kind === 'file');
27
+ if (!ext)
28
+ return files;
29
+ const extLower = ext.toLowerCase();
30
+ return files.filter((e) => e.name.toLowerCase().endsWith(extLower));
31
+ }
32
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,IAAsB,EACtB,GAAG,QAAkB;IAErB,IAAI,OAAO,GAAqB,IAAI,CAAC;IACrC,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACxC,IAAI,CAAC,IAAI;YAAE,OAAO,IAAI,CAAC;QACvB,OAAO,GAAG,IAAI,CAAC;IACjB,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,GAAqB,EACrB,GAAY;IAEZ,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,OAAO,EAAE,CAAC;IAChC,MAAM,KAAK,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAwC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC;IACzF,IAAI,CAAC,GAAG;QAAE,OAAO,KAAK,CAAC;IACvB,MAAM,QAAQ,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC;IACnC,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;AACtE,CAAC"}