@e280/mammoth 0.1.0-4 → 0.1.0-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.
Files changed (46) hide show
  1. package/README.md +13 -11
  2. package/package.json +2 -2
  3. package/s/browser/example.ts +1 -1
  4. package/s/browser/opfs-bucket.ts +0 -6
  5. package/s/core/consts.ts +9 -0
  6. package/s/core/mammoth.ts +37 -93
  7. package/s/core/memory-bucket.ts +0 -4
  8. package/s/core/types.ts +24 -1
  9. package/s/core/utils/is-expired.ts +10 -0
  10. package/s/core/utils/manifest.ts +89 -0
  11. package/s/core/utils/not-found.ts +7 -0
  12. package/s/core/utils/save-and-hash.ts +25 -0
  13. package/s/node/disk-bucket.ts +0 -5
  14. package/s/test.ts +13 -14
  15. package/x/browser/example.js +1 -1
  16. package/x/browser/example.js.map +1 -1
  17. package/x/browser/opfs-bucket.d.ts +0 -1
  18. package/x/browser/opfs-bucket.js +0 -5
  19. package/x/browser/opfs-bucket.js.map +1 -1
  20. package/x/core/consts.d.ts +5 -0
  21. package/x/core/consts.js +7 -0
  22. package/x/core/consts.js.map +1 -0
  23. package/x/core/mammoth.d.ts +3 -9
  24. package/x/core/mammoth.js +36 -76
  25. package/x/core/mammoth.js.map +1 -1
  26. package/x/core/memory-bucket.d.ts +0 -1
  27. package/x/core/memory-bucket.js +0 -3
  28. package/x/core/memory-bucket.js.map +1 -1
  29. package/x/core/types.d.ts +17 -1
  30. package/x/core/utils/is-expired.d.ts +2 -0
  31. package/x/core/utils/is-expired.js +7 -0
  32. package/x/core/utils/is-expired.js.map +1 -0
  33. package/x/core/utils/manifest.d.ts +18 -0
  34. package/x/core/utils/manifest.js +69 -0
  35. package/x/core/utils/manifest.js.map +1 -0
  36. package/x/core/utils/not-found.d.ts +2 -0
  37. package/x/core/utils/not-found.js +4 -0
  38. package/x/core/utils/not-found.js.map +1 -0
  39. package/x/core/utils/save-and-hash.d.ts +5 -0
  40. package/x/core/utils/save-and-hash.js +19 -0
  41. package/x/core/utils/save-and-hash.js.map +1 -0
  42. package/x/node/disk-bucket.d.ts +0 -1
  43. package/x/node/disk-bucket.js +0 -4
  44. package/x/node/disk-bucket.js.map +1 -1
  45. package/x/test.js +13 -14
  46. package/x/test.js.map +1 -1
package/README.md CHANGED
@@ -1,8 +1,10 @@
1
1
 
2
- ![](https://i.imgur.com/qqiMrZ5.png)
2
+ ![](https://i.imgur.com/1TQmFwW.png)
3
3
 
4
- # 🦣 @e280/mammoth
5
- ## big files. small api.
4
+ # 🦣 mammoth
5
+ > *big files. small api.*
6
+
7
+ **mammoth is a content-addressed file store.** files are streamed into a bucket, identified by their blake3 hash, and automatically deduplicated. mammoth works the same whether it's backed by a bucket in memory, on disk, in the cloud, or in the browser's opfs.
6
8
 
7
9
  ```bash
8
10
  npm install @e280/mammoth
@@ -35,8 +37,8 @@ const mammoth = new Mammoth()
35
37
  import {Kv} from "@e280/kv"
36
38
 
37
39
  const mammoth = new Mammoth(
38
- new Kv(),
39
40
  new MemoryBucket(),
41
+ new Kv(),
40
42
  )
41
43
  ```
42
44
  defaults shown, so, this is equivalent:
@@ -51,8 +53,8 @@ const mammoth = new Mammoth()
51
53
  import {LevelDriver} from "@e280/kv/level"
52
54
 
53
55
  const mammoth = new Mammoth(
54
- new Kv(new LevelDriver("./data/kv")),
55
56
  new DiskBucket("./data/bucket"),
57
+ new Kv(new LevelDriver("./data/kv")),
56
58
  )
57
59
  ```
58
60
  - **opfs bucket,** for local storage in the browser. *(note the import paths)*
@@ -62,24 +64,24 @@ const mammoth = new Mammoth()
62
64
  import {Kv, StorageDriver} from "@e280/kv"
63
65
 
64
66
  const mammoth = new Mammoth(
65
- new Kv(new StorageDriver(localStorage)),
66
67
  new OpfsBucket(await navigator.storage.getDirectory()),
68
+ new Kv(new StorageDriver(localStorage)),
67
69
  )
68
70
  ```
69
71
 
70
- ### 🦣 mammoth has a few more methods for you.
72
+ ### 🦣 more mammoth methods.
71
73
  - **check if a file exists,** get back a boolean.
72
74
  ```ts
73
75
  const exists = await mammoth.has(hash)
74
76
  ```
75
- - **get file size,** in bytes.
77
+ - **get file info,** including size in bytes, added timestamp, and bucket id.
76
78
  ```ts
77
- const size = await mammoth.size(hash)
79
+ const {size, added, id} = await mammoth.info(hash)
78
80
  ```
79
81
  - **get stats,** for the whole datalake.
80
82
  ```ts
81
83
  await mammoth.stats()
82
- // {size: 1234567}
84
+ // {count: 123, size: 123456789}
83
85
  ```
84
86
  - **loop over all hashes,** for all stored files.
85
87
  ```ts
@@ -91,5 +93,5 @@ const mammoth = new Mammoth()
91
93
 
92
94
  <br/><br/>
93
95
 
94
- > *by [e280](https://e280.org/)*
96
+ *https://e280.org/*
95
97
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@e280/mammoth",
3
- "version": "0.1.0-4",
3
+ "version": "0.1.0-6",
4
4
  "description": "big file storage",
5
5
  "license": "MIT",
6
6
  "author": "Chase Moskal <chasemoskal@gmail.com>",
@@ -24,7 +24,7 @@
24
24
  },
25
25
  "dependencies": {
26
26
  "@e280/kv": "^0.1.5",
27
- "@e280/stz": "^0.3.4",
27
+ "@e280/stz": "^0.3.5",
28
28
  "@noble/hashes": "^2.2.0"
29
29
  },
30
30
  "devDependencies": {
@@ -15,7 +15,7 @@ export async function example() {
15
15
  const bucket = new OpfsBucket(directory)
16
16
 
17
17
  // setup the mammoth
18
- const mammoth = new Mammoth(kv, bucket)
18
+ const mammoth = new Mammoth(bucket, kv)
19
19
  const myFile = new Blob([new Uint8Array([0xDE, 0xAD, 0xBE, 0xEF])])
20
20
 
21
21
  // write a file as an example
@@ -20,12 +20,6 @@ export class OpfsBucket implements Bucket {
20
20
  }
21
21
  }
22
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
23
  async delete(id: Id) {
30
24
  try {
31
25
  await this.#directory.removeEntry(id)
@@ -0,0 +1,9 @@
1
+
2
+ import {time} from "@e280/stz"
3
+
4
+ export const consts = {
5
+ max_jobs: 64,
6
+ self_clean_limit: 64,
7
+ expiry_time: time.days(7),
8
+ }
9
+
package/s/core/mammoth.ts CHANGED
@@ -1,138 +1,82 @@
1
1
 
2
2
  import {Kv} from "@e280/kv"
3
- import {got, hex, queue, time} from "@e280/stz"
4
- import {blake3} from "@noble/hashes/blake3.js"
3
+ import {lane, queue} from "@e280/stz"
5
4
 
6
- import {Hash, Bucket, Id} from "./types.js"
5
+ import {consts} from "./consts.js"
6
+ import {Hash, Bucket} from "./types.js"
7
+ import {Manifest} from "./utils/manifest.js"
7
8
  import {randomId} from "./utils/random-id.js"
8
9
  import {MemoryBucket} from "./memory-bucket.js"
10
+ import {saveAndHash} from "./utils/save-and-hash.js"
9
11
 
10
12
  /** file storage datalake, content-addressed with blake3 hashes. */
11
13
  export class Mammoth {
12
- #ids
13
- #wip
14
- #stats
15
14
  #bucket
15
+ #manifest
16
+ #wholesome = lane(consts.max_jobs)
16
17
 
17
- constructor(
18
-
19
- /** key-value metadata manifest. */
20
- kv = new Kv(),
21
-
22
- /** dumb raw file blob storage. */
23
- bucket: Bucket = new MemoryBucket(),
24
-
25
- ) {
26
- this.#ids = kv.scope<string>("ids")
27
- this.#wip = kv.scope<{created: number}>("wip")
28
- this.#stats = kv.store<{size: number}>("stats")
18
+ constructor(bucket: Bucket = new MemoryBucket(), kv = new Kv()) {
29
19
  this.#bucket = bucket
20
+ this.#manifest = new Manifest(kv)
30
21
  }
31
22
 
32
23
  async has(hash: Hash) {
33
- return this.#ids.has(hash)
24
+ return this.#manifest.hasHash(hash)
34
25
  }
35
26
 
36
- async size(hash: Hash) {
37
- const id = await this.#needId(hash)
38
- return this.#bucket.size(id)
27
+ async info(hash: Hash) {
28
+ return this.#manifest.needInfo(hash)
39
29
  }
40
30
 
41
31
  async read(hash: Hash) {
42
- const id = await this.#needId(hash)
32
+ const {id} = await this.#manifest.needInfo(hash)
43
33
  return this.#bucket.read(id)
44
34
  }
45
35
 
46
36
  async delete(hash: Hash) {
47
- const id = await this.#getId(hash)
48
- if (id) {
49
- const size = await this.#bucket.size(id)
50
- await this.#ids.del(hash)
51
- await this.#bucket.delete(id)
52
- await this.#addSize(-size)
53
- }
37
+ await this.#wholesome(async() => {
38
+ const info = await this.#manifest.getInfo(hash)
39
+ if (info) {
40
+ await this.#manifest.deleteInfo(hash)
41
+ await this.#bucket.delete(info.id)
42
+ await this.#manifest.statsRemoveFile(info.size)
43
+ }
44
+ })
54
45
  }
55
46
 
56
47
  async write(readable: ReadableStream<Uint8Array>): Promise<Hash> {
57
48
  const id = randomId()
58
- await this.#wip.set(id, {created: Date.now()})
59
49
 
60
- const {hash, size} = await this.#hashAndSave(id, readable)
50
+ await this.#manifest.addWip(id)
51
+ const {hash, size} = await saveAndHash(this.#bucket, id, readable)
61
52
 
62
- await this.#wip.del(id)
63
- await this.#finalizeWrite(hash, id, size)
53
+ await this.#wholesome(async() => {
54
+ if (await this.#manifest.hasHash(hash)) {
55
+ await this.#bucket.delete(id) // forget this new file (we already have it)
56
+ }
57
+ else {
58
+ await this.#manifest.saveInfo(hash, {id, size, added: Date.now()})
59
+ await this.#manifest.statsAddFile(size)
60
+ }
61
+ })
64
62
 
63
+ await this.#manifest.deleteWip(id)
65
64
  void this.#selfClean().catch(() => {})
66
-
67
65
  return hash
68
66
  }
69
67
 
70
68
  async* hashes() {
71
- yield* this.#ids.keys()
69
+ yield* this.#manifest.hashes()
72
70
  }
73
71
 
74
72
  async stats() {
75
- return (await this.#stats.get()) ?? {size: 0}
76
- }
77
-
78
- async #getId(hash: Hash): Promise<Id | undefined> {
79
- return this.#ids.get<string>(hash)
80
- }
81
-
82
- async #needId(hash: Hash): Promise<Id> {
83
- return got(await this.#getId(hash), `file not found by hash "${hash}"`)
84
- }
85
-
86
- async #hashAndSave(id: Id, readable: ReadableStream<Uint8Array>) {
87
- let size = 0
88
- const pipe = new TransformStream()
89
- const done = this.#bucket.write(id, pipe.readable)
90
- const writer = pipe.writable.getWriter()
91
- const hasher = blake3.create()
92
-
93
- for await (const chunk of readable) {
94
- await writer.write(chunk as Uint8Array<ArrayBuffer>)
95
- hasher.update(chunk)
96
- size += chunk.byteLength
97
- }
98
-
99
- await writer.close()
100
- await done
101
-
102
- const hash = hex.fromBytes(hasher.digest())
103
- return {hash, size}
73
+ return this.#manifest.getStats()
104
74
  }
105
75
 
106
- #finalizeWrite = queue(async(hash: Hash, id: Id, size: number) => {
107
- const existingId = await this.#getId(hash)
108
-
109
- if (existingId) {
110
- await this.#bucket.delete(id)
111
- }
112
- else {
113
- await this.#ids.set(hash, id)
114
- await this.#addSize(size)
115
- }
116
- })
117
-
118
- #addSize = queue(async(sizeChange: number) => {
119
- const oldStats = await this.stats()
120
- await this.#stats.set({...oldStats, size: oldStats.size + sizeChange})
121
- })
122
-
123
76
  #selfClean = queue(async() => {
124
- const now = Date.now()
125
- const expiredIds: Id[] = []
126
-
127
- for await (const [id, {created}] of this.#wip.entries({limit: 64})) {
128
- const since = now - created
129
- const isExpired = since > time.days(7)
130
- if (isExpired)
131
- expiredIds.push(id)
132
- }
133
-
77
+ const expiredIds = await this.#manifest.getExpiredIds()
134
78
  await Promise.all(expiredIds.map(id => this.#bucket.delete(id)))
135
- await this.#wip.del(...expiredIds)
79
+ await this.#manifest.deleteWip(...expiredIds)
136
80
  })
137
81
  }
138
82
 
@@ -10,10 +10,6 @@ export class MemoryBucket implements Bucket {
10
10
  return this.#map.has(id)
11
11
  }
12
12
 
13
- async size(id: Id) {
14
- return got(this.#map.get(id)).byteLength
15
- }
16
-
17
13
  async delete(id: Id) {
18
14
  this.#map.delete(id)
19
15
  }
package/s/core/types.ts CHANGED
@@ -2,15 +2,38 @@
2
2
  export type Hash = string
3
3
  export type Id = string
4
4
 
5
+ /** file blob store. */
5
6
  export type Bucket = {
6
7
  has(id: Id): Promise<boolean>
7
- size(id: Id): Promise<number>
8
8
  delete(id: Id): Promise<void>
9
9
  read(id: Id): Promise<Blob>
10
10
  write(id: Id, readable: ReadableStream<Uint8Array>): Promise<void>
11
11
  }
12
12
 
13
+ /** metadata for a single file. */
14
+ export type Info = {
15
+
16
+ /** bucket id for this file's data. */
17
+ id: Id
18
+
19
+ /** file size in bytes. */
20
+ size: number
21
+
22
+ /** when this file was added to the datalake. */
23
+ added: number
24
+ }
25
+
26
+ /** statistics for the whole datalake. */
13
27
  export type Stats = {
28
+
29
+ /** total number of bytes in the whole datalake. */
14
30
  size: number
31
+
32
+ /** total number of files in the datalake. */
33
+ count: number
34
+ }
35
+
36
+ export type Wip = {
37
+ created: number
15
38
  }
16
39
 
@@ -0,0 +1,10 @@
1
+
2
+ import {time} from "@e280/stz"
3
+ import {Wip} from "../types.js"
4
+ import {consts} from "../consts.js"
5
+
6
+ export function isExpired({created}: Wip) {
7
+ const since = Date.now() - created
8
+ return since > time.days(consts.expiry_time)
9
+ }
10
+
@@ -0,0 +1,89 @@
1
+
2
+ import {Kv} from "@e280/kv"
3
+ import {collect, got} from "@e280/stz"
4
+ import {consts} from "../consts.js"
5
+ import {notFound} from "./not-found.js"
6
+ import {isExpired} from "./is-expired.js"
7
+ import {Hash, Id, Info, Stats, Wip} from "../types.js"
8
+
9
+ export class Manifest {
10
+
11
+ /** associates file hashes with bucket ids */
12
+ #info
13
+
14
+ /** temporary record of writes in-progress */
15
+ #wip
16
+
17
+ /** statistics about the whole datalake */
18
+ #stats
19
+
20
+ constructor(kv = new Kv()) {
21
+ this.#info = kv.scope<Info>("info")
22
+ this.#wip = kv.scope<Wip>("wip")
23
+ this.#stats = kv.store<Stats>("stats")
24
+ }
25
+
26
+ async* hashes() {
27
+ yield* this.#info.keys()
28
+ }
29
+
30
+ async hasHash(hash: Hash) {
31
+ return this.#info.has(hash)
32
+ }
33
+
34
+ async saveInfo(hash: Hash, info: Info) {
35
+ await this.#info.set(hash, info)
36
+ }
37
+
38
+ async getInfo(hash: Hash) {
39
+ return await this.#info.get(hash)
40
+ }
41
+
42
+ async needInfo(hash: Hash) {
43
+ return got(await this.getInfo(hash), notFound(hash))
44
+ }
45
+
46
+ async deleteInfo(hash: Hash) {
47
+ await this.#info.del(hash)
48
+ }
49
+
50
+ async addWip(id: Id) {
51
+ await this.#wip.set(id, {created: Date.now()})
52
+ }
53
+
54
+ async deleteWip(...ids: Id[]) {
55
+ await this.#wip.del(...ids)
56
+ }
57
+
58
+ async statsAddFile(size: number) {
59
+ await this.#updateStats(stats => {
60
+ stats.count += 1
61
+ stats.size += size
62
+ })
63
+ }
64
+
65
+ async statsRemoveFile(size: number) {
66
+ await this.#updateStats(stats => {
67
+ stats.count -= 1
68
+ stats.size -= size
69
+ })
70
+ }
71
+
72
+ async getExpiredIds() {
73
+ const limit = consts.self_clean_limit
74
+ return (await collect(this.#wip.entries({limit})))
75
+ .filter(([,wip]) => isExpired(wip))
76
+ .map(([id]) => id)
77
+ }
78
+
79
+ async getStats() {
80
+ return structuredClone((await this.#stats.get()) ?? {count: 0, size: 0})
81
+ }
82
+
83
+ async #updateStats(fn: (stats: Stats) => void) {
84
+ const stats = await this.getStats()
85
+ fn(stats)
86
+ await this.#stats.set(stats)
87
+ }
88
+ }
89
+
@@ -0,0 +1,7 @@
1
+
2
+ import {Hash} from "../types.js"
3
+
4
+ export function notFound(hash: Hash) {
5
+ return `file not found by hash "${hash}"`
6
+ }
7
+
@@ -0,0 +1,25 @@
1
+
2
+ import {hex} from "@e280/stz"
3
+ import {blake3} from "@noble/hashes/blake3.js"
4
+ import {Bucket, Id} from "../types.js"
5
+
6
+ export async function saveAndHash(bucket: Bucket, id: Id, readable: ReadableStream<Uint8Array>) {
7
+ let size = 0
8
+ const pipe = new TransformStream()
9
+ const done = bucket.write(id, pipe.readable)
10
+ const writer = pipe.writable.getWriter()
11
+ const hasher = blake3.create()
12
+
13
+ for await (const chunk of readable) {
14
+ await writer.write(chunk as Uint8Array<ArrayBuffer>)
15
+ hasher.update(chunk)
16
+ size += chunk.byteLength
17
+ }
18
+
19
+ await writer.close()
20
+ await done
21
+
22
+ const hash = hex.fromBytes(hasher.digest())
23
+ return {hash, size}
24
+ }
25
+
@@ -26,11 +26,6 @@ export class DiskBucket implements Bucket {
26
26
  }
27
27
  }
28
28
 
29
- async size(id: Id) {
30
- const stats = await stat(this.#path(id))
31
- return stats.size
32
- }
33
-
34
29
  async delete(id: Id) {
35
30
  try {
36
31
  await unlink(this.#path(id))
package/s/test.ts CHANGED
@@ -13,9 +13,9 @@ const blob = () => new Blob([new Uint8Array([0xDE, 0xAD, 0xBE, 0xEF])])
13
13
  const quickstream = (b: number[]) => new Blob([new Uint8Array(b)]).stream()
14
14
 
15
15
  function setup() {
16
- const manifest = new Kv()
16
+ const kv = new Kv()
17
17
  const bucket = new MemoryBucket()
18
- const mammoth = new Mammoth(manifest, bucket)
18
+ const mammoth = new Mammoth(bucket, kv)
19
19
  return {mammoth}
20
20
  }
21
21
 
@@ -23,15 +23,13 @@ await science.run({
23
23
  "mammoth node": test(async() => {
24
24
  const dataDir = "data"
25
25
  await rm(dataDir, {recursive: true, force: true})
26
- const manifest = new Kv()
27
- const bucket = new DiskBucket(dataDir)
28
- const mammoth = new Mammoth(manifest, bucket)
26
+ const mammoth = new Mammoth(new DiskBucket(dataDir), new Kv())
29
27
  const hash = await mammoth.write(blob().stream())
30
28
  const second = await mammoth.read(hash)
31
29
  expect(bytes.eq(await second.bytes(), await blob().bytes()))
32
30
  }),
33
31
 
34
- "mammoth": {
32
+ "mammoth memory": {
35
33
  ".write and .read": test(async() => {
36
34
  const {mammoth} = setup()
37
35
  const hash = await mammoth.write(blob().stream())
@@ -52,7 +50,7 @@ await science.run({
52
50
  ".write empty file": test(async() => {
53
51
  const {mammoth} = setup()
54
52
  const hash = await mammoth.write(new Blob().stream())
55
- expect(await mammoth.size(hash)).is(0)
53
+ expect((await mammoth.info(hash)).size).is(0)
56
54
  expect((await mammoth.read(hash)).size).is(0)
57
55
  }),
58
56
 
@@ -66,15 +64,15 @@ await science.run({
66
64
 
67
65
  ".stats": test(async() => {
68
66
  const {mammoth} = setup()
69
- expect(await mammoth.stats()).deep({size: 0})
67
+ expect(await mammoth.stats()).deep({count: 0, size: 0})
70
68
  const hashA = await mammoth.write(quickstream([0xC0, 0xFF, 0xEE]))
71
- expect(await mammoth.stats()).deep({size: 3})
69
+ expect(await mammoth.stats()).deep({count: 1, size: 3})
72
70
  const hashB = await mammoth.write(quickstream([0xB0, 0x0B, 0x1E, 0x5]))
73
- expect(await mammoth.stats()).deep({size: 7})
71
+ expect(await mammoth.stats()).deep({count: 2, size: 7})
74
72
  await mammoth.delete(hashA)
75
- expect(await mammoth.stats()).deep({size: 4})
73
+ expect(await mammoth.stats()).deep({count: 1, size: 4})
76
74
  await mammoth.delete(hashB)
77
- expect(await mammoth.stats()).deep({size: 0})
75
+ expect(await mammoth.stats()).deep({count: 0, size: 0})
78
76
  }),
79
77
 
80
78
  ".has": test(async() => {
@@ -86,8 +84,9 @@ await science.run({
86
84
 
87
85
  ".size": test(async() => {
88
86
  const {mammoth} = setup()
89
- const hash = await mammoth.write(blob().stream())
90
- expect(await mammoth.size(hash)).is(4)
87
+ const b = blob()
88
+ const hash = await mammoth.write(b.stream())
89
+ expect((await mammoth.info(hash)).size).is(b.size)
91
90
  }),
92
91
 
93
92
  ".delete": test(async() => {
@@ -10,7 +10,7 @@ export async function example() {
10
10
  const directory = await root.getDirectoryHandle("mammoth", { create: true });
11
11
  const bucket = new OpfsBucket(directory);
12
12
  // setup the mammoth
13
- const mammoth = new Mammoth(kv, bucket);
13
+ const mammoth = new Mammoth(bucket, kv);
14
14
  const myFile = new Blob([new Uint8Array([0xDE, 0xAD, 0xBE, 0xEF])]);
15
15
  // write a file as an example
16
16
  const hash = await mammoth.write(myFile.stream());
@@ -1 +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,UAAU,EAAC,MAAM,kBAAkB,CAAA;AAE3C,MAAM,CAAC,KAAK,UAAU,OAAO;IAE5B,4BAA4B;IAC5B,MAAM,MAAM,GAAG,IAAI,aAAa,CAAC,MAAM,CAAC,YAAY,CAAC,CAAA;IACrD,MAAM,EAAE,GAAG,IAAI,EAAE,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAA;IAE1C,6BAA6B;IAC7B,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,MAAM,GAAG,IAAI,UAAU,CAAC,SAAS,CAAC,CAAA;IAExC,oBAAoB;IACpB,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,EAAE,EAAE,MAAM,CAAC,CAAA;IACvC,MAAM,MAAM,GAAG,IAAI,IAAI,CAAC,CAAC,IAAI,UAAU,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAA;IAEnE,6BAA6B;IAC7B,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAA;IACjD,OAAO,CAAC,GAAG,CAAC,gCAAgC,IAAI,GAAG,CAAC,CAAA;AACrD,CAAC"}
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,UAAU,EAAC,MAAM,kBAAkB,CAAA;AAE3C,MAAM,CAAC,KAAK,UAAU,OAAO;IAE5B,4BAA4B;IAC5B,MAAM,MAAM,GAAG,IAAI,aAAa,CAAC,MAAM,CAAC,YAAY,CAAC,CAAA;IACrD,MAAM,EAAE,GAAG,IAAI,EAAE,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAA;IAE1C,6BAA6B;IAC7B,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,MAAM,GAAG,IAAI,UAAU,CAAC,SAAS,CAAC,CAAA;IAExC,oBAAoB;IACpB,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA;IACvC,MAAM,MAAM,GAAG,IAAI,IAAI,CAAC,CAAC,IAAI,UAAU,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAA;IAEnE,6BAA6B;IAC7B,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAA;IACjD,OAAO,CAAC,GAAG,CAAC,gCAAgC,IAAI,GAAG,CAAC,CAAA;AACrD,CAAC"}
@@ -3,7 +3,6 @@ export declare class OpfsBucket implements Bucket {
3
3
  #private;
4
4
  constructor(directory: FileSystemDirectoryHandle);
5
5
  has(id: Id): Promise<boolean>;
6
- size(id: Id): Promise<number>;
7
6
  delete(id: Id): Promise<void>;
8
7
  read(id: Id): Promise<File>;
9
8
  write(id: Id, readable: ReadableStream<Uint8Array>): Promise<void>;
@@ -15,11 +15,6 @@ export class OpfsBucket {
15
15
  throw error;
16
16
  }
17
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
18
  async delete(id) {
24
19
  try {
25
20
  await this.#directory.removeEntry(id);
@@ -1 +1 @@
1
- {"version":3,"file":"opfs-bucket.js","sourceRoot":"","sources":["../../s/browser/opfs-bucket.ts"],"names":[],"mappings":"AAEA,OAAO,EAAC,UAAU,EAAC,MAAM,yBAAyB,CAAA;AAElD,MAAM,OAAO,UAAU;IACtB,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"}
1
+ {"version":3,"file":"opfs-bucket.js","sourceRoot":"","sources":["../../s/browser/opfs-bucket.ts"],"names":[],"mappings":"AAEA,OAAO,EAAC,UAAU,EAAC,MAAM,yBAAyB,CAAA;AAElD,MAAM,OAAO,UAAU;IACtB,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,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,5 @@
1
+ export declare const consts: {
2
+ max_jobs: number;
3
+ self_clean_limit: number;
4
+ expiry_time: number;
5
+ };
@@ -0,0 +1,7 @@
1
+ import { time } from "@e280/stz";
2
+ export const consts = {
3
+ max_jobs: 64,
4
+ self_clean_limit: 64,
5
+ expiry_time: time.days(7),
6
+ };
7
+ //# sourceMappingURL=consts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consts.js","sourceRoot":"","sources":["../../s/core/consts.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,IAAI,EAAC,MAAM,WAAW,CAAA;AAE9B,MAAM,CAAC,MAAM,MAAM,GAAG;IACrB,QAAQ,EAAE,EAAE;IACZ,gBAAgB,EAAE,EAAE;IACpB,WAAW,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;CACzB,CAAA"}
@@ -3,18 +3,12 @@ import { Hash, Bucket } from "./types.js";
3
3
  /** file storage datalake, content-addressed with blake3 hashes. */
4
4
  export declare class Mammoth {
5
5
  #private;
6
- constructor(
7
- /** key-value metadata manifest. */
8
- kv?: Kv<any>,
9
- /** dumb raw file blob storage. */
10
- bucket?: Bucket);
6
+ constructor(bucket?: Bucket, kv?: Kv<any>);
11
7
  has(hash: Hash): Promise<boolean>;
12
- size(hash: Hash): Promise<number>;
8
+ info(hash: Hash): Promise<import("./types.js").Info>;
13
9
  read(hash: Hash): Promise<Blob>;
14
10
  delete(hash: Hash): Promise<void>;
15
11
  write(readable: ReadableStream<Uint8Array>): Promise<Hash>;
16
12
  hashes(): AsyncGenerator<string, void, unknown>;
17
- stats(): Promise<{
18
- size: number;
19
- }>;
13
+ stats(): Promise<import("./types.js").Stats>;
20
14
  }
package/x/core/mammoth.js CHANGED
@@ -1,106 +1,66 @@
1
1
  import { Kv } from "@e280/kv";
2
- import { got, hex, queue, time } from "@e280/stz";
3
- import { blake3 } from "@noble/hashes/blake3.js";
2
+ import { lane, queue } from "@e280/stz";
3
+ import { consts } from "./consts.js";
4
+ import { Manifest } from "./utils/manifest.js";
4
5
  import { randomId } from "./utils/random-id.js";
5
6
  import { MemoryBucket } from "./memory-bucket.js";
7
+ import { saveAndHash } from "./utils/save-and-hash.js";
6
8
  /** file storage datalake, content-addressed with blake3 hashes. */
7
9
  export class Mammoth {
8
- #ids;
9
- #wip;
10
- #stats;
11
10
  #bucket;
12
- constructor(
13
- /** key-value metadata manifest. */
14
- kv = new Kv(),
15
- /** dumb raw file blob storage. */
16
- bucket = new MemoryBucket()) {
17
- this.#ids = kv.scope("ids");
18
- this.#wip = kv.scope("wip");
19
- this.#stats = kv.store("stats");
11
+ #manifest;
12
+ #wholesome = lane(consts.max_jobs);
13
+ constructor(bucket = new MemoryBucket(), kv = new Kv()) {
20
14
  this.#bucket = bucket;
15
+ this.#manifest = new Manifest(kv);
21
16
  }
22
17
  async has(hash) {
23
- return this.#ids.has(hash);
18
+ return this.#manifest.hasHash(hash);
24
19
  }
25
- async size(hash) {
26
- const id = await this.#needId(hash);
27
- return this.#bucket.size(id);
20
+ async info(hash) {
21
+ return this.#manifest.needInfo(hash);
28
22
  }
29
23
  async read(hash) {
30
- const id = await this.#needId(hash);
24
+ const { id } = await this.#manifest.needInfo(hash);
31
25
  return this.#bucket.read(id);
32
26
  }
33
27
  async delete(hash) {
34
- const id = await this.#getId(hash);
35
- if (id) {
36
- const size = await this.#bucket.size(id);
37
- await this.#ids.del(hash);
38
- await this.#bucket.delete(id);
39
- await this.#addSize(-size);
40
- }
28
+ await this.#wholesome(async () => {
29
+ const info = await this.#manifest.getInfo(hash);
30
+ if (info) {
31
+ await this.#manifest.deleteInfo(hash);
32
+ await this.#bucket.delete(info.id);
33
+ await this.#manifest.statsRemoveFile(info.size);
34
+ }
35
+ });
41
36
  }
42
37
  async write(readable) {
43
38
  const id = randomId();
44
- await this.#wip.set(id, { created: Date.now() });
45
- const { hash, size } = await this.#hashAndSave(id, readable);
46
- await this.#wip.del(id);
47
- await this.#finalizeWrite(hash, id, size);
39
+ await this.#manifest.addWip(id);
40
+ const { hash, size } = await saveAndHash(this.#bucket, id, readable);
41
+ await this.#wholesome(async () => {
42
+ if (await this.#manifest.hasHash(hash)) {
43
+ await this.#bucket.delete(id); // forget this new file (we already have it)
44
+ }
45
+ else {
46
+ await this.#manifest.saveInfo(hash, { id, size, added: Date.now() });
47
+ await this.#manifest.statsAddFile(size);
48
+ }
49
+ });
50
+ await this.#manifest.deleteWip(id);
48
51
  void this.#selfClean().catch(() => { });
49
52
  return hash;
50
53
  }
51
54
  async *hashes() {
52
- yield* this.#ids.keys();
55
+ yield* this.#manifest.hashes();
53
56
  }
54
57
  async stats() {
55
- return (await this.#stats.get()) ?? { size: 0 };
58
+ return this.#manifest.getStats();
56
59
  }
57
- async #getId(hash) {
58
- return this.#ids.get(hash);
59
- }
60
- async #needId(hash) {
61
- return got(await this.#getId(hash), `file not found by hash "${hash}"`);
62
- }
63
- async #hashAndSave(id, readable) {
64
- let size = 0;
65
- const pipe = new TransformStream();
66
- const done = this.#bucket.write(id, pipe.readable);
67
- const writer = pipe.writable.getWriter();
68
- const hasher = blake3.create();
69
- for await (const chunk of readable) {
70
- await writer.write(chunk);
71
- hasher.update(chunk);
72
- size += chunk.byteLength;
73
- }
74
- await writer.close();
75
- await done;
76
- const hash = hex.fromBytes(hasher.digest());
77
- return { hash, size };
78
- }
79
- #finalizeWrite = queue(async (hash, id, size) => {
80
- const existingId = await this.#getId(hash);
81
- if (existingId) {
82
- await this.#bucket.delete(id);
83
- }
84
- else {
85
- await this.#ids.set(hash, id);
86
- await this.#addSize(size);
87
- }
88
- });
89
- #addSize = queue(async (sizeChange) => {
90
- const oldStats = await this.stats();
91
- await this.#stats.set({ ...oldStats, size: oldStats.size + sizeChange });
92
- });
93
60
  #selfClean = queue(async () => {
94
- const now = Date.now();
95
- const expiredIds = [];
96
- for await (const [id, { created }] of this.#wip.entries({ limit: 64 })) {
97
- const since = now - created;
98
- const isExpired = since > time.days(7);
99
- if (isExpired)
100
- expiredIds.push(id);
101
- }
61
+ const expiredIds = await this.#manifest.getExpiredIds();
102
62
  await Promise.all(expiredIds.map(id => this.#bucket.delete(id)));
103
- await this.#wip.del(...expiredIds);
63
+ await this.#manifest.deleteWip(...expiredIds);
104
64
  });
105
65
  }
106
66
  //# sourceMappingURL=mammoth.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"mammoth.js","sourceRoot":"","sources":["../../s/core/mammoth.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"mammoth.js","sourceRoot":"","sources":["../../s/core/mammoth.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,EAAE,EAAC,MAAM,UAAU,CAAA;AAC3B,OAAO,EAAC,IAAI,EAAE,KAAK,EAAC,MAAM,WAAW,CAAA;AAErC,OAAO,EAAC,MAAM,EAAC,MAAM,aAAa,CAAA;AAElC,OAAO,EAAC,QAAQ,EAAC,MAAM,qBAAqB,CAAA;AAC5C,OAAO,EAAC,QAAQ,EAAC,MAAM,sBAAsB,CAAA;AAC7C,OAAO,EAAC,YAAY,EAAC,MAAM,oBAAoB,CAAA;AAC/C,OAAO,EAAC,WAAW,EAAC,MAAM,0BAA0B,CAAA;AAEpD,mEAAmE;AACnE,MAAM,OAAO,OAAO;IACnB,OAAO,CAAA;IACP,SAAS,CAAA;IACT,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;IAElC,YAAY,MAAM,GAAW,IAAI,YAAY,EAAE,EAAE,EAAE,GAAG,IAAI,EAAE,EAAE;QAC7D,IAAI,CAAC,OAAO,GAAG,MAAM,CAAA;QACrB,IAAI,CAAC,SAAS,GAAG,IAAI,QAAQ,CAAC,EAAE,CAAC,CAAA;IAClC,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,IAAU;QACnB,OAAO,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;IACpC,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,IAAU;QACpB,OAAO,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAA;IACrC,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,IAAU;QACpB,MAAM,EAAC,EAAE,EAAC,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAA;QAChD,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IAC7B,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,IAAU;QACtB,MAAM,IAAI,CAAC,UAAU,CAAC,KAAK,IAAG,EAAE;YAC/B,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;YAC/C,IAAI,IAAI,EAAE,CAAC;gBACV,MAAM,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,CAAA;gBACrC,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;gBAClC,MAAM,IAAI,CAAC,SAAS,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YAChD,CAAC;QACF,CAAC,CAAC,CAAA;IACH,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,QAAoC;QAC/C,MAAM,EAAE,GAAG,QAAQ,EAAE,CAAA;QAErB,MAAM,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;QAC/B,MAAM,EAAC,IAAI,EAAE,IAAI,EAAC,GAAG,MAAM,WAAW,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,EAAE,QAAQ,CAAC,CAAA;QAElE,MAAM,IAAI,CAAC,UAAU,CAAC,KAAK,IAAG,EAAE;YAC/B,IAAI,MAAM,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;gBACxC,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA,CAAC,4CAA4C;YAC3E,CAAC;iBACI,CAAC;gBACL,MAAM,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,GAAG,EAAE,EAAC,CAAC,CAAA;gBAClE,MAAM,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,IAAI,CAAC,CAAA;YACxC,CAAC;QACF,CAAC,CAAC,CAAA;QAEF,MAAM,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE,CAAC,CAAA;QAClC,KAAK,IAAI,CAAC,UAAU,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA;QACtC,OAAO,IAAI,CAAA;IACZ,CAAC;IAED,KAAK,CAAA,CAAE,MAAM;QACZ,KAAK,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,CAAA;IAC/B,CAAC;IAED,KAAK,CAAC,KAAK;QACV,OAAO,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,CAAA;IACjC,CAAC;IAED,UAAU,GAAG,KAAK,CAAC,KAAK,IAAG,EAAE;QAC5B,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,aAAa,EAAE,CAAA;QACvD,MAAM,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;QAChE,MAAM,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,GAAG,UAAU,CAAC,CAAA;IAC9C,CAAC,CAAC,CAAA;CACF"}
@@ -2,7 +2,6 @@ import { Bucket, Id } from "./types.js";
2
2
  export declare class MemoryBucket implements Bucket {
3
3
  #private;
4
4
  has(id: Id): Promise<boolean>;
5
- size(id: Id): Promise<number>;
6
5
  delete(id: Id): Promise<void>;
7
6
  read(id: Id): Promise<Blob>;
8
7
  write(id: Id, readable: ReadableStream<Uint8Array>): Promise<void>;
@@ -5,9 +5,6 @@ export class MemoryBucket {
5
5
  async has(id) {
6
6
  return this.#map.has(id);
7
7
  }
8
- async size(id) {
9
- return got(this.#map.get(id)).byteLength;
10
- }
11
8
  async delete(id) {
12
9
  this.#map.delete(id);
13
10
  }
@@ -1 +1 @@
1
- {"version":3,"file":"memory-bucket.js","sourceRoot":"","sources":["../../s/core/memory-bucket.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,YAAY;IACxB,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"}
1
+ {"version":3,"file":"memory-bucket.js","sourceRoot":"","sources":["../../s/core/memory-bucket.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,YAAY;IACxB,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,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"}
package/x/core/types.d.ts CHANGED
@@ -1,12 +1,28 @@
1
1
  export type Hash = string;
2
2
  export type Id = string;
3
+ /** file blob store. */
3
4
  export type Bucket = {
4
5
  has(id: Id): Promise<boolean>;
5
- size(id: Id): Promise<number>;
6
6
  delete(id: Id): Promise<void>;
7
7
  read(id: Id): Promise<Blob>;
8
8
  write(id: Id, readable: ReadableStream<Uint8Array>): Promise<void>;
9
9
  };
10
+ /** metadata for a single file. */
11
+ export type Info = {
12
+ /** bucket id for this file's data. */
13
+ id: Id;
14
+ /** file size in bytes. */
15
+ size: number;
16
+ /** when this file was added to the datalake. */
17
+ added: number;
18
+ };
19
+ /** statistics for the whole datalake. */
10
20
  export type Stats = {
21
+ /** total number of bytes in the whole datalake. */
11
22
  size: number;
23
+ /** total number of files in the datalake. */
24
+ count: number;
25
+ };
26
+ export type Wip = {
27
+ created: number;
12
28
  };
@@ -0,0 +1,2 @@
1
+ import { Wip } from "../types.js";
2
+ export declare function isExpired({ created }: Wip): boolean;
@@ -0,0 +1,7 @@
1
+ import { time } from "@e280/stz";
2
+ import { consts } from "../consts.js";
3
+ export function isExpired({ created }) {
4
+ const since = Date.now() - created;
5
+ return since > time.days(consts.expiry_time);
6
+ }
7
+ //# sourceMappingURL=is-expired.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"is-expired.js","sourceRoot":"","sources":["../../../s/core/utils/is-expired.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,IAAI,EAAC,MAAM,WAAW,CAAA;AAE9B,OAAO,EAAC,MAAM,EAAC,MAAM,cAAc,CAAA;AAEnC,MAAM,UAAU,SAAS,CAAC,EAAC,OAAO,EAAM;IACvC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,CAAA;IAClC,OAAO,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,CAAA;AAC7C,CAAC"}
@@ -0,0 +1,18 @@
1
+ import { Kv } from "@e280/kv";
2
+ import { Hash, Id, Info, Stats } from "../types.js";
3
+ export declare class Manifest {
4
+ #private;
5
+ constructor(kv?: Kv<any>);
6
+ hashes(): AsyncGenerator<string, void, unknown>;
7
+ hasHash(hash: Hash): Promise<boolean>;
8
+ saveInfo(hash: Hash, info: Info): Promise<void>;
9
+ getInfo(hash: Hash): Promise<Info | undefined>;
10
+ needInfo(hash: Hash): Promise<Info>;
11
+ deleteInfo(hash: Hash): Promise<void>;
12
+ addWip(id: Id): Promise<void>;
13
+ deleteWip(...ids: Id[]): Promise<void>;
14
+ statsAddFile(size: number): Promise<void>;
15
+ statsRemoveFile(size: number): Promise<void>;
16
+ getExpiredIds(): Promise<string[]>;
17
+ getStats(): Promise<Stats>;
18
+ }
@@ -0,0 +1,69 @@
1
+ import { Kv } from "@e280/kv";
2
+ import { collect, got } from "@e280/stz";
3
+ import { consts } from "../consts.js";
4
+ import { notFound } from "./not-found.js";
5
+ import { isExpired } from "./is-expired.js";
6
+ export class Manifest {
7
+ /** associates file hashes with bucket ids */
8
+ #info;
9
+ /** temporary record of writes in-progress */
10
+ #wip;
11
+ /** statistics about the whole datalake */
12
+ #stats;
13
+ constructor(kv = new Kv()) {
14
+ this.#info = kv.scope("info");
15
+ this.#wip = kv.scope("wip");
16
+ this.#stats = kv.store("stats");
17
+ }
18
+ async *hashes() {
19
+ yield* this.#info.keys();
20
+ }
21
+ async hasHash(hash) {
22
+ return this.#info.has(hash);
23
+ }
24
+ async saveInfo(hash, info) {
25
+ await this.#info.set(hash, info);
26
+ }
27
+ async getInfo(hash) {
28
+ return await this.#info.get(hash);
29
+ }
30
+ async needInfo(hash) {
31
+ return got(await this.getInfo(hash), notFound(hash));
32
+ }
33
+ async deleteInfo(hash) {
34
+ await this.#info.del(hash);
35
+ }
36
+ async addWip(id) {
37
+ await this.#wip.set(id, { created: Date.now() });
38
+ }
39
+ async deleteWip(...ids) {
40
+ await this.#wip.del(...ids);
41
+ }
42
+ async statsAddFile(size) {
43
+ await this.#updateStats(stats => {
44
+ stats.count += 1;
45
+ stats.size += size;
46
+ });
47
+ }
48
+ async statsRemoveFile(size) {
49
+ await this.#updateStats(stats => {
50
+ stats.count -= 1;
51
+ stats.size -= size;
52
+ });
53
+ }
54
+ async getExpiredIds() {
55
+ const limit = consts.self_clean_limit;
56
+ return (await collect(this.#wip.entries({ limit })))
57
+ .filter(([, wip]) => isExpired(wip))
58
+ .map(([id]) => id);
59
+ }
60
+ async getStats() {
61
+ return structuredClone((await this.#stats.get()) ?? { count: 0, size: 0 });
62
+ }
63
+ async #updateStats(fn) {
64
+ const stats = await this.getStats();
65
+ fn(stats);
66
+ await this.#stats.set(stats);
67
+ }
68
+ }
69
+ //# sourceMappingURL=manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.js","sourceRoot":"","sources":["../../../s/core/utils/manifest.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,EAAE,EAAC,MAAM,UAAU,CAAA;AAC3B,OAAO,EAAC,OAAO,EAAE,GAAG,EAAC,MAAM,WAAW,CAAA;AACtC,OAAO,EAAC,MAAM,EAAC,MAAM,cAAc,CAAA;AACnC,OAAO,EAAC,QAAQ,EAAC,MAAM,gBAAgB,CAAA;AACvC,OAAO,EAAC,SAAS,EAAC,MAAM,iBAAiB,CAAA;AAGzC,MAAM,OAAO,QAAQ;IAEpB,6CAA6C;IAC7C,KAAK,CAAA;IAEL,6CAA6C;IAC7C,IAAI,CAAA;IAEJ,0CAA0C;IAC1C,MAAM,CAAA;IAEN,YAAY,EAAE,GAAG,IAAI,EAAE,EAAE;QACxB,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC,KAAK,CAAO,MAAM,CAAC,CAAA;QACnC,IAAI,CAAC,IAAI,GAAG,EAAE,CAAC,KAAK,CAAM,KAAK,CAAC,CAAA;QAChC,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC,KAAK,CAAQ,OAAO,CAAC,CAAA;IACvC,CAAC;IAED,KAAK,CAAA,CAAE,MAAM;QACZ,KAAK,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAA;IACzB,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,IAAU;QACvB,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;IAC5B,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,IAAU,EAAE,IAAU;QACpC,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;IACjC,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,IAAU;QACvB,OAAO,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;IAClC,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,IAAU;QACxB,OAAO,GAAG,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAA;IACrD,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,IAAU;QAC1B,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;IAC3B,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,EAAM;QAClB,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,EAAC,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE,EAAC,CAAC,CAAA;IAC/C,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,GAAG,GAAS;QAC3B,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,CAAA;IAC5B,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,IAAY;QAC9B,MAAM,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE;YAC/B,KAAK,CAAC,KAAK,IAAI,CAAC,CAAA;YAChB,KAAK,CAAC,IAAI,IAAI,IAAI,CAAA;QACnB,CAAC,CAAC,CAAA;IACH,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,IAAY;QACjC,MAAM,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE;YAC/B,KAAK,CAAC,KAAK,IAAI,CAAC,CAAA;YAChB,KAAK,CAAC,IAAI,IAAI,IAAI,CAAA;QACnB,CAAC,CAAC,CAAA;IACH,CAAC;IAED,KAAK,CAAC,aAAa;QAClB,MAAM,KAAK,GAAG,MAAM,CAAC,gBAAgB,CAAA;QACrC,OAAO,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,EAAC,KAAK,EAAC,CAAC,CAAC,CAAC;aAChD,MAAM,CAAC,CAAC,CAAC,EAAC,GAAG,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;aAClC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAA;IACpB,CAAC;IAED,KAAK,CAAC,QAAQ;QACb,OAAO,eAAe,CAAC,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,IAAI,EAAC,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAC,CAAC,CAAA;IACzE,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,EAA0B;QAC5C,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAA;QACnC,EAAE,CAAC,KAAK,CAAC,CAAA;QACT,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;IAC7B,CAAC;CACD"}
@@ -0,0 +1,2 @@
1
+ import { Hash } from "../types.js";
2
+ export declare function notFound(hash: Hash): string;
@@ -0,0 +1,4 @@
1
+ export function notFound(hash) {
2
+ return `file not found by hash "${hash}"`;
3
+ }
4
+ //# sourceMappingURL=not-found.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"not-found.js","sourceRoot":"","sources":["../../../s/core/utils/not-found.ts"],"names":[],"mappings":"AAGA,MAAM,UAAU,QAAQ,CAAC,IAAU;IAClC,OAAO,2BAA2B,IAAI,GAAG,CAAA;AAC1C,CAAC"}
@@ -0,0 +1,5 @@
1
+ import { Bucket, Id } from "../types.js";
2
+ export declare function saveAndHash(bucket: Bucket, id: Id, readable: ReadableStream<Uint8Array>): Promise<{
3
+ hash: string;
4
+ size: number;
5
+ }>;
@@ -0,0 +1,19 @@
1
+ import { hex } from "@e280/stz";
2
+ import { blake3 } from "@noble/hashes/blake3.js";
3
+ export async function saveAndHash(bucket, id, readable) {
4
+ let size = 0;
5
+ const pipe = new TransformStream();
6
+ const done = bucket.write(id, pipe.readable);
7
+ const writer = pipe.writable.getWriter();
8
+ const hasher = blake3.create();
9
+ for await (const chunk of readable) {
10
+ await writer.write(chunk);
11
+ hasher.update(chunk);
12
+ size += chunk.byteLength;
13
+ }
14
+ await writer.close();
15
+ await done;
16
+ const hash = hex.fromBytes(hasher.digest());
17
+ return { hash, size };
18
+ }
19
+ //# sourceMappingURL=save-and-hash.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"save-and-hash.js","sourceRoot":"","sources":["../../../s/core/utils/save-and-hash.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,GAAG,EAAC,MAAM,WAAW,CAAA;AAC7B,OAAO,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAA;AAG9C,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,MAAc,EAAE,EAAM,EAAE,QAAoC;IAC7F,IAAI,IAAI,GAAG,CAAC,CAAA;IACZ,MAAM,IAAI,GAAG,IAAI,eAAe,EAAE,CAAA;IAClC,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,EAAE,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAA;IAC5C,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,EAAE,CAAA;IACxC,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,CAAA;IAE9B,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,QAAQ,EAAE,CAAC;QACpC,MAAM,MAAM,CAAC,KAAK,CAAC,KAAgC,CAAC,CAAA;QACpD,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;QACpB,IAAI,IAAI,KAAK,CAAC,UAAU,CAAA;IACzB,CAAC;IAED,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;IACpB,MAAM,IAAI,CAAA;IAEV,MAAM,IAAI,GAAG,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAA;IAC3C,OAAO,EAAC,IAAI,EAAE,IAAI,EAAC,CAAA;AACpB,CAAC"}
@@ -3,7 +3,6 @@ export declare class DiskBucket implements Bucket {
3
3
  #private;
4
4
  constructor(directory: string);
5
5
  has(id: Id): Promise<boolean>;
6
- size(id: Id): Promise<number>;
7
6
  delete(id: Id): Promise<void>;
8
7
  read(id: Id): Promise<Blob>;
9
8
  write(id: Id, readable: ReadableStream<Uint8Array>): Promise<void>;
@@ -20,10 +20,6 @@ export class DiskBucket {
20
20
  throw error;
21
21
  }
22
22
  }
23
- async size(id) {
24
- const stats = await stat(this.#path(id));
25
- return stats.size;
26
- }
27
23
  async delete(id) {
28
24
  try {
29
25
  await unlink(this.#path(id));
@@ -1 +1 @@
1
- {"version":3,"file":"disk-bucket.js","sourceRoot":"","sources":["../../s/node/disk-bucket.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,IAAI,EAAC,MAAM,WAAW,CAAA;AAC9B,OAAO,EAAC,QAAQ,EAAC,MAAM,aAAa,CAAA;AACpC,OAAO,EAAC,QAAQ,EAAC,MAAM,sBAAsB,CAAA;AAC7C,OAAO,EAAC,KAAK,EAAE,IAAI,EAAE,MAAM,EAAC,MAAM,kBAAkB,CAAA;AACpD,OAAO,EAAC,iBAAiB,EAAE,UAAU,EAAC,MAAM,SAAS,CAAA;AAGrD,OAAO,EAAC,UAAU,EAAC,MAAM,yBAAyB,CAAA;AAElD,MAAM,OAAO,UAAU;IACtB,UAAU,CAAA;IAEV,YAAY,SAAiB;QAC5B,IAAI,CAAC,UAAU,GAAG,SAAS,CAAA;IAC5B,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,EAAM;QACf,IAAI,CAAC;YACJ,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAA;YAC1B,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,KAAK,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAA;QACxC,OAAO,KAAK,CAAC,IAAI,CAAA;IAClB,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,EAAM;QAClB,IAAI,CAAC;YACJ,MAAM,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAA;QAC7B,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,OAAO,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAA;IAClC,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,EAAM,EAAE,QAAoC;QACvD,MAAM,KAAK,CAAC,IAAI,CAAC,UAAU,EAAE,EAAC,SAAS,EAAE,IAAI,EAAC,CAAC,CAAA;QAC/C,MAAM,QAAQ,CACb,QAAQ,CAAC,OAAO,CAAC,QAAe,CAAC,EACjC,iBAAiB,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CACjC,CAAA;IACF,CAAC;IAED,KAAK,CAAC,EAAM;QACX,OAAO,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE,CAAC,CAAA;IACjC,CAAC;CACD"}
1
+ {"version":3,"file":"disk-bucket.js","sourceRoot":"","sources":["../../s/node/disk-bucket.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,IAAI,EAAC,MAAM,WAAW,CAAA;AAC9B,OAAO,EAAC,QAAQ,EAAC,MAAM,aAAa,CAAA;AACpC,OAAO,EAAC,QAAQ,EAAC,MAAM,sBAAsB,CAAA;AAC7C,OAAO,EAAC,KAAK,EAAE,IAAI,EAAE,MAAM,EAAC,MAAM,kBAAkB,CAAA;AACpD,OAAO,EAAC,iBAAiB,EAAE,UAAU,EAAC,MAAM,SAAS,CAAA;AAGrD,OAAO,EAAC,UAAU,EAAC,MAAM,yBAAyB,CAAA;AAElD,MAAM,OAAO,UAAU;IACtB,UAAU,CAAA;IAEV,YAAY,SAAiB;QAC5B,IAAI,CAAC,UAAU,GAAG,SAAS,CAAA;IAC5B,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,EAAM;QACf,IAAI,CAAC;YACJ,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAA;YAC1B,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,MAAM,CAAC,EAAM;QAClB,IAAI,CAAC;YACJ,MAAM,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAA;QAC7B,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,OAAO,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAA;IAClC,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,EAAM,EAAE,QAAoC;QACvD,MAAM,KAAK,CAAC,IAAI,CAAC,UAAU,EAAE,EAAC,SAAS,EAAE,IAAI,EAAC,CAAC,CAAA;QAC/C,MAAM,QAAQ,CACb,QAAQ,CAAC,OAAO,CAAC,QAAe,CAAC,EACjC,iBAAiB,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CACjC,CAAA;IACF,CAAC;IAED,KAAK,CAAC,EAAM;QACX,OAAO,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE,CAAC,CAAA;IACjC,CAAC;CACD"}
package/x/test.js CHANGED
@@ -9,23 +9,21 @@ import { MemoryBucket } from "./core/memory-bucket.js";
9
9
  const blob = () => new Blob([new Uint8Array([0xDE, 0xAD, 0xBE, 0xEF])]);
10
10
  const quickstream = (b) => new Blob([new Uint8Array(b)]).stream();
11
11
  function setup() {
12
- const manifest = new Kv();
12
+ const kv = new Kv();
13
13
  const bucket = new MemoryBucket();
14
- const mammoth = new Mammoth(manifest, bucket);
14
+ const mammoth = new Mammoth(bucket, kv);
15
15
  return { mammoth };
16
16
  }
17
17
  await science.run({
18
18
  "mammoth node": test(async () => {
19
19
  const dataDir = "data";
20
20
  await rm(dataDir, { recursive: true, force: true });
21
- const manifest = new Kv();
22
- const bucket = new DiskBucket(dataDir);
23
- const mammoth = new Mammoth(manifest, bucket);
21
+ const mammoth = new Mammoth(new DiskBucket(dataDir), new Kv());
24
22
  const hash = await mammoth.write(blob().stream());
25
23
  const second = await mammoth.read(hash);
26
24
  expect(bytes.eq(await second.bytes(), await blob().bytes()));
27
25
  }),
28
- "mammoth": {
26
+ "mammoth memory": {
29
27
  ".write and .read": test(async () => {
30
28
  const { mammoth } = setup();
31
29
  const hash = await mammoth.write(blob().stream());
@@ -44,7 +42,7 @@ await science.run({
44
42
  ".write empty file": test(async () => {
45
43
  const { mammoth } = setup();
46
44
  const hash = await mammoth.write(new Blob().stream());
47
- expect(await mammoth.size(hash)).is(0);
45
+ expect((await mammoth.info(hash)).size).is(0);
48
46
  expect((await mammoth.read(hash)).size).is(0);
49
47
  }),
50
48
  ".write distinct files": test(async () => {
@@ -56,15 +54,15 @@ await science.run({
56
54
  }),
57
55
  ".stats": test(async () => {
58
56
  const { mammoth } = setup();
59
- expect(await mammoth.stats()).deep({ size: 0 });
57
+ expect(await mammoth.stats()).deep({ count: 0, size: 0 });
60
58
  const hashA = await mammoth.write(quickstream([0xC0, 0xFF, 0xEE]));
61
- expect(await mammoth.stats()).deep({ size: 3 });
59
+ expect(await mammoth.stats()).deep({ count: 1, size: 3 });
62
60
  const hashB = await mammoth.write(quickstream([0xB0, 0x0B, 0x1E, 0x5]));
63
- expect(await mammoth.stats()).deep({ size: 7 });
61
+ expect(await mammoth.stats()).deep({ count: 2, size: 7 });
64
62
  await mammoth.delete(hashA);
65
- expect(await mammoth.stats()).deep({ size: 4 });
63
+ expect(await mammoth.stats()).deep({ count: 1, size: 4 });
66
64
  await mammoth.delete(hashB);
67
- expect(await mammoth.stats()).deep({ size: 0 });
65
+ expect(await mammoth.stats()).deep({ count: 0, size: 0 });
68
66
  }),
69
67
  ".has": test(async () => {
70
68
  const { mammoth } = setup();
@@ -74,8 +72,9 @@ await science.run({
74
72
  }),
75
73
  ".size": test(async () => {
76
74
  const { mammoth } = setup();
77
- const hash = await mammoth.write(blob().stream());
78
- expect(await mammoth.size(hash)).is(4);
75
+ const b = blob();
76
+ const hash = await mammoth.write(b.stream());
77
+ expect((await mammoth.info(hash)).size).is(b.size);
79
78
  }),
80
79
  ".delete": test(async () => {
81
80
  const { mammoth } = setup();
package/x/test.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"test.js","sourceRoot":"","sources":["../s/test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"test.js","sourceRoot":"","sources":["../s/test.ts"],"names":[],"mappings":"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"}