assafvayner HF Staff commited on
Commit
e2a9158
·
1 Parent(s): ec4ef43

copy from app in xet-core

Browse files
APP_README.md ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # XORB & Shard File Viewer
2
+
3
+ A static web application built with Svelte that allows you to upload and analyze XORB or Shard object files to view their metadata structure.
4
+
5
+ ## Features
6
+
7
+ - **File Upload**: Drag & drop or click to upload XORB and Shard files
8
+ - **Automatic Detection**: Automatically detects file type based on binary structure
9
+ - **Comprehensive Metadata Display**: Shows detailed information about file contents
10
+ - **Responsive Design**: Works on desktop and mobile devices
11
+ - **Error Handling**: Provides clear error messages for invalid files
12
+
13
+ ## Supported File Types
14
+
15
+ ### 📦 XORB Files
16
+
17
+ XORB (Xet Orb) files contain collections of compressed chunks with metadata. The viewer displays:
18
+
19
+ - Chunk count and sizes
20
+ - Compression ratios
21
+ - Hash information
22
+ - Boundary offsets
23
+ - Individual chunk details
24
+
25
+ ### 🗂️ Shard Files
26
+
27
+ MDB Shard files store file metadata and content-addressable storage information for efficient deduplication. The viewer shows:
28
+
29
+ - File information entries
30
+ - CAS (Content Addressable Storage) data
31
+ - Lookup tables
32
+ - Timestamps and security keys
33
+ - Header and footer details
34
+
35
+ ## Development
36
+
37
+ ### Prerequisites
38
+
39
+ - Node.js 18+
40
+ - pnpm (or npm/yarn)
41
+
42
+ ### Setup
43
+
44
+ ```bash
45
+ cd xorb-shard-viewer
46
+ pnpm install
47
+ pnpm run dev
48
+ ```
49
+
50
+ The application will be available at `http://localhost:5173`
51
+
52
+ ### Build for Production
53
+
54
+ ```bash
55
+ pnpm run build
56
+ ```
57
+
58
+ The static files will be generated in the `build` directory.
59
+
60
+ ## File Format Support
61
+
62
+ The application implements binary parsers for:
63
+
64
+ - **XORB Format**: Based on the CAS object format with chunk compression and merkle hashing
65
+ - **Shard Format**: MDB shard file format v2 with header, footer, file info, and CAS info sections
66
+
67
+ ## Architecture
68
+
69
+ - **Frontend**: Svelte + TypeScript
70
+ - **Parsing**: Custom binary parsers for both file formats
71
+ - **Styling**: Component-scoped CSS with responsive design
72
+ - **Type Safety**: Full TypeScript support with detailed type definitions
73
+
74
+ ## Browser Compatibility
75
+
76
+ Works in all modern browsers that support:
77
+
78
+ - File API
79
+ - ArrayBuffer/Uint8Array
80
+ - ES2020+ features
81
+
82
+ ## Security
83
+
84
+ - All file processing happens client-side
85
+ - No data is uploaded to any server
86
+ - Files are processed entirely in the browser's memory
87
+
88
+ ## Contributing
89
+
90
+ 1. Fork the repository
91
+ 2. Create a feature branch
92
+ 3. Make your changes
93
+ 4. Test thoroughly
94
+ 5. Submit a pull request
95
+
96
+ ## License
97
+
98
+ See the parent repository's LICENSE file for licensing information.
index.html DELETED
@@ -1,13 +0,0 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8" />
5
- <link rel="icon" type="image/svg+xml" href="/vite.svg" />
6
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
- <title>Vite + Svelte + TS</title>
8
- </head>
9
- <body>
10
- <div id="app"></div>
11
- <script type="module" src="/src/main.ts"></script>
12
- </body>
13
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
package.json CHANGED
@@ -1,20 +1,29 @@
1
  {
2
- "name": "svelte",
3
- "private": true,
4
- "version": "0.0.0",
5
- "type": "module",
6
- "scripts": {
7
- "dev": "vite",
8
- "build": "vite build",
9
- "preview": "vite preview",
10
- "check": "svelte-check --tsconfig ./tsconfig.app.json && tsc -p tsconfig.node.json"
11
- },
12
- "devDependencies": {
13
- "@sveltejs/vite-plugin-svelte": "^5.0.3",
14
- "@tsconfig/svelte": "^5.0.4",
15
- "svelte": "^5.28.1",
16
- "svelte-check": "^4.1.6",
17
- "typescript": "~5.8.3",
18
- "vite": "^6.3.5"
19
- }
 
 
 
 
 
 
 
 
 
20
  }
 
1
  {
2
+ "name": "xorb-shard-viewer",
3
+ "private": true,
4
+ "version": "0.0.1",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite dev",
8
+ "build": "vite build",
9
+ "preview": "vite preview",
10
+ "prepare": "svelte-kit sync || echo ''",
11
+ "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
12
+ "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch"
13
+ },
14
+ "devDependencies": {
15
+ "@sveltejs/adapter-auto": "^6.0.0",
16
+ "@sveltejs/kit": "^2.22.0",
17
+ "@sveltejs/vite-plugin-svelte": "^6.0.0",
18
+ "svelte": "^5.0.0",
19
+ "svelte-check": "^4.0.0",
20
+ "typescript": "^5.0.0",
21
+ "vite": "^7.0.4"
22
+ },
23
+ "pnpm": {
24
+ "onlyBuiltDependencies": [
25
+ "esbuild"
26
+ ]
27
+ },
28
+ "packageManager": "[email protected]+sha512.e519b9f7639869dc8d5c3c5dfef73b3f091094b0a006d7317353c72b124e80e1afd429732e28705ad6bfa1ee879c1fce46c128ccebd3192101f43dd67c667912"
29
  }
pnpm-lock.yaml ADDED
@@ -0,0 +1,964 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ lockfileVersion: '9.0'
2
+
3
+ settings:
4
+ autoInstallPeers: true
5
+ excludeLinksFromLockfile: false
6
+
7
+ importers:
8
+
9
+ .:
10
+ devDependencies:
11
+ '@sveltejs/adapter-auto':
12
+ specifier: ^6.0.0
13
14
+ '@sveltejs/kit':
15
+ specifier: ^2.22.0
16
17
+ '@sveltejs/vite-plugin-svelte':
18
+ specifier: ^6.0.0
19
20
+ svelte:
21
+ specifier: ^5.0.0
22
+ version: 5.37.3
23
+ svelte-check:
24
+ specifier: ^4.0.0
25
26
+ typescript:
27
+ specifier: ^5.0.0
28
+ version: 5.9.2
29
+ vite:
30
+ specifier: ^7.0.4
31
+ version: 7.0.6
32
+
33
+ packages:
34
+
35
+ '@ampproject/[email protected]':
36
+ resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==}
37
+ engines: {node: '>=6.0.0'}
38
+
39
+ '@esbuild/[email protected]':
40
+ resolution: {integrity: sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==}
41
+ engines: {node: '>=18'}
42
+ cpu: [ppc64]
43
+ os: [aix]
44
+
45
+ '@esbuild/[email protected]':
46
+ resolution: {integrity: sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==}
47
+ engines: {node: '>=18'}
48
+ cpu: [arm64]
49
+ os: [android]
50
+
51
+ '@esbuild/[email protected]':
52
+ resolution: {integrity: sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==}
53
+ engines: {node: '>=18'}
54
+ cpu: [arm]
55
+ os: [android]
56
+
57
+ '@esbuild/[email protected]':
58
+ resolution: {integrity: sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==}
59
+ engines: {node: '>=18'}
60
+ cpu: [x64]
61
+ os: [android]
62
+
63
+ '@esbuild/[email protected]':
64
+ resolution: {integrity: sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==}
65
+ engines: {node: '>=18'}
66
+ cpu: [arm64]
67
+ os: [darwin]
68
+
69
+ '@esbuild/[email protected]':
70
+ resolution: {integrity: sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==}
71
+ engines: {node: '>=18'}
72
+ cpu: [x64]
73
+ os: [darwin]
74
+
75
+ '@esbuild/[email protected]':
76
+ resolution: {integrity: sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==}
77
+ engines: {node: '>=18'}
78
+ cpu: [arm64]
79
+ os: [freebsd]
80
+
81
+ '@esbuild/[email protected]':
82
+ resolution: {integrity: sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==}
83
+ engines: {node: '>=18'}
84
+ cpu: [x64]
85
+ os: [freebsd]
86
+
87
+ '@esbuild/[email protected]':
88
+ resolution: {integrity: sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==}
89
+ engines: {node: '>=18'}
90
+ cpu: [arm64]
91
+ os: [linux]
92
+
93
+ '@esbuild/[email protected]':
94
+ resolution: {integrity: sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==}
95
+ engines: {node: '>=18'}
96
+ cpu: [arm]
97
+ os: [linux]
98
+
99
+ '@esbuild/[email protected]':
100
+ resolution: {integrity: sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==}
101
+ engines: {node: '>=18'}
102
+ cpu: [ia32]
103
+ os: [linux]
104
+
105
+ '@esbuild/[email protected]':
106
+ resolution: {integrity: sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==}
107
+ engines: {node: '>=18'}
108
+ cpu: [loong64]
109
+ os: [linux]
110
+
111
+ '@esbuild/[email protected]':
112
+ resolution: {integrity: sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==}
113
+ engines: {node: '>=18'}
114
+ cpu: [mips64el]
115
+ os: [linux]
116
+
117
+ '@esbuild/[email protected]':
118
+ resolution: {integrity: sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==}
119
+ engines: {node: '>=18'}
120
+ cpu: [ppc64]
121
+ os: [linux]
122
+
123
+ '@esbuild/[email protected]':
124
+ resolution: {integrity: sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==}
125
+ engines: {node: '>=18'}
126
+ cpu: [riscv64]
127
+ os: [linux]
128
+
129
+ '@esbuild/[email protected]':
130
+ resolution: {integrity: sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==}
131
+ engines: {node: '>=18'}
132
+ cpu: [s390x]
133
+ os: [linux]
134
+
135
+ '@esbuild/[email protected]':
136
+ resolution: {integrity: sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==}
137
+ engines: {node: '>=18'}
138
+ cpu: [x64]
139
+ os: [linux]
140
+
141
+ '@esbuild/[email protected]':
142
+ resolution: {integrity: sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==}
143
+ engines: {node: '>=18'}
144
+ cpu: [arm64]
145
+ os: [netbsd]
146
+
147
+ '@esbuild/[email protected]':
148
+ resolution: {integrity: sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==}
149
+ engines: {node: '>=18'}
150
+ cpu: [x64]
151
+ os: [netbsd]
152
+
153
+ '@esbuild/[email protected]':
154
+ resolution: {integrity: sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==}
155
+ engines: {node: '>=18'}
156
+ cpu: [arm64]
157
+ os: [openbsd]
158
+
159
+ '@esbuild/[email protected]':
160
+ resolution: {integrity: sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==}
161
+ engines: {node: '>=18'}
162
+ cpu: [x64]
163
+ os: [openbsd]
164
+
165
+ '@esbuild/[email protected]':
166
+ resolution: {integrity: sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==}
167
+ engines: {node: '>=18'}
168
+ cpu: [arm64]
169
+ os: [openharmony]
170
+
171
+ '@esbuild/[email protected]':
172
+ resolution: {integrity: sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==}
173
+ engines: {node: '>=18'}
174
+ cpu: [x64]
175
+ os: [sunos]
176
+
177
+ '@esbuild/[email protected]':
178
+ resolution: {integrity: sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==}
179
+ engines: {node: '>=18'}
180
+ cpu: [arm64]
181
+ os: [win32]
182
+
183
+ '@esbuild/[email protected]':
184
+ resolution: {integrity: sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==}
185
+ engines: {node: '>=18'}
186
+ cpu: [ia32]
187
+ os: [win32]
188
+
189
+ '@esbuild/[email protected]':
190
+ resolution: {integrity: sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==}
191
+ engines: {node: '>=18'}
192
+ cpu: [x64]
193
+ os: [win32]
194
+
195
+ '@jridgewell/[email protected]':
196
+ resolution: {integrity: sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==}
197
+
198
+ '@jridgewell/[email protected]':
199
+ resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
200
+ engines: {node: '>=6.0.0'}
201
+
202
+ '@jridgewell/[email protected]':
203
+ resolution: {integrity: sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==}
204
+
205
+ '@jridgewell/[email protected]':
206
+ resolution: {integrity: sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==}
207
+
208
+ '@polka/[email protected]':
209
+ resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==}
210
+
211
+ '@rollup/[email protected]':
212
+ resolution: {integrity: sha512-Zj3Hl6sN34xJtMv7Anwb5Gu01yujyE/cLBDB2gnHTAHaWS1Z38L7kuSG+oAh0giZMqG060f/YBStXtMH6FvPMA==}
213
+ cpu: [arm]
214
+ os: [android]
215
+
216
+ '@rollup/[email protected]':
217
+ resolution: {integrity: sha512-nTeCWY83kN64oQ5MGz3CgtPx8NSOhC5lWtsjTs+8JAJNLcP3QbLCtDDgUKQc/Ro/frpMq4SHUaHN6AMltcEoLQ==}
218
+ cpu: [arm64]
219
+ os: [android]
220
+
221
+ '@rollup/[email protected]':
222
+ resolution: {integrity: sha512-HV7bW2Fb/F5KPdM/9bApunQh68YVDU8sO8BvcW9OngQVN3HHHkw99wFupuUJfGR9pYLLAjcAOA6iO+evsbBaPQ==}
223
+ cpu: [arm64]
224
+ os: [darwin]
225
+
226
+ '@rollup/[email protected]':
227
+ resolution: {integrity: sha512-SSj8TlYV5nJixSsm/y3QXfhspSiLYP11zpfwp6G/YDXctf3Xkdnk4woJIF5VQe0of2OjzTt8EsxnJDCdHd2xMA==}
228
+ cpu: [x64]
229
+ os: [darwin]
230
+
231
+ '@rollup/[email protected]':
232
+ resolution: {integrity: sha512-ZyrsG4TIT9xnOlLsSSi9w/X29tCbK1yegE49RYm3tu3wF1L/B6LVMqnEWyDB26d9Ecx9zrmXCiPmIabVuLmNSg==}
233
+ cpu: [arm64]
234
+ os: [freebsd]
235
+
236
+ '@rollup/[email protected]':
237
+ resolution: {integrity: sha512-pCgHFoOECwVCJ5GFq8+gR8SBKnMO+xe5UEqbemxBpCKYQddRQMgomv1104RnLSg7nNvgKy05sLsY51+OVRyiVw==}
238
+ cpu: [x64]
239
+ os: [freebsd]
240
+
241
+ '@rollup/[email protected]':
242
+ resolution: {integrity: sha512-EtP8aquZ0xQg0ETFcxUbU71MZlHaw9MChwrQzatiE8U/bvi5uv/oChExXC4mWhjiqK7azGJBqU0tt5H123SzVA==}
243
+ cpu: [arm]
244
+ os: [linux]
245
+
246
+ '@rollup/[email protected]':
247
+ resolution: {integrity: sha512-qO7F7U3u1nfxYRPM8HqFtLd+raev2K137dsV08q/LRKRLEc7RsiDWihUnrINdsWQxPR9jqZ8DIIZ1zJJAm5PjQ==}
248
+ cpu: [arm]
249
+ os: [linux]
250
+
251
+ '@rollup/[email protected]':
252
+ resolution: {integrity: sha512-3dRaqLfcOXYsfvw5xMrxAk9Lb1f395gkoBYzSFcc/scgRFptRXL9DOaDpMiehf9CO8ZDRJW2z45b6fpU5nwjng==}
253
+ cpu: [arm64]
254
+ os: [linux]
255
+
256
+ '@rollup/[email protected]':
257
+ resolution: {integrity: sha512-fhHFTutA7SM+IrR6lIfiHskxmpmPTJUXpWIsBXpeEwNgZzZZSg/q4i6FU4J8qOGyJ0TR+wXBwx/L7Ho9z0+uDg==}
258
+ cpu: [arm64]
259
+ os: [linux]
260
+
261
+ '@rollup/[email protected]':
262
+ resolution: {integrity: sha512-i7wfGFXu8x4+FRqPymzjD+Hyav8l95UIZ773j7J7zRYc3Xsxy2wIn4x+llpunexXe6laaO72iEjeeGyUFmjKeA==}
263
+ cpu: [loong64]
264
+ os: [linux]
265
+
266
+ '@rollup/[email protected]':
267
+ resolution: {integrity: sha512-B/l0dFcHVUnqcGZWKcWBSV2PF01YUt0Rvlurci5P+neqY/yMKchGU8ullZvIv5e8Y1C6wOn+U03mrDylP5q9Yw==}
268
+ cpu: [ppc64]
269
+ os: [linux]
270
+
271
+ '@rollup/[email protected]':
272
+ resolution: {integrity: sha512-32k4ENb5ygtkMwPMucAb8MtV8olkPT03oiTxJbgkJa7lJ7dZMr0GCFJlyvy+K8iq7F/iuOr41ZdUHaOiqyR3iQ==}
273
+ cpu: [riscv64]
274
+ os: [linux]
275
+
276
+ '@rollup/[email protected]':
277
+ resolution: {integrity: sha512-t5B2loThlFEauloaQkZg9gxV05BYeITLvLkWOkRXogP4qHXLkWSbSHKM9S6H1schf/0YGP/qNKtiISlxvfmmZw==}
278
+ cpu: [riscv64]
279
+ os: [linux]
280
+
281
+ '@rollup/[email protected]':
282
+ resolution: {integrity: sha512-YKjekwTEKgbB7n17gmODSmJVUIvj8CX7q5442/CK80L8nqOUbMtf8b01QkG3jOqyr1rotrAnW6B/qiHwfcuWQA==}
283
+ cpu: [s390x]
284
+ os: [linux]
285
+
286
+ '@rollup/[email protected]':
287
+ resolution: {integrity: sha512-Jj5a9RUoe5ra+MEyERkDKLwTXVu6s3aACP51nkfnK9wJTraCC8IMe3snOfALkrjTYd2G1ViE1hICj0fZ7ALBPA==}
288
+ cpu: [x64]
289
+ os: [linux]
290
+
291
+ '@rollup/[email protected]':
292
+ resolution: {integrity: sha512-7kX69DIrBeD7yNp4A5b81izs8BqoZkCIaxQaOpumcJ1S/kmqNFjPhDu1LHeVXv0SexfHQv5cqHsxLOjETuqDuA==}
293
+ cpu: [x64]
294
+ os: [linux]
295
+
296
+ '@rollup/[email protected]':
297
+ resolution: {integrity: sha512-wiJWMIpeaak/jsbaq2HMh/rzZxHVW1rU6coyeNNpMwk5isiPjSTx0a4YLSlYDwBH/WBvLz+EtsNqQScZTLJy3g==}
298
+ cpu: [arm64]
299
+ os: [win32]
300
+
301
+ '@rollup/[email protected]':
302
+ resolution: {integrity: sha512-gBgaUDESVzMgWZhcyjfs9QFK16D8K6QZpwAaVNJxYDLHWayOta4ZMjGm/vsAEy3hvlS2GosVFlBlP9/Wb85DqQ==}
303
+ cpu: [ia32]
304
+ os: [win32]
305
+
306
+ '@rollup/[email protected]':
307
+ resolution: {integrity: sha512-CvUo2ixeIQGtF6WvuB87XWqPQkoFAFqW+HUo/WzHwuHDvIwZCtjdWXoYCcr06iKGydiqTclC4jU/TNObC/xKZg==}
308
+ cpu: [x64]
309
+ os: [win32]
310
+
311
+ '@standard-schema/[email protected]':
312
+ resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==}
313
+
314
+ '@sveltejs/[email protected]':
315
+ resolution: {integrity: sha512-IwQk4yfwLdibDlrXVE04jTZYlLnwsTT2PIOQQGNLWfjavGifnk1JD1LcZjZaBTRcxZu2FfPfNLOE04DSu9lqtQ==}
316
+ peerDependencies:
317
+ acorn: ^8.9.0
318
+
319
+ '@sveltejs/[email protected]':
320
+ resolution: {integrity: sha512-mcWud3pYGPWM2Pphdj8G9Qiq24nZ8L4LB7coCUckUEy5Y7wOWGJ/enaZ4AtJTcSm5dNK1rIkBRoqt+ae4zlxcQ==}
321
+ peerDependencies:
322
+ '@sveltejs/kit': ^2.0.0
323
+
324
+ '@sveltejs/[email protected]':
325
+ resolution: {integrity: sha512-pEX1Z2Km8tqmkni+ykIIou+ojp/7gb3M9tpllN5nDWNo9zlI0dI8/hDKFyBwQvb4jYR+EyLriFtrmgJ6GvbnBA==}
326
+ engines: {node: '>=18.13'}
327
+ hasBin: true
328
+ peerDependencies:
329
+ '@sveltejs/vite-plugin-svelte': ^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0
330
+ svelte: ^4.0.0 || ^5.0.0-next.0
331
+ vite: ^5.0.3 || ^6.0.0 || ^7.0.0-beta.0
332
+
333
+ '@sveltejs/[email protected]':
334
+ resolution: {integrity: sha512-iwQ8Z4ET6ZFSt/gC+tVfcsSBHwsqc6RumSaiLUkAurW3BCpJam65cmHw0oOlDMTO0u+PZi9hilBRYN+LZNHTUQ==}
335
+ engines: {node: ^20.19 || ^22.12 || >=24}
336
+ peerDependencies:
337
+ '@sveltejs/vite-plugin-svelte': ^6.0.0-next.0
338
+ svelte: ^5.0.0
339
+ vite: ^6.3.0 || ^7.0.0
340
+
341
+ '@sveltejs/[email protected]':
342
+ resolution: {integrity: sha512-+U6lz1wvGEG/BvQyL4z/flyNdQ9xDNv5vrh+vWBWTHaebqT0c9RNggpZTo/XSPoHsSCWBlYaTlRX8pZ9GATXCw==}
343
+ engines: {node: ^20.19 || ^22.12 || >=24}
344
+ peerDependencies:
345
+ svelte: ^5.0.0
346
+ vite: ^6.3.0 || ^7.0.0
347
+
348
+ '@types/[email protected]':
349
+ resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==}
350
+
351
+ '@types/[email protected]':
352
+ resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
353
+
354
355
+ resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==}
356
+ engines: {node: '>=0.4.0'}
357
+ hasBin: true
358
+
359
360
+ resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==}
361
+ engines: {node: '>= 0.4'}
362
+
363
364
+ resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==}
365
+ engines: {node: '>= 0.4'}
366
+
367
368
+ resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==}
369
+ engines: {node: '>= 14.16.0'}
370
+
371
372
+ resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
373
+ engines: {node: '>=6'}
374
+
375
376
+ resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==}
377
+ engines: {node: '>= 0.6'}
378
+
379
380
+ resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==}
381
+ engines: {node: '>=6.0'}
382
+ peerDependencies:
383
+ supports-color: '*'
384
+ peerDependenciesMeta:
385
+ supports-color:
386
+ optional: true
387
+
388
389
+ resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
390
+ engines: {node: '>=0.10.0'}
391
+
392
393
+ resolution: {integrity: sha512-maua5KUiapvEwiEAe+XnlZ3Rh0GD+qI1J/nb9vrJc3muPXvcF/8gXYTWF76+5DAqHyDUtOIImEuo0YKE9mshVw==}
394
+
395
396
+ resolution: {integrity: sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==}
397
+ engines: {node: '>=18'}
398
+ hasBin: true
399
+
400
401
+ resolution: {integrity: sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==}
402
+
403
404
+ resolution: {integrity: sha512-yzmPNpl7TBbMRC5Lj2JlJZNPml0tzqoqP5B1JXycNUwtqma9AKCO0M2wHrdgsHcy1WRW7S9rJknAMtByg3usgA==}
405
+
406
407
+ resolution: {integrity: sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==}
408
+ peerDependencies:
409
+ picomatch: ^3 || ^4
410
+ peerDependenciesMeta:
411
+ picomatch:
412
+ optional: true
413
+
414
415
+ resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
416
+ engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
417
+ os: [darwin]
418
+
419
420
+ resolution: {integrity: sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==}
421
+
422
423
+ resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==}
424
+ engines: {node: '>=6'}
425
+
426
427
+ resolution: {integrity: sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==}
428
+
429
430
+ resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==}
431
+
432
433
+ resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==}
434
+ engines: {node: '>=4'}
435
+
436
437
+ resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==}
438
+ engines: {node: '>=10'}
439
+
440
441
+ resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
442
+
443
444
+ resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
445
+ engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
446
+ hasBin: true
447
+
448
449
+ resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
450
+
451
452
+ resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==}
453
+ engines: {node: '>=12'}
454
+
455
456
+ resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==}
457
+ engines: {node: ^10 || ^12 || >=14}
458
+
459
460
+ resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==}
461
+ engines: {node: '>= 14.18.0'}
462
+
463
464
+ resolution: {integrity: sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg==}
465
+ engines: {node: '>=18.0.0', npm: '>=8.0.0'}
466
+ hasBin: true
467
+
468
469
+ resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==}
470
+ engines: {node: '>=6'}
471
+
472
473
+ resolution: {integrity: sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==}
474
+
475
476
+ resolution: {integrity: sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A==}
477
+ engines: {node: '>=18'}
478
+
479
480
+ resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
481
+ engines: {node: '>=0.10.0'}
482
+
483
484
+ resolution: {integrity: sha512-lkh8gff5gpHLjxIV+IaApMxQhTGnir2pNUAqcNgeKkvK5bT/30Ey/nzBxNLDlkztCH4dP7PixkMt9SWEKFPBWg==}
485
+ engines: {node: '>= 18.0.0'}
486
+ hasBin: true
487
+ peerDependencies:
488
+ svelte: ^4.0.0 || ^5.0.0-next.0
489
+ typescript: '>=5.0.0'
490
+
491
492
+ resolution: {integrity: sha512-7t/ejshehHd+95z3Z7ebS7wsqHDQxi/8nBTuTRwpMgNegfRBfuitCSKTUDKIBOExqfT2+DhQ2VLG8Xn+cBXoaQ==}
493
+ engines: {node: '>=18'}
494
+
495
496
+ resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==}
497
+ engines: {node: '>=12.0.0'}
498
+
499
500
+ resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==}
501
+ engines: {node: '>=6'}
502
+
503
504
+ resolution: {integrity: sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==}
505
+ engines: {node: '>=14.17'}
506
+ hasBin: true
507
+
508
509
+ resolution: {integrity: sha512-MHFiOENNBd+Bd9uvc8GEsIzdkn1JxMmEeYX35tI3fv0sJBUTfW5tQsoaOwuY4KhBI09A3dUJ/DXf2yxPVPUceg==}
510
+ engines: {node: ^20.19.0 || >=22.12.0}
511
+ hasBin: true
512
+ peerDependencies:
513
+ '@types/node': ^20.19.0 || >=22.12.0
514
+ jiti: '>=1.21.0'
515
+ less: ^4.0.0
516
+ lightningcss: ^1.21.0
517
+ sass: ^1.70.0
518
+ sass-embedded: ^1.70.0
519
+ stylus: '>=0.54.8'
520
+ sugarss: ^5.0.0
521
+ terser: ^5.16.0
522
+ tsx: ^4.8.1
523
+ yaml: ^2.4.2
524
+ peerDependenciesMeta:
525
+ '@types/node':
526
+ optional: true
527
+ jiti:
528
+ optional: true
529
+ less:
530
+ optional: true
531
+ lightningcss:
532
+ optional: true
533
+ sass:
534
+ optional: true
535
+ sass-embedded:
536
+ optional: true
537
+ stylus:
538
+ optional: true
539
+ sugarss:
540
+ optional: true
541
+ terser:
542
+ optional: true
543
+ tsx:
544
+ optional: true
545
+ yaml:
546
+ optional: true
547
+
548
549
+ resolution: {integrity: sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ==}
550
+ peerDependencies:
551
+ vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0
552
+ peerDependenciesMeta:
553
+ vite:
554
+ optional: true
555
+
556
557
+ resolution: {integrity: sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==}
558
+
559
+ snapshots:
560
+
561
+ '@ampproject/[email protected]':
562
+ dependencies:
563
+ '@jridgewell/gen-mapping': 0.3.12
564
+ '@jridgewell/trace-mapping': 0.3.29
565
+
566
+ '@esbuild/[email protected]':
567
+ optional: true
568
+
569
+ '@esbuild/[email protected]':
570
+ optional: true
571
+
572
+ '@esbuild/[email protected]':
573
+ optional: true
574
+
575
+ '@esbuild/[email protected]':
576
+ optional: true
577
+
578
+ '@esbuild/[email protected]':
579
+ optional: true
580
+
581
+ '@esbuild/[email protected]':
582
+ optional: true
583
+
584
+ '@esbuild/[email protected]':
585
+ optional: true
586
+
587
+ '@esbuild/[email protected]':
588
+ optional: true
589
+
590
+ '@esbuild/[email protected]':
591
+ optional: true
592
+
593
+ '@esbuild/[email protected]':
594
+ optional: true
595
+
596
+ '@esbuild/[email protected]':
597
+ optional: true
598
+
599
+ '@esbuild/[email protected]':
600
+ optional: true
601
+
602
+ '@esbuild/[email protected]':
603
+ optional: true
604
+
605
+ '@esbuild/[email protected]':
606
+ optional: true
607
+
608
+ '@esbuild/[email protected]':
609
+ optional: true
610
+
611
+ '@esbuild/[email protected]':
612
+ optional: true
613
+
614
+ '@esbuild/[email protected]':
615
+ optional: true
616
+
617
+ '@esbuild/[email protected]':
618
+ optional: true
619
+
620
+ '@esbuild/[email protected]':
621
+ optional: true
622
+
623
+ '@esbuild/[email protected]':
624
+ optional: true
625
+
626
+ '@esbuild/[email protected]':
627
+ optional: true
628
+
629
+ '@esbuild/[email protected]':
630
+ optional: true
631
+
632
+ '@esbuild/[email protected]':
633
+ optional: true
634
+
635
+ '@esbuild/[email protected]':
636
+ optional: true
637
+
638
+ '@esbuild/[email protected]':
639
+ optional: true
640
+
641
+ '@esbuild/[email protected]':
642
+ optional: true
643
+
644
+ '@jridgewell/[email protected]':
645
+ dependencies:
646
+ '@jridgewell/sourcemap-codec': 1.5.4
647
+ '@jridgewell/trace-mapping': 0.3.29
648
+
649
+ '@jridgewell/[email protected]': {}
650
+
651
+ '@jridgewell/[email protected]': {}
652
+
653
+ '@jridgewell/[email protected]':
654
+ dependencies:
655
+ '@jridgewell/resolve-uri': 3.1.2
656
+ '@jridgewell/sourcemap-codec': 1.5.4
657
+
658
+ '@polka/[email protected]': {}
659
+
660
+ '@rollup/[email protected]':
661
+ optional: true
662
+
663
+ '@rollup/[email protected]':
664
+ optional: true
665
+
666
+ '@rollup/[email protected]':
667
+ optional: true
668
+
669
+ '@rollup/[email protected]':
670
+ optional: true
671
+
672
+ '@rollup/[email protected]':
673
+ optional: true
674
+
675
+ '@rollup/[email protected]':
676
+ optional: true
677
+
678
+ '@rollup/[email protected]':
679
+ optional: true
680
+
681
+ '@rollup/[email protected]':
682
+ optional: true
683
+
684
+ '@rollup/[email protected]':
685
+ optional: true
686
+
687
+ '@rollup/[email protected]':
688
+ optional: true
689
+
690
+ '@rollup/[email protected]':
691
+ optional: true
692
+
693
+ '@rollup/[email protected]':
694
+ optional: true
695
+
696
+ '@rollup/[email protected]':
697
+ optional: true
698
+
699
+ '@rollup/[email protected]':
700
+ optional: true
701
+
702
+ '@rollup/[email protected]':
703
+ optional: true
704
+
705
+ '@rollup/[email protected]':
706
+ optional: true
707
+
708
+ '@rollup/[email protected]':
709
+ optional: true
710
+
711
+ '@rollup/[email protected]':
712
+ optional: true
713
+
714
+ '@rollup/[email protected]':
715
+ optional: true
716
+
717
+ '@rollup/[email protected]':
718
+ optional: true
719
+
720
+ '@standard-schema/[email protected]': {}
721
+
722
723
+ dependencies:
724
+ acorn: 8.15.0
725
+
726
727
+ dependencies:
728
729
+
730
731
+ dependencies:
732
+ '@standard-schema/spec': 1.0.0
733
+ '@sveltejs/acorn-typescript': 1.0.5([email protected])
734
+ '@sveltejs/vite-plugin-svelte': 6.1.0([email protected])([email protected])
735
+ '@types/cookie': 0.6.0
736
+ acorn: 8.15.0
737
+ cookie: 0.6.0
738
+ devalue: 5.1.1
739
+ esm-env: 1.2.2
740
+ kleur: 4.1.5
741
+ magic-string: 0.30.17
742
+ mrmime: 2.0.1
743
+ sade: 1.8.1
744
+ set-cookie-parser: 2.7.1
745
+ sirv: 3.0.1
746
+ svelte: 5.37.3
747
+ vite: 7.0.6
748
+
749
750
+ dependencies:
751
+ '@sveltejs/vite-plugin-svelte': 6.1.0([email protected])([email protected])
752
+ debug: 4.4.1
753
+ svelte: 5.37.3
754
+ vite: 7.0.6
755
+ transitivePeerDependencies:
756
+ - supports-color
757
+
758
759
+ dependencies:
760
+ '@sveltejs/vite-plugin-svelte-inspector': 5.0.0(@sveltejs/[email protected]([email protected])([email protected]))([email protected])([email protected])
761
+ debug: 4.4.1
762
+ deepmerge: 4.3.1
763
+ kleur: 4.1.5
764
+ magic-string: 0.30.17
765
+ svelte: 5.37.3
766
+ vite: 7.0.6
767
+ vitefu: 1.1.1([email protected])
768
+ transitivePeerDependencies:
769
+ - supports-color
770
+
771
+ '@types/[email protected]': {}
772
+
773
+ '@types/[email protected]': {}
774
+
775
776
+
777
778
+
779
780
+
781
782
+ dependencies:
783
+ readdirp: 4.1.2
784
+
785
786
+
787
788
+
789
790
+ dependencies:
791
+ ms: 2.1.3
792
+
793
794
+
795
796
+
797
798
+ optionalDependencies:
799
+ '@esbuild/aix-ppc64': 0.25.8
800
+ '@esbuild/android-arm': 0.25.8
801
+ '@esbuild/android-arm64': 0.25.8
802
+ '@esbuild/android-x64': 0.25.8
803
+ '@esbuild/darwin-arm64': 0.25.8
804
+ '@esbuild/darwin-x64': 0.25.8
805
+ '@esbuild/freebsd-arm64': 0.25.8
806
+ '@esbuild/freebsd-x64': 0.25.8
807
+ '@esbuild/linux-arm': 0.25.8
808
+ '@esbuild/linux-arm64': 0.25.8
809
+ '@esbuild/linux-ia32': 0.25.8
810
+ '@esbuild/linux-loong64': 0.25.8
811
+ '@esbuild/linux-mips64el': 0.25.8
812
+ '@esbuild/linux-ppc64': 0.25.8
813
+ '@esbuild/linux-riscv64': 0.25.8
814
+ '@esbuild/linux-s390x': 0.25.8
815
+ '@esbuild/linux-x64': 0.25.8
816
+ '@esbuild/netbsd-arm64': 0.25.8
817
+ '@esbuild/netbsd-x64': 0.25.8
818
+ '@esbuild/openbsd-arm64': 0.25.8
819
+ '@esbuild/openbsd-x64': 0.25.8
820
+ '@esbuild/openharmony-arm64': 0.25.8
821
+ '@esbuild/sunos-x64': 0.25.8
822
+ '@esbuild/win32-arm64': 0.25.8
823
+ '@esbuild/win32-ia32': 0.25.8
824
+ '@esbuild/win32-x64': 0.25.8
825
+
826
827
+
828
829
+ dependencies:
830
+ '@jridgewell/sourcemap-codec': 1.5.4
831
+
832
833
+ optionalDependencies:
834
+ picomatch: 4.0.3
835
+
836
837
+ optional: true
838
+
839
840
+ dependencies:
841
+ '@types/estree': 1.0.8
842
+
843
844
+
845
846
+
847
848
+ dependencies:
849
+ '@jridgewell/sourcemap-codec': 1.5.4
850
+
851
852
+
853
854
+
855
856
+
857
858
+
859
860
+
861
862
+
863
864
+ dependencies:
865
+ nanoid: 3.3.11
866
+ picocolors: 1.1.1
867
+ source-map-js: 1.2.1
868
+
869
870
+
871
872
+ dependencies:
873
+ '@types/estree': 1.0.8
874
+ optionalDependencies:
875
+ '@rollup/rollup-android-arm-eabi': 4.46.2
876
+ '@rollup/rollup-android-arm64': 4.46.2
877
+ '@rollup/rollup-darwin-arm64': 4.46.2
878
+ '@rollup/rollup-darwin-x64': 4.46.2
879
+ '@rollup/rollup-freebsd-arm64': 4.46.2
880
+ '@rollup/rollup-freebsd-x64': 4.46.2
881
+ '@rollup/rollup-linux-arm-gnueabihf': 4.46.2
882
+ '@rollup/rollup-linux-arm-musleabihf': 4.46.2
883
+ '@rollup/rollup-linux-arm64-gnu': 4.46.2
884
+ '@rollup/rollup-linux-arm64-musl': 4.46.2
885
+ '@rollup/rollup-linux-loongarch64-gnu': 4.46.2
886
+ '@rollup/rollup-linux-ppc64-gnu': 4.46.2
887
+ '@rollup/rollup-linux-riscv64-gnu': 4.46.2
888
+ '@rollup/rollup-linux-riscv64-musl': 4.46.2
889
+ '@rollup/rollup-linux-s390x-gnu': 4.46.2
890
+ '@rollup/rollup-linux-x64-gnu': 4.46.2
891
+ '@rollup/rollup-linux-x64-musl': 4.46.2
892
+ '@rollup/rollup-win32-arm64-msvc': 4.46.2
893
+ '@rollup/rollup-win32-ia32-msvc': 4.46.2
894
+ '@rollup/rollup-win32-x64-msvc': 4.46.2
895
+ fsevents: 2.3.3
896
+
897
898
+ dependencies:
899
+ mri: 1.2.0
900
+
901
902
+
903
904
+ dependencies:
905
+ '@polka/url': 1.0.0-next.29
906
+ mrmime: 2.0.1
907
+ totalist: 3.0.1
908
+
909
910
+
911
912
+ dependencies:
913
+ '@jridgewell/trace-mapping': 0.3.29
914
+ chokidar: 4.0.3
915
+ fdir: 6.4.6([email protected])
916
+ picocolors: 1.1.1
917
+ sade: 1.8.1
918
+ svelte: 5.37.3
919
+ typescript: 5.9.2
920
+ transitivePeerDependencies:
921
+ - picomatch
922
+
923
924
+ dependencies:
925
+ '@ampproject/remapping': 2.3.0
926
+ '@jridgewell/sourcemap-codec': 1.5.4
927
+ '@sveltejs/acorn-typescript': 1.0.5([email protected])
928
+ '@types/estree': 1.0.8
929
+ acorn: 8.15.0
930
+ aria-query: 5.3.2
931
+ axobject-query: 4.1.0
932
+ clsx: 2.1.1
933
+ esm-env: 1.2.2
934
+ esrap: 2.1.0
935
+ is-reference: 3.0.3
936
+ locate-character: 3.0.0
937
+ magic-string: 0.30.17
938
+ zimmerframe: 1.1.2
939
+
940
941
+ dependencies:
942
+ fdir: 6.4.6([email protected])
943
+ picomatch: 4.0.3
944
+
945
946
+
947
948
+
949
950
+ dependencies:
951
+ esbuild: 0.25.8
952
+ fdir: 6.4.6([email protected])
953
+ picomatch: 4.0.3
954
+ postcss: 8.5.6
955
+ rollup: 4.46.2
956
+ tinyglobby: 0.2.14
957
+ optionalDependencies:
958
+ fsevents: 2.3.3
959
+
960
961
+ optionalDependencies:
962
+ vite: 7.0.6
963
+
964
public/vite.svg DELETED
src/App.svelte DELETED
@@ -1,47 +0,0 @@
1
- <script lang="ts">
2
- import svelteLogo from './assets/svelte.svg'
3
- import viteLogo from '/vite.svg'
4
- import Counter from './lib/Counter.svelte'
5
- </script>
6
-
7
- <main>
8
- <div>
9
- <a href="https://vite.dev" target="_blank" rel="noreferrer">
10
- <img src={viteLogo} class="logo" alt="Vite Logo" />
11
- </a>
12
- <a href="https://svelte.dev" target="_blank" rel="noreferrer">
13
- <img src={svelteLogo} class="logo svelte" alt="Svelte Logo" />
14
- </a>
15
- </div>
16
- <h1>Vite + Svelte</h1>
17
-
18
- <div class="card">
19
- <Counter />
20
- </div>
21
-
22
- <p>
23
- Check out <a href="https://github.com/sveltejs/kit#readme" target="_blank" rel="noreferrer">SvelteKit</a>, the official Svelte app framework powered by Vite!
24
- </p>
25
-
26
- <p class="read-the-docs">
27
- Click on the Vite and Svelte logos to learn more
28
- </p>
29
- </main>
30
-
31
- <style>
32
- .logo {
33
- height: 6em;
34
- padding: 1.5em;
35
- will-change: filter;
36
- transition: filter 300ms;
37
- }
38
- .logo:hover {
39
- filter: drop-shadow(0 0 2em #646cffaa);
40
- }
41
- .logo.svelte:hover {
42
- filter: drop-shadow(0 0 2em #ff3e00aa);
43
- }
44
- .read-the-docs {
45
- color: #888;
46
- }
47
- </style>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/app.css DELETED
@@ -1,79 +0,0 @@
1
- :root {
2
- font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
3
- line-height: 1.5;
4
- font-weight: 400;
5
-
6
- color-scheme: light dark;
7
- color: rgba(255, 255, 255, 0.87);
8
- background-color: #242424;
9
-
10
- font-synthesis: none;
11
- text-rendering: optimizeLegibility;
12
- -webkit-font-smoothing: antialiased;
13
- -moz-osx-font-smoothing: grayscale;
14
- }
15
-
16
- a {
17
- font-weight: 500;
18
- color: #646cff;
19
- text-decoration: inherit;
20
- }
21
- a:hover {
22
- color: #535bf2;
23
- }
24
-
25
- body {
26
- margin: 0;
27
- display: flex;
28
- place-items: center;
29
- min-width: 320px;
30
- min-height: 100vh;
31
- }
32
-
33
- h1 {
34
- font-size: 3.2em;
35
- line-height: 1.1;
36
- }
37
-
38
- .card {
39
- padding: 2em;
40
- }
41
-
42
- #app {
43
- max-width: 1280px;
44
- margin: 0 auto;
45
- padding: 2rem;
46
- text-align: center;
47
- }
48
-
49
- button {
50
- border-radius: 8px;
51
- border: 1px solid transparent;
52
- padding: 0.6em 1.2em;
53
- font-size: 1em;
54
- font-weight: 500;
55
- font-family: inherit;
56
- background-color: #1a1a1a;
57
- cursor: pointer;
58
- transition: border-color 0.25s;
59
- }
60
- button:hover {
61
- border-color: #646cff;
62
- }
63
- button:focus,
64
- button:focus-visible {
65
- outline: 4px auto -webkit-focus-ring-color;
66
- }
67
-
68
- @media (prefers-color-scheme: light) {
69
- :root {
70
- color: #213547;
71
- background-color: #ffffff;
72
- }
73
- a:hover {
74
- color: #747bff;
75
- }
76
- button {
77
- background-color: #f9f9f9;
78
- }
79
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/app.d.ts ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // See https://svelte.dev/docs/kit/types#app.d.ts
2
+ // for information about these interfaces
3
+ declare global {
4
+ namespace App {
5
+ // interface Error {}
6
+ // interface Locals {}
7
+ // interface PageData {}
8
+ // interface PageState {}
9
+ // interface Platform {}
10
+ }
11
+ }
12
+
13
+ export {};
src/app.html ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!doctype html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="utf-8" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
7
+ <title>XORB & Shard File Viewer</title>
8
+ <meta name="description" content="Parse and view metadata from XORB and Shard object files" />
9
+ %sveltekit.head%
10
+ </head>
11
+
12
+ <body data-sveltekit-preload-data="hover">
13
+ <div style="display: contents">%sveltekit.body%</div>
14
+ </body>
15
+
16
+ </html>
src/assets/svelte.svg DELETED
src/lib/Counter.svelte DELETED
@@ -1,10 +0,0 @@
1
- <script lang="ts">
2
- let count: number = $state(0)
3
- const increment = () => {
4
- count += 1
5
- }
6
- </script>
7
-
8
- <button onclick={increment}>
9
- count is {count}
10
- </button>
 
 
 
 
 
 
 
 
 
 
 
src/lib/assets/favicon.svg ADDED
src/lib/components/ErrorDisplay.svelte ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <script lang="ts">
2
+ export let error: string;
3
+ export let filename: string = "";
4
+ </script>
5
+
6
+ <div class="error-container">
7
+ <div class="error-content">
8
+ <div class="error-icon">⚠️</div>
9
+ <h3>Error Parsing File</h3>
10
+ {#if filename}
11
+ <p class="filename">File: {filename}</p>
12
+ {/if}
13
+ <div class="error-message">
14
+ {error}
15
+ </div>
16
+ <div class="suggestions">
17
+ <h4>Suggestions:</h4>
18
+ <ul>
19
+ <li>Verify that the file is a valid XORB or Shard file</li>
20
+ <li>Check that the file is not corrupted</li>
21
+ <li>Ensure the file format matches the expected binary structure</li>
22
+ </ul>
23
+ </div>
24
+ </div>
25
+ </div>
26
+
27
+ <style>
28
+ .error-container {
29
+ max-width: 600px;
30
+ margin: 20px auto;
31
+ padding: 20px;
32
+ }
33
+
34
+ .error-content {
35
+ background: #fee;
36
+ border: 1px solid #fcc;
37
+ border-radius: 8px;
38
+ padding: 30px;
39
+ text-align: center;
40
+ }
41
+
42
+ .error-icon {
43
+ font-size: 48px;
44
+ margin-bottom: 16px;
45
+ }
46
+
47
+ .error-content h3 {
48
+ margin: 0 0 16px 0;
49
+ color: #d32f2f;
50
+ font-size: 20px;
51
+ font-weight: 600;
52
+ }
53
+
54
+ .filename {
55
+ font-family: "Monaco", "Consolas", monospace;
56
+ background: #fff;
57
+ padding: 8px 12px;
58
+ border-radius: 4px;
59
+ margin: 0 0 20px 0;
60
+ color: #666;
61
+ border: 1px solid #eee;
62
+ }
63
+
64
+ .error-message {
65
+ background: #fff;
66
+ border: 1px solid #fcc;
67
+ border-radius: 4px;
68
+ padding: 16px;
69
+ margin: 20px 0;
70
+ font-family: "Monaco", "Consolas", monospace;
71
+ font-size: 14px;
72
+ color: #d32f2f;
73
+ text-align: left;
74
+ white-space: pre-wrap;
75
+ }
76
+
77
+ .suggestions {
78
+ text-align: left;
79
+ margin-top: 20px;
80
+ background: #fff9c4;
81
+ border: 1px solid #f7e97d;
82
+ border-radius: 4px;
83
+ padding: 16px;
84
+ }
85
+
86
+ .suggestions h4 {
87
+ margin: 0 0 12px 0;
88
+ color: #8d6e00;
89
+ font-size: 16px;
90
+ }
91
+
92
+ .suggestions ul {
93
+ margin: 0;
94
+ padding-left: 20px;
95
+ color: #8d6e00;
96
+ }
97
+
98
+ .suggestions li {
99
+ margin-bottom: 8px;
100
+ }
101
+ </style>
src/lib/components/FileUpload.svelte ADDED
@@ -0,0 +1,241 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <script lang="ts">
2
+ import { createEventDispatcher } from "svelte";
3
+
4
+ const dispatch = createEventDispatcher<{
5
+ fileSelected: { file: File; type: "xorb" | "shard" };
6
+ }>();
7
+
8
+ let xorbDragActive = false;
9
+ let shardDragActive = false;
10
+ let xorbFileInput: HTMLInputElement;
11
+ let shardFileInput: HTMLInputElement;
12
+
13
+ function handleDragOver(event: DragEvent, type: "xorb" | "shard") {
14
+ event.preventDefault();
15
+ if (type === "xorb") {
16
+ xorbDragActive = true;
17
+ } else {
18
+ shardDragActive = true;
19
+ }
20
+ }
21
+
22
+ function handleDragLeave(type: "xorb" | "shard") {
23
+ if (type === "xorb") {
24
+ xorbDragActive = false;
25
+ } else {
26
+ shardDragActive = false;
27
+ }
28
+ }
29
+
30
+ function handleDrop(event: DragEvent, type: "xorb" | "shard") {
31
+ event.preventDefault();
32
+ if (type === "xorb") {
33
+ xorbDragActive = false;
34
+ } else {
35
+ shardDragActive = false;
36
+ }
37
+
38
+ const files = event.dataTransfer?.files;
39
+ if (files && files.length > 0) {
40
+ handleFile(files[0], type);
41
+ }
42
+ }
43
+
44
+ function handleFileInput(event: Event, type: "xorb" | "shard") {
45
+ const input = event.target as HTMLInputElement;
46
+ if (input.files && input.files.length > 0) {
47
+ handleFile(input.files[0], type);
48
+ }
49
+ }
50
+
51
+ function handleFile(file: File, type: "xorb" | "shard") {
52
+ dispatch("fileSelected", { file, type });
53
+ }
54
+
55
+ function openFileDialog(type: "xorb" | "shard") {
56
+ if (type === "xorb") {
57
+ xorbFileInput.click();
58
+ } else {
59
+ shardFileInput.click();
60
+ }
61
+ }
62
+ </script>
63
+
64
+ <div class="upload-container">
65
+ <div class="upload-areas">
66
+ <!-- XORB Upload Area -->
67
+ <div class="upload-section">
68
+ <h3>📦 Upload XORB File</h3>
69
+ <div
70
+ class="upload-area xorb-area"
71
+ class:drag-active={xorbDragActive}
72
+ on:dragover={(e) => handleDragOver(e, "xorb")}
73
+ on:dragleave={() => handleDragLeave("xorb")}
74
+ on:drop={(e) => handleDrop(e, "xorb")}
75
+ on:click={() => openFileDialog("xorb")}
76
+ on:keydown={(e) => e.key === "Enter" && openFileDialog("xorb")}
77
+ role="button"
78
+ tabindex="0"
79
+ >
80
+ <div class="upload-content">
81
+ <div class="upload-icon">📦</div>
82
+ <h4>Drop XORB file here</h4>
83
+ <p>or click to browse</p>
84
+ <span class="format-tag xorb-tag">XORB</span>
85
+ </div>
86
+ </div>
87
+ </div>
88
+
89
+ <!-- Shard Upload Area -->
90
+ <div class="upload-section">
91
+ <h3>🗂️ Upload Shard File</h3>
92
+ <div
93
+ class="upload-area shard-area"
94
+ class:drag-active={shardDragActive}
95
+ on:dragover={(e) => handleDragOver(e, "shard")}
96
+ on:dragleave={() => handleDragLeave("shard")}
97
+ on:drop={(e) => handleDrop(e, "shard")}
98
+ on:click={() => openFileDialog("shard")}
99
+ on:keydown={(e) => e.key === "Enter" && openFileDialog("shard")}
100
+ role="button"
101
+ tabindex="0"
102
+ >
103
+ <div class="upload-content">
104
+ <div class="upload-icon">🗂️</div>
105
+ <h4>Drop Shard file here</h4>
106
+ <p>or click to browse</p>
107
+ <span class="format-tag shard-tag">SHARD</span>
108
+ </div>
109
+ </div>
110
+ </div>
111
+ </div>
112
+
113
+ <input
114
+ bind:this={xorbFileInput}
115
+ type="file"
116
+ on:change={(e) => handleFileInput(e, "xorb")}
117
+ style="display: none;"
118
+ />
119
+
120
+ <input
121
+ bind:this={shardFileInput}
122
+ type="file"
123
+ on:change={(e) => handleFileInput(e, "shard")}
124
+ style="display: none;"
125
+ />
126
+ </div>
127
+
128
+ <style>
129
+ .upload-container {
130
+ width: 100%;
131
+ max-width: 800px;
132
+ margin: 0 auto;
133
+ }
134
+
135
+ .upload-areas {
136
+ display: grid;
137
+ grid-template-columns: 1fr 1fr;
138
+ gap: 30px;
139
+ }
140
+
141
+ .upload-section {
142
+ text-align: center;
143
+ }
144
+
145
+ .upload-section h3 {
146
+ margin: 0 0 20px 0;
147
+ color: #2c3e50;
148
+ font-size: 18px;
149
+ font-weight: 600;
150
+ }
151
+
152
+ .upload-area {
153
+ border: 2px dashed #ccc;
154
+ border-radius: 8px;
155
+ padding: 30px 20px;
156
+ text-align: center;
157
+ cursor: pointer;
158
+ transition: all 0.3s ease;
159
+ background: #fafafa;
160
+ min-height: 200px;
161
+ display: flex;
162
+ align-items: center;
163
+ justify-content: center;
164
+ }
165
+
166
+ .xorb-area:hover {
167
+ border-color: #28a745;
168
+ background: #f0fff4;
169
+ }
170
+
171
+ .xorb-area.drag-active {
172
+ border-color: #28a745;
173
+ background: #e8f5e8;
174
+ transform: scale(1.02);
175
+ }
176
+
177
+ .shard-area:hover {
178
+ border-color: #007bff;
179
+ background: #f0f8ff;
180
+ }
181
+
182
+ .shard-area.drag-active {
183
+ border-color: #007bff;
184
+ background: #e3f2fd;
185
+ transform: scale(1.02);
186
+ }
187
+
188
+ .upload-content {
189
+ pointer-events: none;
190
+ }
191
+
192
+ .upload-icon {
193
+ font-size: 36px;
194
+ margin-bottom: 12px;
195
+ }
196
+
197
+ .upload-area h4 {
198
+ margin: 0 0 8px 0;
199
+ color: #333;
200
+ font-weight: 600;
201
+ font-size: 16px;
202
+ }
203
+
204
+ .upload-area p {
205
+ margin: 0 0 16px 0;
206
+ color: #666;
207
+ font-size: 14px;
208
+ }
209
+
210
+ .format-tag {
211
+ color: white;
212
+ padding: 4px 12px;
213
+ border-radius: 16px;
214
+ font-size: 12px;
215
+ font-weight: 600;
216
+ }
217
+
218
+ .xorb-tag {
219
+ background: #28a745;
220
+ }
221
+
222
+ .shard-tag {
223
+ background: #007bff;
224
+ }
225
+
226
+ @media (max-width: 768px) {
227
+ .upload-areas {
228
+ grid-template-columns: 1fr;
229
+ gap: 20px;
230
+ }
231
+
232
+ .upload-area {
233
+ padding: 20px 15px;
234
+ min-height: 150px;
235
+ }
236
+
237
+ .upload-icon {
238
+ font-size: 28px;
239
+ }
240
+ }
241
+ </style>
src/lib/components/ShardViewer.svelte ADDED
@@ -0,0 +1,543 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <script lang="ts">
2
+ import type { ShardData } from "../types.js";
3
+ import { formatBytes, formatHash, formatTimestamp } from "../parsers.js";
4
+
5
+ export let data: ShardData;
6
+ export let filename: string;
7
+ export let fileSize: number;
8
+
9
+ $: header = data.header;
10
+ $: footer = data.footer;
11
+ $: totalFiles = data.file_info.length;
12
+ $: totalXorbs = data.cas_info.length;
13
+ $: totalChunks = data.cas_info.reduce(
14
+ (sum, cas) => sum + cas.header.num_entries,
15
+ 0
16
+ );
17
+
18
+ // Check verification and metadata status across all files
19
+ $: {
20
+ const filesWithVerification = data.file_info.filter(
21
+ (fileInfo) => fileInfo.header.file_flags & 0x80000000
22
+ ).length;
23
+ const filesWithMetadata = data.file_info.filter(
24
+ (fileInfo) => fileInfo.header.file_flags & 0x40000000
25
+ ).length;
26
+
27
+ verificationStatus =
28
+ filesWithVerification === totalFiles
29
+ ? "✅"
30
+ : filesWithVerification === 0
31
+ ? "❌"
32
+ : "❓";
33
+
34
+ metadataStatus =
35
+ filesWithMetadata === totalFiles
36
+ ? "✅"
37
+ : filesWithMetadata === 0
38
+ ? "❌"
39
+ : "❓";
40
+ }
41
+
42
+ let verificationStatus = "";
43
+ let metadataStatus = "";
44
+ $: totalStoredBytes = data.cas_info.reduce(
45
+ (sum, cas) => sum + cas.header.num_bytes_in_cas,
46
+ 0
47
+ );
48
+
49
+ // Track which files are expanded
50
+ let expandedFiles = new Set<number>();
51
+
52
+ function toggleFileExpansion(fileIndex: number) {
53
+ if (expandedFiles.has(fileIndex)) {
54
+ expandedFiles.delete(fileIndex);
55
+ } else {
56
+ expandedFiles.add(fileIndex);
57
+ }
58
+ expandedFiles = expandedFiles; // Trigger reactivity
59
+ }
60
+ </script>
61
+
62
+ <div class="shard-viewer">
63
+ <div class="header">
64
+ <h2>🗂️ Shard File Analysis</h2>
65
+ <div class="file-info">
66
+ <span class="filename">{filename}</span>
67
+ <span class="file-size">{formatBytes(fileSize)}</span>
68
+ </div>
69
+ </div>
70
+
71
+ <div class="metadata-grid">
72
+ <div class="metadata-section">
73
+ <h3>Header Information</h3>
74
+ <div class="metadata-item">
75
+ <span class="label">Version:</span>
76
+ <span class="value">{header.version}</span>
77
+ </div>
78
+ <div class="metadata-item">
79
+ <span class="label">Footer Size:</span>
80
+ <span class="value">{formatBytes(header.footer_size)}</span>
81
+ </div>
82
+ </div>
83
+
84
+ <div class="metadata-section">
85
+ <h3>Content Statistics</h3>
86
+ <div class="metadata-item">
87
+ <span class="label">Files:</span>
88
+ <span class="value">{totalFiles.toLocaleString()}</span>
89
+ </div>
90
+ <div class="metadata-item">
91
+ <span class="label">XORBs:</span>
92
+ <span class="value">{totalXorbs.toLocaleString()}</span>
93
+ </div>
94
+ <div class="metadata-item">
95
+ <span class="label">Total Chunks:</span>
96
+ <span class="value">{totalChunks.toLocaleString()}</span>
97
+ </div>
98
+ <div class="metadata-item">
99
+ <span class="label">Stored Bytes:</span>
100
+ <span class="value">{formatBytes(totalStoredBytes)}</span>
101
+ </div>
102
+ </div>
103
+
104
+ <div class="metadata-section">
105
+ <h3>Timestamps & Security</h3>
106
+ <div class="metadata-item">
107
+ <span class="label">Created:</span>
108
+ <span class="value"
109
+ >{formatTimestamp(footer.shard_creation_timestamp)}</span
110
+ >
111
+ </div>
112
+ <div class="metadata-item">
113
+ <span class="label">Key Expiry:</span>
114
+ <span class="value">{formatTimestamp(footer.shard_key_expiry)}</span>
115
+ </div>
116
+ <div class="metadata-item">
117
+ <span class="label">HMAC Key:</span>
118
+ <span class="value hash" title={formatHash(footer.chunk_hash_hmac_key)}>
119
+ {formatHash(footer.chunk_hash_hmac_key)}
120
+ </span>
121
+ </div>
122
+ </div>
123
+ </div>
124
+
125
+ {#if data.file_info.length > 0}
126
+ <div class="file-details">
127
+ <h3>
128
+ File Information ({data.file_info.length} files) - Verification: {verificationStatus}
129
+ Metadata: {metadataStatus}
130
+ </h3>
131
+ <div class="table-container">
132
+ <table class="data-table">
133
+ <thead>
134
+ <tr>
135
+ <th>File Hash</th>
136
+ <th>Entries</th>
137
+ <th>Total Length</th>
138
+ <th>SHA256</th>
139
+ </tr>
140
+ </thead>
141
+ <tbody>
142
+ {#each data.file_info as fileInfo, fileIndex}
143
+ <tr
144
+ class="file-row"
145
+ class:expanded={expandedFiles.has(fileIndex)}
146
+ on:click={() => toggleFileExpansion(fileIndex)}
147
+ role="button"
148
+ tabindex="0"
149
+ on:keydown={(e) =>
150
+ e.key === "Enter" && toggleFileExpansion(fileIndex)}
151
+ >
152
+ <td class="hash" title={formatHash(fileInfo.header.file_hash)}>
153
+ <span class="expand-icon">
154
+ {expandedFiles.has(fileIndex) ? "▼" : "▶"}
155
+ </span>
156
+ {formatHash(fileInfo.header.file_hash)}
157
+ </td>
158
+ <td>{fileInfo.header.num_entries}</td>
159
+ <td>
160
+ {formatBytes(
161
+ fileInfo.entries.reduce(
162
+ (sum, entry) => sum + entry.unpacked_segment_bytes,
163
+ 0
164
+ )
165
+ )}
166
+ </td>
167
+ <td>
168
+ {#if fileInfo.metadata_ext}
169
+ <span
170
+ class="hash"
171
+ title={formatHash(fileInfo.metadata_ext.sha256)}
172
+ >
173
+ {formatHash(fileInfo.metadata_ext.sha256)}
174
+ </span>
175
+ {:else}
176
+ <span class="no-data">—</span>
177
+ {/if}
178
+ </td>
179
+ </tr>
180
+ {#if expandedFiles.has(fileIndex)}
181
+ <tr class="file-details-row">
182
+ <td colspan="4">
183
+ <div class="file-entries-container">
184
+ <div class="entries-table-container">
185
+ <h4>Data Entries for File</h4>
186
+ <table class="entries-table">
187
+ <thead>
188
+ <tr>
189
+ <th>Entry #</th>
190
+ <th>CAS Hash</th>
191
+ <th>Chunk Range</th>
192
+ <th>Unpacked Size</th>
193
+ </tr>
194
+ </thead>
195
+ <tbody>
196
+ {#each fileInfo.entries as entry, entryIndex}
197
+ <tr>
198
+ <td>{entryIndex + 1}</td>
199
+ <td
200
+ class="hash"
201
+ title={formatHash(entry.cas_hash)}
202
+ >
203
+ {formatHash(entry.cas_hash)}
204
+ </td>
205
+ <td
206
+ >{entry.chunk_index_start} - {entry.chunk_index_end}</td
207
+ >
208
+ <td
209
+ >{formatBytes(
210
+ entry.unpacked_segment_bytes
211
+ )}</td
212
+ >
213
+ </tr>
214
+ {/each}
215
+ </tbody>
216
+ </table>
217
+ </div>
218
+ </div>
219
+ </td>
220
+ </tr>
221
+ {/if}
222
+ {/each}
223
+ </tbody>
224
+ </table>
225
+ </div>
226
+ </div>
227
+ {/if}
228
+
229
+ {#if data.cas_info.length > 0}
230
+ <div class="cas-details">
231
+ <h3>CAS Information ({data.cas_info.length} XORBs)</h3>
232
+ <div class="table-container">
233
+ <table class="data-table">
234
+ <thead>
235
+ <tr>
236
+ <th>CAS Hash</th>
237
+ <th>Chunks</th>
238
+ <th>Bytes in CAS</th>
239
+ <th>Bytes on Disk</th>
240
+ <th>Compression</th>
241
+ </tr>
242
+ </thead>
243
+ <tbody>
244
+ {#each data.cas_info as casInfo}
245
+ <tr>
246
+ <td class="hash" title={formatHash(casInfo.header.cas_hash)}>
247
+ {formatHash(casInfo.header.cas_hash)}
248
+ </td>
249
+ <td>{casInfo.header.num_entries}</td>
250
+ <td>{formatBytes(casInfo.header.num_bytes_in_cas)}</td>
251
+ <td>{formatBytes(casInfo.header.num_bytes_on_disk)}</td>
252
+ <td>
253
+ {casInfo.header.num_bytes_in_cas > 0
254
+ ? (
255
+ (casInfo.header.num_bytes_on_disk /
256
+ casInfo.header.num_bytes_in_cas) *
257
+ 100
258
+ ).toFixed(1) + "%"
259
+ : "N/A"}
260
+ </td>
261
+ </tr>
262
+ {/each}
263
+ </tbody>
264
+ </table>
265
+ </div>
266
+ </div>
267
+ {/if}
268
+ </div>
269
+
270
+ <style>
271
+ .shard-viewer {
272
+ max-width: 1200px;
273
+ margin: 0 auto;
274
+ padding: 20px;
275
+ }
276
+
277
+ .header {
278
+ display: flex;
279
+ justify-content: space-between;
280
+ align-items: center;
281
+ margin-bottom: 30px;
282
+ padding-bottom: 20px;
283
+ border-bottom: 2px solid #eee;
284
+ }
285
+
286
+ .header h2 {
287
+ margin: 0;
288
+ color: #333;
289
+ font-size: 24px;
290
+ }
291
+
292
+ .file-info {
293
+ display: flex;
294
+ flex-direction: column;
295
+ align-items: flex-end;
296
+ gap: 4px;
297
+ }
298
+
299
+ .filename {
300
+ font-weight: 600;
301
+ color: #555;
302
+ }
303
+
304
+ .file-size {
305
+ font-size: 14px;
306
+ color: #888;
307
+ }
308
+
309
+ .metadata-grid {
310
+ display: grid;
311
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
312
+ gap: 20px;
313
+ margin-bottom: 30px;
314
+ }
315
+
316
+ .metadata-section {
317
+ background: #f8f9fa;
318
+ padding: 20px;
319
+ border-radius: 8px;
320
+ border: 1px solid #e9ecef;
321
+ }
322
+
323
+ .metadata-section h3 {
324
+ margin: 0 0 15px 0;
325
+ color: #495057;
326
+ font-size: 18px;
327
+ font-weight: 600;
328
+ }
329
+
330
+ .metadata-item {
331
+ display: flex;
332
+ justify-content: space-between;
333
+ align-items: center;
334
+ margin-bottom: 12px;
335
+ padding: 8px 0;
336
+ border-bottom: 1px solid #e9ecef;
337
+ }
338
+
339
+ .metadata-item:last-child {
340
+ border-bottom: none;
341
+ margin-bottom: 0;
342
+ }
343
+
344
+ .metadata-item .label {
345
+ font-weight: 500;
346
+ color: #6c757d;
347
+ margin-right: 10px;
348
+ }
349
+
350
+ .metadata-item .value {
351
+ font-family: "Monaco", "Consolas", monospace;
352
+ font-size: 14px;
353
+ color: #212529;
354
+ text-align: right;
355
+ }
356
+
357
+ .hash {
358
+ font-family: "Monaco", "Consolas", monospace;
359
+ background: #e9ecef;
360
+ padding: 2px 6px;
361
+ border-radius: 4px;
362
+ cursor: help;
363
+ font-size: 12px;
364
+ }
365
+
366
+ .file-details,
367
+ .cas-details {
368
+ margin-top: 30px;
369
+ }
370
+
371
+ .file-details h3,
372
+ .cas-details h3 {
373
+ margin: 0 0 20px 0;
374
+ color: #495057;
375
+ font-size: 18px;
376
+ font-weight: 600;
377
+ }
378
+
379
+ .table-container {
380
+ overflow-x: auto;
381
+ border-radius: 8px;
382
+ border: 1px solid #e9ecef;
383
+ }
384
+
385
+ .data-table {
386
+ width: 100%;
387
+ border-collapse: collapse;
388
+ background: white;
389
+ }
390
+
391
+ .data-table th,
392
+ .data-table td {
393
+ padding: 12px;
394
+ text-align: left;
395
+ border-bottom: 1px solid #e9ecef;
396
+ }
397
+
398
+ .data-table th {
399
+ background: #f8f9fa;
400
+ font-weight: 600;
401
+ color: #495057;
402
+ }
403
+
404
+ .data-table tr:hover {
405
+ background: #f8f9fa;
406
+ }
407
+
408
+ .no-data {
409
+ color: #999;
410
+ font-style: italic;
411
+ }
412
+
413
+ /* Expandable file rows */
414
+ .file-row {
415
+ cursor: pointer;
416
+ transition: background-color 0.2s ease;
417
+ }
418
+
419
+ .file-row:hover {
420
+ background-color: #f0f7ff !important;
421
+ }
422
+
423
+ .file-row.expanded {
424
+ background-color: #e3f2fd;
425
+ }
426
+
427
+ .expand-icon {
428
+ display: inline-block;
429
+ margin-right: 8px;
430
+ font-size: 12px;
431
+ width: 12px;
432
+ transition: transform 0.2s ease;
433
+ }
434
+
435
+ .file-details-row {
436
+ background-color: #f8f9fa;
437
+ }
438
+
439
+ .file-details-row td {
440
+ padding: 0;
441
+ }
442
+
443
+ .file-entries-container {
444
+ padding: 16px;
445
+ border-left: 3px solid #007bff;
446
+ margin-left: 12px;
447
+ max-height: 90vh;
448
+ overflow-y: auto;
449
+ overflow-x: hidden;
450
+ border-radius: 6px;
451
+ background-color: #f8f9fa;
452
+ }
453
+
454
+ /* Scrollbar styling for webkit browsers */
455
+ .file-entries-container::-webkit-scrollbar {
456
+ width: 6px;
457
+ }
458
+
459
+ .file-entries-container::-webkit-scrollbar-track {
460
+ background: #f1f1f1;
461
+ border-radius: 3px;
462
+ }
463
+
464
+ .file-entries-container::-webkit-scrollbar-thumb {
465
+ background: #c1c1c1;
466
+ border-radius: 3px;
467
+ }
468
+
469
+ .file-entries-container::-webkit-scrollbar-thumb:hover {
470
+ background: #a8a8a8;
471
+ }
472
+
473
+ .file-entries-container h4 {
474
+ margin: 0 0 12px 0;
475
+ color: #495057;
476
+ font-size: 14px;
477
+ font-weight: 600;
478
+ position: sticky;
479
+ top: 0;
480
+ background-color: #f8f9fa;
481
+ padding: 8px 0;
482
+ z-index: 1;
483
+ }
484
+
485
+ .entries-table-container {
486
+ overflow-x: auto;
487
+ border-radius: 6px;
488
+ border: 1px solid #dee2e6;
489
+ }
490
+
491
+ .entries-table {
492
+ width: 100%;
493
+ border-collapse: collapse;
494
+ background: white;
495
+ font-size: 13px;
496
+ }
497
+
498
+ .entries-table th,
499
+ .entries-table td {
500
+ padding: 8px 12px;
501
+ text-align: left;
502
+ border-bottom: 1px solid #dee2e6;
503
+ }
504
+
505
+ .entries-table th {
506
+ background: #f1f3f4;
507
+ font-weight: 600;
508
+ color: #495057;
509
+ font-size: 12px;
510
+ }
511
+
512
+ .entries-table tr:hover {
513
+ background: #f8f9fa;
514
+ }
515
+
516
+ .entries-table .hash {
517
+ font-family: "Monaco", "Menlo", "Ubuntu Mono", monospace;
518
+ font-size: 11px;
519
+ color: #6c757d;
520
+ }
521
+
522
+ @media (max-width: 768px) {
523
+ .header {
524
+ flex-direction: column;
525
+ align-items: flex-start;
526
+ gap: 10px;
527
+ }
528
+
529
+ .metadata-grid {
530
+ grid-template-columns: 1fr;
531
+ }
532
+
533
+ .metadata-item {
534
+ flex-direction: column;
535
+ align-items: flex-start;
536
+ gap: 4px;
537
+ }
538
+
539
+ .metadata-item .value {
540
+ text-align: left;
541
+ }
542
+ }
543
+ </style>
src/lib/components/XorbViewer.svelte ADDED
@@ -0,0 +1,273 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <script lang="ts">
2
+ import type { Chunk } from "../types.js";
3
+ import { formatBytes } from "../parsers.js";
4
+
5
+ export let data: Chunk[];
6
+ export let filename: string;
7
+ export let fileSize: number;
8
+
9
+ $: totalChunks = data.length;
10
+ $: totalCompressedSize = data.reduce(
11
+ (sum, chunk) => sum + chunk.header.compressed_size,
12
+ 0
13
+ );
14
+ $: totalUncompressedSize = data.reduce(
15
+ (sum, chunk) => sum + chunk.header.uncompressed_size,
16
+ 0
17
+ );
18
+ $: averageChunkSize =
19
+ totalChunks > 0 ? totalUncompressedSize / totalChunks : 0;
20
+ $: compressionRatio =
21
+ totalUncompressedSize > 0
22
+ ? (totalCompressedSize / totalUncompressedSize) * 100
23
+ : 0;
24
+
25
+ function getCompressionTypeName(type: number): string {
26
+ switch (type) {
27
+ case 0:
28
+ return "None";
29
+ case 1:
30
+ return "LZ4";
31
+ case 2:
32
+ return "Zstd";
33
+ case 3:
34
+ return "Gzip";
35
+ default:
36
+ return `Unknown (${type})`;
37
+ }
38
+ }
39
+ </script>
40
+
41
+ <div class="xorb-viewer">
42
+ <div class="header">
43
+ <h2>📦 XORB File Analysis</h2>
44
+ <div class="file-info">
45
+ <span class="filename">{filename}</span>
46
+ <span class="file-size">{formatBytes(fileSize)}</span>
47
+ </div>
48
+ </div>
49
+
50
+ <div class="metadata-grid">
51
+ <div class="metadata-section">
52
+ <h3>File Information</h3>
53
+ <div class="metadata-item">
54
+ <span class="label">File Size:</span>
55
+ <span class="value">{formatBytes(fileSize)}</span>
56
+ </div>
57
+ <div class="metadata-item">
58
+ <span class="label">Total Chunks:</span>
59
+ <span class="value">{totalChunks.toLocaleString()}</span>
60
+ </div>
61
+ </div>
62
+
63
+ <div class="metadata-section">
64
+ <h3>Compression Statistics</h3>
65
+ <div class="metadata-item">
66
+ <span class="label">Total Compressed Size:</span>
67
+ <span class="value">{formatBytes(totalCompressedSize)}</span>
68
+ </div>
69
+ <div class="metadata-item">
70
+ <span class="label">Total Uncompressed Size:</span>
71
+ <span class="value">{formatBytes(totalUncompressedSize)}</span>
72
+ </div>
73
+ <div class="metadata-item">
74
+ <span class="label">Overall Compression Ratio:</span>
75
+ <span class="value">{compressionRatio.toFixed(1)}%</span>
76
+ </div>
77
+ <div class="metadata-item">
78
+ <span class="label">Average Chunk Size:</span>
79
+ <span class="value">{formatBytes(averageChunkSize)}</span>
80
+ </div>
81
+ </div>
82
+ </div>
83
+
84
+ {#if data.length > 0}
85
+ <div class="chunk-details">
86
+ <h3>Chunk Headers ({data.length} chunks)</h3>
87
+ <div class="chunk-table-container">
88
+ <table class="chunk-table">
89
+ <thead>
90
+ <tr>
91
+ <th>Index</th>
92
+ <th>Version</th>
93
+ <th>Compression Type</th>
94
+ <th>Compressed Size</th>
95
+ <th>Uncompressed Size</th>
96
+ <th>Compression Ratio</th>
97
+ </tr>
98
+ </thead>
99
+ <tbody>
100
+ {#each data as chunk, i}
101
+ <tr>
102
+ <td>{i}</td>
103
+ <td>{chunk.header.version}</td>
104
+ <td>{getCompressionTypeName(chunk.header.compression_type)}</td>
105
+ <td>{formatBytes(chunk.header.compressed_size)}</td>
106
+ <td>{formatBytes(chunk.header.uncompressed_size)}</td>
107
+ <td>
108
+ {chunk.header.uncompressed_size > 0
109
+ ? (
110
+ (chunk.header.compressed_size /
111
+ chunk.header.uncompressed_size) *
112
+ 100
113
+ ).toFixed(1) + "%"
114
+ : "N/A"}
115
+ </td>
116
+ </tr>
117
+ {/each}
118
+ </tbody>
119
+ </table>
120
+ </div>
121
+ </div>
122
+ {/if}
123
+ </div>
124
+
125
+ <style>
126
+ .xorb-viewer {
127
+ max-width: 1200px;
128
+ margin: 0 auto;
129
+ padding: 20px;
130
+ }
131
+
132
+ .header {
133
+ display: flex;
134
+ justify-content: space-between;
135
+ align-items: center;
136
+ margin-bottom: 30px;
137
+ padding-bottom: 20px;
138
+ border-bottom: 2px solid #eee;
139
+ }
140
+
141
+ .header h2 {
142
+ margin: 0;
143
+ color: #333;
144
+ font-size: 24px;
145
+ }
146
+
147
+ .file-info {
148
+ display: flex;
149
+ flex-direction: column;
150
+ align-items: flex-end;
151
+ gap: 4px;
152
+ }
153
+
154
+ .filename {
155
+ font-weight: 600;
156
+ color: #555;
157
+ }
158
+
159
+ .file-size {
160
+ font-size: 14px;
161
+ color: #888;
162
+ }
163
+
164
+ .metadata-grid {
165
+ display: grid;
166
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
167
+ gap: 20px;
168
+ margin-bottom: 30px;
169
+ }
170
+
171
+ .metadata-section {
172
+ background: #f8f9fa;
173
+ padding: 20px;
174
+ border-radius: 8px;
175
+ border: 1px solid #e9ecef;
176
+ }
177
+
178
+ .metadata-section h3 {
179
+ margin: 0 0 15px 0;
180
+ color: #495057;
181
+ font-size: 18px;
182
+ font-weight: 600;
183
+ }
184
+
185
+ .metadata-item {
186
+ display: flex;
187
+ justify-content: space-between;
188
+ align-items: center;
189
+ margin-bottom: 12px;
190
+ padding: 8px 0;
191
+ border-bottom: 1px solid #e9ecef;
192
+ }
193
+
194
+ .metadata-item:last-child {
195
+ border-bottom: none;
196
+ margin-bottom: 0;
197
+ }
198
+
199
+ .metadata-item .label {
200
+ font-weight: 500;
201
+ color: #6c757d;
202
+ margin-right: 10px;
203
+ }
204
+
205
+ .metadata-item .value {
206
+ font-family: "Monaco", "Consolas", monospace;
207
+ font-size: 14px;
208
+ color: #212529;
209
+ text-align: right;
210
+ }
211
+
212
+ .chunk-details {
213
+ margin-top: 30px;
214
+ }
215
+
216
+ .chunk-details h3 {
217
+ margin: 0 0 20px 0;
218
+ color: #495057;
219
+ font-size: 18px;
220
+ font-weight: 600;
221
+ }
222
+
223
+ .chunk-table-container {
224
+ overflow-x: auto;
225
+ border-radius: 8px;
226
+ border: 1px solid #e9ecef;
227
+ }
228
+
229
+ .chunk-table {
230
+ width: 100%;
231
+ border-collapse: collapse;
232
+ background: white;
233
+ }
234
+
235
+ .chunk-table th,
236
+ .chunk-table td {
237
+ padding: 12px;
238
+ text-align: left;
239
+ border-bottom: 1px solid #e9ecef;
240
+ }
241
+
242
+ .chunk-table th {
243
+ background: #f8f9fa;
244
+ font-weight: 600;
245
+ color: #495057;
246
+ }
247
+
248
+ .chunk-table tr:hover {
249
+ background: #f8f9fa;
250
+ }
251
+
252
+ @media (max-width: 768px) {
253
+ .header {
254
+ flex-direction: column;
255
+ align-items: flex-start;
256
+ gap: 10px;
257
+ }
258
+
259
+ .metadata-grid {
260
+ grid-template-columns: 1fr;
261
+ }
262
+
263
+ .metadata-item {
264
+ flex-direction: column;
265
+ align-items: flex-start;
266
+ gap: 4px;
267
+ }
268
+
269
+ .metadata-item .value {
270
+ text-align: left;
271
+ }
272
+ }
273
+ </style>
src/lib/index.ts ADDED
@@ -0,0 +1 @@
 
 
1
+ // place files you want to import through the `$lib` alias in this folder.
src/lib/parsers.ts ADDED
@@ -0,0 +1,412 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Binary parsers for xorb and shard files
2
+
3
+ import type {
4
+ ParsedFileMetadata,
5
+ Chunk,
6
+ ChunkHeader,
7
+ ShardData,
8
+ MerkleHash,
9
+ HMACKey,
10
+ MDBShardFileHeader,
11
+ MDBShardFileFooter,
12
+ FileDataSequenceHeader,
13
+ FileDataSequenceEntry,
14
+ FileVerificationEntry,
15
+ FileMetadataExt,
16
+ CASChunkSequenceHeader,
17
+ CASChunkSequenceEntry,
18
+ MDBFileInfo,
19
+ MDBCASInfo,
20
+ } from "./types.js";
21
+ import { MDB_SHARD_HEADER_TAG, XORB_IDENT } from "./types.js";
22
+
23
+ export class BinaryReader {
24
+ private data: Uint8Array;
25
+ private offset: number = 0;
26
+
27
+ constructor(data: Uint8Array) {
28
+ this.data = data;
29
+ }
30
+
31
+ readUint8(): number {
32
+ if (this.offset >= this.data.length) {
33
+ console.trace();
34
+ throw new Error("Unexpected end of data");
35
+ }
36
+ return this.data[this.offset++];
37
+ }
38
+
39
+ readUint32LE(): number {
40
+ if (this.offset + 4 > this.data.length) {
41
+ console.trace();
42
+ throw new Error("Unexpected end of data");
43
+ }
44
+ const result = new DataView(this.data.buffer).getUint32(this.offset, true);
45
+ this.offset += 4;
46
+ return result;
47
+ }
48
+
49
+ readUint64LE(): bigint {
50
+ if (this.offset + 8 > this.data.length) {
51
+ console.trace();
52
+ throw new Error("Unexpected end of data");
53
+ }
54
+ const result = new DataView(this.data.buffer).getBigUint64(
55
+ this.offset,
56
+ true
57
+ );
58
+ this.offset += 8;
59
+ return result;
60
+ }
61
+
62
+ readBytes(length: number): Uint8Array {
63
+ if (this.offset + length > this.data.length) {
64
+ console.trace();
65
+ throw new Error("Unexpected end of data");
66
+ }
67
+ const result = this.data.slice(this.offset, this.offset + length);
68
+ this.offset += length;
69
+ return result;
70
+ }
71
+
72
+ readHash(): MerkleHash {
73
+ return { data: this.readBytes(32) };
74
+ }
75
+
76
+ readHMACKey(): HMACKey {
77
+ return { data: this.readBytes(32) };
78
+ }
79
+
80
+ readString(length: number): string {
81
+ const bytes = this.readBytes(length);
82
+ return new TextDecoder().decode(bytes);
83
+ }
84
+
85
+ seek(position: number): void {
86
+ this.offset = position;
87
+ }
88
+
89
+ seekFromEnd(offsetFromEnd: number): void {
90
+ this.offset = this.data.length - offsetFromEnd;
91
+ }
92
+
93
+ get position(): number {
94
+ return this.offset;
95
+ }
96
+
97
+ get remaining(): number {
98
+ return this.data.length - this.offset;
99
+ }
100
+ }
101
+
102
+ function arraysEqual(a: Uint8Array, b: Uint8Array): boolean {
103
+ if (a.length !== b.length) return false;
104
+ for (let i = 0; i < a.length; i++) {
105
+ if (a[i] !== b[i]) return false;
106
+ }
107
+ return true;
108
+ }
109
+
110
+ function isBookendHash(hash: MerkleHash): boolean {
111
+ // Bookend hash is all 0xFF bytes
112
+ return hash.data.every((byte) => byte === 0xff);
113
+ }
114
+
115
+ function reorderHash(hash: MerkleHash): Array<number> {
116
+ const data = new Array(32);
117
+ let start = 0;
118
+ for (let i = 0; i < 32; i++) {
119
+ if (i % 8 === 0) {
120
+ start += 8;
121
+ }
122
+ data.push(hash.data[start - (i % 8) - 1]);
123
+ }
124
+ return data;
125
+ }
126
+
127
+ function formatHash(hash: MerkleHash): string {
128
+ return reorderHash(hash)
129
+ .map((b) => b.toString(16).padStart(2, "0"))
130
+ .join("");
131
+ }
132
+
133
+ // File type detection removed - type is now specified by user selection
134
+
135
+ function parseXorbFile(data: Uint8Array): Chunk[] {
136
+ const reader = new BinaryReader(data);
137
+
138
+ const chunks: Chunk[] = [];
139
+
140
+ while (reader.remaining > 0) {
141
+ // Check if we have enough bytes for a header
142
+ if (reader.remaining < 8) {
143
+ console.error("Unexpected end of data parsing xorb file");
144
+ break;
145
+ }
146
+
147
+ const header_bytes = reader.readBytes(8);
148
+ let is_xorb_ident = true;
149
+ // Urgh how do I compare two Uint8Arrays?
150
+ for (let i = 0; i < 7; i++) {
151
+ if (header_bytes[i] !== XORB_IDENT[i]) {
152
+ is_xorb_ident = false;
153
+ break;
154
+ }
155
+ }
156
+ if (is_xorb_ident) {
157
+ // reached optional xorb footer, skip rest
158
+ break;
159
+ }
160
+
161
+ const header = new DataView(header_bytes.buffer);
162
+
163
+ const version = header.getUint8(0);
164
+ const compressed_size =
165
+ header.getUint8(1) |
166
+ (header.getUint8(2) << 8) |
167
+ (header.getUint8(3) << 16);
168
+ const compression_type = header.getUint8(4);
169
+ const uncompressed_size =
170
+ header.getUint8(5) |
171
+ (header.getUint8(6) << 8) |
172
+ (header.getUint8(7) << 16);
173
+
174
+ const chunkHeader: ChunkHeader = {
175
+ version,
176
+ compressed_size,
177
+ compression_type,
178
+ uncompressed_size,
179
+ };
180
+
181
+ const compressed_data = reader.readBytes(compressed_size);
182
+
183
+ chunks.push({ header: chunkHeader, compressed_data });
184
+ }
185
+
186
+ return chunks;
187
+ }
188
+
189
+ function parseShardFile(data: Uint8Array): ShardData {
190
+ const reader = new BinaryReader(data);
191
+
192
+ // Parse header
193
+ const tag = reader.readBytes(32);
194
+ if (!arraysEqual(tag, MDB_SHARD_HEADER_TAG)) {
195
+ throw new Error("Invalid shard file header tag");
196
+ }
197
+
198
+ const header: MDBShardFileHeader = {
199
+ tag,
200
+ version: Number(reader.readUint64LE()),
201
+ footer_size: Number(reader.readUint64LE()),
202
+ };
203
+
204
+ if (header.version !== 2) {
205
+ throw new Error(`Unsupported shard header version: ${header.version}`);
206
+ }
207
+
208
+ // Parse footer (from end of file)
209
+ reader.seekFromEnd(header.footer_size);
210
+ const version = Number(reader.readUint64LE());
211
+ const file_info_offset = Number(reader.readUint64LE());
212
+ const cas_info_offset = Number(reader.readUint64LE());
213
+
214
+ // Skip first buffer (48 bytes)
215
+ reader.readBytes(48);
216
+
217
+ const chunk_hash_hmac_key = reader.readHMACKey();
218
+ const shard_creation_timestamp = Number(reader.readUint64LE());
219
+ const shard_key_expiry = Number(reader.readUint64LE());
220
+
221
+ // Skip second buffer (72 bytes)
222
+ reader.readBytes(72);
223
+
224
+ const footer_offset = Number(reader.readUint64LE());
225
+
226
+ const footer: MDBShardFileFooter = {
227
+ version,
228
+ file_info_offset,
229
+ cas_info_offset,
230
+ chunk_hash_hmac_key,
231
+ shard_creation_timestamp,
232
+ shard_key_expiry,
233
+ footer_offset,
234
+ };
235
+
236
+ if (footer.version !== 1) {
237
+ throw new Error(`Unsupported shard footer version: ${footer.version}`);
238
+ }
239
+
240
+ // Parse file info section
241
+ const file_info: MDBFileInfo[] = [];
242
+ reader.seek(footer.file_info_offset);
243
+
244
+ while (reader.position < footer.cas_info_offset) {
245
+ const pos = reader.position;
246
+ const file_hash = reader.readHash();
247
+
248
+ // Check for bookend
249
+ if (isBookendHash(file_hash)) {
250
+ reader.readBytes(16); // unused
251
+ break;
252
+ }
253
+
254
+ const file_flags = reader.readUint32LE();
255
+ const num_entries = reader.readUint32LE();
256
+ const _unused = reader.readBytes(8);
257
+
258
+ const header: FileDataSequenceHeader = {
259
+ file_hash,
260
+ file_flags,
261
+ num_entries,
262
+ _unused,
263
+ };
264
+
265
+ // Read entries
266
+ const entries: FileDataSequenceEntry[] = [];
267
+ for (let i = 0; i < num_entries; i++) {
268
+ const cas_hash = reader.readHash();
269
+ const cas_flags = reader.readUint32LE();
270
+ const unpacked_segment_bytes = reader.readUint32LE();
271
+ const chunk_index_start = reader.readUint32LE();
272
+ const chunk_index_end = reader.readUint32LE();
273
+ entries.push({
274
+ cas_hash,
275
+ cas_flags,
276
+ unpacked_segment_bytes,
277
+ chunk_index_start,
278
+ chunk_index_end,
279
+ });
280
+ }
281
+
282
+ // Read verification entries if present
283
+ let verification_entries: FileVerificationEntry[] | undefined;
284
+ if (file_flags & 0x80000000) {
285
+ verification_entries = [];
286
+ for (let i = 0; i < num_entries; i++) {
287
+ verification_entries.push({
288
+ chunk_hash: reader.readHash(),
289
+ _unused: reader.readBytes(16),
290
+ });
291
+ }
292
+ }
293
+
294
+ // Read metadata extension if present
295
+ let metadata_ext: FileMetadataExt | undefined;
296
+ if (file_flags & 0x40000000) {
297
+ metadata_ext = {
298
+ sha256: reader.readHash(),
299
+ _unused: reader.readBytes(16),
300
+ };
301
+ }
302
+
303
+ file_info.push({
304
+ header,
305
+ entries,
306
+ verification_entries,
307
+ metadata_ext,
308
+ });
309
+ }
310
+
311
+ // Parse CAS info section
312
+ const cas_info: MDBCASInfo[] = [];
313
+ reader.seek(footer.cas_info_offset);
314
+
315
+ while (reader.position < footer.footer_offset) {
316
+ const cas_hash = reader.readHash();
317
+
318
+ // Check for bookend
319
+ if (isBookendHash(cas_hash)) {
320
+ break;
321
+ }
322
+
323
+ const cas_flags = reader.readUint32LE();
324
+ const num_entries = reader.readUint32LE();
325
+ const num_bytes_in_cas = reader.readUint32LE();
326
+ const num_bytes_on_disk = reader.readUint32LE();
327
+
328
+ const header: CASChunkSequenceHeader = {
329
+ cas_hash,
330
+ cas_flags,
331
+ num_entries,
332
+ num_bytes_in_cas,
333
+ num_bytes_on_disk,
334
+ };
335
+
336
+ // Read entries
337
+ const entries: CASChunkSequenceEntry[] = [];
338
+ for (let i = 0; i < num_entries; i++) {
339
+ entries.push({
340
+ chunk_hash: reader.readHash(),
341
+ chunk_byte_range_start: reader.readUint32LE(),
342
+ unpacked_segment_bytes: reader.readUint32LE(),
343
+ _unused: Number(reader.readUint64LE()),
344
+ });
345
+ }
346
+
347
+ cas_info.push({
348
+ header,
349
+ entries,
350
+ });
351
+ }
352
+
353
+ return {
354
+ header,
355
+ footer,
356
+ file_info,
357
+ cas_info,
358
+ };
359
+ }
360
+
361
+ export async function parseFile(
362
+ file: File,
363
+ fileType: "xorb" | "shard"
364
+ ): Promise<ParsedFileMetadata> {
365
+ try {
366
+ const arrayBuffer = await file.arrayBuffer();
367
+ const data = new Uint8Array(arrayBuffer);
368
+
369
+ let parsedData: Chunk[] | ShardData;
370
+
371
+ if (fileType === "xorb") {
372
+ parsedData = parseXorbFile(data);
373
+ } else {
374
+ parsedData = parseShardFile(data);
375
+ }
376
+
377
+ return {
378
+ type: fileType,
379
+ filename: file.name,
380
+ fileSize: file.size,
381
+ data: parsedData,
382
+ };
383
+ } catch (error) {
384
+ return {
385
+ type: fileType,
386
+ filename: file.name,
387
+ fileSize: file.size,
388
+ data: [] as any,
389
+ error: error instanceof Error ? error.message : "Unknown error occurred",
390
+ };
391
+ }
392
+ }
393
+
394
+ // Helper functions for displaying data
395
+ export function formatBytes(bytes: number): string {
396
+ if (bytes === 0) return "0 B";
397
+ const k = 1000;
398
+ const sizes = ["B", "KB", "MB", "GB"];
399
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
400
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i];
401
+ }
402
+
403
+ export function formatTimestamp(timestamp: number): string {
404
+ return new Date(timestamp * 1000).toISOString();
405
+ }
406
+
407
+ export function formatHashShort(hash: MerkleHash): string {
408
+ const fullHash = formatHash(hash);
409
+ return fullHash.substring(0, 16) + "...";
410
+ }
411
+
412
+ export { formatHash };
src/lib/types.ts ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Type definitions for xorb and shard file formats
2
+
3
+ export interface MerkleHash {
4
+ data: Uint8Array; // 32 bytes
5
+ }
6
+
7
+ export interface HMACKey {
8
+ data: Uint8Array; // 32 bytes
9
+ }
10
+
11
+ // === XORB Types ===
12
+
13
+ export interface ChunkHeader {
14
+ version: number;
15
+ compressed_size: number;
16
+ compression_type: number;
17
+ uncompressed_size: number;
18
+ }
19
+
20
+ export interface Chunk {
21
+ header: ChunkHeader;
22
+ compressed_data: Uint8Array;
23
+ }
24
+
25
+ // === SHARD Types ===
26
+
27
+ export interface MDBShardFileHeader {
28
+ tag: Uint8Array; // 32 bytes magic number
29
+ version: number;
30
+ footer_size: number;
31
+ }
32
+
33
+ export interface MDBShardFileFooter {
34
+ version: number;
35
+ file_info_offset: number;
36
+ cas_info_offset: number;
37
+ chunk_hash_hmac_key: HMACKey;
38
+ shard_creation_timestamp: number;
39
+ shard_key_expiry: number;
40
+ footer_offset: number;
41
+ }
42
+
43
+ export interface FileDataSequenceHeader {
44
+ file_hash: MerkleHash;
45
+ file_flags: number;
46
+ num_entries: number;
47
+ _unused: Uint8Array;
48
+ }
49
+
50
+ export interface FileDataSequenceEntry {
51
+ cas_hash: MerkleHash;
52
+ cas_flags: number;
53
+ unpacked_segment_bytes: number;
54
+ chunk_index_start: number;
55
+ chunk_index_end: number;
56
+ }
57
+
58
+ export interface FileVerificationEntry {
59
+ chunk_hash: MerkleHash;
60
+ _unused: Uint8Array;
61
+ }
62
+
63
+ export interface FileMetadataExt {
64
+ sha256: MerkleHash;
65
+ _unused: Uint8Array;
66
+ }
67
+
68
+ export interface CASChunkSequenceHeader {
69
+ cas_hash: MerkleHash;
70
+ cas_flags: number;
71
+ num_entries: number;
72
+ num_bytes_in_cas: number;
73
+ num_bytes_on_disk: number;
74
+ }
75
+
76
+ export interface CASChunkSequenceEntry {
77
+ chunk_hash: MerkleHash;
78
+ chunk_byte_range_start: number;
79
+ unpacked_segment_bytes: number;
80
+ _unused: number;
81
+ }
82
+
83
+ export interface MDBFileInfo {
84
+ header: FileDataSequenceHeader;
85
+ entries: FileDataSequenceEntry[];
86
+ verification_entries?: FileVerificationEntry[];
87
+ metadata_ext?: FileMetadataExt;
88
+ }
89
+
90
+ export interface MDBCASInfo {
91
+ header: CASChunkSequenceHeader;
92
+ entries: CASChunkSequenceEntry[];
93
+ }
94
+
95
+ export interface ShardData {
96
+ header: MDBShardFileHeader;
97
+ footer: MDBShardFileFooter;
98
+ file_info: MDBFileInfo[];
99
+ cas_info: MDBCASInfo[];
100
+ }
101
+
102
+ // === Parsed Metadata for Display ===
103
+
104
+ export interface ParsedFileMetadata {
105
+ type: "xorb" | "shard";
106
+ filename: string;
107
+ fileSize: number;
108
+ data: Chunk[] | ShardData;
109
+ error?: string;
110
+ }
111
+
112
+ // File type detection
113
+ export const MDB_SHARD_HEADER_TAG = new Uint8Array([
114
+ 0x48, 0x46, 0x52, 0x65, 0x70, 0x6f, 0x4d, 0x65, 0x74, 0x61, 0x44, 0x61, 0x74,
115
+ 0x61, 0x00, 0x55, 0x69, 0x67, 0x45, 0x6a, 0x7b, 0x81, 0x57, 0x83, 0xa5, 0xbd,
116
+ 0xd9, 0x5c, 0xcd, 0xd1, 0x4a, 0xa9,
117
+ ]);
118
+
119
+ export const XORB_IDENT = new Uint8Array([88, 69, 84, 66, 76, 79, 66]);
src/main.ts DELETED
@@ -1,9 +0,0 @@
1
- import { mount } from 'svelte'
2
- import './app.css'
3
- import App from './App.svelte'
4
-
5
- const app = mount(App, {
6
- target: document.getElementById('app')!,
7
- })
8
-
9
- export default app
 
 
 
 
 
 
 
 
 
 
src/routes/+layout.svelte ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <script lang="ts">
2
+ import favicon from '$lib/assets/favicon.svg';
3
+
4
+ let { children } = $props();
5
+ </script>
6
+
7
+ <svelte:head>
8
+ <link rel="icon" href={favicon} />
9
+ </svelte:head>
10
+
11
+ {@render children?.()}
src/routes/+page.svelte ADDED
@@ -0,0 +1,305 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <script lang="ts">
2
+ import FileUpload from "$lib/components/FileUpload.svelte";
3
+ import XorbViewer from "$lib/components/XorbViewer.svelte";
4
+ import ShardViewer from "$lib/components/ShardViewer.svelte";
5
+ import ErrorDisplay from "$lib/components/ErrorDisplay.svelte";
6
+ import { parseFile } from "$lib/parsers.js";
7
+ import type { ParsedFileMetadata, Chunk, ShardData } from "$lib/types.js";
8
+
9
+ let parsedData: ParsedFileMetadata | null = null;
10
+ let loading = false;
11
+
12
+ async function handleFileSelected(
13
+ event: CustomEvent<{ file: File; type: "xorb" | "shard" }>
14
+ ) {
15
+ loading = true;
16
+ parsedData = null;
17
+
18
+ try {
19
+ const { file, type } = event.detail;
20
+ const result = await parseFile(file, type);
21
+ parsedData = result;
22
+ } catch (error) {
23
+ parsedData = {
24
+ type: event.detail.type,
25
+ filename: event.detail.file.name,
26
+ fileSize: event.detail.file.size,
27
+ data: {} as any,
28
+ error:
29
+ error instanceof Error ? error.message : "Unknown error occurred",
30
+ };
31
+ } finally {
32
+ loading = false;
33
+ }
34
+ }
35
+
36
+ function resetView() {
37
+ parsedData = null;
38
+ loading = false;
39
+ }
40
+ </script>
41
+
42
+ <svelte:head>
43
+ <title>XORB & Shard File Viewer</title>
44
+ <meta
45
+ name="description"
46
+ content="Parse and view metadata from XORB and Shard object files"
47
+ />
48
+ </svelte:head>
49
+
50
+ <main>
51
+ <div class="container">
52
+ <header>
53
+ <h1>🔍 XORB & Shard File Viewer</h1>
54
+ <p>
55
+ Upload and analyze XORB or Shard object files to view their metadata
56
+ structure
57
+ </p>
58
+ {#if parsedData}
59
+ <button class="reset-btn" on:click={resetView}>
60
+ ← Upload New File
61
+ </button>
62
+ {/if}
63
+ </header>
64
+
65
+ {#if loading}
66
+ <div class="loading">
67
+ <div class="spinner"></div>
68
+ <p>Parsing file...</p>
69
+ </div>
70
+ {:else if parsedData}
71
+ {#if parsedData.error}
72
+ <ErrorDisplay error={parsedData.error} filename={parsedData.filename} />
73
+ {:else if parsedData.type === "xorb"}
74
+ <XorbViewer
75
+ data={parsedData.data as Chunk[]}
76
+ filename={parsedData.filename}
77
+ fileSize={parsedData.fileSize}
78
+ />
79
+ {:else if parsedData.type === "shard"}
80
+ <ShardViewer
81
+ data={parsedData.data as ShardData}
82
+ filename={parsedData.filename}
83
+ fileSize={parsedData.fileSize}
84
+ />
85
+ {/if}
86
+ {:else}
87
+ <FileUpload on:fileSelected={handleFileSelected} />
88
+
89
+ <div class="info-section">
90
+ <h2>Supported File Types</h2>
91
+ <div class="file-types">
92
+ <div class="file-type">
93
+ <h3>📦 XORB Files</h3>
94
+ <p>
95
+ XORB (Xet Orb) files contain collections of compressed chunks with
96
+ metadata. The viewer will display chunk information, compression
97
+ statistics, and structural details.
98
+ </p>
99
+ <ul>
100
+ <li>Chunk count and sizes</li>
101
+ <li>Compression ratios</li>
102
+ <li>Hash information</li>
103
+ <li>Boundary offsets</li>
104
+ </ul>
105
+ </div>
106
+
107
+ <div class="file-type">
108
+ <h3>🗂️ Shard Files</h3>
109
+ <p>
110
+ MDB Shard files store file metadata and content-addressable
111
+ storage information for efficient deduplication. The viewer shows
112
+ file and CAS details.
113
+ </p>
114
+ <ul>
115
+ <li>File information entries</li>
116
+ <li>CAS (Content Addressable Storage) data</li>
117
+ <li>Lookup tables</li>
118
+ <li>Timestamps and security keys</li>
119
+ </ul>
120
+ </div>
121
+ </div>
122
+ </div>
123
+ {/if}
124
+ </div>
125
+ </main>
126
+
127
+ <style>
128
+ :global(body) {
129
+ margin: 0;
130
+ padding: 0;
131
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
132
+ sans-serif;
133
+ background: #f5f7fa;
134
+ color: #333;
135
+ line-height: 1.6;
136
+ }
137
+
138
+ :global(*) {
139
+ box-sizing: border-box;
140
+ }
141
+
142
+ main {
143
+ min-height: 100vh;
144
+ padding: 20px;
145
+ }
146
+
147
+ .container {
148
+ max-width: 1200px;
149
+ margin: 0 auto;
150
+ }
151
+
152
+ header {
153
+ text-align: center;
154
+ margin-bottom: 40px;
155
+ position: relative;
156
+ }
157
+
158
+ header h1 {
159
+ font-size: 36px;
160
+ margin: 0 0 16px 0;
161
+ color: #2c3e50;
162
+ font-weight: 700;
163
+ }
164
+
165
+ header p {
166
+ font-size: 18px;
167
+ color: #6c757d;
168
+ margin: 0;
169
+ }
170
+
171
+ .reset-btn {
172
+ position: absolute;
173
+ left: 0;
174
+ top: 50%;
175
+ transform: translateY(-50%);
176
+ background: #007bff;
177
+ color: white;
178
+ border: none;
179
+ padding: 10px 20px;
180
+ border-radius: 6px;
181
+ cursor: pointer;
182
+ font-size: 14px;
183
+ font-weight: 500;
184
+ transition: background-color 0.2s;
185
+ }
186
+
187
+ .reset-btn:hover {
188
+ background: #0056b3;
189
+ }
190
+
191
+ .loading {
192
+ text-align: center;
193
+ padding: 60px 20px;
194
+ }
195
+
196
+ .spinner {
197
+ width: 40px;
198
+ height: 40px;
199
+ border: 4px solid #f3f3f3;
200
+ border-top: 4px solid #007bff;
201
+ border-radius: 50%;
202
+ animation: spin 1s linear infinite;
203
+ margin: 0 auto 20px;
204
+ }
205
+
206
+ @keyframes spin {
207
+ 0% {
208
+ transform: rotate(0deg);
209
+ }
210
+ 100% {
211
+ transform: rotate(360deg);
212
+ }
213
+ }
214
+
215
+ .loading p {
216
+ color: #6c757d;
217
+ font-size: 16px;
218
+ margin: 0;
219
+ }
220
+
221
+ .info-section {
222
+ margin-top: 60px;
223
+ text-align: center;
224
+ }
225
+
226
+ .info-section h2 {
227
+ font-size: 24px;
228
+ color: #2c3e50;
229
+ margin: 0 0 30px 0;
230
+ }
231
+
232
+ .file-types {
233
+ display: grid;
234
+ grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
235
+ gap: 30px;
236
+ margin-top: 30px;
237
+ }
238
+
239
+ .file-type {
240
+ background: white;
241
+ padding: 30px;
242
+ border-radius: 12px;
243
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
244
+ text-align: left;
245
+ }
246
+
247
+ .file-type h3 {
248
+ font-size: 20px;
249
+ margin: 0 0 16px 0;
250
+ color: #2c3e50;
251
+ }
252
+
253
+ .file-type p {
254
+ color: #6c757d;
255
+ margin: 0 0 20px 0;
256
+ line-height: 1.6;
257
+ }
258
+
259
+ .file-type ul {
260
+ list-style: none;
261
+ padding: 0;
262
+ margin: 0;
263
+ }
264
+
265
+ .file-type li {
266
+ padding: 8px 0;
267
+ border-bottom: 1px solid #eee;
268
+ color: #495057;
269
+ }
270
+
271
+ .file-type li:before {
272
+ content: "✓";
273
+ color: #28a745;
274
+ font-weight: bold;
275
+ margin-right: 8px;
276
+ }
277
+
278
+ .file-type li:last-child {
279
+ border-bottom: none;
280
+ }
281
+
282
+ @media (max-width: 768px) {
283
+ header h1 {
284
+ font-size: 28px;
285
+ }
286
+
287
+ header p {
288
+ font-size: 16px;
289
+ }
290
+
291
+ .reset-btn {
292
+ position: static;
293
+ transform: none;
294
+ margin-top: 20px;
295
+ }
296
+
297
+ .file-types {
298
+ grid-template-columns: 1fr;
299
+ }
300
+
301
+ .file-type {
302
+ padding: 20px;
303
+ }
304
+ }
305
+ </style>
src/vite-env.d.ts DELETED
@@ -1,2 +0,0 @@
1
- /// <reference types="svelte" />
2
- /// <reference types="vite/client" />
 
 
 
static/robots.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ # allow crawling everything by default
2
+ User-agent: *
3
+ Disallow:
svelte.config.js CHANGED
@@ -1,7 +1,18 @@
1
- import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'
 
2
 
3
- export default {
4
- // Consult https://svelte.dev/docs#compile-time-svelte-preprocess
5
- // for more information about preprocessors
6
- preprocess: vitePreprocess(),
7
- }
 
 
 
 
 
 
 
 
 
 
 
1
+ import adapter from '@sveltejs/adapter-auto';
2
+ import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
3
 
4
+ /** @type {import('@sveltejs/kit').Config} */
5
+ const config = {
6
+ // Consult https://svelte.dev/docs/kit/integrations
7
+ // for more information about preprocessors
8
+ preprocess: vitePreprocess(),
9
+
10
+ kit: {
11
+ // adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list.
12
+ // If your environment is not supported, or you settled on a specific environment, switch out the adapter.
13
+ // See https://svelte.dev/docs/kit/adapters for more information about adapters.
14
+ adapter: adapter()
15
+ }
16
+ };
17
+
18
+ export default config;
tsconfig.app.json DELETED
@@ -1,20 +0,0 @@
1
- {
2
- "extends": "@tsconfig/svelte/tsconfig.json",
3
- "compilerOptions": {
4
- "target": "ESNext",
5
- "useDefineForClassFields": true,
6
- "module": "ESNext",
7
- "resolveJsonModule": true,
8
- /**
9
- * Typecheck JS in `.svelte` and `.js` files by default.
10
- * Disable checkJs if you'd like to use dynamic types in JS.
11
- * Note that setting allowJs false does not prevent the use
12
- * of JS in `.svelte` files.
13
- */
14
- "allowJs": true,
15
- "checkJs": true,
16
- "isolatedModules": true,
17
- "moduleDetection": "force"
18
- },
19
- "include": ["src/**/*.ts", "src/**/*.js", "src/**/*.svelte"]
20
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
tsconfig.json CHANGED
@@ -1,7 +1,19 @@
1
  {
2
- "files": [],
3
- "references": [
4
- { "path": "./tsconfig.app.json" },
5
- { "path": "./tsconfig.node.json" }
6
- ]
 
 
 
 
 
 
 
 
 
 
 
 
7
  }
 
1
  {
2
+ "extends": "./.svelte-kit/tsconfig.json",
3
+ "compilerOptions": {
4
+ "allowJs": true,
5
+ "checkJs": true,
6
+ "esModuleInterop": true,
7
+ "forceConsistentCasingInFileNames": true,
8
+ "resolveJsonModule": true,
9
+ "skipLibCheck": true,
10
+ "sourceMap": true,
11
+ "strict": true,
12
+ "moduleResolution": "bundler"
13
+ }
14
+ // Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias
15
+ // except $lib which is handled by https://svelte.dev/docs/kit/configuration#files
16
+ //
17
+ // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
18
+ // from the referenced tsconfig.json - TypeScript does not merge them in
19
  }
tsconfig.node.json DELETED
@@ -1,25 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
4
- "target": "ES2022",
5
- "lib": ["ES2023"],
6
- "module": "ESNext",
7
- "skipLibCheck": true,
8
-
9
- /* Bundler mode */
10
- "moduleResolution": "bundler",
11
- "allowImportingTsExtensions": true,
12
- "verbatimModuleSyntax": true,
13
- "moduleDetection": "force",
14
- "noEmit": true,
15
-
16
- /* Linting */
17
- "strict": true,
18
- "noUnusedLocals": true,
19
- "noUnusedParameters": true,
20
- "erasableSyntaxOnly": true,
21
- "noFallthroughCasesInSwitch": true,
22
- "noUncheckedSideEffectImports": true
23
- },
24
- "include": ["vite.config.ts"]
25
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vite.config.ts CHANGED
@@ -1,7 +1,6 @@
1
- import { defineConfig } from 'vite'
2
- import { svelte } from '@sveltejs/vite-plugin-svelte'
3
 
4
- // https://vite.dev/config/
5
  export default defineConfig({
6
- plugins: [svelte()],
7
- })
 
1
+ import { sveltekit } from '@sveltejs/kit/vite';
2
+ import { defineConfig } from 'vite';
3
 
 
4
  export default defineConfig({
5
+ plugins: [sveltekit()]
6
+ });