sliftutils 1.2.37 → 1.2.38

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/index.d.ts CHANGED
@@ -929,6 +929,9 @@ declare module "sliftutils/storage/FileFolderAPI" {
929
929
  size: number;
930
930
  lastModified: number;
931
931
  arrayBuffer(): Promise<ArrayBuffer>;
932
+ slice(start: number, end: number): {
933
+ arrayBuffer(): Promise<ArrayBuffer>;
934
+ };
932
935
  }>;
933
936
  createWritable(config?: {
934
937
  keepExistingData?: boolean;
@@ -1026,6 +1029,10 @@ declare module "sliftutils/storage/IStorage" {
1026
1029
  };
1027
1030
  export type IStorageRaw = {
1028
1031
  get(key: string): Promise<Buffer | undefined>;
1032
+ getRange(key: string, config: {
1033
+ start: number;
1034
+ end: number;
1035
+ }): Promise<Buffer | undefined>;
1029
1036
  append(key: string, value: Buffer): Promise<void>;
1030
1037
  set(key: string, value: Buffer): Promise<void>;
1031
1038
  remove(key: string): Promise<void>;
@@ -1117,6 +1124,10 @@ declare module "sliftutils/storage/PrivateFileSystemStorage" {
1117
1124
  private getFileHandle;
1118
1125
  private fileExists;
1119
1126
  get(key: string): Promise<Buffer | undefined>;
1127
+ getRange(key: string, config: {
1128
+ start: number;
1129
+ end: number;
1130
+ }): Promise<Buffer | undefined>;
1120
1131
  set(key: string, value: Buffer): Promise<void>;
1121
1132
  append(key: string, value: Buffer): Promise<void>;
1122
1133
  remove(key: string): Promise<void>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sliftutils",
3
- "version": "1.2.37",
3
+ "version": "1.2.38",
4
4
  "main": "index.js",
5
5
  "license": "MIT",
6
6
  "files": [
@@ -32,6 +32,9 @@ type FileWrapper = {
32
32
  size: number;
33
33
  lastModified: number;
34
34
  arrayBuffer(): Promise<ArrayBuffer>;
35
+ slice(start: number, end: number): {
36
+ arrayBuffer(): Promise<ArrayBuffer>;
37
+ };
35
38
  }>;
36
39
  createWritable(config?: {
37
40
  keepExistingData?: boolean;
@@ -40,6 +40,9 @@ type FileWrapper = {
40
40
  size: number;
41
41
  lastModified: number;
42
42
  arrayBuffer(): Promise<ArrayBuffer>;
43
+ // Matches Blob.slice (which the native File object provides), so the browser
44
+ // implementation works vanilla. End is exclusive, both clamped to the file size.
45
+ slice(start: number, end: number): { arrayBuffer(): Promise<ArrayBuffer> };
43
46
  }>;
44
47
  createWritable(config?: { keepExistingData?: boolean }): Promise<{
45
48
  seek(offset: number): Promise<void>;
@@ -99,13 +102,29 @@ class NodeJSFileHandleWrapper implements FileWrapper {
99
102
 
100
103
  async getFile() {
101
104
  const stats = await fs.promises.stat(this.filePath);
105
+ const filePath = this.filePath;
102
106
  return {
103
107
  size: stats.size,
104
108
  lastModified: stats.mtimeMs,
105
109
  arrayBuffer: async () => {
106
- const buffer = await fs.promises.readFile(this.filePath);
110
+ const buffer = await fs.promises.readFile(filePath);
107
111
  return buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
108
- }
112
+ },
113
+ slice: (start: number, end: number) => ({
114
+ arrayBuffer: async () => {
115
+ const clampedStart = Math.min(Math.max(start, 0), stats.size);
116
+ const clampedEnd = Math.min(Math.max(end, clampedStart), stats.size);
117
+ const length = clampedEnd - clampedStart;
118
+ const fileHandle = await fs.promises.open(filePath, "r");
119
+ try {
120
+ const buffer = Buffer.alloc(length);
121
+ await fileHandle.read(buffer, 0, length, clampedStart);
122
+ return buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
123
+ } finally {
124
+ await fileHandle.close();
125
+ }
126
+ }
127
+ })
109
128
  };
110
129
  }
111
130
 
@@ -440,6 +459,17 @@ function wrapHandleFiles(handle: DirectoryWrapper): IStorageRaw {
440
459
  }
441
460
  },
442
461
 
462
+ async getRange(key: string, config: { start: number; end: number }): Promise<Buffer | undefined> {
463
+ try {
464
+ const file = await handle.getFileHandle(key);
465
+ const fileContent = await file.getFile();
466
+ const arrayBuffer = await fileContent.slice(config.start, config.end).arrayBuffer();
467
+ return Buffer.from(arrayBuffer);
468
+ } catch (error) {
469
+ return undefined;
470
+ }
471
+ },
472
+
443
473
  async append(key: string, value: Buffer): Promise<void> {
444
474
  await appendQueue(key)(async () => {
445
475
  // NOTE: Interesting point. Chrome doesn't optimize this to be an append, and instead
@@ -27,6 +27,10 @@ export type IStorage<T> = {
27
27
  };
28
28
  export type IStorageRaw = {
29
29
  get(key: string): Promise<Buffer | undefined>;
30
+ getRange(key: string, config: {
31
+ start: number;
32
+ end: number;
33
+ }): Promise<Buffer | undefined>;
30
34
  append(key: string, value: Buffer): Promise<void>;
31
35
  set(key: string, value: Buffer): Promise<void>;
32
36
  remove(key: string): Promise<void>;
@@ -29,6 +29,9 @@ export type IStorage<T> = {
29
29
  // (/ makes a folder). And there are even more rules, such as lengths per folder, etc, etc.
30
30
  export type IStorageRaw = {
31
31
  get(key: string): Promise<Buffer | undefined>;
32
+ // Reads bytes in the range [start, end) (end is exclusive, clamped to the file size).
33
+ // Returns undefined if the file doesn't exist.
34
+ getRange(key: string, config: { start: number; end: number }): Promise<Buffer | undefined>;
32
35
  // May or may not be efficient in the underlying storage
33
36
  append(key: string, value: Buffer): Promise<void>;
34
37
  set(key: string, value: Buffer): Promise<void>;
@@ -44,6 +44,12 @@ class VirtualFileStorage implements FileStorage {
44
44
  return badBuffer;
45
45
  }
46
46
 
47
+ async getRange(key: string, config: { start: number; end: number }): Promise<Buffer | undefined> {
48
+ const fullBuffer = await this.get(key);
49
+ if (!fullBuffer) return undefined;
50
+ return fullBuffer.subarray(config.start, config.end);
51
+ }
52
+
47
53
  async append(key: string, value: Buffer): Promise<void> {
48
54
  const store = this.getStore("readwrite");
49
55
  const fullPath = this.id + key;
@@ -11,6 +11,10 @@ export declare class PrivateFileSystemStorage implements IStorageRaw {
11
11
  private getFileHandle;
12
12
  private fileExists;
13
13
  get(key: string): Promise<Buffer | undefined>;
14
+ getRange(key: string, config: {
15
+ start: number;
16
+ end: number;
17
+ }): Promise<Buffer | undefined>;
14
18
  set(key: string, value: Buffer): Promise<void>;
15
19
  append(key: string, value: Buffer): Promise<void>;
16
20
  remove(key: string): Promise<void>;
@@ -114,6 +114,17 @@ export class PrivateFileSystemStorage implements IStorageRaw {
114
114
  }
115
115
  }
116
116
 
117
+ public async getRange(key: string, config: { start: number; end: number }): Promise<Buffer | undefined> {
118
+ const fileHandle = await this.getFileHandle(key, false);
119
+ if (!fileHandle) {
120
+ return undefined;
121
+ }
122
+
123
+ const file = await fileHandle.getFile();
124
+ const arrayBuffer = await file.slice(config.start, config.end).arrayBuffer();
125
+ return Buffer.from(arrayBuffer);
126
+ }
127
+
117
128
  public async set(key: string, value: Buffer): Promise<void> {
118
129
  try {
119
130
  const fileHandle = await this.getFileHandle(key, true);