@haverstack/blob-adapter-disk 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +7 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +28 -6
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -5,13 +5,19 @@
|
|
|
5
5
|
* blobs on the local filesystem. File IDs are SHA-256 hashes
|
|
6
6
|
* of the content, enabling deduplication: if a file with the
|
|
7
7
|
* same hash already exists it is not overwritten.
|
|
8
|
+
*
|
|
9
|
+
* Writes land at their content-addressed path via a temp file
|
|
10
|
+
* plus atomic rename, so a crash mid-write can never leave a
|
|
11
|
+
* torn blob at its final, hash-named path.
|
|
8
12
|
*/
|
|
9
|
-
import type {
|
|
13
|
+
import type { FileId } from '@haverstack/core';
|
|
14
|
+
import type { StackBlobAdapter, BlobFileInfo } from '@haverstack/core/adapter';
|
|
10
15
|
export declare class DiskBlobAdapter implements StackBlobAdapter {
|
|
11
16
|
private readonly dir;
|
|
12
17
|
constructor(dir: string);
|
|
13
18
|
putAttachment(data: Uint8Array): Promise<FileId>;
|
|
14
19
|
getAttachment(fileId: FileId): Promise<Uint8Array>;
|
|
15
20
|
deleteAttachment(fileId: FileId): Promise<void>;
|
|
21
|
+
listFiles(): Promise<BlobFileInfo[]>;
|
|
16
22
|
}
|
|
17
23
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAOH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,KAAK,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAU/E,qBAAa,eAAgB,YAAW,gBAAgB;IAC1C,OAAO,CAAC,QAAQ,CAAC,GAAG;gBAAH,GAAG,EAAE,MAAM;IAIlC,aAAa,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC;IAgBhD,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAOlD,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAS/C,SAAS,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;CAU3C"}
|
package/dist/index.js
CHANGED
|
@@ -5,15 +5,20 @@
|
|
|
5
5
|
* blobs on the local filesystem. File IDs are SHA-256 hashes
|
|
6
6
|
* of the content, enabling deduplication: if a file with the
|
|
7
7
|
* same hash already exists it is not overwritten.
|
|
8
|
+
*
|
|
9
|
+
* Writes land at their content-addressed path via a temp file
|
|
10
|
+
* plus atomic rename, so a crash mid-write can never leave a
|
|
11
|
+
* torn blob at its final, hash-named path.
|
|
8
12
|
*/
|
|
9
|
-
import { createHash } from 'node:crypto';
|
|
13
|
+
import { createHash, randomBytes } from 'node:crypto';
|
|
10
14
|
import { mkdirSync, existsSync } from 'fs';
|
|
11
|
-
import { readFile, writeFile, unlink } from 'fs/promises';
|
|
15
|
+
import { readFile, writeFile, unlink, readdir, stat, rename } from 'fs/promises';
|
|
12
16
|
import { join } from 'path';
|
|
17
|
+
import { StackNotFoundError, StackQueryError } from '@haverstack/core';
|
|
13
18
|
const SHA256_HEX_RE = /^[0-9a-f]{64}$/;
|
|
14
19
|
const assertFileId = (fileId) => {
|
|
15
20
|
if (!SHA256_HEX_RE.test(fileId)) {
|
|
16
|
-
throw new
|
|
21
|
+
throw new StackQueryError(`Invalid fileId: expected 64-character lowercase hex string`);
|
|
17
22
|
}
|
|
18
23
|
};
|
|
19
24
|
export class DiskBlobAdapter {
|
|
@@ -24,15 +29,24 @@ export class DiskBlobAdapter {
|
|
|
24
29
|
}
|
|
25
30
|
async putAttachment(data) {
|
|
26
31
|
const fileId = createHash('sha256').update(data).digest('hex');
|
|
27
|
-
|
|
28
|
-
|
|
32
|
+
const finalPath = join(this.dir, fileId);
|
|
33
|
+
if (!existsSync(finalPath)) {
|
|
34
|
+
const tempPath = join(this.dir, `.tmp-${fileId}-${randomBytes(8).toString('hex')}`);
|
|
35
|
+
try {
|
|
36
|
+
await writeFile(tempPath, data);
|
|
37
|
+
await rename(tempPath, finalPath);
|
|
38
|
+
}
|
|
39
|
+
catch (err) {
|
|
40
|
+
await unlink(tempPath).catch(() => { });
|
|
41
|
+
throw err;
|
|
42
|
+
}
|
|
29
43
|
}
|
|
30
44
|
return fileId;
|
|
31
45
|
}
|
|
32
46
|
async getAttachment(fileId) {
|
|
33
47
|
assertFileId(fileId);
|
|
34
48
|
if (!existsSync(join(this.dir, fileId)))
|
|
35
|
-
throw new
|
|
49
|
+
throw new StackNotFoundError(`Attachment not found: "${fileId}"`);
|
|
36
50
|
return readFile(join(this.dir, fileId));
|
|
37
51
|
}
|
|
38
52
|
async deleteAttachment(fileId) {
|
|
@@ -44,5 +58,13 @@ export class DiskBlobAdapter {
|
|
|
44
58
|
// Non-fatal — file may already be gone.
|
|
45
59
|
}
|
|
46
60
|
}
|
|
61
|
+
async listFiles() {
|
|
62
|
+
const entries = await readdir(this.dir);
|
|
63
|
+
const fileIds = entries.filter((name) => SHA256_HEX_RE.test(name));
|
|
64
|
+
return Promise.all(fileIds.map(async (fileId) => {
|
|
65
|
+
const stats = await stat(join(this.dir, fileId));
|
|
66
|
+
return { fileId, size: stats.size, modifiedAt: stats.mtime };
|
|
67
|
+
}));
|
|
68
|
+
}
|
|
47
69
|
}
|
|
48
70
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@haverstack/blob-adapter-disk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Disk blob adapter for Haverstack",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"storage"
|
|
30
30
|
],
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@haverstack/core": "0.
|
|
32
|
+
"@haverstack/core": "^0.9.0"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"@types/node": "^22.0.0",
|