@componentor/fs 1.2.4 → 1.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +199 -236
- package/dist/index.js.map +1 -1
- package/dist/opfs-hybrid.js +199 -236
- package/dist/opfs-hybrid.js.map +1 -1
- package/dist/opfs-worker.js +199 -236
- package/dist/opfs-worker.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +26 -14
- package/src/packed-storage.ts +209 -262
- package/src/symlink-manager.ts +8 -5
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -188,10 +188,13 @@ export default class OPFS {
|
|
|
188
188
|
|
|
189
189
|
if (this.useSync) {
|
|
190
190
|
const access = await fileHandle.createSyncAccessHandle()
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
191
|
+
try {
|
|
192
|
+
const size = access.getSize()
|
|
193
|
+
buffer = new Uint8Array(size)
|
|
194
|
+
access.read(buffer)
|
|
195
|
+
} finally {
|
|
196
|
+
access.close()
|
|
197
|
+
}
|
|
195
198
|
} else {
|
|
196
199
|
const file = await fileHandle.getFile()
|
|
197
200
|
buffer = new Uint8Array(await file.arrayBuffer())
|
|
@@ -270,10 +273,13 @@ export default class OPFS {
|
|
|
270
273
|
let buffer: Uint8Array
|
|
271
274
|
if (this.useSync) {
|
|
272
275
|
const access = await fileHandle.createSyncAccessHandle()
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
276
|
+
try {
|
|
277
|
+
const size = access.getSize()
|
|
278
|
+
buffer = new Uint8Array(size)
|
|
279
|
+
access.read(buffer)
|
|
280
|
+
} finally {
|
|
281
|
+
access.close()
|
|
282
|
+
}
|
|
277
283
|
} else {
|
|
278
284
|
const file = await fileHandle.getFile()
|
|
279
285
|
buffer = new Uint8Array(await file.arrayBuffer())
|
|
@@ -311,10 +317,13 @@ export default class OPFS {
|
|
|
311
317
|
|
|
312
318
|
if (this.useSync) {
|
|
313
319
|
const access = await fileHandle!.createSyncAccessHandle()
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
320
|
+
try {
|
|
321
|
+
// Set exact size (more efficient than truncate(0) + write)
|
|
322
|
+
access.truncate(buffer.length)
|
|
323
|
+
access.write(buffer, { at: 0 })
|
|
324
|
+
} finally {
|
|
325
|
+
access.close()
|
|
326
|
+
}
|
|
318
327
|
} else {
|
|
319
328
|
const writable = await fileHandle!.createWritable()
|
|
320
329
|
await writable.write(buffer)
|
|
@@ -1037,8 +1046,11 @@ export default class OPFS {
|
|
|
1037
1046
|
|
|
1038
1047
|
if (this.useSync) {
|
|
1039
1048
|
const access = await fileHandle.createSyncAccessHandle()
|
|
1040
|
-
|
|
1041
|
-
|
|
1049
|
+
try {
|
|
1050
|
+
access.truncate(len)
|
|
1051
|
+
} finally {
|
|
1052
|
+
access.close()
|
|
1053
|
+
}
|
|
1042
1054
|
} else {
|
|
1043
1055
|
const file = await fileHandle.getFile()
|
|
1044
1056
|
const data = new Uint8Array(await file.arrayBuffer())
|
package/src/packed-storage.ts
CHANGED
|
@@ -128,7 +128,6 @@ export class PackedStorage {
|
|
|
128
128
|
private useChecksum: boolean
|
|
129
129
|
private index: PackIndex | null = null
|
|
130
130
|
private indexLoaded = false
|
|
131
|
-
private lockPromise: Promise<void> | null = null
|
|
132
131
|
|
|
133
132
|
constructor(handleManager: HandleManager, useSync: boolean, useCompression = false, useChecksum = true) {
|
|
134
133
|
this.handleManager = handleManager
|
|
@@ -138,23 +137,6 @@ export class PackedStorage {
|
|
|
138
137
|
this.useChecksum = useChecksum
|
|
139
138
|
}
|
|
140
139
|
|
|
141
|
-
/**
|
|
142
|
-
* Acquire lock for pack file access (prevents concurrent handle conflicts)
|
|
143
|
-
*/
|
|
144
|
-
private async acquireLock(): Promise<() => void> {
|
|
145
|
-
while (this.lockPromise) {
|
|
146
|
-
await this.lockPromise
|
|
147
|
-
}
|
|
148
|
-
let release: () => void
|
|
149
|
-
this.lockPromise = new Promise(resolve => {
|
|
150
|
-
release = () => {
|
|
151
|
-
this.lockPromise = null
|
|
152
|
-
resolve()
|
|
153
|
-
}
|
|
154
|
-
})
|
|
155
|
-
return release!
|
|
156
|
-
}
|
|
157
|
-
|
|
158
140
|
/**
|
|
159
141
|
* Reset pack storage state (memory only)
|
|
160
142
|
*/
|
|
@@ -256,13 +238,8 @@ export class PackedStorage {
|
|
|
256
238
|
* Check if a path exists in the pack
|
|
257
239
|
*/
|
|
258
240
|
async has(path: string): Promise<boolean> {
|
|
259
|
-
const
|
|
260
|
-
|
|
261
|
-
const index = await this.loadIndex()
|
|
262
|
-
return path in index
|
|
263
|
-
} finally {
|
|
264
|
-
release()
|
|
265
|
-
}
|
|
241
|
+
const index = await this.loadIndex()
|
|
242
|
+
return path in index
|
|
266
243
|
}
|
|
267
244
|
|
|
268
245
|
/**
|
|
@@ -270,15 +247,10 @@ export class PackedStorage {
|
|
|
270
247
|
* Returns originalSize if compressed, otherwise size
|
|
271
248
|
*/
|
|
272
249
|
async getSize(path: string): Promise<number | null> {
|
|
273
|
-
const
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
if (!entry) return null
|
|
278
|
-
return entry.originalSize ?? entry.size
|
|
279
|
-
} finally {
|
|
280
|
-
release()
|
|
281
|
-
}
|
|
250
|
+
const index = await this.loadIndex()
|
|
251
|
+
const entry = index[path]
|
|
252
|
+
if (!entry) return null
|
|
253
|
+
return entry.originalSize ?? entry.size
|
|
282
254
|
}
|
|
283
255
|
|
|
284
256
|
/**
|
|
@@ -286,40 +258,35 @@ export class PackedStorage {
|
|
|
286
258
|
* Handles decompression if file was stored compressed
|
|
287
259
|
*/
|
|
288
260
|
async read(path: string): Promise<Uint8Array | null> {
|
|
289
|
-
const
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
const entry = index[path]
|
|
293
|
-
if (!entry) return null
|
|
294
|
-
|
|
295
|
-
const { fileHandle } = await this.handleManager.getHandle(PACK_FILE)
|
|
296
|
-
if (!fileHandle) return null
|
|
261
|
+
const index = await this.loadIndex()
|
|
262
|
+
const entry = index[path]
|
|
263
|
+
if (!entry) return null
|
|
297
264
|
|
|
298
|
-
|
|
265
|
+
const { fileHandle } = await this.handleManager.getHandle(PACK_FILE)
|
|
266
|
+
if (!fileHandle) return null
|
|
299
267
|
|
|
300
|
-
|
|
301
|
-
const access = await fileHandle.createSyncAccessHandle()
|
|
302
|
-
try {
|
|
303
|
-
buffer = new Uint8Array(entry.size)
|
|
304
|
-
access.read(buffer, { at: entry.offset })
|
|
305
|
-
} finally {
|
|
306
|
-
access.close()
|
|
307
|
-
}
|
|
308
|
-
} else {
|
|
309
|
-
const file = await fileHandle.getFile()
|
|
310
|
-
const data = new Uint8Array(await file.arrayBuffer())
|
|
311
|
-
buffer = data.slice(entry.offset, entry.offset + entry.size)
|
|
312
|
-
}
|
|
268
|
+
let buffer: Uint8Array
|
|
313
269
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
270
|
+
if (this.useSync) {
|
|
271
|
+
const access = await fileHandle.createSyncAccessHandle()
|
|
272
|
+
try {
|
|
273
|
+
buffer = new Uint8Array(entry.size)
|
|
274
|
+
access.read(buffer, { at: entry.offset })
|
|
275
|
+
} finally {
|
|
276
|
+
access.close()
|
|
317
277
|
}
|
|
278
|
+
} else {
|
|
279
|
+
const file = await fileHandle.getFile()
|
|
280
|
+
const data = new Uint8Array(await file.arrayBuffer())
|
|
281
|
+
buffer = data.slice(entry.offset, entry.offset + entry.size)
|
|
282
|
+
}
|
|
318
283
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
284
|
+
// Decompress if needed
|
|
285
|
+
if (entry.originalSize !== undefined) {
|
|
286
|
+
return decompress(buffer)
|
|
322
287
|
}
|
|
288
|
+
|
|
289
|
+
return buffer
|
|
323
290
|
}
|
|
324
291
|
|
|
325
292
|
/**
|
|
@@ -331,74 +298,69 @@ export class PackedStorage {
|
|
|
331
298
|
const results = new Map<string, Uint8Array | null>()
|
|
332
299
|
if (paths.length === 0) return results
|
|
333
300
|
|
|
334
|
-
const
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
const
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
} else {
|
|
345
|
-
results.set(path, null)
|
|
346
|
-
}
|
|
301
|
+
const index = await this.loadIndex()
|
|
302
|
+
|
|
303
|
+
// Find which paths are in the pack
|
|
304
|
+
const toRead: Array<{ path: string; offset: number; size: number; originalSize?: number }> = []
|
|
305
|
+
for (const path of paths) {
|
|
306
|
+
const entry = index[path]
|
|
307
|
+
if (entry) {
|
|
308
|
+
toRead.push({ path, offset: entry.offset, size: entry.size, originalSize: entry.originalSize })
|
|
309
|
+
} else {
|
|
310
|
+
results.set(path, null)
|
|
347
311
|
}
|
|
312
|
+
}
|
|
348
313
|
|
|
349
|
-
|
|
314
|
+
if (toRead.length === 0) return results
|
|
350
315
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
}
|
|
356
|
-
return results
|
|
316
|
+
const { fileHandle } = await this.handleManager.getHandle(PACK_FILE)
|
|
317
|
+
if (!fileHandle) {
|
|
318
|
+
for (const { path } of toRead) {
|
|
319
|
+
results.set(path, null)
|
|
357
320
|
}
|
|
321
|
+
return results
|
|
322
|
+
}
|
|
358
323
|
|
|
359
|
-
|
|
360
|
-
|
|
324
|
+
// Read all files
|
|
325
|
+
const decompressPromises: Array<{ path: string; promise: Promise<Uint8Array> }> = []
|
|
361
326
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
for (const { path, offset, size, originalSize } of toRead) {
|
|
366
|
-
const buffer = new Uint8Array(size)
|
|
367
|
-
access.read(buffer, { at: offset })
|
|
368
|
-
|
|
369
|
-
if (originalSize !== undefined) {
|
|
370
|
-
// Queue for decompression
|
|
371
|
-
decompressPromises.push({ path, promise: decompress(buffer) })
|
|
372
|
-
} else {
|
|
373
|
-
results.set(path, buffer)
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
} finally {
|
|
377
|
-
access.close()
|
|
378
|
-
}
|
|
379
|
-
} else {
|
|
380
|
-
const file = await fileHandle.getFile()
|
|
381
|
-
const data = new Uint8Array(await file.arrayBuffer())
|
|
327
|
+
if (this.useSync) {
|
|
328
|
+
const access = await fileHandle.createSyncAccessHandle()
|
|
329
|
+
try {
|
|
382
330
|
for (const { path, offset, size, originalSize } of toRead) {
|
|
383
|
-
const buffer =
|
|
331
|
+
const buffer = new Uint8Array(size)
|
|
332
|
+
access.read(buffer, { at: offset })
|
|
384
333
|
|
|
385
334
|
if (originalSize !== undefined) {
|
|
335
|
+
// Queue for decompression
|
|
386
336
|
decompressPromises.push({ path, promise: decompress(buffer) })
|
|
387
337
|
} else {
|
|
388
338
|
results.set(path, buffer)
|
|
389
339
|
}
|
|
390
340
|
}
|
|
341
|
+
} finally {
|
|
342
|
+
access.close()
|
|
391
343
|
}
|
|
344
|
+
} else {
|
|
345
|
+
const file = await fileHandle.getFile()
|
|
346
|
+
const data = new Uint8Array(await file.arrayBuffer())
|
|
347
|
+
for (const { path, offset, size, originalSize } of toRead) {
|
|
348
|
+
const buffer = data.slice(offset, offset + size)
|
|
392
349
|
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
350
|
+
if (originalSize !== undefined) {
|
|
351
|
+
decompressPromises.push({ path, promise: decompress(buffer) })
|
|
352
|
+
} else {
|
|
353
|
+
results.set(path, buffer)
|
|
354
|
+
}
|
|
396
355
|
}
|
|
356
|
+
}
|
|
397
357
|
|
|
398
|
-
|
|
399
|
-
}
|
|
400
|
-
|
|
358
|
+
// Wait for all decompressions
|
|
359
|
+
for (const { path, promise } of decompressPromises) {
|
|
360
|
+
results.set(path, await promise)
|
|
401
361
|
}
|
|
362
|
+
|
|
363
|
+
return results
|
|
402
364
|
}
|
|
403
365
|
|
|
404
366
|
/**
|
|
@@ -411,86 +373,81 @@ export class PackedStorage {
|
|
|
411
373
|
async writeBatch(entries: Array<{ path: string; data: Uint8Array }>): Promise<void> {
|
|
412
374
|
if (entries.length === 0) return
|
|
413
375
|
|
|
414
|
-
const
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
processedEntries = entries
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
// Calculate total data size (using compressed sizes where applicable)
|
|
436
|
-
let totalDataSize = 0
|
|
437
|
-
for (const { data } of processedEntries) {
|
|
438
|
-
totalDataSize += data.length
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
// Build index - iterate until offsets stabilize
|
|
442
|
-
// (offset changes -> JSON length changes -> header size changes -> offset changes)
|
|
443
|
-
// Header format: [index length: 4][CRC32: 4][JSON index][file data...]
|
|
444
|
-
const newIndex: PackIndex = {}
|
|
445
|
-
let headerSize = 8 // 4 bytes index length + 4 bytes CRC32
|
|
446
|
-
let prevHeaderSize = 0
|
|
376
|
+
const encoder = new TextEncoder()
|
|
377
|
+
|
|
378
|
+
// Compress data if enabled
|
|
379
|
+
let processedEntries: Array<{ path: string; data: Uint8Array; originalSize?: number }>
|
|
380
|
+
if (this.useCompression) {
|
|
381
|
+
processedEntries = await Promise.all(
|
|
382
|
+
entries.map(async ({ path, data }) => {
|
|
383
|
+
const compressed = await compress(data)
|
|
384
|
+
// Only use compressed if it's actually smaller
|
|
385
|
+
if (compressed.length < data.length) {
|
|
386
|
+
return { path, data: compressed, originalSize: data.length }
|
|
387
|
+
}
|
|
388
|
+
return { path, data }
|
|
389
|
+
})
|
|
390
|
+
)
|
|
391
|
+
} else {
|
|
392
|
+
processedEntries = entries
|
|
393
|
+
}
|
|
447
394
|
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
395
|
+
// Calculate total data size (using compressed sizes where applicable)
|
|
396
|
+
let totalDataSize = 0
|
|
397
|
+
for (const { data } of processedEntries) {
|
|
398
|
+
totalDataSize += data.length
|
|
399
|
+
}
|
|
451
400
|
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
401
|
+
// Build index - iterate until offsets stabilize
|
|
402
|
+
// (offset changes -> JSON length changes -> header size changes -> offset changes)
|
|
403
|
+
// Header format: [index length: 4][CRC32: 4][JSON index][file data...]
|
|
404
|
+
const newIndex: PackIndex = {}
|
|
405
|
+
let headerSize = 8 // 4 bytes index length + 4 bytes CRC32
|
|
406
|
+
let prevHeaderSize = 0
|
|
407
|
+
|
|
408
|
+
// Iterate until stable (usually 2-3 iterations)
|
|
409
|
+
while (headerSize !== prevHeaderSize) {
|
|
410
|
+
prevHeaderSize = headerSize
|
|
411
|
+
|
|
412
|
+
let currentOffset = headerSize
|
|
413
|
+
for (const { path, data, originalSize } of processedEntries) {
|
|
414
|
+
const entry: PackIndexEntry = { offset: currentOffset, size: data.length }
|
|
415
|
+
if (originalSize !== undefined) {
|
|
416
|
+
entry.originalSize = originalSize
|
|
460
417
|
}
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
headerSize = 8 + indexBuf.length
|
|
418
|
+
newIndex[path] = entry
|
|
419
|
+
currentOffset += data.length
|
|
464
420
|
}
|
|
465
421
|
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
422
|
+
const indexBuf = encoder.encode(JSON.stringify(newIndex))
|
|
423
|
+
headerSize = 8 + indexBuf.length
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// Build the complete pack file
|
|
427
|
+
const finalIndexBuf = encoder.encode(JSON.stringify(newIndex))
|
|
428
|
+
const totalSize = headerSize + totalDataSize
|
|
429
|
+
const packBuffer = new Uint8Array(totalSize)
|
|
430
|
+
const view = new DataView(packBuffer.buffer)
|
|
471
431
|
|
|
472
|
-
|
|
473
|
-
|
|
432
|
+
// Write index JSON at offset 8
|
|
433
|
+
packBuffer.set(finalIndexBuf, 8)
|
|
474
434
|
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
435
|
+
// Write data at correct offsets
|
|
436
|
+
for (const { path, data } of processedEntries) {
|
|
437
|
+
const entry = newIndex[path]
|
|
438
|
+
packBuffer.set(data, entry.offset)
|
|
439
|
+
}
|
|
480
440
|
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
441
|
+
// Calculate CRC32 over content (index + data, everything after header) if enabled
|
|
442
|
+
const content = packBuffer.subarray(8)
|
|
443
|
+
const checksum = this.useChecksum ? crc32(content) : 0
|
|
484
444
|
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
445
|
+
// Write header (index length + CRC32)
|
|
446
|
+
view.setUint32(0, finalIndexBuf.length, true)
|
|
447
|
+
view.setUint32(4, checksum, true)
|
|
488
448
|
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
} finally {
|
|
492
|
-
release()
|
|
493
|
-
}
|
|
449
|
+
await this.writePackFile(packBuffer)
|
|
450
|
+
this.index = newIndex
|
|
494
451
|
}
|
|
495
452
|
|
|
496
453
|
/**
|
|
@@ -521,111 +478,101 @@ export class PackedStorage {
|
|
|
521
478
|
* Note: Doesn't reclaim space, just removes from index and recalculates CRC32
|
|
522
479
|
*/
|
|
523
480
|
async remove(path: string): Promise<boolean> {
|
|
524
|
-
const
|
|
525
|
-
|
|
526
|
-
const index = await this.loadIndex()
|
|
527
|
-
if (!(path in index)) return false
|
|
528
|
-
|
|
529
|
-
delete index[path]
|
|
530
|
-
|
|
531
|
-
const { fileHandle } = await this.handleManager.getHandle(PACK_FILE)
|
|
532
|
-
if (!fileHandle) return true
|
|
533
|
-
|
|
534
|
-
// Need to read existing file to recalculate CRC32
|
|
535
|
-
const encoder = new TextEncoder()
|
|
536
|
-
const newIndexBuf = encoder.encode(JSON.stringify(index))
|
|
537
|
-
|
|
538
|
-
if (this.useSync) {
|
|
539
|
-
const access = await fileHandle.createSyncAccessHandle()
|
|
540
|
-
try {
|
|
541
|
-
const size = access.getSize()
|
|
542
|
-
|
|
543
|
-
// Read old header to get old index length
|
|
544
|
-
const oldHeader = new Uint8Array(8)
|
|
545
|
-
access.read(oldHeader, { at: 0 })
|
|
546
|
-
const oldIndexLen = new DataView(oldHeader.buffer).getUint32(0, true)
|
|
547
|
-
|
|
548
|
-
// Read data portion (after old index)
|
|
549
|
-
const dataStart = 8 + oldIndexLen
|
|
550
|
-
const dataSize = size - dataStart
|
|
551
|
-
const dataPortion = new Uint8Array(dataSize)
|
|
552
|
-
if (dataSize > 0) {
|
|
553
|
-
access.read(dataPortion, { at: dataStart })
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
// Build new content (new index + data)
|
|
557
|
-
const newContent = new Uint8Array(newIndexBuf.length + dataSize)
|
|
558
|
-
newContent.set(newIndexBuf, 0)
|
|
559
|
-
if (dataSize > 0) {
|
|
560
|
-
newContent.set(dataPortion, newIndexBuf.length)
|
|
561
|
-
}
|
|
481
|
+
const index = await this.loadIndex()
|
|
482
|
+
if (!(path in index)) return false
|
|
562
483
|
|
|
563
|
-
|
|
564
|
-
const checksum = this.useChecksum ? crc32(newContent) : 0
|
|
484
|
+
delete index[path]
|
|
565
485
|
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
const view = new DataView(newHeader.buffer)
|
|
569
|
-
view.setUint32(0, newIndexBuf.length, true)
|
|
570
|
-
view.setUint32(4, checksum, true)
|
|
486
|
+
const { fileHandle } = await this.handleManager.getHandle(PACK_FILE)
|
|
487
|
+
if (!fileHandle) return true
|
|
571
488
|
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
newFile.set(newContent, 8)
|
|
489
|
+
// Need to read existing file to recalculate CRC32
|
|
490
|
+
const encoder = new TextEncoder()
|
|
491
|
+
const newIndexBuf = encoder.encode(JSON.stringify(index))
|
|
576
492
|
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
}
|
|
582
|
-
} else {
|
|
583
|
-
// For non-sync, rewrite the whole file
|
|
584
|
-
const file = await fileHandle.getFile()
|
|
585
|
-
const oldData = new Uint8Array(await file.arrayBuffer())
|
|
493
|
+
if (this.useSync) {
|
|
494
|
+
const access = await fileHandle.createSyncAccessHandle()
|
|
495
|
+
try {
|
|
496
|
+
const size = access.getSize()
|
|
586
497
|
|
|
587
|
-
|
|
498
|
+
// Read old header to get old index length
|
|
499
|
+
const oldHeader = new Uint8Array(8)
|
|
500
|
+
access.read(oldHeader, { at: 0 })
|
|
501
|
+
const oldIndexLen = new DataView(oldHeader.buffer).getUint32(0, true)
|
|
588
502
|
|
|
589
|
-
|
|
503
|
+
// Read data portion (after old index)
|
|
590
504
|
const dataStart = 8 + oldIndexLen
|
|
591
|
-
const
|
|
505
|
+
const dataSize = size - dataStart
|
|
506
|
+
const dataPortion = new Uint8Array(dataSize)
|
|
507
|
+
if (dataSize > 0) {
|
|
508
|
+
access.read(dataPortion, { at: dataStart })
|
|
509
|
+
}
|
|
592
510
|
|
|
593
|
-
// Build new content
|
|
594
|
-
const newContent = new Uint8Array(newIndexBuf.length +
|
|
511
|
+
// Build new content (new index + data)
|
|
512
|
+
const newContent = new Uint8Array(newIndexBuf.length + dataSize)
|
|
595
513
|
newContent.set(newIndexBuf, 0)
|
|
596
|
-
|
|
514
|
+
if (dataSize > 0) {
|
|
515
|
+
newContent.set(dataPortion, newIndexBuf.length)
|
|
516
|
+
}
|
|
597
517
|
|
|
598
|
-
// Calculate CRC32 if enabled
|
|
518
|
+
// Calculate new CRC32 if enabled
|
|
599
519
|
const checksum = this.useChecksum ? crc32(newContent) : 0
|
|
600
520
|
|
|
601
|
-
// Build new
|
|
602
|
-
const
|
|
603
|
-
const view = new DataView(
|
|
521
|
+
// Build new header
|
|
522
|
+
const newHeader = new Uint8Array(8)
|
|
523
|
+
const view = new DataView(newHeader.buffer)
|
|
604
524
|
view.setUint32(0, newIndexBuf.length, true)
|
|
605
525
|
view.setUint32(4, checksum, true)
|
|
526
|
+
|
|
527
|
+
// Write new file
|
|
528
|
+
const newFile = new Uint8Array(8 + newContent.length)
|
|
529
|
+
newFile.set(newHeader, 0)
|
|
606
530
|
newFile.set(newContent, 8)
|
|
607
531
|
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
532
|
+
access.truncate(newFile.length)
|
|
533
|
+
access.write(newFile, { at: 0 })
|
|
534
|
+
} finally {
|
|
535
|
+
access.close()
|
|
611
536
|
}
|
|
537
|
+
} else {
|
|
538
|
+
// For non-sync, rewrite the whole file
|
|
539
|
+
const file = await fileHandle.getFile()
|
|
540
|
+
const oldData = new Uint8Array(await file.arrayBuffer())
|
|
541
|
+
|
|
542
|
+
if (oldData.length < 8) return true
|
|
543
|
+
|
|
544
|
+
const oldIndexLen = new DataView(oldData.buffer).getUint32(0, true)
|
|
545
|
+
const dataStart = 8 + oldIndexLen
|
|
546
|
+
const dataPortion = oldData.subarray(dataStart)
|
|
547
|
+
|
|
548
|
+
// Build new content
|
|
549
|
+
const newContent = new Uint8Array(newIndexBuf.length + dataPortion.length)
|
|
550
|
+
newContent.set(newIndexBuf, 0)
|
|
551
|
+
newContent.set(dataPortion, newIndexBuf.length)
|
|
612
552
|
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
553
|
+
// Calculate CRC32 if enabled
|
|
554
|
+
const checksum = this.useChecksum ? crc32(newContent) : 0
|
|
555
|
+
|
|
556
|
+
// Build new file
|
|
557
|
+
const newFile = new Uint8Array(8 + newContent.length)
|
|
558
|
+
const view = new DataView(newFile.buffer)
|
|
559
|
+
view.setUint32(0, newIndexBuf.length, true)
|
|
560
|
+
view.setUint32(4, checksum, true)
|
|
561
|
+
newFile.set(newContent, 8)
|
|
562
|
+
|
|
563
|
+
const writable = await fileHandle.createWritable()
|
|
564
|
+
await writable.write(newFile)
|
|
565
|
+
await writable.close()
|
|
616
566
|
}
|
|
567
|
+
|
|
568
|
+
return true
|
|
617
569
|
}
|
|
618
570
|
|
|
619
571
|
/**
|
|
620
572
|
* Check if pack file is being used (has entries)
|
|
621
573
|
*/
|
|
622
574
|
async isEmpty(): Promise<boolean> {
|
|
623
|
-
const
|
|
624
|
-
|
|
625
|
-
const index = await this.loadIndex()
|
|
626
|
-
return Object.keys(index).length === 0
|
|
627
|
-
} finally {
|
|
628
|
-
release()
|
|
629
|
-
}
|
|
575
|
+
const index = await this.loadIndex()
|
|
576
|
+
return Object.keys(index).length === 0
|
|
630
577
|
}
|
|
631
578
|
}
|