@fireproof/core 0.6.5 → 0.7.0-alpha.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.
Files changed (47) hide show
  1. package/README.md +2 -2
  2. package/dist/{src/blockstore.js → blockstore.js} +7 -3
  3. package/dist/{src/database.js → database.js} +74 -48
  4. package/dist/{src/db-index.js → db-index.js} +2 -1
  5. package/dist/fireproof.js +92 -0
  6. package/dist/{src/loader.js → loader.js} +2 -3
  7. package/dist/{src/prolly.js → prolly.js} +1 -0
  8. package/dist/src/fireproof.d.ts +137 -136
  9. package/dist/src/fireproof.js +18179 -11484
  10. package/dist/src/fireproof.js.map +1 -1
  11. package/dist/src/fireproof.mjs +18180 -11484
  12. package/dist/src/fireproof.mjs.map +1 -1
  13. package/dist/{src/storage → storage}/filesystem.js +2 -2
  14. package/dist/{src/sync.js → sync.js} +1 -1
  15. package/dist/valet.js +200 -0
  16. package/package.json +4 -5
  17. package/src/blockstore.js +6 -3
  18. package/src/database.js +80 -52
  19. package/src/db-index.js +2 -1
  20. package/src/fireproof.js +41 -30
  21. package/src/import.js +34 -0
  22. package/src/loader.js +26 -0
  23. package/src/prolly.js +2 -0
  24. package/src/storage/base.js +371 -0
  25. package/src/storage/browser.js +67 -0
  26. package/src/storage/filesystem.js +70 -0
  27. package/src/storage/rest.js +60 -0
  28. package/src/storage/ucan.js +0 -0
  29. package/src/sync.js +1 -1
  30. package/src/valet.js +57 -359
  31. package/dist/hooks/use-fireproof.js +0 -150
  32. package/dist/src/crypto-poly.js +0 -4
  33. package/dist/src/link.js +0 -1
  34. package/dist/src/valet.js +0 -476
  35. package/hooks/use-fireproof.js +0 -173
  36. package/src/listener.js +0 -119
  37. package/src/utils.js +0 -16
  38. /package/dist/{src/clock.js → clock.js} +0 -0
  39. /package/dist/{src/crypto.js → crypto.js} +0 -0
  40. /package/dist/{src/import.js → import.js} +0 -0
  41. /package/dist/{src/listener.js → listener.js} +0 -0
  42. /package/dist/{src/sha1.js → sha1.js} +0 -0
  43. /package/dist/{src/storage → storage}/base.js +0 -0
  44. /package/dist/{src/storage → storage}/browser.js +0 -0
  45. /package/dist/{src/storage → storage}/rest.js +0 -0
  46. /package/dist/{src/storage → storage}/ucan.js +0 -0
  47. /package/dist/{src/utils.js → utils.js} +0 -0
package/src/import.js ADDED
@@ -0,0 +1,34 @@
1
+ import { createReadStream } from 'fs'
2
+ import { join } from 'path'
3
+ import { parse } from '@jsonlines/core'
4
+ import cargoQueue from 'async/cargoQueue.js'
5
+
6
+ // todo maybe this goes in a utils package for tree-shaking?
7
+
8
+ async function loadData (database, filename) {
9
+ const fullFilePath = join(process.cwd(), filename)
10
+ const readableStream = createReadStream(fullFilePath)
11
+ const parseStream = parse()
12
+ readableStream.pipe(parseStream)
13
+
14
+ const saveQueue = cargoQueue(async (tasks, callback) => {
15
+ for (const t of tasks) {
16
+ await database.put(t)
17
+ }
18
+ callback()
19
+ })
20
+
21
+ parseStream.on('data', async data => {
22
+ saveQueue.push(data)
23
+ })
24
+ let res
25
+ const p = new Promise((resolve, reject) => {
26
+ res = resolve
27
+ })
28
+ saveQueue.drain(async x => {
29
+ res()
30
+ })
31
+ return p
32
+ }
33
+
34
+ export { loadData }
package/src/loader.js ADDED
@@ -0,0 +1,26 @@
1
+ import { Browser } from './storage/browser.js'
2
+ import { Filesystem } from './storage/filesystem.js'
3
+ import { Rest } from './storage/rest.js'
4
+
5
+ const FORCE_IDB = typeof process !== 'undefined' && !!process.env?.FORCE_IDB
6
+
7
+ /* global window */
8
+
9
+ export const Loader = {
10
+ appropriate: (name, config = {}) => {
11
+ let isBrowser = false
12
+ try {
13
+ isBrowser = window.localStorage && true
14
+ } catch (e) {}
15
+
16
+ if (config.type === 'rest') {
17
+ return new Rest(name, config)
18
+ }
19
+
20
+ if (FORCE_IDB || isBrowser) {
21
+ return new Browser(name, config)
22
+ } else {
23
+ return new Filesystem(name, config)
24
+ }
25
+ }
26
+ }
package/src/prolly.js CHANGED
@@ -15,6 +15,8 @@ import { nocache as cache } from 'prolly-trees/cache'
15
15
  import { CIDCounter, bf, simpleCompare as compare } from 'prolly-trees/utils'
16
16
  import * as codec from '@ipld/dag-cbor'
17
17
  import { sha256 as hasher } from 'multiformats/hashes/sha2'
18
+ // import { blake2b256 as hasher } from '@multiformats/blake2/blake2b'
19
+
18
20
  import { doTransaction } from './blockstore.js'
19
21
  import { create as createBlock } from 'multiformats/block'
20
22
  const blockOpts = { cache, chunker: bf(30), codec, hasher, compare }
@@ -0,0 +1,371 @@
1
+ import randomBytes from 'randombytes'
2
+ // import { randomBytes } from 'crypto'
3
+ import { create, load } from 'ipld-hashmap'
4
+ import { parse } from 'multiformats/link'
5
+ import { CarReader } from '@ipld/car'
6
+ import { CID } from 'multiformats/cid'
7
+ import { sha256 } from 'multiformats/hashes/sha2'
8
+ import * as Block from 'multiformats/block'
9
+ import * as dagcbor from '@ipld/dag-cbor'
10
+ // @ts-ignore
11
+ import { bf, simpleCompare as compare } from 'prolly-trees/utils'
12
+ // @ts-ignore
13
+ import { nocache as cache } from 'prolly-trees/cache'
14
+ import { Buffer } from 'buffer'
15
+ import { rawSha1 as sha1sync } from '../sha1.js'
16
+ // @ts-ignore
17
+ import * as codec from 'encrypted-block'
18
+ import { blocksToCarBlock, blocksToEncryptedCarBlock, blocksFromEncryptedCarBlock } from '../valet.js'
19
+
20
+ const chunker = bf(30)
21
+ const blockOpts = { cache, chunker, codec: dagcbor, hasher: sha256, compare }
22
+
23
+ const NO_ENCRYPT = typeof process !== 'undefined' && !!process.env?.NO_ENCRYPT
24
+ const NOT_IMPL = true
25
+
26
+ export class Base {
27
+ valetRootCarCid = null // used on initial hydrate, if you change this, set this.valetCarCidMap = null
28
+ keyMaterial = null
29
+ keyId = 'null'
30
+
31
+ constructor (name, config = {}) {
32
+ this.instanceId = Math.random().toString(36).slice(2)
33
+ this.name = name
34
+ this.config = config
35
+
36
+ if (!this.config.branches) {
37
+ this.config.branches = {
38
+ main: { readonly: false }
39
+ }
40
+ }
41
+
42
+ // console.log('this.config', this.instanceId, this.name, this.config)
43
+ // if there is config.key and config.car,
44
+ // then we could skip loading the headers if we want.
45
+ // currently we don't do that, because we only use
46
+ // the config for first run, and then we use the headers
47
+ // once they exist
48
+ this.ready = this.getHeaders().then((blocksReady) => {
49
+ // console.log('blocksReady base', this.name, blocksReady)
50
+ return blocksReady
51
+ })
52
+ }
53
+
54
+ setCarCidMapCarCid (carCid) {
55
+ // console.trace('setCarCidMapCarCid', carCid)
56
+ if (!carCid) return
57
+ this.valetRootCarCid = parse(carCid)
58
+ this.valetCarCidMap = null
59
+ }
60
+
61
+ setKeyMaterial (km) {
62
+ if (km && !NO_ENCRYPT) {
63
+ const hex = Uint8Array.from(Buffer.from(km, 'hex'))
64
+ this.keyMaterial = km
65
+ const hash = sha1sync(hex)
66
+ this.keyId = Buffer.from(hash).toString('hex')
67
+ // console.log('setKeyMaterial', this.instanceId, this.name, km)
68
+ } else {
69
+ // console.log('setKeyMaterial', this.instanceId, this.name, km)
70
+ this.keyMaterial = null
71
+ this.keyId = 'null'
72
+ }
73
+ }
74
+
75
+ async saveCar (carCid, value, cids) {
76
+ const newValetCidCar = await this.updateCarCidMap(carCid, cids)
77
+ // console.log('writeCars', carCid.toString(), newValetCidCar.cid.toString())
78
+ const carList = [
79
+ {
80
+ cid: carCid,
81
+ bytes: value,
82
+ replaces: null
83
+ },
84
+ {
85
+ cid: newValetCidCar.cid,
86
+ bytes: newValetCidCar.bytes,
87
+ replaces: null
88
+ // replaces: this.valetRootCarCid // todo
89
+ }
90
+ ]
91
+
92
+ await this.writeCars(carList)
93
+ this.valetRootCarCid = newValetCidCar.cid
94
+ return newValetCidCar
95
+ }
96
+
97
+ applyHeaders (headers) {
98
+ // console.log('applyHeaders', headers.index)
99
+ this.headers = headers
100
+ // console.log('before applied', this.instanceId, this.name, this.keyMaterial, this.valetRootCarCid)
101
+ for (const [, header] of Object.entries(headers)) {
102
+ if (header) {
103
+ // console.log('applyHeaders', this.instanceId, this.name, header.key, header.car)
104
+ header.key && this.setKeyMaterial(header.key)
105
+ this.setCarCidMapCarCid(header.car)
106
+ }
107
+ }
108
+ if (!this.valetRootCarCid) {
109
+ this.setCarCidMapCarCid(this.config.car)
110
+ }
111
+ if (!this.keyMaterial) {
112
+ const nullKey = this.config.key === null
113
+ if (nullKey || this.config.key) {
114
+ this.setKeyMaterial(this.config.key)
115
+ } else {
116
+ this.setKeyMaterial(randomBytes(32).toString('hex'))
117
+ }
118
+ }
119
+ // console.log('applied', this.instanceId, this.name, this.keyMaterial, this.valetRootCarCid)
120
+ }
121
+
122
+ async getHeaders () {
123
+ const headers = {}
124
+ for (const [branch] of Object.entries(this.config.branches)) {
125
+ const got = await this.loadHeader(branch)
126
+ // console.log('getHeaders', this.name, branch, got)
127
+ headers[branch] = got
128
+ }
129
+ this.applyHeaders(headers)
130
+ return headers
131
+ }
132
+
133
+ loadHeader (branch = 'main') {
134
+ throw new Error('not implemented')
135
+ }
136
+
137
+ async saveHeader (header) {
138
+ // for each branch, save the header
139
+ // console.log('saveHeader', this.config.branches)
140
+ // for (const branch of this.branches) {
141
+ // await this.saveBranchHeader(branch)
142
+ // }
143
+ for (const [branch, { readonly }] of Object.entries(this.config.branches)) {
144
+ if (readonly) continue
145
+ // console.log('saveHeader', this.instanceId, this.name, branch, header)
146
+ await this.writeHeader(branch, header)
147
+ }
148
+ }
149
+
150
+ prepareHeader (header, json = true) {
151
+ header.key = this.keyMaterial
152
+ header.car = this.valetRootCarCid.toString()
153
+ // console.log('prepareHeader', this.instanceId, this.name, header.key, this.valetRootCarCid.toString())
154
+ return json ? JSON.stringify(header) : header
155
+ }
156
+
157
+ writeHeader (branch, header) {
158
+ throw new Error('not implemented')
159
+ }
160
+
161
+ async getCarCIDForCID (cid) {
162
+ const cidMap = await this.getCidCarMap()
163
+ const carCid = cidMap.get(cid.toString())
164
+ if (carCid) {
165
+ return { result: carCid }
166
+ }
167
+ return { result: null }
168
+ }
169
+
170
+ async readCar (carCid) {
171
+ if (NOT_IMPL) throw new Error('not implemented')
172
+ return new Uint8Array(carCid)
173
+ }
174
+
175
+ async getLoaderBlock (dataCID) {
176
+ const { result: carCid } = await this.getCarCIDForCID(dataCID)
177
+ if (!carCid) {
178
+ throw new Error('Missing car for: ' + dataCID)
179
+ }
180
+ // console.log('getLoaderBlock', dataCID, carCid)
181
+ const reader = await this.getCarReader(carCid)
182
+ return { block: await reader.get(dataCID), reader, carCid }
183
+ }
184
+
185
+ /** Private - internal **/
186
+
187
+ async getCidCarMap () {
188
+ // console.log('getCidCarMap', this.constructor.name, this.name, this.valetRootCarCid, typeof this.valetCarCidMap)
189
+ if (this.valetCarCidMap) return this.valetCarCidMap
190
+ if (this.valetRootCarCid) {
191
+ this.valetCarCidMap = await this.mapForIPLDHashmapCarCid(this.valetRootCarCid)
192
+ return this.valetCarCidMap
193
+ } else {
194
+ this.valetCarCidMap = new Map()
195
+ return this.valetCarCidMap
196
+ }
197
+ }
198
+
199
+ async mapForIPLDHashmapCarCid (carCid) {
200
+ // console.log('mapForIPLDHashmapCarCid', carCid)
201
+ const carMapReader = await this.getWriteableCarReader(carCid)
202
+ const indexNode = await load(carMapReader, carMapReader.root.cid, {
203
+ blockHasher: blockOpts.hasher,
204
+ blockCodec: blockOpts.codec
205
+ })
206
+ const theCarMap = new Map()
207
+ for await (const [key, value] of indexNode.entries()) {
208
+ // console.log('mapForIPLDHashmapCarCid', key, value)
209
+ theCarMap.set(key, value)
210
+ }
211
+ return theCarMap
212
+ }
213
+
214
+ async getWriteableCarReader (carCid) {
215
+ // console.log('getWriteableCarReader', carCid)
216
+ const carMapReader = await this.getCarReader(carCid)
217
+ const theseWriteableBlocks = new VMemoryBlockstore()
218
+ const combinedReader = {
219
+ blocks: theseWriteableBlocks,
220
+ root: carMapReader?.root,
221
+ put: async (cid, bytes) => {
222
+ return await theseWriteableBlocks.put(cid, bytes)
223
+ },
224
+ get: async cid => {
225
+ try {
226
+ const got = await theseWriteableBlocks.get(cid)
227
+ return got.bytes
228
+ } catch (e) {
229
+ if (!carMapReader) throw e
230
+ const bytes = await carMapReader.get(cid)
231
+ await theseWriteableBlocks.put(cid, bytes)
232
+ return bytes
233
+ }
234
+ }
235
+ }
236
+ return combinedReader
237
+ }
238
+
239
+ async getCarReader (carCid) {
240
+ carCid = carCid.toString()
241
+ const carBytes = await this.readCar(carCid)
242
+ // console.log('getCarReader', this.constructor.name, carCid, carBytes.length)
243
+ const reader = await CarReader.fromBytes(carBytes)
244
+ if (this.keyMaterial) {
245
+ const roots = await reader.getRoots()
246
+ const readerGetWithCodec = async cid => {
247
+ const got = await reader.get(cid)
248
+ let useCodec = codec
249
+ if (cid.toString().indexOf('bafy') === 0) {
250
+ useCodec = dagcbor // todo this is a dirty check
251
+ }
252
+ const decoded = await Block.decode({
253
+ ...got,
254
+ codec: useCodec,
255
+ hasher: sha256
256
+ })
257
+ return decoded
258
+ }
259
+ const { blocks } = await blocksFromEncryptedCarBlock(roots[0], readerGetWithCodec, this.keyMaterial)
260
+ const rootBlock = blocks[blocks.length - 1]
261
+ const blocksIterable = function * () { for (const block of blocks) yield block }
262
+
263
+ const gat = async dataCID => {
264
+ dataCID = dataCID.toString()
265
+ return blocks.find(b => b.cid.toString() === dataCID)
266
+ }
267
+
268
+ return {
269
+ entries: blocksIterable,
270
+ root: rootBlock,
271
+ gat,
272
+ get: async dataCID => {
273
+ const block = await gat(dataCID)
274
+ if (block) {
275
+ return block.bytes
276
+ }
277
+ }
278
+ }
279
+ } else {
280
+ const gat = async dataCID => {
281
+ return await reader.get(CID.parse(dataCID))
282
+ }
283
+ return {
284
+ // blocks,
285
+ entries: reader.blocks.bind(reader),
286
+ root: reader.getRoots()[0],
287
+ gat,
288
+ get: async dataCID => {
289
+ const gotBlock = await gat(dataCID)
290
+ if (gotBlock) {
291
+ return gotBlock.bytes
292
+ }
293
+ }
294
+ }
295
+ }
296
+ }
297
+
298
+ writeCars (cars) { }
299
+
300
+ async updateCarCidMap (carCid, cids) {
301
+ // this hydrates the map if it has not been hydrated
302
+ const theCarMap = await this.getCidCarMap()
303
+ for (const cid of cids) {
304
+ theCarMap.set(cid, carCid)
305
+ }
306
+ // todo can we debounce this? -- maybe put it into a queue so we can batch it
307
+ return await this.persistCarMap(theCarMap)
308
+ }
309
+
310
+ async persistCarMap (theCarMap) {
311
+ const ipldLoader = await getEmptyLoader()
312
+ const indexNode = await create(ipldLoader, {
313
+ bitWidth: 4,
314
+ bucketSize: 2,
315
+ blockHasher: blockOpts.hasher,
316
+ blockCodec: blockOpts.codec
317
+ })
318
+
319
+ for (const [key, value] of theCarMap.entries()) {
320
+ await indexNode.set(key, value)
321
+ }
322
+
323
+ let newValetCidCar
324
+ if (this.keyMaterial) {
325
+ newValetCidCar = await blocksToEncryptedCarBlock(indexNode.cid, ipldLoader.blocks, this.keyMaterial)
326
+ } else {
327
+ newValetCidCar = await blocksToCarBlock(indexNode.cid, ipldLoader.blocks)
328
+ }
329
+ return newValetCidCar
330
+ }
331
+ }
332
+
333
+ async function getEmptyLoader () {
334
+ const theseWriteableBlocks = new VMemoryBlockstore()
335
+ return {
336
+ blocks: theseWriteableBlocks,
337
+ put: async (cid, bytes) => {
338
+ return await theseWriteableBlocks.put(cid, bytes)
339
+ },
340
+ get: async cid => {
341
+ const got = await theseWriteableBlocks.get(cid)
342
+ return got.bytes
343
+ }
344
+ }
345
+ }
346
+
347
+ export class VMemoryBlockstore {
348
+ /** @type {Map<string, Uint8Array>} */
349
+ blocks = new Map()
350
+ instanceId = Math.random().toString(36).slice(2)
351
+
352
+ async get (cid) {
353
+ const bytes = this.blocks.get(cid.toString())
354
+ if (!bytes) throw new Error('block not found ' + cid.toString())
355
+ return { cid, bytes }
356
+ }
357
+
358
+ /**
359
+ * @param {any} cid
360
+ * @param {Uint8Array} bytes
361
+ */
362
+ async put (cid, bytes) {
363
+ this.blocks.set(cid.toString(), bytes)
364
+ }
365
+
366
+ * entries () {
367
+ for (const [str, bytes] of this.blocks) {
368
+ yield { cid: parse(str), bytes }
369
+ }
370
+ }
371
+ }
@@ -0,0 +1,67 @@
1
+ import { openDB } from 'idb'
2
+ import { Base } from './base.js'
3
+
4
+ const defaultConfig = {
5
+ headerKeyPrefix: 'fp.'
6
+ }
7
+
8
+ /* global localStorage */
9
+
10
+ export class Browser extends Base {
11
+ constructor (name, config = {}) {
12
+ super(name, Object.assign({}, defaultConfig, config))
13
+ this.isBrowser = false
14
+ try {
15
+ this.isBrowser = window.localStorage && true
16
+ } catch (e) {}
17
+ }
18
+
19
+ withDB = async dbWorkFun => {
20
+ if (!this.idb) {
21
+ this.idb = await openDB(`fp.${this.keyId}.${this.name}.valet`, 3, {
22
+ upgrade (db, oldVersion, newVersion, transaction) {
23
+ if (oldVersion < 1) {
24
+ db.createObjectStore('cars')
25
+ }
26
+ }
27
+ })
28
+ }
29
+ return await dbWorkFun(this.idb)
30
+ }
31
+
32
+ async writeCars (cars) {
33
+ if (this.config.readonly) return
34
+ return await this.withDB(async db => {
35
+ const tx = db.transaction(['cars'], 'readwrite')
36
+ for (const { cid, bytes, replaces } of cars) {
37
+ await tx.objectStore('cars').put(bytes, cid.toString())
38
+ // todo remove old maps
39
+ if (replaces) {
40
+ await tx.objectStore('cars').delete(replaces.toString())
41
+ }
42
+ }
43
+ return await tx.done
44
+ })
45
+ }
46
+
47
+ async readCar (carCid) {
48
+ return await this.withDB(async db => {
49
+ const tx = db.transaction(['cars'], 'readonly')
50
+ // console.log('getCarReader', carCid)
51
+ return await tx.objectStore('cars').get(carCid)
52
+ })
53
+ }
54
+
55
+ loadHeader (branch = 'main') {
56
+ return this.isBrowser && localStorage.getItem(this.headerKey(branch))
57
+ }
58
+
59
+ async writeHeader (branch, header) {
60
+ if (this.config.readonly) return
61
+ return this.isBrowser && localStorage.setItem(this.headerKey(branch), this.prepareHeader(header))
62
+ }
63
+
64
+ headerKey (branch = 'main') {
65
+ return this.config.headerKeyPrefix + this.name + '.' + branch
66
+ }
67
+ }
@@ -0,0 +1,70 @@
1
+ import { readFileSync } from 'node:fs'
2
+ import { mkdir, writeFile } from 'node:fs/promises'
3
+ import { join, dirname } from 'path'
4
+ import { homedir } from 'os'
5
+ import { Base } from './base.js'
6
+
7
+ export const defaultConfig = {
8
+ dataDir: join(homedir(), '.fireproof')
9
+ }
10
+
11
+ export class Filesystem extends Base {
12
+ constructor (name, config = {}) {
13
+ const mergedConfig = Object.assign({}, defaultConfig, config)
14
+ // console.log('Filesystem', name, mergedConfig, header)
15
+ super(name, mergedConfig)
16
+ }
17
+
18
+ async writeCars (cars) {
19
+ if (this.config.readonly) return
20
+ const writes = []
21
+ for (const { cid, bytes } of cars) {
22
+ const carFilename = join(this.config.dataDir, this.name, `${cid.toString()}.car`)
23
+ // console.log('writeCars', carFilename)
24
+ writes.push(writeSync(carFilename, bytes))
25
+ }
26
+ await Promise.all(writes)
27
+ }
28
+
29
+ async readCar (carCid) {
30
+ const carFilename = join(this.config.dataDir, this.name, `${carCid.toString()}.car`)
31
+ const got = readFileSync(carFilename)
32
+ // console.log('readCar', carFilename, got.constructor.name)
33
+ return got
34
+ }
35
+
36
+ loadHeader (branch = 'main') {
37
+ const header = loadSync(this.headerFilename(branch))
38
+ // console.log('fs getHeader', this.headerFilename(), header, typeof header)
39
+ if (!header) return null
40
+ return JSON.parse(header)
41
+ }
42
+
43
+ async writeHeader (branch, header) {
44
+ // console.log('saveHeader', this.isBrowser)
45
+ if (this.config.readonly) return
46
+ const pHeader = this.prepareHeader(header)
47
+ // console.log('writeHeader fs', branch, pHeader)
48
+ await writeSync(this.headerFilename(branch), pHeader)
49
+ }
50
+
51
+ headerFilename (branch = 'main') {
52
+ // console.log('headerFilename', this.config.dataDir, this.name)
53
+ return join(this.config.dataDir, this.name, branch + '.json')
54
+ }
55
+ }
56
+
57
+ function loadSync (filename) {
58
+ try {
59
+ return readFileSync(filename, 'utf8').toString()
60
+ } catch (error) {
61
+ // console.log('error', error)
62
+ return null
63
+ }
64
+ }
65
+
66
+ async function writeSync (fullpath, stringValue) {
67
+ await mkdir(dirname(fullpath), { recursive: true })
68
+ // writeFileSync(fullpath, stringValue)
69
+ await writeFile(fullpath, stringValue)
70
+ }
@@ -0,0 +1,60 @@
1
+ import fetch from 'node-fetch'
2
+ import { Base } from './base.js'
3
+
4
+ const defaultConfig = {
5
+ url: 'http://localhost:4000'
6
+ }
7
+
8
+ export class Rest extends Base {
9
+ constructor (name, config = {}) {
10
+ super(name, Object.assign({}, defaultConfig, config))
11
+ // console.log('Rest', name, config)
12
+ }
13
+
14
+ headerURL (branch = 'main') {
15
+ return `${this.config.url}/${branch}.json`
16
+ }
17
+
18
+ async writeCars (cars) {
19
+ if (this.config.readonly) return
20
+ for (const { cid, bytes } of cars) {
21
+ const carURL = `${this.config.url}/${cid.toString()}.car`
22
+ const response = await fetch(carURL, {
23
+ method: 'PUT',
24
+ body: bytes,
25
+ headers: { 'Content-Type': 'application/car' }
26
+ })
27
+ if (!response.ok) throw new Error(`An error occurred: ${response.statusText}`)
28
+ }
29
+ }
30
+
31
+ async readCar (carCid) {
32
+ const carURL = `${this.config.url}/${carCid.toString()}.car`
33
+ const response = await fetch(carURL)
34
+ if (!response.ok) throw new Error(`An error occurred: ${response.statusText}`)
35
+ const got = await response.arrayBuffer()
36
+ return new Uint8Array(got)
37
+ }
38
+
39
+ async loadHeader (branch = 'main') {
40
+ const response = await fetch(this.headerURL(branch))
41
+ // console.log('rest getHeader', response.constructor.name)
42
+ if (!response.ok) return null
43
+ const got = await response.json()
44
+ // console.log('rest getHeader', got)
45
+ return got
46
+ }
47
+
48
+ async writeHeader (branch, header) {
49
+ if (this.config.readonly) return
50
+ const pHeader = this.prepareHeader(header)
51
+ // console.log('writeHeader rt', branch, pHeader)
52
+
53
+ const response = await fetch(this.headerURL(branch), {
54
+ method: 'PUT',
55
+ body: pHeader,
56
+ headers: { 'Content-Type': 'application/json' }
57
+ })
58
+ if (!response.ok) throw new Error(`An error occurred: ${response.statusText}`)
59
+ }
60
+ }
File without changes
package/src/sync.js CHANGED
@@ -206,7 +206,7 @@ export class Sync {
206
206
  }
207
207
 
208
208
  if (typeof key === 'undefined') {
209
- key = blocks.valet?.getKeyMaterial()
209
+ key = blocks.valet?.primary.keyMaterial
210
210
  }
211
211
  if (key) {
212
212
  return blocksToEncryptedCarBlock(