@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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=utils.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.test.d.ts","sourceRoot":"","sources":["../src/utils.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,95 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { HttpDirectory } from './http.js';
3
+ import { drillPath, listFiles } from './utils.js';
4
+ // ---------------------------------------------------------------------------
5
+ // Fixture — a two-level in-memory tree served via HttpDirectory
6
+ // ---------------------------------------------------------------------------
7
+ const BASE = 'http://test.local/root';
8
+ const ROUTES = {
9
+ [`${BASE}/index.json`]: JSON.stringify([
10
+ { type: 'file', name: 'inpa', fullName: 'inpa.ini', originalName: 'INPA', originalFullName: 'INPA.INI', size: 10 },
11
+ { type: 'file', name: 'ms43', fullName: 'ms43.ipo', originalName: 'MS43', originalFullName: 'MS43.IPO', size: 512 },
12
+ { type: 'file', name: 'radio', fullName: 'radio.ipo', originalName: 'RADIO', originalFullName: 'RADIO.IPO', size: 256 },
13
+ { type: 'dir', name: 'ec-apps', fullName: 'ec-apps', originalName: 'EC-APPS', originalFullName: 'EC-APPS', size: 0 },
14
+ ]),
15
+ [`${BASE}/EC-APPS/index.json`]: JSON.stringify([
16
+ { type: 'dir', name: 'inpa', fullName: 'inpa', originalName: 'INPA', originalFullName: 'INPA', size: 0 },
17
+ ]),
18
+ [`${BASE}/EC-APPS/INPA/index.json`]: JSON.stringify([
19
+ { type: 'file', name: 'ms43', fullName: 'ms43.ipo', originalName: 'MS43', originalFullName: 'MS43.IPO', size: 128 },
20
+ ]),
21
+ };
22
+ function makeRoot() {
23
+ return new HttpDirectory(BASE, {
24
+ fetch: async (input) => {
25
+ const url = input.toString();
26
+ const body = ROUTES[url];
27
+ if (!body)
28
+ return new Response(null, { status: 404 });
29
+ return new Response(body, { status: 200 });
30
+ },
31
+ });
32
+ }
33
+ // ---------------------------------------------------------------------------
34
+ // drillPath
35
+ // ---------------------------------------------------------------------------
36
+ describe('drillPath', () => {
37
+ it('returns the root itself when no segments are given', async () => {
38
+ const root = makeRoot();
39
+ const result = await drillPath(root);
40
+ expect(result).toBe(root);
41
+ });
42
+ it('navigates one level', async () => {
43
+ const result = await drillPath(makeRoot(), 'EC-APPS');
44
+ expect(result?.name).toBe('EC-APPS');
45
+ });
46
+ it('navigates multiple levels', async () => {
47
+ const result = await drillPath(makeRoot(), 'EC-APPS', 'INPA');
48
+ expect(result?.name).toBe('INPA');
49
+ });
50
+ it('is case-insensitive at each segment', async () => {
51
+ const result = await drillPath(makeRoot(), 'ec-apps', 'inpa');
52
+ expect(result?.name).toBe('INPA');
53
+ });
54
+ it('returns null when a segment is not found', async () => {
55
+ expect(await drillPath(makeRoot(), 'missing')).toBeNull();
56
+ });
57
+ it('returns null at a missing intermediate segment', async () => {
58
+ expect(await drillPath(makeRoot(), 'EC-APPS', 'missing', 'INPA')).toBeNull();
59
+ });
60
+ });
61
+ // ---------------------------------------------------------------------------
62
+ // listFiles
63
+ // ---------------------------------------------------------------------------
64
+ describe('listFiles', () => {
65
+ it('returns all file entries when no extension filter is given', async () => {
66
+ const files = await listFiles(makeRoot());
67
+ const names = files.map((f) => f.name).sort();
68
+ expect(names).toEqual(['INPA.INI', 'MS43.IPO', 'RADIO.IPO'].sort());
69
+ });
70
+ it('filters by extension case-insensitively', async () => {
71
+ const ipo = await listFiles(makeRoot(), '.IPO');
72
+ expect(ipo.map((f) => f.name).sort()).toEqual(['MS43.IPO', 'RADIO.IPO'].sort());
73
+ });
74
+ it('filter works regardless of entry casing', async () => {
75
+ // Pass lowercase extension against uppercase filenames
76
+ const ipo = await listFiles(makeRoot(), '.ipo');
77
+ expect(ipo).toHaveLength(2);
78
+ });
79
+ it('excludes directories', async () => {
80
+ const files = await listFiles(makeRoot());
81
+ expect(files.every((f) => f.kind === 'file')).toBe(true);
82
+ });
83
+ it('returns empty array when nothing matches the extension', async () => {
84
+ const result = await listFiles(makeRoot(), '.exe');
85
+ expect(result).toHaveLength(0);
86
+ });
87
+ it('returns empty array for an empty directory', async () => {
88
+ const emptyBase = 'http://test.local/empty';
89
+ const emptyDir = new HttpDirectory(emptyBase, {
90
+ fetch: async () => new Response(JSON.stringify([]), { status: 200 }),
91
+ });
92
+ expect(await listFiles(emptyDir)).toHaveLength(0);
93
+ });
94
+ });
95
+ //# sourceMappingURL=utils.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.test.js","sourceRoot":"","sources":["../src/utils.test.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "@emdzej/bimmerz-vfs",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "Read-only virtual file system abstraction for bimmerz tools — same interface over local File System Access API and remote HTTP index.json trees.",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js",
12
+ "default": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "devDependencies": {
19
+ "@types/node": "^22.0.0",
20
+ "typescript": "^5.7.3"
21
+ },
22
+ "publishConfig": {
23
+ "access": "public"
24
+ },
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "https://github.com/emdzej/bimmerz.git",
28
+ "directory": "packages/vfs"
29
+ },
30
+ "scripts": {
31
+ "build": "tsc",
32
+ "dev": "tsc --watch",
33
+ "lint": "eslint .",
34
+ "typecheck": "tsc --noEmit",
35
+ "clean": "rm -rf dist .turbo *.tsbuildinfo"
36
+ }
37
+ }