@e280/mammoth 0.1.0-1

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.
Files changed (51) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +13 -0
  3. package/package.json +47 -0
  4. package/s/browser/example.ts +26 -0
  5. package/s/browser/iceberg-opfs.ts +50 -0
  6. package/s/browser/index.ts +4 -0
  7. package/s/browser/utils/is-not-found.ts +6 -0
  8. package/s/core/hash-stream.ts +19 -0
  9. package/s/core/iceberg-memory.ts +41 -0
  10. package/s/core/index.ts +6 -0
  11. package/s/core/mammoth.ts +72 -0
  12. package/s/core/types.ts +21 -0
  13. package/s/core/utils/random-id.ts +7 -0
  14. package/s/core/utils/relaxer.ts +18 -0
  15. package/s/test.ts +89 -0
  16. package/x/browser/example.d.ts +1 -0
  17. package/x/browser/example.js +20 -0
  18. package/x/browser/example.js.map +1 -0
  19. package/x/browser/iceberg-opfs.d.ts +10 -0
  20. package/x/browser/iceberg-opfs.js +42 -0
  21. package/x/browser/iceberg-opfs.js.map +1 -0
  22. package/x/browser/index.d.ts +2 -0
  23. package/x/browser/index.js +3 -0
  24. package/x/browser/index.js.map +1 -0
  25. package/x/browser/utils/is-not-found.d.ts +1 -0
  26. package/x/browser/utils/is-not-found.js +5 -0
  27. package/x/browser/utils/is-not-found.js.map +1 -0
  28. package/x/core/hash-stream.d.ts +1 -0
  29. package/x/core/hash-stream.js +14 -0
  30. package/x/core/hash-stream.js.map +1 -0
  31. package/x/core/iceberg-memory.d.ts +9 -0
  32. package/x/core/iceberg-memory.js +29 -0
  33. package/x/core/iceberg-memory.js.map +1 -0
  34. package/x/core/index.d.ts +4 -0
  35. package/x/core/index.js +5 -0
  36. package/x/core/index.js.map +1 -0
  37. package/x/core/mammoth.d.ts +12 -0
  38. package/x/core/mammoth.js +56 -0
  39. package/x/core/mammoth.js.map +1 -0
  40. package/x/core/types.d.ts +17 -0
  41. package/x/core/types.js +2 -0
  42. package/x/core/types.js.map +1 -0
  43. package/x/core/utils/random-id.d.ts +1 -0
  44. package/x/core/utils/random-id.js +5 -0
  45. package/x/core/utils/random-id.js.map +1 -0
  46. package/x/core/utils/relaxer.d.ts +2 -0
  47. package/x/core/utils/relaxer.js +14 -0
  48. package/x/core/utils/relaxer.js.map +1 -0
  49. package/x/test.d.ts +1 -0
  50. package/x/test.js +76 -0
  51. package/x/test.js.map +1 -0
package/LICENSE ADDED
@@ -0,0 +1,23 @@
1
+
2
+ MIT License
3
+
4
+ Copyright (c) 2026 Chase Moskal
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
23
+
package/README.md ADDED
@@ -0,0 +1,13 @@
1
+
2
+ # 🦣 @e280/mammoth
3
+
4
+ big files. small api.
5
+
6
+
7
+
8
+ <br/><br/>
9
+
10
+ ## 💖 mammoth is by e280
11
+ reward us with github stars
12
+ build with us at https://e280.org/ but only if you're cool
13
+
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@e280/mammoth",
3
+ "version": "0.1.0-1",
4
+ "description": "big file storage",
5
+ "license": "MIT",
6
+ "author": "Chase Moskal <chasemoskal@gmail.com>",
7
+ "type": "module",
8
+ "main": "./x/index.js",
9
+ "sideEffects": false,
10
+ "exports": {
11
+ ".": "./x/core/index.js",
12
+ "./browser": "./x/browser/index.js"
13
+ },
14
+ "files": [
15
+ "x",
16
+ "s"
17
+ ],
18
+ "scripts": {
19
+ "build": "rm -rf x && mkdir x && tsc",
20
+ "dev": "octo 'tsc -w' 'node --watch x/test.js'",
21
+ "test": "node x/test.js",
22
+ "count": "find s -path '*/_archive' -prune -o -name '*.ts' -exec wc -l {} +"
23
+ },
24
+ "dependencies": {
25
+ "@e280/kv": "^0.1.5",
26
+ "@e280/stz": "^0.3.4",
27
+ "@noble/hashes": "^2.2.0"
28
+ },
29
+ "devDependencies": {
30
+ "@e280/octo": "^0.1.3",
31
+ "@e280/science": "^0.1.11",
32
+ "@types/node": "^26.1.1",
33
+ "typescript": "^7.0.2"
34
+ },
35
+ "allowScripts": {
36
+ "classic-level@3.0.0": true
37
+ },
38
+ "keywords": [],
39
+ "homepage": "https://github.com/e280/mammoth#readme",
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "git+https://github.com/e280/mammoth.git"
43
+ },
44
+ "bugs": {
45
+ "url": "https://github.com/e280/mammoth/issues"
46
+ }
47
+ }
@@ -0,0 +1,26 @@
1
+
2
+ import {Kv, StorageDriver} from "@e280/kv"
3
+ import {Mammoth} from "../core/mammoth.js"
4
+ import {IcebergOpfs} from "./iceberg-opfs.js"
5
+
6
+ export async function example() {
7
+
8
+ // setup the localstorage kv index
9
+ const driver = new StorageDriver(window.localStorage)
10
+ const kv = new Kv(driver)
11
+ const index = kv.scope("glacier_index")
12
+
13
+ // setup the opfs iceberg
14
+ const root = await navigator.storage.getDirectory()
15
+ const directory = await root.getDirectoryHandle("glacier", {create: true})
16
+ const iceberg = new IcebergOpfs(directory)
17
+
18
+ // setup the mammoth
19
+ const mammoth = new Mammoth(index, iceberg)
20
+ const bigFile = new Blob([new Uint8Array([0xDE, 0xAD, 0xBE, 0xEF])])
21
+
22
+ // write a file as an example
23
+ const hash = await mammoth.write(bigFile.stream())
24
+ console.log(`mammoth stored file by hash "${hash}"`)
25
+ }
26
+
@@ -0,0 +1,50 @@
1
+
2
+ import {Iceberg, Id} from "../core/types.js"
3
+ import {isNotFound} from "./utils/is-not-found.js"
4
+
5
+ export class IcebergOpfs implements Iceberg {
6
+ #directory
7
+
8
+ constructor(directory: FileSystemDirectoryHandle) {
9
+ this.#directory = directory
10
+ }
11
+
12
+ async has(id: Id) {
13
+ try {
14
+ await this.#directory.getFileHandle(id)
15
+ return true
16
+ }
17
+ catch (error) {
18
+ if (isNotFound(error)) return false
19
+ throw error
20
+ }
21
+ }
22
+
23
+ async size(id: Id) {
24
+ const handle = await this.#directory.getFileHandle(id)
25
+ const file = await handle.getFile()
26
+ return file.size
27
+ }
28
+
29
+ async delete(id: Id) {
30
+ try {
31
+ await this.#directory.removeEntry(id)
32
+ }
33
+ catch (error) {
34
+ if (!isNotFound(error))
35
+ throw error
36
+ }
37
+ }
38
+
39
+ async read(id: Id) {
40
+ const handle = await this.#directory.getFileHandle(id)
41
+ return handle.getFile()
42
+ }
43
+
44
+ async write(id: Id, readable: ReadableStream<Uint8Array>) {
45
+ const handle = await this.#directory.getFileHandle(id, {create: true})
46
+ const writable = await handle.createWritable()
47
+ await readable.pipeTo(writable)
48
+ }
49
+ }
50
+
@@ -0,0 +1,4 @@
1
+
2
+ export * from "../core/index.js"
3
+ export * from "./iceberg-opfs.js"
4
+
@@ -0,0 +1,6 @@
1
+
2
+ export function isNotFound(error: unknown) {
3
+ return error instanceof DOMException
4
+ && error.name === "NotFoundError"
5
+ }
6
+
@@ -0,0 +1,19 @@
1
+
2
+ import {hex, nap} from "@e280/stz"
3
+ import {blake3} from "@noble/hashes/blake3.js"
4
+ import {relaxer} from "./utils/relaxer.js"
5
+
6
+ export async function hashStream(readable: ReadableStream<Uint8Array>) {
7
+ const hasher = blake3.create()
8
+ const relax = relaxer()
9
+
10
+ for await (const chunk of readable) {
11
+ hasher.update(chunk)
12
+
13
+ if (relax())
14
+ await nap()
15
+ }
16
+
17
+ return hex.fromBytes(hasher.digest())
18
+ }
19
+
@@ -0,0 +1,41 @@
1
+
2
+ import {bytes, got, nap} from "@e280/stz"
3
+ import {Iceberg, Id} from "./types.js"
4
+ import {relaxer} from "./utils/relaxer.js"
5
+
6
+ export class IcebergMemory implements Iceberg {
7
+ #map = new Map<string, Uint8Array>()
8
+
9
+ async has(id: Id) {
10
+ return this.#map.has(id)
11
+ }
12
+
13
+ async size(id: Id) {
14
+ return got(this.#map.get(id)).byteLength
15
+ }
16
+
17
+ async delete(id: Id) {
18
+ this.#map.delete(id)
19
+ }
20
+
21
+ async read(id: Id) {
22
+ return new Blob(
23
+ [got(this.#map.get(id)) as Uint8Array<ArrayBuffer>],
24
+ {type: "application/octet-stream"},
25
+ )
26
+ }
27
+
28
+ async write(id: Id, readable: ReadableStream<Uint8Array>) {
29
+ const relax = relaxer()
30
+ const parts: Uint8Array[] = []
31
+
32
+ for await (const chunk of readable) {
33
+ parts.push(chunk)
34
+ if (relax()) await nap()
35
+ }
36
+
37
+ const payload = bytes.concat(parts)
38
+ this.#map.set(id, payload)
39
+ }
40
+ }
41
+
@@ -0,0 +1,6 @@
1
+
2
+ export * from "./hash-stream.js"
3
+ export * from "./iceberg-memory.js"
4
+ export * from "./mammoth.js"
5
+ export * from "./types.js"
6
+
@@ -0,0 +1,72 @@
1
+
2
+ import {Kv} from "@e280/kv"
3
+ import {got, hex} from "@e280/stz"
4
+ import {blake3} from "@noble/hashes/blake3.js"
5
+
6
+ import {randomId} from "./utils/random-id.js"
7
+ import {Glacier, Hash, Iceberg, Id} from "./types.js"
8
+
9
+ export class Mammoth implements Glacier {
10
+ #index
11
+ #iceberg
12
+
13
+ constructor(index: Kv<string>, iceberg: Iceberg) {
14
+ this.#index = index
15
+ this.#iceberg = iceberg
16
+ }
17
+
18
+ async has(hash: Hash) {
19
+ return this.#index.has(hash)
20
+ }
21
+
22
+ async size(hash: Hash) {
23
+ const id = await this.#needFileId(hash)
24
+ return this.#iceberg.size(id)
25
+ }
26
+
27
+ async read(hash: Hash) {
28
+ const id = await this.#needFileId(hash)
29
+ return this.#iceberg.read(id)
30
+ }
31
+
32
+ async delete(hash: Hash) {
33
+ const id = await this.#getFileId(hash)
34
+ if (id) {
35
+ await this.#index.del(hash)
36
+ await this.#iceberg.delete(id)
37
+ }
38
+ }
39
+
40
+ async write(readable: ReadableStream<Uint8Array>): Promise<Hash> {
41
+ const id = randomId()
42
+ const pipe = new TransformStream()
43
+ const done = this.#iceberg.write(id, pipe.readable)
44
+ const writer = pipe.writable.getWriter()
45
+ const hasher = blake3.create()
46
+
47
+ for await (const chunk of readable) {
48
+ await writer.write(chunk as Uint8Array<ArrayBuffer>)
49
+ hasher.update(chunk)
50
+ }
51
+
52
+ await writer.close()
53
+ await done
54
+ const hash = hex.fromBytes(hasher.digest())
55
+ await this.delete(hash)
56
+ await this.#index.set(hash, id)
57
+ return hash
58
+ }
59
+
60
+ async #getFileId(hash: Hash): Promise<Id | undefined> {
61
+ return this.#index.get<string>(hash)
62
+ }
63
+
64
+ async #needFileId(hash: Hash): Promise<Id> {
65
+ return got(await this.#getFileId(hash), `file not found by hash "${hash}"`)
66
+ }
67
+
68
+ async* keys() {
69
+ yield* this.#index.keys()
70
+ }
71
+ }
72
+
@@ -0,0 +1,21 @@
1
+
2
+ export type Hash = string
3
+ export type Id = string
4
+
5
+ export type Iceberg = {
6
+ has(id: Id): Promise<boolean>
7
+ size(id: Id): Promise<number>
8
+ delete(id: Id): Promise<void>
9
+ read(id: Id): Promise<Blob>
10
+ write(id: Id, readable: ReadableStream<Uint8Array>): Promise<void>
11
+ }
12
+
13
+ export type Glacier = {
14
+ has(hash: Hash): Promise<boolean>
15
+ size(hash: Hash): Promise<number>
16
+ read(hash: Hash): Promise<Blob>
17
+ delete(hash: Hash): Promise<void>
18
+ write(readable: ReadableStream<Uint8Array>): Promise<Hash>
19
+ keys(): AsyncIterable<Hash>
20
+ }
21
+
@@ -0,0 +1,7 @@
1
+
2
+ import {hex} from "@e280/stz"
3
+
4
+ export function randomId() {
5
+ return hex.random(32)
6
+ }
7
+
@@ -0,0 +1,18 @@
1
+
2
+ /** a clock that signals when a long-running operation should yield */
3
+ export function relaxer(delta = 1000 / 60) {
4
+ let last = performance.now()
5
+
6
+ return () => {
7
+ const now = performance.now()
8
+ const since = now - last
9
+
10
+ if (since > delta) {
11
+ last = now
12
+ return true
13
+ }
14
+
15
+ return false
16
+ }
17
+ }
18
+
package/s/test.ts ADDED
@@ -0,0 +1,89 @@
1
+
2
+ import {Kv} from "@e280/kv"
3
+ import {bytes, collect} from "@e280/stz"
4
+ import {science, test, expect} from "@e280/science"
5
+
6
+ import {Mammoth} from "./core/mammoth.js"
7
+ import {randomId} from "./core/utils/random-id.js"
8
+ import {IcebergMemory} from "./core/iceberg-memory.js"
9
+
10
+ const blob = () => new Blob([new Uint8Array([0xDE, 0xAD, 0xBE, 0xEF])])
11
+ const quickstream = (b: number[]) => new Blob([new Uint8Array(b)]).stream()
12
+
13
+ function setup() {
14
+ const index = new Kv<string>()
15
+ const iceberg = new IcebergMemory()
16
+ const mammoth = new Mammoth(index, iceberg)
17
+ return {mammoth}
18
+ }
19
+
20
+ await science.run({
21
+ "mammoth": {
22
+ ".write and .read": test(async() => {
23
+ const {mammoth} = setup()
24
+ const hash = await mammoth.write(blob().stream())
25
+ const second = await mammoth.read(hash)
26
+ expect(bytes.eq(await second.bytes(), await blob().bytes()))
27
+ }),
28
+
29
+ ".write deduplication": test(async() => {
30
+ const {mammoth} = setup()
31
+ const hashA = await mammoth.write(blob().stream())
32
+ const hashB = await mammoth.write(blob().stream())
33
+ const keys = await collect(mammoth.keys())
34
+ expect(hashA).is(hashB)
35
+ expect(keys.length).is(1)
36
+ expect(keys[0]).is(hashA)
37
+ }),
38
+
39
+ ".write empty file": test(async() => {
40
+ const {mammoth} = setup()
41
+ const hash = await mammoth.write(new Blob().stream())
42
+ expect(await mammoth.size(hash)).is(0)
43
+ expect((await mammoth.read(hash)).size).is(0)
44
+ }),
45
+
46
+ ".write distinct files": test(async() => {
47
+ const {mammoth} = setup()
48
+ const hashA = await mammoth.write(quickstream([0xC0, 0xFF, 0xEE]))
49
+ const hashB = await mammoth.write(quickstream([0xB0, 0x0B, 0x1E, 0x5]))
50
+ expect(hashA).not.is(hashB)
51
+ expect((await collect(mammoth.keys())).length).is(2)
52
+ }),
53
+
54
+ ".has": test(async() => {
55
+ const {mammoth} = setup()
56
+ const hash = await mammoth.write(blob().stream())
57
+ expect(await mammoth.has(hash)).is(true)
58
+ expect(await mammoth.has(randomId())).is(false)
59
+ }),
60
+
61
+ ".size": test(async() => {
62
+ const {mammoth} = setup()
63
+ const hash = await mammoth.write(blob().stream())
64
+ expect(await mammoth.size(hash)).is(4)
65
+ }),
66
+
67
+ ".delete": test(async() => {
68
+ const {mammoth} = setup()
69
+ const hash = await mammoth.write(blob().stream())
70
+ await mammoth.delete(hash)
71
+ expect(await mammoth.has(hash)).is(false)
72
+ expect(await collect(mammoth.keys())).deep([])
73
+ }),
74
+
75
+ ".delete idempotent": test(async() => {
76
+ const {mammoth} = setup()
77
+ await mammoth.delete(randomId())
78
+ }),
79
+
80
+ ".keys": test(async() => {
81
+ const {mammoth} = setup()
82
+ const hash = await mammoth.write(blob().stream())
83
+ const keys = await collect(mammoth.keys())
84
+ expect(keys.length).is(1)
85
+ expect(keys[0]).is(hash)
86
+ }),
87
+ },
88
+ })
89
+
@@ -0,0 +1 @@
1
+ export declare function example(): Promise<void>;
@@ -0,0 +1,20 @@
1
+ import { Kv, StorageDriver } from "@e280/kv";
2
+ import { Mammoth } from "../core/mammoth.js";
3
+ import { IcebergOpfs } from "./iceberg-opfs.js";
4
+ export async function example() {
5
+ // setup the localstorage kv index
6
+ const driver = new StorageDriver(window.localStorage);
7
+ const kv = new Kv(driver);
8
+ const index = kv.scope("glacier_index");
9
+ // setup the opfs iceberg
10
+ const root = await navigator.storage.getDirectory();
11
+ const directory = await root.getDirectoryHandle("glacier", { create: true });
12
+ const iceberg = new IcebergOpfs(directory);
13
+ // setup the mammoth
14
+ const mammoth = new Mammoth(index, iceberg);
15
+ const bigFile = new Blob([new Uint8Array([0xDE, 0xAD, 0xBE, 0xEF])]);
16
+ // write a file as an example
17
+ const hash = await mammoth.write(bigFile.stream());
18
+ console.log(`mammoth stored file by hash "${hash}"`);
19
+ }
20
+ //# sourceMappingURL=example.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"example.js","sourceRoot":"","sources":["../../s/browser/example.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,EAAE,EAAE,aAAa,EAAC,MAAM,UAAU,CAAA;AAC1C,OAAO,EAAC,OAAO,EAAC,MAAM,oBAAoB,CAAA;AAC1C,OAAO,EAAC,WAAW,EAAC,MAAM,mBAAmB,CAAA;AAE7C,MAAM,CAAC,KAAK,UAAU,OAAO;IAE5B,kCAAkC;IAClC,MAAM,MAAM,GAAG,IAAI,aAAa,CAAC,MAAM,CAAC,YAAY,CAAC,CAAA;IACrD,MAAM,EAAE,GAAG,IAAI,EAAE,CAAC,MAAM,CAAC,CAAA;IACzB,MAAM,KAAK,GAAG,EAAE,CAAC,KAAK,CAAC,eAAe,CAAC,CAAA;IAEvC,yBAAyB;IACzB,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,OAAO,CAAC,YAAY,EAAE,CAAA;IACnD,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,kBAAkB,CAAC,SAAS,EAAE,EAAC,MAAM,EAAE,IAAI,EAAC,CAAC,CAAA;IAC1E,MAAM,OAAO,GAAG,IAAI,WAAW,CAAC,SAAS,CAAC,CAAA;IAE1C,oBAAoB;IACpB,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,CAAA;IAC3C,MAAM,OAAO,GAAG,IAAI,IAAI,CAAC,CAAC,IAAI,UAAU,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAA;IAEpE,6BAA6B;IAC7B,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAA;IAClD,OAAO,CAAC,GAAG,CAAC,gCAAgC,IAAI,GAAG,CAAC,CAAA;AACrD,CAAC"}
@@ -0,0 +1,10 @@
1
+ import { Iceberg, Id } from "../core/types.js";
2
+ export declare class IcebergOpfs implements Iceberg {
3
+ #private;
4
+ constructor(directory: FileSystemDirectoryHandle);
5
+ has(id: Id): Promise<boolean>;
6
+ size(id: Id): Promise<number>;
7
+ delete(id: Id): Promise<void>;
8
+ read(id: Id): Promise<File>;
9
+ write(id: Id, readable: ReadableStream<Uint8Array>): Promise<void>;
10
+ }
@@ -0,0 +1,42 @@
1
+ import { isNotFound } from "./utils/is-not-found.js";
2
+ export class IcebergOpfs {
3
+ #directory;
4
+ constructor(directory) {
5
+ this.#directory = directory;
6
+ }
7
+ async has(id) {
8
+ try {
9
+ await this.#directory.getFileHandle(id);
10
+ return true;
11
+ }
12
+ catch (error) {
13
+ if (isNotFound(error))
14
+ return false;
15
+ throw error;
16
+ }
17
+ }
18
+ async size(id) {
19
+ const handle = await this.#directory.getFileHandle(id);
20
+ const file = await handle.getFile();
21
+ return file.size;
22
+ }
23
+ async delete(id) {
24
+ try {
25
+ await this.#directory.removeEntry(id);
26
+ }
27
+ catch (error) {
28
+ if (!isNotFound(error))
29
+ throw error;
30
+ }
31
+ }
32
+ async read(id) {
33
+ const handle = await this.#directory.getFileHandle(id);
34
+ return handle.getFile();
35
+ }
36
+ async write(id, readable) {
37
+ const handle = await this.#directory.getFileHandle(id, { create: true });
38
+ const writable = await handle.createWritable();
39
+ await readable.pipeTo(writable);
40
+ }
41
+ }
42
+ //# sourceMappingURL=iceberg-opfs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"iceberg-opfs.js","sourceRoot":"","sources":["../../s/browser/iceberg-opfs.ts"],"names":[],"mappings":"AAEA,OAAO,EAAC,UAAU,EAAC,MAAM,yBAAyB,CAAA;AAElD,MAAM,OAAO,WAAW;IACvB,UAAU,CAAA;IAEV,YAAY,SAAoC;QAC/C,IAAI,CAAC,UAAU,GAAG,SAAS,CAAA;IAC5B,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,EAAM;QACf,IAAI,CAAC;YACJ,MAAM,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC,CAAA;YACvC,OAAO,IAAI,CAAA;QACZ,CAAC;QACD,OAAO,KAAK,EAAE,CAAC;YACd,IAAI,UAAU,CAAC,KAAK,CAAC;gBAAE,OAAO,KAAK,CAAA;YACnC,MAAM,KAAK,CAAA;QACZ,CAAC;IACF,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,EAAM;QAChB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC,CAAA;QACtD,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,OAAO,EAAE,CAAA;QACnC,OAAO,IAAI,CAAC,IAAI,CAAA;IACjB,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,EAAM;QAClB,IAAI,CAAC;YACJ,MAAM,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC,CAAA;QACtC,CAAC;QACD,OAAO,KAAK,EAAE,CAAC;YACd,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC;gBACrB,MAAM,KAAK,CAAA;QACb,CAAC;IACF,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,EAAM;QAChB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC,CAAA;QACtD,OAAO,MAAM,CAAC,OAAO,EAAE,CAAA;IACxB,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,EAAM,EAAE,QAAoC;QACvD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,EAAE,EAAC,MAAM,EAAE,IAAI,EAAC,CAAC,CAAA;QACtE,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,cAAc,EAAE,CAAA;QAC9C,MAAM,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;IAChC,CAAC;CACD"}
@@ -0,0 +1,2 @@
1
+ export * from "../core/index.js";
2
+ export * from "./iceberg-opfs.js";
@@ -0,0 +1,3 @@
1
+ export * from "../core/index.js";
2
+ export * from "./iceberg-opfs.js";
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../s/browser/index.ts"],"names":[],"mappings":"AACA,cAAc,kBAAkB,CAAA;AAChC,cAAc,mBAAmB,CAAA"}
@@ -0,0 +1 @@
1
+ export declare function isNotFound(error: unknown): boolean;
@@ -0,0 +1,5 @@
1
+ export function isNotFound(error) {
2
+ return error instanceof DOMException
3
+ && error.name === "NotFoundError";
4
+ }
5
+ //# sourceMappingURL=is-not-found.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"is-not-found.js","sourceRoot":"","sources":["../../../s/browser/utils/is-not-found.ts"],"names":[],"mappings":"AACA,MAAM,UAAU,UAAU,CAAC,KAAc;IACxC,OAAO,KAAK,YAAY,YAAY;WAChC,KAAK,CAAC,IAAI,KAAK,eAAe,CAAA;AACnC,CAAC"}
@@ -0,0 +1 @@
1
+ export declare function hashStream(readable: ReadableStream<Uint8Array>): Promise<string>;
@@ -0,0 +1,14 @@
1
+ import { hex, nap } from "@e280/stz";
2
+ import { blake3 } from "@noble/hashes/blake3.js";
3
+ import { relaxer } from "./utils/relaxer.js";
4
+ export async function hashStream(readable) {
5
+ const hasher = blake3.create();
6
+ const relax = relaxer();
7
+ for await (const chunk of readable) {
8
+ hasher.update(chunk);
9
+ if (relax())
10
+ await nap();
11
+ }
12
+ return hex.fromBytes(hasher.digest());
13
+ }
14
+ //# sourceMappingURL=hash-stream.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hash-stream.js","sourceRoot":"","sources":["../../s/core/hash-stream.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,GAAG,EAAE,GAAG,EAAC,MAAM,WAAW,CAAA;AAClC,OAAO,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAA;AAC9C,OAAO,EAAC,OAAO,EAAC,MAAM,oBAAoB,CAAA;AAE1C,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,QAAoC;IACpE,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,CAAA;IAC9B,MAAM,KAAK,GAAG,OAAO,EAAE,CAAA;IAEvB,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,QAAQ,EAAE,CAAC;QACpC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;QAEpB,IAAI,KAAK,EAAE;YACV,MAAM,GAAG,EAAE,CAAA;IACb,CAAC;IAED,OAAO,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAA;AACtC,CAAC"}
@@ -0,0 +1,9 @@
1
+ import { Iceberg, Id } from "./types.js";
2
+ export declare class IcebergMemory implements Iceberg {
3
+ #private;
4
+ has(id: Id): Promise<boolean>;
5
+ size(id: Id): Promise<number>;
6
+ delete(id: Id): Promise<void>;
7
+ read(id: Id): Promise<Blob>;
8
+ write(id: Id, readable: ReadableStream<Uint8Array>): Promise<void>;
9
+ }
@@ -0,0 +1,29 @@
1
+ import { bytes, got, nap } from "@e280/stz";
2
+ import { relaxer } from "./utils/relaxer.js";
3
+ export class IcebergMemory {
4
+ #map = new Map();
5
+ async has(id) {
6
+ return this.#map.has(id);
7
+ }
8
+ async size(id) {
9
+ return got(this.#map.get(id)).byteLength;
10
+ }
11
+ async delete(id) {
12
+ this.#map.delete(id);
13
+ }
14
+ async read(id) {
15
+ return new Blob([got(this.#map.get(id))], { type: "application/octet-stream" });
16
+ }
17
+ async write(id, readable) {
18
+ const relax = relaxer();
19
+ const parts = [];
20
+ for await (const chunk of readable) {
21
+ parts.push(chunk);
22
+ if (relax())
23
+ await nap();
24
+ }
25
+ const payload = bytes.concat(parts);
26
+ this.#map.set(id, payload);
27
+ }
28
+ }
29
+ //# sourceMappingURL=iceberg-memory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"iceberg-memory.js","sourceRoot":"","sources":["../../s/core/iceberg-memory.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,KAAK,EAAE,GAAG,EAAE,GAAG,EAAC,MAAM,WAAW,CAAA;AAEzC,OAAO,EAAC,OAAO,EAAC,MAAM,oBAAoB,CAAA;AAE1C,MAAM,OAAO,aAAa;IACzB,IAAI,GAAG,IAAI,GAAG,EAAsB,CAAA;IAEpC,KAAK,CAAC,GAAG,CAAC,EAAM;QACf,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;IACzB,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,EAAM;QAChB,OAAO,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,CAAA;IACzC,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,EAAM;QAClB,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;IACrB,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,EAAM;QAChB,OAAO,IAAI,IAAI,CACd,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAA4B,CAAC,EACnD,EAAC,IAAI,EAAE,0BAA0B,EAAC,CAClC,CAAA;IACF,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,EAAM,EAAE,QAAoC;QACvD,MAAM,KAAK,GAAG,OAAO,EAAE,CAAA;QACvB,MAAM,KAAK,GAAiB,EAAE,CAAA;QAE9B,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,QAAQ,EAAE,CAAC;YACpC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;YACjB,IAAI,KAAK,EAAE;gBAAE,MAAM,GAAG,EAAE,CAAA;QACzB,CAAC;QAED,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;QACnC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,OAAO,CAAC,CAAA;IAC3B,CAAC;CACD"}
@@ -0,0 +1,4 @@
1
+ export * from "./hash-stream.js";
2
+ export * from "./iceberg-memory.js";
3
+ export * from "./mammoth.js";
4
+ export * from "./types.js";
@@ -0,0 +1,5 @@
1
+ export * from "./hash-stream.js";
2
+ export * from "./iceberg-memory.js";
3
+ export * from "./mammoth.js";
4
+ export * from "./types.js";
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../s/core/index.ts"],"names":[],"mappings":"AACA,cAAc,kBAAkB,CAAA;AAChC,cAAc,qBAAqB,CAAA;AACnC,cAAc,cAAc,CAAA;AAC5B,cAAc,YAAY,CAAA"}
@@ -0,0 +1,12 @@
1
+ import { Kv } from "@e280/kv";
2
+ import { Glacier, Hash, Iceberg } from "./types.js";
3
+ export declare class Mammoth implements Glacier {
4
+ #private;
5
+ constructor(index: Kv<string>, iceberg: Iceberg);
6
+ has(hash: Hash): Promise<boolean>;
7
+ size(hash: Hash): Promise<number>;
8
+ read(hash: Hash): Promise<Blob>;
9
+ delete(hash: Hash): Promise<void>;
10
+ write(readable: ReadableStream<Uint8Array>): Promise<Hash>;
11
+ keys(): AsyncGenerator<string, void, unknown>;
12
+ }
@@ -0,0 +1,56 @@
1
+ import { got, hex } from "@e280/stz";
2
+ import { blake3 } from "@noble/hashes/blake3.js";
3
+ import { randomId } from "./utils/random-id.js";
4
+ export class Mammoth {
5
+ #index;
6
+ #iceberg;
7
+ constructor(index, iceberg) {
8
+ this.#index = index;
9
+ this.#iceberg = iceberg;
10
+ }
11
+ async has(hash) {
12
+ return this.#index.has(hash);
13
+ }
14
+ async size(hash) {
15
+ const id = await this.#needFileId(hash);
16
+ return this.#iceberg.size(id);
17
+ }
18
+ async read(hash) {
19
+ const id = await this.#needFileId(hash);
20
+ return this.#iceberg.read(id);
21
+ }
22
+ async delete(hash) {
23
+ const id = await this.#getFileId(hash);
24
+ if (id) {
25
+ await this.#index.del(hash);
26
+ await this.#iceberg.delete(id);
27
+ }
28
+ }
29
+ async write(readable) {
30
+ const id = randomId();
31
+ const pipe = new TransformStream();
32
+ const done = this.#iceberg.write(id, pipe.readable);
33
+ const writer = pipe.writable.getWriter();
34
+ const hasher = blake3.create();
35
+ for await (const chunk of readable) {
36
+ await writer.write(chunk);
37
+ hasher.update(chunk);
38
+ }
39
+ await writer.close();
40
+ await done;
41
+ const hash = hex.fromBytes(hasher.digest());
42
+ await this.delete(hash);
43
+ await this.#index.set(hash, id);
44
+ return hash;
45
+ }
46
+ async #getFileId(hash) {
47
+ return this.#index.get(hash);
48
+ }
49
+ async #needFileId(hash) {
50
+ return got(await this.#getFileId(hash), `file not found by hash "${hash}"`);
51
+ }
52
+ async *keys() {
53
+ yield* this.#index.keys();
54
+ }
55
+ }
56
+ //# sourceMappingURL=mammoth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mammoth.js","sourceRoot":"","sources":["../../s/core/mammoth.ts"],"names":[],"mappings":"AAEA,OAAO,EAAC,GAAG,EAAE,GAAG,EAAC,MAAM,WAAW,CAAA;AAClC,OAAO,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAA;AAE9C,OAAO,EAAC,QAAQ,EAAC,MAAM,sBAAsB,CAAA;AAG7C,MAAM,OAAO,OAAO;IACnB,MAAM,CAAA;IACN,QAAQ,CAAA;IAER,YAAY,KAAiB,EAAE,OAAgB;QAC9C,IAAI,CAAC,MAAM,GAAG,KAAK,CAAA;QACnB,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAA;IACxB,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,IAAU;QACnB,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;IAC7B,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,IAAU;QACpB,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAA;QACvC,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IAC9B,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,IAAU;QACpB,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAA;QACvC,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IAC9B,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,IAAU;QACtB,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAA;QACtC,IAAI,EAAE,EAAE,CAAC;YACR,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;YAC3B,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;QAC/B,CAAC;IACF,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,QAAoC;QAC/C,MAAM,EAAE,GAAG,QAAQ,EAAE,CAAA;QACrB,MAAM,IAAI,GAAG,IAAI,eAAe,EAAE,CAAA;QAClC,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAA;QACnD,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,EAAE,CAAA;QACxC,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,CAAA;QAE9B,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,QAAQ,EAAE,CAAC;YACpC,MAAM,MAAM,CAAC,KAAK,CAAC,KAAgC,CAAC,CAAA;YACpD,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;QACrB,CAAC;QAED,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;QACpB,MAAM,IAAI,CAAA;QACV,MAAM,IAAI,GAAG,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAA;QAC3C,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;QACvB,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,CAAA;QAC/B,OAAO,IAAI,CAAA;IACZ,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,IAAU;QAC1B,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAS,IAAI,CAAC,CAAA;IACrC,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,IAAU;QAC3B,OAAO,GAAG,CAAC,MAAM,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,2BAA2B,IAAI,GAAG,CAAC,CAAA;IAC5E,CAAC;IAED,KAAK,CAAA,CAAE,IAAI;QACV,KAAK,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAA;IAC1B,CAAC;CACD"}
@@ -0,0 +1,17 @@
1
+ export type Hash = string;
2
+ export type Id = string;
3
+ export type Iceberg = {
4
+ has(id: Id): Promise<boolean>;
5
+ size(id: Id): Promise<number>;
6
+ delete(id: Id): Promise<void>;
7
+ read(id: Id): Promise<Blob>;
8
+ write(id: Id, readable: ReadableStream<Uint8Array>): Promise<void>;
9
+ };
10
+ export type Glacier = {
11
+ has(hash: Hash): Promise<boolean>;
12
+ size(hash: Hash): Promise<number>;
13
+ read(hash: Hash): Promise<Blob>;
14
+ delete(hash: Hash): Promise<void>;
15
+ write(readable: ReadableStream<Uint8Array>): Promise<Hash>;
16
+ keys(): AsyncIterable<Hash>;
17
+ };
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../s/core/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1 @@
1
+ export declare function randomId(): string;
@@ -0,0 +1,5 @@
1
+ import { hex } from "@e280/stz";
2
+ export function randomId() {
3
+ return hex.random(32);
4
+ }
5
+ //# sourceMappingURL=random-id.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"random-id.js","sourceRoot":"","sources":["../../../s/core/utils/random-id.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,GAAG,EAAC,MAAM,WAAW,CAAA;AAE7B,MAAM,UAAU,QAAQ;IACvB,OAAO,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;AACtB,CAAC"}
@@ -0,0 +1,2 @@
1
+ /** a clock that signals when a long-running operation should yield */
2
+ export declare function relaxer(delta?: number): () => boolean;
@@ -0,0 +1,14 @@
1
+ /** a clock that signals when a long-running operation should yield */
2
+ export function relaxer(delta = 1000 / 60) {
3
+ let last = performance.now();
4
+ return () => {
5
+ const now = performance.now();
6
+ const since = now - last;
7
+ if (since > delta) {
8
+ last = now;
9
+ return true;
10
+ }
11
+ return false;
12
+ };
13
+ }
14
+ //# sourceMappingURL=relaxer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relaxer.js","sourceRoot":"","sources":["../../../s/core/utils/relaxer.ts"],"names":[],"mappings":"AACA,sEAAsE;AACtE,MAAM,UAAU,OAAO,CAAC,KAAK,GAAG,IAAI,GAAG,EAAE;IACxC,IAAI,IAAI,GAAG,WAAW,CAAC,GAAG,EAAE,CAAA;IAE5B,OAAO,GAAG,EAAE;QACX,MAAM,GAAG,GAAG,WAAW,CAAC,GAAG,EAAE,CAAA;QAC7B,MAAM,KAAK,GAAG,GAAG,GAAG,IAAI,CAAA;QAExB,IAAI,KAAK,GAAG,KAAK,EAAE,CAAC;YACnB,IAAI,GAAG,GAAG,CAAA;YACV,OAAO,IAAI,CAAA;QACZ,CAAC;QAED,OAAO,KAAK,CAAA;IACb,CAAC,CAAA;AACF,CAAC"}
package/x/test.d.ts ADDED
@@ -0,0 +1 @@
1
+ export {};
package/x/test.js ADDED
@@ -0,0 +1,76 @@
1
+ import { Kv } from "@e280/kv";
2
+ import { bytes, collect } from "@e280/stz";
3
+ import { science, test, expect } from "@e280/science";
4
+ import { Mammoth } from "./core/mammoth.js";
5
+ import { randomId } from "./core/utils/random-id.js";
6
+ import { IcebergMemory } from "./core/iceberg-memory.js";
7
+ const blob = () => new Blob([new Uint8Array([0xDE, 0xAD, 0xBE, 0xEF])]);
8
+ const quickstream = (b) => new Blob([new Uint8Array(b)]).stream();
9
+ function setup() {
10
+ const index = new Kv();
11
+ const iceberg = new IcebergMemory();
12
+ const mammoth = new Mammoth(index, iceberg);
13
+ return { mammoth };
14
+ }
15
+ await science.run({
16
+ "mammoth": {
17
+ ".write and .read": test(async () => {
18
+ const { mammoth } = setup();
19
+ const hash = await mammoth.write(blob().stream());
20
+ const second = await mammoth.read(hash);
21
+ expect(bytes.eq(await second.bytes(), await blob().bytes()));
22
+ }),
23
+ ".write deduplication": test(async () => {
24
+ const { mammoth } = setup();
25
+ const hashA = await mammoth.write(blob().stream());
26
+ const hashB = await mammoth.write(blob().stream());
27
+ const keys = await collect(mammoth.keys());
28
+ expect(hashA).is(hashB);
29
+ expect(keys.length).is(1);
30
+ expect(keys[0]).is(hashA);
31
+ }),
32
+ ".write empty file": test(async () => {
33
+ const { mammoth } = setup();
34
+ const hash = await mammoth.write(new Blob().stream());
35
+ expect(await mammoth.size(hash)).is(0);
36
+ expect((await mammoth.read(hash)).size).is(0);
37
+ }),
38
+ ".write distinct files": test(async () => {
39
+ const { mammoth } = setup();
40
+ const hashA = await mammoth.write(quickstream([0xC0, 0xFF, 0xEE]));
41
+ const hashB = await mammoth.write(quickstream([0xB0, 0x0B, 0x1E, 0x5]));
42
+ expect(hashA).not.is(hashB);
43
+ expect((await collect(mammoth.keys())).length).is(2);
44
+ }),
45
+ ".has": test(async () => {
46
+ const { mammoth } = setup();
47
+ const hash = await mammoth.write(blob().stream());
48
+ expect(await mammoth.has(hash)).is(true);
49
+ expect(await mammoth.has(randomId())).is(false);
50
+ }),
51
+ ".size": test(async () => {
52
+ const { mammoth } = setup();
53
+ const hash = await mammoth.write(blob().stream());
54
+ expect(await mammoth.size(hash)).is(4);
55
+ }),
56
+ ".delete": test(async () => {
57
+ const { mammoth } = setup();
58
+ const hash = await mammoth.write(blob().stream());
59
+ await mammoth.delete(hash);
60
+ expect(await mammoth.has(hash)).is(false);
61
+ expect(await collect(mammoth.keys())).deep([]);
62
+ }),
63
+ ".delete idempotent": test(async () => {
64
+ const { mammoth } = setup();
65
+ await mammoth.delete(randomId());
66
+ }),
67
+ ".keys": test(async () => {
68
+ const { mammoth } = setup();
69
+ const hash = await mammoth.write(blob().stream());
70
+ const keys = await collect(mammoth.keys());
71
+ expect(keys.length).is(1);
72
+ expect(keys[0]).is(hash);
73
+ }),
74
+ },
75
+ });
76
+ //# sourceMappingURL=test.js.map
package/x/test.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test.js","sourceRoot":"","sources":["../s/test.ts"],"names":[],"mappings":"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"}